summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 dc0e557ad..77d549822 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -114,6 +114,10 @@
114#include "ssherr.h" 114#include "ssherr.h"
115#include "hostfile.h" 115#include "hostfile.h"
116 116
117#ifdef GSSAPI
118#include "ssh-gss.h"
119#endif
120
117/* import options */ 121/* import options */
118extern Options options; 122extern Options options;
119 123
@@ -1609,6 +1613,15 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1609 /* Do channel operations unless rekeying in progress. */ 1613 /* Do channel operations unless rekeying in progress. */
1610 if (!rekeying) { 1614 if (!rekeying) {
1611 channel_after_select(readset, writeset); 1615 channel_after_select(readset, writeset);
1616
1617#ifdef GSSAPI
1618 if (options.gss_renewal_rekey &&
1619 ssh_gssapi_credentials_updated(NULL)) {
1620 debug("credentials updated - forcing rekey");
1621 need_rekeying = 1;
1622 }
1623#endif
1624
1612 if (need_rekeying || packet_need_rekeying()) { 1625 if (need_rekeying || packet_need_rekeying()) {
1613 debug("need rekeying"); 1626 debug("need rekeying");
1614 active_state->kex->done = 0; 1627 active_state->kex->done = 0;
diff --git a/config.h.in b/config.h.in
index 7e7e38ec2..6c7de98a2 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 bb0095f64..df2169375 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 dbc55ef7e..4d8e6f536 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 f70b81fc1..7194b1453 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 int (*verify_host_key)(struct sshkey *, struct ssh *); 153 int (*verify_host_key)(struct sshkey *, struct ssh *);
@@ -184,6 +193,11 @@ int kexecdh_server(struct ssh *);
184int kexc25519_client(struct ssh *); 193int kexc25519_client(struct ssh *);
185int kexc25519_server(struct ssh *); 194int kexc25519_server(struct ssh *);
186 195
196#ifdef GSSAPI
197int kexgss_client(struct ssh *);
198int kexgss_server(struct ssh *);
199#endif
200
187int kex_dh_hash(const char *, const char *, 201int kex_dh_hash(const char *, const char *,
188 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, 202 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
189 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); 203 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 b4109657e..bdc297292 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 e6217b3d4..71e7c08e2 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1069,7 +1069,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1069} 1069}
1070 1070
1071int 1071int
1072mm_ssh_gssapi_userok(char *user) 1072mm_ssh_gssapi_userok(char *user, struct passwd *pw)
1073{ 1073{
1074 Buffer m; 1074 Buffer m;
1075 int authenticated = 0; 1075 int authenticated = 0;
@@ -1086,5 +1086,50 @@ mm_ssh_gssapi_userok(char *user)
1086 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1086 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1087 return (authenticated); 1087 return (authenticated);
1088} 1088}
1089
1090OM_uint32
1091mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1092{
1093 Buffer m;
1094 OM_uint32 major;
1095 u_int len;
1096
1097 buffer_init(&m);
1098 buffer_put_string(&m, data->value, data->length);
1099
1100 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
1101 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
1102
1103 major = buffer_get_int(&m);
1104 hash->value = buffer_get_string(&m, &len);
1105 hash->length = len;
1106
1107 buffer_free(&m);
1108
1109 return(major);
1110}
1111
1112int
1113mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1114{
1115 Buffer m;
1116 int ok;
1117
1118 buffer_init(&m);
1119
1120 buffer_put_cstring(&m, store->filename ? store->filename : "");
1121 buffer_put_cstring(&m, store->envvar ? store->envvar : "");
1122 buffer_put_cstring(&m, store->envval ? store->envval : "");
1123
1124 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m);
1125 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m);
1126
1127 ok = buffer_get_int(&m);
1128
1129 buffer_free(&m);
1130
1131 return (ok);
1132}
1133
1089#endif /* GSSAPI */ 1134#endif /* GSSAPI */
1090 1135
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 db7d0bbbf..68dac7605 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,
@@ -191,10 +193,19 @@ static struct {
191 { "afstokenpassing", oUnsupported }, 193 { "afstokenpassing", oUnsupported },
192#if defined(GSSAPI) 194#if defined(GSSAPI)
193 { "gssapiauthentication", oGssAuthentication }, 195 { "gssapiauthentication", oGssAuthentication },
196 { "gssapikeyexchange", oGssKeyEx },
194 { "gssapidelegatecredentials", oGssDelegateCreds }, 197 { "gssapidelegatecredentials", oGssDelegateCreds },
198 { "gssapitrustdns", oGssTrustDns },
199 { "gssapiclientidentity", oGssClientIdentity },
200 { "gssapiserveridentity", oGssServerIdentity },
201 { "gssapirenewalforcesrekey", oGssRenewalRekey },
195#else 202#else
196 { "gssapiauthentication", oUnsupported }, 203 { "gssapiauthentication", oUnsupported },
204 { "gssapikeyexchange", oUnsupported },
197 { "gssapidelegatecredentials", oUnsupported }, 205 { "gssapidelegatecredentials", oUnsupported },
206 { "gssapitrustdns", oUnsupported },
207 { "gssapiclientidentity", oUnsupported },
208 { "gssapirenewalforcesrekey", oUnsupported },
198#endif 209#endif
199 { "fallbacktorsh", oDeprecated }, 210 { "fallbacktorsh", oDeprecated },
200 { "usersh", oDeprecated }, 211 { "usersh", oDeprecated },
@@ -892,10 +903,30 @@ parse_time:
892 intptr = &options->gss_authentication; 903 intptr = &options->gss_authentication;
893 goto parse_flag; 904 goto parse_flag;
894 905
906 case oGssKeyEx:
907 intptr = &options->gss_keyex;
908 goto parse_flag;
909
895 case oGssDelegateCreds: 910 case oGssDelegateCreds:
896 intptr = &options->gss_deleg_creds; 911 intptr = &options->gss_deleg_creds;
897 goto parse_flag; 912 goto parse_flag;
898 913
914 case oGssTrustDns:
915 intptr = &options->gss_trust_dns;
916 goto parse_flag;
917
918 case oGssClientIdentity:
919 charptr = &options->gss_client_identity;
920 goto parse_string;
921
922 case oGssServerIdentity:
923 charptr = &options->gss_server_identity;
924 goto parse_string;
925
926 case oGssRenewalRekey:
927 intptr = &options->gss_renewal_rekey;
928 goto parse_flag;
929
899 case oBatchMode: 930 case oBatchMode:
900 intptr = &options->batch_mode; 931 intptr = &options->batch_mode;
901 goto parse_flag; 932 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;
@@ -1728,8 +1764,14 @@ fill_default_options(Options * options)
1728 options->challenge_response_authentication = 1; 1764 options->challenge_response_authentication = 1;
1729 if (options->gss_authentication == -1) 1765 if (options->gss_authentication == -1)
1730 options->gss_authentication = 0; 1766 options->gss_authentication = 0;
1767 if (options->gss_keyex == -1)
1768 options->gss_keyex = 0;
1731 if (options->gss_deleg_creds == -1) 1769 if (options->gss_deleg_creds == -1)
1732 options->gss_deleg_creds = 0; 1770 options->gss_deleg_creds = 0;
1771 if (options->gss_trust_dns == -1)
1772 options->gss_trust_dns = 0;
1773 if (options->gss_renewal_rekey == -1)
1774 options->gss_renewal_rekey = 0;
1733 if (options->password_authentication == -1) 1775 if (options->password_authentication == -1)
1734 options->password_authentication = 1; 1776 options->password_authentication = 1;
1735 if (options->kbd_interactive_authentication == -1) 1777 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index 576b9e352..ef39c4ca4 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 df93fc450..2f7f41e5c 100644
--- a/servconf.c
+++ b/servconf.c
@@ -115,8 +115,10 @@ initialize_server_options(ServerOptions *options)
115 options->kerberos_ticket_cleanup = -1; 115 options->kerberos_ticket_cleanup = -1;
116 options->kerberos_get_afs_token = -1; 116 options->kerberos_get_afs_token = -1;
117 options->gss_authentication=-1; 117 options->gss_authentication=-1;
118 options->gss_keyex = -1;
118 options->gss_cleanup_creds = -1; 119 options->gss_cleanup_creds = -1;
119 options->gss_strict_acceptor = -1; 120 options->gss_strict_acceptor = -1;
121 options->gss_store_rekey = -1;
120 options->password_authentication = -1; 122 options->password_authentication = -1;
121 options->kbd_interactive_authentication = -1; 123 options->kbd_interactive_authentication = -1;
122 options->challenge_response_authentication = -1; 124 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)
@@ -401,6 +407,7 @@ typedef enum {
401 sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes, 407 sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes,
402 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 408 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
403 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 409 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
410 sGssKeyEx, sGssStoreRekey,
404 sAcceptEnv, sPermitTunnel, 411 sAcceptEnv, sPermitTunnel,
405 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 412 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
406 sUsePrivilegeSeparation, sAllowAgentForwarding, 413 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -473,12 +480,20 @@ static struct {
473#ifdef GSSAPI 480#ifdef GSSAPI
474 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 481 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
475 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 482 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
483 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
476 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 484 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
485 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
486 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
477#else 487#else
478 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 488 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
479 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 489 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
490 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
480 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 491 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
492 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
493 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
481#endif 494#endif
495 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
496 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
482 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 497 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
483 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 498 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
484 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 499 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1214,6 +1229,10 @@ process_server_config_line(ServerOptions *options, char *line,
1214 intptr = &options->gss_authentication; 1229 intptr = &options->gss_authentication;
1215 goto parse_flag; 1230 goto parse_flag;
1216 1231
1232 case sGssKeyEx:
1233 intptr = &options->gss_keyex;
1234 goto parse_flag;
1235
1217 case sGssCleanupCreds: 1236 case sGssCleanupCreds:
1218 intptr = &options->gss_cleanup_creds; 1237 intptr = &options->gss_cleanup_creds;
1219 goto parse_flag; 1238 goto parse_flag;
@@ -1222,6 +1241,10 @@ process_server_config_line(ServerOptions *options, char *line,
1222 intptr = &options->gss_strict_acceptor; 1241 intptr = &options->gss_strict_acceptor;
1223 goto parse_flag; 1242 goto parse_flag;
1224 1243
1244 case sGssStoreRekey:
1245 intptr = &options->gss_store_rekey;
1246 goto parse_flag;
1247
1225 case sPasswordAuthentication: 1248 case sPasswordAuthentication:
1226 intptr = &options->password_authentication; 1249 intptr = &options->password_authentication;
1227 goto parse_flag; 1250 goto parse_flag;
@@ -2229,7 +2252,10 @@ dump_config(ServerOptions *o)
2229#endif 2252#endif
2230#ifdef GSSAPI 2253#ifdef GSSAPI
2231 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2254 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2255 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2232 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2256 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2257 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2258 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2233#endif 2259#endif
2234 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2260 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2235 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2261 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 606d80c9d..b99b27067 100644
--- a/servconf.h
+++ b/servconf.h
@@ -117,8 +117,10 @@ typedef struct {
117 int kerberos_get_afs_token; /* If true, try to get AFS token if 117 int kerberos_get_afs_token; /* If true, try to get AFS token if
118 * authenticated with Kerberos. */ 118 * authenticated with Kerberos. */
119 int gss_authentication; /* If true, permit GSSAPI authentication */ 119 int gss_authentication; /* If true, permit GSSAPI authentication */
120 int gss_keyex; /* If true, permit GSSAPI key exchange */
120 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 121 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
121 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 122 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
123 int gss_store_rekey;
122 int password_authentication; /* If true, permit password 124 int password_authentication; /* If true, permit password
123 * authentication. */ 125 * authentication. */
124 int kbd_interactive_authentication; /* If true, permit */ 126 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 268a627b2..59ce400dc 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -744,11 +744,45 @@ Specifies whether user authentication based on GSSAPI is allowed.
744The default is 744The default is
745.Dq no . 745.Dq no .
746Note that this option applies to protocol version 2 only. 746Note that this option applies to protocol version 2 only.
747.It Cm GSSAPIKeyExchange
748Specifies whether key exchange based on GSSAPI may be used. When using
749GSSAPI key exchange the server need not have a host key.
750The default is
751.Dq no .
752Note that this option applies to protocol version 2 only.
753.It Cm GSSAPIClientIdentity
754If set, specifies the GSSAPI client identity that ssh should use when
755connecting to the server. The default is unset, which means that the default
756identity will be used.
757.It Cm GSSAPIServerIdentity
758If set, specifies the GSSAPI server identity that ssh should expect when
759connecting to the server. The default is unset, which means that the
760expected GSSAPI server identity will be determined from the target
761hostname.
747.It Cm GSSAPIDelegateCredentials 762.It Cm GSSAPIDelegateCredentials
748Forward (delegate) credentials to the server. 763Forward (delegate) credentials to the server.
749The default is 764The default is
750.Dq no . 765.Dq no .
751Note that this option applies to protocol version 2 only. 766Note that this option applies to protocol version 2 connections using GSSAPI.
767.It Cm GSSAPIRenewalForcesRekey
768If set to
769.Dq yes
770then renewal of the client's GSSAPI credentials will force the rekeying of the
771ssh connection. With a compatible server, this can delegate the renewed
772credentials to a session on the server.
773The default is
774.Dq no .
775.It Cm GSSAPITrustDns
776Set to
777.Dq yes
778to indicate that the DNS is trusted to securely canonicalize
779the name of the host being connected to. If
780.Dq no ,
781the hostname entered on the
782command line will be passed untouched to the GSSAPI library.
783The default is
784.Dq no .
785This option only applies to protocol version 2 connections using GSSAPI.
752.It Cm HashKnownHosts 786.It Cm HashKnownHosts
753Indicates that 787Indicates that
754.Xr ssh 1 788.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index fcaed6b01..44c89e691 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 if (options.ciphers == (char *)-1) { 191 if (options.ciphers == (char *)-1) {
167 logit("No valid ciphers for protocol version 2 given, using defaults."); 192 logit("No valid ciphers for protocol version 2 given, using defaults.");
168 options.ciphers = NULL; 193 options.ciphers = NULL;
@@ -200,6 +225,17 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
200 myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( 225 myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
201 myproposal[PROPOSAL_KEX_ALGS]); 226 myproposal[PROPOSAL_KEX_ALGS]);
202 227
228#ifdef GSSAPI
229 /* If we've got GSSAPI algorithms, then we also support the
230 * 'null' hostkey, as a last resort */
231 if (options.gss_keyex && gss) {
232 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
233 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
234 "%s,null", orig);
235 free(gss);
236 }
237#endif
238
203 if (options.rekey_limit || options.rekey_interval) 239 if (options.rekey_limit || options.rekey_interval)
204 packet_set_rekey_limits((u_int32_t)options.rekey_limit, 240 packet_set_rekey_limits((u_int32_t)options.rekey_limit,
205 (time_t)options.rekey_interval); 241 (time_t)options.rekey_interval);
@@ -218,10 +254,30 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
218# endif 254# endif
219#endif 255#endif
220 kex->kex[KEX_C25519_SHA256] = kexc25519_client; 256 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
257#ifdef GSSAPI
258 if (options.gss_keyex) {
259 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
260 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
261 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
262 }
263#endif
221 kex->client_version_string=client_version_string; 264 kex->client_version_string=client_version_string;
222 kex->server_version_string=server_version_string; 265 kex->server_version_string=server_version_string;
223 kex->verify_host_key=&verify_host_key_callback; 266 kex->verify_host_key=&verify_host_key_callback;
224 267
268#ifdef GSSAPI
269 if (options.gss_keyex) {
270 kex->gss_deleg_creds = options.gss_deleg_creds;
271 kex->gss_trust_dns = options.gss_trust_dns;
272 kex->gss_client = options.gss_client_identity;
273 if (options.gss_server_identity) {
274 kex->gss_host = options.gss_server_identity;
275 } else {
276 kex->gss_host = gss_host;
277 }
278 }
279#endif
280
225 dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); 281 dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
226 282
227 if (options.use_roaming && !kex->roaming) { 283 if (options.use_roaming && !kex->roaming) {
@@ -313,6 +369,7 @@ int input_gssapi_token(int type, u_int32_t, void *);
313int input_gssapi_hash(int type, u_int32_t, void *); 369int input_gssapi_hash(int type, u_int32_t, void *);
314int input_gssapi_error(int, u_int32_t, void *); 370int input_gssapi_error(int, u_int32_t, void *);
315int input_gssapi_errtok(int, u_int32_t, void *); 371int input_gssapi_errtok(int, u_int32_t, void *);
372int userauth_gsskeyex(Authctxt *authctxt);
316#endif 373#endif
317 374
318void userauth(Authctxt *, char *); 375void userauth(Authctxt *, char *);
@@ -328,6 +385,11 @@ static char *authmethods_get(void);
328 385
329Authmethod authmethods[] = { 386Authmethod authmethods[] = {
330#ifdef GSSAPI 387#ifdef GSSAPI
388 {"gssapi-keyex",
389 userauth_gsskeyex,
390 NULL,
391 &options.gss_authentication,
392 NULL},
331 {"gssapi-with-mic", 393 {"gssapi-with-mic",
332 userauth_gssapi, 394 userauth_gssapi,
333 NULL, 395 NULL,
@@ -634,19 +696,31 @@ userauth_gssapi(Authctxt *authctxt)
634 static u_int mech = 0; 696 static u_int mech = 0;
635 OM_uint32 min; 697 OM_uint32 min;
636 int ok = 0; 698 int ok = 0;
699 const char *gss_host;
700
701 if (options.gss_server_identity)
702 gss_host = options.gss_server_identity;
703 else if (options.gss_trust_dns)
704 gss_host = get_canonical_hostname(1);
705 else
706 gss_host = authctxt->host;
637 707
638 /* Try one GSSAPI method at a time, rather than sending them all at 708 /* Try one GSSAPI method at a time, rather than sending them all at
639 * once. */ 709 * once. */
640 710
641 if (gss_supported == NULL) 711 if (gss_supported == NULL)
642 gss_indicate_mechs(&min, &gss_supported); 712 if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
713 gss_supported = NULL;
714 return 0;
715 }
643 716
644 /* Check to see if the mechanism is usable before we offer it */ 717 /* Check to see if the mechanism is usable before we offer it */
645 while (mech < gss_supported->count && !ok) { 718 while (mech < gss_supported->count && !ok) {
646 /* My DER encoding requires length<128 */ 719 /* My DER encoding requires length<128 */
647 if (gss_supported->elements[mech].length < 128 && 720 if (gss_supported->elements[mech].length < 128 &&
648 ssh_gssapi_check_mechanism(&gssctxt, 721 ssh_gssapi_check_mechanism(&gssctxt,
649 &gss_supported->elements[mech], authctxt->host)) { 722 &gss_supported->elements[mech], gss_host,
723 options.gss_client_identity)) {
650 ok = 1; /* Mechanism works */ 724 ok = 1; /* Mechanism works */
651 } else { 725 } else {
652 mech++; 726 mech++;
@@ -743,8 +817,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt)
743{ 817{
744 Authctxt *authctxt = ctxt; 818 Authctxt *authctxt = ctxt;
745 Gssctxt *gssctxt; 819 Gssctxt *gssctxt;
746 int oidlen; 820 u_int oidlen;
747 char *oidv; 821 u_char *oidv;
748 822
749 if (authctxt == NULL) 823 if (authctxt == NULL)
750 fatal("input_gssapi_response: no authentication context"); 824 fatal("input_gssapi_response: no authentication context");
@@ -857,6 +931,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt)
857 free(lang); 931 free(lang);
858 return 0; 932 return 0;
859} 933}
934
935int
936userauth_gsskeyex(Authctxt *authctxt)
937{
938 Buffer b;
939 gss_buffer_desc gssbuf;
940 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
941 OM_uint32 ms;
942
943 static int attempt = 0;
944 if (attempt++ >= 1)
945 return (0);
946
947 if (gss_kex_context == NULL) {
948 debug("No valid Key exchange context");
949 return (0);
950 }
951
952 ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
953 "gssapi-keyex");
954
955 gssbuf.value = buffer_ptr(&b);
956 gssbuf.length = buffer_len(&b);
957
958 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
959 buffer_free(&b);
960 return (0);
961 }
962
963 packet_start(SSH2_MSG_USERAUTH_REQUEST);
964 packet_put_cstring(authctxt->server_user);
965 packet_put_cstring(authctxt->service);
966 packet_put_cstring(authctxt->method->name);
967 packet_put_string(mic.value, mic.length);
968 packet_send();
969
970 buffer_free(&b);
971 gss_release_buffer(&ms, &mic);
972
973 return (1);
974}
975
860#endif /* GSSAPI */ 976#endif /* GSSAPI */
861 977
862int 978int
diff --git a/sshd.c b/sshd.c
index 6f8c6f2b1..6b85e6c45 100644
--- a/sshd.c
+++ b/sshd.c
@@ -125,6 +125,10 @@
125#include "version.h" 125#include "version.h"
126#include "ssherr.h" 126#include "ssherr.h"
127 127
128#ifdef USE_SECURITY_SESSION_API
129#include <Security/AuthSession.h>
130#endif
131
128#ifndef O_NOCTTY 132#ifndef O_NOCTTY
129#define O_NOCTTY 0 133#define O_NOCTTY 0
130#endif 134#endif
@@ -1823,10 +1827,13 @@ main(int ac, char **av)
1823 logit("Disabling protocol version 1. Could not load host key"); 1827 logit("Disabling protocol version 1. Could not load host key");
1824 options.protocol &= ~SSH_PROTO_1; 1828 options.protocol &= ~SSH_PROTO_1;
1825 } 1829 }
1830#ifndef GSSAPI
1831 /* The GSSAPI key exchange can run without a host key */
1826 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { 1832 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
1827 logit("Disabling protocol version 2. Could not load host key"); 1833 logit("Disabling protocol version 2. Could not load host key");
1828 options.protocol &= ~SSH_PROTO_2; 1834 options.protocol &= ~SSH_PROTO_2;
1829 } 1835 }
1836#endif
1830 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { 1837 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
1831 logit("sshd: no hostkeys available -- exiting."); 1838 logit("sshd: no hostkeys available -- exiting.");
1832 exit(1); 1839 exit(1);
@@ -2141,6 +2148,60 @@ main(int ac, char **av)
2141 remote_ip, remote_port, laddr, get_local_port()); 2148 remote_ip, remote_port, laddr, get_local_port());
2142 free(laddr); 2149 free(laddr);
2143 2150
2151#ifdef USE_SECURITY_SESSION_API
2152 /*
2153 * Create a new security session for use by the new user login if
2154 * the current session is the root session or we are not launched
2155 * by inetd (eg: debugging mode or server mode). We do not
2156 * necessarily need to create a session if we are launched from
2157 * inetd because Panther xinetd will create a session for us.
2158 *
2159 * The only case where this logic will fail is if there is an
2160 * inetd running in a non-root session which is not creating
2161 * new sessions for us. Then all the users will end up in the
2162 * same session (bad).
2163 *
2164 * When the client exits, the session will be destroyed for us
2165 * automatically.
2166 *
2167 * We must create the session before any credentials are stored
2168 * (including AFS pags, which happens a few lines below).
2169 */
2170 {
2171 OSStatus err = 0;
2172 SecuritySessionId sid = 0;
2173 SessionAttributeBits sattrs = 0;
2174
2175 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2176 if (err)
2177 error("SessionGetInfo() failed with error %.8X",
2178 (unsigned) err);
2179 else
2180 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2181 (unsigned) sid, (unsigned) sattrs);
2182
2183 if (inetd_flag && !(sattrs & sessionIsRoot))
2184 debug("Running in inetd mode in a non-root session... "
2185 "assuming inetd created the session for us.");
2186 else {
2187 debug("Creating new security session...");
2188 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2189 if (err)
2190 error("SessionCreate() failed with error %.8X",
2191 (unsigned) err);
2192
2193 err = SessionGetInfo(callerSecuritySession, &sid,
2194 &sattrs);
2195 if (err)
2196 error("SessionGetInfo() failed with error %.8X",
2197 (unsigned) err);
2198 else
2199 debug("New Session ID is %.8X / Session Attributes are %.8X",
2200 (unsigned) sid, (unsigned) sattrs);
2201 }
2202 }
2203#endif
2204
2144 /* 2205 /*
2145 * We don't want to listen forever unless the other side 2206 * We don't want to listen forever unless the other side
2146 * successfully authenticates itself. So we set up an alarm which is 2207 * successfully authenticates itself. So we set up an alarm which is
@@ -2570,6 +2631,48 @@ do_ssh2_kex(void)
2570 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2631 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2571 list_hostkey_types()); 2632 list_hostkey_types());
2572 2633
2634#ifdef GSSAPI
2635 {
2636 char *orig;
2637 char *gss = NULL;
2638 char *newstr = NULL;
2639 orig = myproposal[PROPOSAL_KEX_ALGS];
2640
2641 /*
2642 * If we don't have a host key, then there's no point advertising
2643 * the other key exchange algorithms
2644 */
2645
2646 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2647 orig = NULL;
2648
2649 if (options.gss_keyex)
2650 gss = ssh_gssapi_server_mechanisms();
2651 else
2652 gss = NULL;
2653
2654 if (gss && orig)
2655 xasprintf(&newstr, "%s,%s", gss, orig);
2656 else if (gss)
2657 newstr = gss;
2658 else if (orig)
2659 newstr = orig;
2660
2661 /*
2662 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2663 * key alg, but we can't tell people about it unless its the only
2664 * host key algorithm we support
2665 */
2666 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2667 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2668
2669 if (newstr)
2670 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2671 else
2672 fatal("No supported key exchange algorithms");
2673 }
2674#endif
2675
2573 /* start key exchange */ 2676 /* start key exchange */
2574 if ((r = kex_setup(active_state, myproposal)) != 0) 2677 if ((r = kex_setup(active_state, myproposal)) != 0)
2575 fatal("kex_setup: %s", ssh_err(r)); 2678 fatal("kex_setup: %s", ssh_err(r));
@@ -2584,6 +2687,13 @@ do_ssh2_kex(void)
2584# endif 2687# endif
2585#endif 2688#endif
2586 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2689 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2690#ifdef GSSAPI
2691 if (options.gss_keyex) {
2692 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2693 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2694 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2695 }
2696#endif
2587 kex->server = 1; 2697 kex->server = 1;
2588 kex->client_version_string=client_version_string; 2698 kex->client_version_string=client_version_string;
2589 kex->server_version_string=server_version_string; 2699 kex->server_version_string=server_version_string;
diff --git a/sshd_config b/sshd_config
index cf7d8e1e8..1dfd0f156 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 5ab431890..68424f110 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -616,6 +616,12 @@ Specifies whether user authentication based on GSSAPI is allowed.
616The default is 616The default is
617.Dq no . 617.Dq no .
618Note that this option applies to protocol version 2 only. 618Note that this option applies to protocol version 2 only.
619.It Cm GSSAPIKeyExchange
620Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
621doesn't rely on ssh keys to verify host identity.
622The default is
623.Dq no .
624Note that this option applies to protocol version 2 only.
619.It Cm GSSAPICleanupCredentials 625.It Cm GSSAPICleanupCredentials
620Specifies whether to automatically destroy the user's credentials cache 626Specifies whether to automatically destroy the user's credentials cache
621on logout. 627on logout.
@@ -637,6 +643,11 @@ machine's default store.
637This facility is provided to assist with operation on multi homed machines. 643This facility is provided to assist with operation on multi homed machines.
638The default is 644The default is
639.Dq yes . 645.Dq yes .
646.It Cm GSSAPIStoreCredentialsOnRekey
647Controls whether the user's GSSAPI credentials should be updated following a
648successful connection rekeying. This option can be used to accepted renewed
649or updated credentials from a compatible client. The default is
650.Dq no .
640.It Cm HostbasedAcceptedKeyTypes 651.It Cm HostbasedAcceptedKeyTypes
641Specifies the key types that will be accepted for hostbased authentication 652Specifies the key types that will be accepted for hostbased authentication
642as a comma-separated pattern list. 653as a comma-separated pattern list.
diff --git a/sshkey.c b/sshkey.c
index cfe598080..2c87d8099 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -116,6 +116,7 @@ static const struct keytype keytypes[] = {
116 { "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00", 116 { "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00",
117 KEY_DSA_CERT_V00, 0, 1 }, 117 KEY_DSA_CERT_V00, 0, 1 },
118#endif /* WITH_OPENSSL */ 118#endif /* WITH_OPENSSL */
119 { "null", "null", KEY_NULL, 0, 0 },
119 { NULL, NULL, -1, -1, 0 } 120 { NULL, NULL, -1, -1, 0 }
120}; 121};
121 122
@@ -204,7 +205,7 @@ key_alg_list(int certs_only, int plain_only)
204 const struct keytype *kt; 205 const struct keytype *kt;
205 206
206 for (kt = keytypes; kt->type != -1; kt++) { 207 for (kt = keytypes; kt->type != -1; kt++) {
207 if (kt->name == NULL) 208 if (kt->name == NULL || kt->type == KEY_NULL)
208 continue; 209 continue;
209 if ((certs_only && !kt->cert) || (plain_only && kt->cert)) 210 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
210 continue; 211 continue;
diff --git a/sshkey.h b/sshkey.h
index cdac0e255..b010b8eb6 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -64,6 +64,7 @@ enum sshkey_types {
64 KEY_ED25519_CERT, 64 KEY_ED25519_CERT,
65 KEY_RSA_CERT_V00, 65 KEY_RSA_CERT_V00,
66 KEY_DSA_CERT_V00, 66 KEY_DSA_CERT_V00,
67 KEY_NULL,
67 KEY_UNSPEC 68 KEY_UNSPEC
68}; 69};
69 70