diff options
Diffstat (limited to 'debian/patches')
37 files changed, 6455 insertions, 0 deletions
diff --git a/debian/patches/auth-log-verbosity.patch b/debian/patches/auth-log-verbosity.patch new file mode 100644 index 0000000..cf6febf --- /dev/null +++ b/debian/patches/auth-log-verbosity.patch @@ -0,0 +1,119 @@ +From b2b04daa38b264f346acd81e08d224dbf33bac5b Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:10:02 +0000 +Subject: Quieten logs when multiple from= restrictions are used + +Bug-Debian: http://bugs.debian.org/630606 +Forwarded: no +Last-Update: 2013-09-14 + +Patch-Name: auth-log-verbosity.patch +--- + auth-options.c | 35 ++++++++++++++++++++++++++--------- + auth-options.h | 1 + + auth2-pubkey.c | 3 +++ + 3 files changed, 30 insertions(+), 9 deletions(-) + +diff --git a/auth-options.c b/auth-options.c +index 57b49f7f..7eb87b35 100644 +--- a/auth-options.c ++++ b/auth-options.c +@@ -59,9 +59,20 @@ int forced_tun_device = -1; + /* "principals=" option. */ + char *authorized_principals = NULL; + ++/* Throttle log messages. */ ++int logged_from_hostip = 0; ++int logged_cert_hostip = 0; ++ + extern ServerOptions options; + + void ++auth_start_parse_options(void) ++{ ++ logged_from_hostip = 0; ++ logged_cert_hostip = 0; ++} ++ ++void + auth_clear_options(void) + { + no_agent_forwarding_flag = 0; +@@ -316,10 +327,13 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) + /* FALLTHROUGH */ + case 0: + free(patterns); +- logit("Authentication tried for %.100s with " +- "correct key but not from a permitted " +- "host (host=%.200s, ip=%.200s).", +- pw->pw_name, remote_host, remote_ip); ++ if (!logged_from_hostip) { ++ logit("Authentication tried for %.100s with " ++ "correct key but not from a permitted " ++ "host (host=%.200s, ip=%.200s).", ++ pw->pw_name, remote_host, remote_ip); ++ logged_from_hostip = 1; ++ } + auth_debug_add("Your host '%.200s' is not " + "permitted to use this key for login.", + remote_host); +@@ -543,11 +557,14 @@ parse_option_list(struct sshbuf *oblob, struct passwd *pw, + break; + case 0: + /* no match */ +- logit("Authentication tried for %.100s " +- "with valid certificate but not " +- "from a permitted host " +- "(ip=%.200s).", pw->pw_name, +- remote_ip); ++ if (!logged_cert_hostip) { ++ logit("Authentication tried for %.100s " ++ "with valid certificate but not " ++ "from a permitted host " ++ "(ip=%.200s).", pw->pw_name, ++ remote_ip); ++ logged_cert_hostip = 1; ++ } + auth_debug_add("Your address '%.200s' " + "is not permitted to use this " + "certificate for login.", +diff --git a/auth-options.h b/auth-options.h +index 52cbb42a..82355276 100644 +--- a/auth-options.h ++++ b/auth-options.h +@@ -33,6 +33,7 @@ extern int forced_tun_device; + extern int key_is_cert_authority; + extern char *authorized_principals; + ++void auth_start_parse_options(void); + int auth_parse_options(struct passwd *, char *, char *, u_long); + void auth_clear_options(void); + int auth_cert_options(struct sshkey *, struct passwd *, const char **); +diff --git a/auth2-pubkey.c b/auth2-pubkey.c +index 20f3309e..add77136 100644 +--- a/auth2-pubkey.c ++++ b/auth2-pubkey.c +@@ -566,6 +566,7 @@ process_principals(FILE *f, char *file, struct passwd *pw, + u_long linenum = 0; + u_int i; + ++ auth_start_parse_options(); + while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { + /* Skip leading whitespace. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) +@@ -764,6 +765,7 @@ check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw) + found_key = 0; + + found = NULL; ++ auth_start_parse_options(); + while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { + char *cp, *key_options = NULL, *fp = NULL; + const char *reason = NULL; +@@ -911,6 +913,7 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) + if (key_cert_check_authority(key, 0, 1, + use_authorized_principals ? NULL : pw->pw_name, &reason) != 0) + goto fail_reason; ++ auth_start_parse_options(); + if (auth_cert_options(key, pw, &reason) != 0) + goto fail_reason; + diff --git a/debian/patches/authorized-keys-man-symlink.patch b/debian/patches/authorized-keys-man-symlink.patch new file mode 100644 index 0000000..a3306e3 --- /dev/null +++ b/debian/patches/authorized-keys-man-symlink.patch @@ -0,0 +1,26 @@ +From 7ad6dd01af3f4531ccc8e918bc857738e195fd3d Mon Sep 17 00:00:00 2001 +From: Tomas Pospisek <tpo_deb@sourcepole.ch> +Date: Sun, 9 Feb 2014 16:10:07 +0000 +Subject: Install authorized_keys(5) as a symlink to sshd(8) + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1720 +Bug-Debian: http://bugs.debian.org/441817 +Last-Update: 2013-09-14 + +Patch-Name: authorized-keys-man-symlink.patch +--- + Makefile.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Makefile.in b/Makefile.in +index 00a320e1..a6eb81ec 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -335,6 +335,7 @@ install-files: + $(INSTALL) -m 644 sshd_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/sshd_config.5 + $(INSTALL) -m 644 ssh_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/ssh_config.5 + $(INSTALL) -m 644 sshd.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 ++ ln -s ../$(mansubdir)8/sshd.8 $(DESTDIR)$(mandir)/$(mansubdir)5/authorized_keys.5 + $(INSTALL) -m 644 sftp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 + $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 + $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 diff --git a/debian/patches/debian-banner.patch b/debian/patches/debian-banner.patch new file mode 100644 index 0000000..874728b --- /dev/null +++ b/debian/patches/debian-banner.patch @@ -0,0 +1,111 @@ +From 2a1aeb898e4214f98acc210c992d33334e6710dd Mon Sep 17 00:00:00 2001 +From: Kees Cook <kees@debian.org> +Date: Sun, 9 Feb 2014 16:10:06 +0000 +Subject: Add DebianBanner server configuration option + +Setting this to "no" causes sshd to omit the Debian revision from its +initial protocol handshake, for those scared by package-versioning.patch. + +Bug-Debian: http://bugs.debian.org/562048 +Forwarded: not-needed +Last-Update: 2015-11-29 + +Patch-Name: debian-banner.patch +--- + servconf.c | 9 +++++++++ + servconf.h | 2 ++ + sshd.c | 3 ++- + sshd_config.5 | 5 +++++ + 4 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/servconf.c b/servconf.c +index 49d3bdc8..1cee3d6c 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -166,6 +166,7 @@ initialize_server_options(ServerOptions *options) + options->version_addendum = NULL; + options->fingerprint_hash = -1; + options->disable_forwarding = -1; ++ options->debian_banner = -1; + } + + /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ +@@ -339,6 +340,8 @@ fill_default_server_options(ServerOptions *options) + options->fingerprint_hash = SSH_FP_HASH_DEFAULT; + if (options->disable_forwarding == -1) + options->disable_forwarding = 0; ++ if (options->debian_banner == -1) ++ options->debian_banner = 1; + + assemble_algorithms(options); + +@@ -425,6 +428,7 @@ typedef enum { + sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, + sStreamLocalBindMask, sStreamLocalBindUnlink, + sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding, ++ sDebianBanner, + sDeprecated, sIgnore, sUnsupported + } ServerOpCodes; + +@@ -577,6 +581,7 @@ static struct { + { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL }, + { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, + { "disableforwarding", sDisableForwarding, SSHCFG_ALL }, ++ { "debianbanner", sDebianBanner, SSHCFG_GLOBAL }, + { NULL, sBadOption, 0 } + }; + +@@ -1860,6 +1865,10 @@ process_server_config_line(ServerOptions *options, char *line, + options->fingerprint_hash = value; + break; + ++ case sDebianBanner: ++ intptr = &options->debian_banner; ++ goto parse_int; ++ + case sDeprecated: + case sIgnore: + case sUnsupported: +diff --git a/servconf.h b/servconf.h +index 90dfa4c2..913a21b3 100644 +--- a/servconf.h ++++ b/servconf.h +@@ -191,6 +191,8 @@ typedef struct { + char *auth_methods[MAX_AUTH_METHODS]; + + int fingerprint_hash; ++ ++ int debian_banner; + } ServerOptions; + + /* Information about the incoming connection as used by Match */ +diff --git a/sshd.c b/sshd.c +index 49f3a2e5..eebf1984 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -378,7 +378,8 @@ sshd_exchange_identification(struct ssh *ssh, int sock_in, int sock_out) + char remote_version[256]; /* Must be at least as big as buf. */ + + xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s%s", +- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_RELEASE, ++ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, ++ options.debian_banner ? SSH_RELEASE : SSH_RELEASE_MINIMUM, + *options.version_addendum == '\0' ? "" : " ", + options.version_addendum, newline); + +diff --git a/sshd_config.5 b/sshd_config.5 +index 283ba889..4ea0a9c3 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -526,6 +526,11 @@ or + .Cm no . + The default is + .Cm yes . ++.It Cm DebianBanner ++Specifies whether the distribution-specified extra version suffix is ++included during initial protocol handshake. ++The default is ++.Cm yes . + .It Cm DenyGroups + This keyword can be followed by a list of group name patterns, separated + by spaces. diff --git a/debian/patches/debian-config.patch b/debian/patches/debian-config.patch new file mode 100644 index 0000000..ff3f5f4 --- /dev/null +++ b/debian/patches/debian-config.patch @@ -0,0 +1,238 @@ +From 2b53482aec037f0747198f19e449f51d921acd30 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:10:18 +0000 +Subject: Various Debian-specific configuration changes + +ssh: Enable ForwardX11Trusted, returning to earlier semantics which cause +fewer problems with existing setups (http://bugs.debian.org/237021). + +ssh: Set 'SendEnv LANG LC_*' by default (http://bugs.debian.org/264024). + +ssh: Enable HashKnownHosts by default to try to limit the spread of ssh +worms. + +ssh: Enable GSSAPIAuthentication by default. + +sshd: Enable PAM, disable ChallengeResponseAuthentication, and disable +PrintMotd. + +sshd: Enable X11Forwarding. + +sshd: Set 'AcceptEnv LANG LC_*' by default. + +sshd: Change sftp subsystem path to /usr/lib/openssh/sftp-server. + +Document all of this. + +Author: Russ Allbery <rra@debian.org> +Forwarded: not-needed +Last-Update: 2016-12-26 + +Patch-Name: debian-config.patch +--- + readconf.c | 2 +- + ssh.1 | 21 +++++++++++++++++++++ + ssh_config | 6 +++++- + ssh_config.5 | 19 ++++++++++++++++++- + sshd_config | 16 ++++++++++------ + sshd_config.5 | 22 ++++++++++++++++++++++ + 6 files changed, 77 insertions(+), 9 deletions(-) + +diff --git a/readconf.c b/readconf.c +index c02cdf63..d1091cbd 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -1927,7 +1927,7 @@ fill_default_options(Options * options) + if (options->forward_x11 == -1) + options->forward_x11 = 0; + if (options->forward_x11_trusted == -1) +- options->forward_x11_trusted = 0; ++ options->forward_x11_trusted = 1; + if (options->forward_x11_timeout == -1) + options->forward_x11_timeout = 1200; + /* +diff --git a/ssh.1 b/ssh.1 +index 22e56a7b..6aa57c46 100644 +--- a/ssh.1 ++++ b/ssh.1 +@@ -785,6 +785,16 @@ directive in + .Xr ssh_config 5 + for more information. + .Pp ++(Debian-specific: X11 forwarding is not subjected to X11 SECURITY extension ++restrictions by default, because too many programs currently crash in this ++mode. ++Set the ++.Cm ForwardX11Trusted ++option to ++.Dq no ++to restore the upstream behaviour. ++This may change in future depending on client-side improvements.) ++.Pp + .It Fl x + Disables X11 forwarding. + .Pp +@@ -793,6 +803,17 @@ Enables trusted X11 forwarding. + Trusted X11 forwardings are not subjected to the X11 SECURITY extension + controls. + .Pp ++(Debian-specific: This option does nothing in the default configuration: it ++is equivalent to ++.Dq Cm ForwardX11Trusted No yes , ++which is the default as described above. ++Set the ++.Cm ForwardX11Trusted ++option to ++.Dq no ++to restore the upstream behaviour. ++This may change in future depending on client-side improvements.) ++.Pp + .It Fl y + Send log information using the + .Xr syslog 3 +diff --git a/ssh_config b/ssh_config +index 4e879cd2..093c8366 100644 +--- a/ssh_config ++++ b/ssh_config +@@ -17,9 +17,10 @@ + # list of available options, their meanings and defaults, please see the + # ssh_config(5) man page. + +-# Host * ++Host * + # ForwardAgent no + # ForwardX11 no ++# ForwardX11Trusted yes + # RhostsRSAAuthentication no + # RSAAuthentication yes + # PasswordAuthentication yes +@@ -50,3 +51,6 @@ + # VisualHostKey no + # ProxyCommand ssh -q -W %h:%p gateway.example.com + # RekeyLimit 1G 1h ++ SendEnv LANG LC_* ++ HashKnownHosts yes ++ GSSAPIAuthentication yes +diff --git a/ssh_config.5 b/ssh_config.5 +index 8698c28e..26f983a3 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -74,6 +74,22 @@ Since the first obtained value for each parameter is used, more + host-specific declarations should be given near the beginning of the + file, and general defaults at the end. + .Pp ++Note that the Debian ++.Ic openssh-client ++package sets several options as standard in ++.Pa /etc/ssh/ssh_config ++which are not the default in ++.Xr ssh 1 : ++.Pp ++.Bl -bullet -offset indent -compact ++.It ++.Cm SendEnv No LANG LC_* ++.It ++.Cm HashKnownHosts No yes ++.It ++.Cm GSSAPIAuthentication No yes ++.El ++.Pp + The file contains keyword-argument pairs, one per line. + Lines starting with + .Ql # +@@ -711,11 +727,12 @@ elapsed. + .It Cm ForwardX11Trusted + If this option is set to + .Cm yes , ++(the Debian-specific default), + remote X11 clients will have full access to the original X11 display. + .Pp + If this option is set to + .Cm no +-(the default), ++(the upstream default), + remote X11 clients will be considered untrusted and prevented + from stealing or tampering with data belonging to trusted X11 + clients. +diff --git a/sshd_config b/sshd_config +index 00e5a728..13cbe2c6 100644 +--- a/sshd_config ++++ b/sshd_config +@@ -58,8 +58,9 @@ AuthorizedKeysFile .ssh/authorized_keys + #PasswordAuthentication yes + #PermitEmptyPasswords no + +-# Change to no to disable s/key passwords +-#ChallengeResponseAuthentication yes ++# Change to yes to enable challenge-response passwords (beware issues with ++# some PAM modules and threads) ++ChallengeResponseAuthentication no + + # Kerberos options + #KerberosAuthentication no +@@ -82,16 +83,16 @@ AuthorizedKeysFile .ssh/authorized_keys + # If you just want the PAM account and session checks to run without + # PAM authentication, then enable this but set PasswordAuthentication + # and ChallengeResponseAuthentication to 'no'. +-#UsePAM no ++UsePAM yes + + #AllowAgentForwarding yes + #AllowTcpForwarding yes + #GatewayPorts no +-#X11Forwarding no ++X11Forwarding yes + #X11DisplayOffset 10 + #X11UseLocalhost yes + #PermitTTY yes +-#PrintMotd yes ++PrintMotd no + #PrintLastLog yes + #TCPKeepAlive yes + #UseLogin no +@@ -110,8 +111,11 @@ AuthorizedKeysFile .ssh/authorized_keys + # no default banner path + #Banner none + ++# Allow client to pass locale environment variables ++AcceptEnv LANG LC_* ++ + # override default of no subsystems +-Subsystem sftp /usr/libexec/sftp-server ++Subsystem sftp /usr/lib/openssh/sftp-server + + # Example of overriding settings on a per-user basis + #Match User anoncvs +diff --git a/sshd_config.5 b/sshd_config.5 +index e45a8937..703a9cdd 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -57,6 +57,28 @@ Arguments may optionally be enclosed in double quotes + .Pq \&" + in order to represent arguments containing spaces. + .Pp ++Note that the Debian ++.Ic openssh-server ++package sets several options as standard in ++.Pa /etc/ssh/sshd_config ++which are not the default in ++.Xr sshd 8 : ++.Pp ++.Bl -bullet -offset indent -compact ++.It ++.Cm ChallengeResponseAuthentication No no ++.It ++.Cm X11Forwarding No yes ++.It ++.Cm PrintMotd No no ++.It ++.Cm AcceptEnv No LANG LC_* ++.It ++.Cm Subsystem No sftp /usr/lib/openssh/sftp-server ++.It ++.Cm UsePAM No yes ++.El ++.Pp + The possible + keywords and their meanings are as follows (note that + keywords are case-insensitive and arguments are case-sensitive): diff --git a/debian/patches/dnssec-sshfp.patch b/debian/patches/dnssec-sshfp.patch new file mode 100644 index 0000000..2e2f961 --- /dev/null +++ b/debian/patches/dnssec-sshfp.patch @@ -0,0 +1,94 @@ +From c1248ea6dcbbf5702d65efc1750763f66a97ba19 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:10:01 +0000 +Subject: Force use of DNSSEC even if "options edns0" isn't in resolv.conf + +This allows SSHFP DNS records to be verified if glibc 2.11 is installed. + +Origin: vendor, https://cvs.fedoraproject.org/viewvc/F-12/openssh/openssh-5.2p1-edns.patch?revision=1.1&view=markup +Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=572049 +Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=572049 +Last-Update: 2010-04-06 + +Patch-Name: dnssec-sshfp.patch +--- + dns.c | 14 +++++++++++++- + openbsd-compat/getrrsetbyname.c | 10 +++++----- + openbsd-compat/getrrsetbyname.h | 3 +++ + 3 files changed, 21 insertions(+), 6 deletions(-) + +diff --git a/dns.c b/dns.c +index e813afea..fce2e308 100644 +--- a/dns.c ++++ b/dns.c +@@ -206,6 +206,7 @@ verify_host_key_dns(const char *hostname, struct sockaddr *address, + { + u_int counter; + int result; ++ unsigned int rrset_flags = 0; + struct rrsetinfo *fingerprints = NULL; + + u_int8_t hostkey_algorithm; +@@ -229,8 +230,19 @@ verify_host_key_dns(const char *hostname, struct sockaddr *address, + return -1; + } + ++ /* ++ * Original getrrsetbyname function, found on OpenBSD for example, ++ * doesn't accept any flag and prerequisite for obtaining AD bit in ++ * DNS response is set by "options edns0" in resolv.conf. ++ * ++ * Our version is more clever and use RRSET_FORCE_EDNS0 flag. ++ */ ++#ifndef HAVE_GETRRSETBYNAME ++ rrset_flags |= RRSET_FORCE_EDNS0; ++#endif + result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, +- DNS_RDATATYPE_SSHFP, 0, &fingerprints); ++ DNS_RDATATYPE_SSHFP, rrset_flags, &fingerprints); ++ + if (result) { + verbose("DNS lookup error: %s", dns_result_totext(result)); + return -1; +diff --git a/openbsd-compat/getrrsetbyname.c b/openbsd-compat/getrrsetbyname.c +index dc6fe053..e061a290 100644 +--- a/openbsd-compat/getrrsetbyname.c ++++ b/openbsd-compat/getrrsetbyname.c +@@ -209,8 +209,8 @@ getrrsetbyname(const char *hostname, unsigned int rdclass, + goto fail; + } + +- /* don't allow flags yet, unimplemented */ +- if (flags) { ++ /* Allow RRSET_FORCE_EDNS0 flag only. */ ++ if ((flags & !RRSET_FORCE_EDNS0) != 0) { + result = ERRSET_INVAL; + goto fail; + } +@@ -226,9 +226,9 @@ getrrsetbyname(const char *hostname, unsigned int rdclass, + #endif /* DEBUG */ + + #ifdef RES_USE_DNSSEC +- /* turn on DNSSEC if EDNS0 is configured */ +- if (_resp->options & RES_USE_EDNS0) +- _resp->options |= RES_USE_DNSSEC; ++ /* turn on DNSSEC if required */ ++ if (flags & RRSET_FORCE_EDNS0) ++ _resp->options |= (RES_USE_EDNS0|RES_USE_DNSSEC); + #endif /* RES_USE_DNSEC */ + + /* make query */ +diff --git a/openbsd-compat/getrrsetbyname.h b/openbsd-compat/getrrsetbyname.h +index 1283f550..dbbc85a2 100644 +--- a/openbsd-compat/getrrsetbyname.h ++++ b/openbsd-compat/getrrsetbyname.h +@@ -72,6 +72,9 @@ + #ifndef RRSET_VALIDATED + # define RRSET_VALIDATED 1 + #endif ++#ifndef RRSET_FORCE_EDNS0 ++# define RRSET_FORCE_EDNS0 0x0001 ++#endif + + /* + * Return codes for getrrsetbyname() diff --git a/debian/patches/doc-hash-tab-completion.patch b/debian/patches/doc-hash-tab-completion.patch new file mode 100644 index 0000000..814d8ad --- /dev/null +++ b/debian/patches/doc-hash-tab-completion.patch @@ -0,0 +1,28 @@ +From 87e480b4f405f3249d7f8a912849eb6263456353 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:10:11 +0000 +Subject: Document that HashKnownHosts may break tab-completion + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1727 +Bug-Debian: http://bugs.debian.org/430154 +Last-Update: 2013-09-14 + +Patch-Name: doc-hash-tab-completion.patch +--- + ssh_config.5 | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/ssh_config.5 b/ssh_config.5 +index 0483a1ee..8698c28e 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -805,6 +805,9 @@ Note that existing names and addresses in known hosts files + will not be converted automatically, + but may be manually hashed using + .Xr ssh-keygen 1 . ++Use of this option may break facilities such as tab-completion that rely ++on being able to read unhashed host names from ++.Pa ~/.ssh/known_hosts . + .It Cm HostbasedAuthentication + Specifies whether to try rhosts based authentication with public key + authentication. diff --git a/debian/patches/doc-upstart.patch b/debian/patches/doc-upstart.patch new file mode 100644 index 0000000..14d6ff8 --- /dev/null +++ b/debian/patches/doc-upstart.patch @@ -0,0 +1,29 @@ +From 7ea8a3c1e0c2ff4998b3fe3caaaba8ff42e513ff Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@ubuntu.com> +Date: Sun, 9 Feb 2014 16:10:12 +0000 +Subject: Refer to ssh's Upstart job as well as its init script + +Forwarded: not-needed +Last-Update: 2013-09-14 + +Patch-Name: doc-upstart.patch +--- + sshd.8 | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/sshd.8 b/sshd.8 +index e6915141..38a72540 100644 +--- a/sshd.8 ++++ b/sshd.8 +@@ -65,7 +65,10 @@ over an insecure network. + .Nm + listens for connections from clients. + It is normally started at boot from +-.Pa /etc/init.d/ssh . ++.Pa /etc/init.d/ssh ++(or ++.Pa /etc/init/ssh.conf ++on systems using the Upstart init daemon). + It forks a new + daemon for each incoming connection. + The forked daemons handle diff --git a/debian/patches/gnome-ssh-askpass2-icon.patch b/debian/patches/gnome-ssh-askpass2-icon.patch new file mode 100644 index 0000000..1558dbd --- /dev/null +++ b/debian/patches/gnome-ssh-askpass2-icon.patch @@ -0,0 +1,26 @@ +From 0327e9b3a5f6d1e945f1f028e742e14cf5823962 Mon Sep 17 00:00:00 2001 +From: Vincent Untz <vuntz@ubuntu.com> +Date: Sun, 9 Feb 2014 16:10:16 +0000 +Subject: Give the ssh-askpass-gnome window a default icon + +Bug-Ubuntu: https://bugs.launchpad.net/bugs/27152 +Last-Update: 2010-02-28 + +Patch-Name: gnome-ssh-askpass2-icon.patch +--- + contrib/gnome-ssh-askpass2.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/contrib/gnome-ssh-askpass2.c b/contrib/gnome-ssh-askpass2.c +index 535a6927..e37a1338 100644 +--- a/contrib/gnome-ssh-askpass2.c ++++ b/contrib/gnome-ssh-askpass2.c +@@ -211,6 +211,8 @@ main(int argc, char **argv) + + gtk_init(&argc, &argv); + ++ gtk_window_set_default_icon_from_file ("/usr/share/pixmaps/ssh-askpass-gnome.png", NULL); ++ + if (argc > 1) { + message = g_strjoinv(" ", argv + 1); + } else { diff --git a/debian/patches/gssapi.patch b/debian/patches/gssapi.patch new file mode 100644 index 0000000..7196d16 --- /dev/null +++ b/debian/patches/gssapi.patch @@ -0,0 +1,3297 @@ +From 48fbb156bdc676fb6ba6817770e4e971fbf85b1f Mon Sep 17 00:00:00 2001 +From: Simon Wilkinson <simon@sxw.org.uk> +Date: Sun, 9 Feb 2014 16:09:48 +0000 +Subject: GSSAPI key exchange support + +This patch has been rejected upstream: "None of the OpenSSH developers are +in favour of adding this, and this situation has not changed for several +years. This is not a slight on Simon's patch, which is of fine quality, but +just that a) we don't trust GSSAPI implementations that much and b) we don't +like adding new KEX since they are pre-auth attack surface. This one is +particularly scary, since it requires hooks out to typically root-owned +system resources." + +However, quite a lot of people rely on this in Debian, and it's better to +have it merged into the main openssh package rather than having separate +-krb5 packages (as we used to have). It seems to have a generally good +security history. + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 +Last-Updated: 2017-01-16 + +Patch-Name: gssapi.patch +--- + ChangeLog.gssapi | 113 +++++++++++++++++++ + Makefile.in | 3 +- + auth-krb5.c | 17 ++- + auth.c | 96 +--------------- + auth2-gss.c | 48 +++++++- + auth2.c | 2 + + canohost.c | 93 +++++++++++++++ + canohost.h | 3 + + clientloop.c | 15 ++- + config.h.in | 6 + + configure.ac | 24 ++++ + gss-genr.c | 275 +++++++++++++++++++++++++++++++++++++++++++- + gss-serv-krb5.c | 85 ++++++++++++-- + gss-serv.c | 184 +++++++++++++++++++++++++++--- + kex.c | 19 ++++ + kex.h | 14 +++ + kexgssc.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + kexgsss.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++ + monitor.c | 115 +++++++++++++++++-- + monitor.h | 3 + + monitor_wrap.c | 47 +++++++- + monitor_wrap.h | 4 +- + readconf.c | 42 +++++++ + readconf.h | 5 + + servconf.c | 28 ++++- + servconf.h | 2 + + ssh-gss.h | 41 ++++++- + ssh_config | 2 + + ssh_config.5 | 32 ++++++ + sshconnect2.c | 131 ++++++++++++++++++++- + sshd.c | 112 +++++++++++++++++- + sshd_config | 2 + + sshd_config.5 | 10 ++ + sshkey.c | 3 +- + sshkey.h | 1 + + 35 files changed, 2062 insertions(+), 148 deletions(-) + create mode 100644 ChangeLog.gssapi + create mode 100644 kexgssc.c + create mode 100644 kexgsss.c + +diff --git a/ChangeLog.gssapi b/ChangeLog.gssapi +new file mode 100644 +index 00000000..f117a336 +--- /dev/null ++++ b/ChangeLog.gssapi +@@ -0,0 +1,113 @@ ++20110101 ++ - Finally update for OpenSSH 5.6p1 ++ - Add GSSAPIServerIdentity option from Jim Basney ++ ++20100308 ++ - [ Makefile.in, key.c, key.h ] ++ Updates for OpenSSH 5.4p1 ++ - [ servconf.c ] ++ Include GSSAPI options in the sshd -T configuration dump, and flag ++ some older configuration options as being unsupported. Thanks to Colin ++ Watson. ++ - ++ ++20100124 ++ - [ sshconnect2.c ] ++ Adapt to deal with additional element in Authmethod structure. Thanks to ++ Colin Watson ++ ++20090615 ++ - [ gss-genr.c gss-serv.c kexgssc.c kexgsss.c monitor.c sshconnect2.c ++ sshd.c ] ++ Fix issues identified by Greg Hudson following a code review ++ Check return value of gss_indicate_mechs ++ Protect GSSAPI calls in monitor, so they can only be used if enabled ++ Check return values of bignum functions in key exchange ++ Use BN_clear_free to clear other side's DH value ++ Make ssh_gssapi_id_kex more robust ++ Only configure kex table pointers if GSSAPI is enabled ++ Don't leak mechanism list, or gss mechanism list ++ Cast data.length before printing ++ If serverkey isn't provided, use an empty string, rather than NULL ++ ++20090201 ++ - [ gss-genr.c gss-serv.c kex.h kexgssc.c readconf.c readconf.h ssh-gss.h ++ ssh_config.5 sshconnet2.c ] ++ Add support for the GSSAPIClientIdentity option, which allows the user ++ to specify which GSSAPI identity to use to contact a given server ++ ++20080404 ++ - [ gss-serv.c ] ++ Add code to actually implement GSSAPIStrictAcceptCheck, which had somehow ++ been omitted from a previous version of this patch. Reported by Borislav ++ Stoichkov ++ ++20070317 ++ - [ gss-serv-krb5.c ] ++ Remove C99ism, where new_ccname was being declared in the middle of a ++ function ++ ++20061220 ++ - [ servconf.c ] ++ Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and ++ documented, behaviour. Reported by Dan Watson. ++ ++20060910 ++ - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c ++ ssh-gss.h ] ++ add support for gss-group14-sha1 key exchange mechanisms ++ - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ] ++ Add GSSAPIStrictAcceptorCheck option to allow the disabling of ++ acceptor principal checking on multi-homed machines. ++ <Bugzilla #928> ++ - [ sshd_config ssh_config ] ++ Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample ++ configuration files ++ - [ kexgss.c kegsss.c sshconnect2.c sshd.c ] ++ Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf() ++ Limit length of error messages displayed by client ++ ++20060909 ++ - [ gss-genr.c gss-serv.c ] ++ move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server ++ only, where they belong ++ <Bugzilla #1225> ++ ++20060829 ++ - [ gss-serv-krb5.c ] ++ Fix CCAPI credentials cache name when creating KRB5CCNAME environment ++ variable ++ ++20060828 ++ - [ gss-genr.c ] ++ Avoid Heimdal context freeing problem ++ <Fixed upstream 20060829> ++ ++20060818 ++ - [ gss-genr.c ssh-gss.h sshconnect2.c ] ++ Make sure that SPENGO is disabled ++ <Bugzilla #1218 - Fixed upstream 20060818> ++ ++20060421 ++ - [ gssgenr.c, sshconnect2.c ] ++ a few type changes (signed versus unsigned, int versus size_t) to ++ fix compiler errors/warnings ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ kexgssc.c, sshconnect2.c ] ++ fix uninitialized variable warnings ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ gssgenr.c ] ++ pass oid to gss_display_status (helpful when using GSSAPI mechglue) ++ (from jbasney AT ncsa.uiuc.edu) ++ <Bugzilla #1220 > ++ - [ gss-serv-krb5.c ] ++ #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H ++ (from jbasney AT ncsa.uiuc.edu) ++ <Fixed upstream 20060304> ++ - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c ++ add client-side GssapiKeyExchange option ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ sshconnect2.c ] ++ add support for GssapiTrustDns option for gssapi-with-mic ++ (from jbasney AT ncsa.uiuc.edu) ++ <gssapi-with-mic support is Bugzilla #1008> +diff --git a/Makefile.in b/Makefile.in +index e10f3742..00a320e1 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -92,6 +92,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ + kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ ++ kexgssc.o \ + platform-pledge.o platform-tracing.o + + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ +@@ -105,7 +106,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \ + auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ + auth2-none.o auth2-passwd.o auth2-pubkey.o \ + monitor.o monitor_wrap.o auth-krb5.o \ +- auth2-gss.o gss-serv.o gss-serv-krb5.o \ ++ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ + sftp-server.o sftp-common.o \ + sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ +diff --git a/auth-krb5.c b/auth-krb5.c +index a5a81ed2..38e7fee2 100644 +--- a/auth-krb5.c ++++ b/auth-krb5.c +@@ -182,8 +182,13 @@ auth_krb5_password(Authctxt *authctxt, const char *password) + + len = strlen(authctxt->krb5_ticket_file) + 6; + authctxt->krb5_ccname = xmalloc(len); ++#ifdef USE_CCAPI ++ snprintf(authctxt->krb5_ccname, len, "API:%s", ++ authctxt->krb5_ticket_file); ++#else + snprintf(authctxt->krb5_ccname, len, "FILE:%s", + authctxt->krb5_ticket_file); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -240,15 +245,22 @@ krb5_cleanup_proc(Authctxt *authctxt) + #ifndef HEIMDAL + krb5_error_code + ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { +- int tmpfd, ret, oerrno; ++ int ret, oerrno; + char ccname[40]; + mode_t old_umask; ++#ifdef USE_CCAPI ++ char cctemplate[] = "API:krb5cc_%d"; ++#else ++ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; ++ int tmpfd; ++#endif + + ret = snprintf(ccname, sizeof(ccname), +- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); ++ cctemplate, geteuid()); + if (ret < 0 || (size_t)ret >= sizeof(ccname)) + return ENOMEM; + ++#ifndef USE_CCAPI + old_umask = umask(0177); + tmpfd = mkstemp(ccname + strlen("FILE:")); + oerrno = errno; +@@ -265,6 +277,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { + return oerrno; + } + close(tmpfd); ++#endif + + return (krb5_cc_resolve(ctx, ccname, ccache)); + } +diff --git a/auth.c b/auth.c +index 6ee6116d..c6390687 100644 +--- a/auth.c ++++ b/auth.c +@@ -372,7 +372,8 @@ auth_root_allowed(const char *method) + case PERMIT_NO_PASSWD: + if (strcmp(method, "publickey") == 0 || + strcmp(method, "hostbased") == 0 || +- strcmp(method, "gssapi-with-mic") == 0) ++ strcmp(method, "gssapi-with-mic") == 0 || ++ strcmp(method, "gssapi-keyex") == 0) + return 1; + break; + case PERMIT_FORCED_ONLY: +@@ -795,99 +796,6 @@ fakepw(void) + } + + /* +- * Returns the remote DNS hostname as a string. The returned string must not +- * be freed. NB. this will usually trigger a DNS query the first time it is +- * called. +- * This function does additional checks on the hostname to mitigate some +- * attacks on legacy rhosts-style authentication. +- * XXX is RhostsRSAAuthentication vulnerable to these? +- * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) +- */ +- +-static char * +-remote_hostname(struct ssh *ssh) +-{ +- struct sockaddr_storage from; +- socklen_t fromlen; +- struct addrinfo hints, *ai, *aitop; +- char name[NI_MAXHOST], ntop2[NI_MAXHOST]; +- const char *ntop = ssh_remote_ipaddr(ssh); +- +- /* Get IP address of client. */ +- fromlen = sizeof(from); +- memset(&from, 0, sizeof(from)); +- if (getpeername(ssh_packet_get_connection_in(ssh), +- (struct sockaddr *)&from, &fromlen) < 0) { +- debug("getpeername failed: %.100s", strerror(errno)); +- return strdup(ntop); +- } +- +- ipv64_normalise_mapped(&from, &fromlen); +- if (from.ss_family == AF_INET6) +- fromlen = sizeof(struct sockaddr_in6); +- +- debug3("Trying to reverse map address %.100s.", ntop); +- /* Map the IP address to a host name. */ +- if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), +- NULL, 0, NI_NAMEREQD) != 0) { +- /* Host name not found. Use ip address. */ +- return strdup(ntop); +- } +- +- /* +- * if reverse lookup result looks like a numeric hostname, +- * someone is trying to trick us by PTR record like following: +- * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 +- */ +- memset(&hints, 0, sizeof(hints)); +- hints.ai_socktype = SOCK_DGRAM; /*dummy*/ +- hints.ai_flags = AI_NUMERICHOST; +- if (getaddrinfo(name, NULL, &hints, &ai) == 0) { +- logit("Nasty PTR record \"%s\" is set up for %s, ignoring", +- name, ntop); +- freeaddrinfo(ai); +- return strdup(ntop); +- } +- +- /* Names are stored in lowercase. */ +- lowercase(name); +- +- /* +- * Map it back to an IP address and check that the given +- * address actually is an address of this host. This is +- * necessary because anyone with access to a name server can +- * define arbitrary names for an IP address. Mapping from +- * name to IP address can be trusted better (but can still be +- * fooled if the intruder has access to the name server of +- * the domain). +- */ +- memset(&hints, 0, sizeof(hints)); +- hints.ai_family = from.ss_family; +- hints.ai_socktype = SOCK_STREAM; +- if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { +- logit("reverse mapping checking getaddrinfo for %.700s " +- "[%s] failed.", name, ntop); +- return strdup(ntop); +- } +- /* Look for the address from the list of addresses. */ +- for (ai = aitop; ai; ai = ai->ai_next) { +- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, +- sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && +- (strcmp(ntop, ntop2) == 0)) +- break; +- } +- freeaddrinfo(aitop); +- /* If we reached the end of the list, the address was not there. */ +- if (ai == NULL) { +- /* Address not found for the host name. */ +- logit("Address %.100s maps to %.600s, but this does not " +- "map back to the address.", ntop, name); +- return strdup(ntop); +- } +- return strdup(name); +-} +- +-/* + * Return the canonical name of the host in the other side of the current + * connection. The host name is cached, so it is efficient to call this + * several times. +diff --git a/auth2-gss.c b/auth2-gss.c +index 1ca83577..3b5036df 100644 +--- a/auth2-gss.c ++++ b/auth2-gss.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 markus Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -53,6 +53,40 @@ static int input_gssapi_mic(int type, u_int32_t plen, void *ctxt); + static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); + static int input_gssapi_errtok(int, u_int32_t, void *); + ++/* ++ * The 'gssapi_keyex' userauth mechanism. ++ */ ++static int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ int authenticated = 0; ++ Buffer b; ++ gss_buffer_desc mic, gssbuf; ++ u_int len; ++ ++ mic.value = packet_get_string(&len); ++ mic.length = len; ++ ++ packet_check_eom(); ++ ++ ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = buffer_ptr(&b); ++ gssbuf.length = buffer_len(&b); ++ ++ /* gss_kex_context is NULL with privsep, so we can't check it here */ ++ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, ++ &gssbuf, &mic)))) ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw)); ++ ++ buffer_free(&b); ++ free(mic.value); ++ ++ return (authenticated); ++} ++ + /* + * We only support those mechanisms that we know about (ie ones that we know + * how to check local user kuserok and the like) +@@ -238,7 +272,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) + + packet_check_eom(); + +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw)); + + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); +@@ -274,7 +309,8 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt) + gssbuf.length = buffer_len(&b); + + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = ++ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); + else + logit("GSSAPI MIC check failed"); + +@@ -290,6 +326,12 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt) + return 0; + } + ++Authmethod method_gsskeyex = { ++ "gssapi-keyex", ++ userauth_gsskeyex, ++ &options.gss_authentication ++}; ++ + Authmethod method_gssapi = { + "gssapi-with-mic", + userauth_gssapi, +diff --git a/auth2.c b/auth2.c +index 9108b861..ce0d3760 100644 +--- a/auth2.c ++++ b/auth2.c +@@ -70,6 +70,7 @@ extern Authmethod method_passwd; + extern Authmethod method_kbdint; + extern Authmethod method_hostbased; + #ifdef GSSAPI ++extern Authmethod method_gsskeyex; + extern Authmethod method_gssapi; + #endif + +@@ -77,6 +78,7 @@ Authmethod *authmethods[] = { + &method_none, + &method_pubkey, + #ifdef GSSAPI ++ &method_gsskeyex, + &method_gssapi, + #endif + &method_passwd, +diff --git a/canohost.c b/canohost.c +index f71a0856..404731d2 100644 +--- a/canohost.c ++++ b/canohost.c +@@ -35,6 +35,99 @@ + #include "canohost.h" + #include "misc.h" + ++/* ++ * Returns the remote DNS hostname as a string. The returned string must not ++ * be freed. NB. this will usually trigger a DNS query the first time it is ++ * called. ++ * This function does additional checks on the hostname to mitigate some ++ * attacks on legacy rhosts-style authentication. ++ * XXX is RhostsRSAAuthentication vulnerable to these? ++ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) ++ */ ++ ++char * ++remote_hostname(struct ssh *ssh) ++{ ++ struct sockaddr_storage from; ++ socklen_t fromlen; ++ struct addrinfo hints, *ai, *aitop; ++ char name[NI_MAXHOST], ntop2[NI_MAXHOST]; ++ const char *ntop = ssh_remote_ipaddr(ssh); ++ ++ /* Get IP address of client. */ ++ fromlen = sizeof(from); ++ memset(&from, 0, sizeof(from)); ++ if (getpeername(ssh_packet_get_connection_in(ssh), ++ (struct sockaddr *)&from, &fromlen) < 0) { ++ debug("getpeername failed: %.100s", strerror(errno)); ++ return strdup(ntop); ++ } ++ ++ ipv64_normalise_mapped(&from, &fromlen); ++ if (from.ss_family == AF_INET6) ++ fromlen = sizeof(struct sockaddr_in6); ++ ++ debug3("Trying to reverse map address %.100s.", ntop); ++ /* Map the IP address to a host name. */ ++ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), ++ NULL, 0, NI_NAMEREQD) != 0) { ++ /* Host name not found. Use ip address. */ ++ return strdup(ntop); ++ } ++ ++ /* ++ * if reverse lookup result looks like a numeric hostname, ++ * someone is trying to trick us by PTR record like following: ++ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 ++ */ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_socktype = SOCK_DGRAM; /*dummy*/ ++ hints.ai_flags = AI_NUMERICHOST; ++ if (getaddrinfo(name, NULL, &hints, &ai) == 0) { ++ logit("Nasty PTR record \"%s\" is set up for %s, ignoring", ++ name, ntop); ++ freeaddrinfo(ai); ++ return strdup(ntop); ++ } ++ ++ /* Names are stored in lowercase. */ ++ lowercase(name); ++ ++ /* ++ * Map it back to an IP address and check that the given ++ * address actually is an address of this host. This is ++ * necessary because anyone with access to a name server can ++ * define arbitrary names for an IP address. Mapping from ++ * name to IP address can be trusted better (but can still be ++ * fooled if the intruder has access to the name server of ++ * the domain). ++ */ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = from.ss_family; ++ hints.ai_socktype = SOCK_STREAM; ++ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { ++ logit("reverse mapping checking getaddrinfo for %.700s " ++ "[%s] failed.", name, ntop); ++ return strdup(ntop); ++ } ++ /* Look for the address from the list of addresses. */ ++ for (ai = aitop; ai; ai = ai->ai_next) { ++ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, ++ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && ++ (strcmp(ntop, ntop2) == 0)) ++ break; ++ } ++ freeaddrinfo(aitop); ++ /* If we reached the end of the list, the address was not there. */ ++ if (ai == NULL) { ++ /* Address not found for the host name. */ ++ logit("Address %.100s maps to %.600s, but this does not " ++ "map back to the address.", ntop, name); ++ return strdup(ntop); ++ } ++ return strdup(name); ++} ++ + void + ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len) + { +diff --git a/canohost.h b/canohost.h +index 26d62855..0cadc9f1 100644 +--- a/canohost.h ++++ b/canohost.h +@@ -15,6 +15,9 @@ + #ifndef _CANOHOST_H + #define _CANOHOST_H + ++struct ssh; ++ ++char *remote_hostname(struct ssh *); + char *get_peer_ipaddr(int); + int get_peer_port(int); + char *get_local_ipaddr(int); +diff --git a/clientloop.c b/clientloop.c +index 4289a408..99c68b69 100644 +--- a/clientloop.c ++++ b/clientloop.c +@@ -113,6 +113,10 @@ + #include "ssherr.h" + #include "hostfile.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* import options */ + extern Options options; + +@@ -1664,9 +1668,18 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) + break; + + /* Do channel operations unless rekeying in progress. */ +- if (!ssh_packet_is_rekeying(active_state)) ++ if (!ssh_packet_is_rekeying(active_state)) { + channel_after_select(readset, writeset); + ++#ifdef GSSAPI ++ if (options.gss_renewal_rekey && ++ ssh_gssapi_credentials_updated(NULL)) { ++ debug("credentials updated - forcing rekey"); ++ need_rekeying = 1; ++ } ++#endif ++ } ++ + /* Buffer input from the connection. */ + client_process_net_input(readset); + +diff --git a/config.h.in b/config.h.in +index 75e02ab4..afe540e9 100644 +--- a/config.h.in ++++ b/config.h.in +@@ -1667,6 +1667,9 @@ + /* Use btmp to log bad logins */ + #undef USE_BTMP + ++/* platform uses an in-memory credentials cache */ ++#undef USE_CCAPI ++ + /* Use libedit for sftp */ + #undef USE_LIBEDIT + +@@ -1682,6 +1685,9 @@ + /* Use PIPES instead of a socketpair() */ + #undef USE_PIPES + ++/* platform has the Security Authorization Session API */ ++#undef USE_SECURITY_SESSION_API ++ + /* Define if you have Solaris privileges */ + #undef USE_SOLARIS_PRIVS + +diff --git a/configure.ac b/configure.ac +index eb9f45dc..5fdc696c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -623,6 +623,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) + [Use tunnel device compatibility to OpenBSD]) + AC_DEFINE([SSH_TUN_PREPEND_AF], [1], + [Prepend the address family to IP tunnel traffic]) ++ AC_MSG_CHECKING([if we have the Security Authorization Session API]) ++ AC_TRY_COMPILE([#include <Security/AuthSession.h>], ++ [SessionCreate(0, 0);], ++ [ac_cv_use_security_session_api="yes" ++ AC_DEFINE([USE_SECURITY_SESSION_API], [1], ++ [platform has the Security Authorization Session API]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT([yes])], ++ [ac_cv_use_security_session_api="no" ++ AC_MSG_RESULT([no])]) ++ AC_MSG_CHECKING([if we have an in-memory credentials cache]) ++ AC_TRY_COMPILE( ++ [#include <Kerberos/Kerberos.h>], ++ [cc_context_t c; ++ (void) cc_initialize (&c, 0, NULL, NULL);], ++ [AC_DEFINE([USE_CCAPI], [1], ++ [platform uses an in-memory credentials cache]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT([yes]) ++ if test "x$ac_cv_use_security_session_api" = "xno"; then ++ AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***]) ++ fi], ++ [AC_MSG_RESULT([no])] ++ ) + m4_pattern_allow([AU_IPv]) + AC_CHECK_DECL([AU_IPv4], [], + AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) +diff --git a/gss-genr.c b/gss-genr.c +index 62559ed9..0b3ae073 100644 +--- a/gss-genr.c ++++ b/gss-genr.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-genr.c,v 1.24 2016/09/12 01:22:38 deraadt Exp $ */ + + /* +- * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -40,12 +40,167 @@ + #include "buffer.h" + #include "log.h" + #include "ssh2.h" ++#include "cipher.h" ++#include "key.h" ++#include "kex.h" ++#include <openssl/evp.h> + + #include "ssh-gss.h" + + extern u_char *session_id2; + extern u_int session_id2_len; + ++typedef struct { ++ char *encoded; ++ gss_OID oid; ++} ssh_gss_kex_mapping; ++ ++/* ++ * XXX - It would be nice to find a more elegant way of handling the ++ * XXX passing of the key exchange context to the userauth routines ++ */ ++ ++Gssctxt *gss_kex_context = NULL; ++ ++static ssh_gss_kex_mapping *gss_enc2oid = NULL; ++ ++int ++ssh_gssapi_oid_table_ok(void) { ++ return (gss_enc2oid != NULL); ++} ++ ++/* ++ * Return a list of the gss-group1-sha1 mechanisms supported by this program ++ * ++ * We test mechanisms to ensure that we can use them, to avoid starting ++ * a key exchange with a bad mechanism ++ */ ++ ++char * ++ssh_gssapi_client_mechanisms(const char *host, const char *client) { ++ gss_OID_set gss_supported; ++ OM_uint32 min_status; ++ ++ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) ++ return NULL; ++ ++ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, ++ host, client)); ++} ++ ++char * ++ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, ++ const char *host, const char *client) { ++ Buffer buf; ++ size_t i; ++ int oidpos, enclen; ++ char *mechs, *encoded; ++ u_char digest[EVP_MAX_MD_SIZE]; ++ char deroid[2]; ++ const EVP_MD *evp_md = EVP_md5(); ++ EVP_MD_CTX md; ++ ++ if (gss_enc2oid != NULL) { ++ for (i = 0; gss_enc2oid[i].encoded != NULL; i++) ++ free(gss_enc2oid[i].encoded); ++ free(gss_enc2oid); ++ } ++ ++ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * ++ (gss_supported->count + 1)); ++ ++ buffer_init(&buf); ++ ++ oidpos = 0; ++ for (i = 0; i < gss_supported->count; i++) { ++ if (gss_supported->elements[i].length < 128 && ++ (*check)(NULL, &(gss_supported->elements[i]), host, client)) { ++ ++ deroid[0] = SSH_GSS_OIDTYPE; ++ deroid[1] = gss_supported->elements[i].length; ++ ++ EVP_DigestInit(&md, evp_md); ++ EVP_DigestUpdate(&md, deroid, 2); ++ EVP_DigestUpdate(&md, ++ gss_supported->elements[i].elements, ++ gss_supported->elements[i].length); ++ EVP_DigestFinal(&md, digest, NULL); ++ ++ encoded = xmalloc(EVP_MD_size(evp_md) * 2); ++ enclen = __b64_ntop(digest, EVP_MD_size(evp_md), ++ encoded, EVP_MD_size(evp_md) * 2); ++ ++ if (oidpos != 0) ++ buffer_put_char(&buf, ','); ++ ++ buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, ++ sizeof(KEX_GSS_GEX_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, ++ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, ++ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ ++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); ++ gss_enc2oid[oidpos].encoded = encoded; ++ oidpos++; ++ } ++ } ++ gss_enc2oid[oidpos].oid = NULL; ++ gss_enc2oid[oidpos].encoded = NULL; ++ ++ buffer_put_char(&buf, '\0'); ++ ++ mechs = xmalloc(buffer_len(&buf)); ++ buffer_get(&buf, mechs, buffer_len(&buf)); ++ buffer_free(&buf); ++ ++ if (strlen(mechs) == 0) { ++ free(mechs); ++ mechs = NULL; ++ } ++ ++ return (mechs); ++} ++ ++gss_OID ++ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { ++ int i = 0; ++ ++ switch (kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GEX_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; ++ break; ++ default: ++ return GSS_C_NO_OID; ++ } ++ ++ while (gss_enc2oid[i].encoded != NULL && ++ strcmp(name, gss_enc2oid[i].encoded) != 0) ++ i++; ++ ++ if (gss_enc2oid[i].oid != NULL && ctx != NULL) ++ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); ++ ++ return gss_enc2oid[i].oid; ++} ++ + /* Check that the OID in a data stream matches that in the context */ + int + ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) +@@ -198,7 +353,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, + } + + ctx->major = gss_init_sec_context(&ctx->minor, +- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, ++ ctx->client_creds, &ctx->context, ctx->name, ctx->oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, + 0, NULL, recv_tok, NULL, send_tok, flags, NULL); + +@@ -228,8 +383,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host) + } + + OM_uint32 ++ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) ++{ ++ gss_buffer_desc gssbuf; ++ gss_name_t gssname; ++ OM_uint32 status; ++ gss_OID_set oidset; ++ ++ gssbuf.value = (void *) name; ++ gssbuf.length = strlen(gssbuf.value); ++ ++ gss_create_empty_oid_set(&status, &oidset); ++ gss_add_oid_set_member(&status, ctx->oid, &oidset); ++ ++ ctx->major = gss_import_name(&ctx->minor, &gssbuf, ++ GSS_C_NT_USER_NAME, &gssname); ++ ++ if (!ctx->major) ++ ctx->major = gss_acquire_cred(&ctx->minor, ++ gssname, 0, oidset, GSS_C_INITIATE, ++ &ctx->client_creds, NULL, NULL); ++ ++ gss_release_name(&status, &gssname); ++ gss_release_oid_set(&status, &oidset); ++ ++ if (ctx->major) ++ ssh_gssapi_error(ctx); ++ ++ return(ctx->major); ++} ++ ++OM_uint32 + ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) + { ++ if (ctx == NULL) ++ return -1; ++ + if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash))) + ssh_gssapi_error(ctx); +@@ -237,6 +426,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) + return (ctx->major); + } + ++/* Priviledged when used by server */ ++OM_uint32 ++ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++{ ++ if (ctx == NULL) ++ return -1; ++ ++ ctx->major = gss_verify_mic(&ctx->minor, ctx->context, ++ gssbuf, gssmic, NULL); ++ ++ return (ctx->major); ++} ++ + void + ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, + const char *context) +@@ -250,11 +452,16 @@ ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, + } + + int +-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) ++ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, ++ const char *client) + { + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; ++ Gssctxt *intctx = NULL; ++ ++ if (ctx == NULL) ++ ctx = &intctx; + + /* RFC 4462 says we MUST NOT do SPNEGO */ + if (oid->length == spnego_oid.length && +@@ -264,6 +471,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + major = ssh_gssapi_import_name(*ctx, host); ++ ++ if (!GSS_ERROR(major) && client) ++ major = ssh_gssapi_client_identity(*ctx, client); ++ + if (!GSS_ERROR(major)) { + major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + NULL); +@@ -273,10 +484,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) + GSS_C_NO_BUFFER); + } + +- if (GSS_ERROR(major)) ++ if (GSS_ERROR(major) || intctx != NULL) + ssh_gssapi_delete_ctx(ctx); + + return (!GSS_ERROR(major)); + } + ++int ++ssh_gssapi_credentials_updated(Gssctxt *ctxt) { ++ static gss_name_t saved_name = GSS_C_NO_NAME; ++ static OM_uint32 saved_lifetime = 0; ++ static gss_OID saved_mech = GSS_C_NO_OID; ++ static gss_name_t name; ++ static OM_uint32 last_call = 0; ++ OM_uint32 lifetime, now, major, minor; ++ int equal; ++ ++ now = time(NULL); ++ ++ if (ctxt) { ++ debug("Rekey has happened - updating saved versions"); ++ ++ if (saved_name != GSS_C_NO_NAME) ++ gss_release_name(&minor, &saved_name); ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &saved_name, &saved_lifetime, NULL, NULL); ++ ++ if (!GSS_ERROR(major)) { ++ saved_mech = ctxt->oid; ++ saved_lifetime+= now; ++ } else { ++ /* Handle the error */ ++ } ++ return 0; ++ } ++ ++ if (now - last_call < 10) ++ return 0; ++ ++ last_call = now; ++ ++ if (saved_mech == GSS_C_NO_OID) ++ return 0; ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &name, &lifetime, NULL, NULL); ++ if (major == GSS_S_CREDENTIALS_EXPIRED) ++ return 0; ++ else if (GSS_ERROR(major)) ++ return 0; ++ ++ major = gss_compare_name(&minor, saved_name, name, &equal); ++ gss_release_name(&minor, &name); ++ if (GSS_ERROR(major)) ++ return 0; ++ ++ if (equal && (saved_lifetime < lifetime + now - 10)) ++ return 1; ++ ++ return 0; ++} ++ + #endif /* GSSAPI */ +diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c +index 795992d9..fd8b3718 100644 +--- a/gss-serv-krb5.c ++++ b/gss-serv-krb5.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-serv-krb5.c,v 1.8 2013/07/20 01:55:13 djm Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -121,8 +121,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + krb5_error_code problem; + krb5_principal princ; + OM_uint32 maj_status, min_status; +- int len; + const char *errmsg; ++ const char *new_ccname; + + if (client->creds == NULL) { + debug("No credentials stored"); +@@ -181,11 +181,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + return; + } + +- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); ++ new_ccname = krb5_cc_get_name(krb_context, ccache); ++ + client->store.envvar = "KRB5CCNAME"; +- len = strlen(client->store.filename) + 6; +- client->store.envval = xmalloc(len); +- snprintf(client->store.envval, len, "FILE:%s", client->store.filename); ++#ifdef USE_CCAPI ++ xasprintf(&client->store.envval, "API:%s", new_ccname); ++ client->store.filename = NULL; ++#else ++ xasprintf(&client->store.envval, "FILE:%s", new_ccname); ++ client->store.filename = xstrdup(new_ccname); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -197,6 +202,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + return; + } + ++int ++ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, ++ ssh_gssapi_client *client) ++{ ++ krb5_ccache ccache = NULL; ++ krb5_principal principal = NULL; ++ char *name = NULL; ++ krb5_error_code problem; ++ OM_uint32 maj_status, min_status; ++ ++ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { ++ logit("krb5_cc_resolve(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ return 0; ++ } ++ ++ /* Find out who the principal in this cache is */ ++ if ((problem = krb5_cc_get_principal(krb_context, ccache, ++ &principal))) { ++ logit("krb5_cc_get_principal(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ if ((problem = krb5_unparse_name(krb_context, principal, &name))) { ++ logit("krb5_unparse_name(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ ++ if (strcmp(name,client->exportedname.value)!=0) { ++ debug("Name in local credentials cache differs. Not storing"); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ krb5_free_unparsed_name(krb_context, name); ++ return 0; ++ } ++ krb5_free_unparsed_name(krb_context, name); ++ ++ /* Name matches, so lets get on with it! */ ++ ++ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { ++ logit("krb5_cc_initialize(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ krb5_free_principal(krb_context, principal); ++ ++ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, ++ ccache))) { ++ logit("gss_krb5_copy_ccache() failed. Sorry!"); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ return 1; ++} ++ + ssh_gssapi_mech gssapi_kerberos_mech = { + "toWM5Slw5Ew8Mqkay+al2g==", + "Kerberos", +@@ -204,7 +274,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = { + NULL, + &ssh_gssapi_krb5_userok, + NULL, +- &ssh_gssapi_krb5_storecreds ++ &ssh_gssapi_krb5_storecreds, ++ &ssh_gssapi_krb5_updatecreds + }; + + #endif /* KRB5 */ +diff --git a/gss-serv.c b/gss-serv.c +index 53993d67..2e27cbf9 100644 +--- a/gss-serv.c ++++ b/gss-serv.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -45,17 +45,22 @@ + #include "session.h" + #include "misc.h" + #include "servconf.h" ++#include "uidswap.h" + + #include "ssh-gss.h" ++#include "monitor_wrap.h" ++ ++extern ServerOptions options; + + extern ServerOptions options; + + static ssh_gssapi_client gssapi_client = + { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, +- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; ++ GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, ++ {NULL, NULL, NULL, NULL, NULL}, 0, 0}; + + ssh_gssapi_mech gssapi_null_mech = +- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; ++ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; + + #ifdef KRB5 + extern ssh_gssapi_mech gssapi_kerberos_mech; +@@ -142,6 +147,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) + } + + /* Unprivileged */ ++char * ++ssh_gssapi_server_mechanisms(void) { ++ if (supported_oids == NULL) ++ ssh_gssapi_prepare_supported_oids(); ++ return (ssh_gssapi_kex_mechs(supported_oids, ++ &ssh_gssapi_server_check_mech, NULL, NULL)); ++} ++ ++/* Unprivileged */ ++int ++ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, ++ const char *dummy) { ++ Gssctxt *ctx = NULL; ++ int res; ++ ++ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); ++ ssh_gssapi_delete_ctx(&ctx); ++ ++ return (res); ++} ++ ++/* Unprivileged */ + void + ssh_gssapi_supported_oids(gss_OID_set *oidset) + { +@@ -151,7 +178,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset) + gss_OID_set supported; + + gss_create_empty_oid_set(&min_status, oidset); +- gss_indicate_mechs(&min_status, &supported); ++ ++ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) ++ return; + + while (supported_mechs[i]->name != NULL) { + if (GSS_ERROR(gss_test_oid_set_member(&min_status, +@@ -277,8 +306,48 @@ OM_uint32 + ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + { + int i = 0; ++ int equal = 0; ++ gss_name_t new_name = GSS_C_NO_NAME; ++ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; ++ ++ if (options.gss_store_rekey && client->used && ctx->client_creds) { ++ if (client->mech->oid.length != ctx->oid->length || ++ (memcmp(client->mech->oid.elements, ++ ctx->oid->elements, ctx->oid->length) !=0)) { ++ debug("Rekeyed credentials have different mechanism"); ++ return GSS_S_COMPLETE; ++ } ++ ++ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ++ ctx->client_creds, ctx->oid, &new_name, ++ NULL, NULL, NULL))) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ ++ ctx->major = gss_compare_name(&ctx->minor, client->name, ++ new_name, &equal); ++ ++ if (GSS_ERROR(ctx->major)) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ ++ if (!equal) { ++ debug("Rekeyed credentials have different name"); ++ return GSS_S_COMPLETE; ++ } + +- gss_buffer_desc ename; ++ debug("Marking rekeyed credentials for export"); ++ ++ gss_release_name(&ctx->minor, &client->name); ++ gss_release_cred(&ctx->minor, &client->creds); ++ client->name = new_name; ++ client->creds = ctx->client_creds; ++ ctx->client_creds = GSS_C_NO_CREDENTIAL; ++ client->updated = 1; ++ return GSS_S_COMPLETE; ++ } + + client->mech = NULL; + +@@ -293,6 +362,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + if (client->mech == NULL) + return GSS_S_FAILURE; + ++ if (ctx->client_creds && ++ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ++ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ + if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, + &client->displayname, NULL))) { + ssh_gssapi_error(ctx); +@@ -310,6 +386,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + return (ctx->major); + } + ++ gss_release_buffer(&ctx->minor, &ename); ++ + /* We can't copy this structure, so we just move the pointer to it */ + client->creds = ctx->client_creds; + ctx->client_creds = GSS_C_NO_CREDENTIAL; +@@ -357,7 +435,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep) + + /* Privileged */ + int +-ssh_gssapi_userok(char *user) ++ssh_gssapi_userok(char *user, struct passwd *pw) + { + OM_uint32 lmin; + +@@ -367,9 +445,11 @@ ssh_gssapi_userok(char *user) + return 0; + } + if (gssapi_client.mech && gssapi_client.mech->userok) +- if ((*gssapi_client.mech->userok)(&gssapi_client, user)) ++ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { ++ gssapi_client.used = 1; ++ gssapi_client.store.owner = pw; + return 1; +- else { ++ } else { + /* Destroy delegated credentials if userok fails */ + gss_release_buffer(&lmin, &gssapi_client.displayname); + gss_release_buffer(&lmin, &gssapi_client.exportedname); +@@ -383,14 +463,90 @@ ssh_gssapi_userok(char *user) + return (0); + } + +-/* Privileged */ +-OM_uint32 +-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++/* These bits are only used for rekeying. The unpriviledged child is running ++ * as the user, the monitor is root. ++ * ++ * In the child, we want to : ++ * *) Ask the monitor to store our credentials into the store we specify ++ * *) If it succeeds, maybe do a PAM update ++ */ ++ ++/* Stuff for PAM */ ++ ++#ifdef USE_PAM ++static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, ++ struct pam_response **resp, void *data) + { +- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, +- gssbuf, gssmic, NULL); ++ return (PAM_CONV_ERR); ++} ++#endif + +- return (ctx->major); ++void ++ssh_gssapi_rekey_creds(void) { ++ int ok; ++ int ret; ++#ifdef USE_PAM ++ pam_handle_t *pamh = NULL; ++ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; ++ char *envstr; ++#endif ++ ++ if (gssapi_client.store.filename == NULL && ++ gssapi_client.store.envval == NULL && ++ gssapi_client.store.envvar == NULL) ++ return; ++ ++ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); ++ ++ if (!ok) ++ return; ++ ++ debug("Rekeyed credentials stored successfully"); ++ ++ /* Actually managing to play with the ssh pam stack from here will ++ * be next to impossible. In any case, we may want different options ++ * for rekeying. So, use our own :) ++ */ ++#ifdef USE_PAM ++ if (!use_privsep) { ++ debug("Not even going to try and do PAM with privsep disabled"); ++ return; ++ } ++ ++ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, ++ &pamconv, &pamh); ++ if (ret) ++ return; ++ ++ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, ++ gssapi_client.store.envval); ++ ++ ret = pam_putenv(pamh, envstr); ++ if (!ret) ++ pam_setcred(pamh, PAM_REINITIALIZE_CRED); ++ pam_end(pamh, PAM_SUCCESS); ++#endif ++} ++ ++int ++ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { ++ int ok = 0; ++ ++ /* Check we've got credentials to store */ ++ if (!gssapi_client.updated) ++ return 0; ++ ++ gssapi_client.updated = 0; ++ ++ temporarily_use_uid(gssapi_client.store.owner); ++ if (gssapi_client.mech && gssapi_client.mech->updatecreds) ++ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); ++ else ++ debug("No update function for this mechanism"); ++ ++ restore_uid(); ++ ++ return ok; + } + + #endif +diff --git a/kex.c b/kex.c +index 6a94bc53..d8708684 100644 +--- a/kex.c ++++ b/kex.c +@@ -54,6 +54,10 @@ + #include "sshbuf.h" + #include "digest.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + #if OPENSSL_VERSION_NUMBER >= 0x00907000L + # if defined(HAVE_EVP_SHA256) + # define evp_ssh_sha256 EVP_sha256 +@@ -113,6 +117,14 @@ static const struct kexalg kexalgs[] = { + #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ + { NULL, -1, -1, -1}, + }; ++static const struct kexalg kexalg_prefixes[] = { ++#ifdef GSSAPI ++ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, ++#endif ++ { NULL, -1, -1, -1 }, ++}; + + char * + kex_alg_list(char sep) +@@ -145,6 +157,10 @@ kex_alg_by_name(const char *name) + if (strcmp(k->name, name) == 0) + return k; + } ++ for (k = kexalg_prefixes; k->name != NULL; k++) { ++ if (strncmp(k->name, name, strlen(k->name)) == 0) ++ return k; ++ } + return NULL; + } + +@@ -597,6 +613,9 @@ kex_free(struct kex *kex) + sshbuf_free(kex->peer); + sshbuf_free(kex->my); + free(kex->session_id); ++#ifdef GSSAPI ++ free(kex->gss_host); ++#endif /* GSSAPI */ + free(kex->client_version_string); + free(kex->server_version_string); + free(kex->failed_choice); +diff --git a/kex.h b/kex.h +index 3794f212..fd56171d 100644 +--- a/kex.h ++++ b/kex.h +@@ -99,6 +99,9 @@ enum kex_exchange { + KEX_DH_GEX_SHA256, + KEX_ECDH_SHA2, + KEX_C25519_SHA256, ++ KEX_GSS_GRP1_SHA1, ++ KEX_GSS_GRP14_SHA1, ++ KEX_GSS_GEX_SHA1, + KEX_MAX + }; + +@@ -147,6 +150,12 @@ struct kex { + u_int flags; + int hash_alg; + int ec_nid; ++#ifdef GSSAPI ++ int gss_deleg_creds; ++ int gss_trust_dns; ++ char *gss_host; ++ char *gss_client; ++#endif + char *client_version_string; + char *server_version_string; + char *failed_choice; +@@ -197,6 +206,11 @@ int kexecdh_server(struct ssh *); + int kexc25519_client(struct ssh *); + int kexc25519_server(struct ssh *); + ++#ifdef GSSAPI ++int kexgss_client(struct ssh *); ++int kexgss_server(struct ssh *); ++#endif ++ + int kex_dh_hash(int, const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); +diff --git a/kexgssc.c b/kexgssc.c +new file mode 100644 +index 00000000..10447f2b +--- /dev/null ++++ b/kexgssc.c +@@ -0,0 +1,338 @@ ++/* ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#ifdef GSSAPI ++ ++#include "includes.h" ++ ++#include <openssl/crypto.h> ++#include <openssl/bn.h> ++ ++#include <string.h> ++ ++#include "xmalloc.h" ++#include "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "digest.h" ++ ++#include "ssh-gss.h" ++ ++int ++kexgss_client(struct ssh *ssh) { ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; ++ Gssctxt *ctxt; ++ OM_uint32 maj_status, min_status, ret_flags; ++ u_int klen, kout, slen = 0, strlen; ++ DH *dh; ++ BIGNUM *dh_server_pub = NULL; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *p = NULL; ++ BIGNUM *g = NULL; ++ u_char *kbuf; ++ u_char *serverhostkey = NULL; ++ u_char *empty = ""; ++ char *msg; ++ int type = 0; ++ int first = 1; ++ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (ssh->kex->gss_client && ++ ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange\n"); ++ nbits = dh_estimate(ssh->kex->we_need * 8); ++ packet_start(SSH2_MSG_KEXGSS_GROUPREQ); ++ packet_put_int(min); ++ packet_put_int(nbits); ++ packet_put_int(max); ++ ++ packet_send(); ++ ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUP); ++ ++ if ((p = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(p); ++ if ((g = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(g); ++ packet_check_eom(); ++ ++ if (BN_num_bits(p) < min || BN_num_bits(p) > max) ++ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", ++ min, BN_num_bits(p), max); ++ ++ dh = dh_new_group(g, p); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ /* Step 1 - e is dh->pub_key */ ++ dh_gen_key(dh, ssh->kex->we_need * 8); ++ ++ /* This is f, we initialise it now to make life easier */ ++ dh_server_pub = BN_new(); ++ if (dh_server_pub == NULL) ++ fatal("dh_server_pub == NULL"); ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ ssh->kex->gss_deleg_creds, token_ptr, &send_tok, ++ &ret_flags); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length != 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ fatal("gss_init_context failed"); ++ } ++ ++ /* If we've got an old receive buffer get rid of it */ ++ if (token_ptr != GSS_C_NO_BUFFER) ++ free(recv_tok.value); ++ ++ if (maj_status == GSS_S_COMPLETE) { ++ /* If mutual state flag is not true, kex fails */ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual authentication failed"); ++ ++ /* If integ avail flag is not true kex fails */ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity check failed"); ++ } ++ ++ /* ++ * If we have data to send, then the last message that we ++ * received cannot have been a 'complete'. ++ */ ++ if (send_tok.length != 0) { ++ if (first) { ++ packet_start(SSH2_MSG_KEXGSS_INIT); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ packet_put_bignum2(dh->pub_key); ++ first = 0; ++ } else { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = packet_read(); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ debug("Received KEXGSS_HOSTKEY"); ++ if (serverhostkey) ++ fatal("Server host key received more than once"); ++ serverhostkey = ++ packet_get_string(&slen); ++ } ++ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); ++ ++ switch (type) { ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ debug("Received GSSAPI_CONTINUE"); ++ if (maj_status == GSS_S_COMPLETE) ++ fatal("GSSAPI Continue received from server when complete"); ++ recv_tok.value = packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ packet_get_bignum2(dh_server_pub); ++ msg_tok.value = packet_get_string(&strlen); ++ msg_tok.length = strlen; ++ ++ /* Is there a token included? */ ++ if (packet_get_char()) { ++ recv_tok.value= ++ packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: did not receive final token"); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ maj_status = packet_get_int(); ++ min_status = packet_get_int(); ++ msg = packet_get_string(NULL); ++ (void) packet_get_string_ptr(NULL); ++ fatal("GSSAPI Error: \n%.400s",msg); ++ default: ++ packet_disconnect("Protocol error: didn't expect packet type %d", ++ type); ++ } ++ token_ptr = &recv_tok; ++ } else { ++ /* No data, and not complete */ ++ if (maj_status != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ /* ++ * We _must_ have received a COMPLETE message in reply from the ++ * server, which will have set dh_server_pub and msg_tok ++ */ ++ ++ if (type != SSH2_MSG_KEXGSS_COMPLETE) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ ++ /* Check f in range [1, p-1] */ ++ if (!dh_pub_is_valid(dh, dh_server_pub)) ++ packet_disconnect("bad server public DH value"); ++ ++ /* compute K=f^x mod p */ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_server_pub, dh); ++ if (kout < 0) ++ fatal("DH_compute_key: failed"); ++ ++ shared_secret = BN_new(); ++ if (shared_secret == NULL) ++ fatal("kexgss_client: BN_new failed"); ++ ++ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) ++ fatal("kexdh_client: BN_bin2bn failed"); ++ ++ memset(kbuf, 0, klen); ++ free(kbuf); ++ ++ hashlen = sizeof(hash); ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ++ ssh->kex->server_version_string, ++ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), ++ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ dh->pub_key, /* e */ ++ dh_server_pub, /* f */ ++ shared_secret, /* K */ ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ++ ssh->kex->server_version_string, ++ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), ++ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ min, nbits, max, ++ dh->p, dh->g, ++ dh->pub_key, ++ dh_server_pub, ++ shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ /* Verify that the hash matches the MIC we just got. */ ++ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ packet_disconnect("Hash's MIC didn't verify"); ++ ++ free(msg_tok.value); ++ ++ DH_free(dh); ++ free(serverhostkey); ++ BN_clear_free(dh_server_pub); ++ ++ /* save session id */ ++ if (ssh->kex->session_id == NULL) { ++ ssh->kex->session_id_len = hashlen; ++ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); ++ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); ++ } ++ ++ if (ssh->kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ return kex_send_newkeys(ssh); ++} ++ ++#endif /* GSSAPI */ +diff --git a/kexgsss.c b/kexgsss.c +new file mode 100644 +index 00000000..38ca082b +--- /dev/null ++++ b/kexgsss.c +@@ -0,0 +1,295 @@ ++/* ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#ifdef GSSAPI ++ ++#include <string.h> ++ ++#include <openssl/crypto.h> ++#include <openssl/bn.h> ++ ++#include "xmalloc.h" ++#include "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "misc.h" ++#include "servconf.h" ++#include "digest.h" ++ ++extern ServerOptions options; ++ ++int ++kexgss_server(struct ssh *ssh) ++{ ++ OM_uint32 maj_status, min_status; ++ ++ /* ++ * Some GSSAPI implementations use the input value of ret_flags (an ++ * output variable) as a means of triggering mechanism specific ++ * features. Initializing it to zero avoids inadvertently ++ * activating this non-standard behaviour. ++ */ ++ ++ OM_uint32 ret_flags = 0; ++ gss_buffer_desc gssbuf, recv_tok, msg_tok; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ Gssctxt *ctxt = NULL; ++ u_int slen, klen, kout; ++ u_char *kbuf; ++ DH *dh; ++ int min = -1, max = -1, nbits = -1; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *dh_client_pub = NULL; ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ ++ /* Initialise GSSAPI */ ++ ++ /* If we're rekeying, privsep means that some of the private structures ++ * in the GSSAPI code are no longer available. This kludges them back ++ * into life ++ */ ++ if (!ssh_gssapi_oid_table_ok()) { ++ mechs = ssh_gssapi_server_mechanisms(); ++ free(mechs); ++ } ++ ++ debug2("%s: Identifying %s", __func__, ssh->kex->name); ++ oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type); ++ if (oid == GSS_C_NO_OID) ++ fatal("Unknown gssapi mechanism"); ++ ++ debug2("%s: Acquiring credentials", __func__); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ fatal("Unable to acquire credentials for the server"); ++ ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange"); ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); ++ min = packet_get_int(); ++ nbits = packet_get_int(); ++ max = packet_get_int(); ++ packet_check_eom(); ++ if (max < min || nbits < min || max < nbits) ++ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", ++ min, nbits, max); ++ dh = PRIVSEP(choose_dh(MAX(DH_GRP_MIN, min), ++ nbits, MIN(DH_GRP_MAX, max))); ++ if (dh == NULL) ++ packet_disconnect("Protocol error: no matching group found"); ++ ++ packet_start(SSH2_MSG_KEXGSS_GROUP); ++ packet_put_bignum2(dh->p); ++ packet_put_bignum2(dh->g); ++ packet_send(); ++ ++ packet_write_wait(); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ dh_gen_key(dh, ssh->kex->we_need * 8); ++ ++ do { ++ debug("Wait SSH2_MSG_GSSAPI_INIT"); ++ type = packet_read(); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (dh_client_pub != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ ++ if ((dh_client_pub = BN_new()) == NULL) ++ fatal("dh_client_pub == NULL"); ++ ++ packet_get_bignum2(dh_client_pub); ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ break; ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ break; ++ default: ++ packet_disconnect( ++ "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ ++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags)); ++ ++ free(recv_tok.value); ++ ++ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) ++ fatal("Zero length token output when incomplete"); ++ ++ if (dh_client_pub == NULL) ++ fatal("No client public key"); ++ ++ if (maj_status & GSS_S_CONTINUE_NEEDED) { ++ debug("Sending GSSAPI_CONTINUE"); ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length > 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ } ++ fatal("accept_ctx died"); ++ } ++ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual Authentication flag wasn't set"); ++ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity flag wasn't set"); ++ ++ if (!dh_pub_is_valid(dh, dh_client_pub)) ++ packet_disconnect("bad client public DH value"); ++ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_client_pub, dh); ++ if (kout < 0) ++ fatal("DH_compute_key: failed"); ++ ++ shared_secret = BN_new(); ++ if (shared_secret == NULL) ++ fatal("kexgss_server: BN_new failed"); ++ ++ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) ++ fatal("kexgss_server: BN_bin2bn failed"); ++ ++ memset(kbuf, 0, klen); ++ free(kbuf); ++ ++ hashlen = sizeof(hash); ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ssh->kex->server_version_string, ++ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), ++ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), ++ NULL, 0, /* Change this if we start sending host keys */ ++ dh_client_pub, dh->pub_key, shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ssh->kex->server_version_string, ++ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), ++ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), ++ NULL, 0, ++ min, nbits, max, ++ dh->p, dh->g, ++ dh_client_pub, ++ dh->pub_key, ++ shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ BN_clear_free(dh_client_pub); ++ ++ if (ssh->kex->session_id == NULL) { ++ ssh->kex->session_id_len = hashlen; ++ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); ++ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) ++ fatal("Couldn't get MIC"); ++ ++ packet_start(SSH2_MSG_KEXGSS_COMPLETE); ++ packet_put_bignum2(dh->pub_key); ++ packet_put_string(msg_tok.value,msg_tok.length); ++ ++ if (send_tok.length != 0) { ++ packet_put_char(1); /* true */ ++ packet_put_string(send_tok.value, send_tok.length); ++ } else { ++ packet_put_char(0); /* false */ ++ } ++ packet_send(); ++ ++ gss_release_buffer(&min_status, &send_tok); ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ DH_free(dh); ++ ++ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ kex_send_newkeys(ssh); ++ ++ /* If this was a rekey, then save out any delegated credentials we ++ * just exchanged. */ ++ if (options.gss_store_rekey) ++ ssh_gssapi_rekey_creds(); ++ return 0; ++} ++#endif /* GSSAPI */ +diff --git a/monitor.c b/monitor.c +index 43f48470..76d9e346 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -157,6 +157,8 @@ int mm_answer_gss_setup_ctx(int, Buffer *); + int mm_answer_gss_accept_ctx(int, Buffer *); + int mm_answer_gss_userok(int, Buffer *); + int mm_answer_gss_checkmic(int, Buffer *); ++int mm_answer_gss_sign(int, Buffer *); ++int mm_answer_gss_updatecreds(int, Buffer *); + #endif + + #ifdef SSH_AUDIT_EVENTS +@@ -230,11 +232,18 @@ struct mon_table mon_dispatch_proto20[] = { + {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, + {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, + {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, ++ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, + #endif + {0, 0, NULL} + }; + + struct mon_table mon_dispatch_postauth20[] = { ++#ifdef GSSAPI ++ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, ++ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, ++ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, ++ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds}, ++#endif + #ifdef WITH_OPENSSL + {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, + #endif +@@ -301,6 +310,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) + /* Permit requests for moduli and signatures */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + + /* The first few requests do not require asynchronous access */ + while (!authenticated) { +@@ -400,6 +413,10 @@ monitor_child_postauth(struct monitor *pmonitor) + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + + if (!no_pty_flag) { + monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); +@@ -1601,6 +1618,13 @@ monitor_apply_keystate(struct monitor *pmonitor) + # endif + #endif /* WITH_OPENSSL */ + kex->kex[KEX_C25519_SHA256] = kexc25519_server; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++ } ++#endif + kex->load_host_public_key=&get_hostkey_public_by_type; + kex->load_host_private_key=&get_hostkey_private_by_type; + kex->host_key_index=&get_hostkey_index; +@@ -1680,8 +1704,8 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m) + OM_uint32 major; + u_int len; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + goid.elements = buffer_get_string(m, &len); + goid.length = len; +@@ -1710,8 +1734,8 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m) + OM_uint32 flags = 0; /* GSI needs this */ + u_int len; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + in.value = buffer_get_string(m, &len); + in.length = len; +@@ -1730,6 +1754,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m) + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); + } + return (0); + } +@@ -1741,8 +1766,8 @@ mm_answer_gss_checkmic(int sock, Buffer *m) + OM_uint32 ret; + u_int len; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + gssbuf.value = buffer_get_string(m, &len); + gssbuf.length = len; +@@ -1770,10 +1795,11 @@ mm_answer_gss_userok(int sock, Buffer *m) + { + int authenticated; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + +- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); ++ authenticated = authctxt->valid && ++ ssh_gssapi_userok(authctxt->user, authctxt->pw); + + buffer_clear(m); + buffer_put_int(m, authenticated); +@@ -1786,5 +1812,76 @@ mm_answer_gss_userok(int sock, Buffer *m) + /* Monitor loop will terminate if authenticated */ + return (authenticated); + } ++ ++int ++mm_answer_gss_sign(int socket, Buffer *m) ++{ ++ gss_buffer_desc data; ++ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; ++ OM_uint32 major, minor; ++ u_int len; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); ++ ++ data.value = buffer_get_string(m, &len); ++ data.length = len; ++ if (data.length != 20) ++ fatal("%s: data length incorrect: %d", __func__, ++ (int) data.length); ++ ++ /* Save the session ID on the first time around */ ++ if (session_id2_len == 0) { ++ session_id2_len = data.length; ++ session_id2 = xmalloc(session_id2_len); ++ memcpy(session_id2, data.value, session_id2_len); ++ } ++ major = ssh_gssapi_sign(gsscontext, &data, &hash); ++ ++ free(data.value); ++ ++ buffer_clear(m); ++ buffer_put_int(m, major); ++ buffer_put_string(m, hash.value, hash.length); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); ++ ++ gss_release_buffer(&minor, &hash); ++ ++ /* Turn on getpwnam permissions */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); ++ ++ /* And credential updating, for when rekeying */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1); ++ ++ return (0); ++} ++ ++int ++mm_answer_gss_updatecreds(int socket, Buffer *m) { ++ ssh_gssapi_ccache store; ++ int ok; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); ++ ++ store.filename = buffer_get_string(m, NULL); ++ store.envvar = buffer_get_string(m, NULL); ++ store.envval = buffer_get_string(m, NULL); ++ ++ ok = ssh_gssapi_update_creds(&store); ++ ++ free(store.filename); ++ free(store.envvar); ++ free(store.envval); ++ ++ buffer_clear(m); ++ buffer_put_int(m, ok); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); ++ ++ return(0); ++} ++ + #endif /* GSSAPI */ + +diff --git a/monitor.h b/monitor.h +index d68f6745..ec41404c 100644 +--- a/monitor.h ++++ b/monitor.h +@@ -65,6 +65,9 @@ enum monitor_reqtype { + MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, + MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, + ++ MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151, ++ MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153, ++ + }; + + struct monitor { +diff --git a/monitor_wrap.c b/monitor_wrap.c +index 64ff9288..d5cb640a 100644 +--- a/monitor_wrap.c ++++ b/monitor_wrap.c +@@ -924,7 +924,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) + } + + int +-mm_ssh_gssapi_userok(char *user) ++mm_ssh_gssapi_userok(char *user, struct passwd *pw) + { + Buffer m; + int authenticated = 0; +@@ -941,5 +941,50 @@ mm_ssh_gssapi_userok(char *user) + debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); + return (authenticated); + } ++ ++OM_uint32 ++mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) ++{ ++ Buffer m; ++ OM_uint32 major; ++ u_int len; ++ ++ buffer_init(&m); ++ buffer_put_string(&m, data->value, data->length); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); ++ ++ major = buffer_get_int(&m); ++ hash->value = buffer_get_string(&m, &len); ++ hash->length = len; ++ ++ buffer_free(&m); ++ ++ return(major); ++} ++ ++int ++mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) ++{ ++ Buffer m; ++ int ok; ++ ++ buffer_init(&m); ++ ++ buffer_put_cstring(&m, store->filename ? store->filename : ""); ++ buffer_put_cstring(&m, store->envvar ? store->envvar : ""); ++ buffer_put_cstring(&m, store->envval ? store->envval : ""); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m); ++ ++ ok = buffer_get_int(&m); ++ ++ buffer_free(&m); ++ ++ return (ok); ++} ++ + #endif /* GSSAPI */ + +diff --git a/monitor_wrap.h b/monitor_wrap.h +index db5902f5..8f9dd896 100644 +--- a/monitor_wrap.h ++++ b/monitor_wrap.h +@@ -55,8 +55,10 @@ int mm_key_verify(Key *, u_char *, u_int, u_char *, u_int); + OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); + OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, + gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); +-int mm_ssh_gssapi_userok(char *user); ++int mm_ssh_gssapi_userok(char *user, struct passwd *); + OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); ++OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); ++int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *); + #endif + + #ifdef USE_PAM +diff --git a/readconf.c b/readconf.c +index fa3fab8f..7902ef26 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -160,6 +160,8 @@ typedef enum { + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, ++ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, ++ oGssServerIdentity, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, +@@ -205,10 +207,19 @@ static struct { + { "afstokenpassing", oUnsupported }, + #if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, ++ { "gssapikeyexchange", oGssKeyEx }, + { "gssapidelegatecredentials", oGssDelegateCreds }, ++ { "gssapitrustdns", oGssTrustDns }, ++ { "gssapiclientidentity", oGssClientIdentity }, ++ { "gssapiserveridentity", oGssServerIdentity }, ++ { "gssapirenewalforcesrekey", oGssRenewalRekey }, + #else + { "gssapiauthentication", oUnsupported }, ++ { "gssapikeyexchange", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, ++ { "gssapitrustdns", oUnsupported }, ++ { "gssapiclientidentity", oUnsupported }, ++ { "gssapirenewalforcesrekey", oUnsupported }, + #endif + { "fallbacktorsh", oDeprecated }, + { "usersh", oDeprecated }, +@@ -961,10 +972,30 @@ parse_time: + intptr = &options->gss_authentication; + goto parse_flag; + ++ case oGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case oGssDelegateCreds: + intptr = &options->gss_deleg_creds; + goto parse_flag; + ++ case oGssTrustDns: ++ intptr = &options->gss_trust_dns; ++ goto parse_flag; ++ ++ case oGssClientIdentity: ++ charptr = &options->gss_client_identity; ++ goto parse_string; ++ ++ case oGssServerIdentity: ++ charptr = &options->gss_server_identity; ++ goto parse_string; ++ ++ case oGssRenewalRekey: ++ intptr = &options->gss_renewal_rekey; ++ goto parse_flag; ++ + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; +@@ -1776,7 +1807,12 @@ initialize_options(Options * options) + options->pubkey_authentication = -1; + options->challenge_response_authentication = -1; + options->gss_authentication = -1; ++ options->gss_keyex = -1; + options->gss_deleg_creds = -1; ++ options->gss_trust_dns = -1; ++ options->gss_renewal_rekey = -1; ++ options->gss_client_identity = NULL; ++ options->gss_server_identity = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; +@@ -1920,8 +1956,14 @@ fill_default_options(Options * options) + options->challenge_response_authentication = 1; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_deleg_creds == -1) + options->gss_deleg_creds = 0; ++ if (options->gss_trust_dns == -1) ++ options->gss_trust_dns = 0; ++ if (options->gss_renewal_rekey == -1) ++ options->gss_renewal_rekey = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +diff --git a/readconf.h b/readconf.h +index cef55f71..fd3d7c75 100644 +--- a/readconf.h ++++ b/readconf.h +@@ -45,7 +45,12 @@ typedef struct { + int challenge_response_authentication; + /* Try S/Key or TIS, authentication. */ + int gss_authentication; /* Try GSS authentication */ ++ int gss_keyex; /* Try GSS key exchange */ + int gss_deleg_creds; /* Delegate GSS credentials */ ++ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ ++ int gss_renewal_rekey; /* Credential renewal forces rekey */ ++ char *gss_client_identity; /* Principal to initiate GSSAPI with */ ++ char *gss_server_identity; /* GSSAPI target principal */ + int password_authentication; /* Try password + * authentication. */ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ +diff --git a/servconf.c b/servconf.c +index 795ddbab..14c81fa9 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -113,8 +113,10 @@ initialize_server_options(ServerOptions *options) + options->kerberos_ticket_cleanup = -1; + options->kerberos_get_afs_token = -1; + options->gss_authentication=-1; ++ options->gss_keyex = -1; + options->gss_cleanup_creds = -1; + options->gss_strict_acceptor = -1; ++ options->gss_store_rekey = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -267,10 +269,14 @@ fill_default_server_options(ServerOptions *options) + options->kerberos_get_afs_token = 0; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_cleanup_creds == -1) + options->gss_cleanup_creds = 1; + if (options->gss_strict_acceptor == -1) +- options->gss_strict_acceptor = 0; ++ options->gss_strict_acceptor = 1; ++ if (options->gss_store_rekey == -1) ++ options->gss_store_rekey = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -407,6 +413,7 @@ typedef enum { + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, ++ sGssKeyEx, sGssStoreRekey, + sAcceptEnv, sPermitTunnel, + sMatch, sPermitOpen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, +@@ -480,12 +487,20 @@ static struct { + #ifdef GSSAPI + { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, + { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, ++ { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, + #endif ++ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, + { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, + { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, + { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, +@@ -1207,6 +1222,10 @@ process_server_config_line(ServerOptions *options, char *line, + intptr = &options->gss_authentication; + goto parse_flag; + ++ case sGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case sGssCleanupCreds: + intptr = &options->gss_cleanup_creds; + goto parse_flag; +@@ -1215,6 +1234,10 @@ process_server_config_line(ServerOptions *options, char *line, + intptr = &options->gss_strict_acceptor; + goto parse_flag; + ++ case sGssStoreRekey: ++ intptr = &options->gss_store_rekey; ++ goto parse_flag; ++ + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; +@@ -2248,7 +2271,10 @@ dump_config(ServerOptions *o) + #endif + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); ++ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); ++ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); ++ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); + #endif + dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(sKbdInteractiveAuthentication, +diff --git a/servconf.h b/servconf.h +index 5853a974..90dfa4c2 100644 +--- a/servconf.h ++++ b/servconf.h +@@ -112,8 +112,10 @@ typedef struct { + int kerberos_get_afs_token; /* If true, try to get AFS token if + * authenticated with Kerberos. */ + int gss_authentication; /* If true, permit GSSAPI authentication */ ++ int gss_keyex; /* If true, permit GSSAPI key exchange */ + int gss_cleanup_creds; /* If true, destroy cred cache on logout */ + int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ ++ int gss_store_rekey; + int password_authentication; /* If true, permit password + * authentication. */ + int kbd_interactive_authentication; /* If true, permit */ +diff --git a/ssh-gss.h b/ssh-gss.h +index a99d7f08..914701bc 100644 +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -1,6 +1,6 @@ + /* $OpenBSD: ssh-gss.h,v 1.11 2014/02/26 20:28:44 djm Exp $ */ + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -61,10 +61,22 @@ + + #define SSH_GSS_OIDTYPE 0x06 + ++#define SSH2_MSG_KEXGSS_INIT 30 ++#define SSH2_MSG_KEXGSS_CONTINUE 31 ++#define SSH2_MSG_KEXGSS_COMPLETE 32 ++#define SSH2_MSG_KEXGSS_HOSTKEY 33 ++#define SSH2_MSG_KEXGSS_ERROR 34 ++#define SSH2_MSG_KEXGSS_GROUPREQ 40 ++#define SSH2_MSG_KEXGSS_GROUP 41 ++#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" ++#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" ++#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" ++ + typedef struct { + char *filename; + char *envvar; + char *envval; ++ struct passwd *owner; + void *data; + } ssh_gssapi_ccache; + +@@ -72,8 +84,11 @@ typedef struct { + gss_buffer_desc displayname; + gss_buffer_desc exportedname; + gss_cred_id_t creds; ++ gss_name_t name; + struct ssh_gssapi_mech_struct *mech; + ssh_gssapi_ccache store; ++ int used; ++ int updated; + } ssh_gssapi_client; + + typedef struct ssh_gssapi_mech_struct { +@@ -84,6 +99,7 @@ typedef struct ssh_gssapi_mech_struct { + int (*userok) (ssh_gssapi_client *, char *); + int (*localname) (ssh_gssapi_client *, char **); + void (*storecreds) (ssh_gssapi_client *); ++ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); + } ssh_gssapi_mech; + + typedef struct { +@@ -94,10 +110,11 @@ typedef struct { + gss_OID oid; /* client */ + gss_cred_id_t creds; /* server */ + gss_name_t client; /* server */ +- gss_cred_id_t client_creds; /* server */ ++ gss_cred_id_t client_creds; /* both */ + } Gssctxt; + + extern ssh_gssapi_mech *supported_mechs[]; ++extern Gssctxt *gss_kex_context; + + int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); + void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); +@@ -119,16 +136,32 @@ void ssh_gssapi_build_ctx(Gssctxt **); + void ssh_gssapi_delete_ctx(Gssctxt **); + OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); +-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); ++int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *); ++OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *); ++int ssh_gssapi_credentials_updated(Gssctxt *); + + /* In the server */ ++typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, ++ const char *); ++char *ssh_gssapi_client_mechanisms(const char *, const char *); ++char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, ++ const char *); ++gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); ++int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, ++ const char *); + OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); +-int ssh_gssapi_userok(char *name); ++int ssh_gssapi_userok(char *name, struct passwd *); + OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_do_child(char ***, u_int *); + void ssh_gssapi_cleanup_creds(void); + void ssh_gssapi_storecreds(void); + ++char *ssh_gssapi_server_mechanisms(void); ++int ssh_gssapi_oid_table_ok(void); ++ ++int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); ++void ssh_gssapi_rekey_creds(void); ++ + #endif /* GSSAPI */ + + #endif /* _SSH_GSS_H */ +diff --git a/ssh_config b/ssh_config +index 90fb63f0..4e879cd2 100644 +--- a/ssh_config ++++ b/ssh_config +@@ -26,6 +26,8 @@ + # HostbasedAuthentication no + # GSSAPIAuthentication no + # GSSAPIDelegateCredentials no ++# GSSAPIKeyExchange no ++# GSSAPITrustDNS no + # BatchMode no + # CheckHostIP yes + # AddressFamily any +diff --git a/ssh_config.5 b/ssh_config.5 +index 591365f3..a7703fc7 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -748,10 +748,42 @@ The default is + Specifies whether user authentication based on GSSAPI is allowed. + The default is + .Cm no . ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI may be used. When using ++GSSAPI key exchange the server need not have a host key. ++The default is ++.Cm no . ++.It Cm GSSAPIClientIdentity ++If set, specifies the GSSAPI client identity that ssh should use when ++connecting to the server. The default is unset, which means that the default ++identity will be used. ++.It Cm GSSAPIServerIdentity ++If set, specifies the GSSAPI server identity that ssh should expect when ++connecting to the server. The default is unset, which means that the ++expected GSSAPI server identity will be determined from the target ++hostname. + .It Cm GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. + The default is + .Cm no . ++.It Cm GSSAPIRenewalForcesRekey ++If set to ++.Cm yes ++then renewal of the client's GSSAPI credentials will force the rekeying of the ++ssh connection. With a compatible server, this can delegate the renewed ++credentials to a session on the server. ++The default is ++.Cm no . ++.It Cm GSSAPITrustDns ++Set to ++.Cm yes ++to indicate that the DNS is trusted to securely canonicalize ++the name of the host being connected to. If ++.Cm no , ++the hostname entered on the ++command line will be passed untouched to the GSSAPI library. ++The default is ++.Cm no . + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +diff --git a/sshconnect2.c b/sshconnect2.c +index 103a2b36..c35a0bd5 100644 +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -162,6 +162,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) + struct kex *kex; + int r; + ++#ifdef GSSAPI ++ char *orig = NULL, *gss = NULL; ++ char *gss_host = NULL; ++#endif ++ + xxx_host = host; + xxx_hostaddr = hostaddr; + +@@ -192,6 +197,35 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) + order_hostkeyalgs(host, hostaddr, port)); + } + ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ /* Add the GSSAPI mechanisms currently supported on this ++ * client to the key exchange algorithm proposal */ ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ if (options.gss_server_identity) ++ gss_host = xstrdup(options.gss_server_identity); ++ else if (options.gss_trust_dns) ++ gss_host = remote_hostname(active_state); ++ else ++ gss_host = xstrdup(host); ++ ++ gss = ssh_gssapi_client_mechanisms(gss_host, ++ options.gss_client_identity); ++ if (gss) { ++ debug("Offering GSSAPI proposal: %s", gss); ++ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], ++ "%s,%s", gss, orig); ++ ++ /* If we've got GSSAPI algorithms, then we also ++ * support the 'null' hostkey, as a last resort */ ++ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], ++ "%s,null", orig); ++ } ++ } ++#endif ++ + if (options.rekey_limit || options.rekey_interval) + packet_set_rekey_limits((u_int32_t)options.rekey_limit, + (time_t)options.rekey_interval); +@@ -213,15 +247,41 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) + # endif + #endif + kex->kex[KEX_C25519_SHA256] = kexc25519_client; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; ++ } ++#endif + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->verify_host_key=&verify_host_key_callback; + ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->gss_deleg_creds = options.gss_deleg_creds; ++ kex->gss_trust_dns = options.gss_trust_dns; ++ kex->gss_client = options.gss_client_identity; ++ kex->gss_host = gss_host; ++ } ++#endif ++ + dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); + + /* remove ext-info from the KEX proposals for rekeying */ + myproposal[PROPOSAL_KEX_ALGS] = + compat_kex_proposal(options.kex_algorithms); ++#ifdef GSSAPI ++ /* repair myproposal after it was crumpled by the */ ++ /* ext-info removal above */ ++ if (gss) { ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], ++ "%s,%s", gss, orig); ++ free(gss); ++ } ++#endif + if ((r = kex_prop2buf(kex->my, myproposal)) != 0) + fatal("kex_prop2buf: %s", ssh_err(r)); + +@@ -311,6 +371,7 @@ int input_gssapi_token(int type, u_int32_t, void *); + int input_gssapi_hash(int type, u_int32_t, void *); + int input_gssapi_error(int, u_int32_t, void *); + int input_gssapi_errtok(int, u_int32_t, void *); ++int userauth_gsskeyex(Authctxt *authctxt); + #endif + + void userauth(Authctxt *, char *); +@@ -327,6 +388,11 @@ static char *authmethods_get(void); + + Authmethod authmethods[] = { + #ifdef GSSAPI ++ {"gssapi-keyex", ++ userauth_gsskeyex, ++ NULL, ++ &options.gss_authentication, ++ NULL}, + {"gssapi-with-mic", + userauth_gssapi, + NULL, +@@ -652,25 +718,40 @@ userauth_gssapi(Authctxt *authctxt) + static u_int mech = 0; + OM_uint32 min; + int ok = 0; ++ char *gss_host; ++ ++ if (options.gss_server_identity) ++ gss_host = xstrdup(options.gss_server_identity); ++ else if (options.gss_trust_dns) ++ gss_host = remote_hostname(active_state); ++ else ++ gss_host = xstrdup(authctxt->host); + + /* Try one GSSAPI method at a time, rather than sending them all at + * once. */ + + if (gss_supported == NULL) +- gss_indicate_mechs(&min, &gss_supported); ++ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { ++ gss_supported = NULL; ++ free(gss_host); ++ return 0; ++ } + + /* Check to see if the mechanism is usable before we offer it */ + while (mech < gss_supported->count && !ok) { + /* My DER encoding requires length<128 */ + if (gss_supported->elements[mech].length < 128 && + ssh_gssapi_check_mechanism(&gssctxt, +- &gss_supported->elements[mech], authctxt->host)) { ++ &gss_supported->elements[mech], gss_host, ++ options.gss_client_identity)) { + ok = 1; /* Mechanism works */ + } else { + mech++; + } + } + ++ free(gss_host); ++ + if (!ok) + return 0; + +@@ -761,8 +842,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt) + { + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; +- int oidlen; +- char *oidv; ++ u_int oidlen; ++ u_char *oidv; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); +@@ -875,6 +956,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt) + free(lang); + return 0; + } ++ ++int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ Buffer b; ++ gss_buffer_desc gssbuf; ++ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ms; ++ ++ static int attempt = 0; ++ if (attempt++ >= 1) ++ return (0); ++ ++ if (gss_kex_context == NULL) { ++ debug("No valid Key exchange context"); ++ return (0); ++ } ++ ++ ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = buffer_ptr(&b); ++ gssbuf.length = buffer_len(&b); ++ ++ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { ++ buffer_free(&b); ++ return (0); ++ } ++ ++ packet_start(SSH2_MSG_USERAUTH_REQUEST); ++ packet_put_cstring(authctxt->server_user); ++ packet_put_cstring(authctxt->service); ++ packet_put_cstring(authctxt->method->name); ++ packet_put_string(mic.value, mic.length); ++ packet_send(); ++ ++ buffer_free(&b); ++ gss_release_buffer(&ms, &mic); ++ ++ return (1); ++} ++ + #endif /* GSSAPI */ + + int +diff --git a/sshd.c b/sshd.c +index 1dc4d182..0970f297 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -123,6 +123,10 @@ + #include "version.h" + #include "ssherr.h" + ++#ifdef USE_SECURITY_SESSION_API ++#include <Security/AuthSession.h> ++#endif ++ + /* Re-exec fds */ + #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) + #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) +@@ -531,7 +535,7 @@ privsep_preauth_child(void) + + #ifdef GSSAPI + /* Cache supported mechanism OIDs for later use */ +- if (options.gss_authentication) ++ if (options.gss_authentication || options.gss_keyex) + ssh_gssapi_prepare_supported_oids(); + #endif + +@@ -1705,10 +1709,13 @@ main(int ac, char **av) + key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp); + free(fp); + } ++#ifndef GSSAPI ++ /* The GSSAPI key exchange can run without a host key */ + if (!sensitive_data.have_ssh2_key) { + logit("sshd: no hostkeys available -- exiting."); + exit(1); + } ++#endif + + /* + * Load certificates. They are stored in an array at identical +@@ -1978,6 +1985,60 @@ main(int ac, char **av) + remote_ip, remote_port, laddr, ssh_local_port(ssh)); + free(laddr); + ++#ifdef USE_SECURITY_SESSION_API ++ /* ++ * Create a new security session for use by the new user login if ++ * the current session is the root session or we are not launched ++ * by inetd (eg: debugging mode or server mode). We do not ++ * necessarily need to create a session if we are launched from ++ * inetd because Panther xinetd will create a session for us. ++ * ++ * The only case where this logic will fail is if there is an ++ * inetd running in a non-root session which is not creating ++ * new sessions for us. Then all the users will end up in the ++ * same session (bad). ++ * ++ * When the client exits, the session will be destroyed for us ++ * automatically. ++ * ++ * We must create the session before any credentials are stored ++ * (including AFS pags, which happens a few lines below). ++ */ ++ { ++ OSStatus err = 0; ++ SecuritySessionId sid = 0; ++ SessionAttributeBits sattrs = 0; ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("Current Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ ++ if (inetd_flag && !(sattrs & sessionIsRoot)) ++ debug("Running in inetd mode in a non-root session... " ++ "assuming inetd created the session for us."); ++ else { ++ debug("Creating new security session..."); ++ err = SessionCreate(0, sessionHasTTY | sessionIsRemote); ++ if (err) ++ error("SessionCreate() failed with error %.8X", ++ (unsigned) err); ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, ++ &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("New Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ } ++ } ++#endif ++ + /* + * We don't want to listen forever unless the other side + * successfully authenticates itself. So we set up an alarm which is +@@ -2159,6 +2220,48 @@ do_ssh2_kex(void) + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( + list_hostkey_types()); + ++#ifdef GSSAPI ++ { ++ char *orig; ++ char *gss = NULL; ++ char *newstr = NULL; ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ /* ++ * If we don't have a host key, then there's no point advertising ++ * the other key exchange algorithms ++ */ ++ ++ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) ++ orig = NULL; ++ ++ if (options.gss_keyex) ++ gss = ssh_gssapi_server_mechanisms(); ++ else ++ gss = NULL; ++ ++ if (gss && orig) ++ xasprintf(&newstr, "%s,%s", gss, orig); ++ else if (gss) ++ newstr = gss; ++ else if (orig) ++ newstr = orig; ++ ++ /* ++ * If we've got GSSAPI mechanisms, then we've got the 'null' host ++ * key alg, but we can't tell people about it unless its the only ++ * host key algorithm we support ++ */ ++ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) ++ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; ++ ++ if (newstr) ++ myproposal[PROPOSAL_KEX_ALGS] = newstr; ++ else ++ fatal("No supported key exchange algorithms"); ++ } ++#endif ++ + /* start key exchange */ + if ((r = kex_setup(active_state, myproposal)) != 0) + fatal("kex_setup: %s", ssh_err(r)); +@@ -2176,6 +2279,13 @@ do_ssh2_kex(void) + # endif + #endif + kex->kex[KEX_C25519_SHA256] = kexc25519_server; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++ } ++#endif + kex->server = 1; + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; +diff --git a/sshd_config b/sshd_config +index 9f09e4a6..00e5a728 100644 +--- a/sshd_config ++++ b/sshd_config +@@ -70,6 +70,8 @@ AuthorizedKeysFile .ssh/authorized_keys + # GSSAPI options + #GSSAPIAuthentication no + #GSSAPICleanupCredentials yes ++#GSSAPIStrictAcceptorCheck yes ++#GSSAPIKeyExchange no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will +diff --git a/sshd_config.5 b/sshd_config.5 +index 32b29d24..dd765b39 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -623,6 +623,11 @@ The default is + Specifies whether user authentication based on GSSAPI is allowed. + The default is + .Cm no . ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange ++doesn't rely on ssh keys to verify host identity. ++The default is ++.Cm no . + .It Cm GSSAPICleanupCredentials + Specifies whether to automatically destroy the user's credentials cache + on logout. +@@ -642,6 +647,11 @@ machine's default store. + This facility is provided to assist with operation on multi homed machines. + The default is + .Cm yes . ++.It Cm GSSAPIStoreCredentialsOnRekey ++Controls whether the user's GSSAPI credentials should be updated following a ++successful connection rekeying. This option can be used to accepted renewed ++or updated credentials from a compatible client. The default is ++.Cm no . + .It Cm HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased authentication + as a comma-separated pattern list. +diff --git a/sshkey.c b/sshkey.c +index c01da6c3..377d72fa 100644 +--- a/sshkey.c ++++ b/sshkey.c +@@ -114,6 +114,7 @@ static const struct keytype keytypes[] = { + # endif /* OPENSSL_HAS_NISTP521 */ + # endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ ++ { "null", "null", KEY_NULL, 0, 0, 0 }, + { NULL, NULL, -1, -1, 0, 0 } + }; + +@@ -202,7 +203,7 @@ sshkey_alg_list(int certs_only, int plain_only, char sep) + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { +- if (kt->name == NULL || kt->sigonly) ++ if (kt->name == NULL || kt->sigonly || kt->type == KEY_NULL) + continue; + if ((certs_only && !kt->cert) || (plain_only && kt->cert)) + continue; +diff --git a/sshkey.h b/sshkey.h +index f3936384..7eb2a139 100644 +--- a/sshkey.h ++++ b/sshkey.h +@@ -62,6 +62,7 @@ enum sshkey_types { + KEY_DSA_CERT, + KEY_ECDSA_CERT, + KEY_ED25519_CERT, ++ KEY_NULL, + KEY_UNSPEC + }; + diff --git a/debian/patches/keepalive-extensions.patch b/debian/patches/keepalive-extensions.patch new file mode 100644 index 0000000..59b39cd --- /dev/null +++ b/debian/patches/keepalive-extensions.patch @@ -0,0 +1,134 @@ +From 9078d9722d24a42b8f86621d20a6a6b42ba18d37 Mon Sep 17 00:00:00 2001 +From: Richard Kettlewell <rjk@greenend.org.uk> +Date: Sun, 9 Feb 2014 16:09:52 +0000 +Subject: Various keepalive extensions + +Add compatibility aliases for ProtocolKeepAlives and SetupTimeOut, supported +in previous versions of Debian's OpenSSH package but since superseded by +ServerAliveInterval. (We're probably stuck with this bit for +compatibility.) + +In batch mode, default ServerAliveInterval to five minutes. + +Adjust documentation to match and to give some more advice on use of +keepalives. + +Author: Ian Jackson <ian@chiark.greenend.org.uk> +Author: Matthew Vernon <matthew@debian.org> +Author: Colin Watson <cjwatson@debian.org> +Last-Update: 2016-12-26 + +Patch-Name: keepalive-extensions.patch +--- + readconf.c | 14 ++++++++++++-- + ssh_config.5 | 21 +++++++++++++++++++-- + sshd_config.5 | 3 +++ + 3 files changed, 34 insertions(+), 4 deletions(-) + +diff --git a/readconf.c b/readconf.c +index c1c3aae0..3efba242 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -173,6 +173,7 @@ typedef enum { + oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, + oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes, + oPubkeyAcceptedKeyTypes, oProxyJump, ++ oProtocolKeepAlives, oSetupTimeOut, + oIgnoredUnknownOption, oDeprecated, oUnsupported + } OpCodes; + +@@ -308,6 +309,8 @@ static struct { + { "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes }, + { "ignoreunknown", oIgnoreUnknown }, + { "proxyjump", oProxyJump }, ++ { "protocolkeepalives", oProtocolKeepAlives }, ++ { "setuptimeout", oSetupTimeOut }, + + { NULL, oBadOption } + }; +@@ -1402,6 +1405,8 @@ parse_keytypes: + goto parse_flag; + + case oServerAliveInterval: ++ case oProtocolKeepAlives: /* Debian-specific compatibility alias */ ++ case oSetupTimeOut: /* Debian-specific compatibility alias */ + intptr = &options->server_alive_interval; + goto parse_time; + +@@ -2047,8 +2052,13 @@ fill_default_options(Options * options) + options->rekey_interval = 0; + if (options->verify_host_key_dns == -1) + options->verify_host_key_dns = 0; +- if (options->server_alive_interval == -1) +- options->server_alive_interval = 0; ++ if (options->server_alive_interval == -1) { ++ /* in batch mode, default is 5mins */ ++ if (options->batch_mode == 1) ++ options->server_alive_interval = 300; ++ else ++ options->server_alive_interval = 0; ++ } + if (options->server_alive_count_max == -1) + options->server_alive_count_max = 3; + if (options->control_master == -1) +diff --git a/ssh_config.5 b/ssh_config.5 +index a7703fc7..a0457314 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -250,8 +250,12 @@ Valid arguments are + If set to + .Cm yes , + passphrase/password querying will be disabled. ++In addition, the ++.Cm ServerAliveInterval ++option will be set to 300 seconds by default (Debian-specific). + This option is useful in scripts and other batch jobs where no user +-is present to supply the password. ++is present to supply the password, ++and where it is desirable to detect a broken network swiftly. + The argument must be + .Cm yes + or +@@ -1485,7 +1489,14 @@ from the server, + will send a message through the encrypted + channel to request a response from the server. + The default +-is 0, indicating that these messages will not be sent to the server. ++is 0, indicating that these messages will not be sent to the server, ++or 300 if the ++.Cm BatchMode ++option is set (Debian-specific). ++.Cm ProtocolKeepAlives ++and ++.Cm SetupTimeOut ++are Debian-specific compatibility aliases for this option. + .It Cm StreamLocalBindMask + Sets the octal file creation mode mask + .Pq umask +@@ -1544,6 +1555,12 @@ Specifies whether the system should send TCP keepalive messages to the + other side. + If they are sent, death of the connection or crash of one + of the machines will be properly noticed. ++This option only uses TCP keepalives (as opposed to using ssh level ++keepalives), so takes a long time to notice when the connection dies. ++As such, you probably want ++the ++.Cm ServerAliveInterval ++option as well. + However, this means that + connections will die if the route is down temporarily, and some people + find it annoying. +diff --git a/sshd_config.5 b/sshd_config.5 +index dd765b39..283ba889 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -1427,6 +1427,9 @@ This avoids infinitely hanging sessions. + .Pp + To disable TCP keepalive messages, the value should be set to + .Cm no . ++.Pp ++This option was formerly called ++.Cm KeepAlive . + .It Cm TrustedUserCAKeys + Specifies a file containing public keys of certificate authorities that are + trusted to sign user certificates for authentication, or diff --git a/debian/patches/mention-ssh-keygen-on-keychange.patch b/debian/patches/mention-ssh-keygen-on-keychange.patch new file mode 100644 index 0000000..4d27c68 --- /dev/null +++ b/debian/patches/mention-ssh-keygen-on-keychange.patch @@ -0,0 +1,42 @@ +From 360c4ebd14706887879f1c6d542cd092afffb07b Mon Sep 17 00:00:00 2001 +From: Scott Moser <smoser@ubuntu.com> +Date: Sun, 9 Feb 2014 16:10:03 +0000 +Subject: Mention ssh-keygen in ssh fingerprint changed warning + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1843 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/686607 +Last-Update: 2015-09-08 + +Patch-Name: mention-ssh-keygen-on-keychange.patch +--- + sshconnect.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/sshconnect.c b/sshconnect.c +index 698a0711..1cc556e8 100644 +--- a/sshconnect.c ++++ b/sshconnect.c +@@ -1080,9 +1080,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, + error("%s. This could either mean that", key_msg); + error("DNS SPOOFING is happening or the IP address for the host"); + error("and its host key have changed at the same time."); +- if (ip_status != HOST_NEW) ++ if (ip_status != HOST_NEW) { + error("Offending key for IP in %s:%lu", + ip_found->file, ip_found->line); ++ error(" remove with:"); ++ error(" ssh-keygen -f \"%s\" -R %s", ++ ip_found->file, ip); ++ } + } + /* The host key has changed. */ + warn_changed_key(host_key); +@@ -1090,6 +1094,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, + user_hostfiles[0]); + error("Offending %s key in %s:%lu", key_type(host_found->key), + host_found->file, host_found->line); ++ error(" remove with:"); ++ error(" ssh-keygen -f \"%s\" -R %s", host_found->file, host); + + /* + * If strict host key checking is in use, the user will have diff --git a/debian/patches/no-dsa-host-key-by-default.patch b/debian/patches/no-dsa-host-key-by-default.patch new file mode 100644 index 0000000..bfe6033 --- /dev/null +++ b/debian/patches/no-dsa-host-key-by-default.patch @@ -0,0 +1,83 @@ +From 3f1016b4535faf6e48aa71e21569aa714a25193f Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Mon, 16 Jan 2017 13:53:04 +0000 +Subject: Remove ssh_host_dsa_key from HostKey default + +The client no longer accepts DSA host keys, and servers using the +default HostKey setting should have better host keys available. + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=2662 +Bug-Debian: https://bugs.debian.org/850614 +Last-Update: 2017-01-16 + +Patch-Name: no-dsa-host-key-by-default.patch +--- + servconf.c | 2 -- + sshd.8 | 7 +++---- + sshd_config | 1 - + sshd_config.5 | 7 +++---- + 4 files changed, 6 insertions(+), 11 deletions(-) + +diff --git a/servconf.c b/servconf.c +index 1cee3d6c..202c4506 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -204,8 +204,6 @@ fill_default_server_options(ServerOptions *options) + /* fill default hostkeys for protocols */ + options->host_key_files[options->num_host_key_files++] = + _PATH_HOST_RSA_KEY_FILE; +- options->host_key_files[options->num_host_key_files++] = +- _PATH_HOST_DSA_KEY_FILE; + #ifdef OPENSSL_HAS_ECC + options->host_key_files[options->num_host_key_files++] = + _PATH_HOST_ECDSA_KEY_FILE; +diff --git a/sshd.8 b/sshd.8 +index 38a72540..e8f1fde8 100644 +--- a/sshd.8 ++++ b/sshd.8 +@@ -167,11 +167,10 @@ This option must be given if + is not run as root (as the normal + host key files are normally not readable by anyone but root). + The default is +-.Pa /etc/ssh/ssh_host_dsa_key , +-.Pa /etc/ssh/ssh_host_ecdsa_key , +-.Pa /etc/ssh/ssh_host_ed25519_key ++.Pa /etc/ssh/ssh_host_rsa_key , ++.Pa /etc/ssh/ssh_host_ecdsa_key + and +-.Pa /etc/ssh/ssh_host_rsa_key . ++.Pa /etc/ssh/ssh_host_ed25519_key . + It is possible to have multiple host key files for + the different host key algorithms. + .It Fl i +diff --git a/sshd_config b/sshd_config +index 13cbe2c6..4aea6c72 100644 +--- a/sshd_config ++++ b/sshd_config +@@ -16,7 +16,6 @@ + #ListenAddress :: + + #HostKey /etc/ssh/ssh_host_rsa_key +-#HostKey /etc/ssh/ssh_host_dsa_key + #HostKey /etc/ssh/ssh_host_ecdsa_key + #HostKey /etc/ssh/ssh_host_ed25519_key + +diff --git a/sshd_config.5 b/sshd_config.5 +index 703a9cdd..8f8fbb66 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -733,11 +733,10 @@ is not to load any certificates. + Specifies a file containing a private host key + used by SSH. + The defaults are +-.Pa /etc/ssh/ssh_host_dsa_key , +-.Pa /etc/ssh/ssh_host_ecdsa_key , +-.Pa /etc/ssh/ssh_host_ed25519_key ++.Pa /etc/ssh/ssh_host_rsa_key , ++.Pa /etc/ssh/ssh_host_ecdsa_key + and +-.Pa /etc/ssh/ssh_host_rsa_key . ++.Pa /etc/ssh/ssh_host_ed25519_key . + .Pp + Note that + .Xr sshd 8 diff --git a/debian/patches/no-openssl-version-status.patch b/debian/patches/no-openssl-version-status.patch new file mode 100644 index 0000000..b1c0456 --- /dev/null +++ b/debian/patches/no-openssl-version-status.patch @@ -0,0 +1,62 @@ +From 48c127fe8f40037d0f33efa8da19cb32514b440e Mon Sep 17 00:00:00 2001 +From: Kurt Roeckx <kurt@roeckx.be> +Date: Sun, 9 Feb 2014 16:10:14 +0000 +Subject: Don't check the status field of the OpenSSL version + +There is no reason to check the version of OpenSSL (in Debian). If it's +not compatible the soname will change. OpenSSH seems to want to do a +check for the soname based on the version number, but wants to keep the +status of the release the same. Remove that check on the status since +it doesn't tell you anything about how compatible that version is. + +Author: Colin Watson <cjwatson@debian.org> +Bug-Debian: https://bugs.debian.org/93581 +Bug-Debian: https://bugs.debian.org/664383 +Bug-Debian: https://bugs.debian.org/732940 +Forwarded: not-needed +Last-Update: 2014-10-07 + +Patch-Name: no-openssl-version-status.patch +--- + openbsd-compat/openssl-compat.c | 6 +++--- + openbsd-compat/regress/opensslvertest.c | 1 + + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/openbsd-compat/openssl-compat.c b/openbsd-compat/openssl-compat.c +index 259fccbe..aaa953f2 100644 +--- a/openbsd-compat/openssl-compat.c ++++ b/openbsd-compat/openssl-compat.c +@@ -34,7 +34,7 @@ + /* + * OpenSSL version numbers: MNNFFPPS: major minor fix patch status + * We match major, minor, fix and status (not patch) for <1.0.0. +- * After that, we acceptable compatible fix versions (so we ++ * After that, we accept compatible fix and status versions (so we + * allow 1.0.1 to work with 1.0.0). Going backwards is only allowed + * within a patch series. + */ +@@ -55,10 +55,10 @@ ssh_compatible_openssl(long headerver, long libver) + } + + /* +- * For versions >= 1.0.0, major,minor,status must match and library ++ * For versions >= 1.0.0, major,minor must match and library + * fix version must be equal to or newer than the header. + */ +- mask = 0xfff0000fL; /* major,minor,status */ ++ mask = 0xfff00000L; /* major,minor */ + hfix = (headerver & 0x000ff000) >> 12; + lfix = (libver & 0x000ff000) >> 12; + if ( (headerver & mask) == (libver & mask) && lfix >= hfix) +diff --git a/openbsd-compat/regress/opensslvertest.c b/openbsd-compat/regress/opensslvertest.c +index 5d019b59..58474873 100644 +--- a/openbsd-compat/regress/opensslvertest.c ++++ b/openbsd-compat/regress/opensslvertest.c +@@ -35,6 +35,7 @@ struct version_test { + + /* built with 1.0.1b release headers */ + { 0x1000101fL, 0x1000101fL, 1},/* exact match */ ++ { 0x1000101fL, 0x10001010L, 1}, /* different status: ok */ + { 0x1000101fL, 0x1000102fL, 1}, /* newer library patch version: ok */ + { 0x1000101fL, 0x1000100fL, 1}, /* older library patch version: ok */ + { 0x1000101fL, 0x1000201fL, 1}, /* newer library fix version: ok */ diff --git a/debian/patches/openbsd-docs.patch b/debian/patches/openbsd-docs.patch new file mode 100644 index 0000000..9a7edf9 --- /dev/null +++ b/debian/patches/openbsd-docs.patch @@ -0,0 +1,148 @@ +From 4badfe75ad62ee50394afa9aaac62b3465fd384e Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:10:09 +0000 +Subject: Adjust various OpenBSD-specific references in manual pages + +No single bug reference for this patch, but history includes: + http://bugs.debian.org/154434 (login.conf(5)) + http://bugs.debian.org/513417 (/etc/rc) + http://bugs.debian.org/530692 (ssl(8)) + https://bugs.launchpad.net/bugs/456660 (ssl(8)) + +Forwarded: not-needed +Last-Update: 2014-10-07 + +Patch-Name: openbsd-docs.patch +--- + moduli.5 | 4 ++-- + ssh-keygen.1 | 12 ++++-------- + ssh.1 | 4 ++++ + sshd.8 | 5 ++--- + sshd_config.5 | 3 +-- + 5 files changed, 13 insertions(+), 15 deletions(-) + +diff --git a/moduli.5 b/moduli.5 +index ef0de085..149846c8 100644 +--- a/moduli.5 ++++ b/moduli.5 +@@ -21,7 +21,7 @@ + .Nd Diffie-Hellman moduli + .Sh DESCRIPTION + The +-.Pa /etc/moduli ++.Pa /etc/ssh/moduli + file contains prime numbers and generators for use by + .Xr sshd 8 + in the Diffie-Hellman Group Exchange key exchange method. +@@ -110,7 +110,7 @@ first estimates the size of the modulus required to produce enough + Diffie-Hellman output to sufficiently key the selected symmetric cipher. + .Xr sshd 8 + then randomly selects a modulus from +-.Fa /etc/moduli ++.Fa /etc/ssh/moduli + that best meets the size requirement. + .Sh SEE ALSO + .Xr ssh-keygen 1 , +diff --git a/ssh-keygen.1 b/ssh-keygen.1 +index ce2213c7..01711dff 100644 +--- a/ssh-keygen.1 ++++ b/ssh-keygen.1 +@@ -178,9 +178,7 @@ key in + .Pa ~/.ssh/id_ed25519 + or + .Pa ~/.ssh/id_rsa . +-Additionally, the system administrator may use this to generate host keys, +-as seen in +-.Pa /etc/rc . ++Additionally, the system administrator may use this to generate host keys. + .Pp + Normally this program generates the key and asks for a file in which + to store the private key. +@@ -227,9 +225,7 @@ For each of the key types (rsa1, rsa, dsa, ecdsa and ed25519) + for which host keys + do not exist, generate the host keys with the default key file path, + an empty passphrase, default bits for the key type, and default comment. +-This is used by +-.Pa /etc/rc +-to generate new host keys. ++This is used by system administration scripts to generate new host keys. + .It Fl a Ar rounds + When saving a new-format private key (i.e. an ed25519 key or any SSH protocol + 2 key when the +@@ -644,7 +640,7 @@ option. + Valid generator values are 2, 3, and 5. + .Pp + Screened DH groups may be installed in +-.Pa /etc/moduli . ++.Pa /etc/ssh/moduli . + It is important that this file contains moduli of a range of bit lengths and + that both ends of a connection share common moduli. + .Sh CERTIFICATES +@@ -843,7 +839,7 @@ on all machines + where the user wishes to log in using public key authentication. + There is no need to keep the contents of this file secret. + .Pp +-.It Pa /etc/moduli ++.It Pa /etc/ssh/moduli + Contains Diffie-Hellman groups used for DH-GEX. + The file format is described in + .Xr moduli 5 . +diff --git a/ssh.1 b/ssh.1 +index feef81a5..b1f128c2 100644 +--- a/ssh.1 ++++ b/ssh.1 +@@ -877,6 +877,10 @@ implements public key authentication protocol automatically, + using one of the DSA, ECDSA, Ed25519 or RSA algorithms. + The HISTORY section of + .Xr ssl 8 ++(on non-OpenBSD systems, see ++.nh ++http://www.openbsd.org/cgi\-bin/man.cgi?query=ssl&sektion=8#HISTORY) ++.hy + contains a brief discussion of the DSA and RSA algorithms. + .Pp + The file +diff --git a/sshd.8 b/sshd.8 +index c6784602..e6915141 100644 +--- a/sshd.8 ++++ b/sshd.8 +@@ -65,7 +65,7 @@ over an insecure network. + .Nm + listens for connections from clients. + It is normally started at boot from +-.Pa /etc/rc . ++.Pa /etc/init.d/ssh . + It forks a new + daemon for each incoming connection. + The forked daemons handle +@@ -836,7 +836,7 @@ This file is for host-based authentication (see + .Xr ssh 1 ) . + It should only be writable by root. + .Pp +-.It Pa /etc/moduli ++.It Pa /etc/ssh/moduli + Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange" + key exchange method. + The file format is described in +@@ -936,7 +936,6 @@ The content of this file is not sensitive; it can be world-readable. + .Xr ssh-keyscan 1 , + .Xr chroot 2 , + .Xr hosts_access 5 , +-.Xr login.conf 5 , + .Xr moduli 5 , + .Xr sshd_config 5 , + .Xr inetd 8 , +diff --git a/sshd_config.5 b/sshd_config.5 +index 4ea0a9c3..e45a8937 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -372,8 +372,7 @@ then no banner is displayed. + By default, no banner is displayed. + .It Cm ChallengeResponseAuthentication + Specifies whether challenge-response authentication is allowed (e.g. via +-PAM or through authentication styles supported in +-.Xr login.conf 5 ) ++PAM). + The default is + .Cm yes . + .It Cm ChrootDirectory diff --git a/debian/patches/package-versioning.patch b/debian/patches/package-versioning.patch new file mode 100644 index 0000000..fcc231f --- /dev/null +++ b/debian/patches/package-versioning.patch @@ -0,0 +1,65 @@ +From c89c88a0bcada4616262e3d7d9b165aca709927b Mon Sep 17 00:00:00 2001 +From: Matthew Vernon <matthew@debian.org> +Date: Sun, 9 Feb 2014 16:10:05 +0000 +Subject: Include the Debian version in our identification + +This makes it easier to audit networks for versions patched against security +vulnerabilities. It has little detrimental effect, as attackers will +generally just try attacks rather than bothering to scan for +vulnerable-looking version strings. (However, see debian-banner.patch.) + +Forwarded: not-needed +Last-Update: 2013-09-14 + +Patch-Name: package-versioning.patch +--- + sshconnect.c | 4 ++-- + sshd.c | 2 +- + version.h | 7 ++++++- + 3 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/sshconnect.c b/sshconnect.c +index 1cc556e8..c64c51bb 100644 +--- a/sshconnect.c ++++ b/sshconnect.c +@@ -526,10 +526,10 @@ send_client_banner(int connection_out, int minor1) + /* Send our own protocol version identification. */ + if (compat20) { + xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n", +- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION); ++ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_RELEASE); + } else { + xasprintf(&client_version_string, "SSH-%d.%d-%.100s\n", +- PROTOCOL_MAJOR_1, minor1, SSH_VERSION); ++ PROTOCOL_MAJOR_1, minor1, SSH_RELEASE); + } + if (atomicio(vwrite, connection_out, client_version_string, + strlen(client_version_string)) != strlen(client_version_string)) +diff --git a/sshd.c b/sshd.c +index 9aab36c3..49f3a2e5 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -378,7 +378,7 @@ sshd_exchange_identification(struct ssh *ssh, int sock_in, int sock_out) + char remote_version[256]; /* Must be at least as big as buf. */ + + xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s%s", +- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, ++ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_RELEASE, + *options.version_addendum == '\0' ? "" : " ", + options.version_addendum, newline); + +diff --git a/version.h b/version.h +index 269ebcda..850a2f7d 100644 +--- a/version.h ++++ b/version.h +@@ -3,4 +3,9 @@ + #define SSH_VERSION "OpenSSH_7.4" + + #define SSH_PORTABLE "p1" +-#define SSH_RELEASE SSH_VERSION SSH_PORTABLE ++#define SSH_RELEASE_MINIMUM SSH_VERSION SSH_PORTABLE ++#ifdef SSH_EXTRAVERSION ++#define SSH_RELEASE SSH_RELEASE_MINIMUM " " SSH_EXTRAVERSION ++#else ++#define SSH_RELEASE SSH_RELEASE_MINIMUM ++#endif diff --git a/debian/patches/quieter-signals.patch b/debian/patches/quieter-signals.patch new file mode 100644 index 0000000..c19fc46 --- /dev/null +++ b/debian/patches/quieter-signals.patch @@ -0,0 +1,40 @@ +From 71809791262478c78d1db2ca1004604c39db8150 Mon Sep 17 00:00:00 2001 +From: Peter Samuelson <peter@p12n.org> +Date: Sun, 9 Feb 2014 16:09:55 +0000 +Subject: Reduce severity of "Killed by signal %d" + +This produces irritating messages when using ProxyCommand or other programs +that use ssh under the covers (e.g. Subversion). These messages are more +normally printed by the calling program, such as the shell. + +According to the upstream bug, the right way to avoid this is to use the -q +option, so we may drop this patch after further investigation into whether +any software in Debian is still relying on it. + +Author: Colin Watson <cjwatson@debian.org> +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1118 +Bug-Debian: http://bugs.debian.org/313371 +Last-Update: 2013-09-14 + +Patch-Name: quieter-signals.patch +--- + clientloop.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/clientloop.c b/clientloop.c +index 99c68b69..5876cc9a 100644 +--- a/clientloop.c ++++ b/clientloop.c +@@ -1755,8 +1755,10 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) + exit_status = 0; + } + +- if (received_signal) +- fatal("Killed by signal %d.", (int) received_signal); ++ if (received_signal) { ++ debug("Killed by signal %d.", (int) received_signal); ++ cleanup_exit((int) received_signal + 128); ++ } + + /* + * In interactive mode (with pseudo tty) display a message indicating diff --git a/debian/patches/regress-forwarding-race.patch b/debian/patches/regress-forwarding-race.patch new file mode 100644 index 0000000..f1a535f --- /dev/null +++ b/debian/patches/regress-forwarding-race.patch @@ -0,0 +1,115 @@ +From 166f04046035ffca27c820649df360eaa5dd1b99 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Mon, 2 Jan 2017 14:55:16 +0000 +Subject: Fix race conditions in forwarding tests + +The forwarding tests sometimes seem to fail in a way that suggests ports +are in use even though they shouldn't be. Convert more of them to use a +mux socket rather than relying on sleeps in the hope that that makes +behaviour more consistent. + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=2659 +Patch-Name: regress-forwarding-race.patch + +Last-Update: 2017-01-02 +--- + regress/forwarding.sh | 32 +++++++++++++++++++------------- + 1 file changed, 19 insertions(+), 13 deletions(-) + +diff --git a/regress/forwarding.sh b/regress/forwarding.sh +index 2539db9b..a1a4b13f 100644 +--- a/regress/forwarding.sh ++++ b/regress/forwarding.sh +@@ -11,7 +11,6 @@ base=33 + last=$PORT + fwd="" + CTL=$OBJ/ctl-sock +-rm -f $CTL + + for j in 0 1 2; do + for i in 0 1 2; do +@@ -29,7 +28,8 @@ for p in ${SSH_PROTOCOLS}; do + q=$p + fi + trace "start forwarding, fork to background" +- ${SSH} -$p -F $OBJ/ssh_config -f $fwd somehost sleep 10 ++ rm -f $CTL ++ ${SSH} -S $CTL -M -$p -F $OBJ/ssh_config -f $fwd somehost sleep 10 + + trace "transfer over forwarded channels and check result" + ${SSH} -$q -F $OBJ/ssh_config -p$last -o 'ConnectionAttempts=4' \ +@@ -37,7 +37,7 @@ for p in ${SSH_PROTOCOLS}; do + test -s ${COPY} || fail "failed copy of ${DATA}" + cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" + +- sleep 10 ++ ${SSH} -S $CTL -O exit somehost + done + + for p in ${SSH_PROTOCOLS}; do +@@ -75,7 +75,8 @@ for p in ${SSH_PROTOCOLS}; do + ${SSH} -$p -F $OBJ/ssh_config -oClearAllForwardings=yes somehost true + + trace "clear local forward proto $p" +- ${SSH} -$p -f -F $OBJ/ssh_config -L ${base}01:127.0.0.1:$PORT \ ++ rm -f $CTL ++ ${SSH} -S $CTL -M -$p -f -F $OBJ/ssh_config -L ${base}01:127.0.0.1:$PORT \ + -oClearAllForwardings=yes somehost sleep 10 + if [ $? != 0 ]; then + fail "connection failed with cleared local forwarding" +@@ -85,10 +86,11 @@ for p in ${SSH_PROTOCOLS}; do + >>$TEST_REGRESS_LOGFILE 2>&1 && \ + fail "local forwarding not cleared" + fi +- sleep 10 ++ ${SSH} -S $CTL -O exit somehost + + trace "clear remote forward proto $p" +- ${SSH} -$p -f -F $OBJ/ssh_config -R ${base}01:127.0.0.1:$PORT \ ++ rm -f $CTL ++ ${SSH} -S $CTL -M -$p -f -F $OBJ/ssh_config -R ${base}01:127.0.0.1:$PORT \ + -oClearAllForwardings=yes somehost sleep 10 + if [ $? != 0 ]; then + fail "connection failed with cleared remote forwarding" +@@ -98,7 +100,7 @@ for p in ${SSH_PROTOCOLS}; do + >>$TEST_REGRESS_LOGFILE 2>&1 && \ + fail "remote forwarding not cleared" + fi +- sleep 10 ++ ${SSH} -S $CTL -O exit somehost + done + + for p in 2; do +@@ -115,6 +117,7 @@ echo "LocalForward ${base}01 127.0.0.1:$PORT" >> $OBJ/ssh_config + echo "RemoteForward ${base}02 127.0.0.1:${base}01" >> $OBJ/ssh_config + for p in ${SSH_PROTOCOLS}; do + trace "config file: start forwarding, fork to background" ++ rm -f $CTL + ${SSH} -S $CTL -M -$p -F $OBJ/ssh_config -f somehost sleep 10 + + trace "config file: transfer over forwarded channels and check result" +@@ -129,15 +132,18 @@ done + for p in 2; do + trace "transfer over chained unix domain socket forwards and check result" + rm -f $OBJ/unix-[123].fwd +- ${SSH} -f -F $OBJ/ssh_config -R${base}01:[$OBJ/unix-1.fwd] somehost sleep 10 +- ${SSH} -f -F $OBJ/ssh_config -L[$OBJ/unix-1.fwd]:[$OBJ/unix-2.fwd] somehost sleep 10 +- ${SSH} -f -F $OBJ/ssh_config -R[$OBJ/unix-2.fwd]:[$OBJ/unix-3.fwd] somehost sleep 10 +- ${SSH} -f -F $OBJ/ssh_config -L[$OBJ/unix-3.fwd]:127.0.0.1:$PORT somehost sleep 10 ++ rm -f $CTL $CTL.[123] ++ ${SSH} -S $CTL -M -f -F $OBJ/ssh_config -R${base}01:[$OBJ/unix-1.fwd] somehost sleep 10 ++ ${SSH} -S $CTL.1 -M -f -F $OBJ/ssh_config -L[$OBJ/unix-1.fwd]:[$OBJ/unix-2.fwd] somehost sleep 10 ++ ${SSH} -S $CTL.2 -M -f -F $OBJ/ssh_config -R[$OBJ/unix-2.fwd]:[$OBJ/unix-3.fwd] somehost sleep 10 ++ ${SSH} -S $CTL.3 -M -f -F $OBJ/ssh_config -L[$OBJ/unix-3.fwd]:127.0.0.1:$PORT somehost sleep 10 + ${SSH} -F $OBJ/ssh_config -p${base}01 -o 'ConnectionAttempts=4' \ + somehost cat ${DATA} > ${COPY} + test -s ${COPY} || fail "failed copy ${DATA}" + cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" + +- #wait +- sleep 10 ++ ${SSH} -S $CTL -O exit somehost ++ ${SSH} -S $CTL.1 -O exit somehost ++ ${SSH} -S $CTL.2 -O exit somehost ++ ${SSH} -S $CTL.3 -O exit somehost + done diff --git a/debian/patches/regress-integrity-robust.patch b/debian/patches/regress-integrity-robust.patch new file mode 100644 index 0000000..651a7a8 --- /dev/null +++ b/debian/patches/regress-integrity-robust.patch @@ -0,0 +1,57 @@ +From 7ce93c802065cd926e7cbfd10e629f3a2d352301 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 1 Jan 2017 15:21:10 +0000 +Subject: Make integrity tests more robust against timeouts + +If the first test in a series for a given MAC happens to modify the low +bytes of a packet length, then ssh will time out and this will be +interpreted as a test failure. Handle this failure mode. + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=2658 +Patch-Name: regress-integrity-robust.patch + +Last-Update: 2017-01-01 +--- + regress/integrity.sh | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/regress/integrity.sh b/regress/integrity.sh +index 39d310de..fd7d58bc 100644 +--- a/regress/integrity.sh ++++ b/regress/integrity.sh +@@ -5,8 +5,6 @@ tid="integrity" + cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + + # start at byte 2900 (i.e. after kex) and corrupt at different offsets +-# XXX the test hangs if we modify the low bytes of the packet length +-# XXX and ssh tries to read... + tries=10 + startoffset=2900 + macs=`${SSH} -Q mac` +@@ -27,6 +25,7 @@ for m in $macs; do + elen=0 + epad=0 + emac=0 ++ etmo=0 + ecnt=0 + skip=0 + for off in `jot $tries $startoffset`; do +@@ -61,14 +60,16 @@ for m in $macs; do + Corrupted?MAC* | *message?authentication?code?incorrect*) + emac=`expr $emac + 1`; skip=0;; + padding*) epad=`expr $epad + 1`; skip=0;; ++ *Timeout,?server*) ++ etmo=`expr $etmo + 1`; skip=0;; + *) fail "unexpected error mac $m at $off: $out";; + esac + done +- verbose "test $tid: $ecnt errors: mac $emac padding $epad length $elen" ++ verbose "test $tid: $ecnt errors: mac $emac padding $epad length $elen timeout $etmo" + if [ $emac -eq 0 ]; then + fail "$m: no mac errors" + fi +- expect=`expr $ecnt - $epad - $elen` ++ expect=`expr $ecnt - $epad - $elen - $etmo` + if [ $emac -ne $expect ]; then + fail "$m: expected $expect mac errors, got $emac" + fi diff --git a/debian/patches/regress-mktemp.patch b/debian/patches/regress-mktemp.patch new file mode 100644 index 0000000..f5cfde1 --- /dev/null +++ b/debian/patches/regress-mktemp.patch @@ -0,0 +1,167 @@ +From 6ca09916439a58f0789deb79960ee5defc05a946 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Tue, 3 Jan 2017 12:09:42 +0000 +Subject: Create mux socket for regress in temp directory + +In some setups, creating the socket under OBJ may result in a path that +is too long for a Unix domain socket. Add a helper to let us portably +create a temporary directory instead. + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=2660 +Last-Update: 2017-01-03 + +Patch-Name: regress-mktemp.patch +--- + Makefile.in | 5 +++++ + regress/forwarding.sh | 3 ++- + regress/mkdtemp.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ + regress/multiplex.sh | 3 ++- + regress/test-exec.sh | 11 ++++++++++ + 5 files changed, 79 insertions(+), 2 deletions(-) + create mode 100644 regress/mkdtemp.c + +diff --git a/Makefile.in b/Makefile.in +index a6eb81ec..a00347e2 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -459,6 +459,10 @@ regress/check-perm$(EXEEXT): $(srcdir)/regress/check-perm.c $(REGRESSLIBS) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/check-perm.c \ + $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + ++regress/mkdtemp$(EXEEXT): $(srcdir)/regress/mkdtemp.c $(REGRESSLIBS) ++ $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/mkdtemp.c \ ++ $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) ++ + UNITTESTS_TEST_HELPER_OBJS=\ + regress/unittests/test_helper/test_helper.o \ + regress/unittests/test_helper/fuzz.o +@@ -557,6 +561,7 @@ regress-binaries: regress/modpipe$(EXEEXT) \ + regress/setuid-allowed$(EXEEXT) \ + regress/netcat$(EXEEXT) \ + regress/check-perm$(EXEEXT) \ ++ regress/mkdtemp$(EXEEXT) \ + regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \ + regress/unittests/sshkey/test_sshkey$(EXEEXT) \ + regress/unittests/bitmap/test_bitmap$(EXEEXT) \ +diff --git a/regress/forwarding.sh b/regress/forwarding.sh +index a1a4b13f..592de7bc 100644 +--- a/regress/forwarding.sh ++++ b/regress/forwarding.sh +@@ -10,7 +10,8 @@ start_sshd + base=33 + last=$PORT + fwd="" +-CTL=$OBJ/ctl-sock ++make_tmpdir ++CTL=$TMP/ctl-sock + + for j in 0 1 2; do + for i in 0 1 2; do +diff --git a/regress/mkdtemp.c b/regress/mkdtemp.c +new file mode 100644 +index 00000000..8c7d2e21 +--- /dev/null ++++ b/regress/mkdtemp.c +@@ -0,0 +1,59 @@ ++/* ++ * Copyright (c) 2017 Colin Watson <cjwatson@debian.org> ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++/* Roughly equivalent to "mktemp -d -t TEMPLATE", but portable. */ ++ ++#include "includes.h" ++ ++#include <limits.h> ++#include <stdio.h> ++#include <stdlib.h> ++ ++#include "log.h" ++ ++static void ++usage(void) ++{ ++ fprintf(stderr, "mkdtemp template\n"); ++ exit(1); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ const char *base; ++ const char *tmpdir; ++ char template[PATH_MAX]; ++ int r; ++ char *dir; ++ ++ if (argc != 2) ++ usage(); ++ base = argv[1]; ++ ++ if ((tmpdir = getenv("TMPDIR")) == NULL) ++ tmpdir = "/tmp"; ++ r = snprintf(template, sizeof(template), "%s/%s", tmpdir, base); ++ if (r < 0 || (size_t)r >= sizeof(template)) ++ fatal("template string too long"); ++ dir = mkdtemp(template); ++ if (dir == NULL) { ++ perror("mkdtemp"); ++ exit(1); ++ } ++ puts(dir); ++ return 0; ++} +diff --git a/regress/multiplex.sh b/regress/multiplex.sh +index acb9234d..0ac4065e 100644 +--- a/regress/multiplex.sh ++++ b/regress/multiplex.sh +@@ -1,7 +1,8 @@ + # $OpenBSD: multiplex.sh,v 1.27 2014/12/22 06:14:29 djm Exp $ + # Placed in the Public Domain. + +-CTL=/tmp/openssh.regress.ctl-sock.$$ ++make_tmpdir ++CTL=$TMP/ctl-sock + + tid="connection multiplexing" + +diff --git a/regress/test-exec.sh b/regress/test-exec.sh +index bfa48803..13a8e18f 100644 +--- a/regress/test-exec.sh ++++ b/regress/test-exec.sh +@@ -317,6 +317,14 @@ stop_sshd () + fi + } + ++TMP= ++ ++make_tmpdir () ++{ ++ TMP="$($OBJ/mkdtemp openssh-regress-XXXXXXXXXXXX)" || \ ++ fatal "failed to create temporary directory" ++} ++ + # helper + cleanup () + { +@@ -327,6 +335,9 @@ cleanup () + kill $SSH_PID + fi + fi ++ if [ "x$TMP" != "x" ]; then ++ rm -rf "$TMP" ++ fi + stop_sshd + } + diff --git a/debian/patches/restore-authorized_keys2.patch b/debian/patches/restore-authorized_keys2.patch new file mode 100644 index 0000000..86da09c --- /dev/null +++ b/debian/patches/restore-authorized_keys2.patch @@ -0,0 +1,35 @@ +From e18d2ba71e6bf009c53e65509da84b712c300471 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 5 Mar 2017 02:02:11 +0000 +Subject: Restore reading authorized_keys2 by default + +Upstream seems to intend to gradually phase this out, so don't assume +that this will remain the default forever. However, we were late in +adopting the upstream sshd_config changes, so it makes sense to extend +the grace period. + +Bug-Debian: https://bugs.debian.org/852320 +Forwarded: not-needed +Last-Update: 2017-03-05 + +Patch-Name: restore-authorized_keys2.patch +--- + sshd_config | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/sshd_config b/sshd_config +index 4aea6c72..bcf3ac17 100644 +--- a/sshd_config ++++ b/sshd_config +@@ -36,9 +36,8 @@ + + #PubkeyAuthentication yes + +-# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 +-# but this is overridden so installations will only check .ssh/authorized_keys +-AuthorizedKeysFile .ssh/authorized_keys ++# Expect .ssh/authorized_keys2 to be disregarded by default in future. ++#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2 + + #AuthorizedPrincipalsFile none + diff --git a/debian/patches/restore-tcp-wrappers.patch b/debian/patches/restore-tcp-wrappers.patch new file mode 100644 index 0000000..ec958d3 --- /dev/null +++ b/debian/patches/restore-tcp-wrappers.patch @@ -0,0 +1,172 @@ +From 5488e924267d7a845fb86a0b6b4db1e340799a5a Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Tue, 7 Oct 2014 13:22:41 +0100 +Subject: Restore TCP wrappers support + +Support for TCP wrappers was dropped in OpenSSH 6.7. See this message +and thread: + + https://lists.mindrot.org/pipermail/openssh-unix-dev/2014-April/032497.html + +It is true that this reduces preauth attack surface in sshd. On the +other hand, this support seems to be quite widely used, and abruptly +dropping it (from the perspective of users who don't read +openssh-unix-dev) could easily cause more serious problems in practice. + +It's not entirely clear what the right long-term answer for Debian is, +but it at least probably doesn't involve dropping this feature shortly +before a freeze. + +Forwarded: not-needed +Last-Update: 2014-10-07 + +Patch-Name: restore-tcp-wrappers.patch +--- + configure.ac | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + sshd.8 | 7 +++++++ + sshd.c | 25 +++++++++++++++++++++++++ + 3 files changed, 89 insertions(+) + +diff --git a/configure.ac b/configure.ac +index 5fdc696c..4747ce4a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1491,6 +1491,62 @@ AC_ARG_WITH([skey], + ] + ) + ++# Check whether user wants TCP wrappers support ++TCPW_MSG="no" ++AC_ARG_WITH([tcp-wrappers], ++ [ --with-tcp-wrappers[[=PATH]] Enable tcpwrappers support (optionally in PATH)], ++ [ ++ if test "x$withval" != "xno" ; then ++ saved_LIBS="$LIBS" ++ saved_LDFLAGS="$LDFLAGS" ++ saved_CPPFLAGS="$CPPFLAGS" ++ if test -n "${withval}" && \ ++ test "x${withval}" != "xyes"; then ++ if test -d "${withval}/lib"; then ++ if test -n "${need_dash_r}"; then ++ LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" ++ else ++ LDFLAGS="-L${withval}/lib ${LDFLAGS}" ++ fi ++ else ++ if test -n "${need_dash_r}"; then ++ LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" ++ else ++ LDFLAGS="-L${withval} ${LDFLAGS}" ++ fi ++ fi ++ if test -d "${withval}/include"; then ++ CPPFLAGS="-I${withval}/include ${CPPFLAGS}" ++ else ++ CPPFLAGS="-I${withval} ${CPPFLAGS}" ++ fi ++ fi ++ LIBS="-lwrap $LIBS" ++ AC_MSG_CHECKING([for libwrap]) ++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ ++#include <sys/types.h> ++#include <sys/socket.h> ++#include <netinet/in.h> ++#include <tcpd.h> ++int deny_severity = 0, allow_severity = 0; ++ ]], [[ ++ hosts_access(0); ++ ]])], [ ++ AC_MSG_RESULT([yes]) ++ AC_DEFINE([LIBWRAP], [1], ++ [Define if you want ++ TCP Wrappers support]) ++ SSHDLIBS="$SSHDLIBS -lwrap" ++ TCPW_MSG="yes" ++ ], [ ++ AC_MSG_ERROR([*** libwrap missing]) ++ ++ ]) ++ LIBS="$saved_LIBS" ++ fi ++ ] ++) ++ + # Check whether user wants to use ldns + LDNS_MSG="no" + AC_ARG_WITH(ldns, +@@ -5105,6 +5161,7 @@ echo " KerberosV support: $KRB5_MSG" + echo " SELinux support: $SELINUX_MSG" + echo " Smartcard support: $SCARD_MSG" + echo " S/KEY support: $SKEY_MSG" ++echo " TCP Wrappers support: $TCPW_MSG" + echo " MD5 password support: $MD5_MSG" + echo " libedit support: $LIBEDIT_MSG" + echo " Solaris process contract support: $SPC_MSG" +diff --git a/sshd.8 b/sshd.8 +index 41fc5051..c6784602 100644 +--- a/sshd.8 ++++ b/sshd.8 +@@ -825,6 +825,12 @@ the user's home directory becomes accessible. + This file should be writable only by the user, and need not be + readable by anyone else. + .Pp ++.It Pa /etc/hosts.allow ++.It Pa /etc/hosts.deny ++Access controls that should be enforced by tcp-wrappers are defined here. ++Further details are described in ++.Xr hosts_access 5 . ++.Pp + .It Pa /etc/hosts.equiv + This file is for host-based authentication (see + .Xr ssh 1 ) . +@@ -929,6 +935,7 @@ The content of this file is not sensitive; it can be world-readable. + .Xr ssh-keygen 1 , + .Xr ssh-keyscan 1 , + .Xr chroot 2 , ++.Xr hosts_access 5 , + .Xr login.conf 5 , + .Xr moduli 5 , + .Xr sshd_config 5 , +diff --git a/sshd.c b/sshd.c +index 0970f297..72d85de1 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -127,6 +127,13 @@ + #include <Security/AuthSession.h> + #endif + ++#ifdef LIBWRAP ++#include <tcpd.h> ++#include <syslog.h> ++int allow_severity; ++int deny_severity; ++#endif /* LIBWRAP */ ++ + /* Re-exec fds */ + #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) + #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) +@@ -1978,6 +1985,24 @@ main(int ac, char **av) + #ifdef SSH_AUDIT_EVENTS + audit_connection_from(remote_ip, remote_port); + #endif ++#ifdef LIBWRAP ++ allow_severity = options.log_facility|LOG_INFO; ++ deny_severity = options.log_facility|LOG_WARNING; ++ /* Check whether logins are denied from this host. */ ++ if (packet_connection_is_on_socket()) { ++ struct request_info req; ++ ++ request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0); ++ fromhost(&req); ++ ++ if (!hosts_access(&req)) { ++ debug("Connection refused by tcp wrapper"); ++ refuse(&req); ++ /* NOTREACHED */ ++ fatal("libwrap refuse returns"); ++ } ++ } ++#endif /* LIBWRAP */ + + /* Log the connection. */ + laddr = get_local_ipaddr(sock_in); diff --git a/debian/patches/sandbox-x32-workaround.patch b/debian/patches/sandbox-x32-workaround.patch new file mode 100644 index 0000000..340363d --- /dev/null +++ b/debian/patches/sandbox-x32-workaround.patch @@ -0,0 +1,37 @@ +From 8c1a0893f0e55a793071af9734d2fa2eb1f3a2a6 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Tue, 3 Jan 2017 14:01:56 +0000 +Subject: Work around clock_gettime kernel bug on Linux x32 + +On Linux x32, the clock_gettime VDSO currently falls back to the x86-64 +syscall, so allow that as well as its x32 sibling. + +Bug-Debian: https://bugs.debian.org/849923 +Forwarded: no +Last-Update: 2017-01-03 + +Patch-Name: sandbox-x32-workaround.patch +--- + sandbox-seccomp-filter.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c +index 2e1ed2c5..62c578d3 100644 +--- a/sandbox-seccomp-filter.c ++++ b/sandbox-seccomp-filter.c +@@ -137,6 +137,15 @@ static const struct sock_filter preauth_insns[] = { + #endif + #ifdef __NR_clock_gettime + SC_ALLOW(clock_gettime), ++# if defined(__x86_64__) && defined(__ILP32__) ++ /* On Linux x32, the clock_gettime VDSO currently falls back to the ++ * x86-64 syscall (see https://bugs.debian.org/849923), so allow ++ * that too. ++ */ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ++ __NR_clock_gettime & ~__X32_SYSCALL_BIT, 0, 1), ++ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), ++# endif + #endif + #ifdef __NR_close + SC_ALLOW(close), diff --git a/debian/patches/scp-quoting.patch b/debian/patches/scp-quoting.patch new file mode 100644 index 0000000..f318c49 --- /dev/null +++ b/debian/patches/scp-quoting.patch @@ -0,0 +1,41 @@ +From cfc11fb9604f8049957a409ff0835f642a047496 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Nicolas=20Valc=C3=A1rcel?= <nvalcarcel@ubuntu.com> +Date: Sun, 9 Feb 2014 16:09:59 +0000 +Subject: Adjust scp quoting in verbose mode + +Tweak scp's reporting of filenames in verbose mode to be a bit less +confusing with spaces. + +This should be revised to mimic real shell quoting. + +Bug-Ubuntu: https://bugs.launchpad.net/bugs/89945 +Last-Update: 2010-02-27 + +Patch-Name: scp-quoting.patch +--- + scp.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/scp.c b/scp.c +index b4db8519..18c27720 100644 +--- a/scp.c ++++ b/scp.c +@@ -191,8 +191,16 @@ do_local_cmd(arglist *a) + + if (verbose_mode) { + fprintf(stderr, "Executing:"); +- for (i = 0; i < a->num; i++) +- fmprintf(stderr, " %s", a->list[i]); ++ for (i = 0; i < a->num; i++) { ++ if (i == 0) ++ fmprintf(stderr, " %s", a->list[i]); ++ else ++ /* ++ * TODO: misbehaves if a->list[i] contains a ++ * single quote ++ */ ++ fmprintf(stderr, " '%s'", a->list[i]); ++ } + fprintf(stderr, "\n"); + } + if ((pid = fork()) == -1) diff --git a/debian/patches/selinux-role.patch b/debian/patches/selinux-role.patch new file mode 100644 index 0000000..98be50f --- /dev/null +++ b/debian/patches/selinux-role.patch @@ -0,0 +1,473 @@ +From a01822fe1c50668ef7918dfd28b1c7e88ff16254 Mon Sep 17 00:00:00 2001 +From: Manoj Srivastava <srivasta@debian.org> +Date: Sun, 9 Feb 2014 16:09:49 +0000 +Subject: Handle SELinux authorisation roles + +Rejected upstream due to discomfort with magic usernames; a better approach +will need an SSH protocol change. In the meantime, this came from Debian's +SELinux maintainer, so we'll keep it until we have something better. + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1641 +Bug-Debian: http://bugs.debian.org/394795 +Last-Update: 2015-08-19 + +Patch-Name: selinux-role.patch +--- + auth.h | 1 + + auth2.c | 10 ++++++++-- + monitor.c | 32 +++++++++++++++++++++++++++++--- + monitor.h | 2 ++ + monitor_wrap.c | 22 ++++++++++++++++++++-- + monitor_wrap.h | 3 ++- + openbsd-compat/port-linux.c | 27 ++++++++++++++++++++------- + openbsd-compat/port-linux.h | 4 ++-- + platform.c | 4 ++-- + platform.h | 2 +- + session.c | 10 +++++----- + session.h | 2 +- + sshd.c | 2 +- + sshpty.c | 4 ++-- + sshpty.h | 2 +- + 15 files changed, 97 insertions(+), 30 deletions(-) + +diff --git a/auth.h b/auth.h +index 338a62da..8c658d16 100644 +--- a/auth.h ++++ b/auth.h +@@ -62,6 +62,7 @@ struct Authctxt { + char *service; + struct passwd *pw; /* set if 'valid' */ + char *style; ++ char *role; + void *kbdintctxt; + char *info; /* Extra info for next auth_log */ + #ifdef BSD_AUTH +diff --git a/auth2.c b/auth2.c +index ce0d3760..461311bd 100644 +--- a/auth2.c ++++ b/auth2.c +@@ -216,7 +216,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) + { + Authctxt *authctxt = ctxt; + Authmethod *m = NULL; +- char *user, *service, *method, *style = NULL; ++ char *user, *service, *method, *style = NULL, *role = NULL; + int authenticated = 0; + + if (authctxt == NULL) +@@ -228,8 +228,13 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) + debug("userauth-request for user %s service %s method %s", user, service, method); + debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); + ++ if ((role = strchr(user, '/')) != NULL) ++ *role++ = 0; ++ + if ((style = strchr(user, ':')) != NULL) + *style++ = 0; ++ else if (role && (style = strchr(role, ':')) != NULL) ++ *style++ = '\0'; + + if (authctxt->attempt++ == 0) { + /* setup auth context */ +@@ -253,8 +258,9 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) + use_privsep ? " [net]" : ""); + authctxt->service = xstrdup(service); + authctxt->style = style ? xstrdup(style) : NULL; ++ authctxt->role = role ? xstrdup(role) : NULL; + if (use_privsep) +- mm_inform_authserv(service, style); ++ mm_inform_authserv(service, style, role); + userauth_banner(); + if (auth2_setup_methods_lists(authctxt) != 0) + packet_disconnect("no authentication methods enabled"); +diff --git a/monitor.c b/monitor.c +index 76d9e346..64286a12 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -127,6 +127,7 @@ int mm_answer_sign(int, Buffer *); + int mm_answer_pwnamallow(int, Buffer *); + int mm_answer_auth2_read_banner(int, Buffer *); + int mm_answer_authserv(int, Buffer *); ++int mm_answer_authrole(int, Buffer *); + int mm_answer_authpassword(int, Buffer *); + int mm_answer_bsdauthquery(int, Buffer *); + int mm_answer_bsdauthrespond(int, Buffer *); +@@ -204,6 +205,7 @@ struct mon_table mon_dispatch_proto20[] = { + {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, + {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, + {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, ++ {MONITOR_REQ_AUTHROLE, MON_ONCE, mm_answer_authrole}, + {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, + {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, + #ifdef USE_PAM +@@ -786,6 +788,7 @@ mm_answer_pwnamallow(int sock, Buffer *m) + + /* Allow service/style information on the auth context */ + monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); ++ monitor_permit(mon_dispatch, MONITOR_REQ_AUTHROLE, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); + + #ifdef USE_PAM +@@ -816,14 +819,37 @@ mm_answer_authserv(int sock, Buffer *m) + + authctxt->service = buffer_get_string(m, NULL); + authctxt->style = buffer_get_string(m, NULL); +- debug3("%s: service=%s, style=%s", +- __func__, authctxt->service, authctxt->style); ++ authctxt->role = buffer_get_string(m, NULL); ++ debug3("%s: service=%s, style=%s, role=%s", ++ __func__, authctxt->service, authctxt->style, authctxt->role); + + if (strlen(authctxt->style) == 0) { + free(authctxt->style); + authctxt->style = NULL; + } + ++ if (strlen(authctxt->role) == 0) { ++ free(authctxt->role); ++ authctxt->role = NULL; ++ } ++ ++ return (0); ++} ++ ++int ++mm_answer_authrole(int sock, Buffer *m) ++{ ++ monitor_permit_authentications(1); ++ ++ authctxt->role = buffer_get_string(m, NULL); ++ debug3("%s: role=%s", ++ __func__, authctxt->role); ++ ++ if (strlen(authctxt->role) == 0) { ++ free(authctxt->role); ++ authctxt->role = NULL; ++ } ++ + return (0); + } + +@@ -1458,7 +1484,7 @@ mm_answer_pty(int sock, Buffer *m) + res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); + if (res == 0) + goto error; +- pty_setowner(authctxt->pw, s->tty); ++ pty_setowner(authctxt->pw, s->tty, authctxt->role); + + buffer_put_int(m, 1); + buffer_put_cstring(m, s->tty); +diff --git a/monitor.h b/monitor.h +index ec41404c..4c7955d7 100644 +--- a/monitor.h ++++ b/monitor.h +@@ -68,6 +68,8 @@ enum monitor_reqtype { + MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151, + MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153, + ++ MONITOR_REQ_AUTHROLE = 154, ++ + }; + + struct monitor { +diff --git a/monitor_wrap.c b/monitor_wrap.c +index d5cb640a..2ff8064a 100644 +--- a/monitor_wrap.c ++++ b/monitor_wrap.c +@@ -327,10 +327,10 @@ mm_auth2_read_banner(void) + return (banner); + } + +-/* Inform the privileged process about service and style */ ++/* Inform the privileged process about service, style, and role */ + + void +-mm_inform_authserv(char *service, char *style) ++mm_inform_authserv(char *service, char *style, char *role) + { + Buffer m; + +@@ -339,12 +339,30 @@ mm_inform_authserv(char *service, char *style) + buffer_init(&m); + buffer_put_cstring(&m, service); + buffer_put_cstring(&m, style ? style : ""); ++ buffer_put_cstring(&m, role ? role : ""); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHSERV, &m); + + buffer_free(&m); + } + ++/* Inform the privileged process about role */ ++ ++void ++mm_inform_authrole(char *role) ++{ ++ Buffer m; ++ ++ debug3("%s entering", __func__); ++ ++ buffer_init(&m); ++ buffer_put_cstring(&m, role ? role : ""); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHROLE, &m); ++ ++ buffer_free(&m); ++} ++ + /* Do the password authentication */ + int + mm_auth_password(Authctxt *authctxt, char *password) +diff --git a/monitor_wrap.h b/monitor_wrap.h +index 8f9dd896..3e75867c 100644 +--- a/monitor_wrap.h ++++ b/monitor_wrap.h +@@ -41,7 +41,8 @@ void mm_log_handler(LogLevel, const char *, void *); + int mm_is_monitor(void); + DH *mm_choose_dh(int, int, int); + int mm_key_sign(Key *, u_char **, u_int *, const u_char *, u_int, const char *); +-void mm_inform_authserv(char *, char *); ++void mm_inform_authserv(char *, char *, char *); ++void mm_inform_authrole(char *); + struct passwd *mm_getpwnamallow(const char *); + char *mm_auth2_read_banner(void); + int mm_auth_password(struct Authctxt *, char *); +diff --git a/openbsd-compat/port-linux.c b/openbsd-compat/port-linux.c +index e4c5d1b7..e26faf08 100644 +--- a/openbsd-compat/port-linux.c ++++ b/openbsd-compat/port-linux.c +@@ -27,6 +27,12 @@ + #include <string.h> + #include <stdio.h> + ++#ifdef WITH_SELINUX ++#include "key.h" ++#include "hostfile.h" ++#include "auth.h" ++#endif ++ + #include "log.h" + #include "xmalloc.h" + #include "port-linux.h" +@@ -56,7 +62,7 @@ ssh_selinux_enabled(void) + + /* Return the default security context for the given username */ + static security_context_t +-ssh_selinux_getctxbyname(char *pwname) ++ssh_selinux_getctxbyname(char *pwname, const char *role) + { + security_context_t sc = NULL; + char *sename = NULL, *lvl = NULL; +@@ -71,9 +77,16 @@ ssh_selinux_getctxbyname(char *pwname) + #endif + + #ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL +- r = get_default_context_with_level(sename, lvl, NULL, &sc); ++ if (role != NULL && role[0]) ++ r = get_default_context_with_rolelevel(sename, role, lvl, NULL, ++ &sc); ++ else ++ r = get_default_context_with_level(sename, lvl, NULL, &sc); + #else +- r = get_default_context(sename, NULL, &sc); ++ if (role != NULL && role[0]) ++ r = get_default_context_with_role(sename, role, NULL, &sc); ++ else ++ r = get_default_context(sename, NULL, &sc); + #endif + + if (r != 0) { +@@ -103,7 +116,7 @@ ssh_selinux_getctxbyname(char *pwname) + + /* Set the execution context to the default for the specified user */ + void +-ssh_selinux_setup_exec_context(char *pwname) ++ssh_selinux_setup_exec_context(char *pwname, const char *role) + { + security_context_t user_ctx = NULL; + +@@ -112,7 +125,7 @@ ssh_selinux_setup_exec_context(char *pwname) + + debug3("%s: setting execution context", __func__); + +- user_ctx = ssh_selinux_getctxbyname(pwname); ++ user_ctx = ssh_selinux_getctxbyname(pwname, role); + if (setexeccon(user_ctx) != 0) { + switch (security_getenforce()) { + case -1: +@@ -134,7 +147,7 @@ ssh_selinux_setup_exec_context(char *pwname) + + /* Set the TTY context for the specified user */ + void +-ssh_selinux_setup_pty(char *pwname, const char *tty) ++ssh_selinux_setup_pty(char *pwname, const char *tty, const char *role) + { + security_context_t new_tty_ctx = NULL; + security_context_t user_ctx = NULL; +@@ -145,7 +158,7 @@ ssh_selinux_setup_pty(char *pwname, const char *tty) + + debug3("%s: setting TTY context on %s", __func__, tty); + +- user_ctx = ssh_selinux_getctxbyname(pwname); ++ user_ctx = ssh_selinux_getctxbyname(pwname, role); + + /* XXX: should these calls fatal() upon failure in enforcing mode? */ + +diff --git a/openbsd-compat/port-linux.h b/openbsd-compat/port-linux.h +index 3c22a854..c8812942 100644 +--- a/openbsd-compat/port-linux.h ++++ b/openbsd-compat/port-linux.h +@@ -19,8 +19,8 @@ + + #ifdef WITH_SELINUX + int ssh_selinux_enabled(void); +-void ssh_selinux_setup_pty(char *, const char *); +-void ssh_selinux_setup_exec_context(char *); ++void ssh_selinux_setup_pty(char *, const char *, const char *); ++void ssh_selinux_setup_exec_context(char *, const char *); + void ssh_selinux_change_context(const char *); + void ssh_selinux_setfscreatecon(const char *); + #endif +diff --git a/platform.c b/platform.c +index 973a63e4..cd7bf566 100644 +--- a/platform.c ++++ b/platform.c +@@ -143,7 +143,7 @@ platform_setusercontext(struct passwd *pw) + * called if sshd is running as root. + */ + void +-platform_setusercontext_post_groups(struct passwd *pw) ++platform_setusercontext_post_groups(struct passwd *pw, const char *role) + { + #if !defined(HAVE_LOGIN_CAP) && defined(USE_PAM) + /* +@@ -184,7 +184,7 @@ platform_setusercontext_post_groups(struct passwd *pw) + } + #endif /* HAVE_SETPCRED */ + #ifdef WITH_SELINUX +- ssh_selinux_setup_exec_context(pw->pw_name); ++ ssh_selinux_setup_exec_context(pw->pw_name, role); + #endif + } + +diff --git a/platform.h b/platform.h +index ea4f9c58..60d72ffe 100644 +--- a/platform.h ++++ b/platform.h +@@ -25,7 +25,7 @@ void platform_post_fork_parent(pid_t child_pid); + void platform_post_fork_child(void); + int platform_privileged_uidswap(void); + void platform_setusercontext(struct passwd *); +-void platform_setusercontext_post_groups(struct passwd *); ++void platform_setusercontext_post_groups(struct passwd *, const char *); + char *platform_get_krb5_client(const char *); + char *platform_krb5_get_principal_name(const char *); + int platform_sys_dir_uid(uid_t); +diff --git a/session.c b/session.c +index a08aa69d..ea3871eb 100644 +--- a/session.c ++++ b/session.c +@@ -1325,7 +1325,7 @@ safely_chroot(const char *path, uid_t uid) + + /* Set login name, uid, gid, and groups. */ + void +-do_setusercontext(struct passwd *pw) ++do_setusercontext(struct passwd *pw, const char *role) + { + char *chroot_path, *tmp; + +@@ -1353,7 +1353,7 @@ do_setusercontext(struct passwd *pw) + endgrent(); + #endif + +- platform_setusercontext_post_groups(pw); ++ platform_setusercontext_post_groups(pw, role); + + if (!in_chroot && options.chroot_directory != NULL && + strcasecmp(options.chroot_directory, "none") != 0) { +@@ -1489,7 +1489,7 @@ do_child(Session *s, const char *command) + + /* Force a password change */ + if (s->authctxt->force_pwchange) { +- do_setusercontext(pw); ++ do_setusercontext(pw, s->authctxt->role); + child_close_fds(); + do_pwchange(s); + exit(1); +@@ -1511,7 +1511,7 @@ do_child(Session *s, const char *command) + /* When PAM is enabled we rely on it to do the nologin check */ + if (!options.use_pam) + do_nologin(pw); +- do_setusercontext(pw); ++ do_setusercontext(pw, s->authctxt->role); + /* + * PAM session modules in do_setusercontext may have + * generated messages, so if this in an interactive +@@ -1903,7 +1903,7 @@ session_pty_req(Session *s) + tty_parse_modes(s->ttyfd, &n_bytes); + + if (!use_privsep) +- pty_setowner(s->pw, s->tty); ++ pty_setowner(s->pw, s->tty, s->authctxt->role); + + /* Set window size from the packet. */ + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); +diff --git a/session.h b/session.h +index 98e1dafe..0a31dce4 100644 +--- a/session.h ++++ b/session.h +@@ -76,7 +76,7 @@ void session_pty_cleanup2(Session *); + Session *session_new(void); + Session *session_by_tty(char *); + void session_close(Session *); +-void do_setusercontext(struct passwd *); ++void do_setusercontext(struct passwd *, const char *); + void child_set_env(char ***envp, u_int *envsizep, const char *name, + const char *value); + +diff --git a/sshd.c b/sshd.c +index 72d85de1..9aab36c3 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -678,7 +678,7 @@ privsep_postauth(Authctxt *authctxt) + reseed_prngs(); + + /* Drop privileges */ +- do_setusercontext(authctxt->pw); ++ do_setusercontext(authctxt->pw, authctxt->role); + + skip: + /* It is safe now to apply the key state */ +diff --git a/sshpty.c b/sshpty.c +index fe2fb5aa..feb22b06 100644 +--- a/sshpty.c ++++ b/sshpty.c +@@ -187,7 +187,7 @@ pty_change_window_size(int ptyfd, u_int row, u_int col, + } + + void +-pty_setowner(struct passwd *pw, const char *tty) ++pty_setowner(struct passwd *pw, const char *tty, const char *role) + { + struct group *grp; + gid_t gid; +@@ -209,7 +209,7 @@ pty_setowner(struct passwd *pw, const char *tty) + strerror(errno)); + + #ifdef WITH_SELINUX +- ssh_selinux_setup_pty(pw->pw_name, tty); ++ ssh_selinux_setup_pty(pw->pw_name, tty, role); + #endif + + if (st.st_uid != pw->pw_uid || st.st_gid != gid) { +diff --git a/sshpty.h b/sshpty.h +index 9ec7e9a1..de7e000a 100644 +--- a/sshpty.h ++++ b/sshpty.h +@@ -24,5 +24,5 @@ int pty_allocate(int *, int *, char *, size_t); + void pty_release(const char *); + void pty_make_controlling_tty(int *, const char *); + void pty_change_window_size(int, u_int, u_int, u_int, u_int); +-void pty_setowner(struct passwd *, const char *); ++void pty_setowner(struct passwd *, const char *, const char *); + void disconnect_controlling_tty(void); diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..c5fc814 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,36 @@ +gssapi.patch +restore-tcp-wrappers.patch +selinux-role.patch +ssh-vulnkey-compat.patch +keepalive-extensions.patch +syslog-level-silent.patch +quieter-signals.patch +user-group-modes.patch +scp-quoting.patch +shell-path.patch +dnssec-sshfp.patch +auth-log-verbosity.patch +mention-ssh-keygen-on-keychange.patch +package-versioning.patch +debian-banner.patch +authorized-keys-man-symlink.patch +openbsd-docs.patch +ssh-argv0.patch +doc-hash-tab-completion.patch +doc-upstart.patch +ssh-agent-setgid.patch +no-openssl-version-status.patch +gnome-ssh-askpass2-icon.patch +sigstop.patch +systemd-readiness.patch +debian-config.patch +regress-integrity-robust.patch +regress-forwarding-race.patch +regress-mktemp.patch +sandbox-x32-workaround.patch +no-dsa-host-key-by-default.patch +restore-authorized_keys2.patch +ssh-keygen-hash-corruption.patch +ssh-keyscan-hash-port.patch +ssh-keygen-null-deref.patch +unbreak-unix-forwarding-for-root.patch diff --git a/debian/patches/shell-path.patch b/debian/patches/shell-path.patch new file mode 100644 index 0000000..c263dd7 --- /dev/null +++ b/debian/patches/shell-path.patch @@ -0,0 +1,39 @@ +From 5ec0d5f79166a7e2aeab5c7f13d64bb08c4621bd Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:10:00 +0000 +Subject: Look for $SHELL on the path for ProxyCommand/LocalCommand + +There's some debate on the upstream bug about whether POSIX requires this. +I (Colin Watson) agree with Vincent and think it does. + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1494 +Bug-Debian: http://bugs.debian.org/492728 +Last-Update: 2013-09-14 + +Patch-Name: shell-path.patch +--- + sshconnect.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/sshconnect.c b/sshconnect.c +index 96b91ce1..698a0711 100644 +--- a/sshconnect.c ++++ b/sshconnect.c +@@ -231,7 +231,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) + /* Execute the proxy command. Note that we gave up any + extra privileges above. */ + signal(SIGPIPE, SIG_DFL); +- execv(argv[0], argv); ++ execvp(argv[0], argv); + perror(argv[0]); + exit(1); + } +@@ -1498,7 +1498,7 @@ ssh_local_cmd(const char *args) + if (pid == 0) { + signal(SIGPIPE, SIG_DFL); + debug3("Executing %s -c \"%s\"", shell, args); +- execl(shell, shell, "-c", args, (char *)NULL); ++ execlp(shell, shell, "-c", args, (char *)NULL); + error("Couldn't execute %s -c \"%s\": %s", + shell, args, strerror(errno)); + _exit(1); diff --git a/debian/patches/sigstop.patch b/debian/patches/sigstop.patch new file mode 100644 index 0000000..fa4d0a8 --- /dev/null +++ b/debian/patches/sigstop.patch @@ -0,0 +1,35 @@ +From 218ecbc433b69b8584000380626a9d9aa31c095b Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:10:17 +0000 +Subject: Support synchronisation with service supervisor using SIGSTOP + +Author: Robie Basak <robie.basak@ubuntu.com> +Forwarded: no +Last-Update: 2014-04-14 + +Patch-Name: sigstop.patch +--- + sshd.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/sshd.c b/sshd.c +index eebf1984..b6826c84 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -1878,6 +1878,16 @@ main(int ac, char **av) + } + } + ++ if (getenv("SSH_SIGSTOP")) { ++ /* Tell service supervisor that we are ready. */ ++ kill(getpid(), SIGSTOP); ++ /* The service supervisor only ever expects a single ++ * STOP signal, so do not ever signal it again, even ++ * in the case of a re-exec or future children. ++ */ ++ unsetenv("SSH_SIGSTOP"); ++ } ++ + /* Accept a connection and return in a forked child */ + server_accept_loop(&sock_in, &sock_out, + &newsock, config_s); diff --git a/debian/patches/ssh-agent-setgid.patch b/debian/patches/ssh-agent-setgid.patch new file mode 100644 index 0000000..b14ec01 --- /dev/null +++ b/debian/patches/ssh-agent-setgid.patch @@ -0,0 +1,40 @@ +From 0ae30d0171b789953318670ac8679127ddfb3cd1 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:10:13 +0000 +Subject: Document consequences of ssh-agent being setgid in ssh-agent(1) + +Bug-Debian: http://bugs.debian.org/711623 +Forwarded: no +Last-Update: 2013-06-08 + +Patch-Name: ssh-agent-setgid.patch +--- + ssh-agent.1 | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/ssh-agent.1 b/ssh-agent.1 +index 83b2b41c..7230704a 100644 +--- a/ssh-agent.1 ++++ b/ssh-agent.1 +@@ -206,6 +206,21 @@ environment variable holds the agent's process ID. + .Pp + The agent exits automatically when the command given on the command + line terminates. ++.Pp ++In Debian, ++.Nm ++is installed with the set-group-id bit set, to prevent ++.Xr ptrace 2 ++attacks retrieving private key material. ++This has the side-effect of causing the run-time linker to remove certain ++environment variables which might have security implications for set-id ++programs, including ++.Ev LD_PRELOAD , ++.Ev LD_LIBRARY_PATH , ++and ++.Ev TMPDIR . ++If you need to set any of these environment variables, you will need to do ++so in the program executed by ssh-agent. + .Sh FILES + .Bl -tag -width Ds + .It Pa $TMPDIR/ssh-XXXXXXXXXX/agent.<ppid> diff --git a/debian/patches/ssh-argv0.patch b/debian/patches/ssh-argv0.patch new file mode 100644 index 0000000..7fbaa25 --- /dev/null +++ b/debian/patches/ssh-argv0.patch @@ -0,0 +1,31 @@ +From e39339d49d1b05e1db45c6420d7e6da29cf483dc Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:10:10 +0000 +Subject: ssh(1): Refer to ssh-argv0(1) + +Old versions of OpenSSH (up to 2.5 or thereabouts) allowed creating symlinks +to ssh with the name of the host you want to connect to. Debian ships an +ssh-argv0 script restoring this feature; this patch refers to its manual +page from ssh(1). + +Bug-Debian: http://bugs.debian.org/111341 +Forwarded: not-needed +Last-Update: 2013-09-14 + +Patch-Name: ssh-argv0.patch +--- + ssh.1 | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ssh.1 b/ssh.1 +index b1f128c2..22e56a7b 100644 +--- a/ssh.1 ++++ b/ssh.1 +@@ -1586,6 +1586,7 @@ if an error occurred. + .Xr sftp 1 , + .Xr ssh-add 1 , + .Xr ssh-agent 1 , ++.Xr ssh-argv0 1 , + .Xr ssh-keygen 1 , + .Xr ssh-keyscan 1 , + .Xr tun 4 , diff --git a/debian/patches/ssh-keygen-hash-corruption.patch b/debian/patches/ssh-keygen-hash-corruption.patch new file mode 100644 index 0000000..7ef3c63 --- /dev/null +++ b/debian/patches/ssh-keygen-hash-corruption.patch @@ -0,0 +1,44 @@ +From 78800aa252da1ebbfb55f7e593f43c337e694cc3 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" <djm@openbsd.org> +Date: Fri, 3 Mar 2017 06:13:11 +0000 +Subject: upstream commit + +fix ssh-keygen -H accidentally corrupting known_hosts that +contained already-hashed entries. HKF_MATCH_HOST_HASHED is only set by +hostkeys_foreach() when hostname matching is in use, so we need to look for +the hash marker explicitly. + +Upstream-ID: da82ad653b93e8a753580d3cf5cd448bc2520528 + +Origin: https://anongit.mindrot.org/openssh.git/commit/?id=12d3767ba4c84c32150cbe6ff6494498780f12c9 +Bug-Debian: https://bugs.debian.org/851734 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1668093 +Last-Update: 2017-03-09 + +Patch-Name: ssh-keygen-hash-corruption.patch +--- + ssh-keygen.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ssh-keygen.c b/ssh-keygen.c +index 2a7939bf..0833ee61 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -1082,6 +1082,7 @@ known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) + struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; + char *hashed, *cp, *hosts, *ohosts; + int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); ++ int was_hashed = l->hosts[0] == HASH_DELIM; + + switch (l->status) { + case HKF_STATUS_OK: +@@ -1090,8 +1091,7 @@ known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) + * Don't hash hosts already already hashed, with wildcard + * characters or a CA/revocation marker. + */ +- if ((l->match & HKF_MATCH_HOST_HASHED) != 0 || +- has_wild || l->marker != MRK_NONE) { ++ if (was_hashed || has_wild || l->marker != MRK_NONE) { + fprintf(ctx->out, "%s\n", l->line); + if (has_wild && !find_host) { + logit("%s:%ld: ignoring host name " diff --git a/debian/patches/ssh-keygen-null-deref.patch b/debian/patches/ssh-keygen-null-deref.patch new file mode 100644 index 0000000..0220d7c --- /dev/null +++ b/debian/patches/ssh-keygen-null-deref.patch @@ -0,0 +1,31 @@ +From 35b2ea77a74348b575d680061f35ec7992b26ec8 Mon Sep 17 00:00:00 2001 +From: "dtucker@openbsd.org" <dtucker@openbsd.org> +Date: Mon, 6 Mar 2017 02:03:20 +0000 +Subject: upstream commit + +Check l->hosts before dereferencing; fixes potential null +pointer deref. ok djm@ + +Upstream-ID: 81c0327c6ec361da794b5c680601195cc23d1301 + +Origin: https://anongit.mindrot.org/openssh.git/commit/?id=18501151cf272a15b5f2c5e777f2e0933633c513 +Last-Update: 2017-03-16 + +Patch-Name: ssh-keygen-null-deref.patch +--- + ssh-keygen.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ssh-keygen.c b/ssh-keygen.c +index 0833ee61..a7c1e80b 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -1082,7 +1082,7 @@ known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) + struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; + char *hashed, *cp, *hosts, *ohosts; + int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); +- int was_hashed = l->hosts[0] == HASH_DELIM; ++ int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM; + + switch (l->status) { + case HKF_STATUS_OK: diff --git a/debian/patches/ssh-keyscan-hash-port.patch b/debian/patches/ssh-keyscan-hash-port.patch new file mode 100644 index 0000000..32a2f6a --- /dev/null +++ b/debian/patches/ssh-keyscan-hash-port.patch @@ -0,0 +1,48 @@ +From a0f9daa9c3cc2b37b9707b228263eb717d201371 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" <djm@openbsd.org> +Date: Fri, 10 Mar 2017 03:18:24 +0000 +Subject: upstream commit + +correctly hash hosts with a port number. Reported by Josh +Powers in bz#2692; ok dtucker@ + +Upstream-ID: 468e357ff143e00acc05bdd2803a696b3d4b6442 + +Origin: https://anongit.mindrot.org/openssh.git/commit/?id=8a2834454c73dfc1eb96453c0e97690595f3f4c2 +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=2692 +Bug-Debian: https://bugs.debian.org/857736 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1670745 +Last-Update: 2017-03-14 + +Patch-Name: ssh-keyscan-hash-port.patch +--- + ssh-keyscan.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/ssh-keyscan.c b/ssh-keyscan.c +index c30d54e6..24b51ff1 100644 +--- a/ssh-keyscan.c ++++ b/ssh-keyscan.c +@@ -321,16 +321,17 @@ keygrab_ssh2(con *c) + } + + static void +-keyprint_one(char *host, struct sshkey *key) ++keyprint_one(const char *host, struct sshkey *key) + { + char *hostport; +- +- if (hash_hosts && (host = host_hash(host, NULL, 0)) == NULL) +- fatal("host_hash failed"); ++ const char *known_host, *hashed; + + hostport = put_host_port(host, ssh_port); ++ if (hash_hosts && (hashed = host_hash(host, NULL, 0)) == NULL) ++ fatal("host_hash failed"); ++ known_host = hash_hosts ? hashed : hostport; + if (!get_cert) +- fprintf(stdout, "%s ", hostport); ++ fprintf(stdout, "%s ", known_host); + sshkey_write(key, stdout); + fputs("\n", stdout); + free(hostport); diff --git a/debian/patches/ssh-vulnkey-compat.patch b/debian/patches/ssh-vulnkey-compat.patch new file mode 100644 index 0000000..fbe6433 --- /dev/null +++ b/debian/patches/ssh-vulnkey-compat.patch @@ -0,0 +1,42 @@ +From ffecece153b7caedf997dccf17747633675631fd Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@ubuntu.com> +Date: Sun, 9 Feb 2014 16:09:50 +0000 +Subject: Accept obsolete ssh-vulnkey configuration options + +These options were used as part of Debian's response to CVE-2008-0166. +Nearly six years later, we no longer need to continue carrying the bulk +of that patch, but we do need to avoid failing when the associated +configuration options are still present. + +Last-Update: 2014-02-09 + +Patch-Name: ssh-vulnkey-compat.patch +--- + readconf.c | 1 + + servconf.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/readconf.c b/readconf.c +index 7902ef26..c1c3aae0 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -194,6 +194,7 @@ static struct { + { "passwordauthentication", oPasswordAuthentication }, + { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, + { "kbdinteractivedevices", oKbdInteractiveDevices }, ++ { "useblacklistedkeys", oDeprecated }, + { "rsaauthentication", oRSAAuthentication }, + { "pubkeyauthentication", oPubkeyAuthentication }, + { "dsaauthentication", oPubkeyAuthentication }, /* alias */ +diff --git a/servconf.c b/servconf.c +index 14c81fa9..49d3bdc8 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -521,6 +521,7 @@ static struct { + { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, + { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, + { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, ++ { "permitblacklistedkeys", sDeprecated, SSHCFG_GLOBAL }, + { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, + { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, + { "uselogin", sDeprecated, SSHCFG_GLOBAL }, diff --git a/debian/patches/syslog-level-silent.patch b/debian/patches/syslog-level-silent.patch new file mode 100644 index 0000000..7a4839c --- /dev/null +++ b/debian/patches/syslog-level-silent.patch @@ -0,0 +1,47 @@ +From f4d9efefeae948e1e00212bf9702245c3c51c8c5 Mon Sep 17 00:00:00 2001 +From: Jonathan David Amery <jdamery@ysolde.ucam.org> +Date: Sun, 9 Feb 2014 16:09:54 +0000 +Subject: "LogLevel SILENT" compatibility + +"LogLevel SILENT" (-qq) was introduced in Debian openssh 1:3.0.1p1-1 to +match the behaviour of non-free SSH, in which -q does not suppress fatal +errors. However, this was unintentionally broken in 1:4.6p1-2 and nobody +complained, so we've dropped most of it. The parts that remain are basic +configuration file compatibility, and an adjustment to "Pseudo-terminal will +not be allocated ..." which should be split out into a separate patch. + +Author: Matthew Vernon <matthew@debian.org> +Author: Colin Watson <cjwatson@debian.org> +Last-Update: 2013-09-14 + +Patch-Name: syslog-level-silent.patch +--- + log.c | 1 + + ssh.c | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/log.c b/log.c +index 2b59c427..ffc8ffbb 100644 +--- a/log.c ++++ b/log.c +@@ -93,6 +93,7 @@ static struct { + LogLevel val; + } log_levels[] = + { ++ { "SILENT", SYSLOG_LEVEL_QUIET }, /* compatibility */ + { "QUIET", SYSLOG_LEVEL_QUIET }, + { "FATAL", SYSLOG_LEVEL_FATAL }, + { "ERROR", SYSLOG_LEVEL_ERROR }, +diff --git a/ssh.c b/ssh.c +index ee0b16dc..39609e79 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -1167,7 +1167,7 @@ main(int ac, char **av) + /* Do not allocate a tty if stdin is not a tty. */ + if ((!isatty(fileno(stdin)) || stdin_null_flag) && + options.request_tty != REQUEST_TTY_FORCE) { +- if (tty_flag) ++ if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) + logit("Pseudo-terminal will not be allocated because " + "stdin is not a terminal."); + tty_flag = 0; diff --git a/debian/patches/systemd-readiness.patch b/debian/patches/systemd-readiness.patch new file mode 100644 index 0000000..6c8cf9b --- /dev/null +++ b/debian/patches/systemd-readiness.patch @@ -0,0 +1,84 @@ +From 0fd4134a3ef467e1e69db5b19b7903cf306ec64b Mon Sep 17 00:00:00 2001 +From: Michael Biebl <biebl@debian.org> +Date: Mon, 21 Dec 2015 16:08:47 +0000 +Subject: Add systemd readiness notification support + +Bug-Debian: https://bugs.debian.org/778913 +Forwarded: no +Last-Update: 2016-01-04 + +Patch-Name: systemd-readiness.patch +--- + configure.ac | 24 ++++++++++++++++++++++++ + sshd.c | 9 +++++++++ + 2 files changed, 33 insertions(+) + +diff --git a/configure.ac b/configure.ac +index 4747ce4a..9f59794b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -4364,6 +4364,29 @@ AC_ARG_WITH([kerberos5], + AC_SUBST([GSSLIBS]) + AC_SUBST([K5LIBS]) + ++# Check whether user wants systemd support ++SYSTEMD_MSG="no" ++AC_ARG_WITH(systemd, ++ [ --with-systemd Enable systemd support], ++ [ if test "x$withval" != "xno" ; then ++ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) ++ if test "$PKGCONFIG" != "no"; then ++ AC_MSG_CHECKING([for libsystemd]) ++ if $PKGCONFIG --exists libsystemd; then ++ SYSTEMD_CFLAGS=`$PKGCONFIG --cflags libsystemd` ++ SYSTEMD_LIBS=`$PKGCONFIG --libs libsystemd` ++ CPPFLAGS="$CPPFLAGS $SYSTEMD_CFLAGS" ++ SSHDLIBS="$SSHDLIBS $SYSTEMD_LIBS" ++ AC_MSG_RESULT([yes]) ++ AC_DEFINE(HAVE_SYSTEMD, 1, [Define if you want systemd support.]) ++ SYSTEMD_MSG="yes" ++ else ++ AC_MSG_RESULT([no]) ++ fi ++ fi ++ fi ] ++) ++ + # Looking for programs, paths and files + + PRIVSEP_PATH=/var/empty +@@ -5167,6 +5190,7 @@ echo " libedit support: $LIBEDIT_MSG" + echo " Solaris process contract support: $SPC_MSG" + echo " Solaris project support: $SP_MSG" + echo " Solaris privilege support: $SPP_MSG" ++echo " systemd support: $SYSTEMD_MSG" + echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" + echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" + echo " BSD Auth support: $BSD_AUTH_MSG" +diff --git a/sshd.c b/sshd.c +index b6826c84..027daa9d 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -85,6 +85,10 @@ + #include <prot.h> + #endif + ++#ifdef HAVE_SYSTEMD ++#include <systemd/sd-daemon.h> ++#endif ++ + #include "xmalloc.h" + #include "ssh.h" + #include "ssh2.h" +@@ -1888,6 +1892,11 @@ main(int ac, char **av) + unsetenv("SSH_SIGSTOP"); + } + ++#ifdef HAVE_SYSTEMD ++ /* Signal systemd that we are ready to accept connections */ ++ sd_notify(0, "READY=1"); ++#endif ++ + /* Accept a connection and return in a forked child */ + server_accept_loop(&sock_in, &sock_out, + &newsock, config_s); diff --git a/debian/patches/unbreak-unix-forwarding-for-root.patch b/debian/patches/unbreak-unix-forwarding-for-root.patch new file mode 100644 index 0000000..8408a11 --- /dev/null +++ b/debian/patches/unbreak-unix-forwarding-for-root.patch @@ -0,0 +1,80 @@ +From 904bc482ad87648a2c799c441dc6a8449f24e15a Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" <djm@openbsd.org> +Date: Wed, 4 Jan 2017 05:37:40 +0000 +Subject: upstream commit + +unbreak Unix domain socket forwarding for root; ok +markus@ + +Upstream-ID: 6649c76eb7a3fa15409373295ca71badf56920a2 + +Origin: https://anongit.mindrot.org/openssh.git/commit/?id=51045869fa084cdd016fdd721ea760417c0a3bf3 +Bug-Debian: https://bugs.debian.org/858252 +Last-Update: 2017-03-30 + +Patch-Name: unbreak-unix-forwarding-for-root.patch +--- + serverloop.c | 19 ++++++++++++------- + 1 file changed, 12 insertions(+), 7 deletions(-) + +diff --git a/serverloop.c b/serverloop.c +index c4e4699d..c55d203b 100644 +--- a/serverloop.c ++++ b/serverloop.c +@@ -468,6 +468,10 @@ server_request_direct_streamlocal(void) + Channel *c = NULL; + char *target, *originator; + u_short originator_port; ++ struct passwd *pw = the_authctxt->pw; ++ ++ if (pw == NULL || !the_authctxt->valid) ++ fatal("server_input_global_request: no/invalid user"); + + target = packet_get_string(NULL); + originator = packet_get_string(NULL); +@@ -480,7 +484,7 @@ server_request_direct_streamlocal(void) + /* XXX fine grained permissions */ + if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 && + !no_port_forwarding_flag && !options.disable_forwarding && +- use_privsep) { ++ (pw->pw_uid == 0 || use_privsep)) { + c = channel_connect_to_path(target, + "direct-streamlocal@openssh.com", "direct-streamlocal"); + } else { +@@ -702,6 +706,10 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt) + int want_reply; + int r, success = 0, allocated_listen_port = 0; + struct sshbuf *resp = NULL; ++ struct passwd *pw = the_authctxt->pw; ++ ++ if (pw == NULL || !the_authctxt->valid) ++ fatal("server_input_global_request: no/invalid user"); + + rtype = packet_get_string(NULL); + want_reply = packet_get_char(); +@@ -709,12 +717,8 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt) + + /* -R style forwarding */ + if (strcmp(rtype, "tcpip-forward") == 0) { +- struct passwd *pw; + struct Forward fwd; + +- pw = the_authctxt->pw; +- if (pw == NULL || !the_authctxt->valid) +- fatal("server_input_global_request: no/invalid user"); + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_host = packet_get_string(NULL); + fwd.listen_port = (u_short)packet_get_int(); +@@ -762,9 +766,10 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt) + /* check permissions */ + if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0 + || no_port_forwarding_flag || options.disable_forwarding || +- !use_privsep) { ++ (pw->pw_uid != 0 && !use_privsep)) { + success = 0; +- packet_send_debug("Server has disabled port forwarding."); ++ packet_send_debug("Server has disabled " ++ "streamlocal forwarding."); + } else { + /* Start listening on the socket */ + success = channel_setup_remote_fwd_listener( diff --git a/debian/patches/user-group-modes.patch b/debian/patches/user-group-modes.patch new file mode 100644 index 0000000..2e32f9d --- /dev/null +++ b/debian/patches/user-group-modes.patch @@ -0,0 +1,259 @@ +From c20ad02ad58a523c6f4974e1ca124e71b7b801b1 Mon Sep 17 00:00:00 2001 +From: Colin Watson <cjwatson@debian.org> +Date: Sun, 9 Feb 2014 16:09:58 +0000 +Subject: Allow harmless group-writability + +Allow secure files (~/.ssh/config, ~/.ssh/authorized_keys, etc.) to be +group-writable, provided that the group in question contains only the file's +owner. Rejected upstream for IMO incorrect reasons (e.g. a misunderstanding +about the contents of gr->gr_mem). Given that per-user groups and umask 002 +are the default setup in Debian (for good reasons - this makes operating in +setgid directories with other groups much easier), we need to permit this by +default. + +Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1060 +Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=314347 +Last-Update: 2013-09-14 + +Patch-Name: user-group-modes.patch +--- + auth-rhosts.c | 6 ++---- + auth.c | 9 +++----- + misc.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- + misc.h | 2 ++ + platform.c | 16 -------------- + readconf.c | 3 +-- + ssh.1 | 2 ++ + ssh_config.5 | 2 ++ + 8 files changed, 80 insertions(+), 29 deletions(-) + +diff --git a/auth-rhosts.c b/auth-rhosts.c +index ecf956f0..4dccd5e6 100644 +--- a/auth-rhosts.c ++++ b/auth-rhosts.c +@@ -261,8 +261,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname, + return 0; + } + if (options.strict_modes && +- ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || +- (st.st_mode & 022) != 0)) { ++ !secure_permissions(&st, pw->pw_uid)) { + logit("Rhosts authentication refused for %.100s: " + "bad ownership or modes for home directory.", pw->pw_name); + auth_debug_add("Rhosts authentication refused for %.100s: " +@@ -288,8 +287,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname, + * allowing access to their account by anyone. + */ + if (options.strict_modes && +- ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || +- (st.st_mode & 022) != 0)) { ++ !secure_permissions(&st, pw->pw_uid)) { + logit("Rhosts authentication refused for %.100s: bad modes for %.200s", + pw->pw_name, buf); + auth_debug_add("Bad file modes for %.200s", buf); +diff --git a/auth.c b/auth.c +index c6390687..90390724 100644 +--- a/auth.c ++++ b/auth.c +@@ -444,8 +444,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, + user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); + if (options.strict_modes && + (stat(user_hostfile, &st) == 0) && +- ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || +- (st.st_mode & 022) != 0)) { ++ !secure_permissions(&st, pw->pw_uid)) { + logit("Authentication refused for %.100s: " + "bad owner or modes for %.200s", + pw->pw_name, user_hostfile); +@@ -507,8 +506,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, + snprintf(err, errlen, "%s is not a regular file", buf); + return -1; + } +- if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || +- (stp->st_mode & 022) != 0) { ++ if (!secure_permissions(stp, uid)) { + snprintf(err, errlen, "bad ownership or modes for file %s", + buf); + return -1; +@@ -523,8 +521,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, + strlcpy(buf, cp, sizeof(buf)); + + if (stat(buf, &st) < 0 || +- (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || +- (st.st_mode & 022) != 0) { ++ !secure_permissions(&st, uid)) { + snprintf(err, errlen, + "bad ownership or modes for directory %s", buf); + return -1; +diff --git a/misc.c b/misc.c +index 65c9222a..bf9153a6 100644 +--- a/misc.c ++++ b/misc.c +@@ -51,8 +51,9 @@ + #include <netdb.h> + #ifdef HAVE_PATHS_H + # include <paths.h> +-#include <pwd.h> + #endif ++#include <pwd.h> ++#include <grp.h> + #ifdef SSH_TUN_OPENBSD + #include <net/if.h> + #endif +@@ -61,6 +62,7 @@ + #include "misc.h" + #include "log.h" + #include "ssh.h" ++#include "platform.h" + + /* remove newline at end of string */ + char * +@@ -708,6 +710,71 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, + return -1; + } + ++/* ++ * return 1 if the specified uid is a uid that may own a system directory ++ * otherwise 0. ++ */ ++int ++platform_sys_dir_uid(uid_t uid) ++{ ++ if (uid == 0) ++ return 1; ++#ifdef PLATFORM_SYS_DIR_UID ++ if (uid == PLATFORM_SYS_DIR_UID) ++ return 1; ++#endif ++ return 0; ++} ++ ++int ++secure_permissions(struct stat *st, uid_t uid) ++{ ++ if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid) ++ return 0; ++ if ((st->st_mode & 002) != 0) ++ return 0; ++ if ((st->st_mode & 020) != 0) { ++ /* If the file is group-writable, the group in question must ++ * have exactly one member, namely the file's owner. ++ * (Zero-member groups are typically used by setgid ++ * binaries, and are unlikely to be suitable.) ++ */ ++ struct passwd *pw; ++ struct group *gr; ++ int members = 0; ++ ++ gr = getgrgid(st->st_gid); ++ if (!gr) ++ return 0; ++ ++ /* Check primary group memberships. */ ++ while ((pw = getpwent()) != NULL) { ++ if (pw->pw_gid == gr->gr_gid) { ++ ++members; ++ if (pw->pw_uid != uid) ++ return 0; ++ } ++ } ++ endpwent(); ++ ++ pw = getpwuid(st->st_uid); ++ if (!pw) ++ return 0; ++ ++ /* Check supplementary group memberships. */ ++ if (gr->gr_mem[0]) { ++ ++members; ++ if (strcmp(pw->pw_name, gr->gr_mem[0]) || ++ gr->gr_mem[1]) ++ return 0; ++ } ++ ++ if (!members) ++ return 0; ++ } ++ return 1; ++} ++ + int + tun_open(int tun, int mode) + { +diff --git a/misc.h b/misc.h +index c242f901..8b223b55 100644 +--- a/misc.h ++++ b/misc.h +@@ -143,6 +143,8 @@ char *read_passphrase(const char *, int); + int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); + int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); + ++int secure_permissions(struct stat *st, uid_t uid); ++ + #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) + #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) + #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) +diff --git a/platform.c b/platform.c +index cd7bf566..380ee3a4 100644 +--- a/platform.c ++++ b/platform.c +@@ -197,19 +197,3 @@ platform_krb5_get_principal_name(const char *pw_name) + return NULL; + #endif + } +- +-/* +- * return 1 if the specified uid is a uid that may own a system directory +- * otherwise 0. +- */ +-int +-platform_sys_dir_uid(uid_t uid) +-{ +- if (uid == 0) +- return 1; +-#ifdef PLATFORM_SYS_DIR_UID +- if (uid == PLATFORM_SYS_DIR_UID) +- return 1; +-#endif +- return 0; +-} +diff --git a/readconf.c b/readconf.c +index 3efba242..c02cdf63 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -1752,8 +1752,7 @@ read_config_file_depth(const char *filename, struct passwd *pw, + + if (fstat(fileno(f), &sb) == -1) + fatal("fstat %s: %s", filename, strerror(errno)); +- if (((sb.st_uid != 0 && sb.st_uid != getuid()) || +- (sb.st_mode & 022) != 0)) ++ if (!secure_permissions(&sb, getuid())) + fatal("Bad owner or permissions on %s", filename); + } + +diff --git a/ssh.1 b/ssh.1 +index 4011c65a..feef81a5 100644 +--- a/ssh.1 ++++ b/ssh.1 +@@ -1484,6 +1484,8 @@ The file format and configuration options are described in + .Xr ssh_config 5 . + Because of the potential for abuse, this file must have strict permissions: + read/write for the user, and not writable by others. ++It may be group-writable provided that the group in question contains only ++the user. + .Pp + .It Pa ~/.ssh/environment + Contains additional definitions for environment variables; see +diff --git a/ssh_config.5 b/ssh_config.5 +index a0457314..0483a1ee 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -1803,6 +1803,8 @@ The format of this file is described above. + This file is used by the SSH client. + Because of the potential for abuse, this file must have strict permissions: + read/write for the user, and not accessible by others. ++It may be group-writable provided that the group in question contains only ++the user. + .It Pa /etc/ssh/ssh_config + Systemwide configuration file. + This file provides defaults for those |