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.patch3353
1 files changed, 3353 insertions, 0 deletions
diff --git a/debian/patches/gssapi.patch b/debian/patches/gssapi.patch
new file mode 100644
index 000000000..f62bf6672
--- /dev/null
+++ b/debian/patches/gssapi.patch
@@ -0,0 +1,3353 @@
1From 72b1d308e6400194ef6e4e7dd45bfa48fa39b5e6 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
19Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242
20Last-Updated: 2018-10-20
21
22Patch-Name: gssapi.patch
23---
24 ChangeLog.gssapi | 113 ++++++++++++++++
25 Makefile.in | 3 +-
26 auth-krb5.c | 17 ++-
27 auth.c | 96 +------------
28 auth2-gss.c | 54 +++++++-
29 auth2.c | 2 +
30 canohost.c | 93 +++++++++++++
31 canohost.h | 3 +
32 clientloop.c | 15 ++-
33 config.h.in | 6 +
34 configure.ac | 24 ++++
35 gss-genr.c | 280 +++++++++++++++++++++++++++++++++++++-
36 gss-serv-krb5.c | 85 +++++++++++-
37 gss-serv.c | 184 +++++++++++++++++++++++--
38 kex.c | 19 +++
39 kex.h | 14 ++
40 kexgssc.c | 341 +++++++++++++++++++++++++++++++++++++++++++++++
41 kexgsss.c | 300 +++++++++++++++++++++++++++++++++++++++++
42 monitor.c | 122 +++++++++++++++--
43 monitor.h | 3 +
44 monitor_wrap.c | 53 +++++++-
45 monitor_wrap.h | 4 +-
46 opacket.c | 2 +-
47 opacket.h | 2 +-
48 readconf.c | 43 ++++++
49 readconf.h | 5 +
50 servconf.c | 26 ++++
51 servconf.h | 2 +
52 ssh-gss.h | 41 +++++-
53 ssh_config | 2 +
54 ssh_config.5 | 32 +++++
55 sshconnect2.c | 133 +++++++++++++++++-
56 sshd.c | 110 +++++++++++++++
57 sshd_config | 2 +
58 sshd_config.5 | 10 ++
59 sshkey.c | 3 +-
60 sshkey.h | 1 +
61 37 files changed, 2099 insertions(+), 146 deletions(-)
62 create mode 100644 ChangeLog.gssapi
63 create mode 100644 kexgssc.c
64 create mode 100644 kexgsss.c
65
66diff --git a/ChangeLog.gssapi b/ChangeLog.gssapi
67new file mode 100644
68index 000000000..f117a336a
69--- /dev/null
70+++ b/ChangeLog.gssapi
71@@ -0,0 +1,113 @@
72+20110101
73+ - Finally update for OpenSSH 5.6p1
74+ - Add GSSAPIServerIdentity option from Jim Basney
75+
76+20100308
77+ - [ Makefile.in, key.c, key.h ]
78+ Updates for OpenSSH 5.4p1
79+ - [ servconf.c ]
80+ Include GSSAPI options in the sshd -T configuration dump, and flag
81+ some older configuration options as being unsupported. Thanks to Colin
82+ Watson.
83+ -
84+
85+20100124
86+ - [ sshconnect2.c ]
87+ Adapt to deal with additional element in Authmethod structure. Thanks to
88+ Colin Watson
89+
90+20090615
91+ - [ gss-genr.c gss-serv.c kexgssc.c kexgsss.c monitor.c sshconnect2.c
92+ sshd.c ]
93+ Fix issues identified by Greg Hudson following a code review
94+ Check return value of gss_indicate_mechs
95+ Protect GSSAPI calls in monitor, so they can only be used if enabled
96+ Check return values of bignum functions in key exchange
97+ Use BN_clear_free to clear other side's DH value
98+ Make ssh_gssapi_id_kex more robust
99+ Only configure kex table pointers if GSSAPI is enabled
100+ Don't leak mechanism list, or gss mechanism list
101+ Cast data.length before printing
102+ If serverkey isn't provided, use an empty string, rather than NULL
103+
104+20090201
105+ - [ gss-genr.c gss-serv.c kex.h kexgssc.c readconf.c readconf.h ssh-gss.h
106+ ssh_config.5 sshconnet2.c ]
107+ Add support for the GSSAPIClientIdentity option, which allows the user
108+ to specify which GSSAPI identity to use to contact a given server
109+
110+20080404
111+ - [ gss-serv.c ]
112+ Add code to actually implement GSSAPIStrictAcceptCheck, which had somehow
113+ been omitted from a previous version of this patch. Reported by Borislav
114+ Stoichkov
115+
116+20070317
117+ - [ gss-serv-krb5.c ]
118+ Remove C99ism, where new_ccname was being declared in the middle of a
119+ function
120+
121+20061220
122+ - [ servconf.c ]
123+ Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and
124+ documented, behaviour. Reported by Dan Watson.
125+
126+20060910
127+ - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c
128+ ssh-gss.h ]
129+ add support for gss-group14-sha1 key exchange mechanisms
130+ - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ]
131+ Add GSSAPIStrictAcceptorCheck option to allow the disabling of
132+ acceptor principal checking on multi-homed machines.
133+ <Bugzilla #928>
134+ - [ sshd_config ssh_config ]
135+ Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample
136+ configuration files
137+ - [ kexgss.c kegsss.c sshconnect2.c sshd.c ]
138+ Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf()
139+ Limit length of error messages displayed by client
140+
141+20060909
142+ - [ gss-genr.c gss-serv.c ]
143+ move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server
144+ only, where they belong
145+ <Bugzilla #1225>
146+
147+20060829
148+ - [ gss-serv-krb5.c ]
149+ Fix CCAPI credentials cache name when creating KRB5CCNAME environment
150+ variable
151+
152+20060828
153+ - [ gss-genr.c ]
154+ Avoid Heimdal context freeing problem
155+ <Fixed upstream 20060829>
156+
157+20060818
158+ - [ gss-genr.c ssh-gss.h sshconnect2.c ]
159+ Make sure that SPENGO is disabled
160+ <Bugzilla #1218 - Fixed upstream 20060818>
161+
162+20060421
163+ - [ gssgenr.c, sshconnect2.c ]
164+ a few type changes (signed versus unsigned, int versus size_t) to
165+ fix compiler errors/warnings
166+ (from jbasney AT ncsa.uiuc.edu)
167+ - [ kexgssc.c, sshconnect2.c ]
168+ fix uninitialized variable warnings
169+ (from jbasney AT ncsa.uiuc.edu)
170+ - [ gssgenr.c ]
171+ pass oid to gss_display_status (helpful when using GSSAPI mechglue)
172+ (from jbasney AT ncsa.uiuc.edu)
173+ <Bugzilla #1220 >
174+ - [ gss-serv-krb5.c ]
175+ #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H
176+ (from jbasney AT ncsa.uiuc.edu)
177+ <Fixed upstream 20060304>
178+ - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c
179+ add client-side GssapiKeyExchange option
180+ (from jbasney AT ncsa.uiuc.edu)
181+ - [ sshconnect2.c ]
182+ add support for GssapiTrustDns option for gssapi-with-mic
183+ (from jbasney AT ncsa.uiuc.edu)
184+ <gssapi-with-mic support is Bugzilla #1008>
185diff --git a/Makefile.in b/Makefile.in
186index 126b2c742..70050ffb6 100644
187--- a/Makefile.in
188+++ b/Makefile.in
189@@ -100,6 +100,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
190 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
191 kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \
192 kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \
193+ kexgssc.o \
194 platform-pledge.o platform-tracing.o platform-misc.o
195
196 SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
197@@ -113,7 +114,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
198 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
199 auth2-none.o auth2-passwd.o auth2-pubkey.o \
200 monitor.o monitor_wrap.o auth-krb5.o \
201- auth2-gss.o gss-serv.o gss-serv-krb5.o \
202+ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
203 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
204 sftp-server.o sftp-common.o \
205 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
206diff --git a/auth-krb5.c b/auth-krb5.c
207index 3096f1c8e..204752e1b 100644
208--- a/auth-krb5.c
209+++ b/auth-krb5.c
210@@ -182,8 +182,13 @@ auth_krb5_password(Authctxt *authctxt, const char *password)
211
212 len = strlen(authctxt->krb5_ticket_file) + 6;
213 authctxt->krb5_ccname = xmalloc(len);
214+#ifdef USE_CCAPI
215+ snprintf(authctxt->krb5_ccname, len, "API:%s",
216+ authctxt->krb5_ticket_file);
217+#else
218 snprintf(authctxt->krb5_ccname, len, "FILE:%s",
219 authctxt->krb5_ticket_file);
220+#endif
221
222 #ifdef USE_PAM
223 if (options.use_pam)
224@@ -240,15 +245,22 @@ krb5_cleanup_proc(Authctxt *authctxt)
225 #ifndef HEIMDAL
226 krb5_error_code
227 ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
228- int tmpfd, ret, oerrno;
229+ int ret, oerrno;
230 char ccname[40];
231 mode_t old_umask;
232+#ifdef USE_CCAPI
233+ char cctemplate[] = "API:krb5cc_%d";
234+#else
235+ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX";
236+ int tmpfd;
237+#endif
238
239 ret = snprintf(ccname, sizeof(ccname),
240- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid());
241+ cctemplate, geteuid());
242 if (ret < 0 || (size_t)ret >= sizeof(ccname))
243 return ENOMEM;
244
245+#ifndef USE_CCAPI
246 old_umask = umask(0177);
247 tmpfd = mkstemp(ccname + strlen("FILE:"));
248 oerrno = errno;
249@@ -265,6 +277,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
250 return oerrno;
251 }
252 close(tmpfd);
253+#endif
254
255 return (krb5_cc_resolve(ctx, ccname, ccache));
256 }
257diff --git a/auth.c b/auth.c
258index 3ca3762cc..d8e6b4a3d 100644
259--- a/auth.c
260+++ b/auth.c
261@@ -399,7 +399,8 @@ auth_root_allowed(struct ssh *ssh, const char *method)
262 case PERMIT_NO_PASSWD:
263 if (strcmp(method, "publickey") == 0 ||
264 strcmp(method, "hostbased") == 0 ||
265- strcmp(method, "gssapi-with-mic") == 0)
266+ strcmp(method, "gssapi-with-mic") == 0 ||
267+ strcmp(method, "gssapi-keyex") == 0)
268 return 1;
269 break;
270 case PERMIT_FORCED_ONLY:
271@@ -737,99 +738,6 @@ fakepw(void)
272 return (&fake);
273 }
274
275-/*
276- * Returns the remote DNS hostname as a string. The returned string must not
277- * be freed. NB. this will usually trigger a DNS query the first time it is
278- * called.
279- * This function does additional checks on the hostname to mitigate some
280- * attacks on legacy rhosts-style authentication.
281- * XXX is RhostsRSAAuthentication vulnerable to these?
282- * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
283- */
284-
285-static char *
286-remote_hostname(struct ssh *ssh)
287-{
288- struct sockaddr_storage from;
289- socklen_t fromlen;
290- struct addrinfo hints, *ai, *aitop;
291- char name[NI_MAXHOST], ntop2[NI_MAXHOST];
292- const char *ntop = ssh_remote_ipaddr(ssh);
293-
294- /* Get IP address of client. */
295- fromlen = sizeof(from);
296- memset(&from, 0, sizeof(from));
297- if (getpeername(ssh_packet_get_connection_in(ssh),
298- (struct sockaddr *)&from, &fromlen) < 0) {
299- debug("getpeername failed: %.100s", strerror(errno));
300- return strdup(ntop);
301- }
302-
303- ipv64_normalise_mapped(&from, &fromlen);
304- if (from.ss_family == AF_INET6)
305- fromlen = sizeof(struct sockaddr_in6);
306-
307- debug3("Trying to reverse map address %.100s.", ntop);
308- /* Map the IP address to a host name. */
309- if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
310- NULL, 0, NI_NAMEREQD) != 0) {
311- /* Host name not found. Use ip address. */
312- return strdup(ntop);
313- }
314-
315- /*
316- * if reverse lookup result looks like a numeric hostname,
317- * someone is trying to trick us by PTR record like following:
318- * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
319- */
320- memset(&hints, 0, sizeof(hints));
321- hints.ai_socktype = SOCK_DGRAM; /*dummy*/
322- hints.ai_flags = AI_NUMERICHOST;
323- if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
324- logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
325- name, ntop);
326- freeaddrinfo(ai);
327- return strdup(ntop);
328- }
329-
330- /* Names are stored in lowercase. */
331- lowercase(name);
332-
333- /*
334- * Map it back to an IP address and check that the given
335- * address actually is an address of this host. This is
336- * necessary because anyone with access to a name server can
337- * define arbitrary names for an IP address. Mapping from
338- * name to IP address can be trusted better (but can still be
339- * fooled if the intruder has access to the name server of
340- * the domain).
341- */
342- memset(&hints, 0, sizeof(hints));
343- hints.ai_family = from.ss_family;
344- hints.ai_socktype = SOCK_STREAM;
345- if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
346- logit("reverse mapping checking getaddrinfo for %.700s "
347- "[%s] failed.", name, ntop);
348- return strdup(ntop);
349- }
350- /* Look for the address from the list of addresses. */
351- for (ai = aitop; ai; ai = ai->ai_next) {
352- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
353- sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
354- (strcmp(ntop, ntop2) == 0))
355- break;
356- }
357- freeaddrinfo(aitop);
358- /* If we reached the end of the list, the address was not there. */
359- if (ai == NULL) {
360- /* Address not found for the host name. */
361- logit("Address %.100s maps to %.600s, but this does not "
362- "map back to the address.", ntop, name);
363- return strdup(ntop);
364- }
365- return strdup(name);
366-}
367-
368 /*
369 * Return the canonical name of the host in the other side of the current
370 * connection. The host name is cached, so it is efficient to call this
371diff --git a/auth2-gss.c b/auth2-gss.c
372index 9351e0428..1f12bb113 100644
373--- a/auth2-gss.c
374+++ b/auth2-gss.c
375@@ -1,7 +1,7 @@
376 /* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 djm Exp $ */
377
378 /*
379- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
380+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
381 *
382 * Redistribution and use in source and binary forms, with or without
383 * modification, are permitted provided that the following conditions
384@@ -54,6 +54,46 @@ static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
385 static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
386 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
387
388+/*
389+ * The 'gssapi_keyex' userauth mechanism.
390+ */
391+static int
392+userauth_gsskeyex(struct ssh *ssh)
393+{
394+ Authctxt *authctxt = ssh->authctxt;
395+ int r, authenticated = 0;
396+ struct sshbuf *b;
397+ gss_buffer_desc mic, gssbuf;
398+ u_char *p;
399+ size_t len;
400+
401+ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
402+ (r = sshpkt_get_end(ssh)) != 0)
403+ fatal("%s: %s", __func__, ssh_err(r));
404+ if ((b = sshbuf_new()) == NULL)
405+ fatal("%s: sshbuf_new failed", __func__);
406+ mic.value = p;
407+ mic.length = len;
408+
409+ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
410+ "gssapi-keyex");
411+
412+ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
413+ fatal("%s: sshbuf_mutable_ptr failed", __func__);
414+ gssbuf.length = sshbuf_len(b);
415+
416+ /* gss_kex_context is NULL with privsep, so we can't check it here */
417+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
418+ &gssbuf, &mic))))
419+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
420+ authctxt->pw));
421+
422+ sshbuf_free(b);
423+ free(mic.value);
424+
425+ return (authenticated);
426+}
427+
428 /*
429 * We only support those mechanisms that we know about (ie ones that we know
430 * how to check local user kuserok and the like)
431@@ -260,7 +300,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
432 if ((r = sshpkt_get_end(ssh)) != 0)
433 fatal("%s: %s", __func__, ssh_err(r));
434
435- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
436+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
437+ authctxt->pw));
438
439 if ((!use_privsep || mm_is_monitor()) &&
440 (displayname = ssh_gssapi_displayname()) != NULL)
441@@ -306,7 +347,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
442 gssbuf.length = sshbuf_len(b);
443
444 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
445- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
446+ authenticated =
447+ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
448 else
449 logit("GSSAPI MIC check failed");
450
451@@ -326,6 +368,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
452 return 0;
453 }
454
455+Authmethod method_gsskeyex = {
456+ "gssapi-keyex",
457+ userauth_gsskeyex,
458+ &options.gss_authentication
459+};
460+
461 Authmethod method_gssapi = {
462 "gssapi-with-mic",
463 userauth_gssapi,
464diff --git a/auth2.c b/auth2.c
465index 4d19957a6..a77742819 100644
466--- a/auth2.c
467+++ b/auth2.c
468@@ -74,6 +74,7 @@ extern Authmethod method_passwd;
469 extern Authmethod method_kbdint;
470 extern Authmethod method_hostbased;
471 #ifdef GSSAPI
472+extern Authmethod method_gsskeyex;
473 extern Authmethod method_gssapi;
474 #endif
475
476@@ -81,6 +82,7 @@ Authmethod *authmethods[] = {
477 &method_none,
478 &method_pubkey,
479 #ifdef GSSAPI
480+ &method_gsskeyex,
481 &method_gssapi,
482 #endif
483 &method_passwd,
484diff --git a/canohost.c b/canohost.c
485index f71a08568..404731d24 100644
486--- a/canohost.c
487+++ b/canohost.c
488@@ -35,6 +35,99 @@
489 #include "canohost.h"
490 #include "misc.h"
491
492+/*
493+ * Returns the remote DNS hostname as a string. The returned string must not
494+ * be freed. NB. this will usually trigger a DNS query the first time it is
495+ * called.
496+ * This function does additional checks on the hostname to mitigate some
497+ * attacks on legacy rhosts-style authentication.
498+ * XXX is RhostsRSAAuthentication vulnerable to these?
499+ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
500+ */
501+
502+char *
503+remote_hostname(struct ssh *ssh)
504+{
505+ struct sockaddr_storage from;
506+ socklen_t fromlen;
507+ struct addrinfo hints, *ai, *aitop;
508+ char name[NI_MAXHOST], ntop2[NI_MAXHOST];
509+ const char *ntop = ssh_remote_ipaddr(ssh);
510+
511+ /* Get IP address of client. */
512+ fromlen = sizeof(from);
513+ memset(&from, 0, sizeof(from));
514+ if (getpeername(ssh_packet_get_connection_in(ssh),
515+ (struct sockaddr *)&from, &fromlen) < 0) {
516+ debug("getpeername failed: %.100s", strerror(errno));
517+ return strdup(ntop);
518+ }
519+
520+ ipv64_normalise_mapped(&from, &fromlen);
521+ if (from.ss_family == AF_INET6)
522+ fromlen = sizeof(struct sockaddr_in6);
523+
524+ debug3("Trying to reverse map address %.100s.", ntop);
525+ /* Map the IP address to a host name. */
526+ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
527+ NULL, 0, NI_NAMEREQD) != 0) {
528+ /* Host name not found. Use ip address. */
529+ return strdup(ntop);
530+ }
531+
532+ /*
533+ * if reverse lookup result looks like a numeric hostname,
534+ * someone is trying to trick us by PTR record like following:
535+ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
536+ */
537+ memset(&hints, 0, sizeof(hints));
538+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
539+ hints.ai_flags = AI_NUMERICHOST;
540+ if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
541+ logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
542+ name, ntop);
543+ freeaddrinfo(ai);
544+ return strdup(ntop);
545+ }
546+
547+ /* Names are stored in lowercase. */
548+ lowercase(name);
549+
550+ /*
551+ * Map it back to an IP address and check that the given
552+ * address actually is an address of this host. This is
553+ * necessary because anyone with access to a name server can
554+ * define arbitrary names for an IP address. Mapping from
555+ * name to IP address can be trusted better (but can still be
556+ * fooled if the intruder has access to the name server of
557+ * the domain).
558+ */
559+ memset(&hints, 0, sizeof(hints));
560+ hints.ai_family = from.ss_family;
561+ hints.ai_socktype = SOCK_STREAM;
562+ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
563+ logit("reverse mapping checking getaddrinfo for %.700s "
564+ "[%s] failed.", name, ntop);
565+ return strdup(ntop);
566+ }
567+ /* Look for the address from the list of addresses. */
568+ for (ai = aitop; ai; ai = ai->ai_next) {
569+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
570+ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
571+ (strcmp(ntop, ntop2) == 0))
572+ break;
573+ }
574+ freeaddrinfo(aitop);
575+ /* If we reached the end of the list, the address was not there. */
576+ if (ai == NULL) {
577+ /* Address not found for the host name. */
578+ logit("Address %.100s maps to %.600s, but this does not "
579+ "map back to the address.", ntop, name);
580+ return strdup(ntop);
581+ }
582+ return strdup(name);
583+}
584+
585 void
586 ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
587 {
588diff --git a/canohost.h b/canohost.h
589index 26d62855a..0cadc9f18 100644
590--- a/canohost.h
591+++ b/canohost.h
592@@ -15,6 +15,9 @@
593 #ifndef _CANOHOST_H
594 #define _CANOHOST_H
595
596+struct ssh;
597+
598+char *remote_hostname(struct ssh *);
599 char *get_peer_ipaddr(int);
600 int get_peer_port(int);
601 char *get_local_ipaddr(int);
602diff --git a/clientloop.c b/clientloop.c
603index 8d312cdaa..1464634b0 100644
604--- a/clientloop.c
605+++ b/clientloop.c
606@@ -112,6 +112,10 @@
607 #include "ssherr.h"
608 #include "hostfile.h"
609
610+#ifdef GSSAPI
611+#include "ssh-gss.h"
612+#endif
613+
614 /* import options */
615 extern Options options;
616
617@@ -1370,9 +1374,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
618 break;
619
620 /* Do channel operations unless rekeying in progress. */
621- if (!ssh_packet_is_rekeying(ssh))
622+ if (!ssh_packet_is_rekeying(ssh)) {
623 channel_after_select(ssh, readset, writeset);
624
625+#ifdef GSSAPI
626+ if (options.gss_renewal_rekey &&
627+ ssh_gssapi_credentials_updated(NULL)) {
628+ debug("credentials updated - forcing rekey");
629+ need_rekeying = 1;
630+ }
631+#endif
632+ }
633+
634 /* Buffer input from the connection. */
635 client_process_net_input(readset);
636
637diff --git a/config.h.in b/config.h.in
638index 91b65db8f..209760c7c 100644
639--- a/config.h.in
640+++ b/config.h.in
641@@ -1845,6 +1845,9 @@
642 /* Use btmp to log bad logins */
643 #undef USE_BTMP
644
645+/* platform uses an in-memory credentials cache */
646+#undef USE_CCAPI
647+
648 /* Use libedit for sftp */
649 #undef USE_LIBEDIT
650
651@@ -1860,6 +1863,9 @@
652 /* Use PIPES instead of a socketpair() */
653 #undef USE_PIPES
654
655+/* platform has the Security Authorization Session API */
656+#undef USE_SECURITY_SESSION_API
657+
658 /* Define if you have Solaris privileges */
659 #undef USE_SOLARIS_PRIVS
660
661diff --git a/configure.ac b/configure.ac
662index 7379ab358..023e7cc55 100644
663--- a/configure.ac
664+++ b/configure.ac
665@@ -664,6 +664,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
666 [Use tunnel device compatibility to OpenBSD])
667 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
668 [Prepend the address family to IP tunnel traffic])
669+ AC_MSG_CHECKING([if we have the Security Authorization Session API])
670+ AC_TRY_COMPILE([#include <Security/AuthSession.h>],
671+ [SessionCreate(0, 0);],
672+ [ac_cv_use_security_session_api="yes"
673+ AC_DEFINE([USE_SECURITY_SESSION_API], [1],
674+ [platform has the Security Authorization Session API])
675+ LIBS="$LIBS -framework Security"
676+ AC_MSG_RESULT([yes])],
677+ [ac_cv_use_security_session_api="no"
678+ AC_MSG_RESULT([no])])
679+ AC_MSG_CHECKING([if we have an in-memory credentials cache])
680+ AC_TRY_COMPILE(
681+ [#include <Kerberos/Kerberos.h>],
682+ [cc_context_t c;
683+ (void) cc_initialize (&c, 0, NULL, NULL);],
684+ [AC_DEFINE([USE_CCAPI], [1],
685+ [platform uses an in-memory credentials cache])
686+ LIBS="$LIBS -framework Security"
687+ AC_MSG_RESULT([yes])
688+ if test "x$ac_cv_use_security_session_api" = "xno"; then
689+ AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
690+ fi],
691+ [AC_MSG_RESULT([no])]
692+ )
693 m4_pattern_allow([AU_IPv])
694 AC_CHECK_DECL([AU_IPv4], [],
695 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
696diff --git a/gss-genr.c b/gss-genr.c
697index d56257b4a..491e62cee 100644
698--- a/gss-genr.c
699+++ b/gss-genr.c
700@@ -1,7 +1,7 @@
701 /* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 djm Exp $ */
702
703 /*
704- * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
705+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
706 *
707 * Redistribution and use in source and binary forms, with or without
708 * modification, are permitted provided that the following conditions
709@@ -39,14 +39,37 @@
710 #include "xmalloc.h"
711 #include "ssherr.h"
712 #include "sshbuf.h"
713+#include "sshkey.h"
714 #include "log.h"
715 #include "ssh2.h"
716+#include "cipher.h"
717+#include "kex.h"
718+#include "digest.h"
719
720 #include "ssh-gss.h"
721
722 extern u_char *session_id2;
723 extern u_int session_id2_len;
724
725+typedef struct {
726+ char *encoded;
727+ gss_OID oid;
728+} ssh_gss_kex_mapping;
729+
730+/*
731+ * XXX - It would be nice to find a more elegant way of handling the
732+ * XXX passing of the key exchange context to the userauth routines
733+ */
734+
735+Gssctxt *gss_kex_context = NULL;
736+
737+static ssh_gss_kex_mapping *gss_enc2oid = NULL;
738+
739+int
740+ssh_gssapi_oid_table_ok(void) {
741+ return (gss_enc2oid != NULL);
742+}
743+
744 /* sshbuf_get for gss_buffer_desc */
745 int
746 ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
747@@ -62,6 +85,143 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
748 return 0;
749 }
750
751+/*
752+ * Return a list of the gss-group1-sha1 mechanisms supported by this program
753+ *
754+ * We test mechanisms to ensure that we can use them, to avoid starting
755+ * a key exchange with a bad mechanism
756+ */
757+
758+char *
759+ssh_gssapi_client_mechanisms(const char *host, const char *client) {
760+ gss_OID_set gss_supported;
761+ OM_uint32 min_status;
762+
763+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
764+ return NULL;
765+
766+ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
767+ host, client));
768+}
769+
770+char *
771+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
772+ const char *host, const char *client) {
773+ struct sshbuf *buf;
774+ size_t i;
775+ int r, oidpos, enclen;
776+ char *mechs, *encoded;
777+ u_char digest[SSH_DIGEST_MAX_LENGTH];
778+ char deroid[2];
779+ struct ssh_digest_ctx *md;
780+
781+ if (gss_enc2oid != NULL) {
782+ for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
783+ free(gss_enc2oid[i].encoded);
784+ free(gss_enc2oid);
785+ }
786+
787+ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
788+ (gss_supported->count + 1));
789+
790+ if ((buf = sshbuf_new()) == NULL)
791+ fatal("%s: sshbuf_new failed", __func__);
792+
793+ oidpos = 0;
794+ for (i = 0; i < gss_supported->count; i++) {
795+ if (gss_supported->elements[i].length < 128 &&
796+ (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
797+
798+ deroid[0] = SSH_GSS_OIDTYPE;
799+ deroid[1] = gss_supported->elements[i].length;
800+
801+ if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
802+ ssh_digest_update(md, deroid, 2) != 0 ||
803+ ssh_digest_update(md,
804+ gss_supported->elements[i].elements,
805+ gss_supported->elements[i].length) != 0 ||
806+ ssh_digest_final(md, digest, sizeof(digest)) != 0)
807+ fatal("%s: digest failed", __func__);
808+
809+ encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
810+ * 2);
811+ enclen = __b64_ntop(digest,
812+ ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
813+ ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
814+
815+ if (oidpos != 0) {
816+ if ((r = sshbuf_put_u8(buf, ',')) != 0)
817+ fatal("%s: buffer error: %s",
818+ __func__, ssh_err(r));
819+ }
820+
821+ if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID,
822+ sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 ||
823+ (r = sshbuf_put(buf, encoded, enclen)) != 0 ||
824+ (r = sshbuf_put_u8(buf, ',')) != 0 ||
825+ (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID,
826+ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 ||
827+ (r = sshbuf_put(buf, encoded, enclen)) != 0 ||
828+ (r = sshbuf_put_u8(buf, ',')) != 0 ||
829+ (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID,
830+ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 0 ||
831+ (r = sshbuf_put(buf, encoded, enclen)) != 0)
832+ fatal("%s: buffer error: %s",
833+ __func__, ssh_err(r));
834+
835+ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
836+ gss_enc2oid[oidpos].encoded = encoded;
837+ oidpos++;
838+ }
839+ }
840+ gss_enc2oid[oidpos].oid = NULL;
841+ gss_enc2oid[oidpos].encoded = NULL;
842+
843+ if ((mechs = sshbuf_dup_string(buf)) == NULL)
844+ fatal("%s: sshbuf_dup_string failed", __func__);
845+
846+ if (strlen(mechs) == 0) {
847+ free(mechs);
848+ mechs = NULL;
849+ }
850+
851+ return (mechs);
852+}
853+
854+gss_OID
855+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
856+ int i = 0;
857+
858+ switch (kex_type) {
859+ case KEX_GSS_GRP1_SHA1:
860+ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
861+ return GSS_C_NO_OID;
862+ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
863+ break;
864+ case KEX_GSS_GRP14_SHA1:
865+ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
866+ return GSS_C_NO_OID;
867+ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
868+ break;
869+ case KEX_GSS_GEX_SHA1:
870+ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
871+ return GSS_C_NO_OID;
872+ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
873+ break;
874+ default:
875+ return GSS_C_NO_OID;
876+ }
877+
878+ while (gss_enc2oid[i].encoded != NULL &&
879+ strcmp(name, gss_enc2oid[i].encoded) != 0)
880+ i++;
881+
882+ if (gss_enc2oid[i].oid != NULL && ctx != NULL)
883+ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
884+
885+ return gss_enc2oid[i].oid;
886+}
887+
888 /* Check that the OID in a data stream matches that in the context */
889 int
890 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
891@@ -218,7 +378,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
892 }
893
894 ctx->major = gss_init_sec_context(&ctx->minor,
895- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid,
896+ ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
897 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
898 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
899
900@@ -247,9 +407,43 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
901 return (ctx->major);
902 }
903
904+OM_uint32
905+ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
906+{
907+ gss_buffer_desc gssbuf;
908+ gss_name_t gssname;
909+ OM_uint32 status;
910+ gss_OID_set oidset;
911+
912+ gssbuf.value = (void *) name;
913+ gssbuf.length = strlen(gssbuf.value);
914+
915+ gss_create_empty_oid_set(&status, &oidset);
916+ gss_add_oid_set_member(&status, ctx->oid, &oidset);
917+
918+ ctx->major = gss_import_name(&ctx->minor, &gssbuf,
919+ GSS_C_NT_USER_NAME, &gssname);
920+
921+ if (!ctx->major)
922+ ctx->major = gss_acquire_cred(&ctx->minor,
923+ gssname, 0, oidset, GSS_C_INITIATE,
924+ &ctx->client_creds, NULL, NULL);
925+
926+ gss_release_name(&status, &gssname);
927+ gss_release_oid_set(&status, &oidset);
928+
929+ if (ctx->major)
930+ ssh_gssapi_error(ctx);
931+
932+ return(ctx->major);
933+}
934+
935 OM_uint32
936 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
937 {
938+ if (ctx == NULL)
939+ return -1;
940+
941 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
942 GSS_C_QOP_DEFAULT, buffer, hash)))
943 ssh_gssapi_error(ctx);
944@@ -257,6 +451,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
945 return (ctx->major);
946 }
947
948+/* Priviledged when used by server */
949+OM_uint32
950+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
951+{
952+ if (ctx == NULL)
953+ return -1;
954+
955+ ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
956+ gssbuf, gssmic, NULL);
957+
958+ return (ctx->major);
959+}
960+
961 void
962 ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
963 const char *context)
964@@ -273,11 +480,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
965 }
966
967 int
968-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
969+ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
970+ const char *client)
971 {
972 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
973 OM_uint32 major, minor;
974 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
975+ Gssctxt *intctx = NULL;
976+
977+ if (ctx == NULL)
978+ ctx = &intctx;
979
980 /* RFC 4462 says we MUST NOT do SPNEGO */
981 if (oid->length == spnego_oid.length &&
982@@ -287,6 +499,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
983 ssh_gssapi_build_ctx(ctx);
984 ssh_gssapi_set_oid(*ctx, oid);
985 major = ssh_gssapi_import_name(*ctx, host);
986+
987+ if (!GSS_ERROR(major) && client)
988+ major = ssh_gssapi_client_identity(*ctx, client);
989+
990 if (!GSS_ERROR(major)) {
991 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
992 NULL);
993@@ -296,10 +512,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
994 GSS_C_NO_BUFFER);
995 }
996
997- if (GSS_ERROR(major))
998+ if (GSS_ERROR(major) || intctx != NULL)
999 ssh_gssapi_delete_ctx(ctx);
1000
1001 return (!GSS_ERROR(major));
1002 }
1003
1004+int
1005+ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
1006+ static gss_name_t saved_name = GSS_C_NO_NAME;
1007+ static OM_uint32 saved_lifetime = 0;
1008+ static gss_OID saved_mech = GSS_C_NO_OID;
1009+ static gss_name_t name;
1010+ static OM_uint32 last_call = 0;
1011+ OM_uint32 lifetime, now, major, minor;
1012+ int equal;
1013+
1014+ now = time(NULL);
1015+
1016+ if (ctxt) {
1017+ debug("Rekey has happened - updating saved versions");
1018+
1019+ if (saved_name != GSS_C_NO_NAME)
1020+ gss_release_name(&minor, &saved_name);
1021+
1022+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
1023+ &saved_name, &saved_lifetime, NULL, NULL);
1024+
1025+ if (!GSS_ERROR(major)) {
1026+ saved_mech = ctxt->oid;
1027+ saved_lifetime+= now;
1028+ } else {
1029+ /* Handle the error */
1030+ }
1031+ return 0;
1032+ }
1033+
1034+ if (now - last_call < 10)
1035+ return 0;
1036+
1037+ last_call = now;
1038+
1039+ if (saved_mech == GSS_C_NO_OID)
1040+ return 0;
1041+
1042+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
1043+ &name, &lifetime, NULL, NULL);
1044+ if (major == GSS_S_CREDENTIALS_EXPIRED)
1045+ return 0;
1046+ else if (GSS_ERROR(major))
1047+ return 0;
1048+
1049+ major = gss_compare_name(&minor, saved_name, name, &equal);
1050+ gss_release_name(&minor, &name);
1051+ if (GSS_ERROR(major))
1052+ return 0;
1053+
1054+ if (equal && (saved_lifetime < lifetime + now - 10))
1055+ return 1;
1056+
1057+ return 0;
1058+}
1059+
1060 #endif /* GSSAPI */
1061diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
1062index a151bc1e4..90f8692f5 100644
1063--- a/gss-serv-krb5.c
1064+++ b/gss-serv-krb5.c
1065@@ -1,7 +1,7 @@
1066 /* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */
1067
1068 /*
1069- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
1070+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
1071 *
1072 * Redistribution and use in source and binary forms, with or without
1073 * modification, are permitted provided that the following conditions
1074@@ -120,8 +120,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
1075 krb5_error_code problem;
1076 krb5_principal princ;
1077 OM_uint32 maj_status, min_status;
1078- int len;
1079 const char *errmsg;
1080+ const char *new_ccname;
1081
1082 if (client->creds == NULL) {
1083 debug("No credentials stored");
1084@@ -180,11 +180,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
1085 return;
1086 }
1087
1088- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache));
1089+ new_ccname = krb5_cc_get_name(krb_context, ccache);
1090+
1091 client->store.envvar = "KRB5CCNAME";
1092- len = strlen(client->store.filename) + 6;
1093- client->store.envval = xmalloc(len);
1094- snprintf(client->store.envval, len, "FILE:%s", client->store.filename);
1095+#ifdef USE_CCAPI
1096+ xasprintf(&client->store.envval, "API:%s", new_ccname);
1097+ client->store.filename = NULL;
1098+#else
1099+ xasprintf(&client->store.envval, "FILE:%s", new_ccname);
1100+ client->store.filename = xstrdup(new_ccname);
1101+#endif
1102
1103 #ifdef USE_PAM
1104 if (options.use_pam)
1105@@ -196,6 +201,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
1106 return;
1107 }
1108
1109+int
1110+ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
1111+ ssh_gssapi_client *client)
1112+{
1113+ krb5_ccache ccache = NULL;
1114+ krb5_principal principal = NULL;
1115+ char *name = NULL;
1116+ krb5_error_code problem;
1117+ OM_uint32 maj_status, min_status;
1118+
1119+ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
1120+ logit("krb5_cc_resolve(): %.100s",
1121+ krb5_get_err_text(krb_context, problem));
1122+ return 0;
1123+ }
1124+
1125+ /* Find out who the principal in this cache is */
1126+ if ((problem = krb5_cc_get_principal(krb_context, ccache,
1127+ &principal))) {
1128+ logit("krb5_cc_get_principal(): %.100s",
1129+ krb5_get_err_text(krb_context, problem));
1130+ krb5_cc_close(krb_context, ccache);
1131+ return 0;
1132+ }
1133+
1134+ if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
1135+ logit("krb5_unparse_name(): %.100s",
1136+ krb5_get_err_text(krb_context, problem));
1137+ krb5_free_principal(krb_context, principal);
1138+ krb5_cc_close(krb_context, ccache);
1139+ return 0;
1140+ }
1141+
1142+
1143+ if (strcmp(name,client->exportedname.value)!=0) {
1144+ debug("Name in local credentials cache differs. Not storing");
1145+ krb5_free_principal(krb_context, principal);
1146+ krb5_cc_close(krb_context, ccache);
1147+ krb5_free_unparsed_name(krb_context, name);
1148+ return 0;
1149+ }
1150+ krb5_free_unparsed_name(krb_context, name);
1151+
1152+ /* Name matches, so lets get on with it! */
1153+
1154+ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
1155+ logit("krb5_cc_initialize(): %.100s",
1156+ krb5_get_err_text(krb_context, problem));
1157+ krb5_free_principal(krb_context, principal);
1158+ krb5_cc_close(krb_context, ccache);
1159+ return 0;
1160+ }
1161+
1162+ krb5_free_principal(krb_context, principal);
1163+
1164+ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
1165+ ccache))) {
1166+ logit("gss_krb5_copy_ccache() failed. Sorry!");
1167+ krb5_cc_close(krb_context, ccache);
1168+ return 0;
1169+ }
1170+
1171+ return 1;
1172+}
1173+
1174 ssh_gssapi_mech gssapi_kerberos_mech = {
1175 "toWM5Slw5Ew8Mqkay+al2g==",
1176 "Kerberos",
1177@@ -203,7 +273,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
1178 NULL,
1179 &ssh_gssapi_krb5_userok,
1180 NULL,
1181- &ssh_gssapi_krb5_storecreds
1182+ &ssh_gssapi_krb5_storecreds,
1183+ &ssh_gssapi_krb5_updatecreds
1184 };
1185
1186 #endif /* KRB5 */
1187diff --git a/gss-serv.c b/gss-serv.c
1188index ab3a15f0f..6c087a1b1 100644
1189--- a/gss-serv.c
1190+++ b/gss-serv.c
1191@@ -1,7 +1,7 @@
1192 /* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus Exp $ */
1193
1194 /*
1195- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
1196+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
1197 *
1198 * Redistribution and use in source and binary forms, with or without
1199 * modification, are permitted provided that the following conditions
1200@@ -44,17 +44,22 @@
1201 #include "session.h"
1202 #include "misc.h"
1203 #include "servconf.h"
1204+#include "uidswap.h"
1205
1206 #include "ssh-gss.h"
1207+#include "monitor_wrap.h"
1208+
1209+extern ServerOptions options;
1210
1211 extern ServerOptions options;
1212
1213 static ssh_gssapi_client gssapi_client =
1214 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
1215- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}};
1216+ GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL,
1217+ {NULL, NULL, NULL, NULL, NULL}, 0, 0};
1218
1219 ssh_gssapi_mech gssapi_null_mech =
1220- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
1221+ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
1222
1223 #ifdef KRB5
1224 extern ssh_gssapi_mech gssapi_kerberos_mech;
1225@@ -140,6 +145,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
1226 return (ssh_gssapi_acquire_cred(*ctx));
1227 }
1228
1229+/* Unprivileged */
1230+char *
1231+ssh_gssapi_server_mechanisms(void) {
1232+ if (supported_oids == NULL)
1233+ ssh_gssapi_prepare_supported_oids();
1234+ return (ssh_gssapi_kex_mechs(supported_oids,
1235+ &ssh_gssapi_server_check_mech, NULL, NULL));
1236+}
1237+
1238+/* Unprivileged */
1239+int
1240+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
1241+ const char *dummy) {
1242+ Gssctxt *ctx = NULL;
1243+ int res;
1244+
1245+ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
1246+ ssh_gssapi_delete_ctx(&ctx);
1247+
1248+ return (res);
1249+}
1250+
1251 /* Unprivileged */
1252 void
1253 ssh_gssapi_supported_oids(gss_OID_set *oidset)
1254@@ -150,7 +177,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
1255 gss_OID_set supported;
1256
1257 gss_create_empty_oid_set(&min_status, oidset);
1258- gss_indicate_mechs(&min_status, &supported);
1259+
1260+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
1261+ return;
1262
1263 while (supported_mechs[i]->name != NULL) {
1264 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
1265@@ -276,8 +305,48 @@ OM_uint32
1266 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1267 {
1268 int i = 0;
1269+ int equal = 0;
1270+ gss_name_t new_name = GSS_C_NO_NAME;
1271+ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
1272+
1273+ if (options.gss_store_rekey && client->used && ctx->client_creds) {
1274+ if (client->mech->oid.length != ctx->oid->length ||
1275+ (memcmp(client->mech->oid.elements,
1276+ ctx->oid->elements, ctx->oid->length) !=0)) {
1277+ debug("Rekeyed credentials have different mechanism");
1278+ return GSS_S_COMPLETE;
1279+ }
1280+
1281+ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
1282+ ctx->client_creds, ctx->oid, &new_name,
1283+ NULL, NULL, NULL))) {
1284+ ssh_gssapi_error(ctx);
1285+ return (ctx->major);
1286+ }
1287+
1288+ ctx->major = gss_compare_name(&ctx->minor, client->name,
1289+ new_name, &equal);
1290+
1291+ if (GSS_ERROR(ctx->major)) {
1292+ ssh_gssapi_error(ctx);
1293+ return (ctx->major);
1294+ }
1295+
1296+ if (!equal) {
1297+ debug("Rekeyed credentials have different name");
1298+ return GSS_S_COMPLETE;
1299+ }
1300
1301- gss_buffer_desc ename;
1302+ debug("Marking rekeyed credentials for export");
1303+
1304+ gss_release_name(&ctx->minor, &client->name);
1305+ gss_release_cred(&ctx->minor, &client->creds);
1306+ client->name = new_name;
1307+ client->creds = ctx->client_creds;
1308+ ctx->client_creds = GSS_C_NO_CREDENTIAL;
1309+ client->updated = 1;
1310+ return GSS_S_COMPLETE;
1311+ }
1312
1313 client->mech = NULL;
1314
1315@@ -292,6 +361,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1316 if (client->mech == NULL)
1317 return GSS_S_FAILURE;
1318
1319+ if (ctx->client_creds &&
1320+ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
1321+ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
1322+ ssh_gssapi_error(ctx);
1323+ return (ctx->major);
1324+ }
1325+
1326 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
1327 &client->displayname, NULL))) {
1328 ssh_gssapi_error(ctx);
1329@@ -309,6 +385,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1330 return (ctx->major);
1331 }
1332
1333+ gss_release_buffer(&ctx->minor, &ename);
1334+
1335 /* We can't copy this structure, so we just move the pointer to it */
1336 client->creds = ctx->client_creds;
1337 ctx->client_creds = GSS_C_NO_CREDENTIAL;
1338@@ -356,7 +434,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
1339
1340 /* Privileged */
1341 int
1342-ssh_gssapi_userok(char *user)
1343+ssh_gssapi_userok(char *user, struct passwd *pw)
1344 {
1345 OM_uint32 lmin;
1346
1347@@ -366,9 +444,11 @@ ssh_gssapi_userok(char *user)
1348 return 0;
1349 }
1350 if (gssapi_client.mech && gssapi_client.mech->userok)
1351- if ((*gssapi_client.mech->userok)(&gssapi_client, user))
1352+ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
1353+ gssapi_client.used = 1;
1354+ gssapi_client.store.owner = pw;
1355 return 1;
1356- else {
1357+ } else {
1358 /* Destroy delegated credentials if userok fails */
1359 gss_release_buffer(&lmin, &gssapi_client.displayname);
1360 gss_release_buffer(&lmin, &gssapi_client.exportedname);
1361@@ -382,14 +462,90 @@ ssh_gssapi_userok(char *user)
1362 return (0);
1363 }
1364
1365-/* Privileged */
1366-OM_uint32
1367-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1368+/* These bits are only used for rekeying. The unpriviledged child is running
1369+ * as the user, the monitor is root.
1370+ *
1371+ * In the child, we want to :
1372+ * *) Ask the monitor to store our credentials into the store we specify
1373+ * *) If it succeeds, maybe do a PAM update
1374+ */
1375+
1376+/* Stuff for PAM */
1377+
1378+#ifdef USE_PAM
1379+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
1380+ struct pam_response **resp, void *data)
1381 {
1382- ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
1383- gssbuf, gssmic, NULL);
1384+ return (PAM_CONV_ERR);
1385+}
1386+#endif
1387
1388- return (ctx->major);
1389+void
1390+ssh_gssapi_rekey_creds(void) {
1391+ int ok;
1392+ int ret;
1393+#ifdef USE_PAM
1394+ pam_handle_t *pamh = NULL;
1395+ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
1396+ char *envstr;
1397+#endif
1398+
1399+ if (gssapi_client.store.filename == NULL &&
1400+ gssapi_client.store.envval == NULL &&
1401+ gssapi_client.store.envvar == NULL)
1402+ return;
1403+
1404+ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
1405+
1406+ if (!ok)
1407+ return;
1408+
1409+ debug("Rekeyed credentials stored successfully");
1410+
1411+ /* Actually managing to play with the ssh pam stack from here will
1412+ * be next to impossible. In any case, we may want different options
1413+ * for rekeying. So, use our own :)
1414+ */
1415+#ifdef USE_PAM
1416+ if (!use_privsep) {
1417+ debug("Not even going to try and do PAM with privsep disabled");
1418+ return;
1419+ }
1420+
1421+ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
1422+ &pamconv, &pamh);
1423+ if (ret)
1424+ return;
1425+
1426+ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
1427+ gssapi_client.store.envval);
1428+
1429+ ret = pam_putenv(pamh, envstr);
1430+ if (!ret)
1431+ pam_setcred(pamh, PAM_REINITIALIZE_CRED);
1432+ pam_end(pamh, PAM_SUCCESS);
1433+#endif
1434+}
1435+
1436+int
1437+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
1438+ int ok = 0;
1439+
1440+ /* Check we've got credentials to store */
1441+ if (!gssapi_client.updated)
1442+ return 0;
1443+
1444+ gssapi_client.updated = 0;
1445+
1446+ temporarily_use_uid(gssapi_client.store.owner);
1447+ if (gssapi_client.mech && gssapi_client.mech->updatecreds)
1448+ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
1449+ else
1450+ debug("No update function for this mechanism");
1451+
1452+ restore_uid();
1453+
1454+ return ok;
1455 }
1456
1457 /* Privileged */
1458diff --git a/kex.c b/kex.c
1459index 25f9f66f6..fb5bfaea5 100644
1460--- a/kex.c
1461+++ b/kex.c
1462@@ -54,6 +54,10 @@
1463 #include "sshbuf.h"
1464 #include "digest.h"
1465
1466+#ifdef GSSAPI
1467+#include "ssh-gss.h"
1468+#endif
1469+
1470 /* prototype */
1471 static int kex_choose_conf(struct ssh *);
1472 static int kex_input_newkeys(int, u_int32_t, struct ssh *);
1473@@ -105,6 +109,14 @@ static const struct kexalg kexalgs[] = {
1474 #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
1475 { NULL, -1, -1, -1},
1476 };
1477+static const struct kexalg kexalg_prefixes[] = {
1478+#ifdef GSSAPI
1479+ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
1480+ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
1481+ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
1482+#endif
1483+ { NULL, -1, -1, -1 },
1484+};
1485
1486 char *
1487 kex_alg_list(char sep)
1488@@ -137,6 +149,10 @@ kex_alg_by_name(const char *name)
1489 if (strcmp(k->name, name) == 0)
1490 return k;
1491 }
1492+ for (k = kexalg_prefixes; k->name != NULL; k++) {
1493+ if (strncmp(k->name, name, strlen(k->name)) == 0)
1494+ return k;
1495+ }
1496 return NULL;
1497 }
1498
1499@@ -653,6 +669,9 @@ kex_free(struct kex *kex)
1500 sshbuf_free(kex->peer);
1501 sshbuf_free(kex->my);
1502 free(kex->session_id);
1503+#ifdef GSSAPI
1504+ free(kex->gss_host);
1505+#endif /* GSSAPI */
1506 free(kex->client_version_string);
1507 free(kex->server_version_string);
1508 free(kex->failed_choice);
1509diff --git a/kex.h b/kex.h
1510index 593de1208..4e5ead839 100644
1511--- a/kex.h
1512+++ b/kex.h
1513@@ -100,6 +100,9 @@ enum kex_exchange {
1514 KEX_DH_GEX_SHA256,
1515 KEX_ECDH_SHA2,
1516 KEX_C25519_SHA256,
1517+ KEX_GSS_GRP1_SHA1,
1518+ KEX_GSS_GRP14_SHA1,
1519+ KEX_GSS_GEX_SHA1,
1520 KEX_MAX
1521 };
1522
1523@@ -148,6 +151,12 @@ struct kex {
1524 u_int flags;
1525 int hash_alg;
1526 int ec_nid;
1527+#ifdef GSSAPI
1528+ int gss_deleg_creds;
1529+ int gss_trust_dns;
1530+ char *gss_host;
1531+ char *gss_client;
1532+#endif
1533 char *client_version_string;
1534 char *server_version_string;
1535 char *failed_choice;
1536@@ -198,6 +207,11 @@ int kexecdh_server(struct ssh *);
1537 int kexc25519_client(struct ssh *);
1538 int kexc25519_server(struct ssh *);
1539
1540+#ifdef GSSAPI
1541+int kexgss_client(struct ssh *);
1542+int kexgss_server(struct ssh *);
1543+#endif
1544+
1545 int kex_dh_hash(int, const char *, const char *,
1546 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
1547 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *);
1548diff --git a/kexgssc.c b/kexgssc.c
1549new file mode 100644
1550index 000000000..3c8ae08dd
1551--- /dev/null
1552+++ b/kexgssc.c
1553@@ -0,0 +1,341 @@
1554+/*
1555+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
1556+ *
1557+ * Redistribution and use in source and binary forms, with or without
1558+ * modification, are permitted provided that the following conditions
1559+ * are met:
1560+ * 1. Redistributions of source code must retain the above copyright
1561+ * notice, this list of conditions and the following disclaimer.
1562+ * 2. Redistributions in binary form must reproduce the above copyright
1563+ * notice, this list of conditions and the following disclaimer in the
1564+ * documentation and/or other materials provided with the distribution.
1565+ *
1566+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
1567+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1568+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1569+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1570+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1571+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1572+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1573+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1574+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1575+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1576+ */
1577+
1578+#include "includes.h"
1579+
1580+#ifdef GSSAPI
1581+
1582+#include "includes.h"
1583+
1584+#include <openssl/crypto.h>
1585+#include <openssl/bn.h>
1586+
1587+#include <string.h>
1588+
1589+#include "xmalloc.h"
1590+#include "sshbuf.h"
1591+#include "ssh2.h"
1592+#include "sshkey.h"
1593+#include "cipher.h"
1594+#include "kex.h"
1595+#include "log.h"
1596+#include "packet.h"
1597+#include "dh.h"
1598+#include "digest.h"
1599+
1600+#include "ssh-gss.h"
1601+
1602+int
1603+kexgss_client(struct ssh *ssh) {
1604+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
1605+ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr;
1606+ Gssctxt *ctxt;
1607+ OM_uint32 maj_status, min_status, ret_flags;
1608+ u_int klen, kout, slen = 0, strlen;
1609+ DH *dh;
1610+ BIGNUM *dh_server_pub = NULL;
1611+ BIGNUM *shared_secret = NULL;
1612+ const BIGNUM *pub_key, *dh_p, *dh_g;
1613+ BIGNUM *p = NULL;
1614+ BIGNUM *g = NULL;
1615+ u_char *kbuf;
1616+ u_char *serverhostkey = NULL;
1617+ u_char *empty = "";
1618+ char *msg;
1619+ int type = 0;
1620+ int first = 1;
1621+ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
1622+ u_char hash[SSH_DIGEST_MAX_LENGTH];
1623+ size_t hashlen;
1624+
1625+ /* Initialise our GSSAPI world */
1626+ ssh_gssapi_build_ctx(&ctxt);
1627+ if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type)
1628+ == GSS_C_NO_OID)
1629+ fatal("Couldn't identify host exchange");
1630+
1631+ if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host))
1632+ fatal("Couldn't import hostname");
1633+
1634+ if (ssh->kex->gss_client &&
1635+ ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client))
1636+ fatal("Couldn't acquire client credentials");
1637+
1638+ switch (ssh->kex->kex_type) {
1639+ case KEX_GSS_GRP1_SHA1:
1640+ dh = dh_new_group1();
1641+ break;
1642+ case KEX_GSS_GRP14_SHA1:
1643+ dh = dh_new_group14();
1644+ break;
1645+ case KEX_GSS_GEX_SHA1:
1646+ debug("Doing group exchange\n");
1647+ nbits = dh_estimate(ssh->kex->we_need * 8);
1648+ packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
1649+ packet_put_int(min);
1650+ packet_put_int(nbits);
1651+ packet_put_int(max);
1652+
1653+ packet_send();
1654+
1655+ packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
1656+
1657+ if ((p = BN_new()) == NULL)
1658+ fatal("BN_new() failed");
1659+ packet_get_bignum2(p);
1660+ if ((g = BN_new()) == NULL)
1661+ fatal("BN_new() failed");
1662+ packet_get_bignum2(g);
1663+ packet_check_eom();
1664+
1665+ if (BN_num_bits(p) < min || BN_num_bits(p) > max)
1666+ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
1667+ min, BN_num_bits(p), max);
1668+
1669+ dh = dh_new_group(g, p);
1670+ break;
1671+ default:
1672+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
1673+ }
1674+
1675+ /* Step 1 - e is dh->pub_key */
1676+ dh_gen_key(dh, ssh->kex->we_need * 8);
1677+ DH_get0_key(dh, &pub_key, NULL);
1678+ DH_get0_pqg(dh, &dh_p, NULL, &dh_g);
1679+
1680+ /* This is f, we initialise it now to make life easier */
1681+ dh_server_pub = BN_new();
1682+ if (dh_server_pub == NULL)
1683+ fatal("dh_server_pub == NULL");
1684+
1685+ token_ptr = GSS_C_NO_BUFFER;
1686+
1687+ do {
1688+ debug("Calling gss_init_sec_context");
1689+
1690+ maj_status = ssh_gssapi_init_ctx(ctxt,
1691+ ssh->kex->gss_deleg_creds, token_ptr, &send_tok,
1692+ &ret_flags);
1693+
1694+ if (GSS_ERROR(maj_status)) {
1695+ if (send_tok.length != 0) {
1696+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
1697+ packet_put_string(send_tok.value,
1698+ send_tok.length);
1699+ }
1700+ fatal("gss_init_context failed");
1701+ }
1702+
1703+ /* If we've got an old receive buffer get rid of it */
1704+ if (token_ptr != GSS_C_NO_BUFFER)
1705+ free(recv_tok.value);
1706+
1707+ if (maj_status == GSS_S_COMPLETE) {
1708+ /* If mutual state flag is not true, kex fails */
1709+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
1710+ fatal("Mutual authentication failed");
1711+
1712+ /* If integ avail flag is not true kex fails */
1713+ if (!(ret_flags & GSS_C_INTEG_FLAG))
1714+ fatal("Integrity check failed");
1715+ }
1716+
1717+ /*
1718+ * If we have data to send, then the last message that we
1719+ * received cannot have been a 'complete'.
1720+ */
1721+ if (send_tok.length != 0) {
1722+ if (first) {
1723+ packet_start(SSH2_MSG_KEXGSS_INIT);
1724+ packet_put_string(send_tok.value,
1725+ send_tok.length);
1726+ packet_put_bignum2(pub_key);
1727+ first = 0;
1728+ } else {
1729+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
1730+ packet_put_string(send_tok.value,
1731+ send_tok.length);
1732+ }
1733+ packet_send();
1734+ gss_release_buffer(&min_status, &send_tok);
1735+
1736+ /* If we've sent them data, they should reply */
1737+ do {
1738+ type = packet_read();
1739+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
1740+ debug("Received KEXGSS_HOSTKEY");
1741+ if (serverhostkey)
1742+ fatal("Server host key received more than once");
1743+ serverhostkey =
1744+ packet_get_string(&slen);
1745+ }
1746+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
1747+
1748+ switch (type) {
1749+ case SSH2_MSG_KEXGSS_CONTINUE:
1750+ debug("Received GSSAPI_CONTINUE");
1751+ if (maj_status == GSS_S_COMPLETE)
1752+ fatal("GSSAPI Continue received from server when complete");
1753+ recv_tok.value = packet_get_string(&strlen);
1754+ recv_tok.length = strlen;
1755+ break;
1756+ case SSH2_MSG_KEXGSS_COMPLETE:
1757+ debug("Received GSSAPI_COMPLETE");
1758+ packet_get_bignum2(dh_server_pub);
1759+ msg_tok.value = packet_get_string(&strlen);
1760+ msg_tok.length = strlen;
1761+
1762+ /* Is there a token included? */
1763+ if (packet_get_char()) {
1764+ recv_tok.value=
1765+ packet_get_string(&strlen);
1766+ recv_tok.length = strlen;
1767+ /* If we're already complete - protocol error */
1768+ if (maj_status == GSS_S_COMPLETE)
1769+ packet_disconnect("Protocol error: received token when complete");
1770+ } else {
1771+ /* No token included */
1772+ if (maj_status != GSS_S_COMPLETE)
1773+ packet_disconnect("Protocol error: did not receive final token");
1774+ }
1775+ break;
1776+ case SSH2_MSG_KEXGSS_ERROR:
1777+ debug("Received Error");
1778+ maj_status = packet_get_int();
1779+ min_status = packet_get_int();
1780+ msg = packet_get_string(NULL);
1781+ (void) packet_get_string_ptr(NULL);
1782+ fatal("GSSAPI Error: \n%.400s",msg);
1783+ default:
1784+ packet_disconnect("Protocol error: didn't expect packet type %d",
1785+ type);
1786+ }
1787+ token_ptr = &recv_tok;
1788+ } else {
1789+ /* No data, and not complete */
1790+ if (maj_status != GSS_S_COMPLETE)
1791+ fatal("Not complete, and no token output");
1792+ }
1793+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
1794+
1795+ /*
1796+ * We _must_ have received a COMPLETE message in reply from the
1797+ * server, which will have set dh_server_pub and msg_tok
1798+ */
1799+
1800+ if (type != SSH2_MSG_KEXGSS_COMPLETE)
1801+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
1802+
1803+ /* Check f in range [1, p-1] */
1804+ if (!dh_pub_is_valid(dh, dh_server_pub))
1805+ packet_disconnect("bad server public DH value");
1806+
1807+ /* compute K=f^x mod p */
1808+ klen = DH_size(dh);
1809+ kbuf = xmalloc(klen);
1810+ kout = DH_compute_key(kbuf, dh_server_pub, dh);
1811+ if (kout < 0)
1812+ fatal("DH_compute_key: failed");
1813+
1814+ shared_secret = BN_new();
1815+ if (shared_secret == NULL)
1816+ fatal("kexgss_client: BN_new failed");
1817+
1818+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
1819+ fatal("kexdh_client: BN_bin2bn failed");
1820+
1821+ memset(kbuf, 0, klen);
1822+ free(kbuf);
1823+
1824+ hashlen = sizeof(hash);
1825+ switch (ssh->kex->kex_type) {
1826+ case KEX_GSS_GRP1_SHA1:
1827+ case KEX_GSS_GRP14_SHA1:
1828+ kex_dh_hash(
1829+ ssh->kex->hash_alg,
1830+ ssh->kex->client_version_string,
1831+ ssh->kex->server_version_string,
1832+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
1833+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
1834+ (serverhostkey ? serverhostkey : empty), slen,
1835+ pub_key, /* e */
1836+ dh_server_pub, /* f */
1837+ shared_secret, /* K */
1838+ hash, &hashlen
1839+ );
1840+ break;
1841+ case KEX_GSS_GEX_SHA1:
1842+ kexgex_hash(
1843+ ssh->kex->hash_alg,
1844+ ssh->kex->client_version_string,
1845+ ssh->kex->server_version_string,
1846+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
1847+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
1848+ (serverhostkey ? serverhostkey : empty), slen,
1849+ min, nbits, max,
1850+ dh_p, dh_g,
1851+ pub_key,
1852+ dh_server_pub,
1853+ shared_secret,
1854+ hash, &hashlen
1855+ );
1856+ break;
1857+ default:
1858+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
1859+ }
1860+
1861+ gssbuf.value = hash;
1862+ gssbuf.length = hashlen;
1863+
1864+ /* Verify that the hash matches the MIC we just got. */
1865+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
1866+ packet_disconnect("Hash's MIC didn't verify");
1867+
1868+ free(msg_tok.value);
1869+
1870+ DH_free(dh);
1871+ free(serverhostkey);
1872+ BN_clear_free(dh_server_pub);
1873+
1874+ /* save session id */
1875+ if (ssh->kex->session_id == NULL) {
1876+ ssh->kex->session_id_len = hashlen;
1877+ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
1878+ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
1879+ }
1880+
1881+ if (ssh->kex->gss_deleg_creds)
1882+ ssh_gssapi_credentials_updated(ctxt);
1883+
1884+ if (gss_kex_context == NULL)
1885+ gss_kex_context = ctxt;
1886+ else
1887+ ssh_gssapi_delete_ctx(&ctxt);
1888+
1889+ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
1890+ BN_clear_free(shared_secret);
1891+ return kex_send_newkeys(ssh);
1892+}
1893+
1894+#endif /* GSSAPI */
1895diff --git a/kexgsss.c b/kexgsss.c
1896new file mode 100644
1897index 000000000..18070f1d7
1898--- /dev/null
1899+++ b/kexgsss.c
1900@@ -0,0 +1,300 @@
1901+/*
1902+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
1903+ *
1904+ * Redistribution and use in source and binary forms, with or without
1905+ * modification, are permitted provided that the following conditions
1906+ * are met:
1907+ * 1. Redistributions of source code must retain the above copyright
1908+ * notice, this list of conditions and the following disclaimer.
1909+ * 2. Redistributions in binary form must reproduce the above copyright
1910+ * notice, this list of conditions and the following disclaimer in the
1911+ * documentation and/or other materials provided with the distribution.
1912+ *
1913+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
1914+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1915+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1916+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1917+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1918+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1919+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1920+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1921+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1922+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1923+ */
1924+
1925+#include "includes.h"
1926+
1927+#ifdef GSSAPI
1928+
1929+#include <string.h>
1930+
1931+#include <openssl/crypto.h>
1932+#include <openssl/bn.h>
1933+
1934+#include "xmalloc.h"
1935+#include "sshbuf.h"
1936+#include "ssh2.h"
1937+#include "sshkey.h"
1938+#include "cipher.h"
1939+#include "kex.h"
1940+#include "log.h"
1941+#include "packet.h"
1942+#include "dh.h"
1943+#include "ssh-gss.h"
1944+#include "monitor_wrap.h"
1945+#include "misc.h"
1946+#include "servconf.h"
1947+#include "digest.h"
1948+
1949+extern ServerOptions options;
1950+
1951+int
1952+kexgss_server(struct ssh *ssh)
1953+{
1954+ OM_uint32 maj_status, min_status;
1955+
1956+ /*
1957+ * Some GSSAPI implementations use the input value of ret_flags (an
1958+ * output variable) as a means of triggering mechanism specific
1959+ * features. Initializing it to zero avoids inadvertently
1960+ * activating this non-standard behaviour.
1961+ */
1962+
1963+ OM_uint32 ret_flags = 0;
1964+ gss_buffer_desc gssbuf, recv_tok, msg_tok;
1965+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
1966+ Gssctxt *ctxt = NULL;
1967+ u_int slen, klen, kout;
1968+ u_char *kbuf;
1969+ DH *dh;
1970+ int min = -1, max = -1, nbits = -1;
1971+ const BIGNUM *pub_key, *dh_p, *dh_g;
1972+ BIGNUM *shared_secret = NULL;
1973+ BIGNUM *dh_client_pub = NULL;
1974+ int type = 0;
1975+ gss_OID oid;
1976+ char *mechs;
1977+ u_char hash[SSH_DIGEST_MAX_LENGTH];
1978+ size_t hashlen;
1979+
1980+ /* Initialise GSSAPI */
1981+
1982+ /* If we're rekeying, privsep means that some of the private structures
1983+ * in the GSSAPI code are no longer available. This kludges them back
1984+ * into life
1985+ */
1986+ if (!ssh_gssapi_oid_table_ok()) {
1987+ mechs = ssh_gssapi_server_mechanisms();
1988+ free(mechs);
1989+ }
1990+
1991+ debug2("%s: Identifying %s", __func__, ssh->kex->name);
1992+ oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type);
1993+ if (oid == GSS_C_NO_OID)
1994+ fatal("Unknown gssapi mechanism");
1995+
1996+ debug2("%s: Acquiring credentials", __func__);
1997+
1998+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
1999+ fatal("Unable to acquire credentials for the server");
2000+
2001+ switch (ssh->kex->kex_type) {
2002+ case KEX_GSS_GRP1_SHA1:
2003+ dh = dh_new_group1();
2004+ break;
2005+ case KEX_GSS_GRP14_SHA1:
2006+ dh = dh_new_group14();
2007+ break;
2008+ case KEX_GSS_GEX_SHA1:
2009+ debug("Doing group exchange");
2010+ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
2011+ min = packet_get_int();
2012+ nbits = packet_get_int();
2013+ max = packet_get_int();
2014+ packet_check_eom();
2015+ if (max < min || nbits < min || max < nbits)
2016+ fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
2017+ min, nbits, max);
2018+ dh = PRIVSEP(choose_dh(MAX(DH_GRP_MIN, min),
2019+ nbits, MIN(DH_GRP_MAX, max)));
2020+ if (dh == NULL)
2021+ packet_disconnect("Protocol error: no matching group found");
2022+ DH_get0_pqg(dh, &dh_p, NULL, &dh_g);
2023+
2024+ packet_start(SSH2_MSG_KEXGSS_GROUP);
2025+ packet_put_bignum2(dh_p);
2026+ packet_put_bignum2(dh_g);
2027+ packet_send();
2028+
2029+ packet_write_wait();
2030+ break;
2031+ default:
2032+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
2033+ }
2034+
2035+ dh_gen_key(dh, ssh->kex->we_need * 8);
2036+
2037+ do {
2038+ debug("Wait SSH2_MSG_GSSAPI_INIT");
2039+ type = packet_read();
2040+ switch(type) {
2041+ case SSH2_MSG_KEXGSS_INIT:
2042+ if (dh_client_pub != NULL)
2043+ fatal("Received KEXGSS_INIT after initialising");
2044+ recv_tok.value = packet_get_string(&slen);
2045+ recv_tok.length = slen;
2046+
2047+ if ((dh_client_pub = BN_new()) == NULL)
2048+ fatal("dh_client_pub == NULL");
2049+
2050+ packet_get_bignum2(dh_client_pub);
2051+
2052+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
2053+ break;
2054+ case SSH2_MSG_KEXGSS_CONTINUE:
2055+ recv_tok.value = packet_get_string(&slen);
2056+ recv_tok.length = slen;
2057+ break;
2058+ default:
2059+ packet_disconnect(
2060+ "Protocol error: didn't expect packet type %d",
2061+ type);
2062+ }
2063+
2064+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
2065+ &send_tok, &ret_flags));
2066+
2067+ free(recv_tok.value);
2068+
2069+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
2070+ fatal("Zero length token output when incomplete");
2071+
2072+ if (dh_client_pub == NULL)
2073+ fatal("No client public key");
2074+
2075+ if (maj_status & GSS_S_CONTINUE_NEEDED) {
2076+ debug("Sending GSSAPI_CONTINUE");
2077+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
2078+ packet_put_string(send_tok.value, send_tok.length);
2079+ packet_send();
2080+ gss_release_buffer(&min_status, &send_tok);
2081+ }
2082+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
2083+
2084+ if (GSS_ERROR(maj_status)) {
2085+ if (send_tok.length > 0) {
2086+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
2087+ packet_put_string(send_tok.value, send_tok.length);
2088+ packet_send();
2089+ }
2090+ fatal("accept_ctx died");
2091+ }
2092+
2093+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
2094+ fatal("Mutual Authentication flag wasn't set");
2095+
2096+ if (!(ret_flags & GSS_C_INTEG_FLAG))
2097+ fatal("Integrity flag wasn't set");
2098+
2099+ if (!dh_pub_is_valid(dh, dh_client_pub))
2100+ packet_disconnect("bad client public DH value");
2101+
2102+ klen = DH_size(dh);
2103+ kbuf = xmalloc(klen);
2104+ kout = DH_compute_key(kbuf, dh_client_pub, dh);
2105+ if (kout < 0)
2106+ fatal("DH_compute_key: failed");
2107+
2108+ shared_secret = BN_new();
2109+ if (shared_secret == NULL)
2110+ fatal("kexgss_server: BN_new failed");
2111+
2112+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
2113+ fatal("kexgss_server: BN_bin2bn failed");
2114+
2115+ memset(kbuf, 0, klen);
2116+ free(kbuf);
2117+
2118+ DH_get0_key(dh, &pub_key, NULL);
2119+ DH_get0_pqg(dh, &dh_p, NULL, &dh_g);
2120+
2121+ hashlen = sizeof(hash);
2122+ switch (ssh->kex->kex_type) {
2123+ case KEX_GSS_GRP1_SHA1:
2124+ case KEX_GSS_GRP14_SHA1:
2125+ kex_dh_hash(
2126+ ssh->kex->hash_alg,
2127+ ssh->kex->client_version_string, ssh->kex->server_version_string,
2128+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
2129+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
2130+ NULL, 0, /* Change this if we start sending host keys */
2131+ dh_client_pub, pub_key, shared_secret,
2132+ hash, &hashlen
2133+ );
2134+ break;
2135+ case KEX_GSS_GEX_SHA1:
2136+ kexgex_hash(
2137+ ssh->kex->hash_alg,
2138+ ssh->kex->client_version_string, ssh->kex->server_version_string,
2139+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
2140+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
2141+ NULL, 0,
2142+ min, nbits, max,
2143+ dh_p, dh_g,
2144+ dh_client_pub,
2145+ pub_key,
2146+ shared_secret,
2147+ hash, &hashlen
2148+ );
2149+ break;
2150+ default:
2151+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
2152+ }
2153+
2154+ BN_clear_free(dh_client_pub);
2155+
2156+ if (ssh->kex->session_id == NULL) {
2157+ ssh->kex->session_id_len = hashlen;
2158+ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
2159+ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
2160+ }
2161+
2162+ gssbuf.value = hash;
2163+ gssbuf.length = hashlen;
2164+
2165+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
2166+ fatal("Couldn't get MIC");
2167+
2168+ packet_start(SSH2_MSG_KEXGSS_COMPLETE);
2169+ packet_put_bignum2(pub_key);
2170+ packet_put_string(msg_tok.value,msg_tok.length);
2171+
2172+ if (send_tok.length != 0) {
2173+ packet_put_char(1); /* true */
2174+ packet_put_string(send_tok.value, send_tok.length);
2175+ } else {
2176+ packet_put_char(0); /* false */
2177+ }
2178+ packet_send();
2179+
2180+ gss_release_buffer(&min_status, &send_tok);
2181+ gss_release_buffer(&min_status, &msg_tok);
2182+
2183+ if (gss_kex_context == NULL)
2184+ gss_kex_context = ctxt;
2185+ else
2186+ ssh_gssapi_delete_ctx(&ctxt);
2187+
2188+ DH_free(dh);
2189+
2190+ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
2191+ BN_clear_free(shared_secret);
2192+ kex_send_newkeys(ssh);
2193+
2194+ /* If this was a rekey, then save out any delegated credentials we
2195+ * just exchanged. */
2196+ if (options.gss_store_rekey)
2197+ ssh_gssapi_rekey_creds();
2198+ return 0;
2199+}
2200+#endif /* GSSAPI */
2201diff --git a/monitor.c b/monitor.c
2202index 531b2993a..eabc1e89b 100644
2203--- a/monitor.c
2204+++ b/monitor.c
2205@@ -145,6 +145,8 @@ int mm_answer_gss_setup_ctx(int, struct sshbuf *);
2206 int mm_answer_gss_accept_ctx(int, struct sshbuf *);
2207 int mm_answer_gss_userok(int, struct sshbuf *);
2208 int mm_answer_gss_checkmic(int, struct sshbuf *);
2209+int mm_answer_gss_sign(int, struct sshbuf *);
2210+int mm_answer_gss_updatecreds(int, struct sshbuf *);
2211 #endif
2212
2213 #ifdef SSH_AUDIT_EVENTS
2214@@ -215,11 +217,18 @@ struct mon_table mon_dispatch_proto20[] = {
2215 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
2216 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
2217 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
2218+ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
2219 #endif
2220 {0, 0, NULL}
2221 };
2222
2223 struct mon_table mon_dispatch_postauth20[] = {
2224+#ifdef GSSAPI
2225+ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
2226+ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
2227+ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
2228+ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
2229+#endif
2230 #ifdef WITH_OPENSSL
2231 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
2232 #endif
2233@@ -289,6 +298,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
2234 /* Permit requests for moduli and signatures */
2235 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
2236 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
2237+#ifdef GSSAPI
2238+ /* and for the GSSAPI key exchange */
2239+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
2240+#endif
2241
2242 /* The first few requests do not require asynchronous access */
2243 while (!authenticated) {
2244@@ -401,6 +414,10 @@ monitor_child_postauth(struct monitor *pmonitor)
2245 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
2246 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
2247 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
2248+#ifdef GSSAPI
2249+ /* and for the GSSAPI key exchange */
2250+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
2251+#endif
2252
2253 if (auth_opts->permit_pty_flag) {
2254 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
2255@@ -1666,6 +1683,13 @@ monitor_apply_keystate(struct monitor *pmonitor)
2256 # endif
2257 #endif /* WITH_OPENSSL */
2258 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2259+#ifdef GSSAPI
2260+ if (options.gss_keyex) {
2261+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2262+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2263+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2264+ }
2265+#endif
2266 kex->load_host_public_key=&get_hostkey_public_by_type;
2267 kex->load_host_private_key=&get_hostkey_private_by_type;
2268 kex->host_key_index=&get_hostkey_index;
2269@@ -1756,8 +1780,8 @@ mm_answer_gss_setup_ctx(int sock, struct sshbuf *m)
2270 u_char *p;
2271 int r;
2272
2273- if (!options.gss_authentication)
2274- fatal("%s: GSSAPI authentication not enabled", __func__);
2275+ if (!options.gss_authentication && !options.gss_keyex)
2276+ fatal("%s: GSSAPI not enabled", __func__);
2277
2278 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
2279 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2280@@ -1789,8 +1813,8 @@ mm_answer_gss_accept_ctx(int sock, struct sshbuf *m)
2281 OM_uint32 flags = 0; /* GSI needs this */
2282 int r;
2283
2284- if (!options.gss_authentication)
2285- fatal("%s: GSSAPI authentication not enabled", __func__);
2286+ if (!options.gss_authentication && !options.gss_keyex)
2287+ fatal("%s: GSSAPI not enabled", __func__);
2288
2289 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
2290 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2291@@ -1810,6 +1834,7 @@ mm_answer_gss_accept_ctx(int sock, struct sshbuf *m)
2292 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2293 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2294 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2295+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2296 }
2297 return (0);
2298 }
2299@@ -1821,8 +1846,8 @@ mm_answer_gss_checkmic(int sock, struct sshbuf *m)
2300 OM_uint32 ret;
2301 int r;
2302
2303- if (!options.gss_authentication)
2304- fatal("%s: GSSAPI authentication not enabled", __func__);
2305+ if (!options.gss_authentication && !options.gss_keyex)
2306+ fatal("%s: GSSAPI not enabled", __func__);
2307
2308 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
2309 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
2310@@ -1851,10 +1876,11 @@ mm_answer_gss_userok(int sock, struct sshbuf *m)
2311 int r, authenticated;
2312 const char *displayname;
2313
2314- if (!options.gss_authentication)
2315- fatal("%s: GSSAPI authentication not enabled", __func__);
2316+ if (!options.gss_authentication && !options.gss_keyex)
2317+ fatal("%s: GSSAPI not enabled", __func__);
2318
2319- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
2320+ authenticated = authctxt->valid &&
2321+ ssh_gssapi_userok(authctxt->user, authctxt->pw);
2322
2323 sshbuf_reset(m);
2324 if ((r = sshbuf_put_u32(m, authenticated)) != 0)
2325@@ -1871,5 +1897,83 @@ mm_answer_gss_userok(int sock, struct sshbuf *m)
2326 /* Monitor loop will terminate if authenticated */
2327 return (authenticated);
2328 }
2329+
2330+int
2331+mm_answer_gss_sign(int socket, struct sshbuf *m)
2332+{
2333+ gss_buffer_desc data;
2334+ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2335+ OM_uint32 major, minor;
2336+ size_t len;
2337+ u_char *p;
2338+ int r;
2339+
2340+ if (!options.gss_authentication && !options.gss_keyex)
2341+ fatal("%s: GSSAPI not enabled", __func__);
2342+
2343+ if ((r = sshbuf_get_string(m, &p, &len)) != 0)
2344+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2345+ data.value = p;
2346+ data.length = len;
2347+ if (data.length != 20)
2348+ fatal("%s: data length incorrect: %d", __func__,
2349+ (int) data.length);
2350+
2351+ /* Save the session ID on the first time around */
2352+ if (session_id2_len == 0) {
2353+ session_id2_len = data.length;
2354+ session_id2 = xmalloc(session_id2_len);
2355+ memcpy(session_id2, data.value, session_id2_len);
2356+ }
2357+ major = ssh_gssapi_sign(gsscontext, &data, &hash);
2358+
2359+ free(data.value);
2360+
2361+ sshbuf_reset(m);
2362+ if ((r = sshbuf_put_u32(m, major)) != 0 ||
2363+ (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
2364+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2365+
2366+ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2367+
2368+ gss_release_buffer(&minor, &hash);
2369+
2370+ /* Turn on getpwnam permissions */
2371+ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2372+
2373+ /* And credential updating, for when rekeying */
2374+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2375+
2376+ return (0);
2377+}
2378+
2379+int
2380+mm_answer_gss_updatecreds(int socket, struct sshbuf *m) {
2381+ ssh_gssapi_ccache store;
2382+ int r, ok;
2383+
2384+ if (!options.gss_authentication && !options.gss_keyex)
2385+ fatal("%s: GSSAPI not enabled", __func__);
2386+
2387+ if ((r = sshbuf_get_cstring(m, &store.filename, NULL)) != 0 ||
2388+ (r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 ||
2389+ (r = sshbuf_get_cstring(m, &store.envval, NULL)) != 0)
2390+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2391+
2392+ ok = ssh_gssapi_update_creds(&store);
2393+
2394+ free(store.filename);
2395+ free(store.envvar);
2396+ free(store.envval);
2397+
2398+ sshbuf_reset(m);
2399+ if ((r = sshbuf_put_u32(m, ok)) != 0)
2400+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2401+
2402+ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2403+
2404+ return(0);
2405+}
2406+
2407 #endif /* GSSAPI */
2408
2409diff --git a/monitor.h b/monitor.h
2410index 16047299f..44fbed589 100644
2411--- a/monitor.h
2412+++ b/monitor.h
2413@@ -63,6 +63,9 @@ enum monitor_reqtype {
2414 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
2415 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
2416
2417+ MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
2418+ MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
2419+
2420 };
2421
2422 struct monitor {
2423diff --git a/monitor_wrap.c b/monitor_wrap.c
2424index 732fb3476..1865a122a 100644
2425--- a/monitor_wrap.c
2426+++ b/monitor_wrap.c
2427@@ -984,7 +984,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
2428 }
2429
2430 int
2431-mm_ssh_gssapi_userok(char *user)
2432+mm_ssh_gssapi_userok(char *user, struct passwd *pw)
2433 {
2434 struct sshbuf *m;
2435 int r, authenticated = 0;
2436@@ -1003,4 +1003,55 @@ mm_ssh_gssapi_userok(char *user)
2437 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
2438 return (authenticated);
2439 }
2440+
2441+OM_uint32
2442+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
2443+{
2444+ struct sshbuf *m;
2445+ OM_uint32 major;
2446+ int r;
2447+
2448+ if ((m = sshbuf_new()) == NULL)
2449+ fatal("%s: sshbuf_new failed", __func__);
2450+ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
2451+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2452+
2453+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m);
2454+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m);
2455+
2456+ if ((r = sshbuf_get_u32(m, &major)) != 0 ||
2457+ (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
2458+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2459+
2460+ sshbuf_free(m);
2461+
2462+ return(major);
2463+}
2464+
2465+int
2466+mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
2467+{
2468+ struct sshbuf *m;
2469+ int r, ok;
2470+
2471+ if ((m = sshbuf_new()) == NULL)
2472+ fatal("%s: sshbuf_new failed", __func__);
2473+ if ((r = sshbuf_put_cstring(m,
2474+ store->filename ? store->filename : "")) != 0 ||
2475+ (r = sshbuf_put_cstring(m,
2476+ store->envvar ? store->envvar : "")) != 0 ||
2477+ (r = sshbuf_put_cstring(m,
2478+ store->envval ? store->envval : "")) != 0)
2479+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2480+
2481+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m);
2482+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m);
2483+
2484+ if ((r = sshbuf_get_u32(m, &ok)) != 0)
2485+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2486+ sshbuf_free(m);
2487+
2488+ return (ok);
2489+}
2490+
2491 #endif /* GSSAPI */
2492diff --git a/monitor_wrap.h b/monitor_wrap.h
2493index 644da081d..7f93144ff 100644
2494--- a/monitor_wrap.h
2495+++ b/monitor_wrap.h
2496@@ -60,8 +60,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
2497 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
2498 OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
2499 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
2500-int mm_ssh_gssapi_userok(char *user);
2501+int mm_ssh_gssapi_userok(char *user, struct passwd *);
2502 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
2503+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
2504+int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
2505 #endif
2506
2507 #ifdef USE_PAM
2508diff --git a/opacket.c b/opacket.c
2509index e637d7a71..7672c0b59 100644
2510--- a/opacket.c
2511+++ b/opacket.c
2512@@ -80,7 +80,7 @@ ssh_packet_put_raw(struct ssh *ssh, const void *buf, u_int len)
2513
2514 #ifdef WITH_OPENSSL
2515 void
2516-ssh_packet_put_bignum2(struct ssh *ssh, BIGNUM * value)
2517+ssh_packet_put_bignum2(struct ssh *ssh, const BIGNUM * value)
2518 {
2519 int r;
2520
2521diff --git a/opacket.h b/opacket.h
2522index f92fe586e..1cf66a2d3 100644
2523--- a/opacket.h
2524+++ b/opacket.h
2525@@ -7,7 +7,7 @@ void ssh_packet_start(struct ssh *, u_char);
2526 void ssh_packet_put_char(struct ssh *, int ch);
2527 void ssh_packet_put_int(struct ssh *, u_int value);
2528 void ssh_packet_put_int64(struct ssh *, u_int64_t value);
2529-void ssh_packet_put_bignum2(struct ssh *, BIGNUM * value);
2530+void ssh_packet_put_bignum2(struct ssh *, const BIGNUM * value);
2531 void ssh_packet_put_ecpoint(struct ssh *, const EC_GROUP *, const EC_POINT *);
2532 void ssh_packet_put_string(struct ssh *, const void *buf, u_int len);
2533 void ssh_packet_put_cstring(struct ssh *, const char *str);
2534diff --git a/readconf.c b/readconf.c
2535index 433811521..36bc5e59a 100644
2536--- a/readconf.c
2537+++ b/readconf.c
2538@@ -161,6 +161,8 @@ typedef enum {
2539 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
2540 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
2541 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
2542+ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
2543+ oGssServerIdentity,
2544 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
2545 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
2546 oHashKnownHosts,
2547@@ -201,10 +203,20 @@ static struct {
2548 /* Sometimes-unsupported options */
2549 #if defined(GSSAPI)
2550 { "gssapiauthentication", oGssAuthentication },
2551+ { "gssapikeyexchange", oGssKeyEx },
2552 { "gssapidelegatecredentials", oGssDelegateCreds },
2553+ { "gssapitrustdns", oGssTrustDns },
2554+ { "gssapiclientidentity", oGssClientIdentity },
2555+ { "gssapiserveridentity", oGssServerIdentity },
2556+ { "gssapirenewalforcesrekey", oGssRenewalRekey },
2557 # else
2558 { "gssapiauthentication", oUnsupported },
2559+ { "gssapikeyexchange", oUnsupported },
2560 { "gssapidelegatecredentials", oUnsupported },
2561+ { "gssapitrustdns", oUnsupported },
2562+ { "gssapiclientidentity", oUnsupported },
2563+ { "gssapiserveridentity", oUnsupported },
2564+ { "gssapirenewalforcesrekey", oUnsupported },
2565 #endif
2566 #ifdef ENABLE_PKCS11
2567 { "smartcarddevice", oPKCS11Provider },
2568@@ -974,10 +986,30 @@ parse_time:
2569 intptr = &options->gss_authentication;
2570 goto parse_flag;
2571
2572+ case oGssKeyEx:
2573+ intptr = &options->gss_keyex;
2574+ goto parse_flag;
2575+
2576 case oGssDelegateCreds:
2577 intptr = &options->gss_deleg_creds;
2578 goto parse_flag;
2579
2580+ case oGssTrustDns:
2581+ intptr = &options->gss_trust_dns;
2582+ goto parse_flag;
2583+
2584+ case oGssClientIdentity:
2585+ charptr = &options->gss_client_identity;
2586+ goto parse_string;
2587+
2588+ case oGssServerIdentity:
2589+ charptr = &options->gss_server_identity;
2590+ goto parse_string;
2591+
2592+ case oGssRenewalRekey:
2593+ intptr = &options->gss_renewal_rekey;
2594+ goto parse_flag;
2595+
2596 case oBatchMode:
2597 intptr = &options->batch_mode;
2598 goto parse_flag;
2599@@ -1842,7 +1874,12 @@ initialize_options(Options * options)
2600 options->pubkey_authentication = -1;
2601 options->challenge_response_authentication = -1;
2602 options->gss_authentication = -1;
2603+ options->gss_keyex = -1;
2604 options->gss_deleg_creds = -1;
2605+ options->gss_trust_dns = -1;
2606+ options->gss_renewal_rekey = -1;
2607+ options->gss_client_identity = NULL;
2608+ options->gss_server_identity = NULL;
2609 options->password_authentication = -1;
2610 options->kbd_interactive_authentication = -1;
2611 options->kbd_interactive_devices = NULL;
2612@@ -1988,8 +2025,14 @@ fill_default_options(Options * options)
2613 options->challenge_response_authentication = 1;
2614 if (options->gss_authentication == -1)
2615 options->gss_authentication = 0;
2616+ if (options->gss_keyex == -1)
2617+ options->gss_keyex = 0;
2618 if (options->gss_deleg_creds == -1)
2619 options->gss_deleg_creds = 0;
2620+ if (options->gss_trust_dns == -1)
2621+ options->gss_trust_dns = 0;
2622+ if (options->gss_renewal_rekey == -1)
2623+ options->gss_renewal_rekey = 0;
2624 if (options->password_authentication == -1)
2625 options->password_authentication = 1;
2626 if (options->kbd_interactive_authentication == -1)
2627diff --git a/readconf.h b/readconf.h
2628index fc7e38251..8e4900d01 100644
2629--- a/readconf.h
2630+++ b/readconf.h
2631@@ -40,7 +40,12 @@ typedef struct {
2632 int challenge_response_authentication;
2633 /* Try S/Key or TIS, authentication. */
2634 int gss_authentication; /* Try GSS authentication */
2635+ int gss_keyex; /* Try GSS key exchange */
2636 int gss_deleg_creds; /* Delegate GSS credentials */
2637+ int gss_trust_dns; /* Trust DNS for GSS canonicalization */
2638+ int gss_renewal_rekey; /* Credential renewal forces rekey */
2639+ char *gss_client_identity; /* Principal to initiate GSSAPI with */
2640+ char *gss_server_identity; /* GSSAPI target principal */
2641 int password_authentication; /* Try password
2642 * authentication. */
2643 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
2644diff --git a/servconf.c b/servconf.c
2645index 932d363bb..4668b8a45 100644
2646--- a/servconf.c
2647+++ b/servconf.c
2648@@ -124,8 +124,10 @@ initialize_server_options(ServerOptions *options)
2649 options->kerberos_ticket_cleanup = -1;
2650 options->kerberos_get_afs_token = -1;
2651 options->gss_authentication=-1;
2652+ options->gss_keyex = -1;
2653 options->gss_cleanup_creds = -1;
2654 options->gss_strict_acceptor = -1;
2655+ options->gss_store_rekey = -1;
2656 options->password_authentication = -1;
2657 options->kbd_interactive_authentication = -1;
2658 options->challenge_response_authentication = -1;
2659@@ -337,10 +339,14 @@ fill_default_server_options(ServerOptions *options)
2660 options->kerberos_get_afs_token = 0;
2661 if (options->gss_authentication == -1)
2662 options->gss_authentication = 0;
2663+ if (options->gss_keyex == -1)
2664+ options->gss_keyex = 0;
2665 if (options->gss_cleanup_creds == -1)
2666 options->gss_cleanup_creds = 1;
2667 if (options->gss_strict_acceptor == -1)
2668 options->gss_strict_acceptor = 1;
2669+ if (options->gss_store_rekey == -1)
2670+ options->gss_store_rekey = 0;
2671 if (options->password_authentication == -1)
2672 options->password_authentication = 1;
2673 if (options->kbd_interactive_authentication == -1)
2674@@ -485,6 +491,7 @@ typedef enum {
2675 sHostKeyAlgorithms,
2676 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
2677 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
2678+ sGssKeyEx, sGssStoreRekey,
2679 sAcceptEnv, sSetEnv, sPermitTunnel,
2680 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
2681 sUsePrivilegeSeparation, sAllowAgentForwarding,
2682@@ -559,12 +566,20 @@ static struct {
2683 #ifdef GSSAPI
2684 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
2685 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
2686+ { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
2687 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
2688+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
2689+ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
2690 #else
2691 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
2692 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
2693+ { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
2694 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
2695+ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
2696+ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
2697 #endif
2698+ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
2699+ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
2700 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
2701 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
2702 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
2703@@ -1468,6 +1483,10 @@ process_server_config_line(ServerOptions *options, char *line,
2704 intptr = &options->gss_authentication;
2705 goto parse_flag;
2706
2707+ case sGssKeyEx:
2708+ intptr = &options->gss_keyex;
2709+ goto parse_flag;
2710+
2711 case sGssCleanupCreds:
2712 intptr = &options->gss_cleanup_creds;
2713 goto parse_flag;
2714@@ -1476,6 +1495,10 @@ process_server_config_line(ServerOptions *options, char *line,
2715 intptr = &options->gss_strict_acceptor;
2716 goto parse_flag;
2717
2718+ case sGssStoreRekey:
2719+ intptr = &options->gss_store_rekey;
2720+ goto parse_flag;
2721+
2722 case sPasswordAuthentication:
2723 intptr = &options->password_authentication;
2724 goto parse_flag;
2725@@ -2560,7 +2583,10 @@ dump_config(ServerOptions *o)
2726 #endif
2727 #ifdef GSSAPI
2728 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2729+ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2730 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2731+ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2732+ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2733 #endif
2734 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2735 dump_cfg_fmtint(sKbdInteractiveAuthentication,
2736diff --git a/servconf.h b/servconf.h
2737index 0175e00e8..3b76da816 100644
2738--- a/servconf.h
2739+++ b/servconf.h
2740@@ -125,8 +125,10 @@ typedef struct {
2741 int kerberos_get_afs_token; /* If true, try to get AFS token if
2742 * authenticated with Kerberos. */
2743 int gss_authentication; /* If true, permit GSSAPI authentication */
2744+ int gss_keyex; /* If true, permit GSSAPI key exchange */
2745 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
2746 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
2747+ int gss_store_rekey;
2748 int password_authentication; /* If true, permit password
2749 * authentication. */
2750 int kbd_interactive_authentication; /* If true, permit */
2751diff --git a/ssh-gss.h b/ssh-gss.h
2752index 36180d07a..350ce7882 100644
2753--- a/ssh-gss.h
2754+++ b/ssh-gss.h
2755@@ -1,6 +1,6 @@
2756 /* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */
2757 /*
2758- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
2759+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
2760 *
2761 * Redistribution and use in source and binary forms, with or without
2762 * modification, are permitted provided that the following conditions
2763@@ -61,10 +61,22 @@
2764
2765 #define SSH_GSS_OIDTYPE 0x06
2766
2767+#define SSH2_MSG_KEXGSS_INIT 30
2768+#define SSH2_MSG_KEXGSS_CONTINUE 31
2769+#define SSH2_MSG_KEXGSS_COMPLETE 32
2770+#define SSH2_MSG_KEXGSS_HOSTKEY 33
2771+#define SSH2_MSG_KEXGSS_ERROR 34
2772+#define SSH2_MSG_KEXGSS_GROUPREQ 40
2773+#define SSH2_MSG_KEXGSS_GROUP 41
2774+#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
2775+#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-"
2776+#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
2777+
2778 typedef struct {
2779 char *filename;
2780 char *envvar;
2781 char *envval;
2782+ struct passwd *owner;
2783 void *data;
2784 } ssh_gssapi_ccache;
2785
2786@@ -72,8 +84,11 @@ typedef struct {
2787 gss_buffer_desc displayname;
2788 gss_buffer_desc exportedname;
2789 gss_cred_id_t creds;
2790+ gss_name_t name;
2791 struct ssh_gssapi_mech_struct *mech;
2792 ssh_gssapi_ccache store;
2793+ int used;
2794+ int updated;
2795 } ssh_gssapi_client;
2796
2797 typedef struct ssh_gssapi_mech_struct {
2798@@ -84,6 +99,7 @@ typedef struct ssh_gssapi_mech_struct {
2799 int (*userok) (ssh_gssapi_client *, char *);
2800 int (*localname) (ssh_gssapi_client *, char **);
2801 void (*storecreds) (ssh_gssapi_client *);
2802+ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
2803 } ssh_gssapi_mech;
2804
2805 typedef struct {
2806@@ -94,10 +110,11 @@ typedef struct {
2807 gss_OID oid; /* client */
2808 gss_cred_id_t creds; /* server */
2809 gss_name_t client; /* server */
2810- gss_cred_id_t client_creds; /* server */
2811+ gss_cred_id_t client_creds; /* both */
2812 } Gssctxt;
2813
2814 extern ssh_gssapi_mech *supported_mechs[];
2815+extern Gssctxt *gss_kex_context;
2816
2817 int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
2818 void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
2819@@ -123,17 +140,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **);
2820 OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
2821 void ssh_gssapi_buildmic(struct sshbuf *, const char *,
2822 const char *, const char *);
2823-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
2824+int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
2825+OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
2826+int ssh_gssapi_credentials_updated(Gssctxt *);
2827
2828 /* In the server */
2829+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
2830+ const char *);
2831+char *ssh_gssapi_client_mechanisms(const char *, const char *);
2832+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
2833+ const char *);
2834+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
2835+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
2836+ const char *);
2837 OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
2838-int ssh_gssapi_userok(char *name);
2839+int ssh_gssapi_userok(char *name, struct passwd *);
2840 OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
2841 void ssh_gssapi_do_child(char ***, u_int *);
2842 void ssh_gssapi_cleanup_creds(void);
2843 void ssh_gssapi_storecreds(void);
2844 const char *ssh_gssapi_displayname(void);
2845
2846+char *ssh_gssapi_server_mechanisms(void);
2847+int ssh_gssapi_oid_table_ok(void);
2848+
2849+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
2850+void ssh_gssapi_rekey_creds(void);
2851+
2852 #endif /* GSSAPI */
2853
2854 #endif /* _SSH_GSS_H */
2855diff --git a/ssh_config b/ssh_config
2856index c12f5ef52..bcb9f153d 100644
2857--- a/ssh_config
2858+++ b/ssh_config
2859@@ -24,6 +24,8 @@
2860 # HostbasedAuthentication no
2861 # GSSAPIAuthentication no
2862 # GSSAPIDelegateCredentials no
2863+# GSSAPIKeyExchange no
2864+# GSSAPITrustDNS no
2865 # BatchMode no
2866 # CheckHostIP yes
2867 # AddressFamily any
2868diff --git a/ssh_config.5 b/ssh_config.5
2869index 4d5b01d3e..16c79368a 100644
2870--- a/ssh_config.5
2871+++ b/ssh_config.5
2872@@ -736,10 +736,42 @@ The default is
2873 Specifies whether user authentication based on GSSAPI is allowed.
2874 The default is
2875 .Cm no .
2876+.It Cm GSSAPIKeyExchange
2877+Specifies whether key exchange based on GSSAPI may be used. When using
2878+GSSAPI key exchange the server need not have a host key.
2879+The default is
2880+.Cm no .
2881+.It Cm GSSAPIClientIdentity
2882+If set, specifies the GSSAPI client identity that ssh should use when
2883+connecting to the server. The default is unset, which means that the default
2884+identity will be used.
2885+.It Cm GSSAPIServerIdentity
2886+If set, specifies the GSSAPI server identity that ssh should expect when
2887+connecting to the server. The default is unset, which means that the
2888+expected GSSAPI server identity will be determined from the target
2889+hostname.
2890 .It Cm GSSAPIDelegateCredentials
2891 Forward (delegate) credentials to the server.
2892 The default is
2893 .Cm no .
2894+.It Cm GSSAPIRenewalForcesRekey
2895+If set to
2896+.Cm yes
2897+then renewal of the client's GSSAPI credentials will force the rekeying of the
2898+ssh connection. With a compatible server, this can delegate the renewed
2899+credentials to a session on the server.
2900+The default is
2901+.Cm no .
2902+.It Cm GSSAPITrustDns
2903+Set to
2904+.Cm yes
2905+to indicate that the DNS is trusted to securely canonicalize
2906+the name of the host being connected to. If
2907+.Cm no ,
2908+the hostname entered on the
2909+command line will be passed untouched to the GSSAPI library.
2910+The default is
2911+.Cm no .
2912 .It Cm HashKnownHosts
2913 Indicates that
2914 .Xr ssh 1
2915diff --git a/sshconnect2.c b/sshconnect2.c
2916index 1675f3935..8c872a4fb 100644
2917--- a/sshconnect2.c
2918+++ b/sshconnect2.c
2919@@ -162,6 +162,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
2920 struct kex *kex;
2921 int r;
2922
2923+#ifdef GSSAPI
2924+ char *orig = NULL, *gss = NULL;
2925+ char *gss_host = NULL;
2926+#endif
2927+
2928 xxx_host = host;
2929 xxx_hostaddr = hostaddr;
2930
2931@@ -194,6 +199,35 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
2932 order_hostkeyalgs(host, hostaddr, port));
2933 }
2934
2935+#ifdef GSSAPI
2936+ if (options.gss_keyex) {
2937+ /* Add the GSSAPI mechanisms currently supported on this
2938+ * client to the key exchange algorithm proposal */
2939+ orig = myproposal[PROPOSAL_KEX_ALGS];
2940+
2941+ if (options.gss_server_identity)
2942+ gss_host = xstrdup(options.gss_server_identity);
2943+ else if (options.gss_trust_dns)
2944+ gss_host = remote_hostname(active_state);
2945+ else
2946+ gss_host = xstrdup(host);
2947+
2948+ gss = ssh_gssapi_client_mechanisms(gss_host,
2949+ options.gss_client_identity);
2950+ if (gss) {
2951+ debug("Offering GSSAPI proposal: %s", gss);
2952+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
2953+ "%s,%s", gss, orig);
2954+
2955+ /* If we've got GSSAPI algorithms, then we also
2956+ * support the 'null' hostkey, as a last resort */
2957+ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
2958+ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
2959+ "%s,null", orig);
2960+ }
2961+ }
2962+#endif
2963+
2964 if (options.rekey_limit || options.rekey_interval)
2965 packet_set_rekey_limits(options.rekey_limit,
2966 options.rekey_interval);
2967@@ -215,15 +249,41 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
2968 # endif
2969 #endif
2970 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
2971+#ifdef GSSAPI
2972+ if (options.gss_keyex) {
2973+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
2974+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
2975+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
2976+ }
2977+#endif
2978 kex->client_version_string=client_version_string;
2979 kex->server_version_string=server_version_string;
2980 kex->verify_host_key=&verify_host_key_callback;
2981
2982+#ifdef GSSAPI
2983+ if (options.gss_keyex) {
2984+ kex->gss_deleg_creds = options.gss_deleg_creds;
2985+ kex->gss_trust_dns = options.gss_trust_dns;
2986+ kex->gss_client = options.gss_client_identity;
2987+ kex->gss_host = gss_host;
2988+ }
2989+#endif
2990+
2991 ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done);
2992
2993 /* remove ext-info from the KEX proposals for rekeying */
2994 myproposal[PROPOSAL_KEX_ALGS] =
2995 compat_kex_proposal(options.kex_algorithms);
2996+#ifdef GSSAPI
2997+ /* repair myproposal after it was crumpled by the */
2998+ /* ext-info removal above */
2999+ if (gss) {
3000+ orig = myproposal[PROPOSAL_KEX_ALGS];
3001+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
3002+ "%s,%s", gss, orig);
3003+ free(gss);
3004+ }
3005+#endif
3006 if ((r = kex_prop2buf(kex->my, myproposal)) != 0)
3007 fatal("kex_prop2buf: %s", ssh_err(r));
3008
3009@@ -314,6 +374,7 @@ int input_gssapi_token(int type, u_int32_t, struct ssh *);
3010 int input_gssapi_hash(int type, u_int32_t, struct ssh *);
3011 int input_gssapi_error(int, u_int32_t, struct ssh *);
3012 int input_gssapi_errtok(int, u_int32_t, struct ssh *);
3013+int userauth_gsskeyex(Authctxt *authctxt);
3014 #endif
3015
3016 void userauth(Authctxt *, char *);
3017@@ -330,6 +391,11 @@ static char *authmethods_get(void);
3018
3019 Authmethod authmethods[] = {
3020 #ifdef GSSAPI
3021+ {"gssapi-keyex",
3022+ userauth_gsskeyex,
3023+ NULL,
3024+ &options.gss_authentication,
3025+ NULL},
3026 {"gssapi-with-mic",
3027 userauth_gssapi,
3028 NULL,
3029@@ -686,25 +752,40 @@ userauth_gssapi(Authctxt *authctxt)
3030 static u_int mech = 0;
3031 OM_uint32 min;
3032 int r, ok = 0;
3033+ char *gss_host;
3034+
3035+ if (options.gss_server_identity)
3036+ gss_host = xstrdup(options.gss_server_identity);
3037+ else if (options.gss_trust_dns)
3038+ gss_host = remote_hostname(active_state);
3039+ else
3040+ gss_host = xstrdup(authctxt->host);
3041
3042 /* Try one GSSAPI method at a time, rather than sending them all at
3043 * once. */
3044
3045 if (gss_supported == NULL)
3046- gss_indicate_mechs(&min, &gss_supported);
3047+ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
3048+ gss_supported = NULL;
3049+ free(gss_host);
3050+ return 0;
3051+ }
3052
3053 /* Check to see if the mechanism is usable before we offer it */
3054 while (mech < gss_supported->count && !ok) {
3055 /* My DER encoding requires length<128 */
3056 if (gss_supported->elements[mech].length < 128 &&
3057 ssh_gssapi_check_mechanism(&gssctxt,
3058- &gss_supported->elements[mech], authctxt->host)) {
3059+ &gss_supported->elements[mech], gss_host,
3060+ options.gss_client_identity)) {
3061 ok = 1; /* Mechanism works */
3062 } else {
3063 mech++;
3064 }
3065 }
3066
3067+ free(gss_host);
3068+
3069 if (!ok)
3070 return 0;
3071
3072@@ -935,6 +1016,54 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
3073 free(lang);
3074 return r;
3075 }
3076+
3077+int
3078+userauth_gsskeyex(Authctxt *authctxt)
3079+{
3080+ struct ssh *ssh = active_state; /* XXX */
3081+ struct sshbuf *b;
3082+ gss_buffer_desc gssbuf;
3083+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
3084+ OM_uint32 ms;
3085+ int r;
3086+
3087+ static int attempt = 0;
3088+ if (attempt++ >= 1)
3089+ return (0);
3090+
3091+ if (gss_kex_context == NULL) {
3092+ debug("No valid Key exchange context");
3093+ return (0);
3094+ }
3095+
3096+ if ((b = sshbuf_new()) == NULL)
3097+ fatal("%s: sshbuf_new failed", __func__);
3098+ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
3099+ "gssapi-keyex");
3100+
3101+ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
3102+ fatal("%s: sshbuf_mutable_ptr failed", __func__);
3103+ gssbuf.length = sshbuf_len(b);
3104+
3105+ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
3106+ sshbuf_free(b);
3107+ return (0);
3108+ }
3109+
3110+ if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
3111+ (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
3112+ (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
3113+ (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
3114+ (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
3115+ (r = sshpkt_send(ssh)) != 0)
3116+ fatal("%s: %s", __func__, ssh_err(r));
3117+
3118+ sshbuf_free(b);
3119+ gss_release_buffer(&ms, &mic);
3120+
3121+ return (1);
3122+}
3123+
3124 #endif /* GSSAPI */
3125
3126 int
3127diff --git a/sshd.c b/sshd.c
3128index ba26287ba..539a000fd 100644
3129--- a/sshd.c
3130+++ b/sshd.c
3131@@ -123,6 +123,10 @@
3132 #include "version.h"
3133 #include "ssherr.h"
3134
3135+#ifdef USE_SECURITY_SESSION_API
3136+#include <Security/AuthSession.h>
3137+#endif
3138+
3139 /* Re-exec fds */
3140 #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
3141 #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2)
3142@@ -1810,10 +1814,13 @@ main(int ac, char **av)
3143 free(fp);
3144 }
3145 accumulate_host_timing_secret(cfg, NULL);
3146+#ifndef GSSAPI
3147+ /* The GSSAPI key exchange can run without a host key */
3148 if (!sensitive_data.have_ssh2_key) {
3149 logit("sshd: no hostkeys available -- exiting.");
3150 exit(1);
3151 }
3152+#endif
3153
3154 /*
3155 * Load certificates. They are stored in an array at identical
3156@@ -2104,6 +2111,60 @@ main(int ac, char **av)
3157 rdomain == NULL ? "" : "\"");
3158 free(laddr);
3159
3160+#ifdef USE_SECURITY_SESSION_API
3161+ /*
3162+ * Create a new security session for use by the new user login if
3163+ * the current session is the root session or we are not launched
3164+ * by inetd (eg: debugging mode or server mode). We do not
3165+ * necessarily need to create a session if we are launched from
3166+ * inetd because Panther xinetd will create a session for us.
3167+ *
3168+ * The only case where this logic will fail is if there is an
3169+ * inetd running in a non-root session which is not creating
3170+ * new sessions for us. Then all the users will end up in the
3171+ * same session (bad).
3172+ *
3173+ * When the client exits, the session will be destroyed for us
3174+ * automatically.
3175+ *
3176+ * We must create the session before any credentials are stored
3177+ * (including AFS pags, which happens a few lines below).
3178+ */
3179+ {
3180+ OSStatus err = 0;
3181+ SecuritySessionId sid = 0;
3182+ SessionAttributeBits sattrs = 0;
3183+
3184+ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
3185+ if (err)
3186+ error("SessionGetInfo() failed with error %.8X",
3187+ (unsigned) err);
3188+ else
3189+ debug("Current Session ID is %.8X / Session Attributes are %.8X",
3190+ (unsigned) sid, (unsigned) sattrs);
3191+
3192+ if (inetd_flag && !(sattrs & sessionIsRoot))
3193+ debug("Running in inetd mode in a non-root session... "
3194+ "assuming inetd created the session for us.");
3195+ else {
3196+ debug("Creating new security session...");
3197+ err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
3198+ if (err)
3199+ error("SessionCreate() failed with error %.8X",
3200+ (unsigned) err);
3201+
3202+ err = SessionGetInfo(callerSecuritySession, &sid,
3203+ &sattrs);
3204+ if (err)
3205+ error("SessionGetInfo() failed with error %.8X",
3206+ (unsigned) err);
3207+ else
3208+ debug("New Session ID is %.8X / Session Attributes are %.8X",
3209+ (unsigned) sid, (unsigned) sattrs);
3210+ }
3211+ }
3212+#endif
3213+
3214 /*
3215 * We don't want to listen forever unless the other side
3216 * successfully authenticates itself. So we set up an alarm which is
3217@@ -2287,6 +2348,48 @@ do_ssh2_kex(void)
3218 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
3219 list_hostkey_types());
3220
3221+#ifdef GSSAPI
3222+ {
3223+ char *orig;
3224+ char *gss = NULL;
3225+ char *newstr = NULL;
3226+ orig = myproposal[PROPOSAL_KEX_ALGS];
3227+
3228+ /*
3229+ * If we don't have a host key, then there's no point advertising
3230+ * the other key exchange algorithms
3231+ */
3232+
3233+ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
3234+ orig = NULL;
3235+
3236+ if (options.gss_keyex)
3237+ gss = ssh_gssapi_server_mechanisms();
3238+ else
3239+ gss = NULL;
3240+
3241+ if (gss && orig)
3242+ xasprintf(&newstr, "%s,%s", gss, orig);
3243+ else if (gss)
3244+ newstr = gss;
3245+ else if (orig)
3246+ newstr = orig;
3247+
3248+ /*
3249+ * If we've got GSSAPI mechanisms, then we've got the 'null' host
3250+ * key alg, but we can't tell people about it unless its the only
3251+ * host key algorithm we support
3252+ */
3253+ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
3254+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
3255+
3256+ if (newstr)
3257+ myproposal[PROPOSAL_KEX_ALGS] = newstr;
3258+ else
3259+ fatal("No supported key exchange algorithms");
3260+ }
3261+#endif
3262+
3263 /* start key exchange */
3264 if ((r = kex_setup(active_state, myproposal)) != 0)
3265 fatal("kex_setup: %s", ssh_err(r));
3266@@ -2304,6 +2407,13 @@ do_ssh2_kex(void)
3267 # endif
3268 #endif
3269 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
3270+#ifdef GSSAPI
3271+ if (options.gss_keyex) {
3272+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
3273+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
3274+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
3275+ }
3276+#endif
3277 kex->server = 1;
3278 kex->client_version_string=client_version_string;
3279 kex->server_version_string=server_version_string;
3280diff --git a/sshd_config b/sshd_config
3281index 19b7c91a1..2c48105f8 100644
3282--- a/sshd_config
3283+++ b/sshd_config
3284@@ -69,6 +69,8 @@ AuthorizedKeysFile .ssh/authorized_keys
3285 # GSSAPI options
3286 #GSSAPIAuthentication no
3287 #GSSAPICleanupCredentials yes
3288+#GSSAPIStrictAcceptorCheck yes
3289+#GSSAPIKeyExchange no
3290
3291 # Set this to 'yes' to enable PAM authentication, account processing,
3292 # and session processing. If this is enabled, PAM authentication will
3293diff --git a/sshd_config.5 b/sshd_config.5
3294index c6484370b..985eef5a2 100644
3295--- a/sshd_config.5
3296+++ b/sshd_config.5
3297@@ -648,6 +648,11 @@ The default is
3298 Specifies whether user authentication based on GSSAPI is allowed.
3299 The default is
3300 .Cm no .
3301+.It Cm GSSAPIKeyExchange
3302+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
3303+doesn't rely on ssh keys to verify host identity.
3304+The default is
3305+.Cm no .
3306 .It Cm GSSAPICleanupCredentials
3307 Specifies whether to automatically destroy the user's credentials cache
3308 on logout.
3309@@ -667,6 +672,11 @@ machine's default store.
3310 This facility is provided to assist with operation on multi homed machines.
3311 The default is
3312 .Cm yes .
3313+.It Cm GSSAPIStoreCredentialsOnRekey
3314+Controls whether the user's GSSAPI credentials should be updated following a
3315+successful connection rekeying. This option can be used to accepted renewed
3316+or updated credentials from a compatible client. The default is
3317+.Cm no .
3318 .It Cm HostbasedAcceptedKeyTypes
3319 Specifies the key types that will be accepted for hostbased authentication
3320 as a list of comma-separated patterns.
3321diff --git a/sshkey.c b/sshkey.c
3322index 6555c5ef8..a85c185fc 100644
3323--- a/sshkey.c
3324+++ b/sshkey.c
3325@@ -135,6 +135,7 @@ static const struct keytype keytypes[] = {
3326 # endif /* OPENSSL_HAS_NISTP521 */
3327 # endif /* OPENSSL_HAS_ECC */
3328 #endif /* WITH_OPENSSL */
3329+ { "null", "null", NULL, KEY_NULL, 0, 0, 0 },
3330 { NULL, NULL, NULL, -1, -1, 0, 0 }
3331 };
3332
3333@@ -223,7 +224,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
3334 const struct keytype *kt;
3335
3336 for (kt = keytypes; kt->type != -1; kt++) {
3337- if (kt->name == NULL)
3338+ if (kt->name == NULL || kt->type == KEY_NULL)
3339 continue;
3340 if (!include_sigonly && kt->sigonly)
3341 continue;
3342diff --git a/sshkey.h b/sshkey.h
3343index f6a007fdf..f54deb0c0 100644
3344--- a/sshkey.h
3345+++ b/sshkey.h
3346@@ -64,6 +64,7 @@ enum sshkey_types {
3347 KEY_ED25519_CERT,
3348 KEY_XMSS,
3349 KEY_XMSS_CERT,
3350+ KEY_NULL,
3351 KEY_UNSPEC
3352 };
3353