summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2016-03-10 13:00:39 +0000
commit6dfd41bb6858c6446c1da47449e2108fbabf220e (patch)
treeff5693073f78b4cc1ba6c92c8280f4ec80ced188
parentf0329aac23c61e1a5197d6d57349a63f459bccb0 (diff)
GSSAPI key exchange support
This patch has been rejected upstream: "None of the OpenSSH developers are in favour of adding this, and this situation has not changed for several years. This is not a slight on Simon's patch, which is of fine quality, but just that a) we don't trust GSSAPI implementations that much and b) we don't like adding new KEX since they are pre-auth attack surface. This one is particularly scary, since it requires hooks out to typically root-owned system resources." However, quite a lot of people rely on this in Debian, and it's better to have it merged into the main openssh package rather than having separate -krb5 packages (as we used to have). It seems to have a generally good security history. Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 Last-Updated: 2016-01-04 Patch-Name: gssapi.patch
-rw-r--r--ChangeLog.gssapi113
-rw-r--r--Makefile.in3
-rw-r--r--auth-krb5.c17
-rw-r--r--auth.c3
-rw-r--r--auth2-gss.c48
-rw-r--r--auth2.c2
-rw-r--r--clientloop.c15
-rw-r--r--config.h.in6
-rw-r--r--configure.ac24
-rw-r--r--gss-genr.c275
-rw-r--r--gss-serv-krb5.c85
-rw-r--r--gss-serv.c185
-rw-r--r--kex.c16
-rw-r--r--kex.h14
-rw-r--r--kexgssc.c336
-rw-r--r--kexgsss.c295
-rw-r--r--monitor.c108
-rw-r--r--monitor.h3
-rw-r--r--monitor_wrap.c47
-rw-r--r--monitor_wrap.h4
-rw-r--r--readconf.c42
-rw-r--r--readconf.h5
-rw-r--r--servconf.c28
-rw-r--r--servconf.h2
-rw-r--r--ssh-gss.h41
-rw-r--r--ssh_config2
-rw-r--r--ssh_config.532
-rw-r--r--sshconnect2.c120
-rw-r--r--sshd.c110
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.510
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
33 files changed, 1951 insertions, 46 deletions
diff --git a/ChangeLog.gssapi b/ChangeLog.gssapi
new file mode 100644
index 000000000..f117a336a
--- /dev/null
+++ b/ChangeLog.gssapi
@@ -0,0 +1,113 @@
120110101
2 - Finally update for OpenSSH 5.6p1
3 - Add GSSAPIServerIdentity option from Jim Basney
4
520100308
6 - [ Makefile.in, key.c, key.h ]
7 Updates for OpenSSH 5.4p1
8 - [ servconf.c ]
9 Include GSSAPI options in the sshd -T configuration dump, and flag
10 some older configuration options as being unsupported. Thanks to Colin
11 Watson.
12 -
13
1420100124
15 - [ sshconnect2.c ]
16 Adapt to deal with additional element in Authmethod structure. Thanks to
17 Colin Watson
18
1920090615
20 - [ gss-genr.c gss-serv.c kexgssc.c kexgsss.c monitor.c sshconnect2.c
21 sshd.c ]
22 Fix issues identified by Greg Hudson following a code review
23 Check return value of gss_indicate_mechs
24 Protect GSSAPI calls in monitor, so they can only be used if enabled
25 Check return values of bignum functions in key exchange
26 Use BN_clear_free to clear other side's DH value
27 Make ssh_gssapi_id_kex more robust
28 Only configure kex table pointers if GSSAPI is enabled
29 Don't leak mechanism list, or gss mechanism list
30 Cast data.length before printing
31 If serverkey isn't provided, use an empty string, rather than NULL
32
3320090201
34 - [ gss-genr.c gss-serv.c kex.h kexgssc.c readconf.c readconf.h ssh-gss.h
35 ssh_config.5 sshconnet2.c ]
36 Add support for the GSSAPIClientIdentity option, which allows the user
37 to specify which GSSAPI identity to use to contact a given server
38
3920080404
40 - [ gss-serv.c ]
41 Add code to actually implement GSSAPIStrictAcceptCheck, which had somehow
42 been omitted from a previous version of this patch. Reported by Borislav
43 Stoichkov
44
4520070317
46 - [ gss-serv-krb5.c ]
47 Remove C99ism, where new_ccname was being declared in the middle of a
48 function
49
5020061220
51 - [ servconf.c ]
52 Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and
53 documented, behaviour. Reported by Dan Watson.
54
5520060910
56 - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c
57 ssh-gss.h ]
58 add support for gss-group14-sha1 key exchange mechanisms
59 - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ]
60 Add GSSAPIStrictAcceptorCheck option to allow the disabling of
61 acceptor principal checking on multi-homed machines.
62 <Bugzilla #928>
63 - [ sshd_config ssh_config ]
64 Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample
65 configuration files
66 - [ kexgss.c kegsss.c sshconnect2.c sshd.c ]
67 Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf()
68 Limit length of error messages displayed by client
69
7020060909
71 - [ gss-genr.c gss-serv.c ]
72 move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server
73 only, where they belong
74 <Bugzilla #1225>
75
7620060829
77 - [ gss-serv-krb5.c ]
78 Fix CCAPI credentials cache name when creating KRB5CCNAME environment
79 variable
80
8120060828
82 - [ gss-genr.c ]
83 Avoid Heimdal context freeing problem
84 <Fixed upstream 20060829>
85
8620060818
87 - [ gss-genr.c ssh-gss.h sshconnect2.c ]
88 Make sure that SPENGO is disabled
89 <Bugzilla #1218 - Fixed upstream 20060818>
90
9120060421
92 - [ gssgenr.c, sshconnect2.c ]
93 a few type changes (signed versus unsigned, int versus size_t) to
94 fix compiler errors/warnings
95 (from jbasney AT ncsa.uiuc.edu)
96 - [ kexgssc.c, sshconnect2.c ]
97 fix uninitialized variable warnings
98 (from jbasney AT ncsa.uiuc.edu)
99 - [ gssgenr.c ]
100 pass oid to gss_display_status (helpful when using GSSAPI mechglue)
101 (from jbasney AT ncsa.uiuc.edu)
102 <Bugzilla #1220 >
103 - [ gss-serv-krb5.c ]
104 #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H
105 (from jbasney AT ncsa.uiuc.edu)
106 <Fixed upstream 20060304>
107 - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c
108 add client-side GssapiKeyExchange option
109 (from jbasney AT ncsa.uiuc.edu)
110 - [ sshconnect2.c ]
111 add support for GssapiTrustDns option for gssapi-with-mic
112 (from jbasney AT ncsa.uiuc.edu)
113 <gssapi-with-mic support is Bugzilla #1008>
diff --git a/Makefile.in b/Makefile.in
index d401787db..0954c631d 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -92,6 +92,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
92 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ 92 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
93 kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ 93 kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \
94 kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ 94 kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \
95 kexgssc.o \
95 platform-pledge.o 96 platform-pledge.o
96 97
97SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ 98SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
@@ -105,7 +106,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
105 auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ 106 auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
106 auth2-none.o auth2-passwd.o auth2-pubkey.o \ 107 auth2-none.o auth2-passwd.o auth2-pubkey.o \
107 monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \ 108 monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \
108 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 109 auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
109 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ 110 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
110 sftp-server.o sftp-common.o \ 111 sftp-server.o sftp-common.o \
111 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ 112 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
diff --git a/auth-krb5.c b/auth-krb5.c
index d1c5a2f32..f019fb1a1 100644
--- a/auth-krb5.c
+++ b/auth-krb5.c
@@ -183,8 +183,13 @@ auth_krb5_password(Authctxt *authctxt, const char *password)
183 183
184 len = strlen(authctxt->krb5_ticket_file) + 6; 184 len = strlen(authctxt->krb5_ticket_file) + 6;
185 authctxt->krb5_ccname = xmalloc(len); 185 authctxt->krb5_ccname = xmalloc(len);
186#ifdef USE_CCAPI
187 snprintf(authctxt->krb5_ccname, len, "API:%s",
188 authctxt->krb5_ticket_file);
189#else
186 snprintf(authctxt->krb5_ccname, len, "FILE:%s", 190 snprintf(authctxt->krb5_ccname, len, "FILE:%s",
187 authctxt->krb5_ticket_file); 191 authctxt->krb5_ticket_file);
192#endif
188 193
189#ifdef USE_PAM 194#ifdef USE_PAM
190 if (options.use_pam) 195 if (options.use_pam)
@@ -241,15 +246,22 @@ krb5_cleanup_proc(Authctxt *authctxt)
241#ifndef HEIMDAL 246#ifndef HEIMDAL
242krb5_error_code 247krb5_error_code
243ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { 248ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
244 int tmpfd, ret, oerrno; 249 int ret, oerrno;
245 char ccname[40]; 250 char ccname[40];
246 mode_t old_umask; 251 mode_t old_umask;
252#ifdef USE_CCAPI
253 char cctemplate[] = "API:krb5cc_%d";
254#else
255 char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX";
256 int tmpfd;
257#endif
247 258
248 ret = snprintf(ccname, sizeof(ccname), 259 ret = snprintf(ccname, sizeof(ccname),
249 "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); 260 cctemplate, geteuid());
250 if (ret < 0 || (size_t)ret >= sizeof(ccname)) 261 if (ret < 0 || (size_t)ret >= sizeof(ccname))
251 return ENOMEM; 262 return ENOMEM;
252 263
264#ifndef USE_CCAPI
253 old_umask = umask(0177); 265 old_umask = umask(0177);
254 tmpfd = mkstemp(ccname + strlen("FILE:")); 266 tmpfd = mkstemp(ccname + strlen("FILE:"));
255 oerrno = errno; 267 oerrno = errno;
@@ -266,6 +278,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
266 return oerrno; 278 return oerrno;
267 } 279 }
268 close(tmpfd); 280 close(tmpfd);
281#endif
269 282
270 return (krb5_cc_resolve(ctx, ccname, ccache)); 283 return (krb5_cc_resolve(ctx, ccname, ccache));
271} 284}
diff --git a/auth.c b/auth.c
index 214c2c708..bd6a026a1 100644
--- a/auth.c
+++ b/auth.c
@@ -354,7 +354,8 @@ auth_root_allowed(const char *method)
354 case PERMIT_NO_PASSWD: 354 case PERMIT_NO_PASSWD:
355 if (strcmp(method, "publickey") == 0 || 355 if (strcmp(method, "publickey") == 0 ||
356 strcmp(method, "hostbased") == 0 || 356 strcmp(method, "hostbased") == 0 ||
357 strcmp(method, "gssapi-with-mic") == 0) 357 strcmp(method, "gssapi-with-mic") == 0 ||
358 strcmp(method, "gssapi-keyex") == 0)
358 return 1; 359 return 1;
359 break; 360 break;
360 case PERMIT_FORCED_ONLY: 361 case PERMIT_FORCED_ONLY:
diff --git a/auth2-gss.c b/auth2-gss.c
index 1ca835773..3b5036dfd 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 markus Exp $ */ 1/* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 markus Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -53,6 +53,40 @@ static int input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
53static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); 53static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
54static int input_gssapi_errtok(int, u_int32_t, void *); 54static int input_gssapi_errtok(int, u_int32_t, void *);
55 55
56/*
57 * The 'gssapi_keyex' userauth mechanism.
58 */
59static int
60userauth_gsskeyex(Authctxt *authctxt)
61{
62 int authenticated = 0;
63 Buffer b;
64 gss_buffer_desc mic, gssbuf;
65 u_int len;
66
67 mic.value = packet_get_string(&len);
68 mic.length = len;
69
70 packet_check_eom();
71
72 ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
73 "gssapi-keyex");
74
75 gssbuf.value = buffer_ptr(&b);
76 gssbuf.length = buffer_len(&b);
77
78 /* gss_kex_context is NULL with privsep, so we can't check it here */
79 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
80 &gssbuf, &mic))))
81 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
82 authctxt->pw));
83
84 buffer_free(&b);
85 free(mic.value);
86
87 return (authenticated);
88}
89
56/* 90/*
57 * We only support those mechanisms that we know about (ie ones that we know 91 * We only support those mechanisms that we know about (ie ones that we know
58 * how to check local user kuserok and the like) 92 * how to check local user kuserok and the like)
@@ -238,7 +272,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
238 272
239 packet_check_eom(); 273 packet_check_eom();
240 274
241 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 275 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
276 authctxt->pw));
242 277
243 authctxt->postponed = 0; 278 authctxt->postponed = 0;
244 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 279 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
@@ -274,7 +309,8 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
274 gssbuf.length = buffer_len(&b); 309 gssbuf.length = buffer_len(&b);
275 310
276 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) 311 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
277 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 312 authenticated =
313 PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
278 else 314 else
279 logit("GSSAPI MIC check failed"); 315 logit("GSSAPI MIC check failed");
280 316
@@ -290,6 +326,12 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
290 return 0; 326 return 0;
291} 327}
292 328
329Authmethod method_gsskeyex = {
330 "gssapi-keyex",
331 userauth_gsskeyex,
332 &options.gss_authentication
333};
334
293Authmethod method_gssapi = { 335Authmethod method_gssapi = {
294 "gssapi-with-mic", 336 "gssapi-with-mic",
295 userauth_gssapi, 337 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index 717796228..3f49bdca0 100644
--- a/auth2.c
+++ b/auth2.c
@@ -70,6 +70,7 @@ extern Authmethod method_passwd;
70extern Authmethod method_kbdint; 70extern Authmethod method_kbdint;
71extern Authmethod method_hostbased; 71extern Authmethod method_hostbased;
72#ifdef GSSAPI 72#ifdef GSSAPI
73extern Authmethod method_gsskeyex;
73extern Authmethod method_gssapi; 74extern Authmethod method_gssapi;
74#endif 75#endif
75 76
@@ -77,6 +78,7 @@ Authmethod *authmethods[] = {
77 &method_none, 78 &method_none,
78 &method_pubkey, 79 &method_pubkey,
79#ifdef GSSAPI 80#ifdef GSSAPI
81 &method_gsskeyex,
80 &method_gssapi, 82 &method_gssapi,
81#endif 83#endif
82 &method_passwd, 84 &method_passwd,
diff --git a/clientloop.c b/clientloop.c
index 9820455c4..1567e4a2f 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -114,6 +114,10 @@
114#include "ssherr.h" 114#include "ssherr.h"
115#include "hostfile.h" 115#include "hostfile.h"
116 116
117#ifdef GSSAPI
118#include "ssh-gss.h"
119#endif
120
117/* import options */ 121/* import options */
118extern Options options; 122extern Options options;
119 123
@@ -1662,9 +1666,18 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1662 break; 1666 break;
1663 1667
1664 /* Do channel operations unless rekeying in progress. */ 1668 /* Do channel operations unless rekeying in progress. */
1665 if (!ssh_packet_is_rekeying(active_state)) 1669 if (!ssh_packet_is_rekeying(active_state)) {
1666 channel_after_select(readset, writeset); 1670 channel_after_select(readset, writeset);
1667 1671
1672#ifdef GSSAPI
1673 if (options.gss_renewal_rekey &&
1674 ssh_gssapi_credentials_updated(NULL)) {
1675 debug("credentials updated - forcing rekey");
1676 need_rekeying = 1;
1677 }
1678#endif
1679 }
1680
1668 /* Buffer input from the connection. */ 1681 /* Buffer input from the connection. */
1669 client_process_net_input(readset); 1682 client_process_net_input(readset);
1670 1683
diff --git a/config.h.in b/config.h.in
index 89bf1b0ff..621c1396e 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1641,6 +1641,9 @@
1641/* Use btmp to log bad logins */ 1641/* Use btmp to log bad logins */
1642#undef USE_BTMP 1642#undef USE_BTMP
1643 1643
1644/* platform uses an in-memory credentials cache */
1645#undef USE_CCAPI
1646
1644/* Use libedit for sftp */ 1647/* Use libedit for sftp */
1645#undef USE_LIBEDIT 1648#undef USE_LIBEDIT
1646 1649
@@ -1656,6 +1659,9 @@
1656/* Use PIPES instead of a socketpair() */ 1659/* Use PIPES instead of a socketpair() */
1657#undef USE_PIPES 1660#undef USE_PIPES
1658 1661
1662/* platform has the Security Authorization Session API */
1663#undef USE_SECURITY_SESSION_API
1664
1659/* Define if you have Solaris privileges */ 1665/* Define if you have Solaris privileges */
1660#undef USE_SOLARIS_PRIVS 1666#undef USE_SOLARIS_PRIVS
1661 1667
diff --git a/configure.ac b/configure.ac
index 7258cc0e5..5f1ff740f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -632,6 +632,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
632 [Use tunnel device compatibility to OpenBSD]) 632 [Use tunnel device compatibility to OpenBSD])
633 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 633 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
634 [Prepend the address family to IP tunnel traffic]) 634 [Prepend the address family to IP tunnel traffic])
635 AC_MSG_CHECKING([if we have the Security Authorization Session API])
636 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
637 [SessionCreate(0, 0);],
638 [ac_cv_use_security_session_api="yes"
639 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
640 [platform has the Security Authorization Session API])
641 LIBS="$LIBS -framework Security"
642 AC_MSG_RESULT([yes])],
643 [ac_cv_use_security_session_api="no"
644 AC_MSG_RESULT([no])])
645 AC_MSG_CHECKING([if we have an in-memory credentials cache])
646 AC_TRY_COMPILE(
647 [#include <Kerberos/Kerberos.h>],
648 [cc_context_t c;
649 (void) cc_initialize (&c, 0, NULL, NULL);],
650 [AC_DEFINE([USE_CCAPI], [1],
651 [platform uses an in-memory credentials cache])
652 LIBS="$LIBS -framework Security"
653 AC_MSG_RESULT([yes])
654 if test "x$ac_cv_use_security_session_api" = "xno"; then
655 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
656 fi],
657 [AC_MSG_RESULT([no])]
658 )
635 m4_pattern_allow([AU_IPv]) 659 m4_pattern_allow([AU_IPv])
636 AC_CHECK_DECL([AU_IPv4], [], 660 AC_CHECK_DECL([AU_IPv4], [],
637 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 661 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
diff --git a/gss-genr.c b/gss-genr.c
index d617d600a..b4eca3feb 100644
--- a/gss-genr.c
+++ b/gss-genr.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt Exp $ */ 1/* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -41,12 +41,167 @@
41#include "buffer.h" 41#include "buffer.h"
42#include "log.h" 42#include "log.h"
43#include "ssh2.h" 43#include "ssh2.h"
44#include "cipher.h"
45#include "key.h"
46#include "kex.h"
47#include <openssl/evp.h>
44 48
45#include "ssh-gss.h" 49#include "ssh-gss.h"
46 50
47extern u_char *session_id2; 51extern u_char *session_id2;
48extern u_int session_id2_len; 52extern u_int session_id2_len;
49 53
54typedef struct {
55 char *encoded;
56 gss_OID oid;
57} ssh_gss_kex_mapping;
58
59/*
60 * XXX - It would be nice to find a more elegant way of handling the
61 * XXX passing of the key exchange context to the userauth routines
62 */
63
64Gssctxt *gss_kex_context = NULL;
65
66static ssh_gss_kex_mapping *gss_enc2oid = NULL;
67
68int
69ssh_gssapi_oid_table_ok(void) {
70 return (gss_enc2oid != NULL);
71}
72
73/*
74 * Return a list of the gss-group1-sha1 mechanisms supported by this program
75 *
76 * We test mechanisms to ensure that we can use them, to avoid starting
77 * a key exchange with a bad mechanism
78 */
79
80char *
81ssh_gssapi_client_mechanisms(const char *host, const char *client) {
82 gss_OID_set gss_supported;
83 OM_uint32 min_status;
84
85 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
86 return NULL;
87
88 return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
89 host, client));
90}
91
92char *
93ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
94 const char *host, const char *client) {
95 Buffer buf;
96 size_t i;
97 int oidpos, enclen;
98 char *mechs, *encoded;
99 u_char digest[EVP_MAX_MD_SIZE];
100 char deroid[2];
101 const EVP_MD *evp_md = EVP_md5();
102 EVP_MD_CTX md;
103
104 if (gss_enc2oid != NULL) {
105 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
106 free(gss_enc2oid[i].encoded);
107 free(gss_enc2oid);
108 }
109
110 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
111 (gss_supported->count + 1));
112
113 buffer_init(&buf);
114
115 oidpos = 0;
116 for (i = 0; i < gss_supported->count; i++) {
117 if (gss_supported->elements[i].length < 128 &&
118 (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
119
120 deroid[0] = SSH_GSS_OIDTYPE;
121 deroid[1] = gss_supported->elements[i].length;
122
123 EVP_DigestInit(&md, evp_md);
124 EVP_DigestUpdate(&md, deroid, 2);
125 EVP_DigestUpdate(&md,
126 gss_supported->elements[i].elements,
127 gss_supported->elements[i].length);
128 EVP_DigestFinal(&md, digest, NULL);
129
130 encoded = xmalloc(EVP_MD_size(evp_md) * 2);
131 enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
132 encoded, EVP_MD_size(evp_md) * 2);
133
134 if (oidpos != 0)
135 buffer_put_char(&buf, ',');
136
137 buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
138 sizeof(KEX_GSS_GEX_SHA1_ID) - 1);
139 buffer_append(&buf, encoded, enclen);
140 buffer_put_char(&buf, ',');
141 buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID,
142 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1);
143 buffer_append(&buf, encoded, enclen);
144 buffer_put_char(&buf, ',');
145 buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
146 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1);
147 buffer_append(&buf, encoded, enclen);
148
149 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
150 gss_enc2oid[oidpos].encoded = encoded;
151 oidpos++;
152 }
153 }
154 gss_enc2oid[oidpos].oid = NULL;
155 gss_enc2oid[oidpos].encoded = NULL;
156
157 buffer_put_char(&buf, '\0');
158
159 mechs = xmalloc(buffer_len(&buf));
160 buffer_get(&buf, mechs, buffer_len(&buf));
161 buffer_free(&buf);
162
163 if (strlen(mechs) == 0) {
164 free(mechs);
165 mechs = NULL;
166 }
167
168 return (mechs);
169}
170
171gss_OID
172ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
173 int i = 0;
174
175 switch (kex_type) {
176 case KEX_GSS_GRP1_SHA1:
177 if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
178 return GSS_C_NO_OID;
179 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
180 break;
181 case KEX_GSS_GRP14_SHA1:
182 if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
183 return GSS_C_NO_OID;
184 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
185 break;
186 case KEX_GSS_GEX_SHA1:
187 if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
188 return GSS_C_NO_OID;
189 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
190 break;
191 default:
192 return GSS_C_NO_OID;
193 }
194
195 while (gss_enc2oid[i].encoded != NULL &&
196 strcmp(name, gss_enc2oid[i].encoded) != 0)
197 i++;
198
199 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
200 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
201
202 return gss_enc2oid[i].oid;
203}
204
50/* Check that the OID in a data stream matches that in the context */ 205/* Check that the OID in a data stream matches that in the context */
51int 206int
52ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 207ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -199,7 +354,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
199 } 354 }
200 355
201 ctx->major = gss_init_sec_context(&ctx->minor, 356 ctx->major = gss_init_sec_context(&ctx->minor,
202 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 357 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
203 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 358 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
204 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 359 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
205 360
@@ -229,8 +384,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
229} 384}
230 385
231OM_uint32 386OM_uint32
387ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
388{
389 gss_buffer_desc gssbuf;
390 gss_name_t gssname;
391 OM_uint32 status;
392 gss_OID_set oidset;
393
394 gssbuf.value = (void *) name;
395 gssbuf.length = strlen(gssbuf.value);
396
397 gss_create_empty_oid_set(&status, &oidset);
398 gss_add_oid_set_member(&status, ctx->oid, &oidset);
399
400 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
401 GSS_C_NT_USER_NAME, &gssname);
402
403 if (!ctx->major)
404 ctx->major = gss_acquire_cred(&ctx->minor,
405 gssname, 0, oidset, GSS_C_INITIATE,
406 &ctx->client_creds, NULL, NULL);
407
408 gss_release_name(&status, &gssname);
409 gss_release_oid_set(&status, &oidset);
410
411 if (ctx->major)
412 ssh_gssapi_error(ctx);
413
414 return(ctx->major);
415}
416
417OM_uint32
232ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 418ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
233{ 419{
420 if (ctx == NULL)
421 return -1;
422
234 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 423 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
235 GSS_C_QOP_DEFAULT, buffer, hash))) 424 GSS_C_QOP_DEFAULT, buffer, hash)))
236 ssh_gssapi_error(ctx); 425 ssh_gssapi_error(ctx);
@@ -238,6 +427,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
238 return (ctx->major); 427 return (ctx->major);
239} 428}
240 429
430/* Priviledged when used by server */
431OM_uint32
432ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
433{
434 if (ctx == NULL)
435 return -1;
436
437 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
438 gssbuf, gssmic, NULL);
439
440 return (ctx->major);
441}
442
241void 443void
242ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, 444ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
243 const char *context) 445 const char *context)
@@ -251,11 +453,16 @@ ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
251} 453}
252 454
253int 455int
254ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 456ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
457 const char *client)
255{ 458{
256 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 459 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
257 OM_uint32 major, minor; 460 OM_uint32 major, minor;
258 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 461 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
462 Gssctxt *intctx = NULL;
463
464 if (ctx == NULL)
465 ctx = &intctx;
259 466
260 /* RFC 4462 says we MUST NOT do SPNEGO */ 467 /* RFC 4462 says we MUST NOT do SPNEGO */
261 if (oid->length == spnego_oid.length && 468 if (oid->length == spnego_oid.length &&
@@ -265,6 +472,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
265 ssh_gssapi_build_ctx(ctx); 472 ssh_gssapi_build_ctx(ctx);
266 ssh_gssapi_set_oid(*ctx, oid); 473 ssh_gssapi_set_oid(*ctx, oid);
267 major = ssh_gssapi_import_name(*ctx, host); 474 major = ssh_gssapi_import_name(*ctx, host);
475
476 if (!GSS_ERROR(major) && client)
477 major = ssh_gssapi_client_identity(*ctx, client);
478
268 if (!GSS_ERROR(major)) { 479 if (!GSS_ERROR(major)) {
269 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 480 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
270 NULL); 481 NULL);
@@ -274,10 +485,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
274 GSS_C_NO_BUFFER); 485 GSS_C_NO_BUFFER);
275 } 486 }
276 487
277 if (GSS_ERROR(major)) 488 if (GSS_ERROR(major) || intctx != NULL)
278 ssh_gssapi_delete_ctx(ctx); 489 ssh_gssapi_delete_ctx(ctx);
279 490
280 return (!GSS_ERROR(major)); 491 return (!GSS_ERROR(major));
281} 492}
282 493
494int
495ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
496 static gss_name_t saved_name = GSS_C_NO_NAME;
497 static OM_uint32 saved_lifetime = 0;
498 static gss_OID saved_mech = GSS_C_NO_OID;
499 static gss_name_t name;
500 static OM_uint32 last_call = 0;
501 OM_uint32 lifetime, now, major, minor;
502 int equal;
503
504 now = time(NULL);
505
506 if (ctxt) {
507 debug("Rekey has happened - updating saved versions");
508
509 if (saved_name != GSS_C_NO_NAME)
510 gss_release_name(&minor, &saved_name);
511
512 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
513 &saved_name, &saved_lifetime, NULL, NULL);
514
515 if (!GSS_ERROR(major)) {
516 saved_mech = ctxt->oid;
517 saved_lifetime+= now;
518 } else {
519 /* Handle the error */
520 }
521 return 0;
522 }
523
524 if (now - last_call < 10)
525 return 0;
526
527 last_call = now;
528
529 if (saved_mech == GSS_C_NO_OID)
530 return 0;
531
532 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
533 &name, &lifetime, NULL, NULL);
534 if (major == GSS_S_CREDENTIALS_EXPIRED)
535 return 0;
536 else if (GSS_ERROR(major))
537 return 0;
538
539 major = gss_compare_name(&minor, saved_name, name, &equal);
540 gss_release_name(&minor, &name);
541 if (GSS_ERROR(major))
542 return 0;
543
544 if (equal && (saved_lifetime < lifetime + now - 10))
545 return 1;
546
547 return 0;
548}
549
283#endif /* GSSAPI */ 550#endif /* GSSAPI */
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
index 795992d9f..fd8b37183 100644
--- a/gss-serv-krb5.c
+++ b/gss-serv-krb5.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv-krb5.c,v 1.8 2013/07/20 01:55:13 djm Exp $ */ 1/* $OpenBSD: gss-serv-krb5.c,v 1.8 2013/07/20 01:55:13 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -121,8 +121,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
121 krb5_error_code problem; 121 krb5_error_code problem;
122 krb5_principal princ; 122 krb5_principal princ;
123 OM_uint32 maj_status, min_status; 123 OM_uint32 maj_status, min_status;
124 int len;
125 const char *errmsg; 124 const char *errmsg;
125 const char *new_ccname;
126 126
127 if (client->creds == NULL) { 127 if (client->creds == NULL) {
128 debug("No credentials stored"); 128 debug("No credentials stored");
@@ -181,11 +181,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
181 return; 181 return;
182 } 182 }
183 183
184 client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); 184 new_ccname = krb5_cc_get_name(krb_context, ccache);
185
185 client->store.envvar = "KRB5CCNAME"; 186 client->store.envvar = "KRB5CCNAME";
186 len = strlen(client->store.filename) + 6; 187#ifdef USE_CCAPI
187 client->store.envval = xmalloc(len); 188 xasprintf(&client->store.envval, "API:%s", new_ccname);
188 snprintf(client->store.envval, len, "FILE:%s", client->store.filename); 189 client->store.filename = NULL;
190#else
191 xasprintf(&client->store.envval, "FILE:%s", new_ccname);
192 client->store.filename = xstrdup(new_ccname);
193#endif
189 194
190#ifdef USE_PAM 195#ifdef USE_PAM
191 if (options.use_pam) 196 if (options.use_pam)
@@ -197,6 +202,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
197 return; 202 return;
198} 203}
199 204
205int
206ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
207 ssh_gssapi_client *client)
208{
209 krb5_ccache ccache = NULL;
210 krb5_principal principal = NULL;
211 char *name = NULL;
212 krb5_error_code problem;
213 OM_uint32 maj_status, min_status;
214
215 if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
216 logit("krb5_cc_resolve(): %.100s",
217 krb5_get_err_text(krb_context, problem));
218 return 0;
219 }
220
221 /* Find out who the principal in this cache is */
222 if ((problem = krb5_cc_get_principal(krb_context, ccache,
223 &principal))) {
224 logit("krb5_cc_get_principal(): %.100s",
225 krb5_get_err_text(krb_context, problem));
226 krb5_cc_close(krb_context, ccache);
227 return 0;
228 }
229
230 if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
231 logit("krb5_unparse_name(): %.100s",
232 krb5_get_err_text(krb_context, problem));
233 krb5_free_principal(krb_context, principal);
234 krb5_cc_close(krb_context, ccache);
235 return 0;
236 }
237
238
239 if (strcmp(name,client->exportedname.value)!=0) {
240 debug("Name in local credentials cache differs. Not storing");
241 krb5_free_principal(krb_context, principal);
242 krb5_cc_close(krb_context, ccache);
243 krb5_free_unparsed_name(krb_context, name);
244 return 0;
245 }
246 krb5_free_unparsed_name(krb_context, name);
247
248 /* Name matches, so lets get on with it! */
249
250 if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
251 logit("krb5_cc_initialize(): %.100s",
252 krb5_get_err_text(krb_context, problem));
253 krb5_free_principal(krb_context, principal);
254 krb5_cc_close(krb_context, ccache);
255 return 0;
256 }
257
258 krb5_free_principal(krb_context, principal);
259
260 if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
261 ccache))) {
262 logit("gss_krb5_copy_ccache() failed. Sorry!");
263 krb5_cc_close(krb_context, ccache);
264 return 0;
265 }
266
267 return 1;
268}
269
200ssh_gssapi_mech gssapi_kerberos_mech = { 270ssh_gssapi_mech gssapi_kerberos_mech = {
201 "toWM5Slw5Ew8Mqkay+al2g==", 271 "toWM5Slw5Ew8Mqkay+al2g==",
202 "Kerberos", 272 "Kerberos",
@@ -204,7 +274,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
204 NULL, 274 NULL,
205 &ssh_gssapi_krb5_userok, 275 &ssh_gssapi_krb5_userok,
206 NULL, 276 NULL,
207 &ssh_gssapi_krb5_storecreds 277 &ssh_gssapi_krb5_storecreds,
278 &ssh_gssapi_krb5_updatecreds
208}; 279};
209 280
210#endif /* KRB5 */ 281#endif /* KRB5 */
diff --git a/gss-serv.c b/gss-serv.c
index 53993d674..2f6baf70d 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -45,17 +45,22 @@
45#include "session.h" 45#include "session.h"
46#include "misc.h" 46#include "misc.h"
47#include "servconf.h" 47#include "servconf.h"
48#include "uidswap.h"
48 49
49#include "ssh-gss.h" 50#include "ssh-gss.h"
51#include "monitor_wrap.h"
52
53extern ServerOptions options;
50 54
51extern ServerOptions options; 55extern ServerOptions options;
52 56
53static ssh_gssapi_client gssapi_client = 57static ssh_gssapi_client gssapi_client =
54 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 58 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
55 GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; 59 GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL,
60 {NULL, NULL, NULL, NULL, NULL}, 0, 0};
56 61
57ssh_gssapi_mech gssapi_null_mech = 62ssh_gssapi_mech gssapi_null_mech =
58 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; 63 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
59 64
60#ifdef KRB5 65#ifdef KRB5
61extern ssh_gssapi_mech gssapi_kerberos_mech; 66extern ssh_gssapi_mech gssapi_kerberos_mech;
@@ -142,6 +147,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
142} 147}
143 148
144/* Unprivileged */ 149/* Unprivileged */
150char *
151ssh_gssapi_server_mechanisms(void) {
152 gss_OID_set supported;
153
154 ssh_gssapi_supported_oids(&supported);
155 return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
156 NULL, NULL));
157}
158
159/* Unprivileged */
160int
161ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
162 const char *dummy) {
163 Gssctxt *ctx = NULL;
164 int res;
165
166 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
167 ssh_gssapi_delete_ctx(&ctx);
168
169 return (res);
170}
171
172/* Unprivileged */
145void 173void
146ssh_gssapi_supported_oids(gss_OID_set *oidset) 174ssh_gssapi_supported_oids(gss_OID_set *oidset)
147{ 175{
@@ -151,7 +179,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
151 gss_OID_set supported; 179 gss_OID_set supported;
152 180
153 gss_create_empty_oid_set(&min_status, oidset); 181 gss_create_empty_oid_set(&min_status, oidset);
154 gss_indicate_mechs(&min_status, &supported); 182
183 if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
184 return;
155 185
156 while (supported_mechs[i]->name != NULL) { 186 while (supported_mechs[i]->name != NULL) {
157 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 187 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -277,8 +307,48 @@ OM_uint32
277ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 307ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
278{ 308{
279 int i = 0; 309 int i = 0;
310 int equal = 0;
311 gss_name_t new_name = GSS_C_NO_NAME;
312 gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
313
314 if (options.gss_store_rekey && client->used && ctx->client_creds) {
315 if (client->mech->oid.length != ctx->oid->length ||
316 (memcmp(client->mech->oid.elements,
317 ctx->oid->elements, ctx->oid->length) !=0)) {
318 debug("Rekeyed credentials have different mechanism");
319 return GSS_S_COMPLETE;
320 }
321
322 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
323 ctx->client_creds, ctx->oid, &new_name,
324 NULL, NULL, NULL))) {
325 ssh_gssapi_error(ctx);
326 return (ctx->major);
327 }
328
329 ctx->major = gss_compare_name(&ctx->minor, client->name,
330 new_name, &equal);
331
332 if (GSS_ERROR(ctx->major)) {
333 ssh_gssapi_error(ctx);
334 return (ctx->major);
335 }
336
337 if (!equal) {
338 debug("Rekeyed credentials have different name");
339 return GSS_S_COMPLETE;
340 }
280 341
281 gss_buffer_desc ename; 342 debug("Marking rekeyed credentials for export");
343
344 gss_release_name(&ctx->minor, &client->name);
345 gss_release_cred(&ctx->minor, &client->creds);
346 client->name = new_name;
347 client->creds = ctx->client_creds;
348 ctx->client_creds = GSS_C_NO_CREDENTIAL;
349 client->updated = 1;
350 return GSS_S_COMPLETE;
351 }
282 352
283 client->mech = NULL; 353 client->mech = NULL;
284 354
@@ -293,6 +363,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
293 if (client->mech == NULL) 363 if (client->mech == NULL)
294 return GSS_S_FAILURE; 364 return GSS_S_FAILURE;
295 365
366 if (ctx->client_creds &&
367 (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
368 ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
369 ssh_gssapi_error(ctx);
370 return (ctx->major);
371 }
372
296 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 373 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
297 &client->displayname, NULL))) { 374 &client->displayname, NULL))) {
298 ssh_gssapi_error(ctx); 375 ssh_gssapi_error(ctx);
@@ -310,6 +387,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
310 return (ctx->major); 387 return (ctx->major);
311 } 388 }
312 389
390 gss_release_buffer(&ctx->minor, &ename);
391
313 /* We can't copy this structure, so we just move the pointer to it */ 392 /* We can't copy this structure, so we just move the pointer to it */
314 client->creds = ctx->client_creds; 393 client->creds = ctx->client_creds;
315 ctx->client_creds = GSS_C_NO_CREDENTIAL; 394 ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -357,7 +436,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
357 436
358/* Privileged */ 437/* Privileged */
359int 438int
360ssh_gssapi_userok(char *user) 439ssh_gssapi_userok(char *user, struct passwd *pw)
361{ 440{
362 OM_uint32 lmin; 441 OM_uint32 lmin;
363 442
@@ -367,9 +446,11 @@ ssh_gssapi_userok(char *user)
367 return 0; 446 return 0;
368 } 447 }
369 if (gssapi_client.mech && gssapi_client.mech->userok) 448 if (gssapi_client.mech && gssapi_client.mech->userok)
370 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 449 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
450 gssapi_client.used = 1;
451 gssapi_client.store.owner = pw;
371 return 1; 452 return 1;
372 else { 453 } else {
373 /* Destroy delegated credentials if userok fails */ 454 /* Destroy delegated credentials if userok fails */
374 gss_release_buffer(&lmin, &gssapi_client.displayname); 455 gss_release_buffer(&lmin, &gssapi_client.displayname);
375 gss_release_buffer(&lmin, &gssapi_client.exportedname); 456 gss_release_buffer(&lmin, &gssapi_client.exportedname);
@@ -383,14 +464,90 @@ ssh_gssapi_userok(char *user)
383 return (0); 464 return (0);
384} 465}
385 466
386/* Privileged */ 467/* These bits are only used for rekeying. The unpriviledged child is running
387OM_uint32 468 * as the user, the monitor is root.
388ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 469 *
470 * In the child, we want to :
471 * *) Ask the monitor to store our credentials into the store we specify
472 * *) If it succeeds, maybe do a PAM update
473 */
474
475/* Stuff for PAM */
476
477#ifdef USE_PAM
478static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
479 struct pam_response **resp, void *data)
389{ 480{
390 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 481 return (PAM_CONV_ERR);
391 gssbuf, gssmic, NULL); 482}
483#endif
392 484
393 return (ctx->major); 485void
486ssh_gssapi_rekey_creds(void) {
487 int ok;
488 int ret;
489#ifdef USE_PAM
490 pam_handle_t *pamh = NULL;
491 struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
492 char *envstr;
493#endif
494
495 if (gssapi_client.store.filename == NULL &&
496 gssapi_client.store.envval == NULL &&
497 gssapi_client.store.envvar == NULL)
498 return;
499
500 ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
501
502 if (!ok)
503 return;
504
505 debug("Rekeyed credentials stored successfully");
506
507 /* Actually managing to play with the ssh pam stack from here will
508 * be next to impossible. In any case, we may want different options
509 * for rekeying. So, use our own :)
510 */
511#ifdef USE_PAM
512 if (!use_privsep) {
513 debug("Not even going to try and do PAM with privsep disabled");
514 return;
515 }
516
517 ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
518 &pamconv, &pamh);
519 if (ret)
520 return;
521
522 xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
523 gssapi_client.store.envval);
524
525 ret = pam_putenv(pamh, envstr);
526 if (!ret)
527 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
528 pam_end(pamh, PAM_SUCCESS);
529#endif
530}
531
532int
533ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
534 int ok = 0;
535
536 /* Check we've got credentials to store */
537 if (!gssapi_client.updated)
538 return 0;
539
540 gssapi_client.updated = 0;
541
542 temporarily_use_uid(gssapi_client.store.owner);
543 if (gssapi_client.mech && gssapi_client.mech->updatecreds)
544 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
545 else
546 debug("No update function for this mechanism");
547
548 restore_uid();
549
550 return ok;
394} 551}
395 552
396#endif 553#endif
diff --git a/kex.c b/kex.c
index d371f47c4..913e92392 100644
--- a/kex.c
+++ b/kex.c
@@ -54,6 +54,10 @@
54#include "sshbuf.h" 54#include "sshbuf.h"
55#include "digest.h" 55#include "digest.h"
56 56
57#ifdef GSSAPI
58#include "ssh-gss.h"
59#endif
60
57#if OPENSSL_VERSION_NUMBER >= 0x00907000L 61#if OPENSSL_VERSION_NUMBER >= 0x00907000L
58# if defined(HAVE_EVP_SHA256) 62# if defined(HAVE_EVP_SHA256)
59# define evp_ssh_sha256 EVP_sha256 63# define evp_ssh_sha256 EVP_sha256
@@ -109,6 +113,14 @@ static const struct kexalg kexalgs[] = {
109#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ 113#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
110 { NULL, -1, -1, -1}, 114 { NULL, -1, -1, -1},
111}; 115};
116static const struct kexalg kexalg_prefixes[] = {
117#ifdef GSSAPI
118 { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
119 { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
120 { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
121#endif
122 { NULL, -1, -1, -1 },
123};
112 124
113char * 125char *
114kex_alg_list(char sep) 126kex_alg_list(char sep)
@@ -141,6 +153,10 @@ kex_alg_by_name(const char *name)
141 if (strcmp(k->name, name) == 0) 153 if (strcmp(k->name, name) == 0)
142 return k; 154 return k;
143 } 155 }
156 for (k = kexalg_prefixes; k->name != NULL; k++) {
157 if (strncmp(k->name, name, strlen(k->name)) == 0)
158 return k;
159 }
144 return NULL; 160 return NULL;
145} 161}
146 162
diff --git a/kex.h b/kex.h
index 1c5896605..123ef83c1 100644
--- a/kex.h
+++ b/kex.h
@@ -92,6 +92,9 @@ enum kex_exchange {
92 KEX_DH_GEX_SHA256, 92 KEX_DH_GEX_SHA256,
93 KEX_ECDH_SHA2, 93 KEX_ECDH_SHA2,
94 KEX_C25519_SHA256, 94 KEX_C25519_SHA256,
95 KEX_GSS_GRP1_SHA1,
96 KEX_GSS_GRP14_SHA1,
97 KEX_GSS_GEX_SHA1,
95 KEX_MAX 98 KEX_MAX
96}; 99};
97 100
@@ -140,6 +143,12 @@ struct kex {
140 u_int flags; 143 u_int flags;
141 int hash_alg; 144 int hash_alg;
142 int ec_nid; 145 int ec_nid;
146#ifdef GSSAPI
147 int gss_deleg_creds;
148 int gss_trust_dns;
149 char *gss_host;
150 char *gss_client;
151#endif
143 char *client_version_string; 152 char *client_version_string;
144 char *server_version_string; 153 char *server_version_string;
145 char *failed_choice; 154 char *failed_choice;
@@ -190,6 +199,11 @@ int kexecdh_server(struct ssh *);
190int kexc25519_client(struct ssh *); 199int kexc25519_client(struct ssh *);
191int kexc25519_server(struct ssh *); 200int kexc25519_server(struct ssh *);
192 201
202#ifdef GSSAPI
203int kexgss_client(struct ssh *);
204int kexgss_server(struct ssh *);
205#endif
206
193int kex_dh_hash(const char *, const char *, 207int kex_dh_hash(const char *, const char *,
194 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, 208 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
195 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); 209 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *);
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..a49bac295
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,336 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include "includes.h"
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include <string.h>
35
36#include "xmalloc.h"
37#include "buffer.h"
38#include "ssh2.h"
39#include "key.h"
40#include "cipher.h"
41#include "kex.h"
42#include "log.h"
43#include "packet.h"
44#include "dh.h"
45#include "digest.h"
46
47#include "ssh-gss.h"
48
49int
50kexgss_client(struct ssh *ssh) {
51 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
52 gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr;
53 Gssctxt *ctxt;
54 OM_uint32 maj_status, min_status, ret_flags;
55 u_int klen, kout, slen = 0, strlen;
56 DH *dh;
57 BIGNUM *dh_server_pub = NULL;
58 BIGNUM *shared_secret = NULL;
59 BIGNUM *p = NULL;
60 BIGNUM *g = NULL;
61 u_char *kbuf;
62 u_char *serverhostkey = NULL;
63 u_char *empty = "";
64 char *msg;
65 int type = 0;
66 int first = 1;
67 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
68 u_char hash[SSH_DIGEST_MAX_LENGTH];
69 size_t hashlen;
70
71 /* Initialise our GSSAPI world */
72 ssh_gssapi_build_ctx(&ctxt);
73 if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type)
74 == GSS_C_NO_OID)
75 fatal("Couldn't identify host exchange");
76
77 if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host))
78 fatal("Couldn't import hostname");
79
80 if (ssh->kex->gss_client &&
81 ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client))
82 fatal("Couldn't acquire client credentials");
83
84 switch (ssh->kex->kex_type) {
85 case KEX_GSS_GRP1_SHA1:
86 dh = dh_new_group1();
87 break;
88 case KEX_GSS_GRP14_SHA1:
89 dh = dh_new_group14();
90 break;
91 case KEX_GSS_GEX_SHA1:
92 debug("Doing group exchange\n");
93 nbits = dh_estimate(ssh->kex->we_need * 8);
94 packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
95 packet_put_int(min);
96 packet_put_int(nbits);
97 packet_put_int(max);
98
99 packet_send();
100
101 packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
102
103 if ((p = BN_new()) == NULL)
104 fatal("BN_new() failed");
105 packet_get_bignum2(p);
106 if ((g = BN_new()) == NULL)
107 fatal("BN_new() failed");
108 packet_get_bignum2(g);
109 packet_check_eom();
110
111 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
112 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
113 min, BN_num_bits(p), max);
114
115 dh = dh_new_group(g, p);
116 break;
117 default:
118 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
119 }
120
121 /* Step 1 - e is dh->pub_key */
122 dh_gen_key(dh, ssh->kex->we_need * 8);
123
124 /* This is f, we initialise it now to make life easier */
125 dh_server_pub = BN_new();
126 if (dh_server_pub == NULL)
127 fatal("dh_server_pub == NULL");
128
129 token_ptr = GSS_C_NO_BUFFER;
130
131 do {
132 debug("Calling gss_init_sec_context");
133
134 maj_status = ssh_gssapi_init_ctx(ctxt,
135 ssh->kex->gss_deleg_creds, token_ptr, &send_tok,
136 &ret_flags);
137
138 if (GSS_ERROR(maj_status)) {
139 if (send_tok.length != 0) {
140 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
141 packet_put_string(send_tok.value,
142 send_tok.length);
143 }
144 fatal("gss_init_context failed");
145 }
146
147 /* If we've got an old receive buffer get rid of it */
148 if (token_ptr != GSS_C_NO_BUFFER)
149 free(recv_tok.value);
150
151 if (maj_status == GSS_S_COMPLETE) {
152 /* If mutual state flag is not true, kex fails */
153 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
154 fatal("Mutual authentication failed");
155
156 /* If integ avail flag is not true kex fails */
157 if (!(ret_flags & GSS_C_INTEG_FLAG))
158 fatal("Integrity check failed");
159 }
160
161 /*
162 * If we have data to send, then the last message that we
163 * received cannot have been a 'complete'.
164 */
165 if (send_tok.length != 0) {
166 if (first) {
167 packet_start(SSH2_MSG_KEXGSS_INIT);
168 packet_put_string(send_tok.value,
169 send_tok.length);
170 packet_put_bignum2(dh->pub_key);
171 first = 0;
172 } else {
173 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
174 packet_put_string(send_tok.value,
175 send_tok.length);
176 }
177 packet_send();
178 gss_release_buffer(&min_status, &send_tok);
179
180 /* If we've sent them data, they should reply */
181 do {
182 type = packet_read();
183 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
184 debug("Received KEXGSS_HOSTKEY");
185 if (serverhostkey)
186 fatal("Server host key received more than once");
187 serverhostkey =
188 packet_get_string(&slen);
189 }
190 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
191
192 switch (type) {
193 case SSH2_MSG_KEXGSS_CONTINUE:
194 debug("Received GSSAPI_CONTINUE");
195 if (maj_status == GSS_S_COMPLETE)
196 fatal("GSSAPI Continue received from server when complete");
197 recv_tok.value = packet_get_string(&strlen);
198 recv_tok.length = strlen;
199 break;
200 case SSH2_MSG_KEXGSS_COMPLETE:
201 debug("Received GSSAPI_COMPLETE");
202 packet_get_bignum2(dh_server_pub);
203 msg_tok.value = packet_get_string(&strlen);
204 msg_tok.length = strlen;
205
206 /* Is there a token included? */
207 if (packet_get_char()) {
208 recv_tok.value=
209 packet_get_string(&strlen);
210 recv_tok.length = strlen;
211 /* If we're already complete - protocol error */
212 if (maj_status == GSS_S_COMPLETE)
213 packet_disconnect("Protocol error: received token when complete");
214 } else {
215 /* No token included */
216 if (maj_status != GSS_S_COMPLETE)
217 packet_disconnect("Protocol error: did not receive final token");
218 }
219 break;
220 case SSH2_MSG_KEXGSS_ERROR:
221 debug("Received Error");
222 maj_status = packet_get_int();
223 min_status = packet_get_int();
224 msg = packet_get_string(NULL);
225 (void) packet_get_string_ptr(NULL);
226 fatal("GSSAPI Error: \n%.400s",msg);
227 default:
228 packet_disconnect("Protocol error: didn't expect packet type %d",
229 type);
230 }
231 token_ptr = &recv_tok;
232 } else {
233 /* No data, and not complete */
234 if (maj_status != GSS_S_COMPLETE)
235 fatal("Not complete, and no token output");
236 }
237 } while (maj_status & GSS_S_CONTINUE_NEEDED);
238
239 /*
240 * We _must_ have received a COMPLETE message in reply from the
241 * server, which will have set dh_server_pub and msg_tok
242 */
243
244 if (type != SSH2_MSG_KEXGSS_COMPLETE)
245 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
246
247 /* Check f in range [1, p-1] */
248 if (!dh_pub_is_valid(dh, dh_server_pub))
249 packet_disconnect("bad server public DH value");
250
251 /* compute K=f^x mod p */
252 klen = DH_size(dh);
253 kbuf = xmalloc(klen);
254 kout = DH_compute_key(kbuf, dh_server_pub, dh);
255 if (kout < 0)
256 fatal("DH_compute_key: failed");
257
258 shared_secret = BN_new();
259 if (shared_secret == NULL)
260 fatal("kexgss_client: BN_new failed");
261
262 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
263 fatal("kexdh_client: BN_bin2bn failed");
264
265 memset(kbuf, 0, klen);
266 free(kbuf);
267
268 hashlen = sizeof(hash);
269 switch (ssh->kex->kex_type) {
270 case KEX_GSS_GRP1_SHA1:
271 case KEX_GSS_GRP14_SHA1:
272 kex_dh_hash( ssh->kex->client_version_string,
273 ssh->kex->server_version_string,
274 buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my),
275 buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer),
276 (serverhostkey ? serverhostkey : empty), slen,
277 dh->pub_key, /* e */
278 dh_server_pub, /* f */
279 shared_secret, /* K */
280 hash, &hashlen
281 );
282 break;
283 case KEX_GSS_GEX_SHA1:
284 kexgex_hash(
285 ssh->kex->hash_alg,
286 ssh->kex->client_version_string,
287 ssh->kex->server_version_string,
288 buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my),
289 buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer),
290 (serverhostkey ? serverhostkey : empty), slen,
291 min, nbits, max,
292 dh->p, dh->g,
293 dh->pub_key,
294 dh_server_pub,
295 shared_secret,
296 hash, &hashlen
297 );
298 break;
299 default:
300 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
301 }
302
303 gssbuf.value = hash;
304 gssbuf.length = hashlen;
305
306 /* Verify that the hash matches the MIC we just got. */
307 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
308 packet_disconnect("Hash's MIC didn't verify");
309
310 free(msg_tok.value);
311
312 DH_free(dh);
313 free(serverhostkey);
314 BN_clear_free(dh_server_pub);
315
316 /* save session id */
317 if (ssh->kex->session_id == NULL) {
318 ssh->kex->session_id_len = hashlen;
319 ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
320 memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
321 }
322
323 if (ssh->kex->gss_deleg_creds)
324 ssh_gssapi_credentials_updated(ctxt);
325
326 if (gss_kex_context == NULL)
327 gss_kex_context = ctxt;
328 else
329 ssh_gssapi_delete_ctx(&ctxt);
330
331 kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
332 BN_clear_free(shared_secret);
333 return kex_send_newkeys(ssh);
334}
335
336#endif /* GSSAPI */
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..0847469af
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,295 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include <string.h>
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include "xmalloc.h"
35#include "buffer.h"
36#include "ssh2.h"
37#include "key.h"
38#include "cipher.h"
39#include "kex.h"
40#include "log.h"
41#include "packet.h"
42#include "dh.h"
43#include "ssh-gss.h"
44#include "monitor_wrap.h"
45#include "misc.h"
46#include "servconf.h"
47#include "digest.h"
48
49extern ServerOptions options;
50
51int
52kexgss_server(struct ssh *ssh)
53{
54 OM_uint32 maj_status, min_status;
55
56 /*
57 * Some GSSAPI implementations use the input value of ret_flags (an
58 * output variable) as a means of triggering mechanism specific
59 * features. Initializing it to zero avoids inadvertently
60 * activating this non-standard behaviour.
61 */
62
63 OM_uint32 ret_flags = 0;
64 gss_buffer_desc gssbuf, recv_tok, msg_tok;
65 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
66 Gssctxt *ctxt = NULL;
67 u_int slen, klen, kout;
68 u_char *kbuf;
69 DH *dh;
70 int min = -1, max = -1, nbits = -1;
71 BIGNUM *shared_secret = NULL;
72 BIGNUM *dh_client_pub = NULL;
73 int type = 0;
74 gss_OID oid;
75 char *mechs;
76 u_char hash[SSH_DIGEST_MAX_LENGTH];
77 size_t hashlen;
78
79 /* Initialise GSSAPI */
80
81 /* If we're rekeying, privsep means that some of the private structures
82 * in the GSSAPI code are no longer available. This kludges them back
83 * into life
84 */
85 if (!ssh_gssapi_oid_table_ok()) {
86 mechs = ssh_gssapi_server_mechanisms();
87 free(mechs);
88 }
89
90 debug2("%s: Identifying %s", __func__, ssh->kex->name);
91 oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type);
92 if (oid == GSS_C_NO_OID)
93 fatal("Unknown gssapi mechanism");
94
95 debug2("%s: Acquiring credentials", __func__);
96
97 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
98 fatal("Unable to acquire credentials for the server");
99
100 switch (ssh->kex->kex_type) {
101 case KEX_GSS_GRP1_SHA1:
102 dh = dh_new_group1();
103 break;
104 case KEX_GSS_GRP14_SHA1:
105 dh = dh_new_group14();
106 break;
107 case KEX_GSS_GEX_SHA1:
108 debug("Doing group exchange");
109 packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
110 min = packet_get_int();
111 nbits = packet_get_int();
112 max = packet_get_int();
113 min = MAX(DH_GRP_MIN, min);
114 max = MIN(DH_GRP_MAX, max);
115 packet_check_eom();
116 if (max < min || nbits < min || max < nbits)
117 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
118 min, nbits, max);
119 dh = PRIVSEP(choose_dh(min, nbits, max));
120 if (dh == NULL)
121 packet_disconnect("Protocol error: no matching group found");
122
123 packet_start(SSH2_MSG_KEXGSS_GROUP);
124 packet_put_bignum2(dh->p);
125 packet_put_bignum2(dh->g);
126 packet_send();
127
128 packet_write_wait();
129 break;
130 default:
131 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
132 }
133
134 dh_gen_key(dh, ssh->kex->we_need * 8);
135
136 do {
137 debug("Wait SSH2_MSG_GSSAPI_INIT");
138 type = packet_read();
139 switch(type) {
140 case SSH2_MSG_KEXGSS_INIT:
141 if (dh_client_pub != NULL)
142 fatal("Received KEXGSS_INIT after initialising");
143 recv_tok.value = packet_get_string(&slen);
144 recv_tok.length = slen;
145
146 if ((dh_client_pub = BN_new()) == NULL)
147 fatal("dh_client_pub == NULL");
148
149 packet_get_bignum2(dh_client_pub);
150
151 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
152 break;
153 case SSH2_MSG_KEXGSS_CONTINUE:
154 recv_tok.value = packet_get_string(&slen);
155 recv_tok.length = slen;
156 break;
157 default:
158 packet_disconnect(
159 "Protocol error: didn't expect packet type %d",
160 type);
161 }
162
163 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
164 &send_tok, &ret_flags));
165
166 free(recv_tok.value);
167
168 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
169 fatal("Zero length token output when incomplete");
170
171 if (dh_client_pub == NULL)
172 fatal("No client public key");
173
174 if (maj_status & GSS_S_CONTINUE_NEEDED) {
175 debug("Sending GSSAPI_CONTINUE");
176 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
177 packet_put_string(send_tok.value, send_tok.length);
178 packet_send();
179 gss_release_buffer(&min_status, &send_tok);
180 }
181 } while (maj_status & GSS_S_CONTINUE_NEEDED);
182
183 if (GSS_ERROR(maj_status)) {
184 if (send_tok.length > 0) {
185 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
186 packet_put_string(send_tok.value, send_tok.length);
187 packet_send();
188 }
189 fatal("accept_ctx died");
190 }
191
192 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
193 fatal("Mutual Authentication flag wasn't set");
194
195 if (!(ret_flags & GSS_C_INTEG_FLAG))
196 fatal("Integrity flag wasn't set");
197
198 if (!dh_pub_is_valid(dh, dh_client_pub))
199 packet_disconnect("bad client public DH value");
200
201 klen = DH_size(dh);
202 kbuf = xmalloc(klen);
203 kout = DH_compute_key(kbuf, dh_client_pub, dh);
204 if (kout < 0)
205 fatal("DH_compute_key: failed");
206
207 shared_secret = BN_new();
208 if (shared_secret == NULL)
209 fatal("kexgss_server: BN_new failed");
210
211 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
212 fatal("kexgss_server: BN_bin2bn failed");
213
214 memset(kbuf, 0, klen);
215 free(kbuf);
216
217 hashlen = sizeof(hash);
218 switch (ssh->kex->kex_type) {
219 case KEX_GSS_GRP1_SHA1:
220 case KEX_GSS_GRP14_SHA1:
221 kex_dh_hash(
222 ssh->kex->client_version_string, ssh->kex->server_version_string,
223 buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer),
224 buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my),
225 NULL, 0, /* Change this if we start sending host keys */
226 dh_client_pub, dh->pub_key, shared_secret,
227 hash, &hashlen
228 );
229 break;
230 case KEX_GSS_GEX_SHA1:
231 kexgex_hash(
232 ssh->kex->hash_alg,
233 ssh->kex->client_version_string, ssh->kex->server_version_string,
234 buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer),
235 buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my),
236 NULL, 0,
237 min, nbits, max,
238 dh->p, dh->g,
239 dh_client_pub,
240 dh->pub_key,
241 shared_secret,
242 hash, &hashlen
243 );
244 break;
245 default:
246 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
247 }
248
249 BN_clear_free(dh_client_pub);
250
251 if (ssh->kex->session_id == NULL) {
252 ssh->kex->session_id_len = hashlen;
253 ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
254 memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
255 }
256
257 gssbuf.value = hash;
258 gssbuf.length = hashlen;
259
260 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
261 fatal("Couldn't get MIC");
262
263 packet_start(SSH2_MSG_KEXGSS_COMPLETE);
264 packet_put_bignum2(dh->pub_key);
265 packet_put_string(msg_tok.value,msg_tok.length);
266
267 if (send_tok.length != 0) {
268 packet_put_char(1); /* true */
269 packet_put_string(send_tok.value, send_tok.length);
270 } else {
271 packet_put_char(0); /* false */
272 }
273 packet_send();
274
275 gss_release_buffer(&min_status, &send_tok);
276 gss_release_buffer(&min_status, &msg_tok);
277
278 if (gss_kex_context == NULL)
279 gss_kex_context = ctxt;
280 else
281 ssh_gssapi_delete_ctx(&ctxt);
282
283 DH_free(dh);
284
285 kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
286 BN_clear_free(shared_secret);
287 kex_send_newkeys(ssh);
288
289 /* If this was a rekey, then save out any delegated credentials we
290 * just exchanged. */
291 if (options.gss_store_rekey)
292 ssh_gssapi_rekey_creds();
293 return 0;
294}
295#endif /* GSSAPI */
diff --git a/monitor.c b/monitor.c
index ac7dd3099..6c8202325 100644
--- a/monitor.c
+++ b/monitor.c
@@ -156,6 +156,8 @@ int mm_answer_gss_setup_ctx(int, Buffer *);
156int mm_answer_gss_accept_ctx(int, Buffer *); 156int mm_answer_gss_accept_ctx(int, Buffer *);
157int mm_answer_gss_userok(int, Buffer *); 157int mm_answer_gss_userok(int, Buffer *);
158int mm_answer_gss_checkmic(int, Buffer *); 158int mm_answer_gss_checkmic(int, Buffer *);
159int mm_answer_gss_sign(int, Buffer *);
160int mm_answer_gss_updatecreds(int, Buffer *);
159#endif 161#endif
160 162
161#ifdef SSH_AUDIT_EVENTS 163#ifdef SSH_AUDIT_EVENTS
@@ -233,11 +235,18 @@ struct mon_table mon_dispatch_proto20[] = {
233 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, 235 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
234 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, 236 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
235 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, 237 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
238 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
236#endif 239#endif
237 {0, 0, NULL} 240 {0, 0, NULL}
238}; 241};
239 242
240struct mon_table mon_dispatch_postauth20[] = { 243struct mon_table mon_dispatch_postauth20[] = {
244#ifdef GSSAPI
245 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
246 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
247 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
248 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
249#endif
241#ifdef WITH_OPENSSL 250#ifdef WITH_OPENSSL
242 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 251 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
243#endif 252#endif
@@ -352,6 +361,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
352 /* Permit requests for moduli and signatures */ 361 /* Permit requests for moduli and signatures */
353 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 362 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
354 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 363 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
364#ifdef GSSAPI
365 /* and for the GSSAPI key exchange */
366 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
367#endif
355 } else { 368 } else {
356 mon_dispatch = mon_dispatch_proto15; 369 mon_dispatch = mon_dispatch_proto15;
357 370
@@ -460,6 +473,10 @@ monitor_child_postauth(struct monitor *pmonitor)
460 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 473 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
461 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 474 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
462 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 475 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
476#ifdef GSSAPI
477 /* and for the GSSAPI key exchange */
478 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
479#endif
463 } else { 480 } else {
464 mon_dispatch = mon_dispatch_postauth15; 481 mon_dispatch = mon_dispatch_postauth15;
465 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 482 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
@@ -1861,6 +1878,13 @@ monitor_apply_keystate(struct monitor *pmonitor)
1861# endif 1878# endif
1862#endif /* WITH_OPENSSL */ 1879#endif /* WITH_OPENSSL */
1863 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 1880 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1881#ifdef GSSAPI
1882 if (options.gss_keyex) {
1883 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1884 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1885 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1886 }
1887#endif
1864 kex->load_host_public_key=&get_hostkey_public_by_type; 1888 kex->load_host_public_key=&get_hostkey_public_by_type;
1865 kex->load_host_private_key=&get_hostkey_private_by_type; 1889 kex->load_host_private_key=&get_hostkey_private_by_type;
1866 kex->host_key_index=&get_hostkey_index; 1890 kex->host_key_index=&get_hostkey_index;
@@ -1960,6 +1984,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m)
1960 OM_uint32 major; 1984 OM_uint32 major;
1961 u_int len; 1985 u_int len;
1962 1986
1987 if (!options.gss_authentication && !options.gss_keyex)
1988 fatal("In GSSAPI monitor when GSSAPI is disabled");
1989
1963 goid.elements = buffer_get_string(m, &len); 1990 goid.elements = buffer_get_string(m, &len);
1964 goid.length = len; 1991 goid.length = len;
1965 1992
@@ -1987,6 +2014,9 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
1987 OM_uint32 flags = 0; /* GSI needs this */ 2014 OM_uint32 flags = 0; /* GSI needs this */
1988 u_int len; 2015 u_int len;
1989 2016
2017 if (!options.gss_authentication && !options.gss_keyex)
2018 fatal("In GSSAPI monitor when GSSAPI is disabled");
2019
1990 in.value = buffer_get_string(m, &len); 2020 in.value = buffer_get_string(m, &len);
1991 in.length = len; 2021 in.length = len;
1992 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); 2022 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
@@ -2004,6 +2034,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
2004 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 2034 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2005 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 2035 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2006 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 2036 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2037 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2007 } 2038 }
2008 return (0); 2039 return (0);
2009} 2040}
@@ -2015,6 +2046,9 @@ mm_answer_gss_checkmic(int sock, Buffer *m)
2015 OM_uint32 ret; 2046 OM_uint32 ret;
2016 u_int len; 2047 u_int len;
2017 2048
2049 if (!options.gss_authentication && !options.gss_keyex)
2050 fatal("In GSSAPI monitor when GSSAPI is disabled");
2051
2018 gssbuf.value = buffer_get_string(m, &len); 2052 gssbuf.value = buffer_get_string(m, &len);
2019 gssbuf.length = len; 2053 gssbuf.length = len;
2020 mic.value = buffer_get_string(m, &len); 2054 mic.value = buffer_get_string(m, &len);
@@ -2041,7 +2075,11 @@ mm_answer_gss_userok(int sock, Buffer *m)
2041{ 2075{
2042 int authenticated; 2076 int authenticated;
2043 2077
2044 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 2078 if (!options.gss_authentication && !options.gss_keyex)
2079 fatal("In GSSAPI monitor when GSSAPI is disabled");
2080
2081 authenticated = authctxt->valid &&
2082 ssh_gssapi_userok(authctxt->user, authctxt->pw);
2045 2083
2046 buffer_clear(m); 2084 buffer_clear(m);
2047 buffer_put_int(m, authenticated); 2085 buffer_put_int(m, authenticated);
@@ -2054,5 +2092,73 @@ mm_answer_gss_userok(int sock, Buffer *m)
2054 /* Monitor loop will terminate if authenticated */ 2092 /* Monitor loop will terminate if authenticated */
2055 return (authenticated); 2093 return (authenticated);
2056} 2094}
2095
2096int
2097mm_answer_gss_sign(int socket, Buffer *m)
2098{
2099 gss_buffer_desc data;
2100 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2101 OM_uint32 major, minor;
2102 u_int len;
2103
2104 if (!options.gss_authentication && !options.gss_keyex)
2105 fatal("In GSSAPI monitor when GSSAPI is disabled");
2106
2107 data.value = buffer_get_string(m, &len);
2108 data.length = len;
2109 if (data.length != 20)
2110 fatal("%s: data length incorrect: %d", __func__,
2111 (int) data.length);
2112
2113 /* Save the session ID on the first time around */
2114 if (session_id2_len == 0) {
2115 session_id2_len = data.length;
2116 session_id2 = xmalloc(session_id2_len);
2117 memcpy(session_id2, data.value, session_id2_len);
2118 }
2119 major = ssh_gssapi_sign(gsscontext, &data, &hash);
2120
2121 free(data.value);
2122
2123 buffer_clear(m);
2124 buffer_put_int(m, major);
2125 buffer_put_string(m, hash.value, hash.length);
2126
2127 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2128
2129 gss_release_buffer(&minor, &hash);
2130
2131 /* Turn on getpwnam permissions */
2132 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2133
2134 /* And credential updating, for when rekeying */
2135 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2136
2137 return (0);
2138}
2139
2140int
2141mm_answer_gss_updatecreds(int socket, Buffer *m) {
2142 ssh_gssapi_ccache store;
2143 int ok;
2144
2145 store.filename = buffer_get_string(m, NULL);
2146 store.envvar = buffer_get_string(m, NULL);
2147 store.envval = buffer_get_string(m, NULL);
2148
2149 ok = ssh_gssapi_update_creds(&store);
2150
2151 free(store.filename);
2152 free(store.envvar);
2153 free(store.envval);
2154
2155 buffer_clear(m);
2156 buffer_put_int(m, ok);
2157
2158 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2159
2160 return(0);
2161}
2162
2057#endif /* GSSAPI */ 2163#endif /* GSSAPI */
2058 2164
diff --git a/monitor.h b/monitor.h
index 93b8b66dd..bc50ade1f 100644
--- a/monitor.h
+++ b/monitor.h
@@ -65,6 +65,9 @@ enum monitor_reqtype {
65 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, 65 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
66 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, 66 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
67 67
68 MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
69 MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
70
68}; 71};
69 72
70struct mm_master; 73struct mm_master;
diff --git a/monitor_wrap.c b/monitor_wrap.c
index c5db6df48..74fbd2ef3 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1068,7 +1068,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1068} 1068}
1069 1069
1070int 1070int
1071mm_ssh_gssapi_userok(char *user) 1071mm_ssh_gssapi_userok(char *user, struct passwd *pw)
1072{ 1072{
1073 Buffer m; 1073 Buffer m;
1074 int authenticated = 0; 1074 int authenticated = 0;
@@ -1085,5 +1085,50 @@ mm_ssh_gssapi_userok(char *user)
1085 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1085 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1086 return (authenticated); 1086 return (authenticated);
1087} 1087}
1088
1089OM_uint32
1090mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1091{
1092 Buffer m;
1093 OM_uint32 major;
1094 u_int len;
1095
1096 buffer_init(&m);
1097 buffer_put_string(&m, data->value, data->length);
1098
1099 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
1100 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
1101
1102 major = buffer_get_int(&m);
1103 hash->value = buffer_get_string(&m, &len);
1104 hash->length = len;
1105
1106 buffer_free(&m);
1107
1108 return(major);
1109}
1110
1111int
1112mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1113{
1114 Buffer m;
1115 int ok;
1116
1117 buffer_init(&m);
1118
1119 buffer_put_cstring(&m, store->filename ? store->filename : "");
1120 buffer_put_cstring(&m, store->envvar ? store->envvar : "");
1121 buffer_put_cstring(&m, store->envval ? store->envval : "");
1122
1123 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m);
1124 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m);
1125
1126 ok = buffer_get_int(&m);
1127
1128 buffer_free(&m);
1129
1130 return (ok);
1131}
1132
1088#endif /* GSSAPI */ 1133#endif /* GSSAPI */
1089 1134
diff --git a/monitor_wrap.h b/monitor_wrap.h
index eb820aeea..403f8d00c 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -58,8 +58,10 @@ BIGNUM *mm_auth_rsa_generate_challenge(Key *);
58OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 58OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
59OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, 59OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
60 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); 60 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
61int mm_ssh_gssapi_userok(char *user); 61int mm_ssh_gssapi_userok(char *user, struct passwd *);
62OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 62OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
63OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
64int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
63#endif 65#endif
64 66
65#ifdef USE_PAM 67#ifdef USE_PAM
diff --git a/readconf.c b/readconf.c
index 69d4553af..d2a3d4b1a 100644
--- a/readconf.c
+++ b/readconf.c
@@ -148,6 +148,8 @@ typedef enum {
148 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 148 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
149 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 149 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
150 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 150 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
151 oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
152 oGssServerIdentity,
151 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 153 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
152 oSendEnv, oControlPath, oControlMaster, oControlPersist, 154 oSendEnv, oControlPath, oControlMaster, oControlPersist,
153 oHashKnownHosts, 155 oHashKnownHosts,
@@ -193,10 +195,19 @@ static struct {
193 { "afstokenpassing", oUnsupported }, 195 { "afstokenpassing", oUnsupported },
194#if defined(GSSAPI) 196#if defined(GSSAPI)
195 { "gssapiauthentication", oGssAuthentication }, 197 { "gssapiauthentication", oGssAuthentication },
198 { "gssapikeyexchange", oGssKeyEx },
196 { "gssapidelegatecredentials", oGssDelegateCreds }, 199 { "gssapidelegatecredentials", oGssDelegateCreds },
200 { "gssapitrustdns", oGssTrustDns },
201 { "gssapiclientidentity", oGssClientIdentity },
202 { "gssapiserveridentity", oGssServerIdentity },
203 { "gssapirenewalforcesrekey", oGssRenewalRekey },
197#else 204#else
198 { "gssapiauthentication", oUnsupported }, 205 { "gssapiauthentication", oUnsupported },
206 { "gssapikeyexchange", oUnsupported },
199 { "gssapidelegatecredentials", oUnsupported }, 207 { "gssapidelegatecredentials", oUnsupported },
208 { "gssapitrustdns", oUnsupported },
209 { "gssapiclientidentity", oUnsupported },
210 { "gssapirenewalforcesrekey", oUnsupported },
200#endif 211#endif
201 { "fallbacktorsh", oDeprecated }, 212 { "fallbacktorsh", oDeprecated },
202 { "usersh", oDeprecated }, 213 { "usersh", oDeprecated },
@@ -926,10 +937,30 @@ parse_time:
926 intptr = &options->gss_authentication; 937 intptr = &options->gss_authentication;
927 goto parse_flag; 938 goto parse_flag;
928 939
940 case oGssKeyEx:
941 intptr = &options->gss_keyex;
942 goto parse_flag;
943
929 case oGssDelegateCreds: 944 case oGssDelegateCreds:
930 intptr = &options->gss_deleg_creds; 945 intptr = &options->gss_deleg_creds;
931 goto parse_flag; 946 goto parse_flag;
932 947
948 case oGssTrustDns:
949 intptr = &options->gss_trust_dns;
950 goto parse_flag;
951
952 case oGssClientIdentity:
953 charptr = &options->gss_client_identity;
954 goto parse_string;
955
956 case oGssServerIdentity:
957 charptr = &options->gss_server_identity;
958 goto parse_string;
959
960 case oGssRenewalRekey:
961 intptr = &options->gss_renewal_rekey;
962 goto parse_flag;
963
933 case oBatchMode: 964 case oBatchMode:
934 intptr = &options->batch_mode; 965 intptr = &options->batch_mode;
935 goto parse_flag; 966 goto parse_flag;
@@ -1648,7 +1679,12 @@ initialize_options(Options * options)
1648 options->pubkey_authentication = -1; 1679 options->pubkey_authentication = -1;
1649 options->challenge_response_authentication = -1; 1680 options->challenge_response_authentication = -1;
1650 options->gss_authentication = -1; 1681 options->gss_authentication = -1;
1682 options->gss_keyex = -1;
1651 options->gss_deleg_creds = -1; 1683 options->gss_deleg_creds = -1;
1684 options->gss_trust_dns = -1;
1685 options->gss_renewal_rekey = -1;
1686 options->gss_client_identity = NULL;
1687 options->gss_server_identity = NULL;
1652 options->password_authentication = -1; 1688 options->password_authentication = -1;
1653 options->kbd_interactive_authentication = -1; 1689 options->kbd_interactive_authentication = -1;
1654 options->kbd_interactive_devices = NULL; 1690 options->kbd_interactive_devices = NULL;
@@ -1777,8 +1813,14 @@ fill_default_options(Options * options)
1777 options->challenge_response_authentication = 1; 1813 options->challenge_response_authentication = 1;
1778 if (options->gss_authentication == -1) 1814 if (options->gss_authentication == -1)
1779 options->gss_authentication = 0; 1815 options->gss_authentication = 0;
1816 if (options->gss_keyex == -1)
1817 options->gss_keyex = 0;
1780 if (options->gss_deleg_creds == -1) 1818 if (options->gss_deleg_creds == -1)
1781 options->gss_deleg_creds = 0; 1819 options->gss_deleg_creds = 0;
1820 if (options->gss_trust_dns == -1)
1821 options->gss_trust_dns = 0;
1822 if (options->gss_renewal_rekey == -1)
1823 options->gss_renewal_rekey = 0;
1782 if (options->password_authentication == -1) 1824 if (options->password_authentication == -1)
1783 options->password_authentication = 1; 1825 options->password_authentication = 1;
1784 if (options->kbd_interactive_authentication == -1) 1826 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index c84d068bd..37a055521 100644
--- a/readconf.h
+++ b/readconf.h
@@ -45,7 +45,12 @@ typedef struct {
45 int challenge_response_authentication; 45 int challenge_response_authentication;
46 /* Try S/Key or TIS, authentication. */ 46 /* Try S/Key or TIS, authentication. */
47 int gss_authentication; /* Try GSS authentication */ 47 int gss_authentication; /* Try GSS authentication */
48 int gss_keyex; /* Try GSS key exchange */
48 int gss_deleg_creds; /* Delegate GSS credentials */ 49 int gss_deleg_creds; /* Delegate GSS credentials */
50 int gss_trust_dns; /* Trust DNS for GSS canonicalization */
51 int gss_renewal_rekey; /* Credential renewal forces rekey */
52 char *gss_client_identity; /* Principal to initiate GSSAPI with */
53 char *gss_server_identity; /* GSSAPI target principal */
49 int password_authentication; /* Try password 54 int password_authentication; /* Try password
50 * authentication. */ 55 * authentication. */
51 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 56 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
diff --git a/servconf.c b/servconf.c
index b19d30e18..b8af6dda7 100644
--- a/servconf.c
+++ b/servconf.c
@@ -117,8 +117,10 @@ initialize_server_options(ServerOptions *options)
117 options->kerberos_ticket_cleanup = -1; 117 options->kerberos_ticket_cleanup = -1;
118 options->kerberos_get_afs_token = -1; 118 options->kerberos_get_afs_token = -1;
119 options->gss_authentication=-1; 119 options->gss_authentication=-1;
120 options->gss_keyex = -1;
120 options->gss_cleanup_creds = -1; 121 options->gss_cleanup_creds = -1;
121 options->gss_strict_acceptor = -1; 122 options->gss_strict_acceptor = -1;
123 options->gss_store_rekey = -1;
122 options->password_authentication = -1; 124 options->password_authentication = -1;
123 options->kbd_interactive_authentication = -1; 125 options->kbd_interactive_authentication = -1;
124 options->challenge_response_authentication = -1; 126 options->challenge_response_authentication = -1;
@@ -287,10 +289,14 @@ fill_default_server_options(ServerOptions *options)
287 options->kerberos_get_afs_token = 0; 289 options->kerberos_get_afs_token = 0;
288 if (options->gss_authentication == -1) 290 if (options->gss_authentication == -1)
289 options->gss_authentication = 0; 291 options->gss_authentication = 0;
292 if (options->gss_keyex == -1)
293 options->gss_keyex = 0;
290 if (options->gss_cleanup_creds == -1) 294 if (options->gss_cleanup_creds == -1)
291 options->gss_cleanup_creds = 1; 295 options->gss_cleanup_creds = 1;
292 if (options->gss_strict_acceptor == -1) 296 if (options->gss_strict_acceptor == -1)
293 options->gss_strict_acceptor = 0; 297 options->gss_strict_acceptor = 1;
298 if (options->gss_store_rekey == -1)
299 options->gss_store_rekey = 0;
294 if (options->password_authentication == -1) 300 if (options->password_authentication == -1)
295 options->password_authentication = 1; 301 options->password_authentication = 1;
296 if (options->kbd_interactive_authentication == -1) 302 if (options->kbd_interactive_authentication == -1)
@@ -419,6 +425,7 @@ typedef enum {
419 sHostKeyAlgorithms, 425 sHostKeyAlgorithms,
420 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 426 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
421 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 427 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
428 sGssKeyEx, sGssStoreRekey,
422 sAcceptEnv, sPermitTunnel, 429 sAcceptEnv, sPermitTunnel,
423 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 430 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
424 sUsePrivilegeSeparation, sAllowAgentForwarding, 431 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -492,12 +499,20 @@ static struct {
492#ifdef GSSAPI 499#ifdef GSSAPI
493 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 500 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
494 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 501 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
502 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
495 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 503 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
504 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
505 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
496#else 506#else
497 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 507 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
498 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 508 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
509 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
499 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 510 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
511 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
512 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
500#endif 513#endif
514 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
515 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
501 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 516 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
502 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 517 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
503 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 518 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1242,6 +1257,10 @@ process_server_config_line(ServerOptions *options, char *line,
1242 intptr = &options->gss_authentication; 1257 intptr = &options->gss_authentication;
1243 goto parse_flag; 1258 goto parse_flag;
1244 1259
1260 case sGssKeyEx:
1261 intptr = &options->gss_keyex;
1262 goto parse_flag;
1263
1245 case sGssCleanupCreds: 1264 case sGssCleanupCreds:
1246 intptr = &options->gss_cleanup_creds; 1265 intptr = &options->gss_cleanup_creds;
1247 goto parse_flag; 1266 goto parse_flag;
@@ -1250,6 +1269,10 @@ process_server_config_line(ServerOptions *options, char *line,
1250 intptr = &options->gss_strict_acceptor; 1269 intptr = &options->gss_strict_acceptor;
1251 goto parse_flag; 1270 goto parse_flag;
1252 1271
1272 case sGssStoreRekey:
1273 intptr = &options->gss_store_rekey;
1274 goto parse_flag;
1275
1253 case sPasswordAuthentication: 1276 case sPasswordAuthentication:
1254 intptr = &options->password_authentication; 1277 intptr = &options->password_authentication;
1255 goto parse_flag; 1278 goto parse_flag;
@@ -2265,7 +2288,10 @@ dump_config(ServerOptions *o)
2265#endif 2288#endif
2266#ifdef GSSAPI 2289#ifdef GSSAPI
2267 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2290 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2291 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2268 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2292 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2293 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2294 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2269#endif 2295#endif
2270 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2296 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2271 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2297 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index f4137af7d..778ba1742 100644
--- a/servconf.h
+++ b/servconf.h
@@ -118,8 +118,10 @@ typedef struct {
118 int kerberos_get_afs_token; /* If true, try to get AFS token if 118 int kerberos_get_afs_token; /* If true, try to get AFS token if
119 * authenticated with Kerberos. */ 119 * authenticated with Kerberos. */
120 int gss_authentication; /* If true, permit GSSAPI authentication */ 120 int gss_authentication; /* If true, permit GSSAPI authentication */
121 int gss_keyex; /* If true, permit GSSAPI key exchange */
121 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 122 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
122 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 123 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
124 int gss_store_rekey;
123 int password_authentication; /* If true, permit password 125 int password_authentication; /* If true, permit password
124 * authentication. */ 126 * authentication. */
125 int kbd_interactive_authentication; /* If true, permit */ 127 int kbd_interactive_authentication; /* If true, permit */
diff --git a/ssh-gss.h b/ssh-gss.h
index a99d7f08b..914701bcf 100644
--- a/ssh-gss.h
+++ b/ssh-gss.h
@@ -1,6 +1,6 @@
1/* $OpenBSD: ssh-gss.h,v 1.11 2014/02/26 20:28:44 djm Exp $ */ 1/* $OpenBSD: ssh-gss.h,v 1.11 2014/02/26 20:28:44 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 3 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
@@ -61,10 +61,22 @@
61 61
62#define SSH_GSS_OIDTYPE 0x06 62#define SSH_GSS_OIDTYPE 0x06
63 63
64#define SSH2_MSG_KEXGSS_INIT 30
65#define SSH2_MSG_KEXGSS_CONTINUE 31
66#define SSH2_MSG_KEXGSS_COMPLETE 32
67#define SSH2_MSG_KEXGSS_HOSTKEY 33
68#define SSH2_MSG_KEXGSS_ERROR 34
69#define SSH2_MSG_KEXGSS_GROUPREQ 40
70#define SSH2_MSG_KEXGSS_GROUP 41
71#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
72#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-"
73#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
74
64typedef struct { 75typedef struct {
65 char *filename; 76 char *filename;
66 char *envvar; 77 char *envvar;
67 char *envval; 78 char *envval;
79 struct passwd *owner;
68 void *data; 80 void *data;
69} ssh_gssapi_ccache; 81} ssh_gssapi_ccache;
70 82
@@ -72,8 +84,11 @@ typedef struct {
72 gss_buffer_desc displayname; 84 gss_buffer_desc displayname;
73 gss_buffer_desc exportedname; 85 gss_buffer_desc exportedname;
74 gss_cred_id_t creds; 86 gss_cred_id_t creds;
87 gss_name_t name;
75 struct ssh_gssapi_mech_struct *mech; 88 struct ssh_gssapi_mech_struct *mech;
76 ssh_gssapi_ccache store; 89 ssh_gssapi_ccache store;
90 int used;
91 int updated;
77} ssh_gssapi_client; 92} ssh_gssapi_client;
78 93
79typedef struct ssh_gssapi_mech_struct { 94typedef struct ssh_gssapi_mech_struct {
@@ -84,6 +99,7 @@ typedef struct ssh_gssapi_mech_struct {
84 int (*userok) (ssh_gssapi_client *, char *); 99 int (*userok) (ssh_gssapi_client *, char *);
85 int (*localname) (ssh_gssapi_client *, char **); 100 int (*localname) (ssh_gssapi_client *, char **);
86 void (*storecreds) (ssh_gssapi_client *); 101 void (*storecreds) (ssh_gssapi_client *);
102 int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
87} ssh_gssapi_mech; 103} ssh_gssapi_mech;
88 104
89typedef struct { 105typedef struct {
@@ -94,10 +110,11 @@ typedef struct {
94 gss_OID oid; /* client */ 110 gss_OID oid; /* client */
95 gss_cred_id_t creds; /* server */ 111 gss_cred_id_t creds; /* server */
96 gss_name_t client; /* server */ 112 gss_name_t client; /* server */
97 gss_cred_id_t client_creds; /* server */ 113 gss_cred_id_t client_creds; /* both */
98} Gssctxt; 114} Gssctxt;
99 115
100extern ssh_gssapi_mech *supported_mechs[]; 116extern ssh_gssapi_mech *supported_mechs[];
117extern Gssctxt *gss_kex_context;
101 118
102int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); 119int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
103void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); 120void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
@@ -119,16 +136,32 @@ void ssh_gssapi_build_ctx(Gssctxt **);
119void ssh_gssapi_delete_ctx(Gssctxt **); 136void ssh_gssapi_delete_ctx(Gssctxt **);
120OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); 137OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
121void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); 138void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *);
122int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); 139int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
140OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
141int ssh_gssapi_credentials_updated(Gssctxt *);
123 142
124/* In the server */ 143/* In the server */
144typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
145 const char *);
146char *ssh_gssapi_client_mechanisms(const char *, const char *);
147char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
148 const char *);
149gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
150int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
151 const char *);
125OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 152OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
126int ssh_gssapi_userok(char *name); 153int ssh_gssapi_userok(char *name, struct passwd *);
127OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 154OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
128void ssh_gssapi_do_child(char ***, u_int *); 155void ssh_gssapi_do_child(char ***, u_int *);
129void ssh_gssapi_cleanup_creds(void); 156void ssh_gssapi_cleanup_creds(void);
130void ssh_gssapi_storecreds(void); 157void ssh_gssapi_storecreds(void);
131 158
159char *ssh_gssapi_server_mechanisms(void);
160int ssh_gssapi_oid_table_ok(void);
161
162int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
163void ssh_gssapi_rekey_creds(void);
164
132#endif /* GSSAPI */ 165#endif /* GSSAPI */
133 166
134#endif /* _SSH_GSS_H */ 167#endif /* _SSH_GSS_H */
diff --git a/ssh_config b/ssh_config
index 90fb63f0b..4e879cd20 100644
--- a/ssh_config
+++ b/ssh_config
@@ -26,6 +26,8 @@
26# HostbasedAuthentication no 26# HostbasedAuthentication no
27# GSSAPIAuthentication no 27# GSSAPIAuthentication no
28# GSSAPIDelegateCredentials no 28# GSSAPIDelegateCredentials no
29# GSSAPIKeyExchange no
30# GSSAPITrustDNS no
29# BatchMode no 31# BatchMode no
30# CheckHostIP yes 32# CheckHostIP yes
31# AddressFamily any 33# AddressFamily any
diff --git a/ssh_config.5 b/ssh_config.5
index caf13a62d..9060d5be2 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -826,10 +826,42 @@ The default is
826Specifies whether user authentication based on GSSAPI is allowed. 826Specifies whether user authentication based on GSSAPI is allowed.
827The default is 827The default is
828.Dq no . 828.Dq no .
829.It Cm GSSAPIKeyExchange
830Specifies whether key exchange based on GSSAPI may be used. When using
831GSSAPI key exchange the server need not have a host key.
832The default is
833.Dq no .
834.It Cm GSSAPIClientIdentity
835If set, specifies the GSSAPI client identity that ssh should use when
836connecting to the server. The default is unset, which means that the default
837identity will be used.
838.It Cm GSSAPIServerIdentity
839If set, specifies the GSSAPI server identity that ssh should expect when
840connecting to the server. The default is unset, which means that the
841expected GSSAPI server identity will be determined from the target
842hostname.
829.It Cm GSSAPIDelegateCredentials 843.It Cm GSSAPIDelegateCredentials
830Forward (delegate) credentials to the server. 844Forward (delegate) credentials to the server.
831The default is 845The default is
832.Dq no . 846.Dq no .
847.It Cm GSSAPIRenewalForcesRekey
848If set to
849.Dq yes
850then renewal of the client's GSSAPI credentials will force the rekeying of the
851ssh connection. With a compatible server, this can delegate the renewed
852credentials to a session on the server.
853The default is
854.Dq no .
855.It Cm GSSAPITrustDns
856Set to
857.Dq yes
858to indicate that the DNS is trusted to securely canonicalize
859the name of the host being connected to. If
860.Dq no ,
861the hostname entered on the
862command line will be passed untouched to the GSSAPI library.
863The default is
864.Dq no .
833.It Cm HashKnownHosts 865.It Cm HashKnownHosts
834Indicates that 866Indicates that
835.Xr ssh 1 867.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index f79c96beb..b452eae24 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -161,6 +161,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
161 struct kex *kex; 161 struct kex *kex;
162 int r; 162 int r;
163 163
164#ifdef GSSAPI
165 char *orig = NULL, *gss = NULL;
166 char *gss_host = NULL;
167#endif
168
164 xxx_host = host; 169 xxx_host = host;
165 xxx_hostaddr = hostaddr; 170 xxx_hostaddr = hostaddr;
166 171
@@ -195,6 +200,33 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
195 order_hostkeyalgs(host, hostaddr, port)); 200 order_hostkeyalgs(host, hostaddr, port));
196 } 201 }
197 202
203#ifdef GSSAPI
204 if (options.gss_keyex) {
205 /* Add the GSSAPI mechanisms currently supported on this
206 * client to the key exchange algorithm proposal */
207 orig = myproposal[PROPOSAL_KEX_ALGS];
208
209 if (options.gss_trust_dns)
210 gss_host = (char *)get_canonical_hostname(1);
211 else
212 gss_host = host;
213
214 gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity);
215 if (gss) {
216 debug("Offering GSSAPI proposal: %s", gss);
217 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
218 "%s,%s", gss, orig);
219
220 /* If we've got GSSAPI algorithms, then we also
221 * support the 'null' hostkey, as a last resort */
222 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
223 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
224 "%s,null", orig);
225 free(gss);
226 }
227 }
228#endif
229
198 if (options.rekey_limit || options.rekey_interval) 230 if (options.rekey_limit || options.rekey_interval)
199 packet_set_rekey_limits((u_int32_t)options.rekey_limit, 231 packet_set_rekey_limits((u_int32_t)options.rekey_limit,
200 (time_t)options.rekey_interval); 232 (time_t)options.rekey_interval);
@@ -213,10 +245,30 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
213# endif 245# endif
214#endif 246#endif
215 kex->kex[KEX_C25519_SHA256] = kexc25519_client; 247 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
248#ifdef GSSAPI
249 if (options.gss_keyex) {
250 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
251 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
252 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
253 }
254#endif
216 kex->client_version_string=client_version_string; 255 kex->client_version_string=client_version_string;
217 kex->server_version_string=server_version_string; 256 kex->server_version_string=server_version_string;
218 kex->verify_host_key=&verify_host_key_callback; 257 kex->verify_host_key=&verify_host_key_callback;
219 258
259#ifdef GSSAPI
260 if (options.gss_keyex) {
261 kex->gss_deleg_creds = options.gss_deleg_creds;
262 kex->gss_trust_dns = options.gss_trust_dns;
263 kex->gss_client = options.gss_client_identity;
264 if (options.gss_server_identity) {
265 kex->gss_host = options.gss_server_identity;
266 } else {
267 kex->gss_host = gss_host;
268 }
269 }
270#endif
271
220 dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); 272 dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
221 273
222 /* remove ext-info from the KEX proposals for rekeying */ 274 /* remove ext-info from the KEX proposals for rekeying */
@@ -311,6 +363,7 @@ int input_gssapi_token(int type, u_int32_t, void *);
311int input_gssapi_hash(int type, u_int32_t, void *); 363int input_gssapi_hash(int type, u_int32_t, void *);
312int input_gssapi_error(int, u_int32_t, void *); 364int input_gssapi_error(int, u_int32_t, void *);
313int input_gssapi_errtok(int, u_int32_t, void *); 365int input_gssapi_errtok(int, u_int32_t, void *);
366int userauth_gsskeyex(Authctxt *authctxt);
314#endif 367#endif
315 368
316void userauth(Authctxt *, char *); 369void userauth(Authctxt *, char *);
@@ -326,6 +379,11 @@ static char *authmethods_get(void);
326 379
327Authmethod authmethods[] = { 380Authmethod authmethods[] = {
328#ifdef GSSAPI 381#ifdef GSSAPI
382 {"gssapi-keyex",
383 userauth_gsskeyex,
384 NULL,
385 &options.gss_authentication,
386 NULL},
329 {"gssapi-with-mic", 387 {"gssapi-with-mic",
330 userauth_gssapi, 388 userauth_gssapi,
331 NULL, 389 NULL,
@@ -656,19 +714,31 @@ userauth_gssapi(Authctxt *authctxt)
656 static u_int mech = 0; 714 static u_int mech = 0;
657 OM_uint32 min; 715 OM_uint32 min;
658 int ok = 0; 716 int ok = 0;
717 const char *gss_host;
718
719 if (options.gss_server_identity)
720 gss_host = options.gss_server_identity;
721 else if (options.gss_trust_dns)
722 gss_host = get_canonical_hostname(1);
723 else
724 gss_host = authctxt->host;
659 725
660 /* Try one GSSAPI method at a time, rather than sending them all at 726 /* Try one GSSAPI method at a time, rather than sending them all at
661 * once. */ 727 * once. */
662 728
663 if (gss_supported == NULL) 729 if (gss_supported == NULL)
664 gss_indicate_mechs(&min, &gss_supported); 730 if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
731 gss_supported = NULL;
732 return 0;
733 }
665 734
666 /* Check to see if the mechanism is usable before we offer it */ 735 /* Check to see if the mechanism is usable before we offer it */
667 while (mech < gss_supported->count && !ok) { 736 while (mech < gss_supported->count && !ok) {
668 /* My DER encoding requires length<128 */ 737 /* My DER encoding requires length<128 */
669 if (gss_supported->elements[mech].length < 128 && 738 if (gss_supported->elements[mech].length < 128 &&
670 ssh_gssapi_check_mechanism(&gssctxt, 739 ssh_gssapi_check_mechanism(&gssctxt,
671 &gss_supported->elements[mech], authctxt->host)) { 740 &gss_supported->elements[mech], gss_host,
741 options.gss_client_identity)) {
672 ok = 1; /* Mechanism works */ 742 ok = 1; /* Mechanism works */
673 } else { 743 } else {
674 mech++; 744 mech++;
@@ -765,8 +835,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt)
765{ 835{
766 Authctxt *authctxt = ctxt; 836 Authctxt *authctxt = ctxt;
767 Gssctxt *gssctxt; 837 Gssctxt *gssctxt;
768 int oidlen; 838 u_int oidlen;
769 char *oidv; 839 u_char *oidv;
770 840
771 if (authctxt == NULL) 841 if (authctxt == NULL)
772 fatal("input_gssapi_response: no authentication context"); 842 fatal("input_gssapi_response: no authentication context");
@@ -879,6 +949,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt)
879 free(lang); 949 free(lang);
880 return 0; 950 return 0;
881} 951}
952
953int
954userauth_gsskeyex(Authctxt *authctxt)
955{
956 Buffer b;
957 gss_buffer_desc gssbuf;
958 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
959 OM_uint32 ms;
960
961 static int attempt = 0;
962 if (attempt++ >= 1)
963 return (0);
964
965 if (gss_kex_context == NULL) {
966 debug("No valid Key exchange context");
967 return (0);
968 }
969
970 ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
971 "gssapi-keyex");
972
973 gssbuf.value = buffer_ptr(&b);
974 gssbuf.length = buffer_len(&b);
975
976 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
977 buffer_free(&b);
978 return (0);
979 }
980
981 packet_start(SSH2_MSG_USERAUTH_REQUEST);
982 packet_put_cstring(authctxt->server_user);
983 packet_put_cstring(authctxt->service);
984 packet_put_cstring(authctxt->method->name);
985 packet_put_string(mic.value, mic.length);
986 packet_send();
987
988 buffer_free(&b);
989 gss_release_buffer(&ms, &mic);
990
991 return (1);
992}
993
882#endif /* GSSAPI */ 994#endif /* GSSAPI */
883 995
884int 996int
diff --git a/sshd.c b/sshd.c
index 430569c46..5cd9129d0 100644
--- a/sshd.c
+++ b/sshd.c
@@ -125,6 +125,10 @@
125#include "version.h" 125#include "version.h"
126#include "ssherr.h" 126#include "ssherr.h"
127 127
128#ifdef USE_SECURITY_SESSION_API
129#include <Security/AuthSession.h>
130#endif
131
128#ifndef O_NOCTTY 132#ifndef O_NOCTTY
129#define O_NOCTTY 0 133#define O_NOCTTY 0
130#endif 134#endif
@@ -1833,10 +1837,13 @@ main(int ac, char **av)
1833 logit("Disabling protocol version 1. Could not load host key"); 1837 logit("Disabling protocol version 1. Could not load host key");
1834 options.protocol &= ~SSH_PROTO_1; 1838 options.protocol &= ~SSH_PROTO_1;
1835 } 1839 }
1840#ifndef GSSAPI
1841 /* The GSSAPI key exchange can run without a host key */
1836 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { 1842 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
1837 logit("Disabling protocol version 2. Could not load host key"); 1843 logit("Disabling protocol version 2. Could not load host key");
1838 options.protocol &= ~SSH_PROTO_2; 1844 options.protocol &= ~SSH_PROTO_2;
1839 } 1845 }
1846#endif
1840 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { 1847 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
1841 logit("sshd: no hostkeys available -- exiting."); 1848 logit("sshd: no hostkeys available -- exiting.");
1842 exit(1); 1849 exit(1);
@@ -2151,6 +2158,60 @@ main(int ac, char **av)
2151 remote_ip, remote_port, laddr, get_local_port()); 2158 remote_ip, remote_port, laddr, get_local_port());
2152 free(laddr); 2159 free(laddr);
2153 2160
2161#ifdef USE_SECURITY_SESSION_API
2162 /*
2163 * Create a new security session for use by the new user login if
2164 * the current session is the root session or we are not launched
2165 * by inetd (eg: debugging mode or server mode). We do not
2166 * necessarily need to create a session if we are launched from
2167 * inetd because Panther xinetd will create a session for us.
2168 *
2169 * The only case where this logic will fail is if there is an
2170 * inetd running in a non-root session which is not creating
2171 * new sessions for us. Then all the users will end up in the
2172 * same session (bad).
2173 *
2174 * When the client exits, the session will be destroyed for us
2175 * automatically.
2176 *
2177 * We must create the session before any credentials are stored
2178 * (including AFS pags, which happens a few lines below).
2179 */
2180 {
2181 OSStatus err = 0;
2182 SecuritySessionId sid = 0;
2183 SessionAttributeBits sattrs = 0;
2184
2185 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2186 if (err)
2187 error("SessionGetInfo() failed with error %.8X",
2188 (unsigned) err);
2189 else
2190 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2191 (unsigned) sid, (unsigned) sattrs);
2192
2193 if (inetd_flag && !(sattrs & sessionIsRoot))
2194 debug("Running in inetd mode in a non-root session... "
2195 "assuming inetd created the session for us.");
2196 else {
2197 debug("Creating new security session...");
2198 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2199 if (err)
2200 error("SessionCreate() failed with error %.8X",
2201 (unsigned) err);
2202
2203 err = SessionGetInfo(callerSecuritySession, &sid,
2204 &sattrs);
2205 if (err)
2206 error("SessionGetInfo() failed with error %.8X",
2207 (unsigned) err);
2208 else
2209 debug("New Session ID is %.8X / Session Attributes are %.8X",
2210 (unsigned) sid, (unsigned) sattrs);
2211 }
2212 }
2213#endif
2214
2154 /* 2215 /*
2155 * We don't want to listen forever unless the other side 2216 * We don't want to listen forever unless the other side
2156 * successfully authenticates itself. So we set up an alarm which is 2217 * successfully authenticates itself. So we set up an alarm which is
@@ -2571,6 +2632,48 @@ do_ssh2_kex(void)
2571 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2632 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2572 list_hostkey_types()); 2633 list_hostkey_types());
2573 2634
2635#ifdef GSSAPI
2636 {
2637 char *orig;
2638 char *gss = NULL;
2639 char *newstr = NULL;
2640 orig = myproposal[PROPOSAL_KEX_ALGS];
2641
2642 /*
2643 * If we don't have a host key, then there's no point advertising
2644 * the other key exchange algorithms
2645 */
2646
2647 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2648 orig = NULL;
2649
2650 if (options.gss_keyex)
2651 gss = ssh_gssapi_server_mechanisms();
2652 else
2653 gss = NULL;
2654
2655 if (gss && orig)
2656 xasprintf(&newstr, "%s,%s", gss, orig);
2657 else if (gss)
2658 newstr = gss;
2659 else if (orig)
2660 newstr = orig;
2661
2662 /*
2663 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2664 * key alg, but we can't tell people about it unless its the only
2665 * host key algorithm we support
2666 */
2667 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2668 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2669
2670 if (newstr)
2671 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2672 else
2673 fatal("No supported key exchange algorithms");
2674 }
2675#endif
2676
2574 /* start key exchange */ 2677 /* start key exchange */
2575 if ((r = kex_setup(active_state, myproposal)) != 0) 2678 if ((r = kex_setup(active_state, myproposal)) != 0)
2576 fatal("kex_setup: %s", ssh_err(r)); 2679 fatal("kex_setup: %s", ssh_err(r));
@@ -2585,6 +2688,13 @@ do_ssh2_kex(void)
2585# endif 2688# endif
2586#endif 2689#endif
2587 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2690 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2691#ifdef GSSAPI
2692 if (options.gss_keyex) {
2693 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2694 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2695 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2696 }
2697#endif
2588 kex->server = 1; 2698 kex->server = 1;
2589 kex->client_version_string=client_version_string; 2699 kex->client_version_string=client_version_string;
2590 kex->server_version_string=server_version_string; 2700 kex->server_version_string=server_version_string;
diff --git a/sshd_config b/sshd_config
index a848d73e4..f10329840 100644
--- a/sshd_config
+++ b/sshd_config
@@ -84,6 +84,8 @@ AuthorizedKeysFile .ssh/authorized_keys
84# GSSAPI options 84# GSSAPI options
85#GSSAPIAuthentication no 85#GSSAPIAuthentication no
86#GSSAPICleanupCredentials yes 86#GSSAPICleanupCredentials yes
87#GSSAPIStrictAcceptorCheck yes
88#GSSAPIKeyExchange no
87 89
88# Set this to 'yes' to enable PAM authentication, account processing, 90# Set this to 'yes' to enable PAM authentication, account processing,
89# and session processing. If this is enabled, PAM authentication will 91# and session processing. If this is enabled, PAM authentication will
diff --git a/sshd_config.5 b/sshd_config.5
index a37a3aca3..c6d6858f9 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -623,6 +623,11 @@ The default is
623Specifies whether user authentication based on GSSAPI is allowed. 623Specifies whether user authentication based on GSSAPI is allowed.
624The default is 624The default is
625.Dq no . 625.Dq no .
626.It Cm GSSAPIKeyExchange
627Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
628doesn't rely on ssh keys to verify host identity.
629The default is
630.Dq no .
626.It Cm GSSAPICleanupCredentials 631.It Cm GSSAPICleanupCredentials
627Specifies whether to automatically destroy the user's credentials cache 632Specifies whether to automatically destroy the user's credentials cache
628on logout. 633on logout.
@@ -643,6 +648,11 @@ machine's default store.
643This facility is provided to assist with operation on multi homed machines. 648This facility is provided to assist with operation on multi homed machines.
644The default is 649The default is
645.Dq yes . 650.Dq yes .
651.It Cm GSSAPIStoreCredentialsOnRekey
652Controls whether the user's GSSAPI credentials should be updated following a
653successful connection rekeying. This option can be used to accepted renewed
654or updated credentials from a compatible client. The default is
655.Dq no .
646.It Cm HostbasedAcceptedKeyTypes 656.It Cm HostbasedAcceptedKeyTypes
647Specifies the key types that will be accepted for hostbased authentication 657Specifies the key types that will be accepted for hostbased authentication
648as a comma-separated pattern list. 658as a comma-separated pattern list.
diff --git a/sshkey.c b/sshkey.c
index 87b093e91..e595b1149 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -115,6 +115,7 @@ static const struct keytype keytypes[] = {
115# endif /* OPENSSL_HAS_NISTP521 */ 115# endif /* OPENSSL_HAS_NISTP521 */
116# endif /* OPENSSL_HAS_ECC */ 116# endif /* OPENSSL_HAS_ECC */
117#endif /* WITH_OPENSSL */ 117#endif /* WITH_OPENSSL */
118 { "null", "null", KEY_NULL, 0, 0, 0 },
118 { NULL, NULL, -1, -1, 0, 0 } 119 { NULL, NULL, -1, -1, 0, 0 }
119}; 120};
120 121
@@ -203,7 +204,7 @@ key_alg_list(int certs_only, int plain_only)
203 const struct keytype *kt; 204 const struct keytype *kt;
204 205
205 for (kt = keytypes; kt->type != -1; kt++) { 206 for (kt = keytypes; kt->type != -1; kt++) {
206 if (kt->name == NULL || kt->sigonly) 207 if (kt->name == NULL || kt->sigonly || kt->type == KEY_NULL)
207 continue; 208 continue;
208 if ((certs_only && !kt->cert) || (plain_only && kt->cert)) 209 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
209 continue; 210 continue;
diff --git a/sshkey.h b/sshkey.h
index a20a14f9e..2259cbb62 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -62,6 +62,7 @@ enum sshkey_types {
62 KEY_DSA_CERT, 62 KEY_DSA_CERT,
63 KEY_ECDSA_CERT, 63 KEY_ECDSA_CERT,
64 KEY_ED25519_CERT, 64 KEY_ED25519_CERT,
65 KEY_NULL,
65 KEY_UNSPEC 66 KEY_UNSPEC
66}; 67};
67 68