summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2014-10-07 14:26:43 +0100
commit1c1b6fa17982eb622e2c4e8f4a279f2113f57413 (patch)
treea67e7472f48242904e6a45732508822af63fd331
parent487bdb3a5ef6075887b830ccb8a0b14f6da78e93 (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: 2014-10-07 Patch-Name: gssapi.patch
-rw-r--r--ChangeLog.gssapi113
-rw-r--r--Makefile.in3
-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
-rwxr-xr-xconfigure57
-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.c332
-rw-r--r--kexgsss.c290
-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
33 files changed, 2052 insertions, 59 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 06be3d5d5..086d8ddcf 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -82,6 +82,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
82 atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ 82 atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
83 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ 83 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
84 kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \ 84 kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \
85 kexgssc.o \
85 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ 86 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
86 ssh-pkcs11.o krl.o smult_curve25519_ref.o \ 87 ssh-pkcs11.o krl.o smult_curve25519_ref.o \
87 kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \ 88 kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \
@@ -101,7 +102,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
101 auth2-none.o auth2-passwd.o auth2-pubkey.o \ 102 auth2-none.o auth2-passwd.o auth2-pubkey.o \
102 monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \ 103 monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \
103 kexc25519s.o auth-krb5.o \ 104 kexc25519s.o auth-krb5.o \
104 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 105 auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
105 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ 106 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
106 sftp-server.o sftp-common.o \ 107 sftp-server.o sftp-common.o \
107 roaming_common.o roaming_serv.o \ 108 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 447f896f2..284f364b0 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: auth2-gss.c,v 1.21 2014/02/26 20:28:44 djm Exp $ */ 1/* $OpenBSD: auth2-gss.c,v 1.21 2014/02/26 20:28:44 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
@@ -53,6 +53,40 @@ static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
53static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); 53static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
54static void input_gssapi_errtok(int, u_int32_t, void *); 54static void 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)
@@ -236,7 +270,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
236 270
237 packet_check_eom(); 271 packet_check_eom();
238 272
239 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 273 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
274 authctxt->pw));
240 275
241 authctxt->postponed = 0; 276 authctxt->postponed = 0;
242 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 277 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
@@ -271,7 +306,8 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
271 gssbuf.length = buffer_len(&b); 306 gssbuf.length = buffer_len(&b);
272 307
273 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) 308 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
274 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 309 authenticated =
310 PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
275 else 311 else
276 logit("GSSAPI MIC check failed"); 312 logit("GSSAPI MIC check failed");
277 313
@@ -286,6 +322,12 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
286 userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL); 322 userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
287} 323}
288 324
325Authmethod method_gsskeyex = {
326 "gssapi-keyex",
327 userauth_gsskeyex,
328 &options.gss_authentication
329};
330
289Authmethod method_gssapi = { 331Authmethod method_gssapi = {
290 "gssapi-with-mic", 332 "gssapi-with-mic",
291 userauth_gssapi, 333 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index d9b440ae3..2f0d565ab 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 397c96532..f9175e344 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -111,6 +111,10 @@
111#include "msg.h" 111#include "msg.h"
112#include "roaming.h" 112#include "roaming.h"
113 113
114#ifdef GSSAPI
115#include "ssh-gss.h"
116#endif
117
114/* import options */ 118/* import options */
115extern Options options; 119extern Options options;
116 120
@@ -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 xxx_kex->done = 0; 1614 xxx_kex->done = 0;
diff --git a/config.h.in b/config.h.in
index 16d620615..a9a8b7ae3 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1622,6 +1622,9 @@
1622/* Use btmp to log bad logins */ 1622/* Use btmp to log bad logins */
1623#undef USE_BTMP 1623#undef USE_BTMP
1624 1624
1625/* platform uses an in-memory credentials cache */
1626#undef USE_CCAPI
1627
1625/* Use libedit for sftp */ 1628/* Use libedit for sftp */
1626#undef USE_LIBEDIT 1629#undef USE_LIBEDIT
1627 1630
@@ -1637,6 +1640,9 @@
1637/* Use PIPES instead of a socketpair() */ 1640/* Use PIPES instead of a socketpair() */
1638#undef USE_PIPES 1641#undef USE_PIPES
1639 1642
1643/* platform has the Security Authorization Session API */
1644#undef USE_SECURITY_SESSION_API
1645
1640/* Define if you have Solaris process contracts */ 1646/* Define if you have Solaris process contracts */
1641#undef USE_SOLARIS_PROCESS_CONTRACTS 1647#undef USE_SOLARIS_PROCESS_CONTRACTS
1642 1648
diff --git a/configure b/configure
index 6815388cc..ea5f200e8 100755
--- a/configure
+++ b/configure
@@ -7168,6 +7168,63 @@ $as_echo "#define SSH_TUN_COMPAT_AF 1" >>confdefs.h
7168 7168
7169$as_echo "#define SSH_TUN_PREPEND_AF 1" >>confdefs.h 7169$as_echo "#define SSH_TUN_PREPEND_AF 1" >>confdefs.h
7170 7170
7171 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we have the Security Authorization Session API" >&5
7172$as_echo_n "checking if we have the Security Authorization Session API... " >&6; }
7173 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
7174/* end confdefs.h. */
7175#include <Security/AuthSession.h>
7176int
7177main ()
7178{
7179SessionCreate(0, 0);
7180 ;
7181 return 0;
7182}
7183_ACEOF
7184if ac_fn_c_try_compile "$LINENO"; then :
7185 ac_cv_use_security_session_api="yes"
7186
7187$as_echo "#define USE_SECURITY_SESSION_API 1" >>confdefs.h
7188
7189 LIBS="$LIBS -framework Security"
7190 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7191$as_echo "yes" >&6; }
7192else
7193 ac_cv_use_security_session_api="no"
7194 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7195$as_echo "no" >&6; }
7196fi
7197rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
7198 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we have an in-memory credentials cache" >&5
7199$as_echo_n "checking if we have an in-memory credentials cache... " >&6; }
7200 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
7201/* end confdefs.h. */
7202#include <Kerberos/Kerberos.h>
7203int
7204main ()
7205{
7206cc_context_t c;
7207 (void) cc_initialize (&c, 0, NULL, NULL);
7208 ;
7209 return 0;
7210}
7211_ACEOF
7212if ac_fn_c_try_compile "$LINENO"; then :
7213
7214$as_echo "#define USE_CCAPI 1" >>confdefs.h
7215
7216 LIBS="$LIBS -framework Security"
7217 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7218$as_echo "yes" >&6; }
7219 if test "x$ac_cv_use_security_session_api" = "xno"; then
7220 as_fn_error $? "*** Need a security framework to use the credentials cache API ***" "$LINENO" 5
7221 fi
7222else
7223 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7224$as_echo "no" >&6; }
7225
7226fi
7227rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
7171 7228
7172 ac_fn_c_check_decl "$LINENO" "AU_IPv4" "ac_cv_have_decl_AU_IPv4" "$ac_includes_default" 7229 ac_fn_c_check_decl "$LINENO" "AU_IPv4" "ac_cv_have_decl_AU_IPv4" "$ac_includes_default"
7173if test "x$ac_cv_have_decl_AU_IPv4" = xyes; then : 7230if test "x$ac_cv_have_decl_AU_IPv4" = xyes; then :
diff --git a/configure.ac b/configure.ac
index 67c4486e7..90e81e160 100644
--- a/configure.ac
+++ b/configure.ac
@@ -584,6 +584,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
584 [Use tunnel device compatibility to OpenBSD]) 584 [Use tunnel device compatibility to OpenBSD])
585 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 585 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
586 [Prepend the address family to IP tunnel traffic]) 586 [Prepend the address family to IP tunnel traffic])
587 AC_MSG_CHECKING([if we have the Security Authorization Session API])
588 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
589 [SessionCreate(0, 0);],
590 [ac_cv_use_security_session_api="yes"
591 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
592 [platform has the Security Authorization Session API])
593 LIBS="$LIBS -framework Security"
594 AC_MSG_RESULT([yes])],
595 [ac_cv_use_security_session_api="no"
596 AC_MSG_RESULT([no])])
597 AC_MSG_CHECKING([if we have an in-memory credentials cache])
598 AC_TRY_COMPILE(
599 [#include <Kerberos/Kerberos.h>],
600 [cc_context_t c;
601 (void) cc_initialize (&c, 0, NULL, NULL);],
602 [AC_DEFINE([USE_CCAPI], [1],
603 [platform uses an in-memory credentials cache])
604 LIBS="$LIBS -framework Security"
605 AC_MSG_RESULT([yes])
606 if test "x$ac_cv_use_security_session_api" = "xno"; then
607 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
608 fi],
609 [AC_MSG_RESULT([no])]
610 )
587 m4_pattern_allow([AU_IPv]) 611 m4_pattern_allow([AU_IPv])
588 AC_CHECK_DECL([AU_IPv4], [], 612 AC_CHECK_DECL([AU_IPv4], [],
589 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 613 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
diff --git a/gss-genr.c b/gss-genr.c
index b39281bc1..1e569adc3 100644
--- a/gss-genr.c
+++ b/gss-genr.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-genr.c,v 1.22 2013/11/08 00:39:15 djm Exp $ */ 1/* $OpenBSD: gss-genr.c,v 1.22 2013/11/08 00:39:15 djm 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
@@ -39,12 +39,167 @@
39#include "buffer.h" 39#include "buffer.h"
40#include "log.h" 40#include "log.h"
41#include "ssh2.h" 41#include "ssh2.h"
42#include "cipher.h"
43#include "key.h"
44#include "kex.h"
45#include <openssl/evp.h>
42 46
43#include "ssh-gss.h" 47#include "ssh-gss.h"
44 48
45extern u_char *session_id2; 49extern u_char *session_id2;
46extern u_int session_id2_len; 50extern u_int session_id2_len;
47 51
52typedef struct {
53 char *encoded;
54 gss_OID oid;
55} ssh_gss_kex_mapping;
56
57/*
58 * XXX - It would be nice to find a more elegant way of handling the
59 * XXX passing of the key exchange context to the userauth routines
60 */
61
62Gssctxt *gss_kex_context = NULL;
63
64static ssh_gss_kex_mapping *gss_enc2oid = NULL;
65
66int
67ssh_gssapi_oid_table_ok(void) {
68 return (gss_enc2oid != NULL);
69}
70
71/*
72 * Return a list of the gss-group1-sha1 mechanisms supported by this program
73 *
74 * We test mechanisms to ensure that we can use them, to avoid starting
75 * a key exchange with a bad mechanism
76 */
77
78char *
79ssh_gssapi_client_mechanisms(const char *host, const char *client) {
80 gss_OID_set gss_supported;
81 OM_uint32 min_status;
82
83 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
84 return NULL;
85
86 return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
87 host, client));
88}
89
90char *
91ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
92 const char *host, const char *client) {
93 Buffer buf;
94 size_t i;
95 int oidpos, enclen;
96 char *mechs, *encoded;
97 u_char digest[EVP_MAX_MD_SIZE];
98 char deroid[2];
99 const EVP_MD *evp_md = EVP_md5();
100 EVP_MD_CTX md;
101
102 if (gss_enc2oid != NULL) {
103 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
104 free(gss_enc2oid[i].encoded);
105 free(gss_enc2oid);
106 }
107
108 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
109 (gss_supported->count + 1));
110
111 buffer_init(&buf);
112
113 oidpos = 0;
114 for (i = 0; i < gss_supported->count; i++) {
115 if (gss_supported->elements[i].length < 128 &&
116 (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
117
118 deroid[0] = SSH_GSS_OIDTYPE;
119 deroid[1] = gss_supported->elements[i].length;
120
121 EVP_DigestInit(&md, evp_md);
122 EVP_DigestUpdate(&md, deroid, 2);
123 EVP_DigestUpdate(&md,
124 gss_supported->elements[i].elements,
125 gss_supported->elements[i].length);
126 EVP_DigestFinal(&md, digest, NULL);
127
128 encoded = xmalloc(EVP_MD_size(evp_md) * 2);
129 enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
130 encoded, EVP_MD_size(evp_md) * 2);
131
132 if (oidpos != 0)
133 buffer_put_char(&buf, ',');
134
135 buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
136 sizeof(KEX_GSS_GEX_SHA1_ID) - 1);
137 buffer_append(&buf, encoded, enclen);
138 buffer_put_char(&buf, ',');
139 buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID,
140 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1);
141 buffer_append(&buf, encoded, enclen);
142 buffer_put_char(&buf, ',');
143 buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
144 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1);
145 buffer_append(&buf, encoded, enclen);
146
147 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
148 gss_enc2oid[oidpos].encoded = encoded;
149 oidpos++;
150 }
151 }
152 gss_enc2oid[oidpos].oid = NULL;
153 gss_enc2oid[oidpos].encoded = NULL;
154
155 buffer_put_char(&buf, '\0');
156
157 mechs = xmalloc(buffer_len(&buf));
158 buffer_get(&buf, mechs, buffer_len(&buf));
159 buffer_free(&buf);
160
161 if (strlen(mechs) == 0) {
162 free(mechs);
163 mechs = NULL;
164 }
165
166 return (mechs);
167}
168
169gss_OID
170ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
171 int i = 0;
172
173 switch (kex_type) {
174 case KEX_GSS_GRP1_SHA1:
175 if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
176 return GSS_C_NO_OID;
177 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
178 break;
179 case KEX_GSS_GRP14_SHA1:
180 if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
181 return GSS_C_NO_OID;
182 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
183 break;
184 case KEX_GSS_GEX_SHA1:
185 if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
186 return GSS_C_NO_OID;
187 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
188 break;
189 default:
190 return GSS_C_NO_OID;
191 }
192
193 while (gss_enc2oid[i].encoded != NULL &&
194 strcmp(name, gss_enc2oid[i].encoded) != 0)
195 i++;
196
197 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
198 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
199
200 return gss_enc2oid[i].oid;
201}
202
48/* Check that the OID in a data stream matches that in the context */ 203/* Check that the OID in a data stream matches that in the context */
49int 204int
50ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 205ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -197,7 +352,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
197 } 352 }
198 353
199 ctx->major = gss_init_sec_context(&ctx->minor, 354 ctx->major = gss_init_sec_context(&ctx->minor,
200 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 355 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
201 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 356 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
202 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 357 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
203 358
@@ -227,8 +382,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
227} 382}
228 383
229OM_uint32 384OM_uint32
385ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
386{
387 gss_buffer_desc gssbuf;
388 gss_name_t gssname;
389 OM_uint32 status;
390 gss_OID_set oidset;
391
392 gssbuf.value = (void *) name;
393 gssbuf.length = strlen(gssbuf.value);
394
395 gss_create_empty_oid_set(&status, &oidset);
396 gss_add_oid_set_member(&status, ctx->oid, &oidset);
397
398 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
399 GSS_C_NT_USER_NAME, &gssname);
400
401 if (!ctx->major)
402 ctx->major = gss_acquire_cred(&ctx->minor,
403 gssname, 0, oidset, GSS_C_INITIATE,
404 &ctx->client_creds, NULL, NULL);
405
406 gss_release_name(&status, &gssname);
407 gss_release_oid_set(&status, &oidset);
408
409 if (ctx->major)
410 ssh_gssapi_error(ctx);
411
412 return(ctx->major);
413}
414
415OM_uint32
230ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 416ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
231{ 417{
418 if (ctx == NULL)
419 return -1;
420
232 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 421 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
233 GSS_C_QOP_DEFAULT, buffer, hash))) 422 GSS_C_QOP_DEFAULT, buffer, hash)))
234 ssh_gssapi_error(ctx); 423 ssh_gssapi_error(ctx);
@@ -236,6 +425,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
236 return (ctx->major); 425 return (ctx->major);
237} 426}
238 427
428/* Priviledged when used by server */
429OM_uint32
430ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
431{
432 if (ctx == NULL)
433 return -1;
434
435 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
436 gssbuf, gssmic, NULL);
437
438 return (ctx->major);
439}
440
239void 441void
240ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, 442ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
241 const char *context) 443 const char *context)
@@ -249,11 +451,16 @@ ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
249} 451}
250 452
251int 453int
252ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 454ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
455 const char *client)
253{ 456{
254 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 457 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
255 OM_uint32 major, minor; 458 OM_uint32 major, minor;
256 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 459 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
460 Gssctxt *intctx = NULL;
461
462 if (ctx == NULL)
463 ctx = &intctx;
257 464
258 /* RFC 4462 says we MUST NOT do SPNEGO */ 465 /* RFC 4462 says we MUST NOT do SPNEGO */
259 if (oid->length == spnego_oid.length && 466 if (oid->length == spnego_oid.length &&
@@ -263,6 +470,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
263 ssh_gssapi_build_ctx(ctx); 470 ssh_gssapi_build_ctx(ctx);
264 ssh_gssapi_set_oid(*ctx, oid); 471 ssh_gssapi_set_oid(*ctx, oid);
265 major = ssh_gssapi_import_name(*ctx, host); 472 major = ssh_gssapi_import_name(*ctx, host);
473
474 if (!GSS_ERROR(major) && client)
475 major = ssh_gssapi_client_identity(*ctx, client);
476
266 if (!GSS_ERROR(major)) { 477 if (!GSS_ERROR(major)) {
267 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 478 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
268 NULL); 479 NULL);
@@ -272,10 +483,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
272 GSS_C_NO_BUFFER); 483 GSS_C_NO_BUFFER);
273 } 484 }
274 485
275 if (GSS_ERROR(major)) 486 if (GSS_ERROR(major) || intctx != NULL)
276 ssh_gssapi_delete_ctx(ctx); 487 ssh_gssapi_delete_ctx(ctx);
277 488
278 return (!GSS_ERROR(major)); 489 return (!GSS_ERROR(major));
279} 490}
280 491
492int
493ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
494 static gss_name_t saved_name = GSS_C_NO_NAME;
495 static OM_uint32 saved_lifetime = 0;
496 static gss_OID saved_mech = GSS_C_NO_OID;
497 static gss_name_t name;
498 static OM_uint32 last_call = 0;
499 OM_uint32 lifetime, now, major, minor;
500 int equal;
501
502 now = time(NULL);
503
504 if (ctxt) {
505 debug("Rekey has happened - updating saved versions");
506
507 if (saved_name != GSS_C_NO_NAME)
508 gss_release_name(&minor, &saved_name);
509
510 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
511 &saved_name, &saved_lifetime, NULL, NULL);
512
513 if (!GSS_ERROR(major)) {
514 saved_mech = ctxt->oid;
515 saved_lifetime+= now;
516 } else {
517 /* Handle the error */
518 }
519 return 0;
520 }
521
522 if (now - last_call < 10)
523 return 0;
524
525 last_call = now;
526
527 if (saved_mech == GSS_C_NO_OID)
528 return 0;
529
530 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
531 &name, &lifetime, NULL, NULL);
532 if (major == GSS_S_CREDENTIALS_EXPIRED)
533 return 0;
534 else if (GSS_ERROR(major))
535 return 0;
536
537 major = gss_compare_name(&minor, saved_name, name, &equal);
538 gss_release_name(&minor, &name);
539 if (GSS_ERROR(major))
540 return 0;
541
542 if (equal && (saved_lifetime < lifetime + now - 10))
543 return 1;
544
545 return 0;
546}
547
281#endif /* GSSAPI */ 548#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 5c599247b..50fa43834 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.27 2014/07/03 03:34:09 djm Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.27 2014/07/03 03:34:09 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -45,15 +45,21 @@
45#include "channels.h" 45#include "channels.h"
46#include "session.h" 46#include "session.h"
47#include "misc.h" 47#include "misc.h"
48#include "servconf.h"
49#include "uidswap.h"
48 50
49#include "ssh-gss.h" 51#include "ssh-gss.h"
52#include "monitor_wrap.h"
53
54extern ServerOptions options;
50 55
51static ssh_gssapi_client gssapi_client = 56static ssh_gssapi_client gssapi_client =
52 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 57 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
53 GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; 58 GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL,
59 {NULL, NULL, NULL, NULL, NULL}, 0, 0};
54 60
55ssh_gssapi_mech gssapi_null_mech = 61ssh_gssapi_mech gssapi_null_mech =
56 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; 62 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
57 63
58#ifdef KRB5 64#ifdef KRB5
59extern ssh_gssapi_mech gssapi_kerberos_mech; 65extern ssh_gssapi_mech gssapi_kerberos_mech;
@@ -100,25 +106,32 @@ ssh_gssapi_acquire_cred(Gssctxt *ctx)
100 char lname[NI_MAXHOST]; 106 char lname[NI_MAXHOST];
101 gss_OID_set oidset; 107 gss_OID_set oidset;
102 108
103 gss_create_empty_oid_set(&status, &oidset); 109 if (options.gss_strict_acceptor) {
104 gss_add_oid_set_member(&status, ctx->oid, &oidset); 110 gss_create_empty_oid_set(&status, &oidset);
111 gss_add_oid_set_member(&status, ctx->oid, &oidset);
105 112
106 if (gethostname(lname, sizeof(lname))) { 113 if (gethostname(lname, sizeof(lname))) {
107 gss_release_oid_set(&status, &oidset); 114 gss_release_oid_set(&status, &oidset);
108 return (-1); 115 return (-1);
109 } 116 }
117
118 if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
119 gss_release_oid_set(&status, &oidset);
120 return (ctx->major);
121 }
122
123 if ((ctx->major = gss_acquire_cred(&ctx->minor,
124 ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds,
125 NULL, NULL)))
126 ssh_gssapi_error(ctx);
110 127
111 if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
112 gss_release_oid_set(&status, &oidset); 128 gss_release_oid_set(&status, &oidset);
113 return (ctx->major); 129 return (ctx->major);
130 } else {
131 ctx->name = GSS_C_NO_NAME;
132 ctx->creds = GSS_C_NO_CREDENTIAL;
114 } 133 }
115 134 return GSS_S_COMPLETE;
116 if ((ctx->major = gss_acquire_cred(&ctx->minor,
117 ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL)))
118 ssh_gssapi_error(ctx);
119
120 gss_release_oid_set(&status, &oidset);
121 return (ctx->major);
122} 135}
123 136
124/* Privileged */ 137/* Privileged */
@@ -133,6 +146,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
133} 146}
134 147
135/* Unprivileged */ 148/* Unprivileged */
149char *
150ssh_gssapi_server_mechanisms(void) {
151 gss_OID_set supported;
152
153 ssh_gssapi_supported_oids(&supported);
154 return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
155 NULL, NULL));
156}
157
158/* Unprivileged */
159int
160ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
161 const char *dummy) {
162 Gssctxt *ctx = NULL;
163 int res;
164
165 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
166 ssh_gssapi_delete_ctx(&ctx);
167
168 return (res);
169}
170
171/* Unprivileged */
136void 172void
137ssh_gssapi_supported_oids(gss_OID_set *oidset) 173ssh_gssapi_supported_oids(gss_OID_set *oidset)
138{ 174{
@@ -142,7 +178,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
142 gss_OID_set supported; 178 gss_OID_set supported;
143 179
144 gss_create_empty_oid_set(&min_status, oidset); 180 gss_create_empty_oid_set(&min_status, oidset);
145 gss_indicate_mechs(&min_status, &supported); 181
182 if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
183 return;
146 184
147 while (supported_mechs[i]->name != NULL) { 185 while (supported_mechs[i]->name != NULL) {
148 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 186 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -268,8 +306,48 @@ OM_uint32
268ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 306ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
269{ 307{
270 int i = 0; 308 int i = 0;
309 int equal = 0;
310 gss_name_t new_name = GSS_C_NO_NAME;
311 gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
312
313 if (options.gss_store_rekey && client->used && ctx->client_creds) {
314 if (client->mech->oid.length != ctx->oid->length ||
315 (memcmp(client->mech->oid.elements,
316 ctx->oid->elements, ctx->oid->length) !=0)) {
317 debug("Rekeyed credentials have different mechanism");
318 return GSS_S_COMPLETE;
319 }
320
321 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
322 ctx->client_creds, ctx->oid, &new_name,
323 NULL, NULL, NULL))) {
324 ssh_gssapi_error(ctx);
325 return (ctx->major);
326 }
327
328 ctx->major = gss_compare_name(&ctx->minor, client->name,
329 new_name, &equal);
271 330
272 gss_buffer_desc ename; 331 if (GSS_ERROR(ctx->major)) {
332 ssh_gssapi_error(ctx);
333 return (ctx->major);
334 }
335
336 if (!equal) {
337 debug("Rekeyed credentials have different name");
338 return GSS_S_COMPLETE;
339 }
340
341 debug("Marking rekeyed credentials for export");
342
343 gss_release_name(&ctx->minor, &client->name);
344 gss_release_cred(&ctx->minor, &client->creds);
345 client->name = new_name;
346 client->creds = ctx->client_creds;
347 ctx->client_creds = GSS_C_NO_CREDENTIAL;
348 client->updated = 1;
349 return GSS_S_COMPLETE;
350 }
273 351
274 client->mech = NULL; 352 client->mech = NULL;
275 353
@@ -284,6 +362,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
284 if (client->mech == NULL) 362 if (client->mech == NULL)
285 return GSS_S_FAILURE; 363 return GSS_S_FAILURE;
286 364
365 if (ctx->client_creds &&
366 (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
367 ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
368 ssh_gssapi_error(ctx);
369 return (ctx->major);
370 }
371
287 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 372 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
288 &client->displayname, NULL))) { 373 &client->displayname, NULL))) {
289 ssh_gssapi_error(ctx); 374 ssh_gssapi_error(ctx);
@@ -301,6 +386,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
301 return (ctx->major); 386 return (ctx->major);
302 } 387 }
303 388
389 gss_release_buffer(&ctx->minor, &ename);
390
304 /* We can't copy this structure, so we just move the pointer to it */ 391 /* We can't copy this structure, so we just move the pointer to it */
305 client->creds = ctx->client_creds; 392 client->creds = ctx->client_creds;
306 ctx->client_creds = GSS_C_NO_CREDENTIAL; 393 ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -348,7 +435,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
348 435
349/* Privileged */ 436/* Privileged */
350int 437int
351ssh_gssapi_userok(char *user) 438ssh_gssapi_userok(char *user, struct passwd *pw)
352{ 439{
353 OM_uint32 lmin; 440 OM_uint32 lmin;
354 441
@@ -358,9 +445,11 @@ ssh_gssapi_userok(char *user)
358 return 0; 445 return 0;
359 } 446 }
360 if (gssapi_client.mech && gssapi_client.mech->userok) 447 if (gssapi_client.mech && gssapi_client.mech->userok)
361 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 448 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
449 gssapi_client.used = 1;
450 gssapi_client.store.owner = pw;
362 return 1; 451 return 1;
363 else { 452 } else {
364 /* Destroy delegated credentials if userok fails */ 453 /* Destroy delegated credentials if userok fails */
365 gss_release_buffer(&lmin, &gssapi_client.displayname); 454 gss_release_buffer(&lmin, &gssapi_client.displayname);
366 gss_release_buffer(&lmin, &gssapi_client.exportedname); 455 gss_release_buffer(&lmin, &gssapi_client.exportedname);
@@ -374,14 +463,90 @@ ssh_gssapi_userok(char *user)
374 return (0); 463 return (0);
375} 464}
376 465
377/* Privileged */ 466/* These bits are only used for rekeying. The unpriviledged child is running
378OM_uint32 467 * as the user, the monitor is root.
379ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 468 *
469 * In the child, we want to :
470 * *) Ask the monitor to store our credentials into the store we specify
471 * *) If it succeeds, maybe do a PAM update
472 */
473
474/* Stuff for PAM */
475
476#ifdef USE_PAM
477static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
478 struct pam_response **resp, void *data)
380{ 479{
381 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 480 return (PAM_CONV_ERR);
382 gssbuf, gssmic, NULL); 481}
482#endif
383 483
384 return (ctx->major); 484void
485ssh_gssapi_rekey_creds(void) {
486 int ok;
487 int ret;
488#ifdef USE_PAM
489 pam_handle_t *pamh = NULL;
490 struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
491 char *envstr;
492#endif
493
494 if (gssapi_client.store.filename == NULL &&
495 gssapi_client.store.envval == NULL &&
496 gssapi_client.store.envvar == NULL)
497 return;
498
499 ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
500
501 if (!ok)
502 return;
503
504 debug("Rekeyed credentials stored successfully");
505
506 /* Actually managing to play with the ssh pam stack from here will
507 * be next to impossible. In any case, we may want different options
508 * for rekeying. So, use our own :)
509 */
510#ifdef USE_PAM
511 if (!use_privsep) {
512 debug("Not even going to try and do PAM with privsep disabled");
513 return;
514 }
515
516 ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
517 &pamconv, &pamh);
518 if (ret)
519 return;
520
521 xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
522 gssapi_client.store.envval);
523
524 ret = pam_putenv(pamh, envstr);
525 if (!ret)
526 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
527 pam_end(pamh, PAM_SUCCESS);
528#endif
529}
530
531int
532ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
533 int ok = 0;
534
535 /* Check we've got credentials to store */
536 if (!gssapi_client.updated)
537 return 0;
538
539 gssapi_client.updated = 0;
540
541 temporarily_use_uid(gssapi_client.store.owner);
542 if (gssapi_client.mech && gssapi_client.mech->updatecreds)
543 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
544 else
545 debug("No update function for this mechanism");
546
547 restore_uid();
548
549 return ok;
385} 550}
386 551
387#endif 552#endif
diff --git a/kex.c b/kex.c
index a173e70e3..891852b54 100644
--- a/kex.c
+++ b/kex.c
@@ -53,6 +53,10 @@
53#include "roaming.h" 53#include "roaming.h"
54#include "digest.h" 54#include "digest.h"
55 55
56#ifdef GSSAPI
57#include "ssh-gss.h"
58#endif
59
56#if OPENSSL_VERSION_NUMBER >= 0x00907000L 60#if OPENSSL_VERSION_NUMBER >= 0x00907000L
57# if defined(HAVE_EVP_SHA256) 61# if defined(HAVE_EVP_SHA256)
58# define evp_ssh_sha256 EVP_sha256 62# define evp_ssh_sha256 EVP_sha256
@@ -96,6 +100,14 @@ static const struct kexalg kexalgs[] = {
96#endif /* HAVE_EVP_SHA256 */ 100#endif /* HAVE_EVP_SHA256 */
97 { NULL, -1, -1, -1}, 101 { NULL, -1, -1, -1},
98}; 102};
103static const struct kexalg kexalg_prefixes[] = {
104#ifdef GSSAPI
105 { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
106 { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
107 { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
108#endif
109 { NULL, -1, -1, -1 },
110};
99 111
100char * 112char *
101kex_alg_list(char sep) 113kex_alg_list(char sep)
@@ -124,6 +136,10 @@ kex_alg_by_name(const char *name)
124 if (strcmp(k->name, name) == 0) 136 if (strcmp(k->name, name) == 0)
125 return k; 137 return k;
126 } 138 }
139 for (k = kexalg_prefixes; k->name != NULL; k++) {
140 if (strncmp(k->name, name, strlen(k->name)) == 0)
141 return k;
142 }
127 return NULL; 143 return NULL;
128} 144}
129 145
diff --git a/kex.h b/kex.h
index 4c40ec851..c179a4d06 100644
--- a/kex.h
+++ b/kex.h
@@ -76,6 +76,9 @@ enum kex_exchange {
76 KEX_DH_GEX_SHA256, 76 KEX_DH_GEX_SHA256,
77 KEX_ECDH_SHA2, 77 KEX_ECDH_SHA2,
78 KEX_C25519_SHA256, 78 KEX_C25519_SHA256,
79 KEX_GSS_GRP1_SHA1,
80 KEX_GSS_GRP14_SHA1,
81 KEX_GSS_GEX_SHA1,
79 KEX_MAX 82 KEX_MAX
80}; 83};
81 84
@@ -135,6 +138,12 @@ struct Kex {
135 int flags; 138 int flags;
136 int hash_alg; 139 int hash_alg;
137 int ec_nid; 140 int ec_nid;
141#ifdef GSSAPI
142 int gss_deleg_creds;
143 int gss_trust_dns;
144 char *gss_host;
145 char *gss_client;
146#endif
138 char *client_version_string; 147 char *client_version_string;
139 char *server_version_string; 148 char *server_version_string;
140 int (*verify_host_key)(Key *); 149 int (*verify_host_key)(Key *);
@@ -167,6 +176,11 @@ void kexecdh_server(Kex *);
167void kexc25519_client(Kex *); 176void kexc25519_client(Kex *);
168void kexc25519_server(Kex *); 177void kexc25519_server(Kex *);
169 178
179#ifdef GSSAPI
180void kexgss_client(Kex *);
181void kexgss_server(Kex *);
182#endif
183
170void 184void
171kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, 185kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int,
172 BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); 186 BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *);
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..92a31c5a3
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,332 @@
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
46#include "ssh-gss.h"
47
48void
49kexgss_client(Kex *kex) {
50 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
51 gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr;
52 Gssctxt *ctxt;
53 OM_uint32 maj_status, min_status, ret_flags;
54 u_int klen, kout, slen = 0, hashlen, strlen;
55 DH *dh;
56 BIGNUM *dh_server_pub = NULL;
57 BIGNUM *shared_secret = NULL;
58 BIGNUM *p = NULL;
59 BIGNUM *g = NULL;
60 u_char *kbuf, *hash;
61 u_char *serverhostkey = NULL;
62 u_char *empty = "";
63 char *msg;
64 int type = 0;
65 int first = 1;
66 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
67
68 /* Initialise our GSSAPI world */
69 ssh_gssapi_build_ctx(&ctxt);
70 if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
71 == GSS_C_NO_OID)
72 fatal("Couldn't identify host exchange");
73
74 if (ssh_gssapi_import_name(ctxt, kex->gss_host))
75 fatal("Couldn't import hostname");
76
77 if (kex->gss_client &&
78 ssh_gssapi_client_identity(ctxt, kex->gss_client))
79 fatal("Couldn't acquire client credentials");
80
81 switch (kex->kex_type) {
82 case KEX_GSS_GRP1_SHA1:
83 dh = dh_new_group1();
84 break;
85 case KEX_GSS_GRP14_SHA1:
86 dh = dh_new_group14();
87 break;
88 case KEX_GSS_GEX_SHA1:
89 debug("Doing group exchange\n");
90 nbits = dh_estimate(kex->we_need * 8);
91 packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
92 packet_put_int(min);
93 packet_put_int(nbits);
94 packet_put_int(max);
95
96 packet_send();
97
98 packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
99
100 if ((p = BN_new()) == NULL)
101 fatal("BN_new() failed");
102 packet_get_bignum2(p);
103 if ((g = BN_new()) == NULL)
104 fatal("BN_new() failed");
105 packet_get_bignum2(g);
106 packet_check_eom();
107
108 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
109 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
110 min, BN_num_bits(p), max);
111
112 dh = dh_new_group(g, p);
113 break;
114 default:
115 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
116 }
117
118 /* Step 1 - e is dh->pub_key */
119 dh_gen_key(dh, kex->we_need * 8);
120
121 /* This is f, we initialise it now to make life easier */
122 dh_server_pub = BN_new();
123 if (dh_server_pub == NULL)
124 fatal("dh_server_pub == NULL");
125
126 token_ptr = GSS_C_NO_BUFFER;
127
128 do {
129 debug("Calling gss_init_sec_context");
130
131 maj_status = ssh_gssapi_init_ctx(ctxt,
132 kex->gss_deleg_creds, token_ptr, &send_tok,
133 &ret_flags);
134
135 if (GSS_ERROR(maj_status)) {
136 if (send_tok.length != 0) {
137 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
138 packet_put_string(send_tok.value,
139 send_tok.length);
140 }
141 fatal("gss_init_context failed");
142 }
143
144 /* If we've got an old receive buffer get rid of it */
145 if (token_ptr != GSS_C_NO_BUFFER)
146 free(recv_tok.value);
147
148 if (maj_status == GSS_S_COMPLETE) {
149 /* If mutual state flag is not true, kex fails */
150 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
151 fatal("Mutual authentication failed");
152
153 /* If integ avail flag is not true kex fails */
154 if (!(ret_flags & GSS_C_INTEG_FLAG))
155 fatal("Integrity check failed");
156 }
157
158 /*
159 * If we have data to send, then the last message that we
160 * received cannot have been a 'complete'.
161 */
162 if (send_tok.length != 0) {
163 if (first) {
164 packet_start(SSH2_MSG_KEXGSS_INIT);
165 packet_put_string(send_tok.value,
166 send_tok.length);
167 packet_put_bignum2(dh->pub_key);
168 first = 0;
169 } else {
170 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
171 packet_put_string(send_tok.value,
172 send_tok.length);
173 }
174 packet_send();
175 gss_release_buffer(&min_status, &send_tok);
176
177 /* If we've sent them data, they should reply */
178 do {
179 type = packet_read();
180 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
181 debug("Received KEXGSS_HOSTKEY");
182 if (serverhostkey)
183 fatal("Server host key received more than once");
184 serverhostkey =
185 packet_get_string(&slen);
186 }
187 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
188
189 switch (type) {
190 case SSH2_MSG_KEXGSS_CONTINUE:
191 debug("Received GSSAPI_CONTINUE");
192 if (maj_status == GSS_S_COMPLETE)
193 fatal("GSSAPI Continue received from server when complete");
194 recv_tok.value = packet_get_string(&strlen);
195 recv_tok.length = strlen;
196 break;
197 case SSH2_MSG_KEXGSS_COMPLETE:
198 debug("Received GSSAPI_COMPLETE");
199 packet_get_bignum2(dh_server_pub);
200 msg_tok.value = packet_get_string(&strlen);
201 msg_tok.length = strlen;
202
203 /* Is there a token included? */
204 if (packet_get_char()) {
205 recv_tok.value=
206 packet_get_string(&strlen);
207 recv_tok.length = strlen;
208 /* If we're already complete - protocol error */
209 if (maj_status == GSS_S_COMPLETE)
210 packet_disconnect("Protocol error: received token when complete");
211 } else {
212 /* No token included */
213 if (maj_status != GSS_S_COMPLETE)
214 packet_disconnect("Protocol error: did not receive final token");
215 }
216 break;
217 case SSH2_MSG_KEXGSS_ERROR:
218 debug("Received Error");
219 maj_status = packet_get_int();
220 min_status = packet_get_int();
221 msg = packet_get_string(NULL);
222 (void) packet_get_string_ptr(NULL);
223 fatal("GSSAPI Error: \n%.400s",msg);
224 default:
225 packet_disconnect("Protocol error: didn't expect packet type %d",
226 type);
227 }
228 token_ptr = &recv_tok;
229 } else {
230 /* No data, and not complete */
231 if (maj_status != GSS_S_COMPLETE)
232 fatal("Not complete, and no token output");
233 }
234 } while (maj_status & GSS_S_CONTINUE_NEEDED);
235
236 /*
237 * We _must_ have received a COMPLETE message in reply from the
238 * server, which will have set dh_server_pub and msg_tok
239 */
240
241 if (type != SSH2_MSG_KEXGSS_COMPLETE)
242 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
243
244 /* Check f in range [1, p-1] */
245 if (!dh_pub_is_valid(dh, dh_server_pub))
246 packet_disconnect("bad server public DH value");
247
248 /* compute K=f^x mod p */
249 klen = DH_size(dh);
250 kbuf = xmalloc(klen);
251 kout = DH_compute_key(kbuf, dh_server_pub, dh);
252 if (kout < 0)
253 fatal("DH_compute_key: failed");
254
255 shared_secret = BN_new();
256 if (shared_secret == NULL)
257 fatal("kexgss_client: BN_new failed");
258
259 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
260 fatal("kexdh_client: BN_bin2bn failed");
261
262 memset(kbuf, 0, klen);
263 free(kbuf);
264
265 switch (kex->kex_type) {
266 case KEX_GSS_GRP1_SHA1:
267 case KEX_GSS_GRP14_SHA1:
268 kex_dh_hash( kex->client_version_string,
269 kex->server_version_string,
270 buffer_ptr(&kex->my), buffer_len(&kex->my),
271 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
272 (serverhostkey ? serverhostkey : empty), slen,
273 dh->pub_key, /* e */
274 dh_server_pub, /* f */
275 shared_secret, /* K */
276 &hash, &hashlen
277 );
278 break;
279 case KEX_GSS_GEX_SHA1:
280 kexgex_hash(
281 kex->hash_alg,
282 kex->client_version_string,
283 kex->server_version_string,
284 buffer_ptr(&kex->my), buffer_len(&kex->my),
285 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
286 (serverhostkey ? serverhostkey : empty), slen,
287 min, nbits, max,
288 dh->p, dh->g,
289 dh->pub_key,
290 dh_server_pub,
291 shared_secret,
292 &hash, &hashlen
293 );
294 break;
295 default:
296 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
297 }
298
299 gssbuf.value = hash;
300 gssbuf.length = hashlen;
301
302 /* Verify that the hash matches the MIC we just got. */
303 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
304 packet_disconnect("Hash's MIC didn't verify");
305
306 free(msg_tok.value);
307
308 DH_free(dh);
309 free(serverhostkey);
310 BN_clear_free(dh_server_pub);
311
312 /* save session id */
313 if (kex->session_id == NULL) {
314 kex->session_id_len = hashlen;
315 kex->session_id = xmalloc(kex->session_id_len);
316 memcpy(kex->session_id, hash, kex->session_id_len);
317 }
318
319 if (kex->gss_deleg_creds)
320 ssh_gssapi_credentials_updated(ctxt);
321
322 if (gss_kex_context == NULL)
323 gss_kex_context = ctxt;
324 else
325 ssh_gssapi_delete_ctx(&ctxt);
326
327 kex_derive_keys_bn(kex, hash, hashlen, shared_secret);
328 BN_clear_free(shared_secret);
329 kex_finish(kex);
330}
331
332#endif /* GSSAPI */
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..6a0ece84b
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,290 @@
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
48extern ServerOptions options;
49
50void
51kexgss_server(Kex *kex)
52{
53 OM_uint32 maj_status, min_status;
54
55 /*
56 * Some GSSAPI implementations use the input value of ret_flags (an
57 * output variable) as a means of triggering mechanism specific
58 * features. Initializing it to zero avoids inadvertently
59 * activating this non-standard behaviour.
60 */
61
62 OM_uint32 ret_flags = 0;
63 gss_buffer_desc gssbuf, recv_tok, msg_tok;
64 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
65 Gssctxt *ctxt = NULL;
66 u_int slen, klen, kout, hashlen;
67 u_char *kbuf, *hash;
68 DH *dh;
69 int min = -1, max = -1, nbits = -1;
70 BIGNUM *shared_secret = NULL;
71 BIGNUM *dh_client_pub = NULL;
72 int type = 0;
73 gss_OID oid;
74 char *mechs;
75
76 /* Initialise GSSAPI */
77
78 /* If we're rekeying, privsep means that some of the private structures
79 * in the GSSAPI code are no longer available. This kludges them back
80 * into life
81 */
82 if (!ssh_gssapi_oid_table_ok()) {
83 mechs = ssh_gssapi_server_mechanisms();
84 free(mechs);
85 }
86
87 debug2("%s: Identifying %s", __func__, kex->name);
88 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
89 if (oid == GSS_C_NO_OID)
90 fatal("Unknown gssapi mechanism");
91
92 debug2("%s: Acquiring credentials", __func__);
93
94 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
95 fatal("Unable to acquire credentials for the server");
96
97 switch (kex->kex_type) {
98 case KEX_GSS_GRP1_SHA1:
99 dh = dh_new_group1();
100 break;
101 case KEX_GSS_GRP14_SHA1:
102 dh = dh_new_group14();
103 break;
104 case KEX_GSS_GEX_SHA1:
105 debug("Doing group exchange");
106 packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
107 min = packet_get_int();
108 nbits = packet_get_int();
109 max = packet_get_int();
110 min = MAX(DH_GRP_MIN, min);
111 max = MIN(DH_GRP_MAX, max);
112 packet_check_eom();
113 if (max < min || nbits < min || max < nbits)
114 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
115 min, nbits, max);
116 dh = PRIVSEP(choose_dh(min, nbits, max));
117 if (dh == NULL)
118 packet_disconnect("Protocol error: no matching group found");
119
120 packet_start(SSH2_MSG_KEXGSS_GROUP);
121 packet_put_bignum2(dh->p);
122 packet_put_bignum2(dh->g);
123 packet_send();
124
125 packet_write_wait();
126 break;
127 default:
128 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
129 }
130
131 dh_gen_key(dh, kex->we_need * 8);
132
133 do {
134 debug("Wait SSH2_MSG_GSSAPI_INIT");
135 type = packet_read();
136 switch(type) {
137 case SSH2_MSG_KEXGSS_INIT:
138 if (dh_client_pub != NULL)
139 fatal("Received KEXGSS_INIT after initialising");
140 recv_tok.value = packet_get_string(&slen);
141 recv_tok.length = slen;
142
143 if ((dh_client_pub = BN_new()) == NULL)
144 fatal("dh_client_pub == NULL");
145
146 packet_get_bignum2(dh_client_pub);
147
148 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
149 break;
150 case SSH2_MSG_KEXGSS_CONTINUE:
151 recv_tok.value = packet_get_string(&slen);
152 recv_tok.length = slen;
153 break;
154 default:
155 packet_disconnect(
156 "Protocol error: didn't expect packet type %d",
157 type);
158 }
159
160 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
161 &send_tok, &ret_flags));
162
163 free(recv_tok.value);
164
165 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
166 fatal("Zero length token output when incomplete");
167
168 if (dh_client_pub == NULL)
169 fatal("No client public key");
170
171 if (maj_status & GSS_S_CONTINUE_NEEDED) {
172 debug("Sending GSSAPI_CONTINUE");
173 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
174 packet_put_string(send_tok.value, send_tok.length);
175 packet_send();
176 gss_release_buffer(&min_status, &send_tok);
177 }
178 } while (maj_status & GSS_S_CONTINUE_NEEDED);
179
180 if (GSS_ERROR(maj_status)) {
181 if (send_tok.length > 0) {
182 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
183 packet_put_string(send_tok.value, send_tok.length);
184 packet_send();
185 }
186 fatal("accept_ctx died");
187 }
188
189 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
190 fatal("Mutual Authentication flag wasn't set");
191
192 if (!(ret_flags & GSS_C_INTEG_FLAG))
193 fatal("Integrity flag wasn't set");
194
195 if (!dh_pub_is_valid(dh, dh_client_pub))
196 packet_disconnect("bad client public DH value");
197
198 klen = DH_size(dh);
199 kbuf = xmalloc(klen);
200 kout = DH_compute_key(kbuf, dh_client_pub, dh);
201 if (kout < 0)
202 fatal("DH_compute_key: failed");
203
204 shared_secret = BN_new();
205 if (shared_secret == NULL)
206 fatal("kexgss_server: BN_new failed");
207
208 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
209 fatal("kexgss_server: BN_bin2bn failed");
210
211 memset(kbuf, 0, klen);
212 free(kbuf);
213
214 switch (kex->kex_type) {
215 case KEX_GSS_GRP1_SHA1:
216 case KEX_GSS_GRP14_SHA1:
217 kex_dh_hash(
218 kex->client_version_string, kex->server_version_string,
219 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
220 buffer_ptr(&kex->my), buffer_len(&kex->my),
221 NULL, 0, /* Change this if we start sending host keys */
222 dh_client_pub, dh->pub_key, shared_secret,
223 &hash, &hashlen
224 );
225 break;
226 case KEX_GSS_GEX_SHA1:
227 kexgex_hash(
228 kex->hash_alg,
229 kex->client_version_string, kex->server_version_string,
230 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
231 buffer_ptr(&kex->my), buffer_len(&kex->my),
232 NULL, 0,
233 min, nbits, max,
234 dh->p, dh->g,
235 dh_client_pub,
236 dh->pub_key,
237 shared_secret,
238 &hash, &hashlen
239 );
240 break;
241 default:
242 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
243 }
244
245 BN_clear_free(dh_client_pub);
246
247 if (kex->session_id == NULL) {
248 kex->session_id_len = hashlen;
249 kex->session_id = xmalloc(kex->session_id_len);
250 memcpy(kex->session_id, hash, kex->session_id_len);
251 }
252
253 gssbuf.value = hash;
254 gssbuf.length = hashlen;
255
256 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
257 fatal("Couldn't get MIC");
258
259 packet_start(SSH2_MSG_KEXGSS_COMPLETE);
260 packet_put_bignum2(dh->pub_key);
261 packet_put_string(msg_tok.value,msg_tok.length);
262
263 if (send_tok.length != 0) {
264 packet_put_char(1); /* true */
265 packet_put_string(send_tok.value, send_tok.length);
266 } else {
267 packet_put_char(0); /* false */
268 }
269 packet_send();
270
271 gss_release_buffer(&min_status, &send_tok);
272 gss_release_buffer(&min_status, &msg_tok);
273
274 if (gss_kex_context == NULL)
275 gss_kex_context = ctxt;
276 else
277 ssh_gssapi_delete_ctx(&ctxt);
278
279 DH_free(dh);
280
281 kex_derive_keys_bn(kex, hash, hashlen, shared_secret);
282 BN_clear_free(shared_secret);
283 kex_finish(kex);
284
285 /* If this was a rekey, then save out any delegated credentials we
286 * just exchanged. */
287 if (options.gss_store_rekey)
288 ssh_gssapi_rekey_creds();
289}
290#endif /* GSSAPI */
diff --git a/monitor.c b/monitor.c
index dbe29f128..b0896ef7f 100644
--- a/monitor.c
+++ b/monitor.c
@@ -178,6 +178,8 @@ int mm_answer_gss_setup_ctx(int, Buffer *);
178int mm_answer_gss_accept_ctx(int, Buffer *); 178int mm_answer_gss_accept_ctx(int, Buffer *);
179int mm_answer_gss_userok(int, Buffer *); 179int mm_answer_gss_userok(int, Buffer *);
180int mm_answer_gss_checkmic(int, Buffer *); 180int mm_answer_gss_checkmic(int, Buffer *);
181int mm_answer_gss_sign(int, Buffer *);
182int mm_answer_gss_updatecreds(int, Buffer *);
181#endif 183#endif
182 184
183#ifdef SSH_AUDIT_EVENTS 185#ifdef SSH_AUDIT_EVENTS
@@ -255,11 +257,18 @@ struct mon_table mon_dispatch_proto20[] = {
255 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, 257 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
256 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, 258 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
257 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, 259 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
260 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
258#endif 261#endif
259 {0, 0, NULL} 262 {0, 0, NULL}
260}; 263};
261 264
262struct mon_table mon_dispatch_postauth20[] = { 265struct mon_table mon_dispatch_postauth20[] = {
266#ifdef GSSAPI
267 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
268 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
269 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
270 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
271#endif
263#ifdef WITH_OPENSSL 272#ifdef WITH_OPENSSL
264 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 273 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
265#endif 274#endif
@@ -374,6 +383,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
374 /* Permit requests for moduli and signatures */ 383 /* Permit requests for moduli and signatures */
375 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 384 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
376 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 385 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
386#ifdef GSSAPI
387 /* and for the GSSAPI key exchange */
388 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
389#endif
377 } else { 390 } else {
378 mon_dispatch = mon_dispatch_proto15; 391 mon_dispatch = mon_dispatch_proto15;
379 392
@@ -482,6 +495,10 @@ monitor_child_postauth(struct monitor *pmonitor)
482 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 495 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
483 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 496 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
484 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 497 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
498#ifdef GSSAPI
499 /* and for the GSSAPI key exchange */
500 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
501#endif
485 } else { 502 } else {
486 mon_dispatch = mon_dispatch_postauth15; 503 mon_dispatch = mon_dispatch_postauth15;
487 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 504 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
@@ -1861,6 +1878,13 @@ mm_get_kex(Buffer *m)
1861 kex->kex[KEX_ECDH_SHA2] = kexecdh_server; 1878 kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
1862#endif 1879#endif
1863 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 1880 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1881#ifdef GSSAPI
1882 if (options.gss_keyex) {
1883 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1884 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1885 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1886 }
1887#endif
1864 kex->server = 1; 1888 kex->server = 1;
1865 kex->hostkey_type = buffer_get_int(m); 1889 kex->hostkey_type = buffer_get_int(m);
1866 kex->kex_type = buffer_get_int(m); 1890 kex->kex_type = buffer_get_int(m);
@@ -2068,6 +2092,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m)
2068 OM_uint32 major; 2092 OM_uint32 major;
2069 u_int len; 2093 u_int len;
2070 2094
2095 if (!options.gss_authentication && !options.gss_keyex)
2096 fatal("In GSSAPI monitor when GSSAPI is disabled");
2097
2071 goid.elements = buffer_get_string(m, &len); 2098 goid.elements = buffer_get_string(m, &len);
2072 goid.length = len; 2099 goid.length = len;
2073 2100
@@ -2095,6 +2122,9 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
2095 OM_uint32 flags = 0; /* GSI needs this */ 2122 OM_uint32 flags = 0; /* GSI needs this */
2096 u_int len; 2123 u_int len;
2097 2124
2125 if (!options.gss_authentication && !options.gss_keyex)
2126 fatal("In GSSAPI monitor when GSSAPI is disabled");
2127
2098 in.value = buffer_get_string(m, &len); 2128 in.value = buffer_get_string(m, &len);
2099 in.length = len; 2129 in.length = len;
2100 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); 2130 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
@@ -2112,6 +2142,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
2112 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 2142 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2113 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 2143 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2114 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 2144 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2145 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2115 } 2146 }
2116 return (0); 2147 return (0);
2117} 2148}
@@ -2123,6 +2154,9 @@ mm_answer_gss_checkmic(int sock, Buffer *m)
2123 OM_uint32 ret; 2154 OM_uint32 ret;
2124 u_int len; 2155 u_int len;
2125 2156
2157 if (!options.gss_authentication && !options.gss_keyex)
2158 fatal("In GSSAPI monitor when GSSAPI is disabled");
2159
2126 gssbuf.value = buffer_get_string(m, &len); 2160 gssbuf.value = buffer_get_string(m, &len);
2127 gssbuf.length = len; 2161 gssbuf.length = len;
2128 mic.value = buffer_get_string(m, &len); 2162 mic.value = buffer_get_string(m, &len);
@@ -2149,7 +2183,11 @@ mm_answer_gss_userok(int sock, Buffer *m)
2149{ 2183{
2150 int authenticated; 2184 int authenticated;
2151 2185
2152 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 2186 if (!options.gss_authentication && !options.gss_keyex)
2187 fatal("In GSSAPI monitor when GSSAPI is disabled");
2188
2189 authenticated = authctxt->valid &&
2190 ssh_gssapi_userok(authctxt->user, authctxt->pw);
2153 2191
2154 buffer_clear(m); 2192 buffer_clear(m);
2155 buffer_put_int(m, authenticated); 2193 buffer_put_int(m, authenticated);
@@ -2162,5 +2200,73 @@ mm_answer_gss_userok(int sock, Buffer *m)
2162 /* Monitor loop will terminate if authenticated */ 2200 /* Monitor loop will terminate if authenticated */
2163 return (authenticated); 2201 return (authenticated);
2164} 2202}
2203
2204int
2205mm_answer_gss_sign(int socket, Buffer *m)
2206{
2207 gss_buffer_desc data;
2208 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2209 OM_uint32 major, minor;
2210 u_int len;
2211
2212 if (!options.gss_authentication && !options.gss_keyex)
2213 fatal("In GSSAPI monitor when GSSAPI is disabled");
2214
2215 data.value = buffer_get_string(m, &len);
2216 data.length = len;
2217 if (data.length != 20)
2218 fatal("%s: data length incorrect: %d", __func__,
2219 (int) data.length);
2220
2221 /* Save the session ID on the first time around */
2222 if (session_id2_len == 0) {
2223 session_id2_len = data.length;
2224 session_id2 = xmalloc(session_id2_len);
2225 memcpy(session_id2, data.value, session_id2_len);
2226 }
2227 major = ssh_gssapi_sign(gsscontext, &data, &hash);
2228
2229 free(data.value);
2230
2231 buffer_clear(m);
2232 buffer_put_int(m, major);
2233 buffer_put_string(m, hash.value, hash.length);
2234
2235 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2236
2237 gss_release_buffer(&minor, &hash);
2238
2239 /* Turn on getpwnam permissions */
2240 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2241
2242 /* And credential updating, for when rekeying */
2243 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2244
2245 return (0);
2246}
2247
2248int
2249mm_answer_gss_updatecreds(int socket, Buffer *m) {
2250 ssh_gssapi_ccache store;
2251 int ok;
2252
2253 store.filename = buffer_get_string(m, NULL);
2254 store.envvar = buffer_get_string(m, NULL);
2255 store.envval = buffer_get_string(m, NULL);
2256
2257 ok = ssh_gssapi_update_creds(&store);
2258
2259 free(store.filename);
2260 free(store.envvar);
2261 free(store.envval);
2262
2263 buffer_clear(m);
2264 buffer_put_int(m, ok);
2265
2266 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2267
2268 return(0);
2269}
2270
2165#endif /* GSSAPI */ 2271#endif /* GSSAPI */
2166 2272
diff --git a/monitor.h b/monitor.h
index 5bc41b513..7f32b0c0c 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 45dc16951..e476f0dbc 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1281,7 +1281,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1281} 1281}
1282 1282
1283int 1283int
1284mm_ssh_gssapi_userok(char *user) 1284mm_ssh_gssapi_userok(char *user, struct passwd *pw)
1285{ 1285{
1286 Buffer m; 1286 Buffer m;
1287 int authenticated = 0; 1287 int authenticated = 0;
@@ -1298,5 +1298,50 @@ mm_ssh_gssapi_userok(char *user)
1298 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1298 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1299 return (authenticated); 1299 return (authenticated);
1300} 1300}
1301
1302OM_uint32
1303mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1304{
1305 Buffer m;
1306 OM_uint32 major;
1307 u_int len;
1308
1309 buffer_init(&m);
1310 buffer_put_string(&m, data->value, data->length);
1311
1312 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
1313 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
1314
1315 major = buffer_get_int(&m);
1316 hash->value = buffer_get_string(&m, &len);
1317 hash->length = len;
1318
1319 buffer_free(&m);
1320
1321 return(major);
1322}
1323
1324int
1325mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1326{
1327 Buffer m;
1328 int ok;
1329
1330 buffer_init(&m);
1331
1332 buffer_put_cstring(&m, store->filename ? store->filename : "");
1333 buffer_put_cstring(&m, store->envvar ? store->envvar : "");
1334 buffer_put_cstring(&m, store->envval ? store->envval : "");
1335
1336 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m);
1337 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m);
1338
1339 ok = buffer_get_int(&m);
1340
1341 buffer_free(&m);
1342
1343 return (ok);
1344}
1345
1301#endif /* GSSAPI */ 1346#endif /* GSSAPI */
1302 1347
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 18c25010d..a4e9d24b3 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 7948ce1cd..9127e9324 100644
--- a/readconf.c
+++ b/readconf.c
@@ -142,6 +142,8 @@ typedef enum {
142 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 142 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
143 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 143 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
144 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 144 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
145 oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
146 oGssServerIdentity,
145 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 147 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
146 oSendEnv, oControlPath, oControlMaster, oControlPersist, 148 oSendEnv, oControlPath, oControlMaster, oControlPersist,
147 oHashKnownHosts, 149 oHashKnownHosts,
@@ -185,10 +187,19 @@ static struct {
185 { "afstokenpassing", oUnsupported }, 187 { "afstokenpassing", oUnsupported },
186#if defined(GSSAPI) 188#if defined(GSSAPI)
187 { "gssapiauthentication", oGssAuthentication }, 189 { "gssapiauthentication", oGssAuthentication },
190 { "gssapikeyexchange", oGssKeyEx },
188 { "gssapidelegatecredentials", oGssDelegateCreds }, 191 { "gssapidelegatecredentials", oGssDelegateCreds },
192 { "gssapitrustdns", oGssTrustDns },
193 { "gssapiclientidentity", oGssClientIdentity },
194 { "gssapiserveridentity", oGssServerIdentity },
195 { "gssapirenewalforcesrekey", oGssRenewalRekey },
189#else 196#else
190 { "gssapiauthentication", oUnsupported }, 197 { "gssapiauthentication", oUnsupported },
198 { "gssapikeyexchange", oUnsupported },
191 { "gssapidelegatecredentials", oUnsupported }, 199 { "gssapidelegatecredentials", oUnsupported },
200 { "gssapitrustdns", oUnsupported },
201 { "gssapiclientidentity", oUnsupported },
202 { "gssapirenewalforcesrekey", oUnsupported },
192#endif 203#endif
193 { "fallbacktorsh", oDeprecated }, 204 { "fallbacktorsh", oDeprecated },
194 { "usersh", oDeprecated }, 205 { "usersh", oDeprecated },
@@ -865,10 +876,30 @@ parse_time:
865 intptr = &options->gss_authentication; 876 intptr = &options->gss_authentication;
866 goto parse_flag; 877 goto parse_flag;
867 878
879 case oGssKeyEx:
880 intptr = &options->gss_keyex;
881 goto parse_flag;
882
868 case oGssDelegateCreds: 883 case oGssDelegateCreds:
869 intptr = &options->gss_deleg_creds; 884 intptr = &options->gss_deleg_creds;
870 goto parse_flag; 885 goto parse_flag;
871 886
887 case oGssTrustDns:
888 intptr = &options->gss_trust_dns;
889 goto parse_flag;
890
891 case oGssClientIdentity:
892 charptr = &options->gss_client_identity;
893 goto parse_string;
894
895 case oGssServerIdentity:
896 charptr = &options->gss_server_identity;
897 goto parse_string;
898
899 case oGssRenewalRekey:
900 intptr = &options->gss_renewal_rekey;
901 goto parse_flag;
902
872 case oBatchMode: 903 case oBatchMode:
873 intptr = &options->batch_mode; 904 intptr = &options->batch_mode;
874 goto parse_flag; 905 goto parse_flag;
@@ -1538,7 +1569,12 @@ initialize_options(Options * options)
1538 options->pubkey_authentication = -1; 1569 options->pubkey_authentication = -1;
1539 options->challenge_response_authentication = -1; 1570 options->challenge_response_authentication = -1;
1540 options->gss_authentication = -1; 1571 options->gss_authentication = -1;
1572 options->gss_keyex = -1;
1541 options->gss_deleg_creds = -1; 1573 options->gss_deleg_creds = -1;
1574 options->gss_trust_dns = -1;
1575 options->gss_renewal_rekey = -1;
1576 options->gss_client_identity = NULL;
1577 options->gss_server_identity = NULL;
1542 options->password_authentication = -1; 1578 options->password_authentication = -1;
1543 options->kbd_interactive_authentication = -1; 1579 options->kbd_interactive_authentication = -1;
1544 options->kbd_interactive_devices = NULL; 1580 options->kbd_interactive_devices = NULL;
@@ -1661,8 +1697,14 @@ fill_default_options(Options * options)
1661 options->challenge_response_authentication = 1; 1697 options->challenge_response_authentication = 1;
1662 if (options->gss_authentication == -1) 1698 if (options->gss_authentication == -1)
1663 options->gss_authentication = 0; 1699 options->gss_authentication = 0;
1700 if (options->gss_keyex == -1)
1701 options->gss_keyex = 0;
1664 if (options->gss_deleg_creds == -1) 1702 if (options->gss_deleg_creds == -1)
1665 options->gss_deleg_creds = 0; 1703 options->gss_deleg_creds = 0;
1704 if (options->gss_trust_dns == -1)
1705 options->gss_trust_dns = 0;
1706 if (options->gss_renewal_rekey == -1)
1707 options->gss_renewal_rekey = 0;
1666 if (options->password_authentication == -1) 1708 if (options->password_authentication == -1)
1667 options->password_authentication = 1; 1709 options->password_authentication = 1;
1668 if (options->kbd_interactive_authentication == -1) 1710 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index 0b9cb777a..0e298898a 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 b7f329447..cb3c831ab 100644
--- a/servconf.c
+++ b/servconf.c
@@ -109,7 +109,10 @@ initialize_server_options(ServerOptions *options)
109 options->kerberos_ticket_cleanup = -1; 109 options->kerberos_ticket_cleanup = -1;
110 options->kerberos_get_afs_token = -1; 110 options->kerberos_get_afs_token = -1;
111 options->gss_authentication=-1; 111 options->gss_authentication=-1;
112 options->gss_keyex = -1;
112 options->gss_cleanup_creds = -1; 113 options->gss_cleanup_creds = -1;
114 options->gss_strict_acceptor = -1;
115 options->gss_store_rekey = -1;
113 options->password_authentication = -1; 116 options->password_authentication = -1;
114 options->kbd_interactive_authentication = -1; 117 options->kbd_interactive_authentication = -1;
115 options->challenge_response_authentication = -1; 118 options->challenge_response_authentication = -1;
@@ -250,8 +253,14 @@ fill_default_server_options(ServerOptions *options)
250 options->kerberos_get_afs_token = 0; 253 options->kerberos_get_afs_token = 0;
251 if (options->gss_authentication == -1) 254 if (options->gss_authentication == -1)
252 options->gss_authentication = 0; 255 options->gss_authentication = 0;
256 if (options->gss_keyex == -1)
257 options->gss_keyex = 0;
253 if (options->gss_cleanup_creds == -1) 258 if (options->gss_cleanup_creds == -1)
254 options->gss_cleanup_creds = 1; 259 options->gss_cleanup_creds = 1;
260 if (options->gss_strict_acceptor == -1)
261 options->gss_strict_acceptor = 1;
262 if (options->gss_store_rekey == -1)
263 options->gss_store_rekey = 0;
255 if (options->password_authentication == -1) 264 if (options->password_authentication == -1)
256 options->password_authentication = 1; 265 options->password_authentication = 1;
257 if (options->kbd_interactive_authentication == -1) 266 if (options->kbd_interactive_authentication == -1)
@@ -352,7 +361,9 @@ typedef enum {
352 sBanner, sUseDNS, sHostbasedAuthentication, 361 sBanner, sUseDNS, sHostbasedAuthentication,
353 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, 362 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
354 sClientAliveCountMax, sAuthorizedKeysFile, 363 sClientAliveCountMax, sAuthorizedKeysFile,
355 sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, 364 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
365 sGssKeyEx, sGssStoreRekey,
366 sAcceptEnv, sPermitTunnel,
356 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 367 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
357 sUsePrivilegeSeparation, sAllowAgentForwarding, 368 sUsePrivilegeSeparation, sAllowAgentForwarding,
358 sHostCertificate, 369 sHostCertificate,
@@ -421,10 +432,20 @@ static struct {
421#ifdef GSSAPI 432#ifdef GSSAPI
422 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 433 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
423 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 434 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
435 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
436 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
437 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
438 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
424#else 439#else
425 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 440 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
426 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 441 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
442 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
443 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
444 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
445 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
427#endif 446#endif
447 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
448 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
428 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 449 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
429 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 450 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
430 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 451 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1104,10 +1125,22 @@ process_server_config_line(ServerOptions *options, char *line,
1104 intptr = &options->gss_authentication; 1125 intptr = &options->gss_authentication;
1105 goto parse_flag; 1126 goto parse_flag;
1106 1127
1128 case sGssKeyEx:
1129 intptr = &options->gss_keyex;
1130 goto parse_flag;
1131
1107 case sGssCleanupCreds: 1132 case sGssCleanupCreds:
1108 intptr = &options->gss_cleanup_creds; 1133 intptr = &options->gss_cleanup_creds;
1109 goto parse_flag; 1134 goto parse_flag;
1110 1135
1136 case sGssStrictAcceptor:
1137 intptr = &options->gss_strict_acceptor;
1138 goto parse_flag;
1139
1140 case sGssStoreRekey:
1141 intptr = &options->gss_store_rekey;
1142 goto parse_flag;
1143
1111 case sPasswordAuthentication: 1144 case sPasswordAuthentication:
1112 intptr = &options->password_authentication; 1145 intptr = &options->password_authentication;
1113 goto parse_flag; 1146 goto parse_flag;
@@ -2042,7 +2075,10 @@ dump_config(ServerOptions *o)
2042#endif 2075#endif
2043#ifdef GSSAPI 2076#ifdef GSSAPI
2044 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2077 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2078 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2045 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2079 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2080 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2081 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2046#endif 2082#endif
2047 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2083 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2048 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2084 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 766db3a3d..f8265a8c8 100644
--- a/servconf.h
+++ b/servconf.h
@@ -113,7 +113,10 @@ typedef struct {
113 int kerberos_get_afs_token; /* If true, try to get AFS token if 113 int kerberos_get_afs_token; /* If true, try to get AFS token if
114 * authenticated with Kerberos. */ 114 * authenticated with Kerberos. */
115 int gss_authentication; /* If true, permit GSSAPI authentication */ 115 int gss_authentication; /* If true, permit GSSAPI authentication */
116 int gss_keyex; /* If true, permit GSSAPI key exchange */
116 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 117 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
118 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
119 int gss_store_rekey;
117 int password_authentication; /* If true, permit password 120 int password_authentication; /* If true, permit password
118 * authentication. */ 121 * authentication. */
119 int kbd_interactive_authentication; /* If true, permit */ 122 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 f9ede7a31..e6649acfc 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -701,11 +701,43 @@ Specifies whether user authentication based on GSSAPI is allowed.
701The default is 701The default is
702.Dq no . 702.Dq no .
703Note that this option applies to protocol version 2 only. 703Note that this option applies to protocol version 2 only.
704.It Cm GSSAPIKeyExchange
705Specifies whether key exchange based on GSSAPI may be used. When using
706GSSAPI key exchange the server need not have a host key.
707The default is
708.Dq no .
709Note that this option applies to protocol version 2 only.
710.It Cm GSSAPIClientIdentity
711If set, specifies the GSSAPI client identity that ssh should use when
712connecting to the server. The default is unset, which means that the default
713identity will be used.
714.It Cm GSSAPIServerIdentity
715If set, specifies the GSSAPI server identity that ssh should expect when
716connecting to the server. The default is unset, which means that the
717expected GSSAPI server identity will be determined from the target
718hostname.
704.It Cm GSSAPIDelegateCredentials 719.It Cm GSSAPIDelegateCredentials
705Forward (delegate) credentials to the server. 720Forward (delegate) credentials to the server.
706The default is 721The default is
707.Dq no . 722.Dq no .
708Note that this option applies to protocol version 2 only. 723Note that this option applies to protocol version 2 connections using GSSAPI.
724.It Cm GSSAPIRenewalForcesRekey
725If set to
726.Dq yes
727then renewal of the client's GSSAPI credentials will force the rekeying of the
728ssh connection. With a compatible server, this can delegate the renewed
729credentials to a session on the server.
730The default is
731.Dq no .
732.It Cm GSSAPITrustDns
733Set to
734.Dq yes to indicate that the DNS is trusted to securely canonicalize
735the name of the host being connected to. If
736.Dq no, the hostname entered on the
737command line will be passed untouched to the GSSAPI library.
738The default is
739.Dq no .
740This option only applies to protocol version 2 connections using GSSAPI.
709.It Cm HashKnownHosts 741.It Cm HashKnownHosts
710Indicates that 742Indicates that
711.Xr ssh 1 743.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index 68f7f4fdd..7b478f16d 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -159,9 +159,34 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
159 char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; 159 char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
160 Kex *kex; 160 Kex *kex;
161 161
162#ifdef GSSAPI
163 char *orig = NULL, *gss = NULL;
164 char *gss_host = NULL;
165#endif
166
162 xxx_host = host; 167 xxx_host = host;
163 xxx_hostaddr = hostaddr; 168 xxx_hostaddr = hostaddr;
164 169
170#ifdef GSSAPI
171 if (options.gss_keyex) {
172 /* Add the GSSAPI mechanisms currently supported on this
173 * client to the key exchange algorithm proposal */
174 orig = myproposal[PROPOSAL_KEX_ALGS];
175
176 if (options.gss_trust_dns)
177 gss_host = (char *)get_canonical_hostname(1);
178 else
179 gss_host = host;
180
181 gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity);
182 if (gss) {
183 debug("Offering GSSAPI proposal: %s", gss);
184 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
185 "%s,%s", gss, orig);
186 }
187 }
188#endif
189
165 if (options.ciphers == (char *)-1) { 190 if (options.ciphers == (char *)-1) {
166 logit("No valid ciphers for protocol version 2 given, using defaults."); 191 logit("No valid ciphers for protocol version 2 given, using defaults.");
167 options.ciphers = NULL; 192 options.ciphers = NULL;
@@ -199,6 +224,17 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
199 myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( 224 myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
200 myproposal[PROPOSAL_KEX_ALGS]); 225 myproposal[PROPOSAL_KEX_ALGS]);
201 226
227#ifdef GSSAPI
228 /* If we've got GSSAPI algorithms, then we also support the
229 * 'null' hostkey, as a last resort */
230 if (options.gss_keyex && gss) {
231 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
232 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
233 "%s,null", orig);
234 free(gss);
235 }
236#endif
237
202 if (options.rekey_limit || options.rekey_interval) 238 if (options.rekey_limit || options.rekey_interval)
203 packet_set_rekey_limits((u_int32_t)options.rekey_limit, 239 packet_set_rekey_limits((u_int32_t)options.rekey_limit,
204 (time_t)options.rekey_interval); 240 (time_t)options.rekey_interval);
@@ -213,10 +249,30 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
213 kex->kex[KEX_ECDH_SHA2] = kexecdh_client; 249 kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
214#endif 250#endif
215 kex->kex[KEX_C25519_SHA256] = kexc25519_client; 251 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
252#ifdef GSSAPI
253 if (options.gss_keyex) {
254 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
255 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
256 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
257 }
258#endif
216 kex->client_version_string=client_version_string; 259 kex->client_version_string=client_version_string;
217 kex->server_version_string=server_version_string; 260 kex->server_version_string=server_version_string;
218 kex->verify_host_key=&verify_host_key_callback; 261 kex->verify_host_key=&verify_host_key_callback;
219 262
263#ifdef GSSAPI
264 if (options.gss_keyex) {
265 kex->gss_deleg_creds = options.gss_deleg_creds;
266 kex->gss_trust_dns = options.gss_trust_dns;
267 kex->gss_client = options.gss_client_identity;
268 if (options.gss_server_identity) {
269 kex->gss_host = options.gss_server_identity;
270 } else {
271 kex->gss_host = gss_host;
272 }
273 }
274#endif
275
220 xxx_kex = kex; 276 xxx_kex = kex;
221 277
222 dispatch_run(DISPATCH_BLOCK, &kex->done, kex); 278 dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
@@ -306,6 +362,7 @@ void input_gssapi_token(int type, u_int32_t, void *);
306void input_gssapi_hash(int type, u_int32_t, void *); 362void input_gssapi_hash(int type, u_int32_t, void *);
307void input_gssapi_error(int, u_int32_t, void *); 363void input_gssapi_error(int, u_int32_t, void *);
308void input_gssapi_errtok(int, u_int32_t, void *); 364void input_gssapi_errtok(int, u_int32_t, void *);
365int userauth_gsskeyex(Authctxt *authctxt);
309#endif 366#endif
310 367
311void userauth(Authctxt *, char *); 368void userauth(Authctxt *, char *);
@@ -321,6 +378,11 @@ static char *authmethods_get(void);
321 378
322Authmethod authmethods[] = { 379Authmethod authmethods[] = {
323#ifdef GSSAPI 380#ifdef GSSAPI
381 {"gssapi-keyex",
382 userauth_gsskeyex,
383 NULL,
384 &options.gss_authentication,
385 NULL},
324 {"gssapi-with-mic", 386 {"gssapi-with-mic",
325 userauth_gssapi, 387 userauth_gssapi,
326 NULL, 388 NULL,
@@ -617,19 +679,31 @@ userauth_gssapi(Authctxt *authctxt)
617 static u_int mech = 0; 679 static u_int mech = 0;
618 OM_uint32 min; 680 OM_uint32 min;
619 int ok = 0; 681 int ok = 0;
682 const char *gss_host;
683
684 if (options.gss_server_identity)
685 gss_host = options.gss_server_identity;
686 else if (options.gss_trust_dns)
687 gss_host = get_canonical_hostname(1);
688 else
689 gss_host = authctxt->host;
620 690
621 /* Try one GSSAPI method at a time, rather than sending them all at 691 /* Try one GSSAPI method at a time, rather than sending them all at
622 * once. */ 692 * once. */
623 693
624 if (gss_supported == NULL) 694 if (gss_supported == NULL)
625 gss_indicate_mechs(&min, &gss_supported); 695 if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
696 gss_supported = NULL;
697 return 0;
698 }
626 699
627 /* Check to see if the mechanism is usable before we offer it */ 700 /* Check to see if the mechanism is usable before we offer it */
628 while (mech < gss_supported->count && !ok) { 701 while (mech < gss_supported->count && !ok) {
629 /* My DER encoding requires length<128 */ 702 /* My DER encoding requires length<128 */
630 if (gss_supported->elements[mech].length < 128 && 703 if (gss_supported->elements[mech].length < 128 &&
631 ssh_gssapi_check_mechanism(&gssctxt, 704 ssh_gssapi_check_mechanism(&gssctxt,
632 &gss_supported->elements[mech], authctxt->host)) { 705 &gss_supported->elements[mech], gss_host,
706 options.gss_client_identity)) {
633 ok = 1; /* Mechanism works */ 707 ok = 1; /* Mechanism works */
634 } else { 708 } else {
635 mech++; 709 mech++;
@@ -726,8 +800,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt)
726{ 800{
727 Authctxt *authctxt = ctxt; 801 Authctxt *authctxt = ctxt;
728 Gssctxt *gssctxt; 802 Gssctxt *gssctxt;
729 int oidlen; 803 u_int oidlen;
730 char *oidv; 804 u_char *oidv;
731 805
732 if (authctxt == NULL) 806 if (authctxt == NULL)
733 fatal("input_gssapi_response: no authentication context"); 807 fatal("input_gssapi_response: no authentication context");
@@ -836,6 +910,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt)
836 free(msg); 910 free(msg);
837 free(lang); 911 free(lang);
838} 912}
913
914int
915userauth_gsskeyex(Authctxt *authctxt)
916{
917 Buffer b;
918 gss_buffer_desc gssbuf;
919 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
920 OM_uint32 ms;
921
922 static int attempt = 0;
923 if (attempt++ >= 1)
924 return (0);
925
926 if (gss_kex_context == NULL) {
927 debug("No valid Key exchange context");
928 return (0);
929 }
930
931 ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
932 "gssapi-keyex");
933
934 gssbuf.value = buffer_ptr(&b);
935 gssbuf.length = buffer_len(&b);
936
937 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
938 buffer_free(&b);
939 return (0);
940 }
941
942 packet_start(SSH2_MSG_USERAUTH_REQUEST);
943 packet_put_cstring(authctxt->server_user);
944 packet_put_cstring(authctxt->service);
945 packet_put_cstring(authctxt->method->name);
946 packet_put_string(mic.value, mic.length);
947 packet_send();
948
949 buffer_free(&b);
950 gss_release_buffer(&ms, &mic);
951
952 return (1);
953}
954
839#endif /* GSSAPI */ 955#endif /* GSSAPI */
840 956
841int 957int
diff --git a/sshd.c b/sshd.c
index 481d00155..e6706a886 100644
--- a/sshd.c
+++ b/sshd.c
@@ -123,6 +123,10 @@
123#include "ssh-sandbox.h" 123#include "ssh-sandbox.h"
124#include "version.h" 124#include "version.h"
125 125
126#ifdef USE_SECURITY_SESSION_API
127#include <Security/AuthSession.h>
128#endif
129
126#ifndef O_NOCTTY 130#ifndef O_NOCTTY
127#define O_NOCTTY 0 131#define O_NOCTTY 0
128#endif 132#endif
@@ -1745,10 +1749,13 @@ main(int ac, char **av)
1745 logit("Disabling protocol version 1. Could not load host key"); 1749 logit("Disabling protocol version 1. Could not load host key");
1746 options.protocol &= ~SSH_PROTO_1; 1750 options.protocol &= ~SSH_PROTO_1;
1747 } 1751 }
1752#ifndef GSSAPI
1753 /* The GSSAPI key exchange can run without a host key */
1748 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { 1754 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
1749 logit("Disabling protocol version 2. Could not load host key"); 1755 logit("Disabling protocol version 2. Could not load host key");
1750 options.protocol &= ~SSH_PROTO_2; 1756 options.protocol &= ~SSH_PROTO_2;
1751 } 1757 }
1758#endif
1752 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { 1759 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
1753 logit("sshd: no hostkeys available -- exiting."); 1760 logit("sshd: no hostkeys available -- exiting.");
1754 exit(1); 1761 exit(1);
@@ -2060,6 +2067,60 @@ main(int ac, char **av)
2060 remote_ip, remote_port, 2067 remote_ip, remote_port,
2061 get_local_ipaddr(sock_in), get_local_port()); 2068 get_local_ipaddr(sock_in), get_local_port());
2062 2069
2070#ifdef USE_SECURITY_SESSION_API
2071 /*
2072 * Create a new security session for use by the new user login if
2073 * the current session is the root session or we are not launched
2074 * by inetd (eg: debugging mode or server mode). We do not
2075 * necessarily need to create a session if we are launched from
2076 * inetd because Panther xinetd will create a session for us.
2077 *
2078 * The only case where this logic will fail is if there is an
2079 * inetd running in a non-root session which is not creating
2080 * new sessions for us. Then all the users will end up in the
2081 * same session (bad).
2082 *
2083 * When the client exits, the session will be destroyed for us
2084 * automatically.
2085 *
2086 * We must create the session before any credentials are stored
2087 * (including AFS pags, which happens a few lines below).
2088 */
2089 {
2090 OSStatus err = 0;
2091 SecuritySessionId sid = 0;
2092 SessionAttributeBits sattrs = 0;
2093
2094 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2095 if (err)
2096 error("SessionGetInfo() failed with error %.8X",
2097 (unsigned) err);
2098 else
2099 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2100 (unsigned) sid, (unsigned) sattrs);
2101
2102 if (inetd_flag && !(sattrs & sessionIsRoot))
2103 debug("Running in inetd mode in a non-root session... "
2104 "assuming inetd created the session for us.");
2105 else {
2106 debug("Creating new security session...");
2107 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2108 if (err)
2109 error("SessionCreate() failed with error %.8X",
2110 (unsigned) err);
2111
2112 err = SessionGetInfo(callerSecuritySession, &sid,
2113 &sattrs);
2114 if (err)
2115 error("SessionGetInfo() failed with error %.8X",
2116 (unsigned) err);
2117 else
2118 debug("New Session ID is %.8X / Session Attributes are %.8X",
2119 (unsigned) sid, (unsigned) sattrs);
2120 }
2121 }
2122#endif
2123
2063 /* 2124 /*
2064 * We don't want to listen forever unless the other side 2125 * We don't want to listen forever unless the other side
2065 * successfully authenticates itself. So we set up an alarm which is 2126 * successfully authenticates itself. So we set up an alarm which is
@@ -2482,6 +2543,48 @@ do_ssh2_kex(void)
2482 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2543 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2483 list_hostkey_types()); 2544 list_hostkey_types());
2484 2545
2546#ifdef GSSAPI
2547 {
2548 char *orig;
2549 char *gss = NULL;
2550 char *newstr = NULL;
2551 orig = myproposal[PROPOSAL_KEX_ALGS];
2552
2553 /*
2554 * If we don't have a host key, then there's no point advertising
2555 * the other key exchange algorithms
2556 */
2557
2558 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2559 orig = NULL;
2560
2561 if (options.gss_keyex)
2562 gss = ssh_gssapi_server_mechanisms();
2563 else
2564 gss = NULL;
2565
2566 if (gss && orig)
2567 xasprintf(&newstr, "%s,%s", gss, orig);
2568 else if (gss)
2569 newstr = gss;
2570 else if (orig)
2571 newstr = orig;
2572
2573 /*
2574 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2575 * key alg, but we can't tell people about it unless its the only
2576 * host key algorithm we support
2577 */
2578 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2579 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2580
2581 if (newstr)
2582 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2583 else
2584 fatal("No supported key exchange algorithms");
2585 }
2586#endif
2587
2485 /* start key exchange */ 2588 /* start key exchange */
2486 kex = kex_setup(myproposal); 2589 kex = kex_setup(myproposal);
2487#ifdef WITH_OPENSSL 2590#ifdef WITH_OPENSSL
@@ -2492,6 +2595,13 @@ do_ssh2_kex(void)
2492 kex->kex[KEX_ECDH_SHA2] = kexecdh_server; 2595 kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
2493#endif 2596#endif
2494 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2597 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2598#ifdef GSSAPI
2599 if (options.gss_keyex) {
2600 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2601 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2602 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2603 }
2604#endif
2495 kex->server = 1; 2605 kex->server = 1;
2496 kex->client_version_string=client_version_string; 2606 kex->client_version_string=client_version_string;
2497 kex->server_version_string=server_version_string; 2607 kex->server_version_string=server_version_string;
diff --git a/sshd_config b/sshd_config
index e9045bc4d..d9b859407 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 fd44abe75..c8b43dae3 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -527,12 +527,40 @@ Specifies whether user authentication based on GSSAPI is allowed.
527The default is 527The default is
528.Dq no . 528.Dq no .
529Note that this option applies to protocol version 2 only. 529Note that this option applies to protocol version 2 only.
530.It Cm GSSAPIKeyExchange
531Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
532doesn't rely on ssh keys to verify host identity.
533The default is
534.Dq no .
535Note that this option applies to protocol version 2 only.
530.It Cm GSSAPICleanupCredentials 536.It Cm GSSAPICleanupCredentials
531Specifies whether to automatically destroy the user's credentials cache 537Specifies whether to automatically destroy the user's credentials cache
532on logout. 538on logout.
533The default is 539The default is
534.Dq yes . 540.Dq yes .
535Note that this option applies to protocol version 2 only. 541Note that this option applies to protocol version 2 only.
542.It Cm GSSAPIStrictAcceptorCheck
543Determines whether to be strict about the identity of the GSSAPI acceptor
544a client authenticates against. If
545.Dq yes
546then the client must authenticate against the
547.Pa host
548service on the current hostname. If
549.Dq no
550then the client may authenticate against any service key stored in the
551machine's default store. This facility is provided to assist with operation
552on multi homed machines.
553The default is
554.Dq yes .
555Note that this option applies only to protocol version 2 GSSAPI connections,
556and setting it to
557.Dq no
558may only work with recent Kerberos GSSAPI libraries.
559.It Cm GSSAPIStoreCredentialsOnRekey
560Controls whether the user's GSSAPI credentials should be updated following a
561successful connection rekeying. This option can be used to accepted renewed
562or updated credentials from a compatible client. The default is
563.Dq no .
536.It Cm HostbasedAuthentication 564.It Cm HostbasedAuthentication
537Specifies whether rhosts or /etc/hosts.equiv authentication together 565Specifies whether rhosts or /etc/hosts.equiv authentication together
538with successful public key client host authentication is allowed 566with successful public key client host authentication is allowed
diff --git a/sshkey.c b/sshkey.c
index fdd0c8a89..1a96eae19 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -110,6 +110,7 @@ static const struct keytype keytypes[] = {
110 { "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00", 110 { "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00",
111 KEY_DSA_CERT_V00, 0, 1 }, 111 KEY_DSA_CERT_V00, 0, 1 },
112#endif /* WITH_OPENSSL */ 112#endif /* WITH_OPENSSL */
113 { "null", "null", KEY_NULL, 0, 0 },
113 { NULL, NULL, -1, -1, 0 } 114 { NULL, NULL, -1, -1, 0 }
114}; 115};
115 116
@@ -198,7 +199,7 @@ key_alg_list(int certs_only, int plain_only)
198 const struct keytype *kt; 199 const struct keytype *kt;
199 200
200 for (kt = keytypes; kt->type != -1; kt++) { 201 for (kt = keytypes; kt->type != -1; kt++) {
201 if (kt->name == NULL) 202 if (kt->name == NULL || kt->type == KEY_NULL)
202 continue; 203 continue;
203 if ((certs_only && !kt->cert) || (plain_only && kt->cert)) 204 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
204 continue; 205 continue;
diff --git a/sshkey.h b/sshkey.h
index 450b30c1f..b573e7f33 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