summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2015-08-19 16:33:29 +0100
commit06879e71614170580ffa7568ec5c009f60a9d084 (patch)
tree2264e498417b1968891c6f8a3c3b560b2b3a4761
parentbaccdb349b31c47cd76fb63211f754ed33a9707e (diff)
GSSAPI key exchange support
This patch has been rejected upstream: "None of the OpenSSH developers are in favour of adding this, and this situation has not changed for several years. This is not a slight on Simon's patch, which is of fine quality, but just that a) we don't trust GSSAPI implementations that much and b) we don't like adding new KEX since they are pre-auth attack surface. This one is particularly scary, since it requires hooks out to typically root-owned system resources." However, quite a lot of people rely on this in Debian, and it's better to have it merged into the main openssh package rather than having separate -krb5 packages (as we used to have). It seems to have a generally good security history. Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 Last-Updated: 2015-08-19 Patch-Name: gssapi.patch
-rw-r--r--ChangeLog.gssapi113
-rw-r--r--Makefile.in5
-rw-r--r--auth-krb5.c17
-rw-r--r--auth2-gss.c48
-rw-r--r--auth2.c2
-rw-r--r--clientloop.c13
-rw-r--r--config.h.in6
-rw-r--r--configure.ac24
-rw-r--r--gss-genr.c275
-rw-r--r--gss-serv-krb5.c85
-rw-r--r--gss-serv.c221
-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.c38
-rw-r--r--servconf.h3
-rw-r--r--ssh-gss.h41
-rw-r--r--ssh_config2
-rw-r--r--ssh_config.534
-rw-r--r--sshconnect2.c124
-rw-r--r--sshd.c110
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.528
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
32 files changed, 2005 insertions, 60 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 a9c8a90f0..7df941375 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
@@ -1596,6 +1600,15 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1596 /* Do channel operations unless rekeying in progress. */ 1600 /* Do channel operations unless rekeying in progress. */
1597 if (!rekeying) { 1601 if (!rekeying) {
1598 channel_after_select(readset, writeset); 1602 channel_after_select(readset, writeset);
1603
1604#ifdef GSSAPI
1605 if (options.gss_renewal_rekey &&
1606 ssh_gssapi_credentials_updated(NULL)) {
1607 debug("credentials updated - forcing rekey");
1608 need_rekeying = 1;
1609 }
1610#endif
1611
1599 if (need_rekeying || packet_need_rekeying()) { 1612 if (need_rekeying || packet_need_rekeying()) {
1600 debug("need rekeying"); 1613 debug("need rekeying");
1601 active_state->kex->done = 0; 1614 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 b4d6598d5..216a9fdab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -620,6 +620,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
620 [Use tunnel device compatibility to OpenBSD]) 620 [Use tunnel device compatibility to OpenBSD])
621 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 621 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
622 [Prepend the address family to IP tunnel traffic]) 622 [Prepend the address family to IP tunnel traffic])
623 AC_MSG_CHECKING([if we have the Security Authorization Session API])
624 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
625 [SessionCreate(0, 0);],
626 [ac_cv_use_security_session_api="yes"
627 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
628 [platform has the Security Authorization Session API])
629 LIBS="$LIBS -framework Security"
630 AC_MSG_RESULT([yes])],
631 [ac_cv_use_security_session_api="no"
632 AC_MSG_RESULT([no])])
633 AC_MSG_CHECKING([if we have an in-memory credentials cache])
634 AC_TRY_COMPILE(
635 [#include <Kerberos/Kerberos.h>],
636 [cc_context_t c;
637 (void) cc_initialize (&c, 0, NULL, NULL);],
638 [AC_DEFINE([USE_CCAPI], [1],
639 [platform uses an in-memory credentials cache])
640 LIBS="$LIBS -framework Security"
641 AC_MSG_RESULT([yes])
642 if test "x$ac_cv_use_security_session_api" = "xno"; then
643 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
644 fi],
645 [AC_MSG_RESULT([no])]
646 )
623 m4_pattern_allow([AU_IPv]) 647 m4_pattern_allow([AU_IPv])
624 AC_CHECK_DECL([AU_IPv4], [], 648 AC_CHECK_DECL([AU_IPv4], [],
625 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 649 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
diff --git a/gss-genr.c b/gss-genr.c
index 60ac65f8d..5610f0bf2 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
@@ -40,12 +40,167 @@
40#include "buffer.h" 40#include "buffer.h"
41#include "log.h" 41#include "log.h"
42#include "ssh2.h" 42#include "ssh2.h"
43#include "cipher.h"
44#include "key.h"
45#include "kex.h"
46#include <openssl/evp.h>
43 47
44#include "ssh-gss.h" 48#include "ssh-gss.h"
45 49
46extern u_char *session_id2; 50extern u_char *session_id2;
47extern u_int session_id2_len; 51extern u_int session_id2_len;
48 52
53typedef struct {
54 char *encoded;
55 gss_OID oid;
56} ssh_gss_kex_mapping;
57
58/*
59 * XXX - It would be nice to find a more elegant way of handling the
60 * XXX passing of the key exchange context to the userauth routines
61 */
62
63Gssctxt *gss_kex_context = NULL;
64
65static ssh_gss_kex_mapping *gss_enc2oid = NULL;
66
67int
68ssh_gssapi_oid_table_ok(void) {
69 return (gss_enc2oid != NULL);
70}
71
72/*
73 * Return a list of the gss-group1-sha1 mechanisms supported by this program
74 *
75 * We test mechanisms to ensure that we can use them, to avoid starting
76 * a key exchange with a bad mechanism
77 */
78
79char *
80ssh_gssapi_client_mechanisms(const char *host, const char *client) {
81 gss_OID_set gss_supported;
82 OM_uint32 min_status;
83
84 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
85 return NULL;
86
87 return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
88 host, client));
89}
90
91char *
92ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
93 const char *host, const char *client) {
94 Buffer buf;
95 size_t i;
96 int oidpos, enclen;
97 char *mechs, *encoded;
98 u_char digest[EVP_MAX_MD_SIZE];
99 char deroid[2];
100 const EVP_MD *evp_md = EVP_md5();
101 EVP_MD_CTX md;
102
103 if (gss_enc2oid != NULL) {
104 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
105 free(gss_enc2oid[i].encoded);
106 free(gss_enc2oid);
107 }
108
109 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
110 (gss_supported->count + 1));
111
112 buffer_init(&buf);
113
114 oidpos = 0;
115 for (i = 0; i < gss_supported->count; i++) {
116 if (gss_supported->elements[i].length < 128 &&
117 (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
118
119 deroid[0] = SSH_GSS_OIDTYPE;
120 deroid[1] = gss_supported->elements[i].length;
121
122 EVP_DigestInit(&md, evp_md);
123 EVP_DigestUpdate(&md, deroid, 2);
124 EVP_DigestUpdate(&md,
125 gss_supported->elements[i].elements,
126 gss_supported->elements[i].length);
127 EVP_DigestFinal(&md, digest, NULL);
128
129 encoded = xmalloc(EVP_MD_size(evp_md) * 2);
130 enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
131 encoded, EVP_MD_size(evp_md) * 2);
132
133 if (oidpos != 0)
134 buffer_put_char(&buf, ',');
135
136 buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
137 sizeof(KEX_GSS_GEX_SHA1_ID) - 1);
138 buffer_append(&buf, encoded, enclen);
139 buffer_put_char(&buf, ',');
140 buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID,
141 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1);
142 buffer_append(&buf, encoded, enclen);
143 buffer_put_char(&buf, ',');
144 buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
145 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1);
146 buffer_append(&buf, encoded, enclen);
147
148 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
149 gss_enc2oid[oidpos].encoded = encoded;
150 oidpos++;
151 }
152 }
153 gss_enc2oid[oidpos].oid = NULL;
154 gss_enc2oid[oidpos].encoded = NULL;
155
156 buffer_put_char(&buf, '\0');
157
158 mechs = xmalloc(buffer_len(&buf));
159 buffer_get(&buf, mechs, buffer_len(&buf));
160 buffer_free(&buf);
161
162 if (strlen(mechs) == 0) {
163 free(mechs);
164 mechs = NULL;
165 }
166
167 return (mechs);
168}
169
170gss_OID
171ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
172 int i = 0;
173
174 switch (kex_type) {
175 case KEX_GSS_GRP1_SHA1:
176 if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
177 return GSS_C_NO_OID;
178 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
179 break;
180 case KEX_GSS_GRP14_SHA1:
181 if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
182 return GSS_C_NO_OID;
183 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
184 break;
185 case KEX_GSS_GEX_SHA1:
186 if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
187 return GSS_C_NO_OID;
188 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
189 break;
190 default:
191 return GSS_C_NO_OID;
192 }
193
194 while (gss_enc2oid[i].encoded != NULL &&
195 strcmp(name, gss_enc2oid[i].encoded) != 0)
196 i++;
197
198 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
199 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
200
201 return gss_enc2oid[i].oid;
202}
203
49/* Check that the OID in a data stream matches that in the context */ 204/* Check that the OID in a data stream matches that in the context */
50int 205int
51ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 206ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -198,7 +353,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
198 } 353 }
199 354
200 ctx->major = gss_init_sec_context(&ctx->minor, 355 ctx->major = gss_init_sec_context(&ctx->minor,
201 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 356 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
202 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 357 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
203 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 358 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
204 359
@@ -228,8 +383,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
228} 383}
229 384
230OM_uint32 385OM_uint32
386ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
387{
388 gss_buffer_desc gssbuf;
389 gss_name_t gssname;
390 OM_uint32 status;
391 gss_OID_set oidset;
392
393 gssbuf.value = (void *) name;
394 gssbuf.length = strlen(gssbuf.value);
395
396 gss_create_empty_oid_set(&status, &oidset);
397 gss_add_oid_set_member(&status, ctx->oid, &oidset);
398
399 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
400 GSS_C_NT_USER_NAME, &gssname);
401
402 if (!ctx->major)
403 ctx->major = gss_acquire_cred(&ctx->minor,
404 gssname, 0, oidset, GSS_C_INITIATE,
405 &ctx->client_creds, NULL, NULL);
406
407 gss_release_name(&status, &gssname);
408 gss_release_oid_set(&status, &oidset);
409
410 if (ctx->major)
411 ssh_gssapi_error(ctx);
412
413 return(ctx->major);
414}
415
416OM_uint32
231ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 417ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
232{ 418{
419 if (ctx == NULL)
420 return -1;
421
233 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 422 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
234 GSS_C_QOP_DEFAULT, buffer, hash))) 423 GSS_C_QOP_DEFAULT, buffer, hash)))
235 ssh_gssapi_error(ctx); 424 ssh_gssapi_error(ctx);
@@ -237,6 +426,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
237 return (ctx->major); 426 return (ctx->major);
238} 427}
239 428
429/* Priviledged when used by server */
430OM_uint32
431ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
432{
433 if (ctx == NULL)
434 return -1;
435
436 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
437 gssbuf, gssmic, NULL);
438
439 return (ctx->major);
440}
441
240void 442void
241ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, 443ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
242 const char *context) 444 const char *context)
@@ -250,11 +452,16 @@ ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
250} 452}
251 453
252int 454int
253ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 455ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
456 const char *client)
254{ 457{
255 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 458 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
256 OM_uint32 major, minor; 459 OM_uint32 major, minor;
257 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 460 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
461 Gssctxt *intctx = NULL;
462
463 if (ctx == NULL)
464 ctx = &intctx;
258 465
259 /* RFC 4462 says we MUST NOT do SPNEGO */ 466 /* RFC 4462 says we MUST NOT do SPNEGO */
260 if (oid->length == spnego_oid.length && 467 if (oid->length == spnego_oid.length &&
@@ -264,6 +471,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
264 ssh_gssapi_build_ctx(ctx); 471 ssh_gssapi_build_ctx(ctx);
265 ssh_gssapi_set_oid(*ctx, oid); 472 ssh_gssapi_set_oid(*ctx, oid);
266 major = ssh_gssapi_import_name(*ctx, host); 473 major = ssh_gssapi_import_name(*ctx, host);
474
475 if (!GSS_ERROR(major) && client)
476 major = ssh_gssapi_client_identity(*ctx, client);
477
267 if (!GSS_ERROR(major)) { 478 if (!GSS_ERROR(major)) {
268 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 479 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
269 NULL); 480 NULL);
@@ -273,10 +484,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
273 GSS_C_NO_BUFFER); 484 GSS_C_NO_BUFFER);
274 } 485 }
275 486
276 if (GSS_ERROR(major)) 487 if (GSS_ERROR(major) || intctx != NULL)
277 ssh_gssapi_delete_ctx(ctx); 488 ssh_gssapi_delete_ctx(ctx);
278 489
279 return (!GSS_ERROR(major)); 490 return (!GSS_ERROR(major));
280} 491}
281 492
493int
494ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
495 static gss_name_t saved_name = GSS_C_NO_NAME;
496 static OM_uint32 saved_lifetime = 0;
497 static gss_OID saved_mech = GSS_C_NO_OID;
498 static gss_name_t name;
499 static OM_uint32 last_call = 0;
500 OM_uint32 lifetime, now, major, minor;
501 int equal;
502
503 now = time(NULL);
504
505 if (ctxt) {
506 debug("Rekey has happened - updating saved versions");
507
508 if (saved_name != GSS_C_NO_NAME)
509 gss_release_name(&minor, &saved_name);
510
511 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
512 &saved_name, &saved_lifetime, NULL, NULL);
513
514 if (!GSS_ERROR(major)) {
515 saved_mech = ctxt->oid;
516 saved_lifetime+= now;
517 } else {
518 /* Handle the error */
519 }
520 return 0;
521 }
522
523 if (now - last_call < 10)
524 return 0;
525
526 last_call = now;
527
528 if (saved_mech == GSS_C_NO_OID)
529 return 0;
530
531 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
532 &name, &lifetime, NULL, NULL);
533 if (major == GSS_S_CREDENTIALS_EXPIRED)
534 return 0;
535 else if (GSS_ERROR(major))
536 return 0;
537
538 major = gss_compare_name(&minor, saved_name, name, &equal);
539 gss_release_name(&minor, &name);
540 if (GSS_ERROR(major))
541 return 0;
542
543 if (equal && (saved_lifetime < lifetime + now - 10))
544 return 1;
545
546 return 0;
547}
548
282#endif /* GSSAPI */ 549#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 e7b8c5223..539862d67 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.28 2015/01/20 23:14:00 deraadt Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.28 2015/01/20 23:14:00 deraadt 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
@@ -44,15 +44,21 @@
44#include "channels.h" 44#include "channels.h"
45#include "session.h" 45#include "session.h"
46#include "misc.h" 46#include "misc.h"
47#include "servconf.h"
48#include "uidswap.h"
47 49
48#include "ssh-gss.h" 50#include "ssh-gss.h"
51#include "monitor_wrap.h"
52
53extern ServerOptions options;
49 54
50static ssh_gssapi_client gssapi_client = 55static ssh_gssapi_client gssapi_client =
51 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 56 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
52 GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; 57 GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL,
58 {NULL, NULL, NULL, NULL, NULL}, 0, 0};
53 59
54ssh_gssapi_mech gssapi_null_mech = 60ssh_gssapi_mech gssapi_null_mech =
55 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; 61 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
56 62
57#ifdef KRB5 63#ifdef KRB5
58extern ssh_gssapi_mech gssapi_kerberos_mech; 64extern ssh_gssapi_mech gssapi_kerberos_mech;
@@ -99,25 +105,32 @@ ssh_gssapi_acquire_cred(Gssctxt *ctx)
99 char lname[NI_MAXHOST]; 105 char lname[NI_MAXHOST];
100 gss_OID_set oidset; 106 gss_OID_set oidset;
101 107
102 gss_create_empty_oid_set(&status, &oidset); 108 if (options.gss_strict_acceptor) {
103 gss_add_oid_set_member(&status, ctx->oid, &oidset); 109 gss_create_empty_oid_set(&status, &oidset);
110 gss_add_oid_set_member(&status, ctx->oid, &oidset);
104 111
105 if (gethostname(lname, sizeof(lname))) { 112 if (gethostname(lname, sizeof(lname))) {
106 gss_release_oid_set(&status, &oidset); 113 gss_release_oid_set(&status, &oidset);
107 return (-1); 114 return (-1);
108 } 115 }
116
117 if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
118 gss_release_oid_set(&status, &oidset);
119 return (ctx->major);
120 }
121
122 if ((ctx->major = gss_acquire_cred(&ctx->minor,
123 ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds,
124 NULL, NULL)))
125 ssh_gssapi_error(ctx);
109 126
110 if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
111 gss_release_oid_set(&status, &oidset); 127 gss_release_oid_set(&status, &oidset);
112 return (ctx->major); 128 return (ctx->major);
129 } else {
130 ctx->name = GSS_C_NO_NAME;
131 ctx->creds = GSS_C_NO_CREDENTIAL;
113 } 132 }
114 133 return GSS_S_COMPLETE;
115 if ((ctx->major = gss_acquire_cred(&ctx->minor,
116 ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL)))
117 ssh_gssapi_error(ctx);
118
119 gss_release_oid_set(&status, &oidset);
120 return (ctx->major);
121} 134}
122 135
123/* Privileged */ 136/* Privileged */
@@ -132,6 +145,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
132} 145}
133 146
134/* Unprivileged */ 147/* Unprivileged */
148char *
149ssh_gssapi_server_mechanisms(void) {
150 gss_OID_set supported;
151
152 ssh_gssapi_supported_oids(&supported);
153 return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
154 NULL, NULL));
155}
156
157/* Unprivileged */
158int
159ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
160 const char *dummy) {
161 Gssctxt *ctx = NULL;
162 int res;
163
164 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
165 ssh_gssapi_delete_ctx(&ctx);
166
167 return (res);
168}
169
170/* Unprivileged */
135void 171void
136ssh_gssapi_supported_oids(gss_OID_set *oidset) 172ssh_gssapi_supported_oids(gss_OID_set *oidset)
137{ 173{
@@ -141,7 +177,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
141 gss_OID_set supported; 177 gss_OID_set supported;
142 178
143 gss_create_empty_oid_set(&min_status, oidset); 179 gss_create_empty_oid_set(&min_status, oidset);
144 gss_indicate_mechs(&min_status, &supported); 180
181 if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
182 return;
145 183
146 while (supported_mechs[i]->name != NULL) { 184 while (supported_mechs[i]->name != NULL) {
147 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 185 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -267,8 +305,48 @@ OM_uint32
267ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 305ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
268{ 306{
269 int i = 0; 307 int i = 0;
308 int equal = 0;
309 gss_name_t new_name = GSS_C_NO_NAME;
310 gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
311
312 if (options.gss_store_rekey && client->used && ctx->client_creds) {
313 if (client->mech->oid.length != ctx->oid->length ||
314 (memcmp(client->mech->oid.elements,
315 ctx->oid->elements, ctx->oid->length) !=0)) {
316 debug("Rekeyed credentials have different mechanism");
317 return GSS_S_COMPLETE;
318 }
319
320 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
321 ctx->client_creds, ctx->oid, &new_name,
322 NULL, NULL, NULL))) {
323 ssh_gssapi_error(ctx);
324 return (ctx->major);
325 }
326
327 ctx->major = gss_compare_name(&ctx->minor, client->name,
328 new_name, &equal);
270 329
271 gss_buffer_desc ename; 330 if (GSS_ERROR(ctx->major)) {
331 ssh_gssapi_error(ctx);
332 return (ctx->major);
333 }
334
335 if (!equal) {
336 debug("Rekeyed credentials have different name");
337 return GSS_S_COMPLETE;
338 }
339
340 debug("Marking rekeyed credentials for export");
341
342 gss_release_name(&ctx->minor, &client->name);
343 gss_release_cred(&ctx->minor, &client->creds);
344 client->name = new_name;
345 client->creds = ctx->client_creds;
346 ctx->client_creds = GSS_C_NO_CREDENTIAL;
347 client->updated = 1;
348 return GSS_S_COMPLETE;
349 }
272 350
273 client->mech = NULL; 351 client->mech = NULL;
274 352
@@ -283,6 +361,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
283 if (client->mech == NULL) 361 if (client->mech == NULL)
284 return GSS_S_FAILURE; 362 return GSS_S_FAILURE;
285 363
364 if (ctx->client_creds &&
365 (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
366 ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
367 ssh_gssapi_error(ctx);
368 return (ctx->major);
369 }
370
286 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 371 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
287 &client->displayname, NULL))) { 372 &client->displayname, NULL))) {
288 ssh_gssapi_error(ctx); 373 ssh_gssapi_error(ctx);
@@ -300,6 +385,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
300 return (ctx->major); 385 return (ctx->major);
301 } 386 }
302 387
388 gss_release_buffer(&ctx->minor, &ename);
389
303 /* We can't copy this structure, so we just move the pointer to it */ 390 /* We can't copy this structure, so we just move the pointer to it */
304 client->creds = ctx->client_creds; 391 client->creds = ctx->client_creds;
305 ctx->client_creds = GSS_C_NO_CREDENTIAL; 392 ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -347,7 +434,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
347 434
348/* Privileged */ 435/* Privileged */
349int 436int
350ssh_gssapi_userok(char *user) 437ssh_gssapi_userok(char *user, struct passwd *pw)
351{ 438{
352 OM_uint32 lmin; 439 OM_uint32 lmin;
353 440
@@ -357,9 +444,11 @@ ssh_gssapi_userok(char *user)
357 return 0; 444 return 0;
358 } 445 }
359 if (gssapi_client.mech && gssapi_client.mech->userok) 446 if (gssapi_client.mech && gssapi_client.mech->userok)
360 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 447 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
448 gssapi_client.used = 1;
449 gssapi_client.store.owner = pw;
361 return 1; 450 return 1;
362 else { 451 } else {
363 /* Destroy delegated credentials if userok fails */ 452 /* Destroy delegated credentials if userok fails */
364 gss_release_buffer(&lmin, &gssapi_client.displayname); 453 gss_release_buffer(&lmin, &gssapi_client.displayname);
365 gss_release_buffer(&lmin, &gssapi_client.exportedname); 454 gss_release_buffer(&lmin, &gssapi_client.exportedname);
@@ -373,14 +462,90 @@ ssh_gssapi_userok(char *user)
373 return (0); 462 return (0);
374} 463}
375 464
376/* Privileged */ 465/* These bits are only used for rekeying. The unpriviledged child is running
377OM_uint32 466 * as the user, the monitor is root.
378ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 467 *
468 * In the child, we want to :
469 * *) Ask the monitor to store our credentials into the store we specify
470 * *) If it succeeds, maybe do a PAM update
471 */
472
473/* Stuff for PAM */
474
475#ifdef USE_PAM
476static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
477 struct pam_response **resp, void *data)
379{ 478{
380 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 479 return (PAM_CONV_ERR);
381 gssbuf, gssmic, NULL); 480}
481#endif
382 482
383 return (ctx->major); 483void
484ssh_gssapi_rekey_creds(void) {
485 int ok;
486 int ret;
487#ifdef USE_PAM
488 pam_handle_t *pamh = NULL;
489 struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
490 char *envstr;
491#endif
492
493 if (gssapi_client.store.filename == NULL &&
494 gssapi_client.store.envval == NULL &&
495 gssapi_client.store.envvar == NULL)
496 return;
497
498 ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
499
500 if (!ok)
501 return;
502
503 debug("Rekeyed credentials stored successfully");
504
505 /* Actually managing to play with the ssh pam stack from here will
506 * be next to impossible. In any case, we may want different options
507 * for rekeying. So, use our own :)
508 */
509#ifdef USE_PAM
510 if (!use_privsep) {
511 debug("Not even going to try and do PAM with privsep disabled");
512 return;
513 }
514
515 ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
516 &pamconv, &pamh);
517 if (ret)
518 return;
519
520 xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
521 gssapi_client.store.envval);
522
523 ret = pam_putenv(pamh, envstr);
524 if (!ret)
525 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
526 pam_end(pamh, PAM_SUCCESS);
527#endif
528}
529
530int
531ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
532 int ok = 0;
533
534 /* Check we've got credentials to store */
535 if (!gssapi_client.updated)
536 return 0;
537
538 gssapi_client.updated = 0;
539
540 temporarily_use_uid(gssapi_client.store.owner);
541 if (gssapi_client.mech && gssapi_client.mech->updatecreds)
542 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
543 else
544 debug("No update function for this mechanism");
545
546 restore_uid();
547
548 return ok;
384} 549}
385 550
386#endif 551#endif
diff --git a/kex.c b/kex.c
index 8c2b00179..be938ad04 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 bab6ce87e..a2027e580 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);
@@ -1860,6 +1877,13 @@ monitor_apply_keystate(struct monitor *pmonitor)
1860# endif 1877# endif
1861#endif /* WITH_OPENSSL */ 1878#endif /* WITH_OPENSSL */
1862 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 1879 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1880#ifdef GSSAPI
1881 if (options.gss_keyex) {
1882 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1883 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1884 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1885 }
1886#endif
1863 kex->load_host_public_key=&get_hostkey_public_by_type; 1887 kex->load_host_public_key=&get_hostkey_public_by_type;
1864 kex->load_host_private_key=&get_hostkey_private_by_type; 1888 kex->load_host_private_key=&get_hostkey_private_by_type;
1865 kex->host_key_index=&get_hostkey_index; 1889 kex->host_key_index=&get_hostkey_index;
@@ -1959,6 +1983,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m)
1959 OM_uint32 major; 1983 OM_uint32 major;
1960 u_int len; 1984 u_int len;
1961 1985
1986 if (!options.gss_authentication && !options.gss_keyex)
1987 fatal("In GSSAPI monitor when GSSAPI is disabled");
1988
1962 goid.elements = buffer_get_string(m, &len); 1989 goid.elements = buffer_get_string(m, &len);
1963 goid.length = len; 1990 goid.length = len;
1964 1991
@@ -1986,6 +2013,9 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
1986 OM_uint32 flags = 0; /* GSI needs this */ 2013 OM_uint32 flags = 0; /* GSI needs this */
1987 u_int len; 2014 u_int len;
1988 2015
2016 if (!options.gss_authentication && !options.gss_keyex)
2017 fatal("In GSSAPI monitor when GSSAPI is disabled");
2018
1989 in.value = buffer_get_string(m, &len); 2019 in.value = buffer_get_string(m, &len);
1990 in.length = len; 2020 in.length = len;
1991 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); 2021 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
@@ -2003,6 +2033,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
2003 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 2033 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2004 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 2034 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2005 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 2035 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2036 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2006 } 2037 }
2007 return (0); 2038 return (0);
2008} 2039}
@@ -2014,6 +2045,9 @@ mm_answer_gss_checkmic(int sock, Buffer *m)
2014 OM_uint32 ret; 2045 OM_uint32 ret;
2015 u_int len; 2046 u_int len;
2016 2047
2048 if (!options.gss_authentication && !options.gss_keyex)
2049 fatal("In GSSAPI monitor when GSSAPI is disabled");
2050
2017 gssbuf.value = buffer_get_string(m, &len); 2051 gssbuf.value = buffer_get_string(m, &len);
2018 gssbuf.length = len; 2052 gssbuf.length = len;
2019 mic.value = buffer_get_string(m, &len); 2053 mic.value = buffer_get_string(m, &len);
@@ -2040,7 +2074,11 @@ mm_answer_gss_userok(int sock, Buffer *m)
2040{ 2074{
2041 int authenticated; 2075 int authenticated;
2042 2076
2043 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 2077 if (!options.gss_authentication && !options.gss_keyex)
2078 fatal("In GSSAPI monitor when GSSAPI is disabled");
2079
2080 authenticated = authctxt->valid &&
2081 ssh_gssapi_userok(authctxt->user, authctxt->pw);
2044 2082
2045 buffer_clear(m); 2083 buffer_clear(m);
2046 buffer_put_int(m, authenticated); 2084 buffer_put_int(m, authenticated);
@@ -2053,5 +2091,73 @@ mm_answer_gss_userok(int sock, Buffer *m)
2053 /* Monitor loop will terminate if authenticated */ 2091 /* Monitor loop will terminate if authenticated */
2054 return (authenticated); 2092 return (authenticated);
2055} 2093}
2094
2095int
2096mm_answer_gss_sign(int socket, Buffer *m)
2097{
2098 gss_buffer_desc data;
2099 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2100 OM_uint32 major, minor;
2101 u_int len;
2102
2103 if (!options.gss_authentication && !options.gss_keyex)
2104 fatal("In GSSAPI monitor when GSSAPI is disabled");
2105
2106 data.value = buffer_get_string(m, &len);
2107 data.length = len;
2108 if (data.length != 20)
2109 fatal("%s: data length incorrect: %d", __func__,
2110 (int) data.length);
2111
2112 /* Save the session ID on the first time around */
2113 if (session_id2_len == 0) {
2114 session_id2_len = data.length;
2115 session_id2 = xmalloc(session_id2_len);
2116 memcpy(session_id2, data.value, session_id2_len);
2117 }
2118 major = ssh_gssapi_sign(gsscontext, &data, &hash);
2119
2120 free(data.value);
2121
2122 buffer_clear(m);
2123 buffer_put_int(m, major);
2124 buffer_put_string(m, hash.value, hash.length);
2125
2126 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2127
2128 gss_release_buffer(&minor, &hash);
2129
2130 /* Turn on getpwnam permissions */
2131 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2132
2133 /* And credential updating, for when rekeying */
2134 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2135
2136 return (0);
2137}
2138
2139int
2140mm_answer_gss_updatecreds(int socket, Buffer *m) {
2141 ssh_gssapi_ccache store;
2142 int ok;
2143
2144 store.filename = buffer_get_string(m, NULL);
2145 store.envvar = buffer_get_string(m, NULL);
2146 store.envval = buffer_get_string(m, NULL);
2147
2148 ok = ssh_gssapi_update_creds(&store);
2149
2150 free(store.filename);
2151 free(store.envvar);
2152 free(store.envval);
2153
2154 buffer_clear(m);
2155 buffer_put_int(m, ok);
2156
2157 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2158
2159 return(0);
2160}
2161
2056#endif /* GSSAPI */ 2162#endif /* GSSAPI */
2057 2163
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 b379f0555..b667218b4 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1068,7 +1068,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1068} 1068}
1069 1069
1070int 1070int
1071mm_ssh_gssapi_userok(char *user) 1071mm_ssh_gssapi_userok(char *user, struct passwd *pw)
1072{ 1072{
1073 Buffer m; 1073 Buffer m;
1074 int authenticated = 0; 1074 int authenticated = 0;
@@ -1085,5 +1085,50 @@ mm_ssh_gssapi_userok(char *user)
1085 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1085 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1086 return (authenticated); 1086 return (authenticated);
1087} 1087}
1088
1089OM_uint32
1090mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1091{
1092 Buffer m;
1093 OM_uint32 major;
1094 u_int len;
1095
1096 buffer_init(&m);
1097 buffer_put_string(&m, data->value, data->length);
1098
1099 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
1100 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
1101
1102 major = buffer_get_int(&m);
1103 hash->value = buffer_get_string(&m, &len);
1104 hash->length = len;
1105
1106 buffer_free(&m);
1107
1108 return(major);
1109}
1110
1111int
1112mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1113{
1114 Buffer m;
1115 int ok;
1116
1117 buffer_init(&m);
1118
1119 buffer_put_cstring(&m, store->filename ? store->filename : "");
1120 buffer_put_cstring(&m, store->envvar ? store->envvar : "");
1121 buffer_put_cstring(&m, store->envval ? store->envval : "");
1122
1123 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m);
1124 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m);
1125
1126 ok = buffer_get_int(&m);
1127
1128 buffer_free(&m);
1129
1130 return (ok);
1131}
1132
1088#endif /* GSSAPI */ 1133#endif /* GSSAPI */
1089 1134
diff --git a/monitor_wrap.h b/monitor_wrap.h
index e18784ac4..0c770e8fd 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 42a2961fa..254dbce57 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 318546290..f68c0d0ae 100644
--- a/servconf.c
+++ b/servconf.c
@@ -114,7 +114,10 @@ initialize_server_options(ServerOptions *options)
114 options->kerberos_ticket_cleanup = -1; 114 options->kerberos_ticket_cleanup = -1;
115 options->kerberos_get_afs_token = -1; 115 options->kerberos_get_afs_token = -1;
116 options->gss_authentication=-1; 116 options->gss_authentication=-1;
117 options->gss_keyex = -1;
117 options->gss_cleanup_creds = -1; 118 options->gss_cleanup_creds = -1;
119 options->gss_strict_acceptor = -1;
120 options->gss_store_rekey = -1;
118 options->password_authentication = -1; 121 options->password_authentication = -1;
119 options->kbd_interactive_authentication = -1; 122 options->kbd_interactive_authentication = -1;
120 options->challenge_response_authentication = -1; 123 options->challenge_response_authentication = -1;
@@ -269,8 +272,14 @@ fill_default_server_options(ServerOptions *options)
269 options->kerberos_get_afs_token = 0; 272 options->kerberos_get_afs_token = 0;
270 if (options->gss_authentication == -1) 273 if (options->gss_authentication == -1)
271 options->gss_authentication = 0; 274 options->gss_authentication = 0;
275 if (options->gss_keyex == -1)
276 options->gss_keyex = 0;
272 if (options->gss_cleanup_creds == -1) 277 if (options->gss_cleanup_creds == -1)
273 options->gss_cleanup_creds = 1; 278 options->gss_cleanup_creds = 1;
279 if (options->gss_strict_acceptor == -1)
280 options->gss_strict_acceptor = 1;
281 if (options->gss_store_rekey == -1)
282 options->gss_store_rekey = 0;
274 if (options->password_authentication == -1) 283 if (options->password_authentication == -1)
275 options->password_authentication = 1; 284 options->password_authentication = 1;
276 if (options->kbd_interactive_authentication == -1) 285 if (options->kbd_interactive_authentication == -1)
@@ -391,7 +400,9 @@ typedef enum {
391 sBanner, sUseDNS, sHostbasedAuthentication, 400 sBanner, sUseDNS, sHostbasedAuthentication,
392 sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes, 401 sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes,
393 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 402 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
394 sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, 403 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
404 sGssKeyEx, sGssStoreRekey,
405 sAcceptEnv, sPermitTunnel,
395 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 406 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
396 sUsePrivilegeSeparation, sAllowAgentForwarding, 407 sUsePrivilegeSeparation, sAllowAgentForwarding,
397 sHostCertificate, 408 sHostCertificate,
@@ -462,10 +473,20 @@ static struct {
462#ifdef GSSAPI 473#ifdef GSSAPI
463 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 474 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
464 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 475 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
476 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
477 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
478 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
479 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
465#else 480#else
466 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 481 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
467 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 482 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
483 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
484 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
485 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
486 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
468#endif 487#endif
488 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
489 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
469 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 490 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
470 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 491 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
471 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 492 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1166,10 +1187,22 @@ process_server_config_line(ServerOptions *options, char *line,
1166 intptr = &options->gss_authentication; 1187 intptr = &options->gss_authentication;
1167 goto parse_flag; 1188 goto parse_flag;
1168 1189
1190 case sGssKeyEx:
1191 intptr = &options->gss_keyex;
1192 goto parse_flag;
1193
1169 case sGssCleanupCreds: 1194 case sGssCleanupCreds:
1170 intptr = &options->gss_cleanup_creds; 1195 intptr = &options->gss_cleanup_creds;
1171 goto parse_flag; 1196 goto parse_flag;
1172 1197
1198 case sGssStrictAcceptor:
1199 intptr = &options->gss_strict_acceptor;
1200 goto parse_flag;
1201
1202 case sGssStoreRekey:
1203 intptr = &options->gss_store_rekey;
1204 goto parse_flag;
1205
1173 case sPasswordAuthentication: 1206 case sPasswordAuthentication:
1174 intptr = &options->password_authentication; 1207 intptr = &options->password_authentication;
1175 goto parse_flag; 1208 goto parse_flag;
@@ -2125,7 +2158,10 @@ dump_config(ServerOptions *o)
2125#endif 2158#endif
2126#ifdef GSSAPI 2159#ifdef GSSAPI
2127 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2160 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2161 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2128 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2162 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2163 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2164 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2129#endif 2165#endif
2130 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2166 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2131 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2167 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 9922f0c8c..d2ed4d78b 100644
--- a/servconf.h
+++ b/servconf.h
@@ -115,7 +115,10 @@ typedef struct {
115 int kerberos_get_afs_token; /* If true, try to get AFS token if 115 int kerberos_get_afs_token; /* If true, try to get AFS token if
116 * authenticated with Kerberos. */ 116 * authenticated with Kerberos. */
117 int gss_authentication; /* If true, permit GSSAPI authentication */ 117 int gss_authentication; /* If true, permit GSSAPI authentication */
118 int gss_keyex; /* If true, permit GSSAPI key exchange */
118 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 119 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
120 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
121 int gss_store_rekey;
119 int password_authentication; /* If true, permit password 122 int password_authentication; /* If true, permit password
120 * authentication. */ 123 * authentication. */
121 int kbd_interactive_authentication; /* If true, permit */ 124 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 140d0ba98..447617178 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -743,11 +743,43 @@ Specifies whether user authentication based on GSSAPI is allowed.
743The default is 743The default is
744.Dq no . 744.Dq no .
745Note that this option applies to protocol version 2 only. 745Note that this option applies to protocol version 2 only.
746.It Cm GSSAPIKeyExchange
747Specifies whether key exchange based on GSSAPI may be used. When using
748GSSAPI key exchange the server need not have a host key.
749The default is
750.Dq no .
751Note that this option applies to protocol version 2 only.
752.It Cm GSSAPIClientIdentity
753If set, specifies the GSSAPI client identity that ssh should use when
754connecting to the server. The default is unset, which means that the default
755identity will be used.
756.It Cm GSSAPIServerIdentity
757If set, specifies the GSSAPI server identity that ssh should expect when
758connecting to the server. The default is unset, which means that the
759expected GSSAPI server identity will be determined from the target
760hostname.
746.It Cm GSSAPIDelegateCredentials 761.It Cm GSSAPIDelegateCredentials
747Forward (delegate) credentials to the server. 762Forward (delegate) credentials to the server.
748The default is 763The default is
749.Dq no . 764.Dq no .
750Note that this option applies to protocol version 2 only. 765Note that this option applies to protocol version 2 connections using GSSAPI.
766.It Cm GSSAPIRenewalForcesRekey
767If set to
768.Dq yes
769then renewal of the client's GSSAPI credentials will force the rekeying of the
770ssh connection. With a compatible server, this can delegate the renewed
771credentials to a session on the server.
772The default is
773.Dq no .
774.It Cm GSSAPITrustDns
775Set to
776.Dq yes to indicate that the DNS is trusted to securely canonicalize
777the name of the host being connected to. If
778.Dq no, the hostname entered on the
779command line will be passed untouched to the GSSAPI library.
780The default is
781.Dq no .
782This option only applies to protocol version 2 connections using GSSAPI.
751.It Cm HashKnownHosts 783.It Cm HashKnownHosts
752Indicates that 784Indicates that
753.Xr ssh 1 785.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index ba56f6433..faa8ec589 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 e1c767c14..cf38baebf 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
@@ -1815,10 +1819,13 @@ main(int ac, char **av)
1815 logit("Disabling protocol version 1. Could not load host key"); 1819 logit("Disabling protocol version 1. Could not load host key");
1816 options.protocol &= ~SSH_PROTO_1; 1820 options.protocol &= ~SSH_PROTO_1;
1817 } 1821 }
1822#ifndef GSSAPI
1823 /* The GSSAPI key exchange can run without a host key */
1818 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { 1824 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
1819 logit("Disabling protocol version 2. Could not load host key"); 1825 logit("Disabling protocol version 2. Could not load host key");
1820 options.protocol &= ~SSH_PROTO_2; 1826 options.protocol &= ~SSH_PROTO_2;
1821 } 1827 }
1828#endif
1822 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { 1829 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
1823 logit("sshd: no hostkeys available -- exiting."); 1830 logit("sshd: no hostkeys available -- exiting.");
1824 exit(1); 1831 exit(1);
@@ -2132,6 +2139,60 @@ main(int ac, char **av)
2132 remote_ip, remote_port, 2139 remote_ip, remote_port,
2133 get_local_ipaddr(sock_in), get_local_port()); 2140 get_local_ipaddr(sock_in), get_local_port());
2134 2141
2142#ifdef USE_SECURITY_SESSION_API
2143 /*
2144 * Create a new security session for use by the new user login if
2145 * the current session is the root session or we are not launched
2146 * by inetd (eg: debugging mode or server mode). We do not
2147 * necessarily need to create a session if we are launched from
2148 * inetd because Panther xinetd will create a session for us.
2149 *
2150 * The only case where this logic will fail is if there is an
2151 * inetd running in a non-root session which is not creating
2152 * new sessions for us. Then all the users will end up in the
2153 * same session (bad).
2154 *
2155 * When the client exits, the session will be destroyed for us
2156 * automatically.
2157 *
2158 * We must create the session before any credentials are stored
2159 * (including AFS pags, which happens a few lines below).
2160 */
2161 {
2162 OSStatus err = 0;
2163 SecuritySessionId sid = 0;
2164 SessionAttributeBits sattrs = 0;
2165
2166 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2167 if (err)
2168 error("SessionGetInfo() failed with error %.8X",
2169 (unsigned) err);
2170 else
2171 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2172 (unsigned) sid, (unsigned) sattrs);
2173
2174 if (inetd_flag && !(sattrs & sessionIsRoot))
2175 debug("Running in inetd mode in a non-root session... "
2176 "assuming inetd created the session for us.");
2177 else {
2178 debug("Creating new security session...");
2179 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2180 if (err)
2181 error("SessionCreate() failed with error %.8X",
2182 (unsigned) err);
2183
2184 err = SessionGetInfo(callerSecuritySession, &sid,
2185 &sattrs);
2186 if (err)
2187 error("SessionGetInfo() failed with error %.8X",
2188 (unsigned) err);
2189 else
2190 debug("New Session ID is %.8X / Session Attributes are %.8X",
2191 (unsigned) sid, (unsigned) sattrs);
2192 }
2193 }
2194#endif
2195
2135 /* 2196 /*
2136 * We don't want to listen forever unless the other side 2197 * We don't want to listen forever unless the other side
2137 * successfully authenticates itself. So we set up an alarm which is 2198 * successfully authenticates itself. So we set up an alarm which is
@@ -2561,6 +2622,48 @@ do_ssh2_kex(void)
2561 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2622 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2562 list_hostkey_types()); 2623 list_hostkey_types());
2563 2624
2625#ifdef GSSAPI
2626 {
2627 char *orig;
2628 char *gss = NULL;
2629 char *newstr = NULL;
2630 orig = myproposal[PROPOSAL_KEX_ALGS];
2631
2632 /*
2633 * If we don't have a host key, then there's no point advertising
2634 * the other key exchange algorithms
2635 */
2636
2637 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2638 orig = NULL;
2639
2640 if (options.gss_keyex)
2641 gss = ssh_gssapi_server_mechanisms();
2642 else
2643 gss = NULL;
2644
2645 if (gss && orig)
2646 xasprintf(&newstr, "%s,%s", gss, orig);
2647 else if (gss)
2648 newstr = gss;
2649 else if (orig)
2650 newstr = orig;
2651
2652 /*
2653 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2654 * key alg, but we can't tell people about it unless its the only
2655 * host key algorithm we support
2656 */
2657 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2658 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2659
2660 if (newstr)
2661 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2662 else
2663 fatal("No supported key exchange algorithms");
2664 }
2665#endif
2666
2564 /* start key exchange */ 2667 /* start key exchange */
2565 if ((r = kex_setup(active_state, myproposal)) != 0) 2668 if ((r = kex_setup(active_state, myproposal)) != 0)
2566 fatal("kex_setup: %s", ssh_err(r)); 2669 fatal("kex_setup: %s", ssh_err(r));
@@ -2575,6 +2678,13 @@ do_ssh2_kex(void)
2575# endif 2678# endif
2576#endif 2679#endif
2577 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2680 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2681#ifdef GSSAPI
2682 if (options.gss_keyex) {
2683 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2684 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2685 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2686 }
2687#endif
2578 kex->server = 1; 2688 kex->server = 1;
2579 kex->client_version_string=client_version_string; 2689 kex->client_version_string=client_version_string;
2580 kex->server_version_string=server_version_string; 2690 kex->server_version_string=server_version_string;
diff --git a/sshd_config b/sshd_config
index c9042ac3c..a71ad1951 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 6dce0c70c..033149695 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -564,12 +564,40 @@ Specifies whether user authentication based on GSSAPI is allowed.
564The default is 564The default is
565.Dq no . 565.Dq no .
566Note that this option applies to protocol version 2 only. 566Note that this option applies to protocol version 2 only.
567.It Cm GSSAPIKeyExchange
568Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
569doesn't rely on ssh keys to verify host identity.
570The default is
571.Dq no .
572Note that this option applies to protocol version 2 only.
567.It Cm GSSAPICleanupCredentials 573.It Cm GSSAPICleanupCredentials
568Specifies whether to automatically destroy the user's credentials cache 574Specifies whether to automatically destroy the user's credentials cache
569on logout. 575on logout.
570The default is 576The default is
571.Dq yes . 577.Dq yes .
572Note that this option applies to protocol version 2 only. 578Note that this option applies to protocol version 2 only.
579.It Cm GSSAPIStrictAcceptorCheck
580Determines whether to be strict about the identity of the GSSAPI acceptor
581a client authenticates against. If
582.Dq yes
583then the client must authenticate against the
584.Pa host
585service on the current hostname. If
586.Dq no
587then the client may authenticate against any service key stored in the
588machine's default store. This facility is provided to assist with operation
589on multi homed machines.
590The default is
591.Dq yes .
592Note that this option applies only to protocol version 2 GSSAPI connections,
593and setting it to
594.Dq no
595may only work with recent Kerberos GSSAPI libraries.
596.It Cm GSSAPIStoreCredentialsOnRekey
597Controls whether the user's GSSAPI credentials should be updated following a
598successful connection rekeying. This option can be used to accepted renewed
599or updated credentials from a compatible client. The default is
600.Dq no .
573.It Cm HostbasedAcceptedKeyTypes 601.It Cm HostbasedAcceptedKeyTypes
574Specifies the key types that will be accepted for hostbased authentication 602Specifies the key types that will be accepted for hostbased authentication
575as a comma-separated pattern list. 603as a comma-separated pattern list.
diff --git a/sshkey.c b/sshkey.c
index 476879033..cd5992ecb 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 62c1c3e2f..9314e8513 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