summaryrefslogtreecommitdiff
path: root/debian/patches/gssapi.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/gssapi.patch')
-rw-r--r--debian/patches/gssapi.patch3983
1 files changed, 3983 insertions, 0 deletions
diff --git a/debian/patches/gssapi.patch b/debian/patches/gssapi.patch
new file mode 100644
index 000000000..4bf1d3f73
--- /dev/null
+++ b/debian/patches/gssapi.patch
@@ -0,0 +1,3983 @@
1From 34aff3aa136e5a65f441b25811dd466488fda087 Mon Sep 17 00:00:00 2001
2From: Simon Wilkinson <simon@sxw.org.uk>
3Date: Sun, 9 Feb 2014 16:09:48 +0000
4Subject: GSSAPI key exchange support
5
6This patch has been rejected upstream: "None of the OpenSSH developers are
7in favour of adding this, and this situation has not changed for several
8years. This is not a slight on Simon's patch, which is of fine quality, but
9just that a) we don't trust GSSAPI implementations that much and b) we don't
10like adding new KEX since they are pre-auth attack surface. This one is
11particularly scary, since it requires hooks out to typically root-owned
12system resources."
13
14However, quite a lot of people rely on this in Debian, and it's better to
15have it merged into the main openssh package rather than having separate
16-krb5 packages (as we used to have). It seems to have a generally good
17security history.
18
19Origin: other, https://github.com/openssh-gsskex/openssh-gsskex/commits/debian/master
20Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242
21Last-Updated: 2020-02-21
22
23Patch-Name: gssapi.patch
24---
25 Makefile.in | 3 +-
26 README.md | 33 +++
27 auth.c | 96 +-------
28 auth2-gss.c | 56 ++++-
29 auth2.c | 2 +
30 canohost.c | 93 ++++++++
31 canohost.h | 3 +
32 clientloop.c | 15 +-
33 configure.ac | 24 ++
34 gss-genr.c | 300 +++++++++++++++++++++++-
35 gss-serv-krb5.c | 85 ++++++-
36 gss-serv.c | 186 +++++++++++++--
37 kex.c | 66 +++++-
38 kex.h | 29 +++
39 kexdh.c | 10 +
40 kexgen.c | 2 +-
41 kexgssc.c | 606 ++++++++++++++++++++++++++++++++++++++++++++++++
42 kexgsss.c | 474 +++++++++++++++++++++++++++++++++++++
43 monitor.c | 139 ++++++++++-
44 monitor.h | 2 +
45 monitor_wrap.c | 57 ++++-
46 monitor_wrap.h | 4 +-
47 readconf.c | 70 ++++++
48 readconf.h | 6 +
49 servconf.c | 47 ++++
50 servconf.h | 3 +
51 session.c | 10 +-
52 ssh-gss.h | 50 +++-
53 ssh.1 | 8 +
54 ssh.c | 6 +-
55 ssh_config | 2 +
56 ssh_config.5 | 57 +++++
57 sshconnect2.c | 142 +++++++++++-
58 sshd.c | 62 ++++-
59 sshd_config | 2 +
60 sshd_config.5 | 30 +++
61 sshkey.c | 3 +-
62 sshkey.h | 1 +
63 38 files changed, 2624 insertions(+), 160 deletions(-)
64 create mode 100644 kexgssc.c
65 create mode 100644 kexgsss.c
66
67diff --git a/Makefile.in b/Makefile.in
68index e7549470c..b68c1710f 100644
69--- a/Makefile.in
70+++ b/Makefile.in
71@@ -109,6 +109,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
72 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
73 kexgexc.o kexgexs.o \
74 sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \
75+ kexgssc.o \
76 sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \
77 sshbuf-io.o
78
79@@ -125,7 +126,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
80 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
81 auth2-none.o auth2-passwd.o auth2-pubkey.o \
82 monitor.o monitor_wrap.o auth-krb5.o \
83- auth2-gss.o gss-serv.o gss-serv-krb5.o \
84+ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
85 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
86 sftp-server.o sftp-common.o \
87 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
88diff --git a/README.md b/README.md
89index 28fb43d2a..5b73d24c0 100644
90--- a/README.md
91+++ b/README.md
92@@ -1,3 +1,36 @@
93+Portable OpenSSH with GSSAPI Key Exchange patches
94+=================================================
95+
96+Currently, there are two branches with gssapi key exchange related
97+patches:
98+
99+ * fedora/master: Changes that are shipped in Fedora
100+ * debian/master: Changes that are shipped in Debian
101+
102+The target is to converge to a shared repository with single master
103+branch from where we could build releases for both OSes.
104+
105+
106+What is in:
107+
108+ * The original patch implementing missing parts of RFC4462 by Simon Wilkinson
109+ adapted to the current OpenSSH versions and with several fixes
110+ * New methods for GSSAPI Kex from IETF draft [1] from Jakub Jelen
111+
112+
113+Missing kerberos-related parts:
114+
115+ * .k5login and .kusers support available in Fedora [2] [3].
116+ * Improved handling of kerberos ccache location [4]
117+
118+
119+[1] https://tools.ietf.org/html/draft-ietf-curdle-gss-keyex-sha2-08
120+[2] https://src.fedoraproject.org/rpms/openssh/blob/master/f/openssh-6.6p1-kuserok.patch
121+[3] https://src.fedoraproject.org/rpms/openssh/blob/master/f/openssh-6.6p1-GSSAPIEnablek5users.patch
122+[4] https://bugzilla.mindrot.org/show_bug.cgi?id=2775
123+
124+-------------------------------------------------------------------------------
125+
126 # Portable OpenSSH
127
128 [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh)
129diff --git a/auth.c b/auth.c
130index 086b8ebb1..687c57b42 100644
131--- a/auth.c
132+++ b/auth.c
133@@ -400,7 +400,8 @@ auth_root_allowed(struct ssh *ssh, const char *method)
134 case PERMIT_NO_PASSWD:
135 if (strcmp(method, "publickey") == 0 ||
136 strcmp(method, "hostbased") == 0 ||
137- strcmp(method, "gssapi-with-mic") == 0)
138+ strcmp(method, "gssapi-with-mic") == 0 ||
139+ strcmp(method, "gssapi-keyex") == 0)
140 return 1;
141 break;
142 case PERMIT_FORCED_ONLY:
143@@ -724,99 +725,6 @@ fakepw(void)
144 return (&fake);
145 }
146
147-/*
148- * Returns the remote DNS hostname as a string. The returned string must not
149- * be freed. NB. this will usually trigger a DNS query the first time it is
150- * called.
151- * This function does additional checks on the hostname to mitigate some
152- * attacks on legacy rhosts-style authentication.
153- * XXX is RhostsRSAAuthentication vulnerable to these?
154- * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
155- */
156-
157-static char *
158-remote_hostname(struct ssh *ssh)
159-{
160- struct sockaddr_storage from;
161- socklen_t fromlen;
162- struct addrinfo hints, *ai, *aitop;
163- char name[NI_MAXHOST], ntop2[NI_MAXHOST];
164- const char *ntop = ssh_remote_ipaddr(ssh);
165-
166- /* Get IP address of client. */
167- fromlen = sizeof(from);
168- memset(&from, 0, sizeof(from));
169- if (getpeername(ssh_packet_get_connection_in(ssh),
170- (struct sockaddr *)&from, &fromlen) == -1) {
171- debug("getpeername failed: %.100s", strerror(errno));
172- return xstrdup(ntop);
173- }
174-
175- ipv64_normalise_mapped(&from, &fromlen);
176- if (from.ss_family == AF_INET6)
177- fromlen = sizeof(struct sockaddr_in6);
178-
179- debug3("Trying to reverse map address %.100s.", ntop);
180- /* Map the IP address to a host name. */
181- if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
182- NULL, 0, NI_NAMEREQD) != 0) {
183- /* Host name not found. Use ip address. */
184- return xstrdup(ntop);
185- }
186-
187- /*
188- * if reverse lookup result looks like a numeric hostname,
189- * someone is trying to trick us by PTR record like following:
190- * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
191- */
192- memset(&hints, 0, sizeof(hints));
193- hints.ai_socktype = SOCK_DGRAM; /*dummy*/
194- hints.ai_flags = AI_NUMERICHOST;
195- if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
196- logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
197- name, ntop);
198- freeaddrinfo(ai);
199- return xstrdup(ntop);
200- }
201-
202- /* Names are stored in lowercase. */
203- lowercase(name);
204-
205- /*
206- * Map it back to an IP address and check that the given
207- * address actually is an address of this host. This is
208- * necessary because anyone with access to a name server can
209- * define arbitrary names for an IP address. Mapping from
210- * name to IP address can be trusted better (but can still be
211- * fooled if the intruder has access to the name server of
212- * the domain).
213- */
214- memset(&hints, 0, sizeof(hints));
215- hints.ai_family = from.ss_family;
216- hints.ai_socktype = SOCK_STREAM;
217- if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
218- logit("reverse mapping checking getaddrinfo for %.700s "
219- "[%s] failed.", name, ntop);
220- return xstrdup(ntop);
221- }
222- /* Look for the address from the list of addresses. */
223- for (ai = aitop; ai; ai = ai->ai_next) {
224- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
225- sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
226- (strcmp(ntop, ntop2) == 0))
227- break;
228- }
229- freeaddrinfo(aitop);
230- /* If we reached the end of the list, the address was not there. */
231- if (ai == NULL) {
232- /* Address not found for the host name. */
233- logit("Address %.100s maps to %.600s, but this does not "
234- "map back to the address.", ntop, name);
235- return xstrdup(ntop);
236- }
237- return xstrdup(name);
238-}
239-
240 /*
241 * Return the canonical name of the host in the other side of the current
242 * connection. The host name is cached, so it is efficient to call this
243diff --git a/auth2-gss.c b/auth2-gss.c
244index 9351e0428..d6446c0cf 100644
245--- a/auth2-gss.c
246+++ b/auth2-gss.c
247@@ -1,7 +1,7 @@
248 /* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 djm Exp $ */
249
250 /*
251- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
252+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
253 *
254 * Redistribution and use in source and binary forms, with or without
255 * modification, are permitted provided that the following conditions
256@@ -54,6 +54,48 @@ static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
257 static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
258 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
259
260+/*
261+ * The 'gssapi_keyex' userauth mechanism.
262+ */
263+static int
264+userauth_gsskeyex(struct ssh *ssh)
265+{
266+ Authctxt *authctxt = ssh->authctxt;
267+ int r, authenticated = 0;
268+ struct sshbuf *b = NULL;
269+ gss_buffer_desc mic, gssbuf;
270+ u_char *p;
271+ size_t len;
272+
273+ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
274+ (r = sshpkt_get_end(ssh)) != 0)
275+ fatal("%s: %s", __func__, ssh_err(r));
276+
277+ if ((b = sshbuf_new()) == NULL)
278+ fatal("%s: sshbuf_new failed", __func__);
279+
280+ mic.value = p;
281+ mic.length = len;
282+
283+ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
284+ "gssapi-keyex");
285+
286+ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
287+ fatal("%s: sshbuf_mutable_ptr failed", __func__);
288+ gssbuf.length = sshbuf_len(b);
289+
290+ /* gss_kex_context is NULL with privsep, so we can't check it here */
291+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
292+ &gssbuf, &mic))))
293+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
294+ authctxt->pw, 1));
295+
296+ sshbuf_free(b);
297+ free(mic.value);
298+
299+ return (authenticated);
300+}
301+
302 /*
303 * We only support those mechanisms that we know about (ie ones that we know
304 * how to check local user kuserok and the like)
305@@ -260,7 +302,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
306 if ((r = sshpkt_get_end(ssh)) != 0)
307 fatal("%s: %s", __func__, ssh_err(r));
308
309- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
310+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
311+ authctxt->pw, 1));
312
313 if ((!use_privsep || mm_is_monitor()) &&
314 (displayname = ssh_gssapi_displayname()) != NULL)
315@@ -306,7 +349,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
316 gssbuf.length = sshbuf_len(b);
317
318 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
319- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
320+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
321+ authctxt->pw, 0));
322 else
323 logit("GSSAPI MIC check failed");
324
325@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
326 return 0;
327 }
328
329+Authmethod method_gsskeyex = {
330+ "gssapi-keyex",
331+ userauth_gsskeyex,
332+ &options.gss_authentication
333+};
334+
335 Authmethod method_gssapi = {
336 "gssapi-with-mic",
337 userauth_gssapi,
338diff --git a/auth2.c b/auth2.c
339index 0e7762242..1c217268c 100644
340--- a/auth2.c
341+++ b/auth2.c
342@@ -73,6 +73,7 @@ extern Authmethod method_passwd;
343 extern Authmethod method_kbdint;
344 extern Authmethod method_hostbased;
345 #ifdef GSSAPI
346+extern Authmethod method_gsskeyex;
347 extern Authmethod method_gssapi;
348 #endif
349
350@@ -80,6 +81,7 @@ Authmethod *authmethods[] = {
351 &method_none,
352 &method_pubkey,
353 #ifdef GSSAPI
354+ &method_gsskeyex,
355 &method_gssapi,
356 #endif
357 &method_passwd,
358diff --git a/canohost.c b/canohost.c
359index abea9c6e6..8e81b5193 100644
360--- a/canohost.c
361+++ b/canohost.c
362@@ -35,6 +35,99 @@
363 #include "canohost.h"
364 #include "misc.h"
365
366+/*
367+ * Returns the remote DNS hostname as a string. The returned string must not
368+ * be freed. NB. this will usually trigger a DNS query the first time it is
369+ * called.
370+ * This function does additional checks on the hostname to mitigate some
371+ * attacks on legacy rhosts-style authentication.
372+ * XXX is RhostsRSAAuthentication vulnerable to these?
373+ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
374+ */
375+
376+char *
377+remote_hostname(struct ssh *ssh)
378+{
379+ struct sockaddr_storage from;
380+ socklen_t fromlen;
381+ struct addrinfo hints, *ai, *aitop;
382+ char name[NI_MAXHOST], ntop2[NI_MAXHOST];
383+ const char *ntop = ssh_remote_ipaddr(ssh);
384+
385+ /* Get IP address of client. */
386+ fromlen = sizeof(from);
387+ memset(&from, 0, sizeof(from));
388+ if (getpeername(ssh_packet_get_connection_in(ssh),
389+ (struct sockaddr *)&from, &fromlen) == -1) {
390+ debug("getpeername failed: %.100s", strerror(errno));
391+ return xstrdup(ntop);
392+ }
393+
394+ ipv64_normalise_mapped(&from, &fromlen);
395+ if (from.ss_family == AF_INET6)
396+ fromlen = sizeof(struct sockaddr_in6);
397+
398+ debug3("Trying to reverse map address %.100s.", ntop);
399+ /* Map the IP address to a host name. */
400+ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
401+ NULL, 0, NI_NAMEREQD) != 0) {
402+ /* Host name not found. Use ip address. */
403+ return xstrdup(ntop);
404+ }
405+
406+ /*
407+ * if reverse lookup result looks like a numeric hostname,
408+ * someone is trying to trick us by PTR record like following:
409+ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
410+ */
411+ memset(&hints, 0, sizeof(hints));
412+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
413+ hints.ai_flags = AI_NUMERICHOST;
414+ if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
415+ logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
416+ name, ntop);
417+ freeaddrinfo(ai);
418+ return xstrdup(ntop);
419+ }
420+
421+ /* Names are stored in lowercase. */
422+ lowercase(name);
423+
424+ /*
425+ * Map it back to an IP address and check that the given
426+ * address actually is an address of this host. This is
427+ * necessary because anyone with access to a name server can
428+ * define arbitrary names for an IP address. Mapping from
429+ * name to IP address can be trusted better (but can still be
430+ * fooled if the intruder has access to the name server of
431+ * the domain).
432+ */
433+ memset(&hints, 0, sizeof(hints));
434+ hints.ai_family = from.ss_family;
435+ hints.ai_socktype = SOCK_STREAM;
436+ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
437+ logit("reverse mapping checking getaddrinfo for %.700s "
438+ "[%s] failed.", name, ntop);
439+ return xstrdup(ntop);
440+ }
441+ /* Look for the address from the list of addresses. */
442+ for (ai = aitop; ai; ai = ai->ai_next) {
443+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
444+ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
445+ (strcmp(ntop, ntop2) == 0))
446+ break;
447+ }
448+ freeaddrinfo(aitop);
449+ /* If we reached the end of the list, the address was not there. */
450+ if (ai == NULL) {
451+ /* Address not found for the host name. */
452+ logit("Address %.100s maps to %.600s, but this does not "
453+ "map back to the address.", ntop, name);
454+ return xstrdup(ntop);
455+ }
456+ return xstrdup(name);
457+}
458+
459 void
460 ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
461 {
462diff --git a/canohost.h b/canohost.h
463index 26d62855a..0cadc9f18 100644
464--- a/canohost.h
465+++ b/canohost.h
466@@ -15,6 +15,9 @@
467 #ifndef _CANOHOST_H
468 #define _CANOHOST_H
469
470+struct ssh;
471+
472+char *remote_hostname(struct ssh *);
473 char *get_peer_ipaddr(int);
474 int get_peer_port(int);
475 char *get_local_ipaddr(int);
476diff --git a/clientloop.c b/clientloop.c
477index ebd0dbca1..1bdac6a46 100644
478--- a/clientloop.c
479+++ b/clientloop.c
480@@ -112,6 +112,10 @@
481 #include "ssherr.h"
482 #include "hostfile.h"
483
484+#ifdef GSSAPI
485+#include "ssh-gss.h"
486+#endif
487+
488 /* import options */
489 extern Options options;
490
491@@ -1379,9 +1383,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
492 break;
493
494 /* Do channel operations unless rekeying in progress. */
495- if (!ssh_packet_is_rekeying(ssh))
496+ if (!ssh_packet_is_rekeying(ssh)) {
497 channel_after_select(ssh, readset, writeset);
498
499+#ifdef GSSAPI
500+ if (options.gss_renewal_rekey &&
501+ ssh_gssapi_credentials_updated(NULL)) {
502+ debug("credentials updated - forcing rekey");
503+ need_rekeying = 1;
504+ }
505+#endif
506+ }
507+
508 /* Buffer input from the connection. */
509 client_process_net_input(ssh, readset);
510
511diff --git a/configure.ac b/configure.ac
512index b689db4b5..efafb6bd8 100644
513--- a/configure.ac
514+++ b/configure.ac
515@@ -674,6 +674,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
516 [Use tunnel device compatibility to OpenBSD])
517 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
518 [Prepend the address family to IP tunnel traffic])
519+ AC_MSG_CHECKING([if we have the Security Authorization Session API])
520+ AC_TRY_COMPILE([#include <Security/AuthSession.h>],
521+ [SessionCreate(0, 0);],
522+ [ac_cv_use_security_session_api="yes"
523+ AC_DEFINE([USE_SECURITY_SESSION_API], [1],
524+ [platform has the Security Authorization Session API])
525+ LIBS="$LIBS -framework Security"
526+ AC_MSG_RESULT([yes])],
527+ [ac_cv_use_security_session_api="no"
528+ AC_MSG_RESULT([no])])
529+ AC_MSG_CHECKING([if we have an in-memory credentials cache])
530+ AC_TRY_COMPILE(
531+ [#include <Kerberos/Kerberos.h>],
532+ [cc_context_t c;
533+ (void) cc_initialize (&c, 0, NULL, NULL);],
534+ [AC_DEFINE([USE_CCAPI], [1],
535+ [platform uses an in-memory credentials cache])
536+ LIBS="$LIBS -framework Security"
537+ AC_MSG_RESULT([yes])
538+ if test "x$ac_cv_use_security_session_api" = "xno"; then
539+ AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
540+ fi],
541+ [AC_MSG_RESULT([no])]
542+ )
543 m4_pattern_allow([AU_IPv])
544 AC_CHECK_DECL([AU_IPv4], [],
545 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
546diff --git a/gss-genr.c b/gss-genr.c
547index d56257b4a..763a63ffa 100644
548--- a/gss-genr.c
549+++ b/gss-genr.c
550@@ -1,7 +1,7 @@
551 /* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 djm Exp $ */
552
553 /*
554- * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
555+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
556 *
557 * Redistribution and use in source and binary forms, with or without
558 * modification, are permitted provided that the following conditions
559@@ -41,12 +41,36 @@
560 #include "sshbuf.h"
561 #include "log.h"
562 #include "ssh2.h"
563+#include "cipher.h"
564+#include "sshkey.h"
565+#include "kex.h"
566+#include "digest.h"
567+#include "packet.h"
568
569 #include "ssh-gss.h"
570
571 extern u_char *session_id2;
572 extern u_int session_id2_len;
573
574+typedef struct {
575+ char *encoded;
576+ gss_OID oid;
577+} ssh_gss_kex_mapping;
578+
579+/*
580+ * XXX - It would be nice to find a more elegant way of handling the
581+ * XXX passing of the key exchange context to the userauth routines
582+ */
583+
584+Gssctxt *gss_kex_context = NULL;
585+
586+static ssh_gss_kex_mapping *gss_enc2oid = NULL;
587+
588+int
589+ssh_gssapi_oid_table_ok(void) {
590+ return (gss_enc2oid != NULL);
591+}
592+
593 /* sshbuf_get for gss_buffer_desc */
594 int
595 ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
596@@ -62,6 +86,162 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
597 return 0;
598 }
599
600+/* sshpkt_get of gss_buffer_desc */
601+int
602+ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *ssh, gss_buffer_desc *g)
603+{
604+ int r;
605+ u_char *p;
606+ size_t len;
607+
608+ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
609+ return r;
610+ g->value = p;
611+ g->length = len;
612+ return 0;
613+}
614+
615+/*
616+ * Return a list of the gss-group1-sha1 mechanisms supported by this program
617+ *
618+ * We test mechanisms to ensure that we can use them, to avoid starting
619+ * a key exchange with a bad mechanism
620+ */
621+
622+char *
623+ssh_gssapi_client_mechanisms(const char *host, const char *client,
624+ const char *kex) {
625+ gss_OID_set gss_supported = NULL;
626+ OM_uint32 min_status;
627+
628+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
629+ return NULL;
630+
631+ return ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
632+ host, client, kex);
633+}
634+
635+char *
636+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
637+ const char *host, const char *client, const char *kex) {
638+ struct sshbuf *buf = NULL;
639+ size_t i;
640+ int r = SSH_ERR_ALLOC_FAIL;
641+ int oidpos, enclen;
642+ char *mechs, *encoded;
643+ u_char digest[SSH_DIGEST_MAX_LENGTH];
644+ char deroid[2];
645+ struct ssh_digest_ctx *md = NULL;
646+ char *s, *cp, *p;
647+
648+ if (gss_enc2oid != NULL) {
649+ for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
650+ free(gss_enc2oid[i].encoded);
651+ free(gss_enc2oid);
652+ }
653+
654+ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
655+ (gss_supported->count + 1));
656+
657+ if ((buf = sshbuf_new()) == NULL)
658+ fatal("%s: sshbuf_new failed", __func__);
659+
660+ oidpos = 0;
661+ s = cp = xstrdup(kex);
662+ for (i = 0; i < gss_supported->count; i++) {
663+ if (gss_supported->elements[i].length < 128 &&
664+ (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
665+
666+ deroid[0] = SSH_GSS_OIDTYPE;
667+ deroid[1] = gss_supported->elements[i].length;
668+
669+ if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
670+ (r = ssh_digest_update(md, deroid, 2)) != 0 ||
671+ (r = ssh_digest_update(md,
672+ gss_supported->elements[i].elements,
673+ gss_supported->elements[i].length)) != 0 ||
674+ (r = ssh_digest_final(md, digest, sizeof(digest))) != 0)
675+ fatal("%s: digest failed: %s", __func__,
676+ ssh_err(r));
677+ ssh_digest_free(md);
678+ md = NULL;
679+
680+ encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
681+ * 2);
682+ enclen = __b64_ntop(digest,
683+ ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
684+ ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
685+
686+ cp = strncpy(s, kex, strlen(kex));
687+ for ((p = strsep(&cp, ",")); p && *p != '\0';
688+ (p = strsep(&cp, ","))) {
689+ if (sshbuf_len(buf) != 0 &&
690+ (r = sshbuf_put_u8(buf, ',')) != 0)
691+ fatal("%s: sshbuf_put_u8 error: %s",
692+ __func__, ssh_err(r));
693+ if ((r = sshbuf_put(buf, p, strlen(p))) != 0 ||
694+ (r = sshbuf_put(buf, encoded, enclen)) != 0)
695+ fatal("%s: sshbuf_put error: %s",
696+ __func__, ssh_err(r));
697+ }
698+
699+ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
700+ gss_enc2oid[oidpos].encoded = encoded;
701+ oidpos++;
702+ }
703+ }
704+ free(s);
705+ gss_enc2oid[oidpos].oid = NULL;
706+ gss_enc2oid[oidpos].encoded = NULL;
707+
708+ if ((mechs = sshbuf_dup_string(buf)) == NULL)
709+ fatal("%s: sshbuf_dup_string failed", __func__);
710+
711+ sshbuf_free(buf);
712+
713+ if (strlen(mechs) == 0) {
714+ free(mechs);
715+ mechs = NULL;
716+ }
717+
718+ return (mechs);
719+}
720+
721+gss_OID
722+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
723+ int i = 0;
724+
725+#define SKIP_KEX_NAME(type) \
726+ case type: \
727+ if (strlen(name) < sizeof(type##_ID)) \
728+ return GSS_C_NO_OID; \
729+ name += sizeof(type##_ID) - 1; \
730+ break;
731+
732+ switch (kex_type) {
733+ SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1)
734+ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1)
735+ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256)
736+ SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512)
737+ SKIP_KEX_NAME(KEX_GSS_GEX_SHA1)
738+ SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256)
739+ SKIP_KEX_NAME(KEX_GSS_C25519_SHA256)
740+ default:
741+ return GSS_C_NO_OID;
742+ }
743+
744+#undef SKIP_KEX_NAME
745+
746+ while (gss_enc2oid[i].encoded != NULL &&
747+ strcmp(name, gss_enc2oid[i].encoded) != 0)
748+ i++;
749+
750+ if (gss_enc2oid[i].oid != NULL && ctx != NULL)
751+ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
752+
753+ return gss_enc2oid[i].oid;
754+}
755+
756 /* Check that the OID in a data stream matches that in the context */
757 int
758 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
759@@ -218,7 +398,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
760 }
761
762 ctx->major = gss_init_sec_context(&ctx->minor,
763- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid,
764+ ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
765 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
766 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
767
768@@ -247,9 +427,43 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
769 return (ctx->major);
770 }
771
772+OM_uint32
773+ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
774+{
775+ gss_buffer_desc gssbuf;
776+ gss_name_t gssname;
777+ OM_uint32 status;
778+ gss_OID_set oidset;
779+
780+ gssbuf.value = (void *) name;
781+ gssbuf.length = strlen(gssbuf.value);
782+
783+ gss_create_empty_oid_set(&status, &oidset);
784+ gss_add_oid_set_member(&status, ctx->oid, &oidset);
785+
786+ ctx->major = gss_import_name(&ctx->minor, &gssbuf,
787+ GSS_C_NT_USER_NAME, &gssname);
788+
789+ if (!ctx->major)
790+ ctx->major = gss_acquire_cred(&ctx->minor,
791+ gssname, 0, oidset, GSS_C_INITIATE,
792+ &ctx->client_creds, NULL, NULL);
793+
794+ gss_release_name(&status, &gssname);
795+ gss_release_oid_set(&status, &oidset);
796+
797+ if (ctx->major)
798+ ssh_gssapi_error(ctx);
799+
800+ return(ctx->major);
801+}
802+
803 OM_uint32
804 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
805 {
806+ if (ctx == NULL)
807+ return -1;
808+
809 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
810 GSS_C_QOP_DEFAULT, buffer, hash)))
811 ssh_gssapi_error(ctx);
812@@ -257,6 +471,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
813 return (ctx->major);
814 }
815
816+/* Priviledged when used by server */
817+OM_uint32
818+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
819+{
820+ if (ctx == NULL)
821+ return -1;
822+
823+ ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
824+ gssbuf, gssmic, NULL);
825+
826+ return (ctx->major);
827+}
828+
829 void
830 ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
831 const char *context)
832@@ -273,11 +500,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
833 }
834
835 int
836-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
837+ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
838+ const char *client)
839 {
840 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
841 OM_uint32 major, minor;
842 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
843+ Gssctxt *intctx = NULL;
844+
845+ if (ctx == NULL)
846+ ctx = &intctx;
847
848 /* RFC 4462 says we MUST NOT do SPNEGO */
849 if (oid->length == spnego_oid.length &&
850@@ -287,6 +519,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
851 ssh_gssapi_build_ctx(ctx);
852 ssh_gssapi_set_oid(*ctx, oid);
853 major = ssh_gssapi_import_name(*ctx, host);
854+
855+ if (!GSS_ERROR(major) && client)
856+ major = ssh_gssapi_client_identity(*ctx, client);
857+
858 if (!GSS_ERROR(major)) {
859 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
860 NULL);
861@@ -296,10 +532,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
862 GSS_C_NO_BUFFER);
863 }
864
865- if (GSS_ERROR(major))
866+ if (GSS_ERROR(major) || intctx != NULL)
867 ssh_gssapi_delete_ctx(ctx);
868
869 return (!GSS_ERROR(major));
870 }
871
872+int
873+ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
874+ static gss_name_t saved_name = GSS_C_NO_NAME;
875+ static OM_uint32 saved_lifetime = 0;
876+ static gss_OID saved_mech = GSS_C_NO_OID;
877+ static gss_name_t name;
878+ static OM_uint32 last_call = 0;
879+ OM_uint32 lifetime, now, major, minor;
880+ int equal;
881+
882+ now = time(NULL);
883+
884+ if (ctxt) {
885+ debug("Rekey has happened - updating saved versions");
886+
887+ if (saved_name != GSS_C_NO_NAME)
888+ gss_release_name(&minor, &saved_name);
889+
890+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
891+ &saved_name, &saved_lifetime, NULL, NULL);
892+
893+ if (!GSS_ERROR(major)) {
894+ saved_mech = ctxt->oid;
895+ saved_lifetime+= now;
896+ } else {
897+ /* Handle the error */
898+ }
899+ return 0;
900+ }
901+
902+ if (now - last_call < 10)
903+ return 0;
904+
905+ last_call = now;
906+
907+ if (saved_mech == GSS_C_NO_OID)
908+ return 0;
909+
910+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
911+ &name, &lifetime, NULL, NULL);
912+ if (major == GSS_S_CREDENTIALS_EXPIRED)
913+ return 0;
914+ else if (GSS_ERROR(major))
915+ return 0;
916+
917+ major = gss_compare_name(&minor, saved_name, name, &equal);
918+ gss_release_name(&minor, &name);
919+ if (GSS_ERROR(major))
920+ return 0;
921+
922+ if (equal && (saved_lifetime < lifetime + now - 10))
923+ return 1;
924+
925+ return 0;
926+}
927+
928 #endif /* GSSAPI */
929diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
930index a151bc1e4..ef9beb67c 100644
931--- a/gss-serv-krb5.c
932+++ b/gss-serv-krb5.c
933@@ -1,7 +1,7 @@
934 /* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */
935
936 /*
937- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
938+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
939 *
940 * Redistribution and use in source and binary forms, with or without
941 * modification, are permitted provided that the following conditions
942@@ -120,8 +120,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
943 krb5_error_code problem;
944 krb5_principal princ;
945 OM_uint32 maj_status, min_status;
946- int len;
947 const char *errmsg;
948+ const char *new_ccname;
949
950 if (client->creds == NULL) {
951 debug("No credentials stored");
952@@ -180,11 +180,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
953 return;
954 }
955
956- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache));
957+ new_ccname = krb5_cc_get_name(krb_context, ccache);
958+
959 client->store.envvar = "KRB5CCNAME";
960- len = strlen(client->store.filename) + 6;
961- client->store.envval = xmalloc(len);
962- snprintf(client->store.envval, len, "FILE:%s", client->store.filename);
963+#ifdef USE_CCAPI
964+ xasprintf(&client->store.envval, "API:%s", new_ccname);
965+ client->store.filename = NULL;
966+#else
967+ xasprintf(&client->store.envval, "FILE:%s", new_ccname);
968+ client->store.filename = xstrdup(new_ccname);
969+#endif
970
971 #ifdef USE_PAM
972 if (options.use_pam)
973@@ -196,6 +201,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
974 return;
975 }
976
977+int
978+ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
979+ ssh_gssapi_client *client)
980+{
981+ krb5_ccache ccache = NULL;
982+ krb5_principal principal = NULL;
983+ char *name = NULL;
984+ krb5_error_code problem;
985+ OM_uint32 maj_status, min_status;
986+
987+ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
988+ logit("krb5_cc_resolve(): %.100s",
989+ krb5_get_err_text(krb_context, problem));
990+ return 0;
991+ }
992+
993+ /* Find out who the principal in this cache is */
994+ if ((problem = krb5_cc_get_principal(krb_context, ccache,
995+ &principal))) {
996+ logit("krb5_cc_get_principal(): %.100s",
997+ krb5_get_err_text(krb_context, problem));
998+ krb5_cc_close(krb_context, ccache);
999+ return 0;
1000+ }
1001+
1002+ if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
1003+ logit("krb5_unparse_name(): %.100s",
1004+ krb5_get_err_text(krb_context, problem));
1005+ krb5_free_principal(krb_context, principal);
1006+ krb5_cc_close(krb_context, ccache);
1007+ return 0;
1008+ }
1009+
1010+
1011+ if (strcmp(name,client->exportedname.value)!=0) {
1012+ debug("Name in local credentials cache differs. Not storing");
1013+ krb5_free_principal(krb_context, principal);
1014+ krb5_cc_close(krb_context, ccache);
1015+ krb5_free_unparsed_name(krb_context, name);
1016+ return 0;
1017+ }
1018+ krb5_free_unparsed_name(krb_context, name);
1019+
1020+ /* Name matches, so lets get on with it! */
1021+
1022+ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
1023+ logit("krb5_cc_initialize(): %.100s",
1024+ krb5_get_err_text(krb_context, problem));
1025+ krb5_free_principal(krb_context, principal);
1026+ krb5_cc_close(krb_context, ccache);
1027+ return 0;
1028+ }
1029+
1030+ krb5_free_principal(krb_context, principal);
1031+
1032+ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
1033+ ccache))) {
1034+ logit("gss_krb5_copy_ccache() failed. Sorry!");
1035+ krb5_cc_close(krb_context, ccache);
1036+ return 0;
1037+ }
1038+
1039+ return 1;
1040+}
1041+
1042 ssh_gssapi_mech gssapi_kerberos_mech = {
1043 "toWM5Slw5Ew8Mqkay+al2g==",
1044 "Kerberos",
1045@@ -203,7 +273,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
1046 NULL,
1047 &ssh_gssapi_krb5_userok,
1048 NULL,
1049- &ssh_gssapi_krb5_storecreds
1050+ &ssh_gssapi_krb5_storecreds,
1051+ &ssh_gssapi_krb5_updatecreds
1052 };
1053
1054 #endif /* KRB5 */
1055diff --git a/gss-serv.c b/gss-serv.c
1056index ab3a15f0f..1d47870e7 100644
1057--- a/gss-serv.c
1058+++ b/gss-serv.c
1059@@ -1,7 +1,7 @@
1060 /* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus Exp $ */
1061
1062 /*
1063- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
1064+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
1065 *
1066 * Redistribution and use in source and binary forms, with or without
1067 * modification, are permitted provided that the following conditions
1068@@ -44,17 +44,19 @@
1069 #include "session.h"
1070 #include "misc.h"
1071 #include "servconf.h"
1072+#include "uidswap.h"
1073
1074 #include "ssh-gss.h"
1075+#include "monitor_wrap.h"
1076
1077 extern ServerOptions options;
1078
1079 static ssh_gssapi_client gssapi_client =
1080- { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
1081- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}};
1082+ { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL,
1083+ GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, 0, 0};
1084
1085 ssh_gssapi_mech gssapi_null_mech =
1086- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
1087+ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
1088
1089 #ifdef KRB5
1090 extern ssh_gssapi_mech gssapi_kerberos_mech;
1091@@ -140,6 +142,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
1092 return (ssh_gssapi_acquire_cred(*ctx));
1093 }
1094
1095+/* Unprivileged */
1096+char *
1097+ssh_gssapi_server_mechanisms(void) {
1098+ if (supported_oids == NULL)
1099+ ssh_gssapi_prepare_supported_oids();
1100+ return (ssh_gssapi_kex_mechs(supported_oids,
1101+ &ssh_gssapi_server_check_mech, NULL, NULL,
1102+ options.gss_kex_algorithms));
1103+}
1104+
1105+/* Unprivileged */
1106+int
1107+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
1108+ const char *dummy) {
1109+ Gssctxt *ctx = NULL;
1110+ int res;
1111+
1112+ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
1113+ ssh_gssapi_delete_ctx(&ctx);
1114+
1115+ return (res);
1116+}
1117+
1118 /* Unprivileged */
1119 void
1120 ssh_gssapi_supported_oids(gss_OID_set *oidset)
1121@@ -150,7 +175,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
1122 gss_OID_set supported;
1123
1124 gss_create_empty_oid_set(&min_status, oidset);
1125- gss_indicate_mechs(&min_status, &supported);
1126+
1127+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
1128+ return;
1129
1130 while (supported_mechs[i]->name != NULL) {
1131 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
1132@@ -276,8 +303,48 @@ OM_uint32
1133 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1134 {
1135 int i = 0;
1136+ int equal = 0;
1137+ gss_name_t new_name = GSS_C_NO_NAME;
1138+ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
1139+
1140+ if (options.gss_store_rekey && client->used && ctx->client_creds) {
1141+ if (client->mech->oid.length != ctx->oid->length ||
1142+ (memcmp(client->mech->oid.elements,
1143+ ctx->oid->elements, ctx->oid->length) !=0)) {
1144+ debug("Rekeyed credentials have different mechanism");
1145+ return GSS_S_COMPLETE;
1146+ }
1147+
1148+ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
1149+ ctx->client_creds, ctx->oid, &new_name,
1150+ NULL, NULL, NULL))) {
1151+ ssh_gssapi_error(ctx);
1152+ return (ctx->major);
1153+ }
1154+
1155+ ctx->major = gss_compare_name(&ctx->minor, client->name,
1156+ new_name, &equal);
1157+
1158+ if (GSS_ERROR(ctx->major)) {
1159+ ssh_gssapi_error(ctx);
1160+ return (ctx->major);
1161+ }
1162+
1163+ if (!equal) {
1164+ debug("Rekeyed credentials have different name");
1165+ return GSS_S_COMPLETE;
1166+ }
1167
1168- gss_buffer_desc ename;
1169+ debug("Marking rekeyed credentials for export");
1170+
1171+ gss_release_name(&ctx->minor, &client->name);
1172+ gss_release_cred(&ctx->minor, &client->creds);
1173+ client->name = new_name;
1174+ client->creds = ctx->client_creds;
1175+ ctx->client_creds = GSS_C_NO_CREDENTIAL;
1176+ client->updated = 1;
1177+ return GSS_S_COMPLETE;
1178+ }
1179
1180 client->mech = NULL;
1181
1182@@ -292,6 +359,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1183 if (client->mech == NULL)
1184 return GSS_S_FAILURE;
1185
1186+ if (ctx->client_creds &&
1187+ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
1188+ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
1189+ ssh_gssapi_error(ctx);
1190+ return (ctx->major);
1191+ }
1192+
1193 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
1194 &client->displayname, NULL))) {
1195 ssh_gssapi_error(ctx);
1196@@ -309,6 +383,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1197 return (ctx->major);
1198 }
1199
1200+ gss_release_buffer(&ctx->minor, &ename);
1201+
1202 /* We can't copy this structure, so we just move the pointer to it */
1203 client->creds = ctx->client_creds;
1204 ctx->client_creds = GSS_C_NO_CREDENTIAL;
1205@@ -356,19 +432,23 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
1206
1207 /* Privileged */
1208 int
1209-ssh_gssapi_userok(char *user)
1210+ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
1211 {
1212 OM_uint32 lmin;
1213
1214+ (void) kex; /* used in privilege separation */
1215+
1216 if (gssapi_client.exportedname.length == 0 ||
1217 gssapi_client.exportedname.value == NULL) {
1218 debug("No suitable client data");
1219 return 0;
1220 }
1221 if (gssapi_client.mech && gssapi_client.mech->userok)
1222- if ((*gssapi_client.mech->userok)(&gssapi_client, user))
1223+ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
1224+ gssapi_client.used = 1;
1225+ gssapi_client.store.owner = pw;
1226 return 1;
1227- else {
1228+ } else {
1229 /* Destroy delegated credentials if userok fails */
1230 gss_release_buffer(&lmin, &gssapi_client.displayname);
1231 gss_release_buffer(&lmin, &gssapi_client.exportedname);
1232@@ -382,14 +462,90 @@ ssh_gssapi_userok(char *user)
1233 return (0);
1234 }
1235
1236-/* Privileged */
1237-OM_uint32
1238-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1239+/* These bits are only used for rekeying. The unpriviledged child is running
1240+ * as the user, the monitor is root.
1241+ *
1242+ * In the child, we want to :
1243+ * *) Ask the monitor to store our credentials into the store we specify
1244+ * *) If it succeeds, maybe do a PAM update
1245+ */
1246+
1247+/* Stuff for PAM */
1248+
1249+#ifdef USE_PAM
1250+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
1251+ struct pam_response **resp, void *data)
1252 {
1253- ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
1254- gssbuf, gssmic, NULL);
1255+ return (PAM_CONV_ERR);
1256+}
1257+#endif
1258
1259- return (ctx->major);
1260+void
1261+ssh_gssapi_rekey_creds(void) {
1262+ int ok;
1263+#ifdef USE_PAM
1264+ int ret;
1265+ pam_handle_t *pamh = NULL;
1266+ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
1267+ char *envstr;
1268+#endif
1269+
1270+ if (gssapi_client.store.filename == NULL &&
1271+ gssapi_client.store.envval == NULL &&
1272+ gssapi_client.store.envvar == NULL)
1273+ return;
1274+
1275+ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
1276+
1277+ if (!ok)
1278+ return;
1279+
1280+ debug("Rekeyed credentials stored successfully");
1281+
1282+ /* Actually managing to play with the ssh pam stack from here will
1283+ * be next to impossible. In any case, we may want different options
1284+ * for rekeying. So, use our own :)
1285+ */
1286+#ifdef USE_PAM
1287+ if (!use_privsep) {
1288+ debug("Not even going to try and do PAM with privsep disabled");
1289+ return;
1290+ }
1291+
1292+ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
1293+ &pamconv, &pamh);
1294+ if (ret)
1295+ return;
1296+
1297+ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
1298+ gssapi_client.store.envval);
1299+
1300+ ret = pam_putenv(pamh, envstr);
1301+ if (!ret)
1302+ pam_setcred(pamh, PAM_REINITIALIZE_CRED);
1303+ pam_end(pamh, PAM_SUCCESS);
1304+#endif
1305+}
1306+
1307+int
1308+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
1309+ int ok = 0;
1310+
1311+ /* Check we've got credentials to store */
1312+ if (!gssapi_client.updated)
1313+ return 0;
1314+
1315+ gssapi_client.updated = 0;
1316+
1317+ temporarily_use_uid(gssapi_client.store.owner);
1318+ if (gssapi_client.mech && gssapi_client.mech->updatecreds)
1319+ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
1320+ else
1321+ debug("No update function for this mechanism");
1322+
1323+ restore_uid();
1324+
1325+ return ok;
1326 }
1327
1328 /* Privileged */
1329diff --git a/kex.c b/kex.c
1330index ce85f0439..574c76093 100644
1331--- a/kex.c
1332+++ b/kex.c
1333@@ -57,11 +57,16 @@
1334 #include "misc.h"
1335 #include "dispatch.h"
1336 #include "monitor.h"
1337+#include "xmalloc.h"
1338
1339 #include "ssherr.h"
1340 #include "sshbuf.h"
1341 #include "digest.h"
1342
1343+#ifdef GSSAPI
1344+#include "ssh-gss.h"
1345+#endif
1346+
1347 /* prototype */
1348 static int kex_choose_conf(struct ssh *);
1349 static int kex_input_newkeys(int, u_int32_t, struct ssh *);
1350@@ -115,15 +120,28 @@ static const struct kexalg kexalgs[] = {
1351 #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
1352 { NULL, 0, -1, -1},
1353 };
1354+static const struct kexalg gss_kexalgs[] = {
1355+#ifdef GSSAPI
1356+ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
1357+ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
1358+ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
1359+ { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
1360+ { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
1361+ { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256,
1362+ NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
1363+ { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
1364+#endif
1365+ { NULL, 0, -1, -1},
1366+};
1367
1368-char *
1369-kex_alg_list(char sep)
1370+static char *
1371+kex_alg_list_internal(char sep, const struct kexalg *algs)
1372 {
1373 char *ret = NULL, *tmp;
1374 size_t nlen, rlen = 0;
1375 const struct kexalg *k;
1376
1377- for (k = kexalgs; k->name != NULL; k++) {
1378+ for (k = algs; k->name != NULL; k++) {
1379 if (ret != NULL)
1380 ret[rlen++] = sep;
1381 nlen = strlen(k->name);
1382@@ -138,6 +156,18 @@ kex_alg_list(char sep)
1383 return ret;
1384 }
1385
1386+char *
1387+kex_alg_list(char sep)
1388+{
1389+ return kex_alg_list_internal(sep, kexalgs);
1390+}
1391+
1392+char *
1393+kex_gss_alg_list(char sep)
1394+{
1395+ return kex_alg_list_internal(sep, gss_kexalgs);
1396+}
1397+
1398 static const struct kexalg *
1399 kex_alg_by_name(const char *name)
1400 {
1401@@ -147,6 +177,10 @@ kex_alg_by_name(const char *name)
1402 if (strcmp(k->name, name) == 0)
1403 return k;
1404 }
1405+ for (k = gss_kexalgs; k->name != NULL; k++) {
1406+ if (strncmp(k->name, name, strlen(k->name)) == 0)
1407+ return k;
1408+ }
1409 return NULL;
1410 }
1411
1412@@ -315,6 +349,29 @@ kex_assemble_names(char **listp, const char *def, const char *all)
1413 return r;
1414 }
1415
1416+/* Validate GSS KEX method name list */
1417+int
1418+kex_gss_names_valid(const char *names)
1419+{
1420+ char *s, *cp, *p;
1421+
1422+ if (names == NULL || *names == '\0')
1423+ return 0;
1424+ s = cp = xstrdup(names);
1425+ for ((p = strsep(&cp, ",")); p && *p != '\0';
1426+ (p = strsep(&cp, ","))) {
1427+ if (strncmp(p, "gss-", 4) != 0
1428+ || kex_alg_by_name(p) == NULL) {
1429+ error("Unsupported KEX algorithm \"%.100s\"", p);
1430+ free(s);
1431+ return 0;
1432+ }
1433+ }
1434+ debug3("gss kex names ok: [%s]", names);
1435+ free(s);
1436+ return 1;
1437+}
1438+
1439 /* put algorithm proposal into buffer */
1440 int
1441 kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
1442@@ -698,6 +755,9 @@ kex_free(struct kex *kex)
1443 sshbuf_free(kex->server_version);
1444 sshbuf_free(kex->client_pub);
1445 free(kex->session_id);
1446+#ifdef GSSAPI
1447+ free(kex->gss_host);
1448+#endif /* GSSAPI */
1449 free(kex->failed_choice);
1450 free(kex->hostkey_alg);
1451 free(kex->name);
1452diff --git a/kex.h b/kex.h
1453index a5ae6ac05..fe7141414 100644
1454--- a/kex.h
1455+++ b/kex.h
1456@@ -102,6 +102,15 @@ enum kex_exchange {
1457 KEX_ECDH_SHA2,
1458 KEX_C25519_SHA256,
1459 KEX_KEM_SNTRUP4591761X25519_SHA512,
1460+#ifdef GSSAPI
1461+ KEX_GSS_GRP1_SHA1,
1462+ KEX_GSS_GRP14_SHA1,
1463+ KEX_GSS_GRP14_SHA256,
1464+ KEX_GSS_GRP16_SHA512,
1465+ KEX_GSS_GEX_SHA1,
1466+ KEX_GSS_NISTP256_SHA256,
1467+ KEX_GSS_C25519_SHA256,
1468+#endif
1469 KEX_MAX
1470 };
1471
1472@@ -153,6 +162,12 @@ struct kex {
1473 u_int flags;
1474 int hash_alg;
1475 int ec_nid;
1476+#ifdef GSSAPI
1477+ int gss_deleg_creds;
1478+ int gss_trust_dns;
1479+ char *gss_host;
1480+ char *gss_client;
1481+#endif
1482 char *failed_choice;
1483 int (*verify_host_key)(struct sshkey *, struct ssh *);
1484 struct sshkey *(*load_host_public_key)(int, int, struct ssh *);
1485@@ -174,8 +189,10 @@ struct kex {
1486
1487 int kex_names_valid(const char *);
1488 char *kex_alg_list(char);
1489+char *kex_gss_alg_list(char);
1490 char *kex_names_cat(const char *, const char *);
1491 int kex_assemble_names(char **, const char *, const char *);
1492+int kex_gss_names_valid(const char *);
1493
1494 int kex_exchange_identification(struct ssh *, int, const char *);
1495
1496@@ -202,6 +219,12 @@ int kexgex_client(struct ssh *);
1497 int kexgex_server(struct ssh *);
1498 int kex_gen_client(struct ssh *);
1499 int kex_gen_server(struct ssh *);
1500+#if defined(GSSAPI) && defined(WITH_OPENSSL)
1501+int kexgssgex_client(struct ssh *);
1502+int kexgssgex_server(struct ssh *);
1503+int kexgss_client(struct ssh *);
1504+int kexgss_server(struct ssh *);
1505+#endif
1506
1507 int kex_dh_keypair(struct kex *);
1508 int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
1509@@ -234,6 +257,12 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *,
1510 const BIGNUM *, const u_char *, size_t,
1511 u_char *, size_t *);
1512
1513+int kex_gen_hash(int hash_alg, const struct sshbuf *client_version,
1514+ const struct sshbuf *server_version, const struct sshbuf *client_kexinit,
1515+ const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob,
1516+ const struct sshbuf *client_pub, const struct sshbuf *server_pub,
1517+ const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen);
1518+
1519 void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
1520 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
1521 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
1522diff --git a/kexdh.c b/kexdh.c
1523index 67133e339..edaa46762 100644
1524--- a/kexdh.c
1525+++ b/kexdh.c
1526@@ -48,13 +48,23 @@ kex_dh_keygen(struct kex *kex)
1527 {
1528 switch (kex->kex_type) {
1529 case KEX_DH_GRP1_SHA1:
1530+#ifdef GSSAPI
1531+ case KEX_GSS_GRP1_SHA1:
1532+#endif
1533 kex->dh = dh_new_group1();
1534 break;
1535 case KEX_DH_GRP14_SHA1:
1536 case KEX_DH_GRP14_SHA256:
1537+#ifdef GSSAPI
1538+ case KEX_GSS_GRP14_SHA1:
1539+ case KEX_GSS_GRP14_SHA256:
1540+#endif
1541 kex->dh = dh_new_group14();
1542 break;
1543 case KEX_DH_GRP16_SHA512:
1544+#ifdef GSSAPI
1545+ case KEX_GSS_GRP16_SHA512:
1546+#endif
1547 kex->dh = dh_new_group16();
1548 break;
1549 case KEX_DH_GRP18_SHA512:
1550diff --git a/kexgen.c b/kexgen.c
1551index 69348b964..c0e8c2f44 100644
1552--- a/kexgen.c
1553+++ b/kexgen.c
1554@@ -44,7 +44,7 @@
1555 static int input_kex_gen_init(int, u_int32_t, struct ssh *);
1556 static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh);
1557
1558-static int
1559+int
1560 kex_gen_hash(
1561 int hash_alg,
1562 const struct sshbuf *client_version,
1563diff --git a/kexgssc.c b/kexgssc.c
1564new file mode 100644
1565index 000000000..f6e1405eb
1566--- /dev/null
1567+++ b/kexgssc.c
1568@@ -0,0 +1,606 @@
1569+/*
1570+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
1571+ *
1572+ * Redistribution and use in source and binary forms, with or without
1573+ * modification, are permitted provided that the following conditions
1574+ * are met:
1575+ * 1. Redistributions of source code must retain the above copyright
1576+ * notice, this list of conditions and the following disclaimer.
1577+ * 2. Redistributions in binary form must reproduce the above copyright
1578+ * notice, this list of conditions and the following disclaimer in the
1579+ * documentation and/or other materials provided with the distribution.
1580+ *
1581+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
1582+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1583+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1584+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1585+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1586+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1587+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1588+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1589+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1590+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1591+ */
1592+
1593+#include "includes.h"
1594+
1595+#if defined(GSSAPI) && defined(WITH_OPENSSL)
1596+
1597+#include "includes.h"
1598+
1599+#include <openssl/crypto.h>
1600+#include <openssl/bn.h>
1601+
1602+#include <string.h>
1603+
1604+#include "xmalloc.h"
1605+#include "sshbuf.h"
1606+#include "ssh2.h"
1607+#include "sshkey.h"
1608+#include "cipher.h"
1609+#include "kex.h"
1610+#include "log.h"
1611+#include "packet.h"
1612+#include "dh.h"
1613+#include "digest.h"
1614+#include "ssherr.h"
1615+
1616+#include "ssh-gss.h"
1617+
1618+int
1619+kexgss_client(struct ssh *ssh)
1620+{
1621+ struct kex *kex = ssh->kex;
1622+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
1623+ recv_tok = GSS_C_EMPTY_BUFFER,
1624+ gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
1625+ Gssctxt *ctxt;
1626+ OM_uint32 maj_status, min_status, ret_flags;
1627+ struct sshbuf *server_blob = NULL;
1628+ struct sshbuf *shared_secret = NULL;
1629+ struct sshbuf *server_host_key_blob = NULL;
1630+ struct sshbuf *empty = NULL;
1631+ u_char *msg;
1632+ int type = 0;
1633+ int first = 1;
1634+ u_char hash[SSH_DIGEST_MAX_LENGTH];
1635+ size_t hashlen;
1636+ u_char c;
1637+ int r;
1638+
1639+ /* Initialise our GSSAPI world */
1640+ ssh_gssapi_build_ctx(&ctxt);
1641+ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
1642+ == GSS_C_NO_OID)
1643+ fatal("Couldn't identify host exchange");
1644+
1645+ if (ssh_gssapi_import_name(ctxt, kex->gss_host))
1646+ fatal("Couldn't import hostname");
1647+
1648+ if (kex->gss_client &&
1649+ ssh_gssapi_client_identity(ctxt, kex->gss_client))
1650+ fatal("Couldn't acquire client credentials");
1651+
1652+ /* Step 1 */
1653+ switch (kex->kex_type) {
1654+ case KEX_GSS_GRP1_SHA1:
1655+ case KEX_GSS_GRP14_SHA1:
1656+ case KEX_GSS_GRP14_SHA256:
1657+ case KEX_GSS_GRP16_SHA512:
1658+ r = kex_dh_keypair(kex);
1659+ break;
1660+ case KEX_GSS_NISTP256_SHA256:
1661+ r = kex_ecdh_keypair(kex);
1662+ break;
1663+ case KEX_GSS_C25519_SHA256:
1664+ r = kex_c25519_keypair(kex);
1665+ break;
1666+ default:
1667+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1668+ }
1669+ if (r != 0)
1670+ return r;
1671+
1672+ token_ptr = GSS_C_NO_BUFFER;
1673+
1674+ do {
1675+ debug("Calling gss_init_sec_context");
1676+
1677+ maj_status = ssh_gssapi_init_ctx(ctxt,
1678+ kex->gss_deleg_creds, token_ptr, &send_tok,
1679+ &ret_flags);
1680+
1681+ if (GSS_ERROR(maj_status)) {
1682+ /* XXX Useles code: Missing send? */
1683+ if (send_tok.length != 0) {
1684+ if ((r = sshpkt_start(ssh,
1685+ SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
1686+ (r = sshpkt_put_string(ssh, send_tok.value,
1687+ send_tok.length)) != 0)
1688+ fatal("sshpkt failed: %s", ssh_err(r));
1689+ }
1690+ fatal("gss_init_context failed");
1691+ }
1692+
1693+ /* If we've got an old receive buffer get rid of it */
1694+ if (token_ptr != GSS_C_NO_BUFFER)
1695+ gss_release_buffer(&min_status, &recv_tok);
1696+
1697+ if (maj_status == GSS_S_COMPLETE) {
1698+ /* If mutual state flag is not true, kex fails */
1699+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
1700+ fatal("Mutual authentication failed");
1701+
1702+ /* If integ avail flag is not true kex fails */
1703+ if (!(ret_flags & GSS_C_INTEG_FLAG))
1704+ fatal("Integrity check failed");
1705+ }
1706+
1707+ /*
1708+ * If we have data to send, then the last message that we
1709+ * received cannot have been a 'complete'.
1710+ */
1711+ if (send_tok.length != 0) {
1712+ if (first) {
1713+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
1714+ (r = sshpkt_put_string(ssh, send_tok.value,
1715+ send_tok.length)) != 0 ||
1716+ (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
1717+ fatal("failed to construct packet: %s", ssh_err(r));
1718+ first = 0;
1719+ } else {
1720+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
1721+ (r = sshpkt_put_string(ssh, send_tok.value,
1722+ send_tok.length)) != 0)
1723+ fatal("failed to construct packet: %s", ssh_err(r));
1724+ }
1725+ if ((r = sshpkt_send(ssh)) != 0)
1726+ fatal("failed to send packet: %s", ssh_err(r));
1727+ gss_release_buffer(&min_status, &send_tok);
1728+
1729+ /* If we've sent them data, they should reply */
1730+ do {
1731+ type = ssh_packet_read(ssh);
1732+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
1733+ debug("Received KEXGSS_HOSTKEY");
1734+ if (server_host_key_blob)
1735+ fatal("Server host key received more than once");
1736+ if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
1737+ fatal("Failed to read server host key: %s", ssh_err(r));
1738+ }
1739+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
1740+
1741+ switch (type) {
1742+ case SSH2_MSG_KEXGSS_CONTINUE:
1743+ debug("Received GSSAPI_CONTINUE");
1744+ if (maj_status == GSS_S_COMPLETE)
1745+ fatal("GSSAPI Continue received from server when complete");
1746+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
1747+ &recv_tok)) != 0 ||
1748+ (r = sshpkt_get_end(ssh)) != 0)
1749+ fatal("Failed to read token: %s", ssh_err(r));
1750+ break;
1751+ case SSH2_MSG_KEXGSS_COMPLETE:
1752+ debug("Received GSSAPI_COMPLETE");
1753+ if (msg_tok.value != NULL)
1754+ fatal("Received GSSAPI_COMPLETE twice?");
1755+ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
1756+ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
1757+ &msg_tok)) != 0)
1758+ fatal("Failed to read message: %s", ssh_err(r));
1759+
1760+ /* Is there a token included? */
1761+ if ((r = sshpkt_get_u8(ssh, &c)) != 0)
1762+ fatal("sshpkt failed: %s", ssh_err(r));
1763+ if (c) {
1764+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
1765+ ssh, &recv_tok)) != 0)
1766+ fatal("Failed to read token: %s", ssh_err(r));
1767+ /* If we're already complete - protocol error */
1768+ if (maj_status == GSS_S_COMPLETE)
1769+ sshpkt_disconnect(ssh, "Protocol error: received token when complete");
1770+ } else {
1771+ /* No token included */
1772+ if (maj_status != GSS_S_COMPLETE)
1773+ sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
1774+ }
1775+ if ((r = sshpkt_get_end(ssh)) != 0) {
1776+ fatal("Expecting end of packet.");
1777+ }
1778+ break;
1779+ case SSH2_MSG_KEXGSS_ERROR:
1780+ debug("Received Error");
1781+ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
1782+ (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
1783+ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
1784+ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
1785+ (r = sshpkt_get_end(ssh)) != 0)
1786+ fatal("sshpkt_get failed: %s", ssh_err(r));
1787+ fatal("GSSAPI Error: \n%.400s", msg);
1788+ default:
1789+ sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
1790+ type);
1791+ }
1792+ token_ptr = &recv_tok;
1793+ } else {
1794+ /* No data, and not complete */
1795+ if (maj_status != GSS_S_COMPLETE)
1796+ fatal("Not complete, and no token output");
1797+ }
1798+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
1799+
1800+ /*
1801+ * We _must_ have received a COMPLETE message in reply from the
1802+ * server, which will have set server_blob and msg_tok
1803+ */
1804+
1805+ if (type != SSH2_MSG_KEXGSS_COMPLETE)
1806+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
1807+
1808+ /* compute shared secret */
1809+ switch (kex->kex_type) {
1810+ case KEX_GSS_GRP1_SHA1:
1811+ case KEX_GSS_GRP14_SHA1:
1812+ case KEX_GSS_GRP14_SHA256:
1813+ case KEX_GSS_GRP16_SHA512:
1814+ r = kex_dh_dec(kex, server_blob, &shared_secret);
1815+ break;
1816+ case KEX_GSS_C25519_SHA256:
1817+ if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80)
1818+ fatal("The received key has MSB of last octet set!");
1819+ r = kex_c25519_dec(kex, server_blob, &shared_secret);
1820+ break;
1821+ case KEX_GSS_NISTP256_SHA256:
1822+ if (sshbuf_len(server_blob) != 65)
1823+ fatal("The received NIST-P256 key did not match"
1824+ "expected length (expected 65, got %zu)", sshbuf_len(server_blob));
1825+
1826+ if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
1827+ fatal("The received NIST-P256 key does not have first octet 0x04");
1828+
1829+ r = kex_ecdh_dec(kex, server_blob, &shared_secret);
1830+ break;
1831+ default:
1832+ r = SSH_ERR_INVALID_ARGUMENT;
1833+ break;
1834+ }
1835+ if (r != 0)
1836+ goto out;
1837+
1838+ if ((empty = sshbuf_new()) == NULL) {
1839+ r = SSH_ERR_ALLOC_FAIL;
1840+ goto out;
1841+ }
1842+
1843+ hashlen = sizeof(hash);
1844+ if ((r = kex_gen_hash(
1845+ kex->hash_alg,
1846+ kex->client_version,
1847+ kex->server_version,
1848+ kex->my,
1849+ kex->peer,
1850+ (server_host_key_blob ? server_host_key_blob : empty),
1851+ kex->client_pub,
1852+ server_blob,
1853+ shared_secret,
1854+ hash, &hashlen)) != 0)
1855+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1856+
1857+ gssbuf.value = hash;
1858+ gssbuf.length = hashlen;
1859+
1860+ /* Verify that the hash matches the MIC we just got. */
1861+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
1862+ sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
1863+
1864+ gss_release_buffer(&min_status, &msg_tok);
1865+
1866+ if (kex->gss_deleg_creds)
1867+ ssh_gssapi_credentials_updated(ctxt);
1868+
1869+ if (gss_kex_context == NULL)
1870+ gss_kex_context = ctxt;
1871+ else
1872+ ssh_gssapi_delete_ctx(&ctxt);
1873+
1874+ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
1875+ r = kex_send_newkeys(ssh);
1876+
1877+out:
1878+ explicit_bzero(hash, sizeof(hash));
1879+ explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
1880+ sshbuf_free(empty);
1881+ sshbuf_free(server_host_key_blob);
1882+ sshbuf_free(server_blob);
1883+ sshbuf_free(shared_secret);
1884+ sshbuf_free(kex->client_pub);
1885+ kex->client_pub = NULL;
1886+ return r;
1887+}
1888+
1889+int
1890+kexgssgex_client(struct ssh *ssh)
1891+{
1892+ struct kex *kex = ssh->kex;
1893+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
1894+ recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
1895+ msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
1896+ Gssctxt *ctxt;
1897+ OM_uint32 maj_status, min_status, ret_flags;
1898+ struct sshbuf *shared_secret = NULL;
1899+ BIGNUM *p = NULL;
1900+ BIGNUM *g = NULL;
1901+ struct sshbuf *buf = NULL;
1902+ struct sshbuf *server_host_key_blob = NULL;
1903+ struct sshbuf *server_blob = NULL;
1904+ BIGNUM *dh_server_pub = NULL;
1905+ u_char *msg;
1906+ int type = 0;
1907+ int first = 1;
1908+ u_char hash[SSH_DIGEST_MAX_LENGTH];
1909+ size_t hashlen;
1910+ const BIGNUM *pub_key, *dh_p, *dh_g;
1911+ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
1912+ struct sshbuf *empty = NULL;
1913+ u_char c;
1914+ int r;
1915+
1916+ /* Initialise our GSSAPI world */
1917+ ssh_gssapi_build_ctx(&ctxt);
1918+ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
1919+ == GSS_C_NO_OID)
1920+ fatal("Couldn't identify host exchange");
1921+
1922+ if (ssh_gssapi_import_name(ctxt, kex->gss_host))
1923+ fatal("Couldn't import hostname");
1924+
1925+ if (kex->gss_client &&
1926+ ssh_gssapi_client_identity(ctxt, kex->gss_client))
1927+ fatal("Couldn't acquire client credentials");
1928+
1929+ debug("Doing group exchange");
1930+ nbits = dh_estimate(kex->dh_need * 8);
1931+
1932+ kex->min = DH_GRP_MIN;
1933+ kex->max = DH_GRP_MAX;
1934+ kex->nbits = nbits;
1935+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
1936+ (r = sshpkt_put_u32(ssh, min)) != 0 ||
1937+ (r = sshpkt_put_u32(ssh, nbits)) != 0 ||
1938+ (r = sshpkt_put_u32(ssh, max)) != 0 ||
1939+ (r = sshpkt_send(ssh)) != 0)
1940+ fatal("Failed to construct a packet: %s", ssh_err(r));
1941+
1942+ if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0)
1943+ fatal("Error: %s", ssh_err(r));
1944+
1945+ if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
1946+ (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
1947+ (r = sshpkt_get_end(ssh)) != 0)
1948+ fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
1949+
1950+ if (BN_num_bits(p) < min || BN_num_bits(p) > max)
1951+ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
1952+ min, BN_num_bits(p), max);
1953+
1954+ if ((kex->dh = dh_new_group(g, p)) == NULL)
1955+ fatal("dn_new_group() failed");
1956+ p = g = NULL; /* belong to kex->dh now */
1957+
1958+ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
1959+ goto out;
1960+ DH_get0_key(kex->dh, &pub_key, NULL);
1961+
1962+ token_ptr = GSS_C_NO_BUFFER;
1963+
1964+ do {
1965+ /* Step 2 - call GSS_Init_sec_context() */
1966+ debug("Calling gss_init_sec_context");
1967+
1968+ maj_status = ssh_gssapi_init_ctx(ctxt,
1969+ kex->gss_deleg_creds, token_ptr, &send_tok,
1970+ &ret_flags);
1971+
1972+ if (GSS_ERROR(maj_status)) {
1973+ /* XXX Useles code: Missing send? */
1974+ if (send_tok.length != 0) {
1975+ if ((r = sshpkt_start(ssh,
1976+ SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
1977+ (r = sshpkt_put_string(ssh, send_tok.value,
1978+ send_tok.length)) != 0)
1979+ fatal("sshpkt failed: %s", ssh_err(r));
1980+ }
1981+ fatal("gss_init_context failed");
1982+ }
1983+
1984+ /* If we've got an old receive buffer get rid of it */
1985+ if (token_ptr != GSS_C_NO_BUFFER)
1986+ gss_release_buffer(&min_status, &recv_tok);
1987+
1988+ if (maj_status == GSS_S_COMPLETE) {
1989+ /* If mutual state flag is not true, kex fails */
1990+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
1991+ fatal("Mutual authentication failed");
1992+
1993+ /* If integ avail flag is not true kex fails */
1994+ if (!(ret_flags & GSS_C_INTEG_FLAG))
1995+ fatal("Integrity check failed");
1996+ }
1997+
1998+ /*
1999+ * If we have data to send, then the last message that we
2000+ * received cannot have been a 'complete'.
2001+ */
2002+ if (send_tok.length != 0) {
2003+ if (first) {
2004+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
2005+ (r = sshpkt_put_string(ssh, send_tok.value,
2006+ send_tok.length)) != 0 ||
2007+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
2008+ fatal("sshpkt failed: %s", ssh_err(r));
2009+ first = 0;
2010+ } else {
2011+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2012+ (r = sshpkt_put_string(ssh,send_tok.value,
2013+ send_tok.length)) != 0)
2014+ fatal("sshpkt failed: %s", ssh_err(r));
2015+ }
2016+ if ((r = sshpkt_send(ssh)) != 0)
2017+ fatal("sshpkt_send failed: %s", ssh_err(r));
2018+ gss_release_buffer(&min_status, &send_tok);
2019+
2020+ /* If we've sent them data, they should reply */
2021+ do {
2022+ type = ssh_packet_read(ssh);
2023+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
2024+ debug("Received KEXGSS_HOSTKEY");
2025+ if (server_host_key_blob)
2026+ fatal("Server host key received more than once");
2027+ if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
2028+ fatal("sshpkt failed: %s", ssh_err(r));
2029+ }
2030+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
2031+
2032+ switch (type) {
2033+ case SSH2_MSG_KEXGSS_CONTINUE:
2034+ debug("Received GSSAPI_CONTINUE");
2035+ if (maj_status == GSS_S_COMPLETE)
2036+ fatal("GSSAPI Continue received from server when complete");
2037+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2038+ &recv_tok)) != 0 ||
2039+ (r = sshpkt_get_end(ssh)) != 0)
2040+ fatal("sshpkt failed: %s", ssh_err(r));
2041+ break;
2042+ case SSH2_MSG_KEXGSS_COMPLETE:
2043+ debug("Received GSSAPI_COMPLETE");
2044+ if (msg_tok.value != NULL)
2045+ fatal("Received GSSAPI_COMPLETE twice?");
2046+ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
2047+ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2048+ &msg_tok)) != 0)
2049+ fatal("sshpkt failed: %s", ssh_err(r));
2050+
2051+ /* Is there a token included? */
2052+ if ((r = sshpkt_get_u8(ssh, &c)) != 0)
2053+ fatal("sshpkt failed: %s", ssh_err(r));
2054+ if (c) {
2055+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
2056+ ssh, &recv_tok)) != 0 ||
2057+ (r = sshpkt_get_end(ssh)) != 0)
2058+ fatal("sshpkt failed: %s", ssh_err(r));
2059+ /* If we're already complete - protocol error */
2060+ if (maj_status == GSS_S_COMPLETE)
2061+ sshpkt_disconnect(ssh, "Protocol error: received token when complete");
2062+ } else {
2063+ /* No token included */
2064+ if (maj_status != GSS_S_COMPLETE)
2065+ sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
2066+ }
2067+ break;
2068+ case SSH2_MSG_KEXGSS_ERROR:
2069+ debug("Received Error");
2070+ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
2071+ (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
2072+ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
2073+ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
2074+ (r = sshpkt_get_end(ssh)) != 0)
2075+ fatal("sshpkt failed: %s", ssh_err(r));
2076+ fatal("GSSAPI Error: \n%.400s", msg);
2077+ default:
2078+ sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
2079+ type);
2080+ }
2081+ token_ptr = &recv_tok;
2082+ } else {
2083+ /* No data, and not complete */
2084+ if (maj_status != GSS_S_COMPLETE)
2085+ fatal("Not complete, and no token output");
2086+ }
2087+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
2088+
2089+ /*
2090+ * We _must_ have received a COMPLETE message in reply from the
2091+ * server, which will have set dh_server_pub and msg_tok
2092+ */
2093+
2094+ if (type != SSH2_MSG_KEXGSS_COMPLETE)
2095+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
2096+
2097+ /* 7. C verifies that the key Q_S is valid */
2098+ /* 8. C computes shared secret */
2099+ if ((buf = sshbuf_new()) == NULL ||
2100+ (r = sshbuf_put_stringb(buf, server_blob)) != 0 ||
2101+ (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0)
2102+ goto out;
2103+ sshbuf_free(buf);
2104+ buf = NULL;
2105+
2106+ if ((shared_secret = sshbuf_new()) == NULL) {
2107+ r = SSH_ERR_ALLOC_FAIL;
2108+ goto out;
2109+ }
2110+
2111+ if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
2112+ goto out;
2113+ if ((empty = sshbuf_new()) == NULL) {
2114+ r = SSH_ERR_ALLOC_FAIL;
2115+ goto out;
2116+ }
2117+
2118+ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
2119+ hashlen = sizeof(hash);
2120+ if ((r = kexgex_hash(
2121+ kex->hash_alg,
2122+ kex->client_version,
2123+ kex->server_version,
2124+ kex->my,
2125+ kex->peer,
2126+ (server_host_key_blob ? server_host_key_blob : empty),
2127+ kex->min, kex->nbits, kex->max,
2128+ dh_p, dh_g,
2129+ pub_key,
2130+ dh_server_pub,
2131+ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
2132+ hash, &hashlen)) != 0)
2133+ fatal("Failed to calculate hash: %s", ssh_err(r));
2134+
2135+ gssbuf.value = hash;
2136+ gssbuf.length = hashlen;
2137+
2138+ /* Verify that the hash matches the MIC we just got. */
2139+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
2140+ sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
2141+
2142+ gss_release_buffer(&min_status, &msg_tok);
2143+
2144+ /* save session id */
2145+ if (kex->session_id == NULL) {
2146+ kex->session_id_len = hashlen;
2147+ kex->session_id = xmalloc(kex->session_id_len);
2148+ memcpy(kex->session_id, hash, kex->session_id_len);
2149+ }
2150+
2151+ if (kex->gss_deleg_creds)
2152+ ssh_gssapi_credentials_updated(ctxt);
2153+
2154+ if (gss_kex_context == NULL)
2155+ gss_kex_context = ctxt;
2156+ else
2157+ ssh_gssapi_delete_ctx(&ctxt);
2158+
2159+ /* Finally derive the keys and send them */
2160+ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
2161+ r = kex_send_newkeys(ssh);
2162+out:
2163+ sshbuf_free(buf);
2164+ sshbuf_free(server_blob);
2165+ sshbuf_free(empty);
2166+ explicit_bzero(hash, sizeof(hash));
2167+ DH_free(kex->dh);
2168+ kex->dh = NULL;
2169+ BN_clear_free(dh_server_pub);
2170+ sshbuf_free(shared_secret);
2171+ sshbuf_free(server_host_key_blob);
2172+ return r;
2173+}
2174+#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
2175diff --git a/kexgsss.c b/kexgsss.c
2176new file mode 100644
2177index 000000000..60bc02deb
2178--- /dev/null
2179+++ b/kexgsss.c
2180@@ -0,0 +1,474 @@
2181+/*
2182+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
2183+ *
2184+ * Redistribution and use in source and binary forms, with or without
2185+ * modification, are permitted provided that the following conditions
2186+ * are met:
2187+ * 1. Redistributions of source code must retain the above copyright
2188+ * notice, this list of conditions and the following disclaimer.
2189+ * 2. Redistributions in binary form must reproduce the above copyright
2190+ * notice, this list of conditions and the following disclaimer in the
2191+ * documentation and/or other materials provided with the distribution.
2192+ *
2193+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
2194+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2195+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2196+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2197+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2198+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2199+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2200+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2201+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2202+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2203+ */
2204+
2205+#include "includes.h"
2206+
2207+#if defined(GSSAPI) && defined(WITH_OPENSSL)
2208+
2209+#include <string.h>
2210+
2211+#include <openssl/crypto.h>
2212+#include <openssl/bn.h>
2213+
2214+#include "xmalloc.h"
2215+#include "sshbuf.h"
2216+#include "ssh2.h"
2217+#include "sshkey.h"
2218+#include "cipher.h"
2219+#include "kex.h"
2220+#include "log.h"
2221+#include "packet.h"
2222+#include "dh.h"
2223+#include "ssh-gss.h"
2224+#include "monitor_wrap.h"
2225+#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */
2226+#include "servconf.h"
2227+#include "ssh-gss.h"
2228+#include "digest.h"
2229+#include "ssherr.h"
2230+
2231+extern ServerOptions options;
2232+
2233+int
2234+kexgss_server(struct ssh *ssh)
2235+{
2236+ struct kex *kex = ssh->kex;
2237+ OM_uint32 maj_status, min_status;
2238+
2239+ /*
2240+ * Some GSSAPI implementations use the input value of ret_flags (an
2241+ * output variable) as a means of triggering mechanism specific
2242+ * features. Initializing it to zero avoids inadvertently
2243+ * activating this non-standard behaviour.
2244+ */
2245+
2246+ OM_uint32 ret_flags = 0;
2247+ gss_buffer_desc gssbuf, recv_tok, msg_tok;
2248+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
2249+ Gssctxt *ctxt = NULL;
2250+ struct sshbuf *shared_secret = NULL;
2251+ struct sshbuf *client_pubkey = NULL;
2252+ struct sshbuf *server_pubkey = NULL;
2253+ struct sshbuf *empty = sshbuf_new();
2254+ int type = 0;
2255+ gss_OID oid;
2256+ char *mechs;
2257+ u_char hash[SSH_DIGEST_MAX_LENGTH];
2258+ size_t hashlen;
2259+ int r;
2260+
2261+ /* Initialise GSSAPI */
2262+
2263+ /* If we're rekeying, privsep means that some of the private structures
2264+ * in the GSSAPI code are no longer available. This kludges them back
2265+ * into life
2266+ */
2267+ if (!ssh_gssapi_oid_table_ok()) {
2268+ mechs = ssh_gssapi_server_mechanisms();
2269+ free(mechs);
2270+ }
2271+
2272+ debug2("%s: Identifying %s", __func__, kex->name);
2273+ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
2274+ if (oid == GSS_C_NO_OID)
2275+ fatal("Unknown gssapi mechanism");
2276+
2277+ debug2("%s: Acquiring credentials", __func__);
2278+
2279+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
2280+ fatal("Unable to acquire credentials for the server");
2281+
2282+ do {
2283+ debug("Wait SSH2_MSG_KEXGSS_INIT");
2284+ type = ssh_packet_read(ssh);
2285+ switch(type) {
2286+ case SSH2_MSG_KEXGSS_INIT:
2287+ if (client_pubkey != NULL)
2288+ fatal("Received KEXGSS_INIT after initialising");
2289+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2290+ &recv_tok)) != 0 ||
2291+ (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
2292+ (r = sshpkt_get_end(ssh)) != 0)
2293+ fatal("sshpkt failed: %s", ssh_err(r));
2294+
2295+ switch (kex->kex_type) {
2296+ case KEX_GSS_GRP1_SHA1:
2297+ case KEX_GSS_GRP14_SHA1:
2298+ case KEX_GSS_GRP14_SHA256:
2299+ case KEX_GSS_GRP16_SHA512:
2300+ r = kex_dh_enc(kex, client_pubkey, &server_pubkey,
2301+ &shared_secret);
2302+ break;
2303+ case KEX_GSS_NISTP256_SHA256:
2304+ r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
2305+ &shared_secret);
2306+ break;
2307+ case KEX_GSS_C25519_SHA256:
2308+ r = kex_c25519_enc(kex, client_pubkey, &server_pubkey,
2309+ &shared_secret);
2310+ break;
2311+ default:
2312+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
2313+ }
2314+ if (r != 0)
2315+ goto out;
2316+
2317+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
2318+ break;
2319+ case SSH2_MSG_KEXGSS_CONTINUE:
2320+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2321+ &recv_tok)) != 0 ||
2322+ (r = sshpkt_get_end(ssh)) != 0)
2323+ fatal("sshpkt failed: %s", ssh_err(r));
2324+ break;
2325+ default:
2326+ sshpkt_disconnect(ssh,
2327+ "Protocol error: didn't expect packet type %d",
2328+ type);
2329+ }
2330+
2331+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
2332+ &send_tok, &ret_flags));
2333+
2334+ gss_release_buffer(&min_status, &recv_tok);
2335+
2336+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
2337+ fatal("Zero length token output when incomplete");
2338+
2339+ if (client_pubkey == NULL)
2340+ fatal("No client public key");
2341+
2342+ if (maj_status & GSS_S_CONTINUE_NEEDED) {
2343+ debug("Sending GSSAPI_CONTINUE");
2344+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2345+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
2346+ (r = sshpkt_send(ssh)) != 0)
2347+ fatal("sshpkt failed: %s", ssh_err(r));
2348+ gss_release_buffer(&min_status, &send_tok);
2349+ }
2350+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
2351+
2352+ if (GSS_ERROR(maj_status)) {
2353+ if (send_tok.length > 0) {
2354+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2355+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
2356+ (r = sshpkt_send(ssh)) != 0)
2357+ fatal("sshpkt failed: %s", ssh_err(r));
2358+ }
2359+ fatal("accept_ctx died");
2360+ }
2361+
2362+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
2363+ fatal("Mutual Authentication flag wasn't set");
2364+
2365+ if (!(ret_flags & GSS_C_INTEG_FLAG))
2366+ fatal("Integrity flag wasn't set");
2367+
2368+ hashlen = sizeof(hash);
2369+ if ((r = kex_gen_hash(
2370+ kex->hash_alg,
2371+ kex->client_version,
2372+ kex->server_version,
2373+ kex->peer,
2374+ kex->my,
2375+ empty,
2376+ client_pubkey,
2377+ server_pubkey,
2378+ shared_secret,
2379+ hash, &hashlen)) != 0)
2380+ goto out;
2381+
2382+ gssbuf.value = hash;
2383+ gssbuf.length = hashlen;
2384+
2385+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
2386+ fatal("Couldn't get MIC");
2387+
2388+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
2389+ (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 ||
2390+ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
2391+ fatal("sshpkt failed: %s", ssh_err(r));
2392+
2393+ if (send_tok.length != 0) {
2394+ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
2395+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
2396+ fatal("sshpkt failed: %s", ssh_err(r));
2397+ } else {
2398+ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
2399+ fatal("sshpkt failed: %s", ssh_err(r));
2400+ }
2401+ if ((r = sshpkt_send(ssh)) != 0)
2402+ fatal("sshpkt_send failed: %s", ssh_err(r));
2403+
2404+ gss_release_buffer(&min_status, &send_tok);
2405+ gss_release_buffer(&min_status, &msg_tok);
2406+
2407+ if (gss_kex_context == NULL)
2408+ gss_kex_context = ctxt;
2409+ else
2410+ ssh_gssapi_delete_ctx(&ctxt);
2411+
2412+ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
2413+ r = kex_send_newkeys(ssh);
2414+
2415+ /* If this was a rekey, then save out any delegated credentials we
2416+ * just exchanged. */
2417+ if (options.gss_store_rekey)
2418+ ssh_gssapi_rekey_creds();
2419+out:
2420+ sshbuf_free(empty);
2421+ explicit_bzero(hash, sizeof(hash));
2422+ sshbuf_free(shared_secret);
2423+ sshbuf_free(client_pubkey);
2424+ sshbuf_free(server_pubkey);
2425+ return r;
2426+}
2427+
2428+int
2429+kexgssgex_server(struct ssh *ssh)
2430+{
2431+ struct kex *kex = ssh->kex;
2432+ OM_uint32 maj_status, min_status;
2433+
2434+ /*
2435+ * Some GSSAPI implementations use the input value of ret_flags (an
2436+ * output variable) as a means of triggering mechanism specific
2437+ * features. Initializing it to zero avoids inadvertently
2438+ * activating this non-standard behaviour.
2439+ */
2440+
2441+ OM_uint32 ret_flags = 0;
2442+ gss_buffer_desc gssbuf, recv_tok, msg_tok;
2443+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
2444+ Gssctxt *ctxt = NULL;
2445+ struct sshbuf *shared_secret = NULL;
2446+ int type = 0;
2447+ gss_OID oid;
2448+ char *mechs;
2449+ u_char hash[SSH_DIGEST_MAX_LENGTH];
2450+ size_t hashlen;
2451+ BIGNUM *dh_client_pub = NULL;
2452+ const BIGNUM *pub_key, *dh_p, *dh_g;
2453+ int min = -1, max = -1, nbits = -1;
2454+ int cmin = -1, cmax = -1; /* client proposal */
2455+ struct sshbuf *empty = sshbuf_new();
2456+ int r;
2457+
2458+ /* Initialise GSSAPI */
2459+
2460+ /* If we're rekeying, privsep means that some of the private structures
2461+ * in the GSSAPI code are no longer available. This kludges them back
2462+ * into life
2463+ */
2464+ if (!ssh_gssapi_oid_table_ok())
2465+ if ((mechs = ssh_gssapi_server_mechanisms()))
2466+ free(mechs);
2467+
2468+ debug2("%s: Identifying %s", __func__, kex->name);
2469+ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
2470+ if (oid == GSS_C_NO_OID)
2471+ fatal("Unknown gssapi mechanism");
2472+
2473+ debug2("%s: Acquiring credentials", __func__);
2474+
2475+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
2476+ fatal("Unable to acquire credentials for the server");
2477+
2478+ /* 5. S generates an ephemeral key pair (do the allocations early) */
2479+ debug("Doing group exchange");
2480+ ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ);
2481+ /* store client proposal to provide valid signature */
2482+ if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
2483+ (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
2484+ (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
2485+ (r = sshpkt_get_end(ssh)) != 0)
2486+ fatal("sshpkt failed: %s", ssh_err(r));
2487+ kex->nbits = nbits;
2488+ kex->min = cmin;
2489+ kex->max = cmax;
2490+ min = MAX(DH_GRP_MIN, cmin);
2491+ max = MIN(DH_GRP_MAX, cmax);
2492+ nbits = MAXIMUM(DH_GRP_MIN, nbits);
2493+ nbits = MINIMUM(DH_GRP_MAX, nbits);
2494+ if (max < min || nbits < min || max < nbits)
2495+ fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
2496+ min, nbits, max);
2497+ kex->dh = PRIVSEP(choose_dh(min, nbits, max));
2498+ if (kex->dh == NULL) {
2499+ sshpkt_disconnect(ssh, "Protocol error: no matching group found");
2500+ fatal("Protocol error: no matching group found");
2501+ }
2502+
2503+ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
2504+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
2505+ (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
2506+ (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
2507+ (r = sshpkt_send(ssh)) != 0)
2508+ fatal("sshpkt failed: %s", ssh_err(r));
2509+
2510+ if ((r = ssh_packet_write_wait(ssh)) != 0)
2511+ fatal("ssh_packet_write_wait: %s", ssh_err(r));
2512+
2513+ /* Compute our exchange value in parallel with the client */
2514+ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
2515+ goto out;
2516+
2517+ do {
2518+ debug("Wait SSH2_MSG_GSSAPI_INIT");
2519+ type = ssh_packet_read(ssh);
2520+ switch(type) {
2521+ case SSH2_MSG_KEXGSS_INIT:
2522+ if (dh_client_pub != NULL)
2523+ fatal("Received KEXGSS_INIT after initialising");
2524+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2525+ &recv_tok)) != 0 ||
2526+ (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 ||
2527+ (r = sshpkt_get_end(ssh)) != 0)
2528+ fatal("sshpkt failed: %s", ssh_err(r));
2529+
2530+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
2531+ break;
2532+ case SSH2_MSG_KEXGSS_CONTINUE:
2533+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2534+ &recv_tok)) != 0 ||
2535+ (r = sshpkt_get_end(ssh)) != 0)
2536+ fatal("sshpkt failed: %s", ssh_err(r));
2537+ break;
2538+ default:
2539+ sshpkt_disconnect(ssh,
2540+ "Protocol error: didn't expect packet type %d",
2541+ type);
2542+ }
2543+
2544+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
2545+ &send_tok, &ret_flags));
2546+
2547+ gss_release_buffer(&min_status, &recv_tok);
2548+
2549+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
2550+ fatal("Zero length token output when incomplete");
2551+
2552+ if (dh_client_pub == NULL)
2553+ fatal("No client public key");
2554+
2555+ if (maj_status & GSS_S_CONTINUE_NEEDED) {
2556+ debug("Sending GSSAPI_CONTINUE");
2557+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2558+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
2559+ (r = sshpkt_send(ssh)) != 0)
2560+ fatal("sshpkt failed: %s", ssh_err(r));
2561+ gss_release_buffer(&min_status, &send_tok);
2562+ }
2563+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
2564+
2565+ if (GSS_ERROR(maj_status)) {
2566+ if (send_tok.length > 0) {
2567+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2568+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
2569+ (r = sshpkt_send(ssh)) != 0)
2570+ fatal("sshpkt failed: %s", ssh_err(r));
2571+ }
2572+ fatal("accept_ctx died");
2573+ }
2574+
2575+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
2576+ fatal("Mutual Authentication flag wasn't set");
2577+
2578+ if (!(ret_flags & GSS_C_INTEG_FLAG))
2579+ fatal("Integrity flag wasn't set");
2580+
2581+ /* calculate shared secret */
2582+ if ((shared_secret = sshbuf_new()) == NULL) {
2583+ r = SSH_ERR_ALLOC_FAIL;
2584+ goto out;
2585+ }
2586+ if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0)
2587+ goto out;
2588+
2589+ DH_get0_key(kex->dh, &pub_key, NULL);
2590+ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
2591+ hashlen = sizeof(hash);
2592+ if ((r = kexgex_hash(
2593+ kex->hash_alg,
2594+ kex->client_version,
2595+ kex->server_version,
2596+ kex->peer,
2597+ kex->my,
2598+ empty,
2599+ cmin, nbits, cmax,
2600+ dh_p, dh_g,
2601+ dh_client_pub,
2602+ pub_key,
2603+ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
2604+ hash, &hashlen)) != 0)
2605+ fatal("kexgex_hash failed: %s", ssh_err(r));
2606+
2607+ gssbuf.value = hash;
2608+ gssbuf.length = hashlen;
2609+
2610+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
2611+ fatal("Couldn't get MIC");
2612+
2613+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
2614+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
2615+ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
2616+ fatal("sshpkt failed: %s", ssh_err(r));
2617+
2618+ if (send_tok.length != 0) {
2619+ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
2620+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
2621+ fatal("sshpkt failed: %s", ssh_err(r));
2622+ } else {
2623+ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
2624+ fatal("sshpkt failed: %s", ssh_err(r));
2625+ }
2626+ if ((r = sshpkt_send(ssh)) != 0)
2627+ fatal("sshpkt failed: %s", ssh_err(r));
2628+
2629+ gss_release_buffer(&min_status, &send_tok);
2630+ gss_release_buffer(&min_status, &msg_tok);
2631+
2632+ if (gss_kex_context == NULL)
2633+ gss_kex_context = ctxt;
2634+ else
2635+ ssh_gssapi_delete_ctx(&ctxt);
2636+
2637+ /* Finally derive the keys and send them */
2638+ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
2639+ r = kex_send_newkeys(ssh);
2640+
2641+ /* If this was a rekey, then save out any delegated credentials we
2642+ * just exchanged. */
2643+ if (options.gss_store_rekey)
2644+ ssh_gssapi_rekey_creds();
2645+out:
2646+ sshbuf_free(empty);
2647+ explicit_bzero(hash, sizeof(hash));
2648+ DH_free(kex->dh);
2649+ kex->dh = NULL;
2650+ BN_clear_free(dh_client_pub);
2651+ sshbuf_free(shared_secret);
2652+ return r;
2653+}
2654+#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
2655diff --git a/monitor.c b/monitor.c
2656index 2ce89fe90..ebf76c7f9 100644
2657--- a/monitor.c
2658+++ b/monitor.c
2659@@ -148,6 +148,8 @@ int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *);
2660 int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *);
2661 int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *);
2662 int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *);
2663+int mm_answer_gss_sign(struct ssh *, int, struct sshbuf *);
2664+int mm_answer_gss_updatecreds(struct ssh *, int, struct sshbuf *);
2665 #endif
2666
2667 #ifdef SSH_AUDIT_EVENTS
2668@@ -220,11 +222,18 @@ struct mon_table mon_dispatch_proto20[] = {
2669 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
2670 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
2671 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
2672+ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
2673 #endif
2674 {0, 0, NULL}
2675 };
2676
2677 struct mon_table mon_dispatch_postauth20[] = {
2678+#ifdef GSSAPI
2679+ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
2680+ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
2681+ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
2682+ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
2683+#endif
2684 #ifdef WITH_OPENSSL
2685 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
2686 #endif
2687@@ -293,6 +302,10 @@ monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor)
2688 /* Permit requests for moduli and signatures */
2689 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
2690 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
2691+#ifdef GSSAPI
2692+ /* and for the GSSAPI key exchange */
2693+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
2694+#endif
2695
2696 /* The first few requests do not require asynchronous access */
2697 while (!authenticated) {
2698@@ -406,6 +419,10 @@ monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor)
2699 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
2700 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
2701 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
2702+#ifdef GSSAPI
2703+ /* and for the GSSAPI key exchange */
2704+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
2705+#endif
2706
2707 if (auth_opts->permit_pty_flag) {
2708 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
2709@@ -1713,6 +1730,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
2710 # ifdef OPENSSL_HAS_ECC
2711 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
2712 # endif
2713+# ifdef GSSAPI
2714+ if (options.gss_keyex) {
2715+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2716+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2717+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
2718+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
2719+ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
2720+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
2721+ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
2722+ }
2723+# endif
2724 #endif /* WITH_OPENSSL */
2725 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
2726 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
2727@@ -1806,8 +1834,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
2728 u_char *p;
2729 int r;
2730
2731- if (!options.gss_authentication)
2732- fatal("%s: GSSAPI authentication not enabled", __func__);
2733+ if (!options.gss_authentication && !options.gss_keyex)
2734+ fatal("%s: GSSAPI not enabled", __func__);
2735
2736 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
2737 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2738@@ -1839,8 +1867,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
2739 OM_uint32 flags = 0; /* GSI needs this */
2740 int r;
2741
2742- if (!options.gss_authentication)
2743- fatal("%s: GSSAPI authentication not enabled", __func__);
2744+ if (!options.gss_authentication && !options.gss_keyex)
2745+ fatal("%s: GSSAPI not enabled", __func__);
2746
2747 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
2748 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2749@@ -1860,6 +1888,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
2750 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2751 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2752 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2753+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2754 }
2755 return (0);
2756 }
2757@@ -1871,8 +1900,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
2758 OM_uint32 ret;
2759 int r;
2760
2761- if (!options.gss_authentication)
2762- fatal("%s: GSSAPI authentication not enabled", __func__);
2763+ if (!options.gss_authentication && !options.gss_keyex)
2764+ fatal("%s: GSSAPI not enabled", __func__);
2765
2766 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
2767 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
2768@@ -1898,13 +1927,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
2769 int
2770 mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
2771 {
2772- int r, authenticated;
2773+ int r, authenticated, kex;
2774 const char *displayname;
2775
2776- if (!options.gss_authentication)
2777- fatal("%s: GSSAPI authentication not enabled", __func__);
2778+ if (!options.gss_authentication && !options.gss_keyex)
2779+ fatal("%s: GSSAPI not enabled", __func__);
2780
2781- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
2782+ if ((r = sshbuf_get_u32(m, &kex)) != 0)
2783+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2784+
2785+ authenticated = authctxt->valid &&
2786+ ssh_gssapi_userok(authctxt->user, authctxt->pw, kex);
2787
2788 sshbuf_reset(m);
2789 if ((r = sshbuf_put_u32(m, authenticated)) != 0)
2790@@ -1913,7 +1946,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
2791 debug3("%s: sending result %d", __func__, authenticated);
2792 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
2793
2794- auth_method = "gssapi-with-mic";
2795+ if (kex) {
2796+ auth_method = "gssapi-keyex";
2797+ } else {
2798+ auth_method = "gssapi-with-mic";
2799+ }
2800
2801 if ((displayname = ssh_gssapi_displayname()) != NULL)
2802 auth2_record_info(authctxt, "%s", displayname);
2803@@ -1921,5 +1958,85 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
2804 /* Monitor loop will terminate if authenticated */
2805 return (authenticated);
2806 }
2807+
2808+int
2809+mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m)
2810+{
2811+ gss_buffer_desc data;
2812+ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2813+ OM_uint32 major, minor;
2814+ size_t len;
2815+ u_char *p = NULL;
2816+ int r;
2817+
2818+ if (!options.gss_authentication && !options.gss_keyex)
2819+ fatal("%s: GSSAPI not enabled", __func__);
2820+
2821+ if ((r = sshbuf_get_string(m, &p, &len)) != 0)
2822+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2823+ data.value = p;
2824+ data.length = len;
2825+ /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */
2826+ if (data.length != 20 && data.length != 32 && data.length != 64)
2827+ fatal("%s: data length incorrect: %d", __func__,
2828+ (int) data.length);
2829+
2830+ /* Save the session ID on the first time around */
2831+ if (session_id2_len == 0) {
2832+ session_id2_len = data.length;
2833+ session_id2 = xmalloc(session_id2_len);
2834+ memcpy(session_id2, data.value, session_id2_len);
2835+ }
2836+ major = ssh_gssapi_sign(gsscontext, &data, &hash);
2837+
2838+ free(data.value);
2839+
2840+ sshbuf_reset(m);
2841+
2842+ if ((r = sshbuf_put_u32(m, major)) != 0 ||
2843+ (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
2844+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2845+
2846+ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2847+
2848+ gss_release_buffer(&minor, &hash);
2849+
2850+ /* Turn on getpwnam permissions */
2851+ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2852+
2853+ /* And credential updating, for when rekeying */
2854+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2855+
2856+ return (0);
2857+}
2858+
2859+int
2860+mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) {
2861+ ssh_gssapi_ccache store;
2862+ int r, ok;
2863+
2864+ if (!options.gss_authentication && !options.gss_keyex)
2865+ fatal("%s: GSSAPI not enabled", __func__);
2866+
2867+ if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 ||
2868+ (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 ||
2869+ (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0)
2870+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2871+
2872+ ok = ssh_gssapi_update_creds(&store);
2873+
2874+ free(store.filename);
2875+ free(store.envvar);
2876+ free(store.envval);
2877+
2878+ sshbuf_reset(m);
2879+ if ((r = sshbuf_put_u32(m, ok)) != 0)
2880+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2881+
2882+ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2883+
2884+ return(0);
2885+}
2886+
2887 #endif /* GSSAPI */
2888
2889diff --git a/monitor.h b/monitor.h
2890index 683e5e071..2b1a2d590 100644
2891--- a/monitor.h
2892+++ b/monitor.h
2893@@ -63,6 +63,8 @@ enum monitor_reqtype {
2894 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
2895 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
2896
2897+ MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
2898+ MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
2899 };
2900
2901 struct ssh;
2902diff --git a/monitor_wrap.c b/monitor_wrap.c
2903index 001a8fa1c..6edb509a3 100644
2904--- a/monitor_wrap.c
2905+++ b/monitor_wrap.c
2906@@ -993,13 +993,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
2907 }
2908
2909 int
2910-mm_ssh_gssapi_userok(char *user)
2911+mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
2912 {
2913 struct sshbuf *m;
2914 int r, authenticated = 0;
2915
2916 if ((m = sshbuf_new()) == NULL)
2917 fatal("%s: sshbuf_new failed", __func__);
2918+ if ((r = sshbuf_put_u32(m, kex)) != 0)
2919+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2920
2921 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m);
2922 mm_request_receive_expect(pmonitor->m_recvfd,
2923@@ -1012,4 +1014,57 @@ mm_ssh_gssapi_userok(char *user)
2924 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
2925 return (authenticated);
2926 }
2927+
2928+OM_uint32
2929+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
2930+{
2931+ struct sshbuf *m;
2932+ OM_uint32 major;
2933+ int r;
2934+
2935+ if ((m = sshbuf_new()) == NULL)
2936+ fatal("%s: sshbuf_new failed", __func__);
2937+ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
2938+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2939+
2940+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m);
2941+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m);
2942+
2943+ if ((r = sshbuf_get_u32(m, &major)) != 0 ||
2944+ (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
2945+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2946+
2947+ sshbuf_free(m);
2948+
2949+ return (major);
2950+}
2951+
2952+int
2953+mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
2954+{
2955+ struct sshbuf *m;
2956+ int r, ok;
2957+
2958+ if ((m = sshbuf_new()) == NULL)
2959+ fatal("%s: sshbuf_new failed", __func__);
2960+
2961+ if ((r = sshbuf_put_cstring(m,
2962+ store->filename ? store->filename : "")) != 0 ||
2963+ (r = sshbuf_put_cstring(m,
2964+ store->envvar ? store->envvar : "")) != 0 ||
2965+ (r = sshbuf_put_cstring(m,
2966+ store->envval ? store->envval : "")) != 0)
2967+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2968+
2969+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m);
2970+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m);
2971+
2972+ if ((r = sshbuf_get_u32(m, &ok)) != 0)
2973+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2974+
2975+ sshbuf_free(m);
2976+
2977+ return (ok);
2978+}
2979+
2980 #endif /* GSSAPI */
2981diff --git a/monitor_wrap.h b/monitor_wrap.h
2982index 23ab096aa..485590c18 100644
2983--- a/monitor_wrap.h
2984+++ b/monitor_wrap.h
2985@@ -64,8 +64,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
2986 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
2987 OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
2988 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
2989-int mm_ssh_gssapi_userok(char *user);
2990+int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex);
2991 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
2992+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
2993+int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
2994 #endif
2995
2996 #ifdef USE_PAM
2997diff --git a/readconf.c b/readconf.c
2998index f3cac6b3a..da8022dd0 100644
2999--- a/readconf.c
3000+++ b/readconf.c
3001@@ -67,6 +67,7 @@
3002 #include "uidswap.h"
3003 #include "myproposal.h"
3004 #include "digest.h"
3005+#include "ssh-gss.h"
3006
3007 /* Format of the configuration file:
3008
3009@@ -160,6 +161,8 @@ typedef enum {
3010 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
3011 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
3012 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
3013+ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
3014+ oGssServerIdentity, oGssKexAlgorithms,
3015 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
3016 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
3017 oHashKnownHosts,
3018@@ -204,10 +207,22 @@ static struct {
3019 /* Sometimes-unsupported options */
3020 #if defined(GSSAPI)
3021 { "gssapiauthentication", oGssAuthentication },
3022+ { "gssapikeyexchange", oGssKeyEx },
3023 { "gssapidelegatecredentials", oGssDelegateCreds },
3024+ { "gssapitrustdns", oGssTrustDns },
3025+ { "gssapiclientidentity", oGssClientIdentity },
3026+ { "gssapiserveridentity", oGssServerIdentity },
3027+ { "gssapirenewalforcesrekey", oGssRenewalRekey },
3028+ { "gssapikexalgorithms", oGssKexAlgorithms },
3029 # else
3030 { "gssapiauthentication", oUnsupported },
3031+ { "gssapikeyexchange", oUnsupported },
3032 { "gssapidelegatecredentials", oUnsupported },
3033+ { "gssapitrustdns", oUnsupported },
3034+ { "gssapiclientidentity", oUnsupported },
3035+ { "gssapiserveridentity", oUnsupported },
3036+ { "gssapirenewalforcesrekey", oUnsupported },
3037+ { "gssapikexalgorithms", oUnsupported },
3038 #endif
3039 #ifdef ENABLE_PKCS11
3040 { "pkcs11provider", oPKCS11Provider },
3041@@ -1029,10 +1044,42 @@ parse_time:
3042 intptr = &options->gss_authentication;
3043 goto parse_flag;
3044
3045+ case oGssKeyEx:
3046+ intptr = &options->gss_keyex;
3047+ goto parse_flag;
3048+
3049 case oGssDelegateCreds:
3050 intptr = &options->gss_deleg_creds;
3051 goto parse_flag;
3052
3053+ case oGssTrustDns:
3054+ intptr = &options->gss_trust_dns;
3055+ goto parse_flag;
3056+
3057+ case oGssClientIdentity:
3058+ charptr = &options->gss_client_identity;
3059+ goto parse_string;
3060+
3061+ case oGssServerIdentity:
3062+ charptr = &options->gss_server_identity;
3063+ goto parse_string;
3064+
3065+ case oGssRenewalRekey:
3066+ intptr = &options->gss_renewal_rekey;
3067+ goto parse_flag;
3068+
3069+ case oGssKexAlgorithms:
3070+ arg = strdelim(&s);
3071+ if (!arg || *arg == '\0')
3072+ fatal("%.200s line %d: Missing argument.",
3073+ filename, linenum);
3074+ if (!kex_gss_names_valid(arg))
3075+ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
3076+ filename, linenum, arg ? arg : "<NONE>");
3077+ if (*activep && options->gss_kex_algorithms == NULL)
3078+ options->gss_kex_algorithms = xstrdup(arg);
3079+ break;
3080+
3081 case oBatchMode:
3082 intptr = &options->batch_mode;
3083 goto parse_flag;
3084@@ -1911,7 +1958,13 @@ initialize_options(Options * options)
3085 options->pubkey_authentication = -1;
3086 options->challenge_response_authentication = -1;
3087 options->gss_authentication = -1;
3088+ options->gss_keyex = -1;
3089 options->gss_deleg_creds = -1;
3090+ options->gss_trust_dns = -1;
3091+ options->gss_renewal_rekey = -1;
3092+ options->gss_client_identity = NULL;
3093+ options->gss_server_identity = NULL;
3094+ options->gss_kex_algorithms = NULL;
3095 options->password_authentication = -1;
3096 options->kbd_interactive_authentication = -1;
3097 options->kbd_interactive_devices = NULL;
3098@@ -2059,8 +2112,18 @@ fill_default_options(Options * options)
3099 options->challenge_response_authentication = 1;
3100 if (options->gss_authentication == -1)
3101 options->gss_authentication = 0;
3102+ if (options->gss_keyex == -1)
3103+ options->gss_keyex = 0;
3104 if (options->gss_deleg_creds == -1)
3105 options->gss_deleg_creds = 0;
3106+ if (options->gss_trust_dns == -1)
3107+ options->gss_trust_dns = 0;
3108+ if (options->gss_renewal_rekey == -1)
3109+ options->gss_renewal_rekey = 0;
3110+#ifdef GSSAPI
3111+ if (options->gss_kex_algorithms == NULL)
3112+ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
3113+#endif
3114 if (options->password_authentication == -1)
3115 options->password_authentication = 1;
3116 if (options->kbd_interactive_authentication == -1)
3117@@ -2702,7 +2765,14 @@ dump_client_config(Options *o, const char *host)
3118 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
3119 #ifdef GSSAPI
3120 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
3121+ dump_cfg_fmtint(oGssKeyEx, o->gss_keyex);
3122 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
3123+ dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns);
3124+ dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
3125+ dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
3126+ dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
3127+ dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ?
3128+ o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX);
3129 #endif /* GSSAPI */
3130 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
3131 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
3132diff --git a/readconf.h b/readconf.h
3133index feedb3d20..a8a8870d7 100644
3134--- a/readconf.h
3135+++ b/readconf.h
3136@@ -41,7 +41,13 @@ typedef struct {
3137 int challenge_response_authentication;
3138 /* Try S/Key or TIS, authentication. */
3139 int gss_authentication; /* Try GSS authentication */
3140+ int gss_keyex; /* Try GSS key exchange */
3141 int gss_deleg_creds; /* Delegate GSS credentials */
3142+ int gss_trust_dns; /* Trust DNS for GSS canonicalization */
3143+ int gss_renewal_rekey; /* Credential renewal forces rekey */
3144+ char *gss_client_identity; /* Principal to initiate GSSAPI with */
3145+ char *gss_server_identity; /* GSSAPI target principal */
3146+ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
3147 int password_authentication; /* Try password
3148 * authentication. */
3149 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
3150diff --git a/servconf.c b/servconf.c
3151index 70f5f73f0..191575a16 100644
3152--- a/servconf.c
3153+++ b/servconf.c
3154@@ -69,6 +69,7 @@
3155 #include "auth.h"
3156 #include "myproposal.h"
3157 #include "digest.h"
3158+#include "ssh-gss.h"
3159
3160 static void add_listen_addr(ServerOptions *, const char *,
3161 const char *, int);
3162@@ -133,8 +134,11 @@ initialize_server_options(ServerOptions *options)
3163 options->kerberos_ticket_cleanup = -1;
3164 options->kerberos_get_afs_token = -1;
3165 options->gss_authentication=-1;
3166+ options->gss_keyex = -1;
3167 options->gss_cleanup_creds = -1;
3168 options->gss_strict_acceptor = -1;
3169+ options->gss_store_rekey = -1;
3170+ options->gss_kex_algorithms = NULL;
3171 options->password_authentication = -1;
3172 options->kbd_interactive_authentication = -1;
3173 options->challenge_response_authentication = -1;
3174@@ -375,10 +379,18 @@ fill_default_server_options(ServerOptions *options)
3175 options->kerberos_get_afs_token = 0;
3176 if (options->gss_authentication == -1)
3177 options->gss_authentication = 0;
3178+ if (options->gss_keyex == -1)
3179+ options->gss_keyex = 0;
3180 if (options->gss_cleanup_creds == -1)
3181 options->gss_cleanup_creds = 1;
3182 if (options->gss_strict_acceptor == -1)
3183 options->gss_strict_acceptor = 1;
3184+ if (options->gss_store_rekey == -1)
3185+ options->gss_store_rekey = 0;
3186+#ifdef GSSAPI
3187+ if (options->gss_kex_algorithms == NULL)
3188+ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
3189+#endif
3190 if (options->password_authentication == -1)
3191 options->password_authentication = 1;
3192 if (options->kbd_interactive_authentication == -1)
3193@@ -531,6 +543,7 @@ typedef enum {
3194 sHostKeyAlgorithms,
3195 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
3196 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
3197+ sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey,
3198 sAcceptEnv, sSetEnv, sPermitTunnel,
3199 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
3200 sUsePrivilegeSeparation, sAllowAgentForwarding,
3201@@ -607,12 +620,22 @@ static struct {
3202 #ifdef GSSAPI
3203 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
3204 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
3205+ { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
3206 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
3207+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
3208+ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
3209+ { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL },
3210 #else
3211 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
3212 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
3213+ { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
3214 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
3215+ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
3216+ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
3217+ { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL },
3218 #endif
3219+ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
3220+ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
3221 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
3222 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
3223 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
3224@@ -1548,6 +1571,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
3225 intptr = &options->gss_authentication;
3226 goto parse_flag;
3227
3228+ case sGssKeyEx:
3229+ intptr = &options->gss_keyex;
3230+ goto parse_flag;
3231+
3232 case sGssCleanupCreds:
3233 intptr = &options->gss_cleanup_creds;
3234 goto parse_flag;
3235@@ -1556,6 +1583,22 @@ process_server_config_line_depth(ServerOptions *options, char *line,
3236 intptr = &options->gss_strict_acceptor;
3237 goto parse_flag;
3238
3239+ case sGssStoreRekey:
3240+ intptr = &options->gss_store_rekey;
3241+ goto parse_flag;
3242+
3243+ case sGssKexAlgorithms:
3244+ arg = strdelim(&cp);
3245+ if (!arg || *arg == '\0')
3246+ fatal("%.200s line %d: Missing argument.",
3247+ filename, linenum);
3248+ if (!kex_gss_names_valid(arg))
3249+ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
3250+ filename, linenum, arg ? arg : "<NONE>");
3251+ if (*activep && options->gss_kex_algorithms == NULL)
3252+ options->gss_kex_algorithms = xstrdup(arg);
3253+ break;
3254+
3255 case sPasswordAuthentication:
3256 intptr = &options->password_authentication;
3257 goto parse_flag;
3258@@ -2777,6 +2820,10 @@ dump_config(ServerOptions *o)
3259 #ifdef GSSAPI
3260 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
3261 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
3262+ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
3263+ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
3264+ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
3265+ dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
3266 #endif
3267 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
3268 dump_cfg_fmtint(sKbdInteractiveAuthentication,
3269diff --git a/servconf.h b/servconf.h
3270index 4202a2d02..3f47ea25e 100644
3271--- a/servconf.h
3272+++ b/servconf.h
3273@@ -132,8 +132,11 @@ typedef struct {
3274 int kerberos_get_afs_token; /* If true, try to get AFS token if
3275 * authenticated with Kerberos. */
3276 int gss_authentication; /* If true, permit GSSAPI authentication */
3277+ int gss_keyex; /* If true, permit GSSAPI key exchange */
3278 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
3279 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
3280+ int gss_store_rekey;
3281+ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
3282 int password_authentication; /* If true, permit password
3283 * authentication. */
3284 int kbd_interactive_authentication; /* If true, permit */
3285diff --git a/session.c b/session.c
3286index 8c0e54f79..06a33442a 100644
3287--- a/session.c
3288+++ b/session.c
3289@@ -2678,13 +2678,19 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
3290
3291 #ifdef KRB5
3292 if (options.kerberos_ticket_cleanup &&
3293- authctxt->krb5_ctx)
3294+ authctxt->krb5_ctx) {
3295+ temporarily_use_uid(authctxt->pw);
3296 krb5_cleanup_proc(authctxt);
3297+ restore_uid();
3298+ }
3299 #endif
3300
3301 #ifdef GSSAPI
3302- if (options.gss_cleanup_creds)
3303+ if (options.gss_cleanup_creds) {
3304+ temporarily_use_uid(authctxt->pw);
3305 ssh_gssapi_cleanup_creds();
3306+ restore_uid();
3307+ }
3308 #endif
3309
3310 /* remove agent socket */
3311diff --git a/ssh-gss.h b/ssh-gss.h
3312index 36180d07a..70dd36658 100644
3313--- a/ssh-gss.h
3314+++ b/ssh-gss.h
3315@@ -1,6 +1,6 @@
3316 /* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */
3317 /*
3318- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
3319+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3320 *
3321 * Redistribution and use in source and binary forms, with or without
3322 * modification, are permitted provided that the following conditions
3323@@ -61,10 +61,30 @@
3324
3325 #define SSH_GSS_OIDTYPE 0x06
3326
3327+#define SSH2_MSG_KEXGSS_INIT 30
3328+#define SSH2_MSG_KEXGSS_CONTINUE 31
3329+#define SSH2_MSG_KEXGSS_COMPLETE 32
3330+#define SSH2_MSG_KEXGSS_HOSTKEY 33
3331+#define SSH2_MSG_KEXGSS_ERROR 34
3332+#define SSH2_MSG_KEXGSS_GROUPREQ 40
3333+#define SSH2_MSG_KEXGSS_GROUP 41
3334+#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
3335+#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-"
3336+#define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-"
3337+#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-"
3338+#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
3339+#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-"
3340+#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-"
3341+
3342+#define GSS_KEX_DEFAULT_KEX \
3343+ KEX_GSS_GEX_SHA1_ID "," \
3344+ KEX_GSS_GRP14_SHA1_ID
3345+
3346 typedef struct {
3347 char *filename;
3348 char *envvar;
3349 char *envval;
3350+ struct passwd *owner;
3351 void *data;
3352 } ssh_gssapi_ccache;
3353
3354@@ -72,8 +92,11 @@ typedef struct {
3355 gss_buffer_desc displayname;
3356 gss_buffer_desc exportedname;
3357 gss_cred_id_t creds;
3358+ gss_name_t name;
3359 struct ssh_gssapi_mech_struct *mech;
3360 ssh_gssapi_ccache store;
3361+ int used;
3362+ int updated;
3363 } ssh_gssapi_client;
3364
3365 typedef struct ssh_gssapi_mech_struct {
3366@@ -84,6 +107,7 @@ typedef struct ssh_gssapi_mech_struct {
3367 int (*userok) (ssh_gssapi_client *, char *);
3368 int (*localname) (ssh_gssapi_client *, char **);
3369 void (*storecreds) (ssh_gssapi_client *);
3370+ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
3371 } ssh_gssapi_mech;
3372
3373 typedef struct {
3374@@ -94,10 +118,11 @@ typedef struct {
3375 gss_OID oid; /* client */
3376 gss_cred_id_t creds; /* server */
3377 gss_name_t client; /* server */
3378- gss_cred_id_t client_creds; /* server */
3379+ gss_cred_id_t client_creds; /* both */
3380 } Gssctxt;
3381
3382 extern ssh_gssapi_mech *supported_mechs[];
3383+extern Gssctxt *gss_kex_context;
3384
3385 int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
3386 void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
3387@@ -109,6 +134,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *);
3388
3389 struct sshbuf;
3390 int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *);
3391+int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *);
3392
3393 OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *);
3394 OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int,
3395@@ -123,17 +149,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **);
3396 OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
3397 void ssh_gssapi_buildmic(struct sshbuf *, const char *,
3398 const char *, const char *);
3399-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
3400+int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
3401+OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
3402+int ssh_gssapi_credentials_updated(Gssctxt *);
3403
3404 /* In the server */
3405+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
3406+ const char *);
3407+char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *);
3408+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
3409+ const char *, const char *);
3410+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
3411+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
3412+ const char *);
3413 OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
3414-int ssh_gssapi_userok(char *name);
3415+int ssh_gssapi_userok(char *name, struct passwd *, int kex);
3416 OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
3417 void ssh_gssapi_do_child(char ***, u_int *);
3418 void ssh_gssapi_cleanup_creds(void);
3419 void ssh_gssapi_storecreds(void);
3420 const char *ssh_gssapi_displayname(void);
3421
3422+char *ssh_gssapi_server_mechanisms(void);
3423+int ssh_gssapi_oid_table_ok(void);
3424+
3425+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
3426+void ssh_gssapi_rekey_creds(void);
3427+
3428 #endif /* GSSAPI */
3429
3430 #endif /* _SSH_GSS_H */
3431diff --git a/ssh.1 b/ssh.1
3432index 60de6087a..db5c65bc7 100644
3433--- a/ssh.1
3434+++ b/ssh.1
3435@@ -503,7 +503,13 @@ For full details of the options listed below, and their possible values, see
3436 .It GatewayPorts
3437 .It GlobalKnownHostsFile
3438 .It GSSAPIAuthentication
3439+.It GSSAPIKeyExchange
3440+.It GSSAPIClientIdentity
3441 .It GSSAPIDelegateCredentials
3442+.It GSSAPIKexAlgorithms
3443+.It GSSAPIRenewalForcesRekey
3444+.It GSSAPIServerIdentity
3445+.It GSSAPITrustDns
3446 .It HashKnownHosts
3447 .It Host
3448 .It HostbasedAuthentication
3449@@ -579,6 +585,8 @@ flag),
3450 (supported message integrity codes),
3451 .Ar kex
3452 (key exchange algorithms),
3453+.Ar kex-gss
3454+(GSSAPI key exchange algorithms),
3455 .Ar key
3456 (key types),
3457 .Ar key-cert
3458diff --git a/ssh.c b/ssh.c
3459index 15aee569e..110cf9c19 100644
3460--- a/ssh.c
3461+++ b/ssh.c
3462@@ -747,6 +747,8 @@ main(int ac, char **av)
3463 else if (strcmp(optarg, "kex") == 0 ||
3464 strcasecmp(optarg, "KexAlgorithms") == 0)
3465 cp = kex_alg_list('\n');
3466+ else if (strcmp(optarg, "kex-gss") == 0)
3467+ cp = kex_gss_alg_list('\n');
3468 else if (strcmp(optarg, "key") == 0)
3469 cp = sshkey_alg_list(0, 0, 0, '\n');
3470 else if (strcmp(optarg, "key-cert") == 0)
3471@@ -772,8 +774,8 @@ main(int ac, char **av)
3472 } else if (strcmp(optarg, "help") == 0) {
3473 cp = xstrdup(
3474 "cipher\ncipher-auth\ncompression\nkex\n"
3475- "key\nkey-cert\nkey-plain\nkey-sig\nmac\n"
3476- "protocol-version\nsig");
3477+ "kex-gss\nkey\nkey-cert\nkey-plain\n"
3478+ "key-sig\nmac\nprotocol-version\nsig");
3479 }
3480 if (cp == NULL)
3481 fatal("Unsupported query \"%s\"", optarg);
3482diff --git a/ssh_config b/ssh_config
3483index 5e8ef548b..1ff999b68 100644
3484--- a/ssh_config
3485+++ b/ssh_config
3486@@ -24,6 +24,8 @@
3487 # HostbasedAuthentication no
3488 # GSSAPIAuthentication no
3489 # GSSAPIDelegateCredentials no
3490+# GSSAPIKeyExchange no
3491+# GSSAPITrustDNS no
3492 # BatchMode no
3493 # CheckHostIP yes
3494 # AddressFamily any
3495diff --git a/ssh_config.5 b/ssh_config.5
3496index 06a32d314..3f4906972 100644
3497--- a/ssh_config.5
3498+++ b/ssh_config.5
3499@@ -766,10 +766,67 @@ The default is
3500 Specifies whether user authentication based on GSSAPI is allowed.
3501 The default is
3502 .Cm no .
3503+.It Cm GSSAPIClientIdentity
3504+If set, specifies the GSSAPI client identity that ssh should use when
3505+connecting to the server. The default is unset, which means that the default
3506+identity will be used.
3507 .It Cm GSSAPIDelegateCredentials
3508 Forward (delegate) credentials to the server.
3509 The default is
3510 .Cm no .
3511+.It Cm GSSAPIKeyExchange
3512+Specifies whether key exchange based on GSSAPI may be used. When using
3513+GSSAPI key exchange the server need not have a host key.
3514+The default is
3515+.Dq no .
3516+.It Cm GSSAPIRenewalForcesRekey
3517+If set to
3518+.Dq yes
3519+then renewal of the client's GSSAPI credentials will force the rekeying of the
3520+ssh connection. With a compatible server, this will delegate the renewed
3521+credentials to a session on the server.
3522+.Pp
3523+Checks are made to ensure that credentials are only propagated when the new
3524+credentials match the old ones on the originating client and where the
3525+receiving server still has the old set in its cache.
3526+.Pp
3527+The default is
3528+.Dq no .
3529+.Pp
3530+For this to work
3531+.Cm GSSAPIKeyExchange
3532+needs to be enabled in the server and also used by the client.
3533+.It Cm GSSAPIServerIdentity
3534+If set, specifies the GSSAPI server identity that ssh should expect when
3535+connecting to the server. The default is unset, which means that the
3536+expected GSSAPI server identity will be determined from the target
3537+hostname.
3538+.It Cm GSSAPITrustDns
3539+Set to
3540+.Dq yes
3541+to indicate that the DNS is trusted to securely canonicalize
3542+the name of the host being connected to. If
3543+.Dq no ,
3544+the hostname entered on the
3545+command line will be passed untouched to the GSSAPI library.
3546+The default is
3547+.Dq no .
3548+.It Cm GSSAPIKexAlgorithms
3549+The list of key exchange algorithms that are offered for GSSAPI
3550+key exchange. Possible values are
3551+.Bd -literal -offset 3n
3552+gss-gex-sha1-,
3553+gss-group1-sha1-,
3554+gss-group14-sha1-,
3555+gss-group14-sha256-,
3556+gss-group16-sha512-,
3557+gss-nistp256-sha256-,
3558+gss-curve25519-sha256-
3559+.Ed
3560+.Pp
3561+The default is
3562+.Dq gss-gex-sha1-,gss-group14-sha1- .
3563+This option only applies to protocol version 2 connections using GSSAPI.
3564 .It Cm HashKnownHosts
3565 Indicates that
3566 .Xr ssh 1
3567diff --git a/sshconnect2.c b/sshconnect2.c
3568index af00fb30c..03bc87eb4 100644
3569--- a/sshconnect2.c
3570+++ b/sshconnect2.c
3571@@ -80,8 +80,6 @@
3572 #endif
3573
3574 /* import */
3575-extern char *client_version_string;
3576-extern char *server_version_string;
3577 extern Options options;
3578
3579 /*
3580@@ -163,6 +161,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
3581 char *s, *all_key;
3582 int r, use_known_hosts_order = 0;
3583
3584+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3585+ char *orig = NULL, *gss = NULL;
3586+ char *gss_host = NULL;
3587+#endif
3588+
3589 xxx_host = host;
3590 xxx_hostaddr = hostaddr;
3591
3592@@ -206,6 +209,35 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
3593 compat_pkalg_proposal(options.hostkeyalgorithms);
3594 }
3595
3596+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3597+ if (options.gss_keyex) {
3598+ /* Add the GSSAPI mechanisms currently supported on this
3599+ * client to the key exchange algorithm proposal */
3600+ orig = myproposal[PROPOSAL_KEX_ALGS];
3601+
3602+ if (options.gss_server_identity)
3603+ gss_host = xstrdup(options.gss_server_identity);
3604+ else if (options.gss_trust_dns)
3605+ gss_host = remote_hostname(ssh);
3606+ else
3607+ gss_host = xstrdup(host);
3608+
3609+ gss = ssh_gssapi_client_mechanisms(gss_host,
3610+ options.gss_client_identity, options.gss_kex_algorithms);
3611+ if (gss) {
3612+ debug("Offering GSSAPI proposal: %s", gss);
3613+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
3614+ "%s,%s", gss, orig);
3615+
3616+ /* If we've got GSSAPI algorithms, then we also support the
3617+ * 'null' hostkey, as a last resort */
3618+ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
3619+ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
3620+ "%s,null", orig);
3621+ }
3622+ }
3623+#endif
3624+
3625 if (options.rekey_limit || options.rekey_interval)
3626 ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
3627 options.rekey_interval);
3628@@ -224,16 +256,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
3629 # ifdef OPENSSL_HAS_ECC
3630 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
3631 # endif
3632-#endif
3633+# ifdef GSSAPI
3634+ if (options.gss_keyex) {
3635+ ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
3636+ ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
3637+ ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
3638+ ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
3639+ ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
3640+ ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
3641+ ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
3642+ }
3643+# endif
3644+#endif /* WITH_OPENSSL */
3645 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
3646 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client;
3647 ssh->kex->verify_host_key=&verify_host_key_callback;
3648
3649+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3650+ if (options.gss_keyex) {
3651+ ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
3652+ ssh->kex->gss_trust_dns = options.gss_trust_dns;
3653+ ssh->kex->gss_client = options.gss_client_identity;
3654+ ssh->kex->gss_host = gss_host;
3655+ }
3656+#endif
3657+
3658 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
3659
3660 /* remove ext-info from the KEX proposals for rekeying */
3661 myproposal[PROPOSAL_KEX_ALGS] =
3662 compat_kex_proposal(options.kex_algorithms);
3663+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3664+ /* repair myproposal after it was crumpled by the */
3665+ /* ext-info removal above */
3666+ if (gss) {
3667+ orig = myproposal[PROPOSAL_KEX_ALGS];
3668+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
3669+ "%s,%s", gss, orig);
3670+ free(gss);
3671+ }
3672+#endif
3673 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
3674 fatal("kex_prop2buf: %s", ssh_err(r));
3675
3676@@ -330,6 +392,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *);
3677 static int input_gssapi_token(int type, u_int32_t, struct ssh *);
3678 static int input_gssapi_error(int, u_int32_t, struct ssh *);
3679 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
3680+static int userauth_gsskeyex(struct ssh *);
3681 #endif
3682
3683 void userauth(struct ssh *, char *);
3684@@ -346,6 +409,11 @@ static char *authmethods_get(void);
3685
3686 Authmethod authmethods[] = {
3687 #ifdef GSSAPI
3688+ {"gssapi-keyex",
3689+ userauth_gsskeyex,
3690+ NULL,
3691+ &options.gss_keyex,
3692+ NULL},
3693 {"gssapi-with-mic",
3694 userauth_gssapi,
3695 userauth_gssapi_cleanup,
3696@@ -716,12 +784,25 @@ userauth_gssapi(struct ssh *ssh)
3697 OM_uint32 min;
3698 int r, ok = 0;
3699 gss_OID mech = NULL;
3700+ char *gss_host;
3701+
3702+ if (options.gss_server_identity)
3703+ gss_host = xstrdup(options.gss_server_identity);
3704+ else if (options.gss_trust_dns)
3705+ gss_host = remote_hostname(ssh);
3706+ else
3707+ gss_host = xstrdup(authctxt->host);
3708
3709 /* Try one GSSAPI method at a time, rather than sending them all at
3710 * once. */
3711
3712 if (authctxt->gss_supported_mechs == NULL)
3713- gss_indicate_mechs(&min, &authctxt->gss_supported_mechs);
3714+ if (GSS_ERROR(gss_indicate_mechs(&min,
3715+ &authctxt->gss_supported_mechs))) {
3716+ authctxt->gss_supported_mechs = NULL;
3717+ free(gss_host);
3718+ return 0;
3719+ }
3720
3721 /* Check to see whether the mechanism is usable before we offer it */
3722 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
3723@@ -730,13 +811,15 @@ userauth_gssapi(struct ssh *ssh)
3724 elements[authctxt->mech_tried];
3725 /* My DER encoding requires length<128 */
3726 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
3727- mech, authctxt->host)) {
3728+ mech, gss_host, options.gss_client_identity)) {
3729 ok = 1; /* Mechanism works */
3730 } else {
3731 authctxt->mech_tried++;
3732 }
3733 }
3734
3735+ free(gss_host);
3736+
3737 if (!ok || mech == NULL)
3738 return 0;
3739
3740@@ -976,6 +1059,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
3741 free(lang);
3742 return r;
3743 }
3744+
3745+int
3746+userauth_gsskeyex(struct ssh *ssh)
3747+{
3748+ struct sshbuf *b = NULL;
3749+ Authctxt *authctxt = ssh->authctxt;
3750+ gss_buffer_desc gssbuf;
3751+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
3752+ OM_uint32 ms;
3753+ int r;
3754+
3755+ static int attempt = 0;
3756+ if (attempt++ >= 1)
3757+ return (0);
3758+
3759+ if (gss_kex_context == NULL) {
3760+ debug("No valid Key exchange context");
3761+ return (0);
3762+ }
3763+
3764+ if ((b = sshbuf_new()) == NULL)
3765+ fatal("%s: sshbuf_new failed", __func__);
3766+
3767+ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
3768+ "gssapi-keyex");
3769+
3770+ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
3771+ fatal("%s: sshbuf_mutable_ptr failed", __func__);
3772+ gssbuf.length = sshbuf_len(b);
3773+
3774+ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
3775+ sshbuf_free(b);
3776+ return (0);
3777+ }
3778+
3779+ if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
3780+ (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
3781+ (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
3782+ (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
3783+ (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
3784+ (r = sshpkt_send(ssh)) != 0)
3785+ fatal("%s: %s", __func__, ssh_err(r));
3786+
3787+ sshbuf_free(b);
3788+ gss_release_buffer(&ms, &mic);
3789+
3790+ return (1);
3791+}
3792+
3793 #endif /* GSSAPI */
3794
3795 static int
3796diff --git a/sshd.c b/sshd.c
3797index 60b2aaf73..d92f03aaf 100644
3798--- a/sshd.c
3799+++ b/sshd.c
3800@@ -817,8 +817,8 @@ notify_hostkeys(struct ssh *ssh)
3801 }
3802 debug3("%s: sent %u hostkeys", __func__, nkeys);
3803 if (nkeys == 0)
3804- fatal("%s: no hostkeys", __func__);
3805- if ((r = sshpkt_send(ssh)) != 0)
3806+ debug3("%s: no hostkeys", __func__);
3807+ else if ((r = sshpkt_send(ssh)) != 0)
3808 sshpkt_fatal(ssh, r, "%s: send", __func__);
3809 sshbuf_free(buf);
3810 }
3811@@ -1852,7 +1852,8 @@ main(int ac, char **av)
3812 free(fp);
3813 }
3814 accumulate_host_timing_secret(cfg, NULL);
3815- if (!sensitive_data.have_ssh2_key) {
3816+ /* The GSSAPI key exchange can run without a host key */
3817+ if (!sensitive_data.have_ssh2_key && !options.gss_keyex) {
3818 logit("sshd: no hostkeys available -- exiting.");
3819 exit(1);
3820 }
3821@@ -2347,6 +2348,48 @@ do_ssh2_kex(struct ssh *ssh)
3822 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
3823 list_hostkey_types());
3824
3825+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3826+ {
3827+ char *orig;
3828+ char *gss = NULL;
3829+ char *newstr = NULL;
3830+ orig = myproposal[PROPOSAL_KEX_ALGS];
3831+
3832+ /*
3833+ * If we don't have a host key, then there's no point advertising
3834+ * the other key exchange algorithms
3835+ */
3836+
3837+ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
3838+ orig = NULL;
3839+
3840+ if (options.gss_keyex)
3841+ gss = ssh_gssapi_server_mechanisms();
3842+ else
3843+ gss = NULL;
3844+
3845+ if (gss && orig)
3846+ xasprintf(&newstr, "%s,%s", gss, orig);
3847+ else if (gss)
3848+ newstr = gss;
3849+ else if (orig)
3850+ newstr = orig;
3851+
3852+ /*
3853+ * If we've got GSSAPI mechanisms, then we've got the 'null' host
3854+ * key alg, but we can't tell people about it unless its the only
3855+ * host key algorithm we support
3856+ */
3857+ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
3858+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
3859+
3860+ if (newstr)
3861+ myproposal[PROPOSAL_KEX_ALGS] = newstr;
3862+ else
3863+ fatal("No supported key exchange algorithms");
3864+ }
3865+#endif
3866+
3867 /* start key exchange */
3868 if ((r = kex_setup(ssh, myproposal)) != 0)
3869 fatal("kex_setup: %s", ssh_err(r));
3870@@ -2362,7 +2405,18 @@ do_ssh2_kex(struct ssh *ssh)
3871 # ifdef OPENSSL_HAS_ECC
3872 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
3873 # endif
3874-#endif
3875+# ifdef GSSAPI
3876+ if (options.gss_keyex) {
3877+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
3878+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
3879+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
3880+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
3881+ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
3882+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
3883+ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
3884+ }
3885+# endif
3886+#endif /* WITH_OPENSSL */
3887 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
3888 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
3889 kex->load_host_public_key=&get_hostkey_public_by_type;
3890diff --git a/sshd_config b/sshd_config
3891index 19b7c91a1..2c48105f8 100644
3892--- a/sshd_config
3893+++ b/sshd_config
3894@@ -69,6 +69,8 @@ AuthorizedKeysFile .ssh/authorized_keys
3895 # GSSAPI options
3896 #GSSAPIAuthentication no
3897 #GSSAPICleanupCredentials yes
3898+#GSSAPIStrictAcceptorCheck yes
3899+#GSSAPIKeyExchange no
3900
3901 # Set this to 'yes' to enable PAM authentication, account processing,
3902 # and session processing. If this is enabled, PAM authentication will
3903diff --git a/sshd_config.5 b/sshd_config.5
3904index 70ccea449..f6b41a2f8 100644
3905--- a/sshd_config.5
3906+++ b/sshd_config.5
3907@@ -646,6 +646,11 @@ Specifies whether to automatically destroy the user's credentials cache
3908 on logout.
3909 The default is
3910 .Cm yes .
3911+.It Cm GSSAPIKeyExchange
3912+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
3913+doesn't rely on ssh keys to verify host identity.
3914+The default is
3915+.Cm no .
3916 .It Cm GSSAPIStrictAcceptorCheck
3917 Determines whether to be strict about the identity of the GSSAPI acceptor
3918 a client authenticates against.
3919@@ -660,6 +665,31 @@ machine's default store.
3920 This facility is provided to assist with operation on multi homed machines.
3921 The default is
3922 .Cm yes .
3923+.It Cm GSSAPIStoreCredentialsOnRekey
3924+Controls whether the user's GSSAPI credentials should be updated following a
3925+successful connection rekeying. This option can be used to accepted renewed
3926+or updated credentials from a compatible client. The default is
3927+.Dq no .
3928+.Pp
3929+For this to work
3930+.Cm GSSAPIKeyExchange
3931+needs to be enabled in the server and also used by the client.
3932+.It Cm GSSAPIKexAlgorithms
3933+The list of key exchange algorithms that are accepted by GSSAPI
3934+key exchange. Possible values are
3935+.Bd -literal -offset 3n
3936+gss-gex-sha1-,
3937+gss-group1-sha1-,
3938+gss-group14-sha1-,
3939+gss-group14-sha256-,
3940+gss-group16-sha512-,
3941+gss-nistp256-sha256-,
3942+gss-curve25519-sha256-
3943+.Ed
3944+.Pp
3945+The default is
3946+.Dq gss-gex-sha1-,gss-group14-sha1- .
3947+This option only applies to protocol version 2 connections using GSSAPI.
3948 .It Cm HostbasedAcceptedKeyTypes
3949 Specifies the key types that will be accepted for hostbased authentication
3950 as a list of comma-separated patterns.
3951diff --git a/sshkey.c b/sshkey.c
3952index 57995ee68..fd5b77246 100644
3953--- a/sshkey.c
3954+++ b/sshkey.c
3955@@ -154,6 +154,7 @@ static const struct keytype keytypes[] = {
3956 KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 },
3957 # endif /* OPENSSL_HAS_ECC */
3958 #endif /* WITH_OPENSSL */
3959+ { "null", "null", NULL, KEY_NULL, 0, 0, 0 },
3960 { NULL, NULL, NULL, -1, -1, 0, 0 }
3961 };
3962
3963@@ -255,7 +256,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
3964 const struct keytype *kt;
3965
3966 for (kt = keytypes; kt->type != -1; kt++) {
3967- if (kt->name == NULL)
3968+ if (kt->name == NULL || kt->type == KEY_NULL)
3969 continue;
3970 if (!include_sigonly && kt->sigonly)
3971 continue;
3972diff --git a/sshkey.h b/sshkey.h
3973index 71a3fddcb..37a43a67a 100644
3974--- a/sshkey.h
3975+++ b/sshkey.h
3976@@ -69,6 +69,7 @@ enum sshkey_types {
3977 KEY_ECDSA_SK_CERT,
3978 KEY_ED25519_SK,
3979 KEY_ED25519_SK_CERT,
3980+ KEY_NULL,
3981 KEY_UNSPEC
3982 };
3983