summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2015-11-29 17:36:18 +0000
commit09c4d9b7d41ab3c9973f07e0109e931f57c59c43 (patch)
tree238d7d86bfd8c0080d01fb55cef2ad37df46bd2e
parent651211fd4a199b299540c00c54a46e27fadb04be (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: 2015-11-29 Patch-Name: gssapi.patch
-rw-r--r--ChangeLog.gssapi113
-rw-r--r--Makefile.in5
-rw-r--r--auth-krb5.c17
-rw-r--r--auth2-gss.c48
-rw-r--r--auth2.c2
-rw-r--r--clientloop.c13
-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.536
-rw-r--r--sshconnect2.c124
-rw-r--r--sshd.c110
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.511
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
32 files changed, 1957 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 40cc7aae1..3d2a328e7 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -91,7 +91,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
91 sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \ 91 sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \
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 96
96SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ 97SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
97 sshconnect.o sshconnect1.o sshconnect2.o mux.o \ 98 sshconnect.o sshconnect1.o sshconnect2.o mux.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 roaming_common.o roaming_serv.o \ 112 roaming_common.o roaming_serv.o \
diff --git a/auth-krb5.c b/auth-krb5.c
index 0089b1844..ec4786924 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/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 87ceb3dab..fba1b5496 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -115,6 +115,10 @@
115#include "ssherr.h" 115#include "ssherr.h"
116#include "hostfile.h" 116#include "hostfile.h"
117 117
118#ifdef GSSAPI
119#include "ssh-gss.h"
120#endif
121
118/* import options */ 122/* import options */
119extern Options options; 123extern Options options;
120 124
@@ -1610,6 +1614,15 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1610 /* Do channel operations unless rekeying in progress. */ 1614 /* Do channel operations unless rekeying in progress. */
1611 if (!rekeying) { 1615 if (!rekeying) {
1612 channel_after_select(readset, writeset); 1616 channel_after_select(readset, writeset);
1617
1618#ifdef GSSAPI
1619 if (options.gss_renewal_rekey &&
1620 ssh_gssapi_credentials_updated(NULL)) {
1621 debug("credentials updated - forcing rekey");
1622 need_rekeying = 1;
1623 }
1624#endif
1625
1613 if (need_rekeying || packet_need_rekeying()) { 1626 if (need_rekeying || packet_need_rekeying()) {
1614 debug("need rekeying"); 1627 debug("need rekeying");
1615 active_state->kex->done = 0; 1628 active_state->kex->done = 0;
diff --git a/config.h.in b/config.h.in
index 7500df532..97accd8ec 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1623,6 +1623,9 @@
1623/* Use btmp to log bad logins */ 1623/* Use btmp to log bad logins */
1624#undef USE_BTMP 1624#undef USE_BTMP
1625 1625
1626/* platform uses an in-memory credentials cache */
1627#undef USE_CCAPI
1628
1626/* Use libedit for sftp */ 1629/* Use libedit for sftp */
1627#undef USE_LIBEDIT 1630#undef USE_LIBEDIT
1628 1631
@@ -1638,6 +1641,9 @@
1638/* Use PIPES instead of a socketpair() */ 1641/* Use PIPES instead of a socketpair() */
1639#undef USE_PIPES 1642#undef USE_PIPES
1640 1643
1644/* platform has the Security Authorization Session API */
1645#undef USE_SECURITY_SESSION_API
1646
1641/* Define if you have Solaris process contracts */ 1647/* Define if you have Solaris process contracts */
1642#undef USE_SOLARIS_PROCESS_CONTRACTS 1648#undef USE_SOLARIS_PROCESS_CONTRACTS
1643 1649
diff --git a/configure.ac b/configure.ac
index 9b05c30f8..7a256034d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -625,6 +625,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
625 [Use tunnel device compatibility to OpenBSD]) 625 [Use tunnel device compatibility to OpenBSD])
626 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 626 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
627 [Prepend the address family to IP tunnel traffic]) 627 [Prepend the address family to IP tunnel traffic])
628 AC_MSG_CHECKING([if we have the Security Authorization Session API])
629 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
630 [SessionCreate(0, 0);],
631 [ac_cv_use_security_session_api="yes"
632 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
633 [platform has the Security Authorization Session API])
634 LIBS="$LIBS -framework Security"
635 AC_MSG_RESULT([yes])],
636 [ac_cv_use_security_session_api="no"
637 AC_MSG_RESULT([no])])
638 AC_MSG_CHECKING([if we have an in-memory credentials cache])
639 AC_TRY_COMPILE(
640 [#include <Kerberos/Kerberos.h>],
641 [cc_context_t c;
642 (void) cc_initialize (&c, 0, NULL, NULL);],
643 [AC_DEFINE([USE_CCAPI], [1],
644 [platform uses an in-memory credentials cache])
645 LIBS="$LIBS -framework Security"
646 AC_MSG_RESULT([yes])
647 if test "x$ac_cv_use_security_session_api" = "xno"; then
648 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
649 fi],
650 [AC_MSG_RESULT([no])]
651 )
628 m4_pattern_allow([AU_IPv]) 652 m4_pattern_allow([AU_IPv])
629 AC_CHECK_DECL([AU_IPv4], [], 653 AC_CHECK_DECL([AU_IPv4], [],
630 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 654 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 5100c661d..39a6f98c4 100644
--- a/kex.c
+++ b/kex.c
@@ -55,6 +55,10 @@
55#include "sshbuf.h" 55#include "sshbuf.h"
56#include "digest.h" 56#include "digest.h"
57 57
58#ifdef GSSAPI
59#include "ssh-gss.h"
60#endif
61
58#if OPENSSL_VERSION_NUMBER >= 0x00907000L 62#if OPENSSL_VERSION_NUMBER >= 0x00907000L
59# if defined(HAVE_EVP_SHA256) 63# if defined(HAVE_EVP_SHA256)
60# define evp_ssh_sha256 EVP_sha256 64# define evp_ssh_sha256 EVP_sha256
@@ -97,6 +101,14 @@ static const struct kexalg kexalgs[] = {
97#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ 101#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
98 { NULL, -1, -1, -1}, 102 { NULL, -1, -1, -1},
99}; 103};
104static const struct kexalg kexalg_prefixes[] = {
105#ifdef GSSAPI
106 { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
107 { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
108 { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
109#endif
110 { NULL, -1, -1, -1 },
111};
100 112
101char * 113char *
102kex_alg_list(char sep) 114kex_alg_list(char sep)
@@ -129,6 +141,10 @@ kex_alg_by_name(const char *name)
129 if (strcmp(k->name, name) == 0) 141 if (strcmp(k->name, name) == 0)
130 return k; 142 return k;
131 } 143 }
144 for (k = kexalg_prefixes; k->name != NULL; k++) {
145 if (strncmp(k->name, name, strlen(k->name)) == 0)
146 return k;
147 }
132 return NULL; 148 return NULL;
133} 149}
134 150
diff --git a/kex.h b/kex.h
index d71b53293..ee468157c 100644
--- a/kex.h
+++ b/kex.h
@@ -93,6 +93,9 @@ enum kex_exchange {
93 KEX_DH_GEX_SHA256, 93 KEX_DH_GEX_SHA256,
94 KEX_ECDH_SHA2, 94 KEX_ECDH_SHA2,
95 KEX_C25519_SHA256, 95 KEX_C25519_SHA256,
96 KEX_GSS_GRP1_SHA1,
97 KEX_GSS_GRP14_SHA1,
98 KEX_GSS_GEX_SHA1,
96 KEX_MAX 99 KEX_MAX
97}; 100};
98 101
@@ -139,6 +142,12 @@ struct kex {
139 u_int flags; 142 u_int flags;
140 int hash_alg; 143 int hash_alg;
141 int ec_nid; 144 int ec_nid;
145#ifdef GSSAPI
146 int gss_deleg_creds;
147 int gss_trust_dns;
148 char *gss_host;
149 char *gss_client;
150#endif
142 char *client_version_string; 151 char *client_version_string;
143 char *server_version_string; 152 char *server_version_string;
144 char *failed_choice; 153 char *failed_choice;
@@ -187,6 +196,11 @@ int kexecdh_server(struct ssh *);
187int kexc25519_client(struct ssh *); 196int kexc25519_client(struct ssh *);
188int kexc25519_server(struct ssh *); 197int kexc25519_server(struct ssh *);
189 198
199#ifdef GSSAPI
200int kexgss_client(struct ssh *);
201int kexgss_server(struct ssh *);
202#endif
203
190int kex_dh_hash(const char *, const char *, 204int kex_dh_hash(const char *, const char *,
191 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, 205 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
192 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); 206 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 a91420983..2658aaa53 100644
--- a/monitor.c
+++ b/monitor.c
@@ -157,6 +157,8 @@ int mm_answer_gss_setup_ctx(int, Buffer *);
157int mm_answer_gss_accept_ctx(int, Buffer *); 157int mm_answer_gss_accept_ctx(int, Buffer *);
158int mm_answer_gss_userok(int, Buffer *); 158int mm_answer_gss_userok(int, Buffer *);
159int mm_answer_gss_checkmic(int, Buffer *); 159int mm_answer_gss_checkmic(int, Buffer *);
160int mm_answer_gss_sign(int, Buffer *);
161int mm_answer_gss_updatecreds(int, Buffer *);
160#endif 162#endif
161 163
162#ifdef SSH_AUDIT_EVENTS 164#ifdef SSH_AUDIT_EVENTS
@@ -234,11 +236,18 @@ struct mon_table mon_dispatch_proto20[] = {
234 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, 236 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
235 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, 237 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
236 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, 238 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
239 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
237#endif 240#endif
238 {0, 0, NULL} 241 {0, 0, NULL}
239}; 242};
240 243
241struct mon_table mon_dispatch_postauth20[] = { 244struct mon_table mon_dispatch_postauth20[] = {
245#ifdef GSSAPI
246 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
247 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
248 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
249 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
250#endif
242#ifdef WITH_OPENSSL 251#ifdef WITH_OPENSSL
243 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 252 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
244#endif 253#endif
@@ -353,6 +362,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
353 /* Permit requests for moduli and signatures */ 362 /* Permit requests for moduli and signatures */
354 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 363 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
355 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 364 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
365#ifdef GSSAPI
366 /* and for the GSSAPI key exchange */
367 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
368#endif
356 } else { 369 } else {
357 mon_dispatch = mon_dispatch_proto15; 370 mon_dispatch = mon_dispatch_proto15;
358 371
@@ -461,6 +474,10 @@ monitor_child_postauth(struct monitor *pmonitor)
461 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 474 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
462 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 475 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
463 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 476 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
477#ifdef GSSAPI
478 /* and for the GSSAPI key exchange */
479 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
480#endif
464 } else { 481 } else {
465 mon_dispatch = mon_dispatch_postauth15; 482 mon_dispatch = mon_dispatch_postauth15;
466 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 483 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
@@ -1864,6 +1881,13 @@ monitor_apply_keystate(struct monitor *pmonitor)
1864# endif 1881# endif
1865#endif /* WITH_OPENSSL */ 1882#endif /* WITH_OPENSSL */
1866 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 1883 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1884#ifdef GSSAPI
1885 if (options.gss_keyex) {
1886 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1887 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1888 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1889 }
1890#endif
1867 kex->load_host_public_key=&get_hostkey_public_by_type; 1891 kex->load_host_public_key=&get_hostkey_public_by_type;
1868 kex->load_host_private_key=&get_hostkey_private_by_type; 1892 kex->load_host_private_key=&get_hostkey_private_by_type;
1869 kex->host_key_index=&get_hostkey_index; 1893 kex->host_key_index=&get_hostkey_index;
@@ -1963,6 +1987,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m)
1963 OM_uint32 major; 1987 OM_uint32 major;
1964 u_int len; 1988 u_int len;
1965 1989
1990 if (!options.gss_authentication && !options.gss_keyex)
1991 fatal("In GSSAPI monitor when GSSAPI is disabled");
1992
1966 goid.elements = buffer_get_string(m, &len); 1993 goid.elements = buffer_get_string(m, &len);
1967 goid.length = len; 1994 goid.length = len;
1968 1995
@@ -1990,6 +2017,9 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
1990 OM_uint32 flags = 0; /* GSI needs this */ 2017 OM_uint32 flags = 0; /* GSI needs this */
1991 u_int len; 2018 u_int len;
1992 2019
2020 if (!options.gss_authentication && !options.gss_keyex)
2021 fatal("In GSSAPI monitor when GSSAPI is disabled");
2022
1993 in.value = buffer_get_string(m, &len); 2023 in.value = buffer_get_string(m, &len);
1994 in.length = len; 2024 in.length = len;
1995 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); 2025 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
@@ -2007,6 +2037,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
2007 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 2037 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2008 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 2038 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2009 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 2039 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2040 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2010 } 2041 }
2011 return (0); 2042 return (0);
2012} 2043}
@@ -2018,6 +2049,9 @@ mm_answer_gss_checkmic(int sock, Buffer *m)
2018 OM_uint32 ret; 2049 OM_uint32 ret;
2019 u_int len; 2050 u_int len;
2020 2051
2052 if (!options.gss_authentication && !options.gss_keyex)
2053 fatal("In GSSAPI monitor when GSSAPI is disabled");
2054
2021 gssbuf.value = buffer_get_string(m, &len); 2055 gssbuf.value = buffer_get_string(m, &len);
2022 gssbuf.length = len; 2056 gssbuf.length = len;
2023 mic.value = buffer_get_string(m, &len); 2057 mic.value = buffer_get_string(m, &len);
@@ -2044,7 +2078,11 @@ mm_answer_gss_userok(int sock, Buffer *m)
2044{ 2078{
2045 int authenticated; 2079 int authenticated;
2046 2080
2047 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 2081 if (!options.gss_authentication && !options.gss_keyex)
2082 fatal("In GSSAPI monitor when GSSAPI is disabled");
2083
2084 authenticated = authctxt->valid &&
2085 ssh_gssapi_userok(authctxt->user, authctxt->pw);
2048 2086
2049 buffer_clear(m); 2087 buffer_clear(m);
2050 buffer_put_int(m, authenticated); 2088 buffer_put_int(m, authenticated);
@@ -2057,5 +2095,73 @@ mm_answer_gss_userok(int sock, Buffer *m)
2057 /* Monitor loop will terminate if authenticated */ 2095 /* Monitor loop will terminate if authenticated */
2058 return (authenticated); 2096 return (authenticated);
2059} 2097}
2098
2099int
2100mm_answer_gss_sign(int socket, Buffer *m)
2101{
2102 gss_buffer_desc data;
2103 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2104 OM_uint32 major, minor;
2105 u_int len;
2106
2107 if (!options.gss_authentication && !options.gss_keyex)
2108 fatal("In GSSAPI monitor when GSSAPI is disabled");
2109
2110 data.value = buffer_get_string(m, &len);
2111 data.length = len;
2112 if (data.length != 20)
2113 fatal("%s: data length incorrect: %d", __func__,
2114 (int) data.length);
2115
2116 /* Save the session ID on the first time around */
2117 if (session_id2_len == 0) {
2118 session_id2_len = data.length;
2119 session_id2 = xmalloc(session_id2_len);
2120 memcpy(session_id2, data.value, session_id2_len);
2121 }
2122 major = ssh_gssapi_sign(gsscontext, &data, &hash);
2123
2124 free(data.value);
2125
2126 buffer_clear(m);
2127 buffer_put_int(m, major);
2128 buffer_put_string(m, hash.value, hash.length);
2129
2130 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2131
2132 gss_release_buffer(&minor, &hash);
2133
2134 /* Turn on getpwnam permissions */
2135 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2136
2137 /* And credential updating, for when rekeying */
2138 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2139
2140 return (0);
2141}
2142
2143int
2144mm_answer_gss_updatecreds(int socket, Buffer *m) {
2145 ssh_gssapi_ccache store;
2146 int ok;
2147
2148 store.filename = buffer_get_string(m, NULL);
2149 store.envvar = buffer_get_string(m, NULL);
2150 store.envval = buffer_get_string(m, NULL);
2151
2152 ok = ssh_gssapi_update_creds(&store);
2153
2154 free(store.filename);
2155 free(store.envvar);
2156 free(store.envval);
2157
2158 buffer_clear(m);
2159 buffer_put_int(m, ok);
2160
2161 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2162
2163 return(0);
2164}
2165
2060#endif /* GSSAPI */ 2166#endif /* GSSAPI */
2061 2167
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 eac421ba1..81ceddb8f 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 de4a08f99..975829027 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 1d03bdf72..43b7570a4 100644
--- a/readconf.c
+++ b/readconf.c
@@ -147,6 +147,8 @@ typedef enum {
147 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 147 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
148 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 148 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
149 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 149 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
150 oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
151 oGssServerIdentity,
150 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 152 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
151 oSendEnv, oControlPath, oControlMaster, oControlPersist, 153 oSendEnv, oControlPath, oControlMaster, oControlPersist,
152 oHashKnownHosts, 154 oHashKnownHosts,
@@ -192,10 +194,19 @@ static struct {
192 { "afstokenpassing", oUnsupported }, 194 { "afstokenpassing", oUnsupported },
193#if defined(GSSAPI) 195#if defined(GSSAPI)
194 { "gssapiauthentication", oGssAuthentication }, 196 { "gssapiauthentication", oGssAuthentication },
197 { "gssapikeyexchange", oGssKeyEx },
195 { "gssapidelegatecredentials", oGssDelegateCreds }, 198 { "gssapidelegatecredentials", oGssDelegateCreds },
199 { "gssapitrustdns", oGssTrustDns },
200 { "gssapiclientidentity", oGssClientIdentity },
201 { "gssapiserveridentity", oGssServerIdentity },
202 { "gssapirenewalforcesrekey", oGssRenewalRekey },
196#else 203#else
197 { "gssapiauthentication", oUnsupported }, 204 { "gssapiauthentication", oUnsupported },
205 { "gssapikeyexchange", oUnsupported },
198 { "gssapidelegatecredentials", oUnsupported }, 206 { "gssapidelegatecredentials", oUnsupported },
207 { "gssapitrustdns", oUnsupported },
208 { "gssapiclientidentity", oUnsupported },
209 { "gssapirenewalforcesrekey", oUnsupported },
199#endif 210#endif
200 { "fallbacktorsh", oDeprecated }, 211 { "fallbacktorsh", oDeprecated },
201 { "usersh", oDeprecated }, 212 { "usersh", oDeprecated },
@@ -894,10 +905,30 @@ parse_time:
894 intptr = &options->gss_authentication; 905 intptr = &options->gss_authentication;
895 goto parse_flag; 906 goto parse_flag;
896 907
908 case oGssKeyEx:
909 intptr = &options->gss_keyex;
910 goto parse_flag;
911
897 case oGssDelegateCreds: 912 case oGssDelegateCreds:
898 intptr = &options->gss_deleg_creds; 913 intptr = &options->gss_deleg_creds;
899 goto parse_flag; 914 goto parse_flag;
900 915
916 case oGssTrustDns:
917 intptr = &options->gss_trust_dns;
918 goto parse_flag;
919
920 case oGssClientIdentity:
921 charptr = &options->gss_client_identity;
922 goto parse_string;
923
924 case oGssServerIdentity:
925 charptr = &options->gss_server_identity;
926 goto parse_string;
927
928 case oGssRenewalRekey:
929 intptr = &options->gss_renewal_rekey;
930 goto parse_flag;
931
901 case oBatchMode: 932 case oBatchMode:
902 intptr = &options->batch_mode; 933 intptr = &options->batch_mode;
903 goto parse_flag; 934 goto parse_flag;
@@ -1601,7 +1632,12 @@ initialize_options(Options * options)
1601 options->pubkey_authentication = -1; 1632 options->pubkey_authentication = -1;
1602 options->challenge_response_authentication = -1; 1633 options->challenge_response_authentication = -1;
1603 options->gss_authentication = -1; 1634 options->gss_authentication = -1;
1635 options->gss_keyex = -1;
1604 options->gss_deleg_creds = -1; 1636 options->gss_deleg_creds = -1;
1637 options->gss_trust_dns = -1;
1638 options->gss_renewal_rekey = -1;
1639 options->gss_client_identity = NULL;
1640 options->gss_server_identity = NULL;
1605 options->password_authentication = -1; 1641 options->password_authentication = -1;
1606 options->kbd_interactive_authentication = -1; 1642 options->kbd_interactive_authentication = -1;
1607 options->kbd_interactive_devices = NULL; 1643 options->kbd_interactive_devices = NULL;
@@ -1729,8 +1765,14 @@ fill_default_options(Options * options)
1729 options->challenge_response_authentication = 1; 1765 options->challenge_response_authentication = 1;
1730 if (options->gss_authentication == -1) 1766 if (options->gss_authentication == -1)
1731 options->gss_authentication = 0; 1767 options->gss_authentication = 0;
1768 if (options->gss_keyex == -1)
1769 options->gss_keyex = 0;
1732 if (options->gss_deleg_creds == -1) 1770 if (options->gss_deleg_creds == -1)
1733 options->gss_deleg_creds = 0; 1771 options->gss_deleg_creds = 0;
1772 if (options->gss_trust_dns == -1)
1773 options->gss_trust_dns = 0;
1774 if (options->gss_renewal_rekey == -1)
1775 options->gss_renewal_rekey = 0;
1734 if (options->password_authentication == -1) 1776 if (options->password_authentication == -1)
1735 options->password_authentication = 1; 1777 options->password_authentication = 1;
1736 if (options->kbd_interactive_authentication == -1) 1778 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index bb2d55283..e7e80c344 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 6c7a91e6b..cfe7029e6 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;
@@ -275,10 +277,14 @@ fill_default_server_options(ServerOptions *options)
275 options->kerberos_get_afs_token = 0; 277 options->kerberos_get_afs_token = 0;
276 if (options->gss_authentication == -1) 278 if (options->gss_authentication == -1)
277 options->gss_authentication = 0; 279 options->gss_authentication = 0;
280 if (options->gss_keyex == -1)
281 options->gss_keyex = 0;
278 if (options->gss_cleanup_creds == -1) 282 if (options->gss_cleanup_creds == -1)
279 options->gss_cleanup_creds = 1; 283 options->gss_cleanup_creds = 1;
280 if (options->gss_strict_acceptor == -1) 284 if (options->gss_strict_acceptor == -1)
281 options->gss_strict_acceptor = 0; 285 options->gss_strict_acceptor = 1;
286 if (options->gss_store_rekey == -1)
287 options->gss_store_rekey = 0;
282 if (options->password_authentication == -1) 288 if (options->password_authentication == -1)
283 options->password_authentication = 1; 289 options->password_authentication = 1;
284 if (options->kbd_interactive_authentication == -1) 290 if (options->kbd_interactive_authentication == -1)
@@ -412,6 +418,7 @@ typedef enum {
412 sHostKeyAlgorithms, 418 sHostKeyAlgorithms,
413 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 419 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
414 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 420 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
421 sGssKeyEx, sGssStoreRekey,
415 sAcceptEnv, sPermitTunnel, 422 sAcceptEnv, sPermitTunnel,
416 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 423 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
417 sUsePrivilegeSeparation, sAllowAgentForwarding, 424 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -485,12 +492,20 @@ static struct {
485#ifdef GSSAPI 492#ifdef GSSAPI
486 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 493 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
487 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 494 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
495 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
488 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 496 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
497 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
498 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
489#else 499#else
490 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 500 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
491 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 501 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
502 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
492 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 503 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
504 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
505 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
493#endif 506#endif
507 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
508 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
494 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 509 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
495 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 510 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
496 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 511 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1231,6 +1246,10 @@ process_server_config_line(ServerOptions *options, char *line,
1231 intptr = &options->gss_authentication; 1246 intptr = &options->gss_authentication;
1232 goto parse_flag; 1247 goto parse_flag;
1233 1248
1249 case sGssKeyEx:
1250 intptr = &options->gss_keyex;
1251 goto parse_flag;
1252
1234 case sGssCleanupCreds: 1253 case sGssCleanupCreds:
1235 intptr = &options->gss_cleanup_creds; 1254 intptr = &options->gss_cleanup_creds;
1236 goto parse_flag; 1255 goto parse_flag;
@@ -1239,6 +1258,10 @@ process_server_config_line(ServerOptions *options, char *line,
1239 intptr = &options->gss_strict_acceptor; 1258 intptr = &options->gss_strict_acceptor;
1240 goto parse_flag; 1259 goto parse_flag;
1241 1260
1261 case sGssStoreRekey:
1262 intptr = &options->gss_store_rekey;
1263 goto parse_flag;
1264
1242 case sPasswordAuthentication: 1265 case sPasswordAuthentication:
1243 intptr = &options->password_authentication; 1266 intptr = &options->password_authentication;
1244 goto parse_flag; 1267 goto parse_flag;
@@ -2246,7 +2269,10 @@ dump_config(ServerOptions *o)
2246#endif 2269#endif
2247#ifdef GSSAPI 2270#ifdef GSSAPI
2248 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2271 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2272 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2249 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2273 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2274 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2275 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2250#endif 2276#endif
2251 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2277 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2252 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2278 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 03a228fbd..228e5abce 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 a47f3ca9e..cac8cdaa7 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -749,11 +749,45 @@ Specifies whether user authentication based on GSSAPI is allowed.
749The default is 749The default is
750.Dq no . 750.Dq no .
751Note that this option applies to protocol version 2 only. 751Note that this option applies to protocol version 2 only.
752.It Cm GSSAPIKeyExchange
753Specifies whether key exchange based on GSSAPI may be used. When using
754GSSAPI key exchange the server need not have a host key.
755The default is
756.Dq no .
757Note that this option applies to protocol version 2 only.
758.It Cm GSSAPIClientIdentity
759If set, specifies the GSSAPI client identity that ssh should use when
760connecting to the server. The default is unset, which means that the default
761identity will be used.
762.It Cm GSSAPIServerIdentity
763If set, specifies the GSSAPI server identity that ssh should expect when
764connecting to the server. The default is unset, which means that the
765expected GSSAPI server identity will be determined from the target
766hostname.
752.It Cm GSSAPIDelegateCredentials 767.It Cm GSSAPIDelegateCredentials
753Forward (delegate) credentials to the server. 768Forward (delegate) credentials to the server.
754The default is 769The default is
755.Dq no . 770.Dq no .
756Note that this option applies to protocol version 2 only. 771Note that this option applies to protocol version 2 connections using GSSAPI.
772.It Cm GSSAPIRenewalForcesRekey
773If set to
774.Dq yes
775then renewal of the client's GSSAPI credentials will force the rekeying of the
776ssh connection. With a compatible server, this can delegate the renewed
777credentials to a session on the server.
778The default is
779.Dq no .
780.It Cm GSSAPITrustDns
781Set to
782.Dq yes
783to indicate that the DNS is trusted to securely canonicalize
784the name of the host being connected to. If
785.Dq no ,
786the hostname entered on the
787command line will be passed untouched to the GSSAPI library.
788The default is
789.Dq no .
790This option only applies to protocol version 2 connections using GSSAPI.
757.It Cm HashKnownHosts 791.It Cm HashKnownHosts
758Indicates that 792Indicates that
759.Xr ssh 1 793.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index 775103185..e2ea82656 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -160,9 +160,34 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
160 struct kex *kex; 160 struct kex *kex;
161 int r; 161 int r;
162 162
163#ifdef GSSAPI
164 char *orig = NULL, *gss = NULL;
165 char *gss_host = NULL;
166#endif
167
163 xxx_host = host; 168 xxx_host = host;
164 xxx_hostaddr = hostaddr; 169 xxx_hostaddr = hostaddr;
165 170
171#ifdef GSSAPI
172 if (options.gss_keyex) {
173 /* Add the GSSAPI mechanisms currently supported on this
174 * client to the key exchange algorithm proposal */
175 orig = myproposal[PROPOSAL_KEX_ALGS];
176
177 if (options.gss_trust_dns)
178 gss_host = (char *)get_canonical_hostname(1);
179 else
180 gss_host = host;
181
182 gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity);
183 if (gss) {
184 debug("Offering GSSAPI proposal: %s", gss);
185 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
186 "%s,%s", gss, orig);
187 }
188 }
189#endif
190
166 myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( 191 myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
167 options.kex_algorithms); 192 options.kex_algorithms);
168 myproposal[PROPOSAL_ENC_ALGS_CTOS] = 193 myproposal[PROPOSAL_ENC_ALGS_CTOS] =
@@ -193,6 +218,17 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
193 order_hostkeyalgs(host, hostaddr, port)); 218 order_hostkeyalgs(host, hostaddr, port));
194 } 219 }
195 220
221#ifdef GSSAPI
222 /* If we've got GSSAPI algorithms, then we also support the
223 * 'null' hostkey, as a last resort */
224 if (options.gss_keyex && gss) {
225 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
226 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
227 "%s,null", orig);
228 free(gss);
229 }
230#endif
231
196 if (options.rekey_limit || options.rekey_interval) 232 if (options.rekey_limit || options.rekey_interval)
197 packet_set_rekey_limits((u_int32_t)options.rekey_limit, 233 packet_set_rekey_limits((u_int32_t)options.rekey_limit,
198 (time_t)options.rekey_interval); 234 (time_t)options.rekey_interval);
@@ -211,10 +247,30 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
211# endif 247# endif
212#endif 248#endif
213 kex->kex[KEX_C25519_SHA256] = kexc25519_client; 249 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
250#ifdef GSSAPI
251 if (options.gss_keyex) {
252 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
253 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
254 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
255 }
256#endif
214 kex->client_version_string=client_version_string; 257 kex->client_version_string=client_version_string;
215 kex->server_version_string=server_version_string; 258 kex->server_version_string=server_version_string;
216 kex->verify_host_key=&verify_host_key_callback; 259 kex->verify_host_key=&verify_host_key_callback;
217 260
261#ifdef GSSAPI
262 if (options.gss_keyex) {
263 kex->gss_deleg_creds = options.gss_deleg_creds;
264 kex->gss_trust_dns = options.gss_trust_dns;
265 kex->gss_client = options.gss_client_identity;
266 if (options.gss_server_identity) {
267 kex->gss_host = options.gss_server_identity;
268 } else {
269 kex->gss_host = gss_host;
270 }
271 }
272#endif
273
218 dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); 274 dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
219 275
220 if (options.use_roaming && !kex->roaming) { 276 if (options.use_roaming && !kex->roaming) {
@@ -306,6 +362,7 @@ int input_gssapi_token(int type, u_int32_t, void *);
306int input_gssapi_hash(int type, u_int32_t, void *); 362int input_gssapi_hash(int type, u_int32_t, void *);
307int input_gssapi_error(int, u_int32_t, void *); 363int input_gssapi_error(int, u_int32_t, void *);
308int input_gssapi_errtok(int, u_int32_t, void *); 364int input_gssapi_errtok(int, u_int32_t, void *);
365int userauth_gsskeyex(Authctxt *authctxt);
309#endif 366#endif
310 367
311void userauth(Authctxt *, char *); 368void userauth(Authctxt *, char *);
@@ -321,6 +378,11 @@ static char *authmethods_get(void);
321 378
322Authmethod authmethods[] = { 379Authmethod authmethods[] = {
323#ifdef GSSAPI 380#ifdef GSSAPI
381 {"gssapi-keyex",
382 userauth_gsskeyex,
383 NULL,
384 &options.gss_authentication,
385 NULL},
324 {"gssapi-with-mic", 386 {"gssapi-with-mic",
325 userauth_gssapi, 387 userauth_gssapi,
326 NULL, 388 NULL,
@@ -627,19 +689,31 @@ userauth_gssapi(Authctxt *authctxt)
627 static u_int mech = 0; 689 static u_int mech = 0;
628 OM_uint32 min; 690 OM_uint32 min;
629 int ok = 0; 691 int ok = 0;
692 const char *gss_host;
693
694 if (options.gss_server_identity)
695 gss_host = options.gss_server_identity;
696 else if (options.gss_trust_dns)
697 gss_host = get_canonical_hostname(1);
698 else
699 gss_host = authctxt->host;
630 700
631 /* Try one GSSAPI method at a time, rather than sending them all at 701 /* Try one GSSAPI method at a time, rather than sending them all at
632 * once. */ 702 * once. */
633 703
634 if (gss_supported == NULL) 704 if (gss_supported == NULL)
635 gss_indicate_mechs(&min, &gss_supported); 705 if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
706 gss_supported = NULL;
707 return 0;
708 }
636 709
637 /* Check to see if the mechanism is usable before we offer it */ 710 /* Check to see if the mechanism is usable before we offer it */
638 while (mech < gss_supported->count && !ok) { 711 while (mech < gss_supported->count && !ok) {
639 /* My DER encoding requires length<128 */ 712 /* My DER encoding requires length<128 */
640 if (gss_supported->elements[mech].length < 128 && 713 if (gss_supported->elements[mech].length < 128 &&
641 ssh_gssapi_check_mechanism(&gssctxt, 714 ssh_gssapi_check_mechanism(&gssctxt,
642 &gss_supported->elements[mech], authctxt->host)) { 715 &gss_supported->elements[mech], gss_host,
716 options.gss_client_identity)) {
643 ok = 1; /* Mechanism works */ 717 ok = 1; /* Mechanism works */
644 } else { 718 } else {
645 mech++; 719 mech++;
@@ -736,8 +810,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt)
736{ 810{
737 Authctxt *authctxt = ctxt; 811 Authctxt *authctxt = ctxt;
738 Gssctxt *gssctxt; 812 Gssctxt *gssctxt;
739 int oidlen; 813 u_int oidlen;
740 char *oidv; 814 u_char *oidv;
741 815
742 if (authctxt == NULL) 816 if (authctxt == NULL)
743 fatal("input_gssapi_response: no authentication context"); 817 fatal("input_gssapi_response: no authentication context");
@@ -850,6 +924,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt)
850 free(lang); 924 free(lang);
851 return 0; 925 return 0;
852} 926}
927
928int
929userauth_gsskeyex(Authctxt *authctxt)
930{
931 Buffer b;
932 gss_buffer_desc gssbuf;
933 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
934 OM_uint32 ms;
935
936 static int attempt = 0;
937 if (attempt++ >= 1)
938 return (0);
939
940 if (gss_kex_context == NULL) {
941 debug("No valid Key exchange context");
942 return (0);
943 }
944
945 ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
946 "gssapi-keyex");
947
948 gssbuf.value = buffer_ptr(&b);
949 gssbuf.length = buffer_len(&b);
950
951 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
952 buffer_free(&b);
953 return (0);
954 }
955
956 packet_start(SSH2_MSG_USERAUTH_REQUEST);
957 packet_put_cstring(authctxt->server_user);
958 packet_put_cstring(authctxt->service);
959 packet_put_cstring(authctxt->method->name);
960 packet_put_string(mic.value, mic.length);
961 packet_send();
962
963 buffer_free(&b);
964 gss_release_buffer(&ms, &mic);
965
966 return (1);
967}
968
853#endif /* GSSAPI */ 969#endif /* GSSAPI */
854 970
855int 971int
diff --git a/sshd.c b/sshd.c
index 65ef7e850..839c2e02d 100644
--- a/sshd.c
+++ b/sshd.c
@@ -126,6 +126,10 @@
126#include "version.h" 126#include "version.h"
127#include "ssherr.h" 127#include "ssherr.h"
128 128
129#ifdef USE_SECURITY_SESSION_API
130#include <Security/AuthSession.h>
131#endif
132
129#ifndef O_NOCTTY 133#ifndef O_NOCTTY
130#define O_NOCTTY 0 134#define O_NOCTTY 0
131#endif 135#endif
@@ -1827,10 +1831,13 @@ main(int ac, char **av)
1827 logit("Disabling protocol version 1. Could not load host key"); 1831 logit("Disabling protocol version 1. Could not load host key");
1828 options.protocol &= ~SSH_PROTO_1; 1832 options.protocol &= ~SSH_PROTO_1;
1829 } 1833 }
1834#ifndef GSSAPI
1835 /* The GSSAPI key exchange can run without a host key */
1830 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { 1836 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
1831 logit("Disabling protocol version 2. Could not load host key"); 1837 logit("Disabling protocol version 2. Could not load host key");
1832 options.protocol &= ~SSH_PROTO_2; 1838 options.protocol &= ~SSH_PROTO_2;
1833 } 1839 }
1840#endif
1834 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { 1841 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
1835 logit("sshd: no hostkeys available -- exiting."); 1842 logit("sshd: no hostkeys available -- exiting.");
1836 exit(1); 1843 exit(1);
@@ -2145,6 +2152,60 @@ main(int ac, char **av)
2145 remote_ip, remote_port, laddr, get_local_port()); 2152 remote_ip, remote_port, laddr, get_local_port());
2146 free(laddr); 2153 free(laddr);
2147 2154
2155#ifdef USE_SECURITY_SESSION_API
2156 /*
2157 * Create a new security session for use by the new user login if
2158 * the current session is the root session or we are not launched
2159 * by inetd (eg: debugging mode or server mode). We do not
2160 * necessarily need to create a session if we are launched from
2161 * inetd because Panther xinetd will create a session for us.
2162 *
2163 * The only case where this logic will fail is if there is an
2164 * inetd running in a non-root session which is not creating
2165 * new sessions for us. Then all the users will end up in the
2166 * same session (bad).
2167 *
2168 * When the client exits, the session will be destroyed for us
2169 * automatically.
2170 *
2171 * We must create the session before any credentials are stored
2172 * (including AFS pags, which happens a few lines below).
2173 */
2174 {
2175 OSStatus err = 0;
2176 SecuritySessionId sid = 0;
2177 SessionAttributeBits sattrs = 0;
2178
2179 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2180 if (err)
2181 error("SessionGetInfo() failed with error %.8X",
2182 (unsigned) err);
2183 else
2184 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2185 (unsigned) sid, (unsigned) sattrs);
2186
2187 if (inetd_flag && !(sattrs & sessionIsRoot))
2188 debug("Running in inetd mode in a non-root session... "
2189 "assuming inetd created the session for us.");
2190 else {
2191 debug("Creating new security session...");
2192 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2193 if (err)
2194 error("SessionCreate() failed with error %.8X",
2195 (unsigned) err);
2196
2197 err = SessionGetInfo(callerSecuritySession, &sid,
2198 &sattrs);
2199 if (err)
2200 error("SessionGetInfo() failed with error %.8X",
2201 (unsigned) err);
2202 else
2203 debug("New Session ID is %.8X / Session Attributes are %.8X",
2204 (unsigned) sid, (unsigned) sattrs);
2205 }
2206 }
2207#endif
2208
2148 /* 2209 /*
2149 * We don't want to listen forever unless the other side 2210 * We don't want to listen forever unless the other side
2150 * successfully authenticates itself. So we set up an alarm which is 2211 * successfully authenticates itself. So we set up an alarm which is
@@ -2563,6 +2624,48 @@ do_ssh2_kex(void)
2563 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2624 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2564 list_hostkey_types()); 2625 list_hostkey_types());
2565 2626
2627#ifdef GSSAPI
2628 {
2629 char *orig;
2630 char *gss = NULL;
2631 char *newstr = NULL;
2632 orig = myproposal[PROPOSAL_KEX_ALGS];
2633
2634 /*
2635 * If we don't have a host key, then there's no point advertising
2636 * the other key exchange algorithms
2637 */
2638
2639 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2640 orig = NULL;
2641
2642 if (options.gss_keyex)
2643 gss = ssh_gssapi_server_mechanisms();
2644 else
2645 gss = NULL;
2646
2647 if (gss && orig)
2648 xasprintf(&newstr, "%s,%s", gss, orig);
2649 else if (gss)
2650 newstr = gss;
2651 else if (orig)
2652 newstr = orig;
2653
2654 /*
2655 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2656 * key alg, but we can't tell people about it unless its the only
2657 * host key algorithm we support
2658 */
2659 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2660 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2661
2662 if (newstr)
2663 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2664 else
2665 fatal("No supported key exchange algorithms");
2666 }
2667#endif
2668
2566 /* start key exchange */ 2669 /* start key exchange */
2567 if ((r = kex_setup(active_state, myproposal)) != 0) 2670 if ((r = kex_setup(active_state, myproposal)) != 0)
2568 fatal("kex_setup: %s", ssh_err(r)); 2671 fatal("kex_setup: %s", ssh_err(r));
@@ -2577,6 +2680,13 @@ do_ssh2_kex(void)
2577# endif 2680# endif
2578#endif 2681#endif
2579 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2682 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2683#ifdef GSSAPI
2684 if (options.gss_keyex) {
2685 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2686 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2687 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2688 }
2689#endif
2580 kex->server = 1; 2690 kex->server = 1;
2581 kex->client_version_string=client_version_string; 2691 kex->client_version_string=client_version_string;
2582 kex->server_version_string=server_version_string; 2692 kex->server_version_string=server_version_string;
diff --git a/sshd_config b/sshd_config
index 4d77f05aa..64786c935 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 b18d340af..5491c89cf 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -621,6 +621,12 @@ Specifies whether user authentication based on GSSAPI is allowed.
621The default is 621The default is
622.Dq no . 622.Dq no .
623Note that this option applies to protocol version 2 only. 623Note that this option applies to protocol version 2 only.
624.It Cm GSSAPIKeyExchange
625Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
626doesn't rely on ssh keys to verify host identity.
627The default is
628.Dq no .
629Note that this option applies to protocol version 2 only.
624.It Cm GSSAPICleanupCredentials 630.It Cm GSSAPICleanupCredentials
625Specifies whether to automatically destroy the user's credentials cache 631Specifies whether to automatically destroy the user's credentials cache
626on logout. 632on logout.
@@ -642,6 +648,11 @@ machine's default store.
642This facility is provided to assist with operation on multi homed machines. 648This facility is provided to assist with operation on multi homed machines.
643The default is 649The default is
644.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 .
645.It Cm HostbasedAcceptedKeyTypes 656.It Cm HostbasedAcceptedKeyTypes
646Specifies the key types that will be accepted for hostbased authentication 657Specifies the key types that will be accepted for hostbased authentication
647as a comma-separated pattern list. 658as a comma-separated pattern list.
diff --git a/sshkey.c b/sshkey.c
index 32dd8f225..5368e7cd3 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -112,6 +112,7 @@ static const struct keytype keytypes[] = {
112# endif /* OPENSSL_HAS_NISTP521 */ 112# endif /* OPENSSL_HAS_NISTP521 */
113# endif /* OPENSSL_HAS_ECC */ 113# endif /* OPENSSL_HAS_ECC */
114#endif /* WITH_OPENSSL */ 114#endif /* WITH_OPENSSL */
115 { "null", "null", KEY_NULL, 0, 0 },
115 { NULL, NULL, -1, -1, 0 } 116 { NULL, NULL, -1, -1, 0 }
116}; 117};
117 118
@@ -200,7 +201,7 @@ key_alg_list(int certs_only, int plain_only)
200 const struct keytype *kt; 201 const struct keytype *kt;
201 202
202 for (kt = keytypes; kt->type != -1; kt++) { 203 for (kt = keytypes; kt->type != -1; kt++) {
203 if (kt->name == NULL) 204 if (kt->name == NULL || kt->type == KEY_NULL)
204 continue; 205 continue;
205 if ((certs_only && !kt->cert) || (plain_only && kt->cert)) 206 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
206 continue; 207 continue;
diff --git a/sshkey.h b/sshkey.h
index c8d3cddca..5cf4e5d8f 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