diff options
author | Christoph Gysin | 2020-03-10 22:56:23 +0200 |
---|---|---|
committer | Christoph Gysin | 2020-03-10 23:09:27 +0200 |
commit | 18e5acb5b4852f63ffca7e10e32eb9cfd26de0f1 (patch) | |
tree | 17602aa721228475a0d31442bfaf3e848cc0debd | |
parent | d5b1a075ad110ee2c23ef20d4f6c8467add450ae (diff) | |
download | aur-18e5acb5b4852f63ffca7e10e32eb9cfd26de0f1.tar.gz |
update to 8.2p1
-rw-r--r-- | .SRCINFO | 46 | ||||
-rw-r--r-- | PKGBUILD | 63 | ||||
-rw-r--r-- | get_canonical_hostname.patch | 388 | ||||
-rw-r--r-- | glibc-2.31.patch | 100 | ||||
-rw-r--r-- | gssapi-openssl-1.1.0.patch | 178 | ||||
-rw-r--r-- | gssapi.patch | 2995 | ||||
-rw-r--r-- | install | 32 | ||||
-rw-r--r-- | openssh-gssapi-debian.patch | 3877 | ||||
-rw-r--r-- | openssl-1.1.0.patch | 2217 | ||||
-rw-r--r-- | sshd.service | 3 | ||||
-rw-r--r-- | sshd.socket | 10 | ||||
-rw-r--r-- | sshd@.service | 8 | ||||
-rw-r--r-- | sshdgenkeys.service | 6 |
13 files changed, 4052 insertions, 5871 deletions
@@ -1,49 +1,43 @@ -# Generated by mksrcinfo v8 -# Wed Sep 27 21:10:46 UTC 2017 pkgbase = openssh-gssapi - pkgdesc = Free version of the SSH connectivity tools - pkgver = 7.5p1 - pkgrel = 3 + pkgdesc = Premier connectivity tool for remote login with the SSH protocol + pkgver = 8.2p1 + pkgrel = 1 url = https://www.openssh.com/portable.html - arch = i686 + install = install arch = x86_64 license = custom:BSD makedepends = linux-headers + makedepends = git + makedepends = libfido2 depends = krb5 depends = openssl depends = libedit depends = ldns optdepends = xorg-xauth: X11 forwarding optdepends = x11-ssh-askpass: input passphrase in X + optdepends = libfido2: FIDO/U2F support provides = openssh conflicts = openssh backup = etc/ssh/ssh_config backup = etc/ssh/sshd_config backup = etc/pam.d/sshd - source = https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-7.5p1.tar.gz - source = https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-7.5p1.tar.gz.asc + source = https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-8.2p1.tar.gz + source = https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-8.2p1.tar.gz.asc source = sshdgenkeys.service - source = sshd@.service source = sshd.service - source = sshd.socket source = sshd.conf source = sshd.pam - source = openssl-1.1.0.patch - source = get_canonical_hostname.patch - source = gssapi.patch - source = gssapi-openssl-1.1.0.patch - sha1sums = 5e8f185d00afb4f4f89801e9b0f8b9cee9d87ebd - sha1sums = SKIP - sha1sums = cc1ceec606c98c7407e7ac21ade23aed81e31405 - sha1sums = 6a0ff3305692cf83aca96e10f3bb51e1c26fccda - sha1sums = ec49c6beba923e201505f5669cea48cad29014db - sha1sums = e12fa910b26a5634e5a6ac39ce1399a132cf6796 - sha1sums = c9b2e4ce259cd62ddb00364d3ee6f00a8bf2d05f - sha1sums = d93dca5ebda4610ff7647187f8928a3de28703f3 - sha1sums = 6d9ea19bb4fa2e4b5f14cad331f36bfbdaafd067 - sha1sums = 16a3dc0ddcffbcfb7b166dc5839cee6536597c8e - sha1sums = 1f835864ef2a64d919e57c8337f711a1b9442af4 - sha1sums = 445edf93cd3f66294d6465a95ee32437ac1d4c56 + source = glibc-2.31.patch + source = openssh-gssapi-debian.patch + validpgpkeys = 59C2118ED206D927E667EBE3D3E5F56B6D920D30 + sha256sums = 43925151e6cf6cee1450190c0e9af4dc36b41c12737619edff8bcebdff64e671 + sha256sums = SKIP + sha256sums = 4031577db6416fcbaacf8a26a024ecd3939e5c10fe6a86ee3f0eea5093d533b7 + sha256sums = e40f8b7c8e5e2ecf3084b3511a6c36d5b5c9f9e61f2bb13e3726c71dc7d4fbc7 + sha256sums = 4effac1186cc62617f44385415103021f72f674f8b8e26447fc1139c670090f6 + sha256sums = 64576021515c0a98b0aaf0a0ae02e0f5ebe8ee525b1e647ab68f369f81ecd846 + sha256sums = 25b4a4d9e2d9d3289ef30636a30e85fa1c71dd930d5efd712cca1a01a5019f93 + sha256sums = 9c3e3fb636809b8973db971d89fca0d347b4558ee447c111d6a66199aadeb7e9 pkgname = openssh-gssapi @@ -1,4 +1,3 @@ -# $Id$ # Maintainer: Gaetan Bisson <bisson@archlinux.org> # Contributor: Aaron Griffin <aaron@archlinux.org> # Contributor: judd <jvinet@zeroflux.org> @@ -6,55 +5,45 @@ pkgname=openssh-gssapi _pkgname=openssh -pkgver=7.5p1 -pkgrel=3 -pkgdesc='Free version of the SSH connectivity tools' +pkgver=8.2p1 +pkgrel=1 +pkgdesc='Premier connectivity tool for remote login with the SSH protocol' url='https://www.openssh.com/portable.html' license=('custom:BSD') -arch=('i686' 'x86_64') -makedepends=('linux-headers') conflicts=(${_pkgname}) provides=(${_pkgname}) +arch=('x86_64') +makedepends=('linux-headers' 'git' 'libfido2') depends=('krb5' 'openssl' 'libedit' 'ldns') optdepends=('xorg-xauth: X11 forwarding' - 'x11-ssh-askpass: input passphrase in X') + 'x11-ssh-askpass: input passphrase in X' + 'libfido2: FIDO/U2F support') validpgpkeys=('59C2118ED206D927E667EBE3D3E5F56B6D920D30') source=("https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/${_pkgname}-${pkgver}.tar.gz"{,.asc} 'sshdgenkeys.service' - 'sshd@.service' 'sshd.service' - 'sshd.socket' 'sshd.conf' 'sshd.pam' - 'openssl-1.1.0.patch' - 'get_canonical_hostname.patch' - 'gssapi.patch' - 'gssapi-openssl-1.1.0.patch') -sha1sums=('5e8f185d00afb4f4f89801e9b0f8b9cee9d87ebd' - 'SKIP' - 'cc1ceec606c98c7407e7ac21ade23aed81e31405' - '6a0ff3305692cf83aca96e10f3bb51e1c26fccda' - 'ec49c6beba923e201505f5669cea48cad29014db' - 'e12fa910b26a5634e5a6ac39ce1399a132cf6796' - 'c9b2e4ce259cd62ddb00364d3ee6f00a8bf2d05f' - 'd93dca5ebda4610ff7647187f8928a3de28703f3' - '6d9ea19bb4fa2e4b5f14cad331f36bfbdaafd067' - '16a3dc0ddcffbcfb7b166dc5839cee6536597c8e' - '1f835864ef2a64d919e57c8337f711a1b9442af4' - '445edf93cd3f66294d6465a95ee32437ac1d4c56') + 'glibc-2.31.patch' + 'openssh-gssapi-debian.patch') +sha256sums=('43925151e6cf6cee1450190c0e9af4dc36b41c12737619edff8bcebdff64e671' + 'SKIP' + '4031577db6416fcbaacf8a26a024ecd3939e5c10fe6a86ee3f0eea5093d533b7' + 'e40f8b7c8e5e2ecf3084b3511a6c36d5b5c9f9e61f2bb13e3726c71dc7d4fbc7' + '4effac1186cc62617f44385415103021f72f674f8b8e26447fc1139c670090f6' + '64576021515c0a98b0aaf0a0ae02e0f5ebe8ee525b1e647ab68f369f81ecd846' + '25b4a4d9e2d9d3289ef30636a30e85fa1c71dd930d5efd712cca1a01a5019f93' + '10441cbd6116b45f409afd95aa0b682b1ed389c771996a8d9eb117633131f6fa') backup=('etc/ssh/ssh_config' 'etc/ssh/sshd_config' 'etc/pam.d/sshd') +install=install + prepare() { cd "${srcdir}/${_pkgname}-${pkgver}" - - # OpenSSL 1.1.0 patch from http://vega.pgw.jp/~kabe/vsd/patch/openssh-7.4p1-openssl-1.1.0c.patch.html - patch -Np1 -i ../openssl-1.1.0.patch - - # GSSAPI patches - patch -Np1 -i ../get_canonical_hostname.patch - patch -Np1 -i ../gssapi.patch - patch -Np1 -i ../gssapi-openssl-1.1.0.patch + patch -p1 -i ../glibc-2.31.patch + patch -p1 -i ../openssh-gssapi-debian.patch + autoreconf } build() { @@ -65,17 +54,19 @@ build() { --sbindir=/usr/bin \ --libexecdir=/usr/lib/ssh \ --sysconfdir=/etc/ssh \ + --disable-strip \ --with-ldns \ --with-libedit \ + --with-security-key-builtin \ --with-ssl-engine \ --with-pam \ --with-privsep-user=nobody \ --with-kerberos5=/usr \ --with-xauth=/usr/bin/xauth \ - --with-mantype=man \ --with-md5-passwords \ --with-pid-dir=/run \ - --with-gssapi + --with-default-path='/usr/local/sbin:/usr/local/bin:/usr/bin' \ + --with-gssapi \ make } @@ -100,9 +91,7 @@ package() { install -Dm644 LICENCE "${pkgdir}/usr/share/licenses/${_pkgname}/LICENCE" install -Dm644 ../sshdgenkeys.service "${pkgdir}"/usr/lib/systemd/system/sshdgenkeys.service - install -Dm644 ../sshd@.service "${pkgdir}"/usr/lib/systemd/system/sshd@.service install -Dm644 ../sshd.service "${pkgdir}"/usr/lib/systemd/system/sshd.service - install -Dm644 ../sshd.socket "${pkgdir}"/usr/lib/systemd/system/sshd.socket install -Dm644 ../sshd.conf "${pkgdir}"/usr/lib/tmpfiles.d/sshd.conf install -Dm644 ../sshd.pam "${pkgdir}"/etc/pam.d/sshd diff --git a/get_canonical_hostname.patch b/get_canonical_hostname.patch deleted file mode 100644 index bc9697a2a2b1..000000000000 --- a/get_canonical_hostname.patch +++ /dev/null @@ -1,388 +0,0 @@ -commit 75a930fc820eb0ab50b53824a68aea0e321e2536 -Author: Christoph Gysin <christoph.gysin@gmail.com> -Date: Mon Sep 25 23:40:36 2017 +0300 - - move get_canonical_hostname back to canohost - -diff --git a/auth-options.c b/auth-options.c -index 57b49f7..fbc2af2 100644 ---- a/auth-options.c -+++ b/auth-options.c -@@ -276,7 +276,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) - cp = "from=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - const char *remote_ip = ssh_remote_ipaddr(ssh); -- const char *remote_host = auth_get_canonical_hostname( -+ const char *remote_host = get_canonical_hostname( - ssh, options.use_dns); - char *patterns = xmalloc(strlen(opts) + 1); - -diff --git a/auth-pam.c b/auth-pam.c -index bc8e5e0..1aa4183 100644 ---- a/auth-pam.c -+++ b/auth-pam.c -@@ -643,7 +643,7 @@ sshpam_init(Authctxt *authctxt) - sshpam_handle = NULL; - return (-1); - } -- pam_rhost = auth_get_canonical_hostname(ssh, options.use_dns); -+ pam_rhost = get_canonical_hostname(ssh, options.use_dns); - debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost); - sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost); - if (sshpam_err != PAM_SUCCESS) { -@@ -801,7 +801,7 @@ sshpam_query(void *ctx, char **name, char **info, - error("PAM: %s for %s%.100s from %.100s", msg, - sshpam_authctxt->valid ? "" : "illegal user ", - sshpam_authctxt->user, -- auth_get_canonical_hostname(ssh, options.use_dns)); -+ get_canonical_hostname(ssh, options.use_dns)); - /* FALLTHROUGH */ - default: - *num = 0; -diff --git a/auth.c b/auth.c -index 6ee6116..3cd12e6 100644 ---- a/auth.c -+++ b/auth.c -@@ -186,7 +186,7 @@ allowed_user(struct passwd * pw) - - if (options.num_deny_users > 0 || options.num_allow_users > 0 || - options.num_deny_groups > 0 || options.num_allow_groups > 0) { -- hostname = auth_get_canonical_hostname(ssh, options.use_dns); -+ hostname = get_canonical_hostname(ssh, options.use_dns); - ipaddr = ssh_remote_ipaddr(ssh); - } - -@@ -328,11 +328,11 @@ auth_log(Authctxt *authctxt, int authenticated, int partial, - strncmp(method, "keyboard-interactive", 20) == 0 || - strcmp(method, "challenge-response") == 0)) - record_failed_login(authctxt->user, -- auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); -+ get_canonical_hostname(ssh, options.use_dns), "ssh"); - # ifdef WITH_AIXAUTHENTICATE - if (authenticated) - sys_auth_record_login(authctxt->user, -- auth_get_canonical_hostname(ssh, options.use_dns), "ssh", -+ get_canonical_hostname(ssh, options.use_dns), "ssh", - &loginmsg); - # endif - #endif -@@ -663,7 +663,7 @@ getpwnamallow(const char *user) - user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); - #ifdef CUSTOM_FAILED_LOGIN - record_failed_login(user, -- auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); -+ get_canonical_hostname(ssh, options.use_dns), "ssh"); - #endif - #ifdef SSH_AUDIT_EVENTS - audit_event(SSH_INVALID_USER); -@@ -793,117 +793,3 @@ fakepw(void) - - return (&fake); - } -- --/* -- * 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. -- */ -- --const char * --auth_get_canonical_hostname(struct ssh *ssh, int use_dns) --{ -- static char *dnsname; -- -- if (!use_dns) -- return ssh_remote_ipaddr(ssh); -- else if (dnsname != NULL) -- return dnsname; -- else { -- dnsname = remote_hostname(ssh); -- return dnsname; -- } --} -diff --git a/auth.h b/auth.h -index 338a62d..17eb9d7 100644 ---- a/auth.h -+++ b/auth.h -@@ -184,8 +184,6 @@ FILE *auth_openkeyfile(const char *, struct passwd *, int); - FILE *auth_openprincipals(const char *, struct passwd *, int); - int auth_key_is_revoked(Key *); - --const char *auth_get_canonical_hostname(struct ssh *, int); -- - HostStatus - check_key_in_hostfiles(struct passwd *, Key *, const char *, - const char *, const char *); -diff --git a/auth2-hostbased.c b/auth2-hostbased.c -index 1b3c3b2..e6d3e3d 100644 ---- a/auth2-hostbased.c -+++ b/auth2-hostbased.c -@@ -169,7 +169,7 @@ hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, - if (auth_key_is_revoked(key)) - return 0; - -- resolvedname = auth_get_canonical_hostname(ssh, options.use_dns); -+ resolvedname = get_canonical_hostname(ssh, options.use_dns); - ipaddr = ssh_remote_ipaddr(ssh); - - debug2("%s: chost %s resolvedname %s ipaddr %s", __func__, -diff --git a/canohost.c b/canohost.c -index f71a085..2d94025 100644 ---- a/canohost.c -+++ b/canohost.c -@@ -202,3 +202,117 @@ get_local_port(int sock) - { - return get_sock_port(sock, 1); - } -+ -+/* -+ * 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. -+ */ -+ -+const char * -+get_canonical_hostname(struct ssh *ssh, int use_dns) -+{ -+ static char *dnsname; -+ -+ if (!use_dns) -+ return ssh_remote_ipaddr(ssh); -+ else if (dnsname != NULL) -+ return dnsname; -+ else { -+ dnsname = remote_hostname(ssh); -+ return dnsname; -+ } -+} -diff --git a/canohost.h b/canohost.h -index 26d6285..626d708 100644 ---- a/canohost.h -+++ b/canohost.h -@@ -15,6 +15,8 @@ - #ifndef _CANOHOST_H - #define _CANOHOST_H - -+struct ssh; -+ - char *get_peer_ipaddr(int); - int get_peer_port(int); - char *get_local_ipaddr(int); -@@ -24,3 +26,5 @@ int get_local_port(int); - #endif /* _CANOHOST_H */ - - void ipv64_normalise_mapped(struct sockaddr_storage *, socklen_t *); -+ -+const char *get_canonical_hostname(struct ssh *, int); -diff --git a/servconf.c b/servconf.c -index 56b8316..0955ef7 100644 ---- a/servconf.c -+++ b/servconf.c -@@ -700,7 +700,7 @@ get_connection_info(int populate, int use_dns) - - if (!populate) - return &ci; -- ci.host = auth_get_canonical_hostname(ssh, use_dns); -+ ci.host = get_canonical_hostname(ssh, use_dns); - ci.address = ssh_remote_ipaddr(ssh); - ci.laddress = ssh_local_ipaddr(ssh); - ci.lport = ssh_local_port(ssh); -diff --git a/session.c b/session.c -index a08aa69..5aa6f4b 100644 ---- a/session.c -+++ b/session.c -@@ -2572,7 +2572,7 @@ session_get_remote_name_or_ip(struct ssh *ssh, u_int utmp_size, int use_dns) - const char *remote = ""; - - if (utmp_size > 0) -- remote = auth_get_canonical_hostname(ssh, use_dns); -+ remote = get_canonical_hostname(ssh, use_dns); - if (utmp_size == 0 || strlen(remote) > utmp_size) - remote = ssh_remote_ipaddr(ssh); - return remote; diff --git a/glibc-2.31.patch b/glibc-2.31.patch new file mode 100644 index 000000000000..187042870deb --- /dev/null +++ b/glibc-2.31.patch @@ -0,0 +1,100 @@ +From beee0ef61866cb567b9abc23bd850f922e59e3f0 Mon Sep 17 00:00:00 2001 +From: Darren Tucker <dtucker@dtucker.net> +Date: Wed, 13 Nov 2019 23:19:35 +1100 +Subject: [PATCH] seccomp: Allow clock_nanosleep() in sandbox. + +seccomp: Allow clock_nanosleep() to make OpenSSH working with latest +glibc. Patch from Jakub Jelen <jjelen@redhat.com> via bz #3093. +--- + sandbox-seccomp-filter.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c +index b5cda70bb..96ab141f7 100644 +--- a/sandbox-seccomp-filter.c ++++ b/sandbox-seccomp-filter.c +@@ -242,6 +242,12 @@ static const struct sock_filter preauth_insns[] = { + #ifdef __NR_nanosleep + SC_ALLOW(__NR_nanosleep), + #endif ++#ifdef __NR_clock_nanosleep ++ SC_ALLOW(__NR_clock_nanosleep), ++#endif ++#ifdef __NR_clock_nanosleep ++ SC_ALLOW(__NR_clock_nanosleep), ++#endif + #ifdef __NR__newselect + SC_ALLOW(__NR__newselect), + #endif +From 69298ebfc2c066acee5d187eac8ce9f38c796630 Mon Sep 17 00:00:00 2001 +From: Darren Tucker <dtucker@dtucker.net> +Date: Wed, 13 Nov 2019 23:27:31 +1100 +Subject: [PATCH] Remove duplicate __NR_clock_nanosleep + +--- + sandbox-seccomp-filter.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c +index 96ab141f7..be2397671 100644 +--- a/sandbox-seccomp-filter.c ++++ b/sandbox-seccomp-filter.c +@@ -245,9 +245,6 @@ static const struct sock_filter preauth_insns[] = { + #ifdef __NR_clock_nanosleep + SC_ALLOW(__NR_clock_nanosleep), + #endif +-#ifdef __NR_clock_nanosleep +- SC_ALLOW(__NR_clock_nanosleep), +-#endif + #ifdef __NR__newselect + SC_ALLOW(__NR__newselect), + #endif +From 030b4c2b8029563bc8a9fd764288fde08fa2347c Mon Sep 17 00:00:00 2001 +From: Darren Tucker <dtucker@dtucker.net> +Date: Mon, 16 Dec 2019 13:55:56 +1100 +Subject: [PATCH] Allow clock_nanosleep_time64 in seccomp sandbox. + +Needed on Linux ARM. bz#3100, patch from jjelen@redhat.com. +--- + sandbox-seccomp-filter.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c +index be2397671..3ef30c9d5 100644 +--- a/sandbox-seccomp-filter.c ++++ b/sandbox-seccomp-filter.c +@@ -245,6 +245,9 @@ static const struct sock_filter preauth_insns[] = { + #ifdef __NR_clock_nanosleep + SC_ALLOW(__NR_clock_nanosleep), + #endif ++#ifdef __NR_clock_nanosleep_time64 ++ SC_ALLOW(__NR_clock_nanosleep_time64), ++#endif + #ifdef __NR__newselect + SC_ALLOW(__NR__newselect), + #endif +From a991cc5ed5a7c455fefe909a30cf082011ef5dff Mon Sep 17 00:00:00 2001 +From: Khem Raj <raj.khem@gmail.com> +Date: Tue, 7 Jan 2020 16:26:45 -0800 +Subject: [PATCH] seccomp: Allow clock_gettime64() in sandbox. + +This helps sshd accept connections on mips platforms with +upcoming glibc ( 2.31 ) +--- + sandbox-seccomp-filter.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c +index 3ef30c9d5..999c46c9f 100644 +--- a/sandbox-seccomp-filter.c ++++ b/sandbox-seccomp-filter.c +@@ -248,6 +248,9 @@ static const struct sock_filter preauth_insns[] = { + #ifdef __NR_clock_nanosleep_time64 + SC_ALLOW(__NR_clock_nanosleep_time64), + #endif ++#ifdef __NR_clock_gettime64 ++ SC_ALLOW(__NR_clock_gettime64), ++#endif + #ifdef __NR__newselect + SC_ALLOW(__NR__newselect), + #endif diff --git a/gssapi-openssl-1.1.0.patch b/gssapi-openssl-1.1.0.patch deleted file mode 100644 index 31981f1f19b5..000000000000 --- a/gssapi-openssl-1.1.0.patch +++ /dev/null @@ -1,178 +0,0 @@ -commit 62325de6d29b58db4e2c5d1c63547714d774e625 -Author: Christoph Gysin <christoph.gysin@gmail.com> -Date: Tue Sep 26 20:01:13 2017 +0300 - - gssapi openssl-1.1 - -diff --git a/gss-genr.c b/gss-genr.c -index 0a929bc..a091200 100644 ---- a/gss-genr.c -+++ b/gss-genr.c -@@ -98,7 +98,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, - u_char digest[EVP_MAX_MD_SIZE]; - char deroid[2]; - const EVP_MD *evp_md = EVP_md5(); -- EVP_MD_CTX md; -+ EVP_MD_CTX *md = EVP_MD_CTX_new(); - - if (gss_enc2oid != NULL) { - for (i = 0; gss_enc2oid[i].encoded != NULL; i++) -@@ -119,12 +119,12 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, - 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, -+ 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); -+ EVP_DigestFinal(md, digest, NULL); - - encoded = xmalloc(EVP_MD_size(evp_md) * 2); - enclen = __b64_ntop(digest, EVP_MD_size(evp_md), -@@ -164,6 +164,8 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, - mechs = NULL; - } - -+ EVP_MD_CTX_free(md); -+ - return (mechs); - } - -diff --git a/kexgssc.c b/kexgssc.c -index 4708fd0..172b14a 100644 ---- a/kexgssc.c -+++ b/kexgssc.c -@@ -56,8 +56,9 @@ kexgss_client(struct ssh *ssh) { - DH *dh; - BIGNUM *dh_server_pub = NULL; - BIGNUM *shared_secret = NULL; -- BIGNUM *p = NULL; -- BIGNUM *g = NULL; -+ const BIGNUM *p = NULL; -+ const BIGNUM *g = NULL; -+ const BIGNUM *pub_key = NULL; - u_char *kbuf; - u_char *serverhostkey = NULL; - u_char *empty = ""; -@@ -167,7 +168,8 @@ kexgss_client(struct ssh *ssh) { - packet_start(SSH2_MSG_KEXGSS_INIT); - packet_put_string(send_tok.value, - send_tok.length); -- packet_put_bignum2(dh->pub_key); -+ DH_get0_key(dh, &pub_key, NULL); -+ packet_put_bignum2(pub_key); - first = 0; - } else { - packet_start(SSH2_MSG_KEXGSS_CONTINUE); -@@ -269,6 +271,7 @@ kexgss_client(struct ssh *ssh) { - switch (ssh->kex->kex_type) { - case KEX_GSS_GRP1_SHA1: - case KEX_GSS_GRP14_SHA1: -+ DH_get0_key(dh, &pub_key, NULL); - kex_dh_hash( - ssh->kex->hash_alg, - ssh->kex->client_version_string, -@@ -276,13 +279,15 @@ kexgss_client(struct ssh *ssh) { - 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 */ -+ pub_key, /* e */ - dh_server_pub, /* f */ - shared_secret, /* K */ - hash, &hashlen - ); - break; - case KEX_GSS_GEX_SHA1: -+ DH_get0_pqg(dh, &p, NULL, &g); -+ DH_get0_key(dh, &pub_key, NULL); - kexgex_hash( - ssh->kex->hash_alg, - ssh->kex->client_version_string, -@@ -291,8 +296,8 @@ kexgss_client(struct ssh *ssh) { - buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), - (serverhostkey ? serverhostkey : empty), slen, - min, nbits, max, -- dh->p, dh->g, -- dh->pub_key, -+ p, g, -+ pub_key, - dh_server_pub, - shared_secret, - hash, &hashlen -diff --git a/kexgsss.c b/kexgsss.c -index cfb8680..922c4ec 100644 ---- a/kexgsss.c -+++ b/kexgsss.c -@@ -70,6 +70,9 @@ kexgss_server(struct ssh *ssh) - int min = -1, max = -1, nbits = -1; - BIGNUM *shared_secret = NULL; - BIGNUM *dh_client_pub = NULL; -+ const BIGNUM *p = NULL; -+ const BIGNUM *g = NULL; -+ const BIGNUM *pub_key = NULL; - int type = 0; - gss_OID oid; - char *mechs; -@@ -121,8 +124,9 @@ kexgss_server(struct ssh *ssh) - packet_disconnect("Protocol error: no matching group found"); - - packet_start(SSH2_MSG_KEXGSS_GROUP); -- packet_put_bignum2(dh->p); -- packet_put_bignum2(dh->g); -+ DH_get0_pqg(dh, &p, NULL, &g); -+ packet_put_bignum2(p); -+ packet_put_bignum2(g); - packet_send(); - - packet_write_wait(); -@@ -218,6 +222,7 @@ kexgss_server(struct ssh *ssh) - switch (ssh->kex->kex_type) { - case KEX_GSS_GRP1_SHA1: - case KEX_GSS_GRP14_SHA1: -+ DH_get0_key(dh, &pub_key, NULL); - kex_dh_hash( - ssh->kex->hash_alg, - ssh->kex->client_version_string, -@@ -225,11 +230,13 @@ kexgss_server(struct ssh *ssh) - 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, -+ dh_client_pub, pub_key, shared_secret, - hash, &hashlen - ); - break; - case KEX_GSS_GEX_SHA1: -+ DH_get0_pqg(dh, &p, NULL, &g); -+ DH_get0_key(dh, &pub_key, NULL); - kexgex_hash( - ssh->kex->hash_alg, - ssh->kex->client_version_string, ssh->kex->server_version_string, -@@ -237,9 +244,9 @@ kexgss_server(struct ssh *ssh) - buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), - NULL, 0, - min, nbits, max, -- dh->p, dh->g, -+ p, g, - dh_client_pub, -- dh->pub_key, -+ pub_key, - shared_secret, - hash, &hashlen - ); -@@ -263,7 +270,8 @@ kexgss_server(struct ssh *ssh) - fatal("Couldn't get MIC"); - - packet_start(SSH2_MSG_KEXGSS_COMPLETE); -- packet_put_bignum2(dh->pub_key); -+ DH_get0_key(dh, &pub_key, NULL); -+ packet_put_bignum2(pub_key); - packet_put_string(msg_tok.value,msg_tok.length); - - if (send_tok.length != 0) { diff --git a/gssapi.patch b/gssapi.patch deleted file mode 100644 index 539301207d35..000000000000 --- a/gssapi.patch +++ /dev/null @@ -1,2995 +0,0 @@ -commit ef9608da67e2c6e2c1bb869c5ec4b3900fef8c16 -Author: Christoph Gysin <christoph.gysin@gmail.com> -Date: Wed Sep 27 15:49:41 2017 +0300 - - gssapi - -diff --git a/ChangeLog.gssapi b/ChangeLog.gssapi -new file mode 100644 -index 0000000..ec6f2ee ---- /dev/null -+++ b/ChangeLog.gssapi -@@ -0,0 +1,115 @@ -+20170925 -+ - Update to OpenSSH 7.5p1 -+ -+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 5870e9e..6b774c1 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 a5a81ed..1fd6190 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) -@@ -192,7 +197,7 @@ auth_krb5_password(Authctxt *authctxt, const char *password) - - out: - restore_uid(); -- -+ - free(platform_client); - - if (problem) { -@@ -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/auth2-gss.c b/auth2-gss.c -index 1ca8357..43a466f 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 97dd2ef..946e923 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/clientloop.c b/clientloop.c -index 0648162..57ed7a3 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; - -@@ -1667,6 +1671,14 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) - 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 b65420e..1820898 100644 ---- a/config.h.in -+++ b/config.h.in -@@ -1670,6 +1670,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 - -@@ -1688,6 +1691,9 @@ - /* Define if you have Solaris privileges */ - #undef USE_SOLARIS_PRIVS - -+/* platform has the Security Authorization Session API */ -+#undef USE_SECURITY_SESSION_API -+ - /* Define if you have Solaris process contracts */ - #undef USE_SOLARIS_PROCESS_CONTRACTS - -diff --git a/configure.ac b/configure.ac -index c2878e3..98b5ab5 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 62559ed..0a929bc 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); - -@@ -227,9 +382,43 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host) - return (ctx->major); - } - -+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,22 +452,31 @@ 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 && -+ if (oid->length == spnego_oid.length && - (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) - return 0; /* false */ - - 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, -+ major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, - NULL); - gss_release_buffer(&minor, &token); - if ((*ctx)->context != GSS_C_NO_CONTEXT) -@@ -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 795992d..93c38d0 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 53993d6..481e347 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; -@@ -141,6 +146,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) - return (ssh_gssapi_acquire_cred(*ctx)); - } - -+/* Unprivileged */ -+char * -+ssh_gssapi_server_mechanisms(void) { -+ gss_OID_set supported; -+ -+ ssh_gssapi_supported_oids(&supported); -+ return (ssh_gssapi_kex_mechs(supported, &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 +179,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 +307,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; - -- gss_buffer_desc ename; -+ 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; -+ } -+ -+ 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 +363,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 +387,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 +436,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 +446,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 +464,90 @@ ssh_gssapi_userok(char *user) - return (0); - } - --/* Privileged */ --OM_uint32 --ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) --{ -- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, -- gssbuf, gssmic, NULL); -+/* 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 -+ */ - -- return (ctx->major); -+/* 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) -+{ -+ return (PAM_CONV_ERR); -+} -+#endif -+ -+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 cf4ac0d..09f5d83 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; - } - -diff --git a/kex.h b/kex.h -index 3794f21..fd56171 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 0000000..4708fd0 ---- /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 0000000..cfb8680 ---- /dev/null -+++ b/kexgsss.c -@@ -0,0 +1,297 @@ -+/* -+ * 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(); -+ min = MAX(DH_GRP_MIN, min); -+ max = MIN(DH_GRP_MAX, max); -+ 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(min, nbits, 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 6252a78..cd909cd 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 -@@ -302,6 +311,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) { -@@ -402,6 +415,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); -@@ -1608,6 +1625,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; -@@ -1690,6 +1714,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m) - if (!options.gss_authentication) - fatal("%s: GSSAPI authentication not enabled", __func__); - -+ if (!options.gss_authentication && !options.gss_keyex) -+ fatal("In GSSAPI monitor when GSSAPI is disabled"); -+ - goid.elements = buffer_get_string(m, &len); - goid.length = len; - -@@ -1720,6 +1747,9 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m) - if (!options.gss_authentication) - fatal("%s: GSSAPI authentication not enabled", __func__); - -+ if (!options.gss_authentication && !options.gss_keyex) -+ fatal("In GSSAPI monitor when GSSAPI is disabled"); -+ - in.value = buffer_get_string(m, &len); - in.length = len; - major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); -@@ -1737,6 +1767,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); - } -@@ -1751,6 +1782,9 @@ mm_answer_gss_checkmic(int sock, Buffer *m) - if (!options.gss_authentication) - fatal("%s: GSSAPI authentication not enabled", __func__); - -+ if (!options.gss_authentication && !options.gss_keyex) -+ fatal("In GSSAPI monitor when GSSAPI is disabled"); -+ - gssbuf.value = buffer_get_string(m, &len); - gssbuf.length = len; - mic.value = buffer_get_string(m, &len); -@@ -1780,7 +1814,11 @@ mm_answer_gss_userok(int sock, Buffer *m) - if (!options.gss_authentication) - fatal("%s: GSSAPI authentication not enabled", __func__); - -- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); -+ if (!options.gss_authentication && !options.gss_keyex) -+ fatal("In GSSAPI monitor when GSSAPI is disabled"); -+ -+ authenticated = authctxt->valid && -+ ssh_gssapi_userok(authctxt->user, authctxt->pw); - - buffer_clear(m); - buffer_put_int(m, authenticated); -@@ -1793,5 +1831,73 @@ 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("In GSSAPI monitor when GSSAPI is disabled"); -+ -+ 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; -+ -+ 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 d68f674..ec41404 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 64ff928..d5cb640 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 db5902f..8f9dd89 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 9d59493..ea525c5 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, -@@ -196,10 +198,19 @@ static struct { - /* Sometimes-unsupported options */ - #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 - #ifdef ENABLE_PKCS11 - { "smartcarddevice", oPKCS11Provider }, -@@ -973,10 +984,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; -@@ -1798,7 +1829,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; -@@ -1942,8 +1978,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 cef55f7..fd3d7c7 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 56b8316..d796b7c 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 = 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 }, -@@ -1217,6 +1232,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; -@@ -1225,6 +1244,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; -@@ -2250,7 +2273,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 5853a97..90dfa4c 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 a99d7f0..d467229 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 90fb63f..4e879cd 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 532745b..90d820d 100644 ---- a/ssh_config.5 -+++ b/ssh_config.5 -@@ -752,10 +752,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 -+.Dq no . -+Note that this option applies to protocol version 2 only. -+.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 -+.Dq 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 -+.Dq no . -+.It Cm GSSAPITrustDns -+Set to -+.Dq yes to indicate that the DNS is trusted to securely canonicalize -+the name of the host being connected to. If -+.Dq no, the hostname entered on the -+command line will be passed untouched to the GSSAPI library. -+The default is -+.Dq no . -+This option only applies to protocol version 2 connections using GSSAPI. - .It Cm HashKnownHosts - Indicates that - .Xr ssh 1 -diff --git a/sshconnect2.c b/sshconnect2.c -index f8a54be..8ea9b3c 100644 ---- a/sshconnect2.c -+++ b/sshconnect2.c -@@ -161,10 +161,34 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) - char *s; - struct kex *kex; - int r; -+ struct ssh *ssh = active_state; -+ -+#ifdef GSSAPI -+ char *orig = NULL, *gss = NULL; -+ char *gss_host = NULL; -+#endif - - xxx_host = host; - xxx_hostaddr = hostaddr; - -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ /* Add the GSSAPI mechanisms currently supported on this -+ * client to the key exchange algorithm proposal */ -+ orig = options.kex_algorithms; -+ if (options.gss_trust_dns) -+ gss_host = (char *)get_canonical_hostname(ssh, 1); -+ else -+ gss_host = host; -+ -+ gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); -+ if (gss) { -+ debug("Offering GSSAPI proposal: %s", gss); -+ xasprintf(&options.kex_algorithms, "%s,%s", gss, orig); -+ } -+ } -+#endif -+ - if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) - fatal("%s: kex_names_cat", __func__); - myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); -@@ -192,6 +216,17 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) - order_hostkeyalgs(host, hostaddr, port)); - } - -+#ifdef GSSAPI -+ /* If we've got GSSAPI algorithms, then we also support the -+ * 'null' hostkey, as a last resort */ -+ if (options.gss_keyex && gss) { -+ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; -+ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], -+ "%s,null", orig); -+ free(gss); -+ } -+#endif -+ - if (options.rekey_limit || options.rekey_interval) - packet_set_rekey_limits(options.rekey_limit, - options.rekey_interval); -@@ -213,10 +248,30 @@ 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; -+ if (options.gss_server_identity) { -+ kex->gss_host = options.gss_server_identity; -+ } else { -+ kex->gss_host = gss_host; -+ } -+ } -+#endif -+ - dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); - - /* remove ext-info from the KEX proposals for rekeying */ -@@ -311,6 +366,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 +383,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,19 +713,32 @@ userauth_gssapi(Authctxt *authctxt) - static u_int mech = 0; - OM_uint32 min; - int ok = 0; -+ const char *gss_host; -+ struct ssh *ssh = active_state; -+ -+ if (options.gss_server_identity) -+ gss_host = options.gss_server_identity; -+ else if (options.gss_trust_dns) -+ gss_host = get_canonical_hostname(ssh, 1); -+ else -+ gss_host = 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; -+ 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)) { -+ ssh_gssapi_check_mechanism(&gssctxt, -+ &gss_supported->elements[mech], gss_host, -+ options.gss_client_identity)) { - ok = 1; /* Mechanism works */ - } else { - mech++; -@@ -761,8 +835,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 +949,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 010a2c3..b52c5d8 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) -@@ -1485,7 +1489,7 @@ main(int ac, char **av) - fprintf(stderr, "too many host keys.\n"); - exit(1); - } -- options.host_key_files[options.num_host_key_files++] = -+ options.host_key_files[options.num_host_key_files++] = - derelativise_path(optarg); - break; - case 't': -@@ -1719,10 +1723,14 @@ 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 -@@ -1992,6 +2000,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 -@@ -2173,6 +2235,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)); -@@ -2190,6 +2294,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 4eb2e02..c01dd65 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 ac6ccc7..ab35547 100644 ---- a/sshd_config.5 -+++ b/sshd_config.5 -@@ -627,6 +627,12 @@ 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 -+.Dq no . -+Note that this option applies to protocol version 2 only. - .It Cm GSSAPICleanupCredentials - Specifies whether to automatically destroy the user's credentials cache - on logout. -@@ -646,6 +652,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 -+.Dq 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 829bd04..9a58396 100644 ---- a/sshkey.c -+++ b/sshkey.c -@@ -116,6 +116,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 } - }; - -@@ -204,7 +205,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep) - const struct keytype *kt; - - for (kt = keytypes; kt->type != -1; kt++) { -- if (kt->name == NULL) -+ if (kt->name == NULL || kt->type == KEY_NULL) - continue; - if (!include_sigonly && kt->sigonly) - continue; -diff --git a/sshkey.h b/sshkey.h -index 1b9e42f..f91e4a0 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 - }; - @@ -1,10 +1,32 @@ -post_upgrade() { - if [[ $(vercmp $2 6.2p2) = -1 ]]; then +pre_upgrade() { + # Remove socket activation. See: https://bugs.archlinux.org/task/62248 + if (( $(vercmp $2 8.0p1-3) < 0 )); then + if systemctl is-enabled -q sshd.socket; then cat <<EOF +==> This package no longer provides sshd.socket and sshd@.service; +==> copies of those files will be placed under /etc/systemd/system +==> but please migrate to sshd.service whenever possible. +EOF + src=/usr/lib/systemd/system + dst=/etc/systemd/system + for i in sshd.socket sshd\@.service; do + if [[ ! -e "$dst/$i" ]]; then + cp -v "$src/$i" "$dst/$i" + fi + done + systemctl reenable sshd.socket + fi + fi +} -==> The sshd daemon has been moved to /usr/bin alongside all binaries. -==> Please update this path in your scripts if applicable. - +post_upgrade() { + if (( $(vercmp $2 8.2p1-3) < 0 )); then + if systemctl is-active sshd.service >/dev/null; then + cat <<EOF +==> After this upgrade, your existing SSH daemon may be unable to accept +==> new connections. To fix this, your SSH daemon will now be restarted. EOF + systemctl restart sshd.service + fi fi } diff --git a/openssh-gssapi-debian.patch b/openssh-gssapi-debian.patch new file mode 100644 index 000000000000..3bea8807e020 --- /dev/null +++ b/openssh-gssapi-debian.patch @@ -0,0 +1,3877 @@ +diff --git a/Makefile.in b/Makefile.in +index e7549470..b68c1710 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -109,6 +109,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kexgexc.o kexgexs.o \ + sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \ ++ kexgssc.o \ + sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \ + sshbuf-io.o + +@@ -125,7 +126,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.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.c b/auth.c +index 086b8ebb..687c57b4 100644 +--- a/auth.c ++++ b/auth.c +@@ -400,7 +400,8 @@ auth_root_allowed(struct ssh *ssh, 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: +@@ -724,99 +725,6 @@ fakepw(void) + return (&fake); + } + +-/* +- * 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) == -1) { +- debug("getpeername failed: %.100s", strerror(errno)); +- return xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(ntop); +- } +- return xstrdup(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 +diff --git a/auth2-gss.c b/auth2-gss.c +index 9351e042..d6446c0c 100644 +--- a/auth2-gss.c ++++ b/auth2-gss.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 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 +@@ -54,6 +54,48 @@ static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh); + static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); + static int input_gssapi_errtok(int, u_int32_t, struct ssh *); + ++/* ++ * The 'gssapi_keyex' userauth mechanism. ++ */ ++static int ++userauth_gsskeyex(struct ssh *ssh) ++{ ++ Authctxt *authctxt = ssh->authctxt; ++ int r, authenticated = 0; ++ struct sshbuf *b = NULL; ++ gss_buffer_desc mic, gssbuf; ++ u_char *p; ++ size_t len; ++ ++ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("%s: %s", __func__, ssh_err(r)); ++ ++ if ((b = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ mic.value = p; ++ mic.length = len; ++ ++ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, ++ "gssapi-keyex"); ++ ++ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) ++ fatal("%s: sshbuf_mutable_ptr failed", __func__); ++ gssbuf.length = sshbuf_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, 1)); ++ ++ sshbuf_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) +@@ -260,7 +302,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) + if ((r = sshpkt_get_end(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw, 1)); + + if ((!use_privsep || mm_is_monitor()) && + (displayname = ssh_gssapi_displayname()) != NULL) +@@ -306,7 +349,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) + gssbuf.length = sshbuf_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, 0)); + else + logit("GSSAPI MIC check failed"); + +@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) + 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 0e776224..1c217268 100644 +--- a/auth2.c ++++ b/auth2.c +@@ -73,6 +73,7 @@ extern Authmethod method_passwd; + extern Authmethod method_kbdint; + extern Authmethod method_hostbased; + #ifdef GSSAPI ++extern Authmethod method_gsskeyex; + extern Authmethod method_gssapi; + #endif + +@@ -80,6 +81,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 abea9c6e..8e81b519 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) == -1) { ++ debug("getpeername failed: %.100s", strerror(errno)); ++ return xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(ntop); ++ } ++ return xstrdup(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 ebd0dbca..1bdac6a4 100644 +--- a/clientloop.c ++++ b/clientloop.c +@@ -112,6 +112,10 @@ + #include "ssherr.h" + #include "hostfile.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* import options */ + extern Options options; + +@@ -1379,9 +1383,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, + break; + + /* Do channel operations unless rekeying in progress. */ +- if (!ssh_packet_is_rekeying(ssh)) ++ if (!ssh_packet_is_rekeying(ssh)) { + channel_after_select(ssh, 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(ssh, readset); + +diff --git a/configure.ac b/configure.ac +index b689db4b..efafb6bd 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -674,6 +674,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 d56257b4..763a63ff 100644 +--- a/gss-genr.c ++++ b/gss-genr.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 djm 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 +@@ -41,12 +41,36 @@ + #include "sshbuf.h" + #include "log.h" + #include "ssh2.h" ++#include "cipher.h" ++#include "sshkey.h" ++#include "kex.h" ++#include "digest.h" ++#include "packet.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); ++} ++ + /* sshbuf_get for gss_buffer_desc */ + int + ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) +@@ -62,6 +86,162 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) + return 0; + } + ++/* sshpkt_get of gss_buffer_desc */ ++int ++ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *ssh, gss_buffer_desc *g) ++{ ++ int r; ++ u_char *p; ++ size_t len; ++ ++ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0) ++ return r; ++ g->value = p; ++ g->length = len; ++ return 0; ++} ++ ++/* ++ * 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, ++ const char *kex) { ++ gss_OID_set gss_supported = NULL; ++ 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, kex); ++} ++ ++char * ++ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, ++ const char *host, const char *client, const char *kex) { ++ struct sshbuf *buf = NULL; ++ size_t i; ++ int r = SSH_ERR_ALLOC_FAIL; ++ int oidpos, enclen; ++ char *mechs, *encoded; ++ u_char digest[SSH_DIGEST_MAX_LENGTH]; ++ char deroid[2]; ++ struct ssh_digest_ctx *md = NULL; ++ char *s, *cp, *p; ++ ++ 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)); ++ ++ if ((buf = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ oidpos = 0; ++ s = cp = xstrdup(kex); ++ 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; ++ ++ if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || ++ (r = ssh_digest_update(md, deroid, 2)) != 0 || ++ (r = ssh_digest_update(md, ++ gss_supported->elements[i].elements, ++ gss_supported->elements[i].length)) != 0 || ++ (r = ssh_digest_final(md, digest, sizeof(digest))) != 0) ++ fatal("%s: digest failed: %s", __func__, ++ ssh_err(r)); ++ ssh_digest_free(md); ++ md = NULL; ++ ++ encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5) ++ * 2); ++ enclen = __b64_ntop(digest, ++ ssh_digest_bytes(SSH_DIGEST_MD5), encoded, ++ ssh_digest_bytes(SSH_DIGEST_MD5) * 2); ++ ++ cp = strncpy(s, kex, strlen(kex)); ++ for ((p = strsep(&cp, ",")); p && *p != '\0'; ++ (p = strsep(&cp, ","))) { ++ if (sshbuf_len(buf) != 0 && ++ (r = sshbuf_put_u8(buf, ',')) != 0) ++ fatal("%s: sshbuf_put_u8 error: %s", ++ __func__, ssh_err(r)); ++ if ((r = sshbuf_put(buf, p, strlen(p))) != 0 || ++ (r = sshbuf_put(buf, encoded, enclen)) != 0) ++ fatal("%s: sshbuf_put error: %s", ++ __func__, ssh_err(r)); ++ } ++ ++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); ++ gss_enc2oid[oidpos].encoded = encoded; ++ oidpos++; ++ } ++ } ++ free(s); ++ gss_enc2oid[oidpos].oid = NULL; ++ gss_enc2oid[oidpos].encoded = NULL; ++ ++ if ((mechs = sshbuf_dup_string(buf)) == NULL) ++ fatal("%s: sshbuf_dup_string failed", __func__); ++ ++ sshbuf_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; ++ ++#define SKIP_KEX_NAME(type) \ ++ case type: \ ++ if (strlen(name) < sizeof(type##_ID)) \ ++ return GSS_C_NO_OID; \ ++ name += sizeof(type##_ID) - 1; \ ++ break; ++ ++ switch (kex_type) { ++ SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1) ++ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1) ++ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256) ++ SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512) ++ SKIP_KEX_NAME(KEX_GSS_GEX_SHA1) ++ SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256) ++ SKIP_KEX_NAME(KEX_GSS_C25519_SHA256) ++ default: ++ return GSS_C_NO_OID; ++ } ++ ++#undef SKIP_KEX_NAME ++ ++ 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) +@@ -218,7 +398,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); + +@@ -247,9 +427,43 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host) + return (ctx->major); + } + ++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); +@@ -257,6 +471,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(struct sshbuf *b, const char *user, const char *service, + const char *context) +@@ -273,11 +500,16 @@ ssh_gssapi_buildmic(struct sshbuf *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 && +@@ -287,6 +519,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); +@@ -296,10 +532,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 a151bc1e..ef9beb67 100644 +--- a/gss-serv-krb5.c ++++ b/gss-serv-krb5.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 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 +@@ -120,8 +120,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"); +@@ -180,11 +180,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) +@@ -196,6 +201,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", +@@ -203,7 +273,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 ab3a15f0..1d47870e 100644 +--- a/gss-serv.c ++++ b/gss-serv.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus 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 +@@ -44,17 +44,19 @@ + #include "session.h" + #include "misc.h" + #include "servconf.h" ++#include "uidswap.h" + + #include "ssh-gss.h" ++#include "monitor_wrap.h" + + 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_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 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; +@@ -140,6 +142,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) + return (ssh_gssapi_acquire_cred(*ctx)); + } + ++/* 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, ++ options.gss_kex_algorithms)); ++} ++ ++/* 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) +@@ -150,7 +175,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, +@@ -276,8 +303,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; + +- gss_buffer_desc ename; ++ 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; ++ } ++ ++ 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; + +@@ -292,6 +359,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); +@@ -309,6 +383,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; +@@ -356,19 +432,23 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep) + + /* Privileged */ + int +-ssh_gssapi_userok(char *user) ++ssh_gssapi_userok(char *user, struct passwd *pw, int kex) + { + OM_uint32 lmin; + ++ (void) kex; /* used in privilege separation */ ++ + if (gssapi_client.exportedname.length == 0 || + gssapi_client.exportedname.value == NULL) { + debug("No suitable client data"); + 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); +@@ -382,14 +462,90 @@ ssh_gssapi_userok(char *user) + return (0); + } + +-/* Privileged */ +-OM_uint32 +-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) +-{ +- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, +- gssbuf, gssmic, NULL); ++/* 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 ++ */ + +- return (ctx->major); ++/* 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) ++{ ++ return (PAM_CONV_ERR); ++} ++#endif ++ ++void ++ssh_gssapi_rekey_creds(void) { ++ int ok; ++#ifdef USE_PAM ++ int ret; ++ 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; + } + + /* Privileged */ +diff --git a/kex.c b/kex.c +index ce85f043..574c7609 100644 +--- a/kex.c ++++ b/kex.c +@@ -57,11 +57,16 @@ + #include "misc.h" + #include "dispatch.h" + #include "monitor.h" ++#include "xmalloc.h" + + #include "ssherr.h" + #include "sshbuf.h" + #include "digest.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* prototype */ + static int kex_choose_conf(struct ssh *); + static int kex_input_newkeys(int, u_int32_t, struct ssh *); +@@ -115,15 +120,28 @@ static const struct kexalg kexalgs[] = { + #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ + { NULL, 0, -1, -1}, + }; ++static const struct kexalg gss_kexalgs[] = { ++#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 }, ++ { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, ++ { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, ++ { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256, ++ NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, ++ { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, ++#endif ++ { NULL, 0, -1, -1}, ++}; + +-char * +-kex_alg_list(char sep) ++static char * ++kex_alg_list_internal(char sep, const struct kexalg *algs) + { + char *ret = NULL, *tmp; + size_t nlen, rlen = 0; + const struct kexalg *k; + +- for (k = kexalgs; k->name != NULL; k++) { ++ for (k = algs; k->name != NULL; k++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(k->name); +@@ -138,6 +156,18 @@ kex_alg_list(char sep) + return ret; + } + ++char * ++kex_alg_list(char sep) ++{ ++ return kex_alg_list_internal(sep, kexalgs); ++} ++ ++char * ++kex_gss_alg_list(char sep) ++{ ++ return kex_alg_list_internal(sep, gss_kexalgs); ++} ++ + static const struct kexalg * + kex_alg_by_name(const char *name) + { +@@ -147,6 +177,10 @@ kex_alg_by_name(const char *name) + if (strcmp(k->name, name) == 0) + return k; + } ++ for (k = gss_kexalgs; k->name != NULL; k++) { ++ if (strncmp(k->name, name, strlen(k->name)) == 0) ++ return k; ++ } + return NULL; + } + +@@ -315,6 +349,29 @@ kex_assemble_names(char **listp, const char *def, const char *all) + return r; + } + ++/* Validate GSS KEX method name list */ ++int ++kex_gss_names_valid(const char *names) ++{ ++ char *s, *cp, *p; ++ ++ if (names == NULL || *names == '\0') ++ return 0; ++ s = cp = xstrdup(names); ++ for ((p = strsep(&cp, ",")); p && *p != '\0'; ++ (p = strsep(&cp, ","))) { ++ if (strncmp(p, "gss-", 4) != 0 ++ || kex_alg_by_name(p) == NULL) { ++ error("Unsupported KEX algorithm \"%.100s\"", p); ++ free(s); ++ return 0; ++ } ++ } ++ debug3("gss kex names ok: [%s]", names); ++ free(s); ++ return 1; ++} ++ + /* put algorithm proposal into buffer */ + int + kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) +@@ -698,6 +755,9 @@ kex_free(struct kex *kex) + sshbuf_free(kex->server_version); + sshbuf_free(kex->client_pub); + free(kex->session_id); ++#ifdef GSSAPI ++ free(kex->gss_host); ++#endif /* GSSAPI */ + free(kex->failed_choice); + free(kex->hostkey_alg); + free(kex->name); +diff --git a/kex.h b/kex.h +index a5ae6ac0..fe714141 100644 +--- a/kex.h ++++ b/kex.h +@@ -102,6 +102,15 @@ enum kex_exchange { + KEX_ECDH_SHA2, + KEX_C25519_SHA256, + KEX_KEM_SNTRUP4591761X25519_SHA512, ++#ifdef GSSAPI ++ KEX_GSS_GRP1_SHA1, ++ KEX_GSS_GRP14_SHA1, ++ KEX_GSS_GRP14_SHA256, ++ KEX_GSS_GRP16_SHA512, ++ KEX_GSS_GEX_SHA1, ++ KEX_GSS_NISTP256_SHA256, ++ KEX_GSS_C25519_SHA256, ++#endif + KEX_MAX + }; + +@@ -153,6 +162,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 *failed_choice; + int (*verify_host_key)(struct sshkey *, struct ssh *); + struct sshkey *(*load_host_public_key)(int, int, struct ssh *); +@@ -174,8 +189,10 @@ struct kex { + + int kex_names_valid(const char *); + char *kex_alg_list(char); ++char *kex_gss_alg_list(char); + char *kex_names_cat(const char *, const char *); + int kex_assemble_names(char **, const char *, const char *); ++int kex_gss_names_valid(const char *); + + int kex_exchange_identification(struct ssh *, int, const char *); + +@@ -202,6 +219,12 @@ int kexgex_client(struct ssh *); + int kexgex_server(struct ssh *); + int kex_gen_client(struct ssh *); + int kex_gen_server(struct ssh *); ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++int kexgssgex_client(struct ssh *); ++int kexgssgex_server(struct ssh *); ++int kexgss_client(struct ssh *); ++int kexgss_server(struct ssh *); ++#endif + + int kex_dh_keypair(struct kex *); + int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, +@@ -234,6 +257,12 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *, + const BIGNUM *, const u_char *, size_t, + u_char *, size_t *); + ++int kex_gen_hash(int hash_alg, const struct sshbuf *client_version, ++ const struct sshbuf *server_version, const struct sshbuf *client_kexinit, ++ const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob, ++ const struct sshbuf *client_pub, const struct sshbuf *server_pub, ++ const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen); ++ + void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) + __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) + __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); +diff --git a/kexdh.c b/kexdh.c +index 67133e33..edaa4676 100644 +--- a/kexdh.c ++++ b/kexdh.c +@@ -48,13 +48,23 @@ kex_dh_keygen(struct kex *kex) + { + switch (kex->kex_type) { + case KEX_DH_GRP1_SHA1: ++#ifdef GSSAPI ++ case KEX_GSS_GRP1_SHA1: ++#endif + kex->dh = dh_new_group1(); + break; + case KEX_DH_GRP14_SHA1: + case KEX_DH_GRP14_SHA256: ++#ifdef GSSAPI ++ case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++#endif + kex->dh = dh_new_group14(); + break; + case KEX_DH_GRP16_SHA512: ++#ifdef GSSAPI ++ case KEX_GSS_GRP16_SHA512: ++#endif + kex->dh = dh_new_group16(); + break; + case KEX_DH_GRP18_SHA512: +diff --git a/kexgen.c b/kexgen.c +index 69348b96..c0e8c2f4 100644 +--- a/kexgen.c ++++ b/kexgen.c +@@ -44,7 +44,7 @@ + static int input_kex_gen_init(int, u_int32_t, struct ssh *); + static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh); + +-static int ++int + kex_gen_hash( + int hash_alg, + const struct sshbuf *client_version, +diff --git a/kexgssc.c b/kexgssc.c +new file mode 100644 +index 00000000..f6e1405e +--- /dev/null ++++ b/kexgssc.c +@@ -0,0 +1,606 @@ ++/* ++ * 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" ++ ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ ++#include "includes.h" ++ ++#include <openssl/crypto.h> ++#include <openssl/bn.h> ++ ++#include <string.h> ++ ++#include "xmalloc.h" ++#include "sshbuf.h" ++#include "ssh2.h" ++#include "sshkey.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "digest.h" ++#include "ssherr.h" ++ ++#include "ssh-gss.h" ++ ++int ++kexgss_client(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER, ++ recv_tok = GSS_C_EMPTY_BUFFER, ++ gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr; ++ Gssctxt *ctxt; ++ OM_uint32 maj_status, min_status, ret_flags; ++ struct sshbuf *server_blob = NULL; ++ struct sshbuf *shared_secret = NULL; ++ struct sshbuf *server_host_key_blob = NULL; ++ struct sshbuf *empty = NULL; ++ u_char *msg; ++ int type = 0; ++ int first = 1; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ u_char c; ++ int r; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (kex->gss_client && ++ ssh_gssapi_client_identity(ctxt, kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ /* Step 1 */ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++ case KEX_GSS_GRP16_SHA512: ++ r = kex_dh_keypair(kex); ++ break; ++ case KEX_GSS_NISTP256_SHA256: ++ r = kex_ecdh_keypair(kex); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ r = kex_c25519_keypair(kex); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ if (r != 0) ++ return r; ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ kex->gss_deleg_creds, token_ptr, &send_tok, ++ &ret_flags); ++ ++ if (GSS_ERROR(maj_status)) { ++ /* XXX Useles code: Missing send? */ ++ if (send_tok.length != 0) { ++ if ((r = sshpkt_start(ssh, ++ SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ fatal("gss_init_context failed"); ++ } ++ ++ /* If we've got an old receive buffer get rid of it */ ++ if (token_ptr != GSS_C_NO_BUFFER) ++ gss_release_buffer(&min_status, &recv_tok); ++ ++ 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) { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0 || ++ (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0) ++ fatal("failed to construct packet: %s", ssh_err(r)); ++ first = 0; ++ } else { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0) ++ fatal("failed to construct packet: %s", ssh_err(r)); ++ } ++ if ((r = sshpkt_send(ssh)) != 0) ++ fatal("failed to send packet: %s", ssh_err(r)); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = ssh_packet_read(ssh); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ debug("Received KEXGSS_HOSTKEY"); ++ if (server_host_key_blob) ++ fatal("Server host key received more than once"); ++ if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0) ++ fatal("Failed to read server host key: %s", ssh_err(r)); ++ } ++ } 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"); ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("Failed to read token: %s", ssh_err(r)); ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ if (msg_tok.value != NULL) ++ fatal("Received GSSAPI_COMPLETE twice?"); ++ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || ++ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &msg_tok)) != 0) ++ fatal("Failed to read message: %s", ssh_err(r)); ++ ++ /* Is there a token included? */ ++ if ((r = sshpkt_get_u8(ssh, &c)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ if (c) { ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc( ++ ssh, &recv_tok)) != 0) ++ fatal("Failed to read token: %s", ssh_err(r)); ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: did not receive final token"); ++ } ++ if ((r = sshpkt_get_end(ssh)) != 0) { ++ fatal("Expecting end of packet."); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 || ++ (r = sshpkt_get_u32(ssh, &min_status)) != 0 || ++ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || ++ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */ ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt_get failed: %s", ssh_err(r)); ++ fatal("GSSAPI Error: \n%.400s", msg); ++ default: ++ sshpkt_disconnect(ssh, "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 server_blob and msg_tok ++ */ ++ ++ if (type != SSH2_MSG_KEXGSS_COMPLETE) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ ++ /* compute shared secret */ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++ case KEX_GSS_GRP16_SHA512: ++ r = kex_dh_dec(kex, server_blob, &shared_secret); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80) ++ fatal("The received key has MSB of last octet set!"); ++ r = kex_c25519_dec(kex, server_blob, &shared_secret); ++ break; ++ case KEX_GSS_NISTP256_SHA256: ++ if (sshbuf_len(server_blob) != 65) ++ fatal("The received NIST-P256 key did not match" ++ "expected length (expected 65, got %zu)", sshbuf_len(server_blob)); ++ ++ if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED) ++ fatal("The received NIST-P256 key does not have first octet 0x04"); ++ ++ r = kex_ecdh_dec(kex, server_blob, &shared_secret); ++ break; ++ default: ++ r = SSH_ERR_INVALID_ARGUMENT; ++ break; ++ } ++ if (r != 0) ++ goto out; ++ ++ if ((empty = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ hashlen = sizeof(hash); ++ if ((r = kex_gen_hash( ++ kex->hash_alg, ++ kex->client_version, ++ kex->server_version, ++ kex->my, ++ kex->peer, ++ (server_host_key_blob ? server_host_key_blob : empty), ++ kex->client_pub, ++ server_blob, ++ shared_secret, ++ hash, &hashlen)) != 0) ++ fatal("%s: Unexpected KEX type %d", __func__, 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))) ++ sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); ++ ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ r = kex_send_newkeys(ssh); ++ ++out: ++ explicit_bzero(hash, sizeof(hash)); ++ explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); ++ sshbuf_free(empty); ++ sshbuf_free(server_host_key_blob); ++ sshbuf_free(server_blob); ++ sshbuf_free(shared_secret); ++ sshbuf_free(kex->client_pub); ++ kex->client_pub = NULL; ++ return r; ++} ++ ++int ++kexgssgex_client(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER, ++ recv_tok = GSS_C_EMPTY_BUFFER, gssbuf, ++ msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr; ++ Gssctxt *ctxt; ++ OM_uint32 maj_status, min_status, ret_flags; ++ struct sshbuf *shared_secret = NULL; ++ BIGNUM *p = NULL; ++ BIGNUM *g = NULL; ++ struct sshbuf *buf = NULL; ++ struct sshbuf *server_host_key_blob = NULL; ++ struct sshbuf *server_blob = NULL; ++ BIGNUM *dh_server_pub = NULL; ++ u_char *msg; ++ int type = 0; ++ int first = 1; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ const BIGNUM *pub_key, *dh_p, *dh_g; ++ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; ++ struct sshbuf *empty = NULL; ++ u_char c; ++ int r; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (kex->gss_client && ++ ssh_gssapi_client_identity(ctxt, kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ debug("Doing group exchange"); ++ nbits = dh_estimate(kex->dh_need * 8); ++ ++ kex->min = DH_GRP_MIN; ++ kex->max = DH_GRP_MAX; ++ kex->nbits = nbits; ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 || ++ (r = sshpkt_put_u32(ssh, min)) != 0 || ++ (r = sshpkt_put_u32(ssh, nbits)) != 0 || ++ (r = sshpkt_put_u32(ssh, max)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("Failed to construct a packet: %s", ssh_err(r)); ++ ++ if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0) ++ fatal("Error: %s", ssh_err(r)); ++ ++ if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 || ++ (r = sshpkt_get_bignum2(ssh, &g)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("shpkt_get_bignum2 failed: %s", ssh_err(r)); ++ ++ 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); ++ ++ if ((kex->dh = dh_new_group(g, p)) == NULL) ++ fatal("dn_new_group() failed"); ++ p = g = NULL; /* belong to kex->dh now */ ++ ++ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) ++ goto out; ++ DH_get0_key(kex->dh, &pub_key, NULL); ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ /* Step 2 - call GSS_Init_sec_context() */ ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ kex->gss_deleg_creds, token_ptr, &send_tok, ++ &ret_flags); ++ ++ if (GSS_ERROR(maj_status)) { ++ /* XXX Useles code: Missing send? */ ++ if (send_tok.length != 0) { ++ if ((r = sshpkt_start(ssh, ++ SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ fatal("gss_init_context failed"); ++ } ++ ++ /* If we've got an old receive buffer get rid of it */ ++ if (token_ptr != GSS_C_NO_BUFFER) ++ gss_release_buffer(&min_status, &recv_tok); ++ ++ 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) { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ first = 0; ++ } else { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh,send_tok.value, ++ send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ if ((r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt_send failed: %s", ssh_err(r)); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = ssh_packet_read(ssh); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ debug("Received KEXGSS_HOSTKEY"); ++ if (server_host_key_blob) ++ fatal("Server host key received more than once"); ++ if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ } 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"); ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ if (msg_tok.value != NULL) ++ fatal("Received GSSAPI_COMPLETE twice?"); ++ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || ++ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &msg_tok)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ /* Is there a token included? */ ++ if ((r = sshpkt_get_u8(ssh, &c)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ if (c) { ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc( ++ ssh, &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: did not receive final token"); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 || ++ (r = sshpkt_get_u32(ssh, &min_status)) != 0 || ++ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || ++ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */ ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ fatal("GSSAPI Error: \n%.400s", msg); ++ default: ++ sshpkt_disconnect(ssh, "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"); ++ ++ /* 7. C verifies that the key Q_S is valid */ ++ /* 8. C computes shared secret */ ++ if ((buf = sshbuf_new()) == NULL || ++ (r = sshbuf_put_stringb(buf, server_blob)) != 0 || ++ (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0) ++ goto out; ++ sshbuf_free(buf); ++ buf = NULL; ++ ++ if ((shared_secret = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0) ++ goto out; ++ if ((empty = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); ++ hashlen = sizeof(hash); ++ if ((r = kexgex_hash( ++ kex->hash_alg, ++ kex->client_version, ++ kex->server_version, ++ kex->my, ++ kex->peer, ++ (server_host_key_blob ? server_host_key_blob : empty), ++ kex->min, kex->nbits, kex->max, ++ dh_p, dh_g, ++ pub_key, ++ dh_server_pub, ++ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), ++ hash, &hashlen)) != 0) ++ fatal("Failed to calculate hash: %s", ssh_err(r)); ++ ++ 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))) ++ sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); ++ ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ /* save session id */ ++ if (kex->session_id == NULL) { ++ kex->session_id_len = hashlen; ++ kex->session_id = xmalloc(kex->session_id_len); ++ memcpy(kex->session_id, hash, kex->session_id_len); ++ } ++ ++ if (kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ /* Finally derive the keys and send them */ ++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ r = kex_send_newkeys(ssh); ++out: ++ sshbuf_free(buf); ++ sshbuf_free(server_blob); ++ sshbuf_free(empty); ++ explicit_bzero(hash, sizeof(hash)); ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ BN_clear_free(dh_server_pub); ++ sshbuf_free(shared_secret); ++ sshbuf_free(server_host_key_blob); ++ return r; ++} ++#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */ +diff --git a/kexgsss.c b/kexgsss.c +new file mode 100644 +index 00000000..60bc02de +--- /dev/null ++++ b/kexgsss.c +@@ -0,0 +1,474 @@ ++/* ++ * 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" ++ ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ ++#include <string.h> ++ ++#include <openssl/crypto.h> ++#include <openssl/bn.h> ++ ++#include "xmalloc.h" ++#include "sshbuf.h" ++#include "ssh2.h" ++#include "sshkey.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" /* servconf.h needs misc.h for struct ForwardOptions */ ++#include "servconf.h" ++#include "ssh-gss.h" ++#include "digest.h" ++#include "ssherr.h" ++ ++extern ServerOptions options; ++ ++int ++kexgss_server(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ 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; ++ struct sshbuf *shared_secret = NULL; ++ struct sshbuf *client_pubkey = NULL; ++ struct sshbuf *server_pubkey = NULL; ++ struct sshbuf *empty = sshbuf_new(); ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ int r; ++ ++ /* 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__, kex->name); ++ oid = ssh_gssapi_id_kex(NULL, kex->name, 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"); ++ ++ do { ++ debug("Wait SSH2_MSG_KEXGSS_INIT"); ++ type = ssh_packet_read(ssh); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (client_pubkey != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++ case KEX_GSS_GRP16_SHA512: ++ r = kex_dh_enc(kex, client_pubkey, &server_pubkey, ++ &shared_secret); ++ break; ++ case KEX_GSS_NISTP256_SHA256: ++ r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey, ++ &shared_secret); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ r = kex_c25519_enc(kex, client_pubkey, &server_pubkey, ++ &shared_secret); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ if (r != 0) ++ goto out; ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ break; ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ break; ++ default: ++ sshpkt_disconnect(ssh, ++ "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ ++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags)); ++ ++ gss_release_buffer(&min_status, &recv_tok); ++ ++ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) ++ fatal("Zero length token output when incomplete"); ++ ++ if (client_pubkey == NULL) ++ fatal("No client public key"); ++ ++ if (maj_status & GSS_S_CONTINUE_NEEDED) { ++ debug("Sending GSSAPI_CONTINUE"); ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ gss_release_buffer(&min_status, &send_tok); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length > 0) { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ 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"); ++ ++ hashlen = sizeof(hash); ++ if ((r = kex_gen_hash( ++ kex->hash_alg, ++ kex->client_version, ++ kex->server_version, ++ kex->peer, ++ kex->my, ++ empty, ++ client_pubkey, ++ server_pubkey, ++ shared_secret, ++ hash, &hashlen)) != 0) ++ goto out; ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))) ++ fatal("Couldn't get MIC"); ++ ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 || ++ (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 || ++ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ if (send_tok.length != 0) { ++ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */ ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } else { ++ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */ ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ if ((r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt_send failed: %s", ssh_err(r)); ++ ++ 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); ++ ++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ r = 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(); ++out: ++ sshbuf_free(empty); ++ explicit_bzero(hash, sizeof(hash)); ++ sshbuf_free(shared_secret); ++ sshbuf_free(client_pubkey); ++ sshbuf_free(server_pubkey); ++ return r; ++} ++ ++int ++kexgssgex_server(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ 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; ++ struct sshbuf *shared_secret = NULL; ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ BIGNUM *dh_client_pub = NULL; ++ const BIGNUM *pub_key, *dh_p, *dh_g; ++ int min = -1, max = -1, nbits = -1; ++ int cmin = -1, cmax = -1; /* client proposal */ ++ struct sshbuf *empty = sshbuf_new(); ++ int r; ++ ++ /* 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()) ++ if ((mechs = ssh_gssapi_server_mechanisms())) ++ free(mechs); ++ ++ debug2("%s: Identifying %s", __func__, kex->name); ++ oid = ssh_gssapi_id_kex(NULL, kex->name, 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"); ++ ++ /* 5. S generates an ephemeral key pair (do the allocations early) */ ++ debug("Doing group exchange"); ++ ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ); ++ /* store client proposal to provide valid signature */ ++ if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 || ++ (r = sshpkt_get_u32(ssh, &nbits)) != 0 || ++ (r = sshpkt_get_u32(ssh, &cmax)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ kex->nbits = nbits; ++ kex->min = cmin; ++ kex->max = cmax; ++ min = MAX(DH_GRP_MIN, cmin); ++ max = MIN(DH_GRP_MAX, cmax); ++ nbits = MAXIMUM(DH_GRP_MIN, nbits); ++ nbits = MINIMUM(DH_GRP_MAX, nbits); ++ if (max < min || nbits < min || max < nbits) ++ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", ++ min, nbits, max); ++ kex->dh = PRIVSEP(choose_dh(min, nbits, max)); ++ if (kex->dh == NULL) { ++ sshpkt_disconnect(ssh, "Protocol error: no matching group found"); ++ fatal("Protocol error: no matching group found"); ++ } ++ ++ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ if ((r = ssh_packet_write_wait(ssh)) != 0) ++ fatal("ssh_packet_write_wait: %s", ssh_err(r)); ++ ++ /* Compute our exchange value in parallel with the client */ ++ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) ++ goto out; ++ ++ do { ++ debug("Wait SSH2_MSG_GSSAPI_INIT"); ++ type = ssh_packet_read(ssh); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (dh_client_pub != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ break; ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ break; ++ default: ++ sshpkt_disconnect(ssh, ++ "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ ++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags)); ++ ++ gss_release_buffer(&min_status, &recv_tok); ++ ++ 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"); ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ gss_release_buffer(&min_status, &send_tok); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length > 0) { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ 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"); ++ ++ /* calculate shared secret */ ++ if ((shared_secret = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0) ++ goto out; ++ ++ DH_get0_key(kex->dh, &pub_key, NULL); ++ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); ++ hashlen = sizeof(hash); ++ if ((r = kexgex_hash( ++ kex->hash_alg, ++ kex->client_version, ++ kex->server_version, ++ kex->peer, ++ kex->my, ++ empty, ++ cmin, nbits, cmax, ++ dh_p, dh_g, ++ dh_client_pub, ++ pub_key, ++ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), ++ hash, &hashlen)) != 0) ++ fatal("kexgex_hash failed: %s", ssh_err(r)); ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))) ++ fatal("Couldn't get MIC"); ++ ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || ++ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ if (send_tok.length != 0) { ++ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */ ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } else { ++ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */ ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ if ((r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ 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); ++ ++ /* Finally derive the keys and send them */ ++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ r = 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(); ++out: ++ sshbuf_free(empty); ++ explicit_bzero(hash, sizeof(hash)); ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ BN_clear_free(dh_client_pub); ++ sshbuf_free(shared_secret); ++ return r; ++} ++#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */ +diff --git a/monitor.c b/monitor.c +index 2ce89fe9..ebf76c7f 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -148,6 +148,8 @@ int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *); + int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *); + int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *); + int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *); ++int mm_answer_gss_sign(struct ssh *, int, struct sshbuf *); ++int mm_answer_gss_updatecreds(struct ssh *, int, struct sshbuf *); + #endif + + #ifdef SSH_AUDIT_EVENTS +@@ -220,11 +222,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 +@@ -293,6 +302,10 @@ monitor_child_preauth(struct ssh *ssh, 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) { +@@ -406,6 +419,10 @@ monitor_child_postauth(struct ssh *ssh, 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 (auth_opts->permit_pty_flag) { + monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); +@@ -1713,6 +1730,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor) + # ifdef OPENSSL_HAS_ECC + kex->kex[KEX_ECDH_SHA2] = kex_gen_server; + # endif ++# 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_GRP14_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server; ++ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server; ++ } ++# endif + #endif /* WITH_OPENSSL */ + kex->kex[KEX_C25519_SHA256] = kex_gen_server; + kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; +@@ -1806,8 +1834,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m) + u_char *p; + int r; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + if ((r = sshbuf_get_string(m, &p, &len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +@@ -1839,8 +1867,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m) + OM_uint32 flags = 0; /* GSI needs this */ + int r; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +@@ -1860,6 +1888,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *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); + } +@@ -1871,8 +1900,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m) + OM_uint32 ret; + int r; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || + (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) +@@ -1898,13 +1927,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m) + int + mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) + { +- int r, authenticated; ++ int r, authenticated, kex; + const char *displayname; + +- 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); ++ if ((r = sshbuf_get_u32(m, &kex)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ authenticated = authctxt->valid && ++ ssh_gssapi_userok(authctxt->user, authctxt->pw, kex); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, authenticated)) != 0) +@@ -1913,7 +1946,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) + debug3("%s: sending result %d", __func__, authenticated); + mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); + +- auth_method = "gssapi-with-mic"; ++ if (kex) { ++ auth_method = "gssapi-keyex"; ++ } else { ++ auth_method = "gssapi-with-mic"; ++ } + + if ((displayname = ssh_gssapi_displayname()) != NULL) + auth2_record_info(authctxt, "%s", displayname); +@@ -1921,5 +1958,85 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) + /* Monitor loop will terminate if authenticated */ + return (authenticated); + } ++ ++int ++mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m) ++{ ++ gss_buffer_desc data; ++ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; ++ OM_uint32 major, minor; ++ size_t len; ++ u_char *p = NULL; ++ int r; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); ++ ++ if ((r = sshbuf_get_string(m, &p, &len)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ data.value = p; ++ data.length = len; ++ /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */ ++ if (data.length != 20 && data.length != 32 && data.length != 64) ++ 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); ++ ++ sshbuf_reset(m); ++ ++ if ((r = sshbuf_put_u32(m, major)) != 0 || ++ (r = sshbuf_put_string(m, hash.value, hash.length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ 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(struct ssh *ssh, int socket, struct sshbuf *m) { ++ ssh_gssapi_ccache store; ++ int r, ok; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); ++ ++ if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 || ++ (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 || ++ (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ ok = ssh_gssapi_update_creds(&store); ++ ++ free(store.filename); ++ free(store.envvar); ++ free(store.envval); ++ ++ sshbuf_reset(m); ++ if ((r = sshbuf_put_u32(m, ok)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); ++ ++ return(0); ++} ++ + #endif /* GSSAPI */ + +diff --git a/monitor.h b/monitor.h +index 683e5e07..2b1a2d59 100644 +--- a/monitor.h ++++ b/monitor.h +@@ -63,6 +63,8 @@ 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 ssh; +diff --git a/monitor_wrap.c b/monitor_wrap.c +index 001a8fa1..6edb509a 100644 +--- a/monitor_wrap.c ++++ b/monitor_wrap.c +@@ -993,13 +993,15 @@ 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, int kex) + { + struct sshbuf *m; + int r, authenticated = 0; + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_u32(m, kex)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); + mm_request_receive_expect(pmonitor->m_recvfd, +@@ -1012,4 +1014,57 @@ 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) ++{ ++ struct sshbuf *m; ++ OM_uint32 major; ++ int r; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m); ++ ++ if ((r = sshbuf_get_u32(m, &major)) != 0 || ++ (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(m); ++ ++ return (major); ++} ++ ++int ++mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) ++{ ++ struct sshbuf *m; ++ int r, ok; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ if ((r = sshbuf_put_cstring(m, ++ store->filename ? store->filename : "")) != 0 || ++ (r = sshbuf_put_cstring(m, ++ store->envvar ? store->envvar : "")) != 0 || ++ (r = sshbuf_put_cstring(m, ++ store->envval ? store->envval : "")) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m); ++ ++ if ((r = sshbuf_get_u32(m, &ok)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(m); ++ ++ return (ok); ++} ++ + #endif /* GSSAPI */ +diff --git a/monitor_wrap.h b/monitor_wrap.h +index 23ab096a..485590c1 100644 +--- a/monitor_wrap.h ++++ b/monitor_wrap.h +@@ -64,8 +64,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t, + 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 *, int kex); + 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 f3cac6b3..da8022dd 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -67,6 +67,7 @@ + #include "uidswap.h" + #include "myproposal.h" + #include "digest.h" ++#include "ssh-gss.h" + + /* Format of the configuration file: + +@@ -160,6 +161,8 @@ typedef enum { + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, ++ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, ++ oGssServerIdentity, oGssKexAlgorithms, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, +@@ -204,10 +207,22 @@ static struct { + /* Sometimes-unsupported options */ + #if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, ++ { "gssapikeyexchange", oGssKeyEx }, + { "gssapidelegatecredentials", oGssDelegateCreds }, ++ { "gssapitrustdns", oGssTrustDns }, ++ { "gssapiclientidentity", oGssClientIdentity }, ++ { "gssapiserveridentity", oGssServerIdentity }, ++ { "gssapirenewalforcesrekey", oGssRenewalRekey }, ++ { "gssapikexalgorithms", oGssKexAlgorithms }, + # else + { "gssapiauthentication", oUnsupported }, ++ { "gssapikeyexchange", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, ++ { "gssapitrustdns", oUnsupported }, ++ { "gssapiclientidentity", oUnsupported }, ++ { "gssapiserveridentity", oUnsupported }, ++ { "gssapirenewalforcesrekey", oUnsupported }, ++ { "gssapikexalgorithms", oUnsupported }, + #endif + #ifdef ENABLE_PKCS11 + { "pkcs11provider", oPKCS11Provider }, +@@ -1029,10 +1044,42 @@ 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 oGssKexAlgorithms: ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing argument.", ++ filename, linenum); ++ if (!kex_gss_names_valid(arg)) ++ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", ++ filename, linenum, arg ? arg : "<NONE>"); ++ if (*activep && options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = xstrdup(arg); ++ break; ++ + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; +@@ -1911,7 +1958,13 @@ 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->gss_kex_algorithms = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; +@@ -2059,8 +2112,18 @@ 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; ++#ifdef GSSAPI ++ if (options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); ++#endif + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -2702,7 +2765,14 @@ dump_client_config(Options *o, const char *host) + dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); + #ifdef GSSAPI + dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); ++ dump_cfg_fmtint(oGssKeyEx, o->gss_keyex); + dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); ++ dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns); ++ dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey); ++ dump_cfg_string(oGssClientIdentity, o->gss_client_identity); ++ dump_cfg_string(oGssServerIdentity, o->gss_server_identity); ++ dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ? ++ o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX); + #endif /* GSSAPI */ + dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); + dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); +diff --git a/readconf.h b/readconf.h +index feedb3d2..a8a8870d 100644 +--- a/readconf.h ++++ b/readconf.h +@@ -41,7 +41,13 @@ 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 */ ++ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ + int password_authentication; /* Try password + * authentication. */ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ +diff --git a/servconf.c b/servconf.c +index 70f5f73f..191575a1 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -69,6 +69,7 @@ + #include "auth.h" + #include "myproposal.h" + #include "digest.h" ++#include "ssh-gss.h" + + static void add_listen_addr(ServerOptions *, const char *, + const char *, int); +@@ -133,8 +134,11 @@ 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->gss_kex_algorithms = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -375,10 +379,18 @@ 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 = 1; ++ if (options->gss_store_rekey == -1) ++ options->gss_store_rekey = 0; ++#ifdef GSSAPI ++ if (options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); ++#endif + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -531,6 +543,7 @@ typedef enum { + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, ++ sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey, + sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, +@@ -607,12 +620,22 @@ 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 }, ++ { "gssapikexalgorithms", sGssKexAlgorithms, 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 }, ++ { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL }, + #endif ++ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, + { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, + { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, + { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, +@@ -1548,6 +1571,10 @@ process_server_config_line_depth(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; +@@ -1556,6 +1583,22 @@ process_server_config_line_depth(ServerOptions *options, char *line, + intptr = &options->gss_strict_acceptor; + goto parse_flag; + ++ case sGssStoreRekey: ++ intptr = &options->gss_store_rekey; ++ goto parse_flag; ++ ++ case sGssKexAlgorithms: ++ arg = strdelim(&cp); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing argument.", ++ filename, linenum); ++ if (!kex_gss_names_valid(arg)) ++ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", ++ filename, linenum, arg ? arg : "<NONE>"); ++ if (*activep && options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = xstrdup(arg); ++ break; ++ + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; +@@ -2777,6 +2820,10 @@ dump_config(ServerOptions *o) + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); ++ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); ++ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); ++ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); ++ dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms); + #endif + dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(sKbdInteractiveAuthentication, +diff --git a/servconf.h b/servconf.h +index 4202a2d0..3f47ea25 100644 +--- a/servconf.h ++++ b/servconf.h +@@ -132,8 +132,11 @@ 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; ++ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ + int password_authentication; /* If true, permit password + * authentication. */ + int kbd_interactive_authentication; /* If true, permit */ +diff --git a/session.c b/session.c +index 8c0e54f7..06a33442 100644 +--- a/session.c ++++ b/session.c +@@ -2678,13 +2678,19 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt) + + #ifdef KRB5 + if (options.kerberos_ticket_cleanup && +- authctxt->krb5_ctx) ++ authctxt->krb5_ctx) { ++ temporarily_use_uid(authctxt->pw); + krb5_cleanup_proc(authctxt); ++ restore_uid(); ++ } + #endif + + #ifdef GSSAPI +- if (options.gss_cleanup_creds) ++ if (options.gss_cleanup_creds) { ++ temporarily_use_uid(authctxt->pw); + ssh_gssapi_cleanup_creds(); ++ restore_uid(); ++ } + #endif + + /* remove agent socket */ +diff --git a/ssh-gss.h b/ssh-gss.h +index 36180d07..70dd3665 100644 +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -1,6 +1,6 @@ + /* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 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,30 @@ + + #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_GRP14_SHA256_ID "gss-group14-sha256-" ++#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" ++#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" ++#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-" ++#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-" ++ ++#define GSS_KEX_DEFAULT_KEX \ ++ KEX_GSS_GEX_SHA1_ID "," \ ++ KEX_GSS_GRP14_SHA1_ID ++ + typedef struct { + char *filename; + char *envvar; + char *envval; ++ struct passwd *owner; + void *data; + } ssh_gssapi_ccache; + +@@ -72,8 +92,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 +107,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 +118,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); +@@ -109,6 +134,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *); + + struct sshbuf; + int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *); ++int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *); + + OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *); + OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int, +@@ -123,17 +149,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **); + OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_buildmic(struct sshbuf *, 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 *, const char *); ++char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, ++ 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 *, int kex); + 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); + const char *ssh_gssapi_displayname(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.1 b/ssh.1 +index 60de6087..db5c65bc 100644 +--- a/ssh.1 ++++ b/ssh.1 +@@ -503,7 +503,13 @@ For full details of the options listed below, and their possible values, see + .It GatewayPorts + .It GlobalKnownHostsFile + .It GSSAPIAuthentication ++.It GSSAPIKeyExchange ++.It GSSAPIClientIdentity + .It GSSAPIDelegateCredentials ++.It GSSAPIKexAlgorithms ++.It GSSAPIRenewalForcesRekey ++.It GSSAPIServerIdentity ++.It GSSAPITrustDns + .It HashKnownHosts + .It Host + .It HostbasedAuthentication +@@ -579,6 +585,8 @@ flag), + (supported message integrity codes), + .Ar kex + (key exchange algorithms), ++.Ar kex-gss ++(GSSAPI key exchange algorithms), + .Ar key + (key types), + .Ar key-cert +diff --git a/ssh.c b/ssh.c +index 15aee569..110cf9c1 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -747,6 +747,8 @@ main(int ac, char **av) + else if (strcmp(optarg, "kex") == 0 || + strcasecmp(optarg, "KexAlgorithms") == 0) + cp = kex_alg_list('\n'); ++ else if (strcmp(optarg, "kex-gss") == 0) ++ cp = kex_gss_alg_list('\n'); + else if (strcmp(optarg, "key") == 0) + cp = sshkey_alg_list(0, 0, 0, '\n'); + else if (strcmp(optarg, "key-cert") == 0) +@@ -772,8 +774,8 @@ main(int ac, char **av) + } else if (strcmp(optarg, "help") == 0) { + cp = xstrdup( + "cipher\ncipher-auth\ncompression\nkex\n" +- "key\nkey-cert\nkey-plain\nkey-sig\nmac\n" +- "protocol-version\nsig"); ++ "kex-gss\nkey\nkey-cert\nkey-plain\n" ++ "key-sig\nmac\nprotocol-version\nsig"); + } + if (cp == NULL) + fatal("Unsupported query \"%s\"", optarg); +diff --git a/ssh_config b/ssh_config +index 5e8ef548..1ff999b6 100644 +--- a/ssh_config ++++ b/ssh_config +@@ -24,6 +24,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 06a32d31..3f490697 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -766,10 +766,67 @@ The default is + Specifies whether user authentication based on GSSAPI is allowed. + 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 GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. + 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 ++.Dq no . ++.It Cm GSSAPIRenewalForcesRekey ++If set to ++.Dq yes ++then renewal of the client's GSSAPI credentials will force the rekeying of the ++ssh connection. With a compatible server, this will delegate the renewed ++credentials to a session on the server. ++.Pp ++Checks are made to ensure that credentials are only propagated when the new ++credentials match the old ones on the originating client and where the ++receiving server still has the old set in its cache. ++.Pp ++The default is ++.Dq no . ++.Pp ++For this to work ++.Cm GSSAPIKeyExchange ++needs to be enabled in the server and also used by the client. ++.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 GSSAPITrustDns ++Set to ++.Dq yes ++to indicate that the DNS is trusted to securely canonicalize ++the name of the host being connected to. If ++.Dq no , ++the hostname entered on the ++command line will be passed untouched to the GSSAPI library. ++The default is ++.Dq no . ++.It Cm GSSAPIKexAlgorithms ++The list of key exchange algorithms that are offered for GSSAPI ++key exchange. Possible values are ++.Bd -literal -offset 3n ++gss-gex-sha1-, ++gss-group1-sha1-, ++gss-group14-sha1-, ++gss-group14-sha256-, ++gss-group16-sha512-, ++gss-nistp256-sha256-, ++gss-curve25519-sha256- ++.Ed ++.Pp ++The default is ++.Dq gss-gex-sha1-,gss-group14-sha1- . ++This option only applies to protocol version 2 connections using GSSAPI. + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +diff --git a/sshconnect2.c b/sshconnect2.c +index af00fb30..03bc87eb 100644 +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -80,8 +80,6 @@ + #endif + + /* import */ +-extern char *client_version_string; +-extern char *server_version_string; + extern Options options; + + /* +@@ -163,6 +161,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) + char *s, *all_key; + int r, use_known_hosts_order = 0; + ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ char *orig = NULL, *gss = NULL; ++ char *gss_host = NULL; ++#endif ++ + xxx_host = host; + xxx_hostaddr = hostaddr; + +@@ -206,6 +209,35 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) + compat_pkalg_proposal(options.hostkeyalgorithms); + } + ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ 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(ssh); ++ else ++ gss_host = xstrdup(host); ++ ++ gss = ssh_gssapi_client_mechanisms(gss_host, ++ options.gss_client_identity, options.gss_kex_algorithms); ++ 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) + ssh_packet_set_rekey_limits(ssh, options.rekey_limit, + options.rekey_interval); +@@ -224,16 +256,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) + # ifdef OPENSSL_HAS_ECC + ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; + # endif +-#endif ++# ifdef GSSAPI ++ if (options.gss_keyex) { ++ ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client; ++ ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client; ++ } ++# endif ++#endif /* WITH_OPENSSL */ + ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; + ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; + ssh->kex->verify_host_key=&verify_host_key_callback; + ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ if (options.gss_keyex) { ++ ssh->kex->gss_deleg_creds = options.gss_deleg_creds; ++ ssh->kex->gss_trust_dns = options.gss_trust_dns; ++ ssh->kex->gss_client = options.gss_client_identity; ++ ssh->kex->gss_host = gss_host; ++ } ++#endif ++ + ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); + + /* remove ext-info from the KEX proposals for rekeying */ + myproposal[PROPOSAL_KEX_ALGS] = + compat_kex_proposal(options.kex_algorithms); ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ /* 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(ssh->kex->my, myproposal)) != 0) + fatal("kex_prop2buf: %s", ssh_err(r)); + +@@ -330,6 +392,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *); + static int input_gssapi_token(int type, u_int32_t, struct ssh *); + static int input_gssapi_error(int, u_int32_t, struct ssh *); + static int input_gssapi_errtok(int, u_int32_t, struct ssh *); ++static int userauth_gsskeyex(struct ssh *); + #endif + + void userauth(struct ssh *, char *); +@@ -346,6 +409,11 @@ static char *authmethods_get(void); + + Authmethod authmethods[] = { + #ifdef GSSAPI ++ {"gssapi-keyex", ++ userauth_gsskeyex, ++ NULL, ++ &options.gss_keyex, ++ NULL}, + {"gssapi-with-mic", + userauth_gssapi, + userauth_gssapi_cleanup, +@@ -716,12 +784,25 @@ userauth_gssapi(struct ssh *ssh) + OM_uint32 min; + int r, ok = 0; + gss_OID mech = NULL; ++ 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(ssh); ++ else ++ gss_host = xstrdup(authctxt->host); + + /* Try one GSSAPI method at a time, rather than sending them all at + * once. */ + + if (authctxt->gss_supported_mechs == NULL) +- gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); ++ if (GSS_ERROR(gss_indicate_mechs(&min, ++ &authctxt->gss_supported_mechs))) { ++ authctxt->gss_supported_mechs = NULL; ++ free(gss_host); ++ return 0; ++ } + + /* Check to see whether the mechanism is usable before we offer it */ + while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && +@@ -730,13 +811,15 @@ userauth_gssapi(struct ssh *ssh) + elements[authctxt->mech_tried]; + /* My DER encoding requires length<128 */ + if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, +- mech, authctxt->host)) { ++ mech, gss_host, options.gss_client_identity)) { + ok = 1; /* Mechanism works */ + } else { + authctxt->mech_tried++; + } + } + ++ free(gss_host); ++ + if (!ok || mech == NULL) + return 0; + +@@ -976,6 +1059,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh) + free(lang); + return r; + } ++ ++int ++userauth_gsskeyex(struct ssh *ssh) ++{ ++ struct sshbuf *b = NULL; ++ Authctxt *authctxt = ssh->authctxt; ++ gss_buffer_desc gssbuf; ++ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ms; ++ int r; ++ ++ static int attempt = 0; ++ if (attempt++ >= 1) ++ return (0); ++ ++ if (gss_kex_context == NULL) { ++ debug("No valid Key exchange context"); ++ return (0); ++ } ++ ++ if ((b = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, ++ "gssapi-keyex"); ++ ++ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) ++ fatal("%s: sshbuf_mutable_ptr failed", __func__); ++ gssbuf.length = sshbuf_len(b); ++ ++ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { ++ sshbuf_free(b); ++ return (0); ++ } ++ ++ if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || ++ (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || ++ (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || ++ (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || ++ (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("%s: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(b); ++ gss_release_buffer(&ms, &mic); ++ ++ return (1); ++} ++ + #endif /* GSSAPI */ + + static int +diff --git a/sshd.c b/sshd.c +index 60b2aaf7..d92f03aa 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -817,8 +817,8 @@ notify_hostkeys(struct ssh *ssh) + } + debug3("%s: sent %u hostkeys", __func__, nkeys); + if (nkeys == 0) +- fatal("%s: no hostkeys", __func__); +- if ((r = sshpkt_send(ssh)) != 0) ++ debug3("%s: no hostkeys", __func__); ++ else if ((r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send", __func__); + sshbuf_free(buf); + } +@@ -1852,7 +1852,8 @@ main(int ac, char **av) + free(fp); + } + accumulate_host_timing_secret(cfg, NULL); +- if (!sensitive_data.have_ssh2_key) { ++ /* The GSSAPI key exchange can run without a host key */ ++ if (!sensitive_data.have_ssh2_key && !options.gss_keyex) { + logit("sshd: no hostkeys available -- exiting."); + exit(1); + } +@@ -2347,6 +2348,48 @@ do_ssh2_kex(struct ssh *ssh) + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( + list_hostkey_types()); + ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ { ++ 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(ssh, myproposal)) != 0) + fatal("kex_setup: %s", ssh_err(r)); +@@ -2362,7 +2405,18 @@ do_ssh2_kex(struct ssh *ssh) + # ifdef OPENSSL_HAS_ECC + kex->kex[KEX_ECDH_SHA2] = kex_gen_server; + # endif +-#endif ++# 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_GRP14_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server; ++ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server; ++ } ++# endif ++#endif /* WITH_OPENSSL */ + kex->kex[KEX_C25519_SHA256] = kex_gen_server; + kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; + kex->load_host_public_key=&get_hostkey_public_by_type; +diff --git a/sshd_config b/sshd_config +index 19b7c91a..2c48105f 100644 +--- a/sshd_config ++++ b/sshd_config +@@ -69,6 +69,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 70ccea44..f6b41a2f 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -646,6 +646,11 @@ Specifies whether to automatically destroy the user's credentials cache + on logout. + The default is + .Cm yes . ++.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 GSSAPIStrictAcceptorCheck + Determines whether to be strict about the identity of the GSSAPI acceptor + a client authenticates against. +@@ -660,6 +665,31 @@ 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 ++.Dq no . ++.Pp ++For this to work ++.Cm GSSAPIKeyExchange ++needs to be enabled in the server and also used by the client. ++.It Cm GSSAPIKexAlgorithms ++The list of key exchange algorithms that are accepted by GSSAPI ++key exchange. Possible values are ++.Bd -literal -offset 3n ++gss-gex-sha1-, ++gss-group1-sha1-, ++gss-group14-sha1-, ++gss-group14-sha256-, ++gss-group16-sha512-, ++gss-nistp256-sha256-, ++gss-curve25519-sha256- ++.Ed ++.Pp ++The default is ++.Dq gss-gex-sha1-,gss-group14-sha1- . ++This option only applies to protocol version 2 connections using GSSAPI. + .It Cm HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased authentication + as a list of comma-separated patterns. +diff --git a/sshkey.c b/sshkey.c +index 57995ee6..fd5b7724 100644 +--- a/sshkey.c ++++ b/sshkey.c +@@ -154,6 +154,7 @@ static const struct keytype keytypes[] = { + KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 }, + # endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ ++ { "null", "null", NULL, KEY_NULL, 0, 0, 0 }, + { NULL, NULL, NULL, -1, -1, 0, 0 } + }; + +@@ -255,7 +256,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep) + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { +- if (kt->name == NULL) ++ if (kt->name == NULL || kt->type == KEY_NULL) + continue; + if (!include_sigonly && kt->sigonly) + continue; +diff --git a/sshkey.h b/sshkey.h +index 71a3fddc..37a43a67 100644 +--- a/sshkey.h ++++ b/sshkey.h +@@ -69,6 +69,7 @@ enum sshkey_types { + KEY_ECDSA_SK_CERT, + KEY_ED25519_SK, + KEY_ED25519_SK_CERT, ++ KEY_NULL, + KEY_UNSPEC + }; + diff --git a/openssl-1.1.0.patch b/openssl-1.1.0.patch deleted file mode 100644 index 02d1796c631b..000000000000 --- a/openssl-1.1.0.patch +++ /dev/null @@ -1,2217 +0,0 @@ -diff -Naur old/auth-pam.c new/auth-pam.c ---- old/auth-pam.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/auth-pam.c 2017-03-20 11:01:28.792662449 -1000 -@@ -129,6 +129,10 @@ - typedef pthread_t sp_pthread_t; - #else - typedef pid_t sp_pthread_t; -+# define pthread_create(a, b, c, d) _ssh_compat_pthread_create(a, b, c, d) -+# define pthread_exit(a) _ssh_compat_pthread_exit(a) -+# define pthread_cancel(a) _ssh_compat_pthread_cancel(a) -+# define pthread_join(a, b) _ssh_compat_pthread_join(a, b) - #endif - - struct pam_ctxt { -diff -Naur old/cipher-3des1.c new/cipher-3des1.c ---- old/cipher-3des1.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/cipher-3des1.c 2017-03-20 11:01:25.018828127 -1000 -@@ -44,7 +44,7 @@ - */ - struct ssh1_3des_ctx - { -- EVP_CIPHER_CTX k1, k2, k3; -+ EVP_CIPHER_CTX *k1, *k2, *k3; - }; - - const EVP_CIPHER * evp_ssh1_3des(void); -@@ -65,7 +65,7 @@ - if (key == NULL) - return 1; - if (enc == -1) -- enc = ctx->encrypt; -+ enc = EVP_CIPHER_CTX_encrypting(ctx); - k1 = k2 = k3 = (u_char *) key; - k2 += 8; - if (EVP_CIPHER_CTX_key_length(ctx) >= 16+8) { -@@ -74,12 +74,17 @@ - else - k1 += 16; - } -- EVP_CIPHER_CTX_init(&c->k1); -- EVP_CIPHER_CTX_init(&c->k2); -- EVP_CIPHER_CTX_init(&c->k3); -- if (EVP_CipherInit(&c->k1, EVP_des_cbc(), k1, NULL, enc) == 0 || -- EVP_CipherInit(&c->k2, EVP_des_cbc(), k2, NULL, !enc) == 0 || -- EVP_CipherInit(&c->k3, EVP_des_cbc(), k3, NULL, enc) == 0) { -+ c->k1 = c->k2 = c->k3 = NULL; -+ c->k1 = EVP_CIPHER_CTX_new(); -+ c->k2 = EVP_CIPHER_CTX_new(); -+ c->k3 = EVP_CIPHER_CTX_new(); -+ if (c->k1 == NULL || c->k2 == NULL || c->k3 == NULL || -+ EVP_CipherInit(c->k1, EVP_des_cbc(), k1, NULL, enc) == 0 || -+ EVP_CipherInit(c->k2, EVP_des_cbc(), k2, NULL, !enc) == 0 || -+ EVP_CipherInit(c->k3, EVP_des_cbc(), k3, NULL, enc) == 0) { -+ EVP_CIPHER_CTX_free(c->k1); -+ EVP_CIPHER_CTX_free(c->k2); -+ EVP_CIPHER_CTX_free(c->k3); - explicit_bzero(c, sizeof(*c)); - free(c); - EVP_CIPHER_CTX_set_app_data(ctx, NULL); -@@ -95,9 +100,9 @@ - - if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) - return 0; -- if (EVP_Cipher(&c->k1, dest, (u_char *)src, len) == 0 || -- EVP_Cipher(&c->k2, dest, dest, len) == 0 || -- EVP_Cipher(&c->k3, dest, dest, len) == 0) -+ if (EVP_Cipher(c->k1, dest, (u_char *)src, len) == 0 || -+ EVP_Cipher(c->k2, dest, dest, len) == 0 || -+ EVP_Cipher(c->k3, dest, dest, len) == 0) - return 0; - return 1; - } -@@ -108,9 +113,9 @@ - struct ssh1_3des_ctx *c; - - if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) != NULL) { -- EVP_CIPHER_CTX_cleanup(&c->k1); -- EVP_CIPHER_CTX_cleanup(&c->k2); -- EVP_CIPHER_CTX_cleanup(&c->k3); -+ EVP_CIPHER_CTX_free(c->k1); -+ EVP_CIPHER_CTX_free(c->k2); -+ EVP_CIPHER_CTX_free(c->k3); - explicit_bzero(c, sizeof(*c)); - free(c); - EVP_CIPHER_CTX_set_app_data(ctx, NULL); -@@ -128,13 +133,13 @@ - if ((c = EVP_CIPHER_CTX_get_app_data(evp)) == NULL) - return SSH_ERR_INTERNAL_ERROR; - if (doset) { -- memcpy(c->k1.iv, iv, 8); -- memcpy(c->k2.iv, iv + 8, 8); -- memcpy(c->k3.iv, iv + 16, 8); -+ memcpy(EVP_CIPHER_CTX_iv_noconst(c->k1), iv, 8); -+ memcpy(EVP_CIPHER_CTX_iv_noconst(c->k2), iv + 8, 8); -+ memcpy(EVP_CIPHER_CTX_iv_noconst(c->k3), iv + 16, 8); - } else { -- memcpy(iv, c->k1.iv, 8); -- memcpy(iv + 8, c->k2.iv, 8); -- memcpy(iv + 16, c->k3.iv, 8); -+ memcpy(iv, EVP_CIPHER_CTX_iv(c->k1), 8); -+ memcpy(iv + 8, EVP_CIPHER_CTX_iv(c->k2), 8); -+ memcpy(iv + 16, EVP_CIPHER_CTX_iv(c->k3), 8); - } - return 0; - } -@@ -142,6 +147,7 @@ - const EVP_CIPHER * - evp_ssh1_3des(void) - { -+#if OPENSSL_VERSION_NUMBER < 0x10100000UL /*{*/ - static EVP_CIPHER ssh1_3des; - - memset(&ssh1_3des, 0, sizeof(ssh1_3des)); -@@ -154,5 +160,15 @@ - ssh1_3des.do_cipher = ssh1_3des_cbc; - ssh1_3des.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH; - return &ssh1_3des; -+#else /*}{*/ -+ static EVP_CIPHER *ssh1_3des_p; -+ ssh1_3des_p = EVP_CIPHER_meth_new(NID_undef, /*block_size*/8, /*key_len*/16); /*XXX check return*/ -+ EVP_CIPHER_meth_set_iv_length(ssh1_3des_p, 0); -+ EVP_CIPHER_meth_set_init(ssh1_3des_p, ssh1_3des_init); -+ EVP_CIPHER_meth_set_cleanup(ssh1_3des_p, ssh1_3des_cleanup); -+ EVP_CIPHER_meth_set_do_cipher(ssh1_3des_p, ssh1_3des_cbc); -+ EVP_CIPHER_meth_set_flags(ssh1_3des_p, EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH); -+ return ssh1_3des_p; -+#endif /*}*/ - } - #endif /* WITH_SSH1 */ -diff -Naur old/cipher-bf1.c new/cipher-bf1.c ---- old/cipher-bf1.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/cipher-bf1.c 2017-03-20 11:01:25.018828127 -1000 -@@ -29,6 +29,7 @@ - #include <string.h> - - #include <openssl/evp.h> -+#include <openssl/blowfish.h> /*BF_KEY*/ - - #include "openbsd-compat/openssl-compat.h" - -@@ -89,17 +90,27 @@ - const EVP_CIPHER * - evp_ssh1_bf(void) - { -- static EVP_CIPHER ssh1_bf; -+ static EVP_CIPHER *ssh1_bfp; - -- memcpy(&ssh1_bf, EVP_bf_cbc(), sizeof(EVP_CIPHER)); -- orig_bf = ssh1_bf.do_cipher; -- ssh1_bf.nid = NID_undef; -+ ssh1_bfp = EVP_CIPHER_meth_new(NID_undef, /*block_size*/8, /*key_len*/32); -+ -+ orig_bf = EVP_CIPHER_meth_get_do_cipher(EVP_bf_cbc()); - #ifdef SSH_OLD_EVP - ssh1_bf.init = bf_ssh1_init; - #endif -- ssh1_bf.do_cipher = bf_ssh1_cipher; -- ssh1_bf.key_len = 32; -- return (&ssh1_bf); -+ EVP_CIPHER_meth_set_do_cipher(ssh1_bfp, bf_ssh1_cipher); -+ -+ /* set remaining members... */ -+ EVP_CIPHER_meth_set_iv_length(ssh1_bfp, 8); -+ EVP_CIPHER_meth_set_flags(ssh1_bfp, EVP_CIPH_VARIABLE_LENGTH | EVP_CIPH_CBC_MODE); -+ EVP_CIPHER_meth_set_init(ssh1_bfp, EVP_CIPHER_meth_get_init(EVP_bf_cbc())); -+ EVP_CIPHER_meth_set_cleanup(ssh1_bfp, EVP_CIPHER_meth_get_cleanup(EVP_bf_cbc())); -+ EVP_CIPHER_meth_set_impl_ctx_size(ssh1_bfp, /*sizeof(EVP_BF_KEY) == */sizeof(BF_KEY)); -+ EVP_CIPHER_meth_set_set_asn1_params(ssh1_bfp, EVP_CIPHER_set_asn1_iv); -+ EVP_CIPHER_meth_set_get_asn1_params(ssh1_bfp, EVP_CIPHER_get_asn1_iv); -+ EVP_CIPHER_meth_set_ctrl(ssh1_bfp, NULL); -+ /*app_data = NULL*/ -+ return (ssh1_bfp); - } - #endif /* defined(WITH_OPENSSL) && !defined(OPENSSL_NO_BF) */ - -diff -Naur old/cipher.c new/cipher.c ---- old/cipher.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/cipher.c 2017-03-20 11:01:25.018828127 -1000 -@@ -390,7 +390,10 @@ - goto out; - } - } -- if (EVP_CipherInit(cc->evp, NULL, (u_char *)key, NULL, -1) == 0) { -+ /* in OpenSSL 1.1.0, EVP_CipherInit clears all previous setups; -+ use EVP_CipherInit_ex for augmenting */ -+ if (EVP_CipherInit_ex(cc->evp, NULL, NULL, (u_char *)key, NULL, -1) == 0) -+ { - ret = SSH_ERR_LIBCRYPTO_ERROR; - goto out; - } -@@ -625,7 +628,7 @@ - len, iv)) - return SSH_ERR_LIBCRYPTO_ERROR; - } else -- memcpy(iv, cc->evp->iv, len); -+ memcpy(iv, EVP_CIPHER_CTX_iv(cc->evp), len); - break; - #endif - #ifdef WITH_SSH1 -@@ -671,7 +674,7 @@ - EVP_CTRL_GCM_SET_IV_FIXED, -1, (void *)iv)) - return SSH_ERR_LIBCRYPTO_ERROR; - } else -- memcpy(cc->evp->iv, iv, evplen); -+ memcpy(EVP_CIPHER_CTX_iv_noconst(cc->evp), iv, evplen); /*XXX using EVP_CIPHER_CTX_iv_noconst as LHS*/ - break; - #endif - #ifdef WITH_SSH1 -@@ -685,8 +688,13 @@ - } - - #ifdef WITH_OPENSSL --#define EVP_X_STATE(evp) (evp)->cipher_data --#define EVP_X_STATE_LEN(evp) (evp)->cipher->ctx_size -+# if OPENSSL_VERSION_NUMBER >= 0x10100000UL -+#define EVP_X_STATE(evp) EVP_CIPHER_CTX_get_cipher_data(evp) -+#define EVP_X_STATE_LEN(evp) EVP_CIPHER_impl_ctx_size(EVP_CIPHER_CTX_cipher(evp)) -+# else -+#define EVP_X_STATE(evp) (evp).cipher_data -+#define EVP_X_STATE_LEN(evp) (evp).cipher->ctx_size -+# endif - #endif - - int -diff -Naur old/cipher.h new/cipher.h ---- old/cipher.h 2017-03-19 16:39:27.000000000 -1000 -+++ new/cipher.h 2017-03-20 11:01:25.018828127 -1000 -@@ -63,7 +63,18 @@ - #define CIPHER_DECRYPT 0 - - struct sshcipher; -+#if 0 -+struct sshcipher_ctx { -+ int plaintext; -+ int encrypt; -+ EVP_CIPHER_CTX *evp; -+ struct chachapoly_ctx cp_ctx; /* XXX union with evp? */ -+ struct aesctr_ctx ac_ctx; /* XXX union with evp? */ -+ const struct sshcipher *cipher; -+}; -+#else - struct sshcipher_ctx; -+#endif - - u_int cipher_mask_ssh1(int); - const struct sshcipher *cipher_by_name(const char *); -diff -Naur old/dh.c new/dh.c ---- old/dh.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/dh.c 2017-03-20 11:01:25.022161903 -1000 -@@ -212,14 +212,15 @@ - /* diffie-hellman-groupN-sha1 */ - - int --dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) -+dh_pub_is_valid(const DH *dh, const BIGNUM *dh_pub) - { - int i; - int n = BN_num_bits(dh_pub); - int bits_set = 0; - BIGNUM *tmp; -+ const BIGNUM *p; - -- if (dh_pub->neg) { -+ if (BN_is_negative(dh_pub)) { - logit("invalid public DH value: negative"); - return 0; - } -@@ -232,7 +233,8 @@ - error("%s: BN_new failed", __func__); - return 0; - } -- if (!BN_sub(tmp, dh->p, BN_value_one()) || -+ DH_get0_pqg(dh, &p, NULL, NULL); -+ if (!BN_sub(tmp, p, BN_value_one()) || - BN_cmp(dh_pub, tmp) != -1) { /* pub_exp > p-2 */ - BN_clear_free(tmp); - logit("invalid public DH value: >= p-1"); -@@ -243,14 +245,14 @@ - for (i = 0; i <= n; i++) - if (BN_is_bit_set(dh_pub, i)) - bits_set++; -- debug2("bits set: %d/%d", bits_set, BN_num_bits(dh->p)); -+ debug2("bits set: %d/%d", bits_set, BN_num_bits(p)); - - /* - * if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial - */ - if (bits_set < 4) { - logit("invalid public DH value (%d/%d)", -- bits_set, BN_num_bits(dh->p)); -+ bits_set, BN_num_bits(p)); - return 0; - } - return 1; -@@ -260,9 +262,13 @@ - dh_gen_key(DH *dh, int need) - { - int pbits; -+ const BIGNUM *p, *pub_key; -+ BIGNUM *priv_key; - -- if (need < 0 || dh->p == NULL || -- (pbits = BN_num_bits(dh->p)) <= 0 || -+ DH_get0_pqg(dh, &p, NULL, NULL); -+ -+ if (need < 0 || p == NULL || -+ (pbits = BN_num_bits(p)) <= 0 || - need > INT_MAX / 2 || 2 * need > pbits) - return SSH_ERR_INVALID_ARGUMENT; - if (need < 256) -@@ -271,10 +277,13 @@ - * Pollard Rho, Big step/Little Step attacks are O(sqrt(n)), - * so double requested need here. - */ -- dh->length = MINIMUM(need * 2, pbits - 1); -- if (DH_generate_key(dh) == 0 || -- !dh_pub_is_valid(dh, dh->pub_key)) { -- BN_clear_free(dh->priv_key); -+ DH_set_length(dh, MIN(need * 2, pbits - 1)); -+ if (DH_generate_key(dh) == 0) { -+ return SSH_ERR_LIBCRYPTO_ERROR; -+ } -+ DH_get0_key(dh, &pub_key, &priv_key); -+ if (!dh_pub_is_valid(dh, pub_key)) { -+ BN_clear(priv_key); - return SSH_ERR_LIBCRYPTO_ERROR; - } - return 0; -@@ -283,16 +292,27 @@ - DH * - dh_new_group_asc(const char *gen, const char *modulus) - { -- DH *dh; -+ DH *dh = NULL; -+ BIGNUM *p=NULL, *g=NULL; - -- if ((dh = DH_new()) == NULL) -- return NULL; -- if (BN_hex2bn(&dh->p, modulus) == 0 || -- BN_hex2bn(&dh->g, gen) == 0) { -- DH_free(dh); -- return NULL; -+ if ((dh = DH_new()) == NULL || -+ (p = BN_new()) == NULL || -+ (g = BN_new()) == NULL) -+ goto null; -+ if (BN_hex2bn(&p, modulus) == 0 || -+ BN_hex2bn(&g, gen) == 0) { -+ goto null; - } -+ if (DH_set0_pqg(dh, p, NULL, g) == 0) { -+ goto null; -+ } -+ p = g = NULL; - return (dh); -+null: -+ BN_free(p); -+ BN_free(g); -+ DH_free(dh); -+ return NULL; - } - - /* -@@ -307,8 +327,8 @@ - - if ((dh = DH_new()) == NULL) - return NULL; -- dh->p = modulus; -- dh->g = gen; -+ if (DH_set0_pqg(dh, modulus, NULL, gen) == 0) -+ return NULL; - - return (dh); - } -diff -Naur old/dh.h new/dh.h ---- old/dh.h 2017-03-19 16:39:27.000000000 -1000 -+++ new/dh.h 2017-03-20 11:01:25.022161903 -1000 -@@ -42,7 +42,7 @@ - DH *dh_new_group_fallback(int); - - int dh_gen_key(DH *, int); --int dh_pub_is_valid(DH *, BIGNUM *); -+int dh_pub_is_valid(const DH *, const BIGNUM *); - - u_int dh_estimate(int); - -diff -Naur old/digest-openssl.c new/digest-openssl.c ---- old/digest-openssl.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/digest-openssl.c 2017-03-20 11:01:25.022161903 -1000 -@@ -43,7 +43,7 @@ - - struct ssh_digest_ctx { - int alg; -- EVP_MD_CTX mdctx; -+ EVP_MD_CTX *mdctx; - }; - - struct ssh_digest { -@@ -107,20 +107,21 @@ - size_t - ssh_digest_blocksize(struct ssh_digest_ctx *ctx) - { -- return EVP_MD_CTX_block_size(&ctx->mdctx); -+ return EVP_MD_CTX_block_size(ctx->mdctx); - } - - struct ssh_digest_ctx * - ssh_digest_start(int alg) - { - const struct ssh_digest *digest = ssh_digest_by_alg(alg); -- struct ssh_digest_ctx *ret; -+ struct ssh_digest_ctx *ret = NULL; - - if (digest == NULL || ((ret = calloc(1, sizeof(*ret))) == NULL)) - return NULL; - ret->alg = alg; -- EVP_MD_CTX_init(&ret->mdctx); -- if (EVP_DigestInit_ex(&ret->mdctx, digest->mdfunc(), NULL) != 1) { -+ if ((ret->mdctx = EVP_MD_CTX_new()) == NULL || -+ EVP_DigestInit_ex(ret->mdctx, digest->mdfunc(), NULL) != 1) { -+ EVP_MD_CTX_free(ret->mdctx); - free(ret); - return NULL; - } -@@ -133,7 +134,7 @@ - if (from->alg != to->alg) - return SSH_ERR_INVALID_ARGUMENT; - /* we have bcopy-style order while openssl has memcpy-style */ -- if (!EVP_MD_CTX_copy_ex(&to->mdctx, &from->mdctx)) -+ if (!EVP_MD_CTX_copy_ex(to->mdctx, from->mdctx)) - return SSH_ERR_LIBCRYPTO_ERROR; - return 0; - } -@@ -141,7 +142,7 @@ - int - ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) - { -- if (EVP_DigestUpdate(&ctx->mdctx, m, mlen) != 1) -+ if (EVP_DigestUpdate(ctx->mdctx, m, mlen) != 1) - return SSH_ERR_LIBCRYPTO_ERROR; - return 0; - } -@@ -162,7 +163,7 @@ - return SSH_ERR_INVALID_ARGUMENT; - if (dlen < digest->digest_len) /* No truncation allowed */ - return SSH_ERR_INVALID_ARGUMENT; -- if (EVP_DigestFinal_ex(&ctx->mdctx, d, &l) != 1) -+ if (EVP_DigestFinal_ex(ctx->mdctx, d, &l) != 1) - return SSH_ERR_LIBCRYPTO_ERROR; - if (l != digest->digest_len) /* sanity */ - return SSH_ERR_INTERNAL_ERROR; -@@ -173,7 +174,7 @@ - ssh_digest_free(struct ssh_digest_ctx *ctx) - { - if (ctx != NULL) { -- EVP_MD_CTX_cleanup(&ctx->mdctx); -+ EVP_MD_CTX_free(ctx->mdctx); - explicit_bzero(ctx, sizeof(*ctx)); - free(ctx); - } -diff -Naur old/kexdhc.c new/kexdhc.c ---- old/kexdhc.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/kexdhc.c 2017-03-20 11:01:25.022161903 -1000 -@@ -81,11 +81,16 @@ - goto out; - } - debug("sending SSH2_MSG_KEXDH_INIT"); -- if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || -- (r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || -+ { -+ const BIGNUM *pub_key; -+ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) -+ goto out; -+ DH_get0_key(kex->dh, &pub_key, NULL); -+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || -+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || - (r = sshpkt_send(ssh)) != 0) - goto out; -+ } - #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, kex->dh); - fprintf(stderr, "pub= "); -@@ -170,6 +175,9 @@ - - /* calc and verify H */ - hashlen = sizeof(hash); -+ { -+ const BIGNUM *pub_key; -+ DH_get0_key(kex->dh, &pub_key, NULL); - if ((r = kex_dh_hash( - kex->hash_alg, - kex->client_version_string, -@@ -177,11 +185,13 @@ - sshbuf_ptr(kex->my), sshbuf_len(kex->my), - sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), - server_host_key_blob, sbloblen, -- kex->dh->pub_key, -+ pub_key, - dh_server_pub, - shared_secret, -- hash, &hashlen)) != 0) -+ hash, &hashlen)) != 0) { - goto out; -+ } -+ } - - if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen, - ssh->compat)) != 0) -diff -Naur old/kexdhs.c new/kexdhs.c ---- old/kexdhs.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/kexdhs.c 2017-03-20 11:01:25.022161903 -1000 -@@ -87,6 +87,10 @@ - ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_INIT, &input_kex_dh_init); - r = 0; - out: -+ if (r != 0) { -+ if (kex->dh) DH_free(kex->dh); -+ kex->dh = NULL; -+ } - return r; - } - -@@ -164,6 +168,9 @@ - goto out; - /* calc H */ - hashlen = sizeof(hash); -+ { -+ const BIGNUM *pub_key; -+ DH_get0_key(kex->dh, &pub_key, NULL); - if ((r = kex_dh_hash( - kex->hash_alg, - kex->client_version_string, -@@ -172,10 +179,12 @@ - sshbuf_ptr(kex->my), sshbuf_len(kex->my), - server_host_key_blob, sbloblen, - dh_client_pub, -- kex->dh->pub_key, -+ pub_key, - shared_secret, -- hash, &hashlen)) != 0) -+ hash, &hashlen)) != 0) { - goto out; -+ } -+ } - - /* save session id := H */ - if (kex->session_id == NULL) { -@@ -196,12 +205,17 @@ - /* destroy_sensitive_data(); */ - - /* send server hostkey, DH pubkey 'f' and singed H */ -+ { -+ const BIGNUM *pub_key; -+ DH_get0_key(kex->dh, &pub_key, NULL); - if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 || - (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ -+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || /* f */ - (r = sshpkt_put_string(ssh, signature, slen)) != 0 || -- (r = sshpkt_send(ssh)) != 0) -+ (r = sshpkt_send(ssh)) != 0) { - goto out; -+ } -+ } - - if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) - r = kex_send_newkeys(ssh); -diff -Naur old/kexgexc.c new/kexgexc.c ---- old/kexgexc.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/kexgexc.c 2017-03-20 11:01:25.022161903 -1000 -@@ -119,11 +119,17 @@ - p = g = NULL; /* belong to kex->dh now */ - - /* generate and send 'e', client DH public key */ -- if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || -- (r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || -- (r = sshpkt_send(ssh)) != 0) -+ { -+ const BIGNUM *pub_key; -+ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) -+ goto out; -+ DH_get0_key(kex->dh, &pub_key, NULL); -+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || -+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || -+ (r = sshpkt_send(ssh)) != 0) { - goto out; -+ } -+ } - debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); - #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, kex->dh); -@@ -135,10 +141,12 @@ - ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply); - r = 0; - out: -- if (p) -+ if (r != 0) { - BN_clear_free(p); -- if (g) - BN_clear_free(g); -+ DH_free(kex->dh); -+ kex->dh = NULL; -+ } - return r; - } - -@@ -220,6 +228,10 @@ - - /* calc and verify H */ - hashlen = sizeof(hash); -+ { -+ const BIGNUM *p, *g, *pub_key; -+ DH_get0_pqg(kex->dh, &p, NULL, &g); -+ DH_get0_key(kex->dh, &pub_key, NULL); - if ((r = kexgex_hash( - kex->hash_alg, - kex->client_version_string, -@@ -228,12 +240,14 @@ - sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), - server_host_key_blob, sbloblen, - kex->min, kex->nbits, kex->max, -- kex->dh->p, kex->dh->g, -- kex->dh->pub_key, -+ p, g, -+ pub_key, - dh_server_pub, - shared_secret, -- hash, &hashlen)) != 0) -+ hash, &hashlen)) != 0) { - goto out; -+ } -+ } - - if ((r = sshkey_verify(server_host_key, signature, slen, hash, - hashlen, ssh->compat)) != 0) -diff -Naur old/kexgexs.c new/kexgexs.c ---- old/kexgexs.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/kexgexs.c 2017-03-20 11:01:25.025495678 -1000 -@@ -102,11 +102,16 @@ - goto out; - } - debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); -+ { -+ const BIGNUM *p, *g; -+ DH_get0_pqg(kex->dh, &p, NULL, &g); - if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || -- (r = sshpkt_send(ssh)) != 0) -+ (r = sshpkt_put_bignum2(ssh, p)) != 0 || -+ (r = sshpkt_put_bignum2(ssh, g)) != 0 || -+ (r = sshpkt_send(ssh)) != 0) { - goto out; -+ } -+ } - - /* Compute our exchange value in parallel with the client */ - if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) -@@ -116,6 +121,10 @@ - ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); - r = 0; - out: -+ if (r != 0) { -+ DH_free(kex->dh); -+ kex->dh = NULL; -+ } - return r; - } - -@@ -193,6 +202,10 @@ - goto out; - /* calc H */ - hashlen = sizeof(hash); -+ { -+ const BIGNUM *p, *g, *pub_key; -+ DH_get0_pqg(kex->dh, &p, NULL, &g); -+ DH_get0_key(kex->dh, &pub_key, NULL); - if ((r = kexgex_hash( - kex->hash_alg, - kex->client_version_string, -@@ -201,12 +214,14 @@ - sshbuf_ptr(kex->my), sshbuf_len(kex->my), - server_host_key_blob, sbloblen, - kex->min, kex->nbits, kex->max, -- kex->dh->p, kex->dh->g, -+ p, g, - dh_client_pub, -- kex->dh->pub_key, -+ pub_key, - shared_secret, -- hash, &hashlen)) != 0) -+ hash, &hashlen)) != 0) { - goto out; -+ } -+ } - - /* save session id := H */ - if (kex->session_id == NULL) { -@@ -227,12 +242,17 @@ - /* destroy_sensitive_data(); */ - - /* send server hostkey, DH pubkey 'f' and singed H */ -+ { -+ const BIGNUM *pub_key; -+ DH_get0_key(kex->dh, &pub_key, NULL); - if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 || - (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ -+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || /* f */ - (r = sshpkt_put_string(ssh, signature, slen)) != 0 || -- (r = sshpkt_send(ssh)) != 0) -+ (r = sshpkt_send(ssh)) != 0) { - goto out; -+ } -+ } - - if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) - r = kex_send_newkeys(ssh); -diff -Naur old/monitor.c new/monitor.c ---- old/monitor.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/monitor.c 2017-03-20 11:01:25.025495678 -1000 -@@ -580,10 +580,12 @@ - buffer_put_char(m, 0); - return (0); - } else { -+ const BIGNUM *p, *g; -+ DH_get0_pqg(dh, &p, NULL, &g); - /* Send first bignum */ - buffer_put_char(m, 1); -- buffer_put_bignum2(m, dh->p); -- buffer_put_bignum2(m, dh->g); -+ buffer_put_bignum2(m, p); -+ buffer_put_bignum2(m, g); - - DH_free(dh); - } -diff -Naur old/openbsd-compat/openssl-compat.c new/openbsd-compat/openssl-compat.c ---- old/openbsd-compat/openssl-compat.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/openbsd-compat/openssl-compat.c 2017-03-20 11:02:25.132784743 -1000 -@@ -75,7 +75,6 @@ - /* Enable use of crypto hardware */ - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); -- OPENSSL_config(NULL); - } - #endif - -diff -Naur old/regress/unittests/sshkey/test_file.c new/regress/unittests/sshkey/test_file.c ---- old/regress/unittests/sshkey/test_file.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/regress/unittests/sshkey/test_file.c 2017-03-20 11:01:25.028829454 -1000 -@@ -109,9 +109,14 @@ - a = load_bignum("rsa_1.param.n"); - b = load_bignum("rsa_1.param.p"); - c = load_bignum("rsa_1.param.q"); -- ASSERT_BIGNUM_EQ(k1->rsa->n, a); -- ASSERT_BIGNUM_EQ(k1->rsa->p, b); -- ASSERT_BIGNUM_EQ(k1->rsa->q, c); -+ { -+ const BIGNUM *n, *p, *q; -+ RSA_get0_key(k1->rsa, &n, NULL, NULL); -+ RSA_get0_factors(k1->rsa, &p, &q); -+ ASSERT_BIGNUM_EQ(n, a); -+ ASSERT_BIGNUM_EQ(p, b); -+ ASSERT_BIGNUM_EQ(q, c); -+ } - BN_free(a); - BN_free(b); - BN_free(c); -@@ -200,9 +205,14 @@ - a = load_bignum("dsa_1.param.g"); - b = load_bignum("dsa_1.param.priv"); - c = load_bignum("dsa_1.param.pub"); -- ASSERT_BIGNUM_EQ(k1->dsa->g, a); -- ASSERT_BIGNUM_EQ(k1->dsa->priv_key, b); -- ASSERT_BIGNUM_EQ(k1->dsa->pub_key, c); -+ { -+ const BIGNUM *g, *priv_key, *pub_key; -+ DSA_get0_pqg(k1->dsa, NULL, NULL, &g); -+ DSA_get0_key(k1->dsa, &pub_key, &priv_key); -+ ASSERT_BIGNUM_EQ(g, a); -+ ASSERT_BIGNUM_EQ(priv_key, b); -+ ASSERT_BIGNUM_EQ(pub_key, c); -+ } - BN_free(a); - BN_free(b); - BN_free(c); -diff -Naur old/regress/unittests/sshkey/test_sshkey.c new/regress/unittests/sshkey/test_sshkey.c ---- old/regress/unittests/sshkey/test_sshkey.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/regress/unittests/sshkey/test_sshkey.c 2017-03-20 11:01:25.028829454 -1000 -@@ -197,9 +197,14 @@ - k1 = sshkey_new(KEY_RSA1); - ASSERT_PTR_NE(k1, NULL); - ASSERT_PTR_NE(k1->rsa, NULL); -- ASSERT_PTR_NE(k1->rsa->n, NULL); -- ASSERT_PTR_NE(k1->rsa->e, NULL); -- ASSERT_PTR_EQ(k1->rsa->p, NULL); -+ { -+ const BIGNUM *n, *e, *p; -+ RSA_get0_key(k1->rsa, &n, &e, NULL); -+ RSA_get0_factors(k1->rsa, &p, NULL); -+ ASSERT_PTR_NE(n, NULL); -+ ASSERT_PTR_NE(e, NULL); -+ ASSERT_PTR_EQ(p, NULL); -+ } - sshkey_free(k1); - TEST_DONE(); - -@@ -207,9 +212,14 @@ - k1 = sshkey_new(KEY_RSA); - ASSERT_PTR_NE(k1, NULL); - ASSERT_PTR_NE(k1->rsa, NULL); -- ASSERT_PTR_NE(k1->rsa->n, NULL); -- ASSERT_PTR_NE(k1->rsa->e, NULL); -- ASSERT_PTR_EQ(k1->rsa->p, NULL); -+ { -+ const BIGNUM *n, *e, *p; -+ RSA_get0_key(k1->rsa, &n, &e, NULL); -+ RSA_get0_factors(k1->rsa, &p, NULL); -+ ASSERT_PTR_NE(n, NULL); -+ ASSERT_PTR_NE(e, NULL); -+ ASSERT_PTR_EQ(p, NULL); -+ } - sshkey_free(k1); - TEST_DONE(); - -@@ -217,8 +227,13 @@ - k1 = sshkey_new(KEY_DSA); - ASSERT_PTR_NE(k1, NULL); - ASSERT_PTR_NE(k1->dsa, NULL); -- ASSERT_PTR_NE(k1->dsa->g, NULL); -- ASSERT_PTR_EQ(k1->dsa->priv_key, NULL); -+ { -+ const BIGNUM *g, *priv_key; -+ DSA_get0_pqg(k1->dsa, NULL, NULL, &g); -+ DSA_get0_key(k1->dsa, NULL, &priv_key); -+ ASSERT_PTR_NE(g, NULL); -+ ASSERT_PTR_EQ(priv_key, NULL); -+ } - sshkey_free(k1); - TEST_DONE(); - -@@ -244,9 +259,14 @@ - k1 = sshkey_new_private(KEY_RSA); - ASSERT_PTR_NE(k1, NULL); - ASSERT_PTR_NE(k1->rsa, NULL); -- ASSERT_PTR_NE(k1->rsa->n, NULL); -- ASSERT_PTR_NE(k1->rsa->e, NULL); -- ASSERT_PTR_NE(k1->rsa->p, NULL); -+ { -+ const BIGNUM *n, *e, *p; -+ RSA_get0_key(k1->rsa, &n, &e, NULL); -+ RSA_get0_factors(k1->rsa, &p, NULL); -+ ASSERT_PTR_NE(n, NULL); -+ ASSERT_PTR_NE(e, NULL); -+ ASSERT_PTR_NE(p, NULL); -+ } - ASSERT_INT_EQ(sshkey_add_private(k1), 0); - sshkey_free(k1); - TEST_DONE(); -@@ -255,8 +275,13 @@ - k1 = sshkey_new_private(KEY_DSA); - ASSERT_PTR_NE(k1, NULL); - ASSERT_PTR_NE(k1->dsa, NULL); -- ASSERT_PTR_NE(k1->dsa->g, NULL); -- ASSERT_PTR_NE(k1->dsa->priv_key, NULL); -+ { -+ const BIGNUM *g, *priv_key; -+ DSA_get0_pqg(k1->dsa, NULL, NULL, &g); -+ DSA_get0_key(k1->dsa, NULL, &priv_key); -+ ASSERT_PTR_NE(g, NULL); -+ ASSERT_PTR_NE(priv_key, NULL); -+ } - ASSERT_INT_EQ(sshkey_add_private(k1), 0); - sshkey_free(k1); - TEST_DONE(); -@@ -295,18 +320,28 @@ - ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1024, &kr), 0); - ASSERT_PTR_NE(kr, NULL); - ASSERT_PTR_NE(kr->rsa, NULL); -- ASSERT_PTR_NE(kr->rsa->n, NULL); -- ASSERT_PTR_NE(kr->rsa->e, NULL); -- ASSERT_PTR_NE(kr->rsa->p, NULL); -- ASSERT_INT_EQ(BN_num_bits(kr->rsa->n), 1024); -+ { -+ const BIGNUM *n, *e, *p; -+ RSA_get0_key(kr->rsa, &n, &e, NULL); -+ RSA_get0_factors(kr->rsa, &p, NULL); -+ ASSERT_PTR_NE(n, NULL); -+ ASSERT_PTR_NE(e, NULL); -+ ASSERT_PTR_NE(p, NULL); -+ ASSERT_INT_EQ(BN_num_bits(n), 1024); -+ } - TEST_DONE(); - - TEST_START("generate KEY_DSA"); - ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &kd), 0); - ASSERT_PTR_NE(kd, NULL); - ASSERT_PTR_NE(kd->dsa, NULL); -- ASSERT_PTR_NE(kd->dsa->g, NULL); -- ASSERT_PTR_NE(kd->dsa->priv_key, NULL); -+ { -+ const BIGNUM *g, *priv_key; -+ DSA_get0_pqg(kd->dsa, NULL, NULL, &g); -+ DSA_get0_key(kd->dsa, NULL, &priv_key); -+ ASSERT_PTR_NE(g, NULL); -+ ASSERT_PTR_NE(priv_key, NULL); -+ } - TEST_DONE(); - - #ifdef OPENSSL_HAS_ECC -@@ -333,9 +368,14 @@ - ASSERT_PTR_NE(kr, k1); - ASSERT_INT_EQ(k1->type, KEY_RSA); - ASSERT_PTR_NE(k1->rsa, NULL); -- ASSERT_PTR_NE(k1->rsa->n, NULL); -- ASSERT_PTR_NE(k1->rsa->e, NULL); -- ASSERT_PTR_EQ(k1->rsa->p, NULL); -+ { -+ const BIGNUM *n, *e, *p; -+ RSA_get0_key(k1->rsa, &n, &e, NULL); -+ RSA_get0_factors(k1->rsa, &p, NULL); -+ ASSERT_PTR_NE(n, NULL); -+ ASSERT_PTR_NE(e, NULL); -+ ASSERT_PTR_EQ(p, NULL); -+ } - TEST_DONE(); - - TEST_START("equal KEY_RSA/demoted KEY_RSA"); -@@ -349,8 +389,13 @@ - ASSERT_PTR_NE(kd, k1); - ASSERT_INT_EQ(k1->type, KEY_DSA); - ASSERT_PTR_NE(k1->dsa, NULL); -- ASSERT_PTR_NE(k1->dsa->g, NULL); -- ASSERT_PTR_EQ(k1->dsa->priv_key, NULL); -+ { -+ const BIGNUM *g, *priv_key; -+ DSA_get0_pqg(k1->dsa, NULL, NULL, &g); -+ DSA_get0_key(k1->dsa, NULL, &priv_key); -+ ASSERT_PTR_NE(g, NULL); -+ ASSERT_PTR_EQ(priv_key, NULL); -+ } - TEST_DONE(); - - TEST_START("equal KEY_DSA/demoted KEY_DSA"); -diff -Naur old/rsa.c new/rsa.c ---- old/rsa.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/rsa.c 2017-03-20 11:01:25.025495678 -1000 -@@ -76,11 +76,14 @@ - { - u_char *inbuf = NULL, *outbuf = NULL; - int len, ilen, olen, r = SSH_ERR_INTERNAL_ERROR; -+ const BIGNUM *e, *n; - -- if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) -+ RSA_get0_key(key, &n, &e, NULL); -+ -+ if (BN_num_bits(e) < 2 || !BN_is_odd(e)) - return SSH_ERR_INVALID_ARGUMENT; - -- olen = BN_num_bytes(key->n); -+ olen = BN_num_bytes(n); - if ((outbuf = malloc(olen)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; -@@ -123,7 +126,7 @@ - u_char *inbuf = NULL, *outbuf = NULL; - int len, ilen, olen, r = SSH_ERR_INTERNAL_ERROR; - -- olen = BN_num_bytes(key->n); -+ olen = RSA_size(key); - if ((outbuf = malloc(olen)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; -@@ -172,13 +175,27 @@ - goto out; - } - -- if ((BN_sub(aux, rsa->q, BN_value_one()) == 0) || -- (BN_mod(rsa->dmq1, rsa->d, aux, ctx) == 0) || -- (BN_sub(aux, rsa->p, BN_value_one()) == 0) || -- (BN_mod(rsa->dmp1, rsa->d, aux, ctx) == 0)) { -+ { -+ const BIGNUM *q, *d, *p; -+ BIGNUM *dmq1=NULL, *dmp1=NULL; -+ if ((dmq1 = BN_new()) == NULL || -+ (dmp1 = BN_new()) == NULL ) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ RSA_get0_key(rsa, NULL, NULL, &d); -+ RSA_get0_factors(rsa, &p, &q); -+ if ((BN_sub(aux, q, BN_value_one()) == 0) || -+ (BN_mod(dmq1, d, aux, ctx) == 0) || -+ (BN_sub(aux, p, BN_value_one()) == 0) || -+ (BN_mod(dmp1, d, aux, ctx) == 0) || -+ RSA_set0_crt_params(rsa, dmp1, dmq1, NULL) == 0) { - r = SSH_ERR_LIBCRYPTO_ERROR; -+ BN_clear_free(dmp1); -+ BN_clear_free(dmq1); - goto out; - } -+ } - r = 0; - out: - BN_clear_free(aux); -diff -Naur old/ssh-dss.c new/ssh-dss.c ---- old/ssh-dss.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/ssh-dss.c 2017-03-20 11:01:25.025495678 -1000 -@@ -53,6 +53,7 @@ - DSA_SIG *sig = NULL; - u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN]; - size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); -+ const BIGNUM *r, *s; - struct sshbuf *b = NULL; - int ret = SSH_ERR_INVALID_ARGUMENT; - -@@ -76,15 +77,16 @@ - goto out; - } - -- rlen = BN_num_bytes(sig->r); -- slen = BN_num_bytes(sig->s); -+ DSA_SIG_get0(sig, &r, &s); -+ rlen = BN_num_bytes(r); -+ slen = BN_num_bytes(s); - if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { - ret = SSH_ERR_INTERNAL_ERROR; - goto out; - } - explicit_bzero(sigblob, SIGBLOB_LEN); -- BN_bn2bin(sig->r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); -- BN_bn2bin(sig->s, sigblob + SIGBLOB_LEN - slen); -+ BN_bn2bin(r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); -+ BN_bn2bin(s, sigblob + SIGBLOB_LEN - slen); - - if (compat & SSH_BUG_SIGBLOB) { - if (sigp != NULL) { -@@ -176,17 +178,26 @@ - } - - /* parse signature */ -+ { -+ BIGNUM *r=NULL, *s=NULL; - if ((sig = DSA_SIG_new()) == NULL || -- (sig->r = BN_new()) == NULL || -- (sig->s = BN_new()) == NULL) { -+ (r = BN_new()) == NULL || -+ (s = BN_new()) == NULL) { - ret = SSH_ERR_ALLOC_FAIL; -+ BN_free(r); -+ BN_free(s); - goto out; - } -- if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig->r) == NULL) || -- (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) { -+ if ((BN_bin2bn(sigblob, INTBLOB_LEN, r) == NULL) || -+ (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, s) == NULL)) { - ret = SSH_ERR_LIBCRYPTO_ERROR; -+ BN_free(r); -+ BN_free(s); - goto out; - } -+ DSA_SIG_set0(sig, r, s); -+ r = s = NULL; -+ } - - /* sha1 the data */ - if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, -diff -Naur old/ssh-ecdsa.c new/ssh-ecdsa.c ---- old/ssh-ecdsa.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/ssh-ecdsa.c 2017-03-20 11:01:25.025495678 -1000 -@@ -80,9 +80,14 @@ - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 || -- (ret = sshbuf_put_bignum2(bb, sig->s)) != 0) -+ { -+ const BIGNUM *r, *s; -+ ECDSA_SIG_get0(sig, &r, &s); -+ if ((ret = sshbuf_put_bignum2(bb, r)) != 0 || -+ (ret = sshbuf_put_bignum2(bb, s)) != 0) { - goto out; -+ } -+ } - if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || - (ret = sshbuf_put_stringb(b, bb)) != 0) - goto out; -@@ -151,11 +156,27 @@ - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if (sshbuf_get_bignum2(sigbuf, sig->r) != 0 || -- sshbuf_get_bignum2(sigbuf, sig->s) != 0) { -+ { -+ BIGNUM *r=NULL, *s=NULL; -+ if ((r = BN_new()) == NULL || -+ (s = BN_new()) == NULL) { -+ ret = SSH_ERR_ALLOC_FAIL; -+ goto out_rs; -+ } -+ if (sshbuf_get_bignum2(sigbuf, r) != 0 || -+ sshbuf_get_bignum2(sigbuf, s) != 0) { - ret = SSH_ERR_INVALID_FORMAT; -+ goto out_rs; -+ } -+ if (ECDSA_SIG_set0(sig, r, s) == 0) { -+ ret = SSH_ERR_LIBCRYPTO_ERROR; -+out_rs: -+ BN_free(r); -+ BN_free(s); - goto out; - } -+ r = s = NULL; -+ } - if (sshbuf_len(sigbuf) != 0) { - ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; - goto out; -diff -Naur old/ssh-keygen.c new/ssh-keygen.c ---- old/ssh-keygen.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/ssh-keygen.c 2017-03-20 11:01:25.025495678 -1000 -@@ -483,11 +483,33 @@ - - switch (key->type) { - case KEY_DSA: -- buffer_get_bignum_bits(b, key->dsa->p); -- buffer_get_bignum_bits(b, key->dsa->g); -- buffer_get_bignum_bits(b, key->dsa->q); -- buffer_get_bignum_bits(b, key->dsa->pub_key); -- buffer_get_bignum_bits(b, key->dsa->priv_key); -+ { -+ BIGNUM *p=NULL, *g=NULL, *q=NULL, *pub_key=NULL, *priv_key=NULL; -+ if ((p=BN_new()) == NULL || -+ (g=BN_new()) == NULL || -+ (q=BN_new()) == NULL || -+ (pub_key=BN_new()) == NULL || -+ (priv_key=BN_new()) == NULL) { -+ BN_free(p); -+ BN_free(g); -+ BN_free(q); -+ BN_free(pub_key); -+ BN_free(priv_key); -+ return NULL; -+ } -+ buffer_get_bignum_bits(b, p); -+ buffer_get_bignum_bits(b, g); -+ buffer_get_bignum_bits(b, q); -+ buffer_get_bignum_bits(b, pub_key); -+ buffer_get_bignum_bits(b, priv_key); -+ if (DSA_set0_pqg(key->dsa, p, q, g) == 0 || -+ DSA_set0_key(key->dsa, pub_key, priv_key) == 0) { -+ fatal("failed to set DSA key"); -+ BN_free(p); BN_free(g); BN_free(q); -+ BN_free(pub_key); BN_free(priv_key); -+ return NULL; -+ } -+ } - break; - case KEY_RSA: - if ((r = sshbuf_get_u8(b, &e1)) != 0 || -@@ -504,16 +526,52 @@ - e += e3; - debug("e %lx", e); - } -- if (!BN_set_word(key->rsa->e, e)) { -+ { -+ BIGNUM *rsa_e = NULL; -+ BIGNUM *d=NULL, *n=NULL, *iqmp=NULL, *q=NULL, *p=NULL; -+ BIGNUM *dmp1=NULL, *dmq1=NULL; /* dummy input to set in RSA_set0_crt_params */ -+ rsa_e = BN_new(); -+ if (!rsa_e || !BN_set_word(rsa_e, e)) { -+ if (rsa_e) BN_free(rsa_e); - sshbuf_free(b); - sshkey_free(key); - return NULL; - } -- buffer_get_bignum_bits(b, key->rsa->d); -- buffer_get_bignum_bits(b, key->rsa->n); -- buffer_get_bignum_bits(b, key->rsa->iqmp); -- buffer_get_bignum_bits(b, key->rsa->q); -- buffer_get_bignum_bits(b, key->rsa->p); -+ if ((d=BN_new()) == NULL || -+ (n=BN_new()) == NULL || -+ (iqmp=BN_new()) == NULL || -+ (q=BN_new()) == NULL || -+ (p=BN_new()) == NULL || -+ (dmp1=BN_new()) == NULL || -+ (dmq1=BN_new()) == NULL) { -+ BN_free(d); BN_free(n); BN_free(iqmp); -+ BN_free(q); BN_free(p); -+ BN_free(dmp1); BN_free(dmq1); -+ return NULL; -+ } -+ BN_clear(dmp1); BN_clear(dmq1); -+ buffer_get_bignum_bits(b, d); -+ buffer_get_bignum_bits(b, n); -+ buffer_get_bignum_bits(b, iqmp); -+ buffer_get_bignum_bits(b, q); -+ buffer_get_bignum_bits(b, p); -+ if (RSA_set0_key(key->rsa, n, rsa_e, d) == 0) -+ goto null; -+ n = d = NULL; -+ if (RSA_set0_factors(key->rsa, p, q) == 0) -+ goto null; -+ p = q = NULL; -+ /* dmp1, dmq1 should not be NULL for initial set0 */ -+ if (RSA_set0_crt_params(key->rsa, dmp1, dmq1, iqmp) == 0) { -+null: -+ fatal("Failed to set RSA parameters"); -+ BN_free(d); BN_free(n); BN_free(iqmp); -+ BN_free(q); BN_free(p); -+ BN_free(dmp1); BN_free(dmq1); -+ return NULL; -+ } -+ dmp1 = dmq1 = iqmp = NULL; -+ } - if ((r = rsa_generate_additional_parameters(key->rsa)) != 0) - fatal("generate RSA parameters failed: %s", ssh_err(r)); - break; -@@ -623,7 +681,7 @@ - identity_file); - } - fclose(fp); -- switch (EVP_PKEY_type(pubkey->type)) { -+ switch (EVP_PKEY_type(EVP_PKEY_id(pubkey))) { - case EVP_PKEY_RSA: - if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) - fatal("sshkey_new failed"); -@@ -647,7 +705,7 @@ - #endif - default: - fatal("%s: unsupported pubkey type %d", __func__, -- EVP_PKEY_type(pubkey->type)); -+ EVP_PKEY_type(EVP_PKEY_id(pubkey))); - } - EVP_PKEY_free(pubkey); - return; -diff -Naur old/ssh-pkcs11-client.c new/ssh-pkcs11-client.c ---- old/ssh-pkcs11-client.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/ssh-pkcs11-client.c 2017-03-20 11:01:25.025495678 -1000 -@@ -143,12 +143,13 @@ - static int - wrap_key(RSA *rsa) - { -- static RSA_METHOD helper_rsa; -+ static RSA_METHOD *helper_rsa; - -- memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa)); -- helper_rsa.name = "ssh-pkcs11-helper"; -- helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt; -- RSA_set_method(rsa, &helper_rsa); -+ if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL) -+ return (-1); /* XXX but caller isn't checking */ -+ RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper"); -+ RSA_meth_set_priv_enc(helper_rsa, pkcs11_rsa_private_encrypt); -+ RSA_set_method(rsa, helper_rsa); - return (0); - } - -diff -Naur old/ssh-pkcs11.c new/ssh-pkcs11.c ---- old/ssh-pkcs11.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/ssh-pkcs11.c 2017-03-20 11:01:25.025495678 -1000 -@@ -67,7 +67,7 @@ - struct pkcs11_provider *provider; - CK_ULONG slotidx; - int (*orig_finish)(RSA *rsa); -- RSA_METHOD rsa_method; -+ RSA_METHOD *rsa_method; - char *keyid; - int keyid_len; - }; -@@ -326,13 +326,15 @@ - k11->keyid = xmalloc(k11->keyid_len); - memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); - } -- k11->orig_finish = def->finish; -- memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); -- k11->rsa_method.name = "pkcs11"; -- k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt; -- k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt; -- k11->rsa_method.finish = pkcs11_rsa_finish; -- RSA_set_method(rsa, &k11->rsa_method); -+ k11->orig_finish = RSA_meth_get_finish(def); -+ -+ if ((k11->rsa_method = RSA_meth_new("pkcs11", RSA_meth_get_flags(def))) == NULL) -+ return -1; -+ RSA_meth_set_priv_enc(k11->rsa_method, pkcs11_rsa_private_encrypt); -+ RSA_meth_set_priv_dec(k11->rsa_method, pkcs11_rsa_private_decrypt); -+ RSA_meth_set_finish(k11->rsa_method, pkcs11_rsa_finish); -+ -+ RSA_set_method(rsa, k11->rsa_method); - RSA_set_app_data(rsa, k11); - return (0); - } -@@ -512,10 +514,19 @@ - if ((rsa = RSA_new()) == NULL) { - error("RSA_new failed"); - } else { -- rsa->n = BN_bin2bn(attribs[1].pValue, -- attribs[1].ulValueLen, NULL); -- rsa->e = BN_bin2bn(attribs[2].pValue, -- attribs[2].ulValueLen, NULL); -+ BIGNUM *n=NULL, *e=NULL; -+ n = BN_new(); -+ e = BN_new(); -+ if (n == NULL || e == NULL) -+ error("BN_new alloc failed"); -+ if (BN_bin2bn(attribs[1].pValue, -+ attribs[1].ulValueLen, n) == NULL || -+ BN_bin2bn(attribs[2].pValue, -+ attribs[2].ulValueLen, e) == NULL) -+ error("BN_bin2bn failed"); -+ if (RSA_set0_key(rsa, n, e, NULL) == 0) -+ error("RSA_set0_key failed"); -+ n = e = NULL; - } - } else { - cp = attribs[2].pValue; -@@ -525,17 +536,20 @@ - == NULL) { - error("d2i_X509 failed"); - } else if ((evp = X509_get_pubkey(x509)) == NULL || -- evp->type != EVP_PKEY_RSA || -- evp->pkey.rsa == NULL) { -+ EVP_PKEY_id(evp) != EVP_PKEY_RSA || -+ EVP_PKEY_get0_RSA(evp) == NULL) { - debug("X509_get_pubkey failed or no rsa"); -- } else if ((rsa = RSAPublicKey_dup(evp->pkey.rsa)) -+ } else if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) - == NULL) { - error("RSAPublicKey_dup"); - } - if (x509) - X509_free(x509); - } -- if (rsa && rsa->n && rsa->e && -+ { -+ const BIGNUM *n, *e; -+ RSA_get0_key(rsa, &n, &e, NULL); -+ if (rsa && n && e && - pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { - key = sshkey_new(KEY_UNSPEC); - key->rsa = rsa; -@@ -554,6 +568,7 @@ - } else if (rsa) { - RSA_free(rsa); - } -+ } - for (i = 0; i < 3; i++) - free(attribs[i].pValue); - } -diff -Naur old/ssh-rsa.c new/ssh-rsa.c ---- old/ssh-rsa.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/ssh-rsa.c 2017-03-20 11:01:25.028829454 -1000 -@@ -100,7 +100,7 @@ - hash_alg = rsa_hash_alg_from_ident(alg_ident); - if (key == NULL || key->rsa == NULL || hash_alg == -1 || - sshkey_type_plain(key->type) != KEY_RSA || -- BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) -+ RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) - return SSH_ERR_INVALID_ARGUMENT; - slen = RSA_size(key->rsa); - if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) -@@ -172,7 +172,7 @@ - - if (key == NULL || key->rsa == NULL || - sshkey_type_plain(key->type) != KEY_RSA || -- BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE || -+ RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE || - sig == NULL || siglen == 0) - return SSH_ERR_INVALID_ARGUMENT; - -diff -Naur old/sshkey.c new/sshkey.c ---- old/sshkey.c 2017-03-19 16:39:27.000000000 -1000 -+++ new/sshkey.c 2017-03-20 11:04:12.616131625 -1000 -@@ -275,10 +275,18 @@ - case KEY_RSA1: - case KEY_RSA: - case KEY_RSA_CERT: -+#if OPENSSL_VERSION_NUMBER >= 0x10100000UL -+ return RSA_bits(k->rsa); -+#else - return BN_num_bits(k->rsa->n); -+#endif - case KEY_DSA: - case KEY_DSA_CERT: -+#if OPENSSL_VERSION_NUMBER >= 0x10100000UL -+ return DSA_bits(k->dsa); -+#else - return BN_num_bits(k->dsa->p); -+#endif - case KEY_ECDSA: - case KEY_ECDSA_CERT: - return sshkey_curve_nid_to_bits(k->ecdsa_nid); -@@ -478,28 +486,55 @@ - case KEY_RSA1: - case KEY_RSA: - case KEY_RSA_CERT: -+ { -+ BIGNUM *n=NULL, *e=NULL; /* just allocate */ - if ((rsa = RSA_new()) == NULL || -- (rsa->n = BN_new()) == NULL || -- (rsa->e = BN_new()) == NULL) { -+ (n = BN_new()) == NULL || -+ (e = BN_new()) == NULL) { -+ BN_free(n); -+ BN_free(e); - if (rsa != NULL) - RSA_free(rsa); - free(k); - return NULL; - } -+ BN_clear(n); BN_clear(e); -+ if (RSA_set0_key(rsa, n, e, NULL) == 0) -+ return NULL; -+ n = e = NULL; -+ } - k->rsa = rsa; - break; - case KEY_DSA: - case KEY_DSA_CERT: -+ { -+ BIGNUM *p=NULL, *q=NULL, *g=NULL, *pubkey=NULL; /* just allocate */ - if ((dsa = DSA_new()) == NULL || -- (dsa->p = BN_new()) == NULL || -- (dsa->q = BN_new()) == NULL || -- (dsa->g = BN_new()) == NULL || -- (dsa->pub_key = BN_new()) == NULL) { -+ (p = BN_new()) == NULL || -+ (q = BN_new()) == NULL || -+ (g = BN_new()) == NULL || -+ (pubkey = BN_new()) == NULL) { -+ BN_free(p); -+ BN_free(q); -+ BN_free(g); -+ BN_free(pubkey); - if (dsa != NULL) - DSA_free(dsa); - free(k); - return NULL; - } -+ if (DSA_set0_pqg(dsa, p, q, g) == 0) { -+ BN_free(p); BN_free(q); BN_free(g); -+ BN_free(pubkey); -+ return NULL; -+ } -+ p = q = g = NULL; -+ if (DSA_set0_key(dsa, pubkey, NULL) == 0) { -+ BN_free(pubkey); -+ return NULL; -+ } -+ pubkey = NULL; -+ } - k->dsa = dsa; - break; - case KEY_ECDSA: -@@ -536,6 +571,51 @@ - case KEY_RSA1: - case KEY_RSA: - case KEY_RSA_CERT: -+#if OPENSSL_VERSION_NUMBER >= 0x10100000UL -+ /* Allocate BIGNUM. This is a mess. -+ For OpenSSL 1.1.x API these shouldn't be mandatory, -+ but some regression tests for non-NULL pointer of -+ the data. */ -+#define new_or_dup(bn, nbn) \ -+ if (bn == NULL) { \ -+ if ((nbn = BN_new()) == NULL) \ -+ return SSH_ERR_ALLOC_FAIL; \ -+ } else { \ -+ /* otherwise use-after-free will occur */ \ -+ if ((nbn = BN_dup(bn)) == NULL) \ -+ return SSH_ERR_ALLOC_FAIL; \ -+ } -+ { -+ const BIGNUM *d, *iqmp, *q, *p, *dmq1, *dmp1; /* allocate if NULL */ -+ BIGNUM *nd, *niqmp, *nq, *np, *ndmq1, *ndmp1; -+ -+ RSA_get0_key(k->rsa, NULL, NULL, &d); -+ RSA_get0_factors(k->rsa, &p, &q); -+ RSA_get0_crt_params(k->rsa, &dmp1, &dmq1, &iqmp); -+ -+ new_or_dup(d, nd); -+ new_or_dup(iqmp, niqmp); -+ new_or_dup(q, nq); -+ new_or_dup(p, np); -+ new_or_dup(dmq1, ndmq1); -+ new_or_dup(dmp1, ndmp1); -+ -+ if (RSA_set0_key(k->rsa, NULL, NULL, nd) == 0) -+ goto error1; -+ nd = NULL; -+ if (RSA_set0_factors(k->rsa, np, nq) == 0) -+ goto error1; -+ np = nq = NULL; -+ if (RSA_set0_crt_params(k->rsa, ndmp1, ndmq1, niqmp) == 0) { -+error1: -+ BN_free(nd); -+ BN_free(np); BN_free(nq); -+ BN_free(ndmp1); BN_free(ndmq1); BN_free(niqmp); -+ return SSH_ERR_LIBCRYPTO_ERROR; -+ } -+ ndmp1 = ndmq1 = niqmp = NULL; -+ } -+#else - #define bn_maybe_alloc_failed(p) (p == NULL && (p = BN_new()) == NULL) - if (bn_maybe_alloc_failed(k->rsa->d) || - bn_maybe_alloc_failed(k->rsa->iqmp) || -@@ -544,13 +624,28 @@ - bn_maybe_alloc_failed(k->rsa->dmq1) || - bn_maybe_alloc_failed(k->rsa->dmp1)) - return SSH_ERR_ALLOC_FAIL; -+#endif - break; - case KEY_DSA: - case KEY_DSA_CERT: -+#if OPENSSL_VERSION_NUMBER >= 0x10100000UL -+ { -+ const BIGNUM *priv_key; -+ BIGNUM *npriv_key; -+ DSA_get0_key(k->dsa, NULL, &priv_key); -+ new_or_dup(priv_key, npriv_key); -+ if (DSA_set0_key(k->dsa, NULL, npriv_key) == 0) { -+ BN_free(npriv_key); -+ return SSH_ERR_LIBCRYPTO_ERROR; -+ } -+ } -+#else - if (bn_maybe_alloc_failed(k->dsa->priv_key)) - return SSH_ERR_ALLOC_FAIL; -+#endif - break; - #undef bn_maybe_alloc_failed -+#undef new_or_dup - case KEY_ECDSA: - case KEY_ECDSA_CERT: - /* Cannot do anything until we know the group */ -@@ -670,16 +765,34 @@ - case KEY_RSA1: - case KEY_RSA_CERT: - case KEY_RSA: -- return a->rsa != NULL && b->rsa != NULL && -- BN_cmp(a->rsa->e, b->rsa->e) == 0 && -- BN_cmp(a->rsa->n, b->rsa->n) == 0; -+ { -+ const BIGNUM *a_e, *b_e, *a_n, *b_n; -+ const BIGNUM *a_d, *b_d; -+ if (a->rsa == NULL) return 0; -+ if (b->rsa == NULL) return 0; -+ RSA_get0_key(a->rsa, &a_n, &a_e, &a_d); -+ RSA_get0_key(b->rsa, &b_n, &b_e, &b_d); -+ return -+ BN_cmp(a_e, b_e) == 0 && -+ BN_cmp(a_n, b_n) == 0; -+ } - case KEY_DSA_CERT: - case KEY_DSA: -- return a->dsa != NULL && b->dsa != NULL && -- BN_cmp(a->dsa->p, b->dsa->p) == 0 && -- BN_cmp(a->dsa->q, b->dsa->q) == 0 && -- BN_cmp(a->dsa->g, b->dsa->g) == 0 && -- BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; -+ { -+ const BIGNUM *a_p, *a_q, *a_g, *a_pub_key; -+ const BIGNUM *b_p, *b_q, *b_g, *b_pub_key; -+ if (a->dsa == NULL) return 0; -+ if (b->dsa == NULL) return 0; -+ DSA_get0_pqg(a->dsa, &a_p, &a_q, &a_g); -+ DSA_get0_pqg(b->dsa, &b_p, &b_q, &b_g); -+ DSA_get0_key(a->dsa, &a_pub_key, NULL); -+ DSA_get0_key(b->dsa, &b_pub_key, NULL); -+ return -+ BN_cmp(a_p, b_p) == 0 && -+ BN_cmp(a_q, b_q) == 0 && -+ BN_cmp(a_g, b_g) == 0 && -+ BN_cmp(a_pub_key, b_pub_key) == 0; -+ } - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: - case KEY_ECDSA: -@@ -757,12 +870,17 @@ - case KEY_DSA: - if (key->dsa == NULL) - return SSH_ERR_INVALID_ARGUMENT; -+ { -+ const BIGNUM *p, *q, *g, *pub_key; -+ DSA_get0_pqg(key->dsa, &p, &q, &g); -+ DSA_get0_key(key->dsa, &pub_key, NULL); - if ((ret = sshbuf_put_cstring(b, typename)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0) -+ (ret = sshbuf_put_bignum2(b, p)) != 0 || -+ (ret = sshbuf_put_bignum2(b, q)) != 0 || -+ (ret = sshbuf_put_bignum2(b, g)) != 0 || -+ (ret = sshbuf_put_bignum2(b, pub_key)) != 0) - return ret; -+ } - break; - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: -@@ -778,10 +896,14 @@ - case KEY_RSA: - if (key->rsa == NULL) - return SSH_ERR_INVALID_ARGUMENT; -+ { -+ const BIGNUM *e, *n; -+ RSA_get0_key(key->rsa, &n, &e, NULL); - if ((ret = sshbuf_put_cstring(b, typename)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->rsa->n)) != 0) -+ (ret = sshbuf_put_bignum2(b, e)) != 0 || -+ (ret = sshbuf_put_bignum2(b, n)) != 0) - return ret; -+ } - break; - #endif /* WITH_OPENSSL */ - case KEY_ED25519: -@@ -887,8 +1009,16 @@ - - if (k->type == KEY_RSA1) { - #ifdef WITH_OPENSSL -- int nlen = BN_num_bytes(k->rsa->n); -- int elen = BN_num_bytes(k->rsa->e); -+ int nlen, elen; -+#if OPENSSL_VERSION_NUMBER >= 0x10100000UL -+ const BIGNUM *n, *e; -+ RSA_get0_key(k->rsa, &n, &e, NULL); -+ nlen = BN_num_bytes(n); -+ elen = BN_num_bytes(e); -+#else -+ nlen = BN_num_bytes(k->rsa->n); -+ elen = BN_num_bytes(k->rsa->e); -+#endif - - if (nlen < 0 || elen < 0 || nlen >= INT_MAX - elen) { - r = SSH_ERR_INVALID_FORMAT; -@@ -899,8 +1029,13 @@ - r = SSH_ERR_ALLOC_FAIL; - goto out; - } -+#if OPENSSL_VERSION_NUMBER >= 0x10100000UL -+ BN_bn2bin(n, blob); -+ BN_bn2bin(e, blob + nlen); -+#else - BN_bn2bin(k->rsa->n, blob); - BN_bn2bin(k->rsa->e, blob + nlen); -+#endif - #endif /* WITH_OPENSSL */ - } else if ((r = to_blob(k, &blob, &blob_len, 1)) != 0) - goto out; -@@ -1772,13 +1907,32 @@ - case KEY_DSA_CERT: - if ((n = sshkey_new(k->type)) == NULL) - return SSH_ERR_ALLOC_FAIL; -- if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || -- (BN_copy(n->dsa->q, k->dsa->q) == NULL) || -- (BN_copy(n->dsa->g, k->dsa->g) == NULL) || -- (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) { -+ { -+ const BIGNUM *p, *q, *g, *pub_key, *priv_key; -+ BIGNUM *cp=NULL, *cq=NULL, *cg=NULL, *cpub_key=NULL; -+ DSA_get0_pqg(k->dsa, &p, &q, &g); -+ DSA_get0_key(k->dsa, &pub_key, &priv_key); -+ if ((cp = BN_dup(p)) == NULL || -+ (cq = BN_dup(q)) == NULL || -+ (cg = BN_dup(g)) == NULL || -+ (cpub_key = BN_dup(pub_key)) == NULL) { -+ BN_free(cp); BN_free(cq); BN_free(cg); -+ BN_free(cpub_key); - sshkey_free(n); - return SSH_ERR_ALLOC_FAIL; - } -+ if (DSA_set0_pqg(n->dsa, cp, cq, cg) == 0) -+ goto error1; -+ cp = cq = cg = NULL; -+ if (DSA_set0_key(n->dsa, cpub_key, NULL) == 0) { -+error1: -+ BN_free(cp); BN_free(cq); BN_free(cg); -+ BN_free(cpub_key); -+ sshkey_free(n); -+ return SSH_ERR_LIBCRYPTO_ERROR; -+ } -+ cpub_key = NULL; -+ } - break; - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: -@@ -1803,11 +1957,23 @@ - case KEY_RSA_CERT: - if ((n = sshkey_new(k->type)) == NULL) - return SSH_ERR_ALLOC_FAIL; -- if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || -- (BN_copy(n->rsa->e, k->rsa->e) == NULL)) { -+ { -+ const BIGNUM *nn, *e, *d; -+ BIGNUM *cn=NULL, *ce=NULL; -+ RSA_get0_key(k->rsa, &nn, &e, &d); -+ if ((cn = BN_dup(nn)) == NULL || -+ (ce = BN_dup(e)) == NULL ) { -+ BN_free(cn); BN_free(ce); - sshkey_free(n); - return SSH_ERR_ALLOC_FAIL; - } -+ if (RSA_set0_key(n->rsa, cn, ce, NULL) == 0) { -+ BN_free(cn); BN_free(ce); -+ sshkey_free(n); -+ return SSH_ERR_LIBCRYPTO_ERROR; -+ } -+ cn = ce = NULL; -+ } - break; - #endif /* WITH_OPENSSL */ - case KEY_ED25519: -@@ -2004,11 +2170,26 @@ - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if (sshbuf_get_bignum2(b, key->rsa->e) != 0 || -- sshbuf_get_bignum2(b, key->rsa->n) != 0) { -+ { -+ BIGNUM *e=NULL, *n=NULL; -+ if ((e = BN_new()) == NULL || -+ (n = BN_new()) == NULL ) { -+ ret = SSH_ERR_ALLOC_FAIL; -+ BN_free(e); BN_free(n); -+ goto out; -+ } -+ if (sshbuf_get_bignum2(b, e) != 0 || -+ sshbuf_get_bignum2(b, n) != 0) { - ret = SSH_ERR_INVALID_FORMAT; -+ BN_free(e); BN_free(n); - goto out; - } -+ if (RSA_set0_key(key->rsa, n, e, NULL) == 0) { -+ BN_free(e); BN_free(n); -+ return SSH_ERR_LIBCRYPTO_ERROR; -+ } -+ n = e = NULL; -+ } - #ifdef DEBUG_PK - RSA_print_fp(stderr, key->rsa, 8); - #endif -@@ -2025,13 +2206,36 @@ - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if (sshbuf_get_bignum2(b, key->dsa->p) != 0 || -- sshbuf_get_bignum2(b, key->dsa->q) != 0 || -- sshbuf_get_bignum2(b, key->dsa->g) != 0 || -- sshbuf_get_bignum2(b, key->dsa->pub_key) != 0) { -+ { -+ BIGNUM *p=NULL, *q=NULL, *g=NULL, *pub_key=NULL; -+ if ((p = BN_new()) == NULL || -+ (q = BN_new()) == NULL || -+ (g = BN_new()) == NULL || -+ (pub_key = BN_new()) == NULL) { -+ ret = SSH_ERR_ALLOC_FAIL; -+ goto error1; -+ } -+ if (sshbuf_get_bignum2(b, p) != 0 || -+ sshbuf_get_bignum2(b, q) != 0 || -+ sshbuf_get_bignum2(b, g) != 0 || -+ sshbuf_get_bignum2(b, pub_key) != 0) { - ret = SSH_ERR_INVALID_FORMAT; -+ goto error1; -+ } -+ if (DSA_set0_pqg(key->dsa, p, q, g) == 0) { -+ ret = SSH_ERR_LIBCRYPTO_ERROR; -+ goto error1; -+ } -+ p = q = g = NULL; -+ if (DSA_set0_key(key->dsa, pub_key, NULL) == 0) { -+ ret = SSH_ERR_LIBCRYPTO_ERROR; -+error1: -+ BN_free(p); BN_free(q); BN_free(g); -+ BN_free(pub_key); - goto out; - } -+ pub_key = NULL; -+ } - #ifdef DEBUG_PK - DSA_print_fp(stderr, key->dsa, 8); - #endif -@@ -2271,26 +2475,63 @@ - /* FALLTHROUGH */ - case KEY_RSA1: - case KEY_RSA: -- if ((pk->rsa = RSA_new()) == NULL || -- (pk->rsa->e = BN_dup(k->rsa->e)) == NULL || -- (pk->rsa->n = BN_dup(k->rsa->n)) == NULL) { -+ if ((pk->rsa = RSA_new()) == NULL ){ -+ ret = SSH_ERR_ALLOC_FAIL; -+ goto fail; -+ } -+ { -+ const BIGNUM *ke, *kn; -+ BIGNUM *pke=NULL, *pkn=NULL; -+ RSA_get0_key(k->rsa, &kn, &ke, NULL); -+ if ((pke = BN_dup(ke)) == NULL || -+ (pkn = BN_dup(kn)) == NULL) { - ret = SSH_ERR_ALLOC_FAIL; -+ BN_free(pke); BN_free(pkn); - goto fail; - } -+ if (RSA_set0_key(pk->rsa, pkn, pke, NULL) == 0) { -+ ret = SSH_ERR_LIBCRYPTO_ERROR; -+ BN_free(pke); BN_free(pkn); -+ goto fail; -+ } -+ pkn = pke = NULL; -+ } - break; - case KEY_DSA_CERT: - if ((ret = sshkey_cert_copy(k, pk)) != 0) - goto fail; - /* FALLTHROUGH */ - case KEY_DSA: -- if ((pk->dsa = DSA_new()) == NULL || -- (pk->dsa->p = BN_dup(k->dsa->p)) == NULL || -- (pk->dsa->q = BN_dup(k->dsa->q)) == NULL || -- (pk->dsa->g = BN_dup(k->dsa->g)) == NULL || -- (pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) { -+ if ((pk->dsa = DSA_new()) == NULL ) { - ret = SSH_ERR_ALLOC_FAIL; - goto fail; - } -+ { -+ const BIGNUM *kp, *kq, *kg, *kpub_key; -+ BIGNUM *pkp=NULL, *pkq=NULL, *pkg=NULL, *pkpub_key=NULL; -+ DSA_get0_pqg(k->dsa, &kp, &kq, &kg); -+ DSA_get0_key(k->dsa, &kpub_key, NULL); -+ if ((pkp = BN_dup(kp)) == NULL || -+ (pkq = BN_dup(kq)) == NULL || -+ (pkg = BN_dup(kg)) == NULL || -+ (pkpub_key = BN_dup(kpub_key)) == NULL) { -+ ret = SSH_ERR_ALLOC_FAIL; -+ goto error1; -+ } -+ if (DSA_set0_pqg(pk->dsa, pkp, pkq, pkg) == 0) { -+ ret = SSH_ERR_LIBCRYPTO_ERROR; -+ goto error1; -+ } -+ pkp = pkq = pkg = NULL; -+ if (DSA_set0_key(pk->dsa, pkpub_key, NULL) == 0) { -+ ret = SSH_ERR_LIBCRYPTO_ERROR; -+error1: -+ BN_free(pkp); BN_free(pkq); BN_free(pkg); -+ BN_free(pkpub_key); -+ goto fail; -+ } -+ pkpub_key = NULL; -+ } - break; - case KEY_ECDSA_CERT: - if ((ret = sshkey_cert_copy(k, pk)) != 0) -@@ -2411,11 +2652,17 @@ - switch (k->type) { - #ifdef WITH_OPENSSL - case KEY_DSA_CERT: -- if ((ret = sshbuf_put_bignum2(cert, k->dsa->p)) != 0 || -- (ret = sshbuf_put_bignum2(cert, k->dsa->q)) != 0 || -- (ret = sshbuf_put_bignum2(cert, k->dsa->g)) != 0 || -- (ret = sshbuf_put_bignum2(cert, k->dsa->pub_key)) != 0) -+ { -+ const BIGNUM *p, *q, *g, *pub_key; -+ DSA_get0_pqg(k->dsa, &p, &q, &g); -+ DSA_get0_key(k->dsa, &pub_key, NULL); -+ if ((ret = sshbuf_put_bignum2(cert, p)) != 0 || -+ (ret = sshbuf_put_bignum2(cert, q)) != 0 || -+ (ret = sshbuf_put_bignum2(cert, g)) != 0 || -+ (ret = sshbuf_put_bignum2(cert, pub_key)) != 0) { - goto out; -+ } -+ } - break; - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: -@@ -2428,9 +2675,15 @@ - break; - # endif /* OPENSSL_HAS_ECC */ - case KEY_RSA_CERT: -- if ((ret = sshbuf_put_bignum2(cert, k->rsa->e)) != 0 || -- (ret = sshbuf_put_bignum2(cert, k->rsa->n)) != 0) -+ { -+ const BIGNUM *e, *n; -+ RSA_get0_key(k->rsa, &n, &e, NULL); -+ if (n == NULL || e == NULL || -+ (ret = sshbuf_put_bignum2(cert, e)) != 0 || -+ (ret = sshbuf_put_bignum2(cert, n)) != 0) { - goto out; -+ } -+ } - break; - #endif /* WITH_OPENSSL */ - case KEY_ED25519_CERT: -@@ -2588,42 +2841,67 @@ - switch (key->type) { - #ifdef WITH_OPENSSL - case KEY_RSA: -- if ((r = sshbuf_put_bignum2(b, key->rsa->n)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) -+ { -+ const BIGNUM *n, *e, *d, *iqmp, *p, *q; -+ RSA_get0_key(key->rsa, &n, &e, &d); -+ RSA_get0_crt_params(key->rsa, NULL, NULL, &iqmp); -+ RSA_get0_factors(key->rsa, &p, &q); -+ if ((r = sshbuf_put_bignum2(b, n)) != 0 || -+ (r = sshbuf_put_bignum2(b, e)) != 0 || -+ (r = sshbuf_put_bignum2(b, d)) != 0 || -+ (r = sshbuf_put_bignum2(b, iqmp)) != 0 || -+ (r = sshbuf_put_bignum2(b, p)) != 0 || -+ (r = sshbuf_put_bignum2(b, q)) != 0) { - goto out; -+ } -+ } - break; - case KEY_RSA_CERT: - if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { - r = SSH_ERR_INVALID_ARGUMENT; - goto out; - } -+ { -+ const BIGNUM *d, *iqmp, *p, *q; -+ RSA_get0_key(key->rsa, NULL, NULL, &d); -+ RSA_get0_crt_params(key->rsa, NULL, NULL, &iqmp); -+ RSA_get0_factors(key->rsa, &p, &q); - if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) -+ (r = sshbuf_put_bignum2(b, d)) != 0 || -+ (r = sshbuf_put_bignum2(b, iqmp)) != 0 || -+ (r = sshbuf_put_bignum2(b, p)) != 0 || -+ (r = sshbuf_put_bignum2(b, q)) != 0) { - goto out; -+ } -+ } - break; - case KEY_DSA: -- if ((r = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || -- (r = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || -- (r = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || -- (r = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0 || -- (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) -+ { -+ const BIGNUM *p, *q, *g, *pub_key, *priv_key; -+ DSA_get0_pqg(key->dsa, &p, &q, &g); -+ DSA_get0_key(key->dsa, &pub_key, &priv_key); -+ if ((r = sshbuf_put_bignum2(b, p)) != 0 || -+ (r = sshbuf_put_bignum2(b, q)) != 0 || -+ (r = sshbuf_put_bignum2(b, g)) != 0 || -+ (r = sshbuf_put_bignum2(b, pub_key)) != 0 || -+ (r = sshbuf_put_bignum2(b, priv_key)) != 0) { - goto out; -+ } -+ } - break; - case KEY_DSA_CERT: - if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { - r = SSH_ERR_INVALID_ARGUMENT; - goto out; - } -+ { -+ const BIGNUM *priv_key; -+ DSA_get0_key(key->dsa, NULL, &priv_key); - if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || -- (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) -+ (r = sshbuf_put_bignum2(b, priv_key)) != 0) { - goto out; -+ } -+ } - break; - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: -@@ -2699,18 +2977,61 @@ - r = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if ((r = sshbuf_get_bignum2(buf, k->dsa->p)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->q)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->g)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->pub_key)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) -+ { -+ BIGNUM *p=NULL, *q=NULL, *g=NULL, *pub_key=NULL, *priv_key=NULL; -+ if ((p = BN_new()) == NULL || -+ (q = BN_new()) == NULL || -+ (g = BN_new()) == NULL || -+ (pub_key = BN_new()) == NULL || -+ (priv_key = BN_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto error1; -+ } -+ if (p == NULL || q == NULL || g == NULL || -+ pub_key == NULL || priv_key == NULL || -+ (r = sshbuf_get_bignum2(buf, p)) != 0 || -+ (r = sshbuf_get_bignum2(buf, q)) != 0 || -+ (r = sshbuf_get_bignum2(buf, g)) != 0 || -+ (r = sshbuf_get_bignum2(buf, pub_key)) != 0 || -+ (r = sshbuf_get_bignum2(buf, priv_key)) != 0) { -+ goto error1; -+ } -+ if (DSA_set0_pqg(k->dsa, p, q, g) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto error1; -+ } -+ p = q = g = NULL; -+ if (DSA_set0_key(k->dsa, pub_key, priv_key) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+error1: -+ BN_free(p); BN_free(q); BN_free(g); -+ BN_free(pub_key); BN_free(priv_key); - goto out; -+ } -+ pub_key = priv_key = NULL; -+ } - break; - case KEY_DSA_CERT: -- if ((r = sshkey_froms(buf, &k)) != 0 || -+ { -+ BIGNUM *priv_key=NULL; -+ if ((priv_key = BN_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if (priv_key == NULL || -+ (r = sshkey_froms(buf, &k)) != 0 || - (r = sshkey_add_private(k)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) -+ (r = sshbuf_get_bignum2(buf, priv_key)) != 0) { -+ BN_free(priv_key); - goto out; -+ } -+ if (DSA_set0_key(k->dsa, NULL, priv_key) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ BN_free(priv_key); -+ goto out; -+ } -+ priv_key = NULL; -+ } - break; - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: -@@ -2769,24 +3090,99 @@ - r = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if ((r = sshbuf_get_bignum2(buf, k->rsa->n)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->e)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 || -- (r = rsa_generate_additional_parameters(k->rsa)) != 0) -+ { -+ BIGNUM *n=NULL, *e=NULL, *d=NULL, *iqmp=NULL, *p=NULL, *q=NULL; -+ BIGNUM *dmp1=NULL, *dmq1=NULL; /* dummy for RSA_set0_crt_params */ -+ if ((n = BN_new()) == NULL || -+ (e = BN_new()) == NULL || -+ (d = BN_new()) == NULL || -+ (iqmp = BN_new()) == NULL || -+ (p = BN_new()) == NULL || -+ (q = BN_new()) == NULL || -+ (dmp1 = BN_new()) == NULL || -+ (dmq1 = BN_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto error2; -+ } -+ BN_clear(dmp1); BN_clear(dmq1); -+ if ((r = sshbuf_get_bignum2(buf, n)) != 0 || -+ (r = sshbuf_get_bignum2(buf, e)) != 0 || -+ (r = sshbuf_get_bignum2(buf, d)) != 0 || -+ (r = sshbuf_get_bignum2(buf, iqmp)) != 0 || -+ (r = sshbuf_get_bignum2(buf, p)) != 0 || -+ (r = sshbuf_get_bignum2(buf, q)) != 0) { -+ goto error2; -+ } -+ if (RSA_set0_key(k->rsa, n, e, d) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto error2; -+ } -+ n = e = d = NULL; -+ /* dmp1,dmpq1 should be non NULL to set iqmp value */ -+ if (RSA_set0_crt_params(k->rsa, dmp1, dmq1, iqmp) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto error2; -+ } -+ dmp1 = dmq1 = iqmp = NULL; -+ if (RSA_set0_factors(k->rsa, p, q) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+error2: -+ BN_free(n); BN_free(e); BN_free(d); -+ BN_free(iqmp); -+ BN_free(p); BN_free(q); -+ BN_free(dmp1); BN_free(dmq1); - goto out; -+ } -+ p = q = NULL; -+ if ((r = rsa_generate_additional_parameters(k->rsa)) != 0) { -+ goto out; -+ } -+ } - break; - case KEY_RSA_CERT: -+ { -+ BIGNUM *d=NULL, *iqmp=NULL, *p=NULL, *q=NULL; -+ BIGNUM *dmp1=NULL, *dmq1=NULL; /* dummy for RSA_set0_crt_params */ -+ if ((d = BN_new()) == NULL || -+ (iqmp = BN_new()) == NULL || -+ (p = BN_new()) == NULL || -+ (q = BN_new()) == NULL || -+ (dmp1 = BN_new()) == NULL || -+ (dmq1 = BN_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto error3; -+ } -+ BN_clear(dmp1); BN_clear(dmq1); - if ((r = sshkey_froms(buf, &k)) != 0 || - (r = sshkey_add_private(k)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 || -- (r = rsa_generate_additional_parameters(k->rsa)) != 0) -+ (r = sshbuf_get_bignum2(buf, d)) != 0 || -+ (r = sshbuf_get_bignum2(buf, iqmp)) != 0 || -+ (r = sshbuf_get_bignum2(buf, p)) != 0 || -+ (r = sshbuf_get_bignum2(buf, q)) != 0) { -+ goto error3; -+ } -+ if (RSA_set0_key(k->rsa, NULL, NULL, d) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto error3; -+ } -+ /* dmp1,dmpq1 should be non NULL to set value */ -+ if (RSA_set0_crt_params(k->rsa, dmp1, dmq1, iqmp) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto error3; -+ } -+ dmp1 = dmq1 = iqmp = NULL; -+ if (RSA_set0_factors(k->rsa, p, q) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+error3: -+ BN_free(d); BN_free(iqmp); -+ BN_free(p); BN_free(q); -+ BN_free(dmp1); BN_free(dmq1); -+ goto out; -+ } -+ p = q = NULL; -+ if ((r = rsa_generate_additional_parameters(k->rsa)) != 0) - goto out; -+ } - break; - #endif /* WITH_OPENSSL */ - case KEY_ED25519: -@@ -3593,7 +3989,7 @@ - } - } - --#ifdef WITH_SSH1 -+#ifdef WITH_SSH1 /*{*/ - /* - * Parse the public, unencrypted portion of a RSA1 key. - */ -@@ -3768,7 +4164,7 @@ - sshbuf_free(decrypted); - return r; - } --#endif /* WITH_SSH1 */ -+#endif /* } WITH_SSH1 */ - - #ifdef WITH_OPENSSL - static int -@@ -3817,7 +4213,6 @@ - case EVP_R_BAD_DECRYPT: - r = SSH_ERR_KEY_WRONG_PASSPHRASE; - goto out; -- case EVP_R_BN_DECODE_ERROR: - case EVP_R_DECODE_ERROR: - #ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR - case EVP_R_PRIVATE_KEY_DECODE_ERROR: -@@ -3835,7 +4230,7 @@ - r = SSH_ERR_LIBCRYPTO_ERROR; - goto out; - } -- if (pk->type == EVP_PKEY_RSA && -+ if (EVP_PKEY_id(pk) == EVP_PKEY_RSA && - (type == KEY_UNSPEC || type == KEY_RSA)) { - if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; -@@ -3850,7 +4245,7 @@ - r = SSH_ERR_LIBCRYPTO_ERROR; - goto out; - } -- } else if (pk->type == EVP_PKEY_DSA && -+ } else if (EVP_PKEY_id(pk) == EVP_PKEY_DSA && - (type == KEY_UNSPEC || type == KEY_DSA)) { - if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; -@@ -3862,7 +4257,7 @@ - DSA_print_fp(stderr, prv->dsa, 8); - #endif - #ifdef OPENSSL_HAS_ECC -- } else if (pk->type == EVP_PKEY_EC && -+ } else if (EVP_PKEY_id(pk) == EVP_PKEY_EC && - (type == KEY_UNSPEC || type == KEY_ECDSA)) { - if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; diff --git a/sshd.service b/sshd.service index 55ed95322da7..a893724f1767 100644 --- a/sshd.service +++ b/sshd.service @@ -12,6 +12,3 @@ Restart=always [Install] WantedBy=multi-user.target - -# This service file runs an SSH daemon that forks for each incoming connection. -# If you prefer to spawn on-demand daemons, use sshd.socket and sshd@.service. diff --git a/sshd.socket b/sshd.socket deleted file mode 100644 index e09e328690fd..000000000000 --- a/sshd.socket +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Conflicts=sshd.service -Wants=sshdgenkeys.service - -[Socket] -ListenStream=22 -Accept=yes - -[Install] -WantedBy=sockets.target diff --git a/sshd@.service b/sshd@.service deleted file mode 100644 index 7ce3d37baa43..000000000000 --- a/sshd@.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=OpenSSH Per-Connection Daemon -After=sshdgenkeys.service - -[Service] -ExecStart=-/usr/bin/sshd -i -StandardInput=socket -StandardError=syslog diff --git a/sshdgenkeys.service b/sshdgenkeys.service index 1d01b7acff4b..cfb9f6aa17f1 100644 --- a/sshdgenkeys.service +++ b/sshdgenkeys.service @@ -1,15 +1,13 @@ [Unit] Description=SSH Key Generation -ConditionPathExists=|!/etc/ssh/ssh_host_key -ConditionPathExists=|!/etc/ssh/ssh_host_key.pub -ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key -ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key.pub ConditionPathExists=|!/etc/ssh/ssh_host_dsa_key ConditionPathExists=|!/etc/ssh/ssh_host_dsa_key.pub ConditionPathExists=|!/etc/ssh/ssh_host_ecdsa_key ConditionPathExists=|!/etc/ssh/ssh_host_ecdsa_key.pub ConditionPathExists=|!/etc/ssh/ssh_host_ed25519_key ConditionPathExists=|!/etc/ssh/ssh_host_ed25519_key.pub +ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key +ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key.pub [Service] ExecStart=/usr/bin/ssh-keygen -A |