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