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-03-19 16:39:52 +0000
commit429c595dbaff7f7c2b3a53fe4235211f6d788025 (patch)
tree085cf7273c133b74238c968c9c9f591f8fb0308e
parent9a975a9faed7c4f334e8c8490db3e77e102f2b21 (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-03-19 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.c289
-rw-r--r--key.c3
-rw-r--r--key.h1
-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
33 files changed, 2051 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 a8aa1272a..35c6fd6ea 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -72,6 +72,7 @@ LIBSSH_OBJS=authfd.o authfile.o bufaux.o bufbn.o buffer.o \
72 atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ 72 atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
73 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ 73 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
74 kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \ 74 kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \
75 kexgssc.o \
75 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ 76 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
76 jpake.o schnorr.o ssh-pkcs11.o krl.o smult_curve25519_ref.o \ 77 jpake.o schnorr.o ssh-pkcs11.o krl.o smult_curve25519_ref.o \
77 kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \ 78 kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \
@@ -91,7 +92,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
91 auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-jpake.o \ 92 auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-jpake.o \
92 monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \ 93 monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \
93 kexc25519s.o auth-krb5.o \ 94 kexc25519s.o auth-krb5.o \
94 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 95 auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
95 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ 96 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
96 sftp-server.o sftp-common.o \ 97 sftp-server.o sftp-common.o \
97 roaming_common.o roaming_serv.o \ 98 roaming_common.o roaming_serv.o \
diff --git a/auth-krb5.c b/auth-krb5.c
index 6c62bdf54..69a1a53e2 100644
--- a/auth-krb5.c
+++ b/auth-krb5.c
@@ -182,8 +182,13 @@ auth_krb5_password(Authctxt *authctxt, const char *password)
182 182
183 len = strlen(authctxt->krb5_ticket_file) + 6; 183 len = strlen(authctxt->krb5_ticket_file) + 6;
184 authctxt->krb5_ccname = xmalloc(len); 184 authctxt->krb5_ccname = xmalloc(len);
185#ifdef USE_CCAPI
186 snprintf(authctxt->krb5_ccname, len, "API:%s",
187 authctxt->krb5_ticket_file);
188#else
185 snprintf(authctxt->krb5_ccname, len, "FILE:%s", 189 snprintf(authctxt->krb5_ccname, len, "FILE:%s",
186 authctxt->krb5_ticket_file); 190 authctxt->krb5_ticket_file);
191#endif
187 192
188#ifdef USE_PAM 193#ifdef USE_PAM
189 if (options.use_pam) 194 if (options.use_pam)
@@ -240,15 +245,22 @@ krb5_cleanup_proc(Authctxt *authctxt)
240#ifndef HEIMDAL 245#ifndef HEIMDAL
241krb5_error_code 246krb5_error_code
242ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { 247ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
243 int tmpfd, ret, oerrno; 248 int ret, oerrno;
244 char ccname[40]; 249 char ccname[40];
245 mode_t old_umask; 250 mode_t old_umask;
251#ifdef USE_CCAPI
252 char cctemplate[] = "API:krb5cc_%d";
253#else
254 char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX";
255 int tmpfd;
256#endif
246 257
247 ret = snprintf(ccname, sizeof(ccname), 258 ret = snprintf(ccname, sizeof(ccname),
248 "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); 259 cctemplate, geteuid());
249 if (ret < 0 || (size_t)ret >= sizeof(ccname)) 260 if (ret < 0 || (size_t)ret >= sizeof(ccname))
250 return ENOMEM; 261 return ENOMEM;
251 262
263#ifndef USE_CCAPI
252 old_umask = umask(0177); 264 old_umask = umask(0177);
253 tmpfd = mkstemp(ccname + strlen("FILE:")); 265 tmpfd = mkstemp(ccname + strlen("FILE:"));
254 oerrno = errno; 266 oerrno = errno;
@@ -265,6 +277,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
265 return oerrno; 277 return oerrno;
266 } 278 }
267 close(tmpfd); 279 close(tmpfd);
280#endif
268 281
269 return (krb5_cc_resolve(ctx, ccname, ccache)); 282 return (krb5_cc_resolve(ctx, ccname, ccache));
270} 283}
diff --git a/auth2-gss.c b/auth2-gss.c
index 638d8f88e..b8db8204f 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: auth2-gss.c,v 1.20 2013/05/17 00:13:13 djm Exp $ */ 1/* $OpenBSD: auth2-gss.c,v 1.20 2013/05/17 00:13: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
@@ -52,6 +52,40 @@ static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
52static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); 52static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
53static void input_gssapi_errtok(int, u_int32_t, void *); 53static void input_gssapi_errtok(int, u_int32_t, void *);
54 54
55/*
56 * The 'gssapi_keyex' userauth mechanism.
57 */
58static int
59userauth_gsskeyex(Authctxt *authctxt)
60{
61 int authenticated = 0;
62 Buffer b;
63 gss_buffer_desc mic, gssbuf;
64 u_int len;
65
66 mic.value = packet_get_string(&len);
67 mic.length = len;
68
69 packet_check_eom();
70
71 ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
72 "gssapi-keyex");
73
74 gssbuf.value = buffer_ptr(&b);
75 gssbuf.length = buffer_len(&b);
76
77 /* gss_kex_context is NULL with privsep, so we can't check it here */
78 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
79 &gssbuf, &mic))))
80 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
81 authctxt->pw));
82
83 buffer_free(&b);
84 free(mic.value);
85
86 return (authenticated);
87}
88
55/* 89/*
56 * We only support those mechanisms that we know about (ie ones that we know 90 * We only support those mechanisms that we know about (ie ones that we know
57 * how to check local user kuserok and the like) 91 * how to check local user kuserok and the like)
@@ -240,7 +274,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
240 274
241 packet_check_eom(); 275 packet_check_eom();
242 276
243 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 277 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
278 authctxt->pw));
244 279
245 authctxt->postponed = 0; 280 authctxt->postponed = 0;
246 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 281 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
@@ -275,7 +310,8 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
275 gssbuf.length = buffer_len(&b); 310 gssbuf.length = buffer_len(&b);
276 311
277 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) 312 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
278 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 313 authenticated =
314 PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
279 else 315 else
280 logit("GSSAPI MIC check failed"); 316 logit("GSSAPI MIC check failed");
281 317
@@ -290,6 +326,12 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
290 userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL); 326 userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
291} 327}
292 328
329Authmethod method_gsskeyex = {
330 "gssapi-keyex",
331 userauth_gsskeyex,
332 &options.gss_authentication
333};
334
293Authmethod method_gssapi = { 335Authmethod method_gssapi = {
294 "gssapi-with-mic", 336 "gssapi-with-mic",
295 userauth_gssapi, 337 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index f0cab8cc0..6ed8f042b 100644
--- a/auth2.c
+++ b/auth2.c
@@ -69,6 +69,7 @@ extern Authmethod method_passwd;
69extern Authmethod method_kbdint; 69extern Authmethod method_kbdint;
70extern Authmethod method_hostbased; 70extern Authmethod method_hostbased;
71#ifdef GSSAPI 71#ifdef GSSAPI
72extern Authmethod method_gsskeyex;
72extern Authmethod method_gssapi; 73extern Authmethod method_gssapi;
73#endif 74#endif
74#ifdef JPAKE 75#ifdef JPAKE
@@ -79,6 +80,7 @@ Authmethod *authmethods[] = {
79 &method_none, 80 &method_none,
80 &method_pubkey, 81 &method_pubkey,
81#ifdef GSSAPI 82#ifdef GSSAPI
83 &method_gsskeyex,
82 &method_gssapi, 84 &method_gssapi,
83#endif 85#endif
84#ifdef JPAKE 86#ifdef JPAKE
diff --git a/clientloop.c b/clientloop.c
index f30c8b6b5..cc23e355e 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
@@ -1608,6 +1612,15 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1608 /* Do channel operations unless rekeying in progress. */ 1612 /* Do channel operations unless rekeying in progress. */
1609 if (!rekeying) { 1613 if (!rekeying) {
1610 channel_after_select(readset, writeset); 1614 channel_after_select(readset, writeset);
1615
1616#ifdef GSSAPI
1617 if (options.gss_renewal_rekey &&
1618 ssh_gssapi_credentials_updated(NULL)) {
1619 debug("credentials updated - forcing rekey");
1620 need_rekeying = 1;
1621 }
1622#endif
1623
1611 if (need_rekeying || packet_need_rekeying()) { 1624 if (need_rekeying || packet_need_rekeying()) {
1612 debug("need rekeying"); 1625 debug("need rekeying");
1613 xxx_kex->done = 0; 1626 xxx_kex->done = 0;
diff --git a/config.h.in b/config.h.in
index 075c619f6..906e5497f 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1616,6 +1616,9 @@
1616/* Use btmp to log bad logins */ 1616/* Use btmp to log bad logins */
1617#undef USE_BTMP 1617#undef USE_BTMP
1618 1618
1619/* platform uses an in-memory credentials cache */
1620#undef USE_CCAPI
1621
1619/* Use libedit for sftp */ 1622/* Use libedit for sftp */
1620#undef USE_LIBEDIT 1623#undef USE_LIBEDIT
1621 1624
@@ -1631,6 +1634,9 @@
1631/* Use PIPES instead of a socketpair() */ 1634/* Use PIPES instead of a socketpair() */
1632#undef USE_PIPES 1635#undef USE_PIPES
1633 1636
1637/* platform has the Security Authorization Session API */
1638#undef USE_SECURITY_SESSION_API
1639
1634/* Define if you have Solaris process contracts */ 1640/* Define if you have Solaris process contracts */
1635#undef USE_SOLARIS_PROCESS_CONTRACTS 1641#undef USE_SOLARIS_PROCESS_CONTRACTS
1636 1642
diff --git a/configure b/configure
index 2d714acae..5a9db2d05 100755
--- a/configure
+++ b/configure
@@ -7170,6 +7170,63 @@ $as_echo "#define SSH_TUN_COMPAT_AF 1" >>confdefs.h
7170 7170
7171$as_echo "#define SSH_TUN_PREPEND_AF 1" >>confdefs.h 7171$as_echo "#define SSH_TUN_PREPEND_AF 1" >>confdefs.h
7172 7172
7173 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we have the Security Authorization Session API" >&5
7174$as_echo_n "checking if we have the Security Authorization Session API... " >&6; }
7175 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
7176/* end confdefs.h. */
7177#include <Security/AuthSession.h>
7178int
7179main ()
7180{
7181SessionCreate(0, 0);
7182 ;
7183 return 0;
7184}
7185_ACEOF
7186if ac_fn_c_try_compile "$LINENO"; then :
7187 ac_cv_use_security_session_api="yes"
7188
7189$as_echo "#define USE_SECURITY_SESSION_API 1" >>confdefs.h
7190
7191 LIBS="$LIBS -framework Security"
7192 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7193$as_echo "yes" >&6; }
7194else
7195 ac_cv_use_security_session_api="no"
7196 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7197$as_echo "no" >&6; }
7198fi
7199rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
7200 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we have an in-memory credentials cache" >&5
7201$as_echo_n "checking if we have an in-memory credentials cache... " >&6; }
7202 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
7203/* end confdefs.h. */
7204#include <Kerberos/Kerberos.h>
7205int
7206main ()
7207{
7208cc_context_t c;
7209 (void) cc_initialize (&c, 0, NULL, NULL);
7210 ;
7211 return 0;
7212}
7213_ACEOF
7214if ac_fn_c_try_compile "$LINENO"; then :
7215
7216$as_echo "#define USE_CCAPI 1" >>confdefs.h
7217
7218 LIBS="$LIBS -framework Security"
7219 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7220$as_echo "yes" >&6; }
7221 if test "x$ac_cv_use_security_session_api" = "xno"; then
7222 as_fn_error $? "*** Need a security framework to use the credentials cache API ***" "$LINENO" 5
7223 fi
7224else
7225 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7226$as_echo "no" >&6; }
7227
7228fi
7229rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
7173 7230
7174 ac_fn_c_check_decl "$LINENO" "AU_IPv4" "ac_cv_have_decl_AU_IPv4" "$ac_includes_default" 7231 ac_fn_c_check_decl "$LINENO" "AU_IPv4" "ac_cv_have_decl_AU_IPv4" "$ac_includes_default"
7175if test "x$ac_cv_have_decl_AU_IPv4" = xyes; then : 7232if test "x$ac_cv_have_decl_AU_IPv4" = xyes; then :
diff --git a/configure.ac b/configure.ac
index dfd32cd85..90eebf5fd 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 759fa104f..e678a2757 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
@@ -120,8 +120,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
120 krb5_error_code problem; 120 krb5_error_code problem;
121 krb5_principal princ; 121 krb5_principal princ;
122 OM_uint32 maj_status, min_status; 122 OM_uint32 maj_status, min_status;
123 int len;
124 const char *errmsg; 123 const char *errmsg;
124 const char *new_ccname;
125 125
126 if (client->creds == NULL) { 126 if (client->creds == NULL) {
127 debug("No credentials stored"); 127 debug("No credentials stored");
@@ -180,11 +180,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
180 return; 180 return;
181 } 181 }
182 182
183 client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); 183 new_ccname = krb5_cc_get_name(krb_context, ccache);
184
184 client->store.envvar = "KRB5CCNAME"; 185 client->store.envvar = "KRB5CCNAME";
185 len = strlen(client->store.filename) + 6; 186#ifdef USE_CCAPI
186 client->store.envval = xmalloc(len); 187 xasprintf(&client->store.envval, "API:%s", new_ccname);
187 snprintf(client->store.envval, len, "FILE:%s", client->store.filename); 188 client->store.filename = NULL;
189#else
190 xasprintf(&client->store.envval, "FILE:%s", new_ccname);
191 client->store.filename = xstrdup(new_ccname);
192#endif
188 193
189#ifdef USE_PAM 194#ifdef USE_PAM
190 if (options.use_pam) 195 if (options.use_pam)
@@ -196,6 +201,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
196 return; 201 return;
197} 202}
198 203
204int
205ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
206 ssh_gssapi_client *client)
207{
208 krb5_ccache ccache = NULL;
209 krb5_principal principal = NULL;
210 char *name = NULL;
211 krb5_error_code problem;
212 OM_uint32 maj_status, min_status;
213
214 if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
215 logit("krb5_cc_resolve(): %.100s",
216 krb5_get_err_text(krb_context, problem));
217 return 0;
218 }
219
220 /* Find out who the principal in this cache is */
221 if ((problem = krb5_cc_get_principal(krb_context, ccache,
222 &principal))) {
223 logit("krb5_cc_get_principal(): %.100s",
224 krb5_get_err_text(krb_context, problem));
225 krb5_cc_close(krb_context, ccache);
226 return 0;
227 }
228
229 if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
230 logit("krb5_unparse_name(): %.100s",
231 krb5_get_err_text(krb_context, problem));
232 krb5_free_principal(krb_context, principal);
233 krb5_cc_close(krb_context, ccache);
234 return 0;
235 }
236
237
238 if (strcmp(name,client->exportedname.value)!=0) {
239 debug("Name in local credentials cache differs. Not storing");
240 krb5_free_principal(krb_context, principal);
241 krb5_cc_close(krb_context, ccache);
242 krb5_free_unparsed_name(krb_context, name);
243 return 0;
244 }
245 krb5_free_unparsed_name(krb_context, name);
246
247 /* Name matches, so lets get on with it! */
248
249 if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
250 logit("krb5_cc_initialize(): %.100s",
251 krb5_get_err_text(krb_context, problem));
252 krb5_free_principal(krb_context, principal);
253 krb5_cc_close(krb_context, ccache);
254 return 0;
255 }
256
257 krb5_free_principal(krb_context, principal);
258
259 if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
260 ccache))) {
261 logit("gss_krb5_copy_ccache() failed. Sorry!");
262 krb5_cc_close(krb_context, ccache);
263 return 0;
264 }
265
266 return 1;
267}
268
199ssh_gssapi_mech gssapi_kerberos_mech = { 269ssh_gssapi_mech gssapi_kerberos_mech = {
200 "toWM5Slw5Ew8Mqkay+al2g==", 270 "toWM5Slw5Ew8Mqkay+al2g==",
201 "Kerberos", 271 "Kerberos",
@@ -203,7 +273,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
203 NULL, 273 NULL,
204 &ssh_gssapi_krb5_userok, 274 &ssh_gssapi_krb5_userok,
205 NULL, 275 NULL,
206 &ssh_gssapi_krb5_storecreds 276 &ssh_gssapi_krb5_storecreds,
277 &ssh_gssapi_krb5_updatecreds
207}; 278};
208 279
209#endif /* KRB5 */ 280#endif /* KRB5 */
diff --git a/gss-serv.c b/gss-serv.c
index 95348e251..feb1ed763 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.24 2013/07/20 01:55:13 djm Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.24 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-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;
@@ -81,25 +87,32 @@ ssh_gssapi_acquire_cred(Gssctxt *ctx)
81 char lname[MAXHOSTNAMELEN]; 87 char lname[MAXHOSTNAMELEN];
82 gss_OID_set oidset; 88 gss_OID_set oidset;
83 89
84 gss_create_empty_oid_set(&status, &oidset); 90 if (options.gss_strict_acceptor) {
85 gss_add_oid_set_member(&status, ctx->oid, &oidset); 91 gss_create_empty_oid_set(&status, &oidset);
92 gss_add_oid_set_member(&status, ctx->oid, &oidset);
86 93
87 if (gethostname(lname, MAXHOSTNAMELEN)) { 94 if (gethostname(lname, MAXHOSTNAMELEN)) {
88 gss_release_oid_set(&status, &oidset); 95 gss_release_oid_set(&status, &oidset);
89 return (-1); 96 return (-1);
90 } 97 }
98
99 if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
100 gss_release_oid_set(&status, &oidset);
101 return (ctx->major);
102 }
103
104 if ((ctx->major = gss_acquire_cred(&ctx->minor,
105 ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds,
106 NULL, NULL)))
107 ssh_gssapi_error(ctx);
91 108
92 if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
93 gss_release_oid_set(&status, &oidset); 109 gss_release_oid_set(&status, &oidset);
94 return (ctx->major); 110 return (ctx->major);
111 } else {
112 ctx->name = GSS_C_NO_NAME;
113 ctx->creds = GSS_C_NO_CREDENTIAL;
95 } 114 }
96 115 return GSS_S_COMPLETE;
97 if ((ctx->major = gss_acquire_cred(&ctx->minor,
98 ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL)))
99 ssh_gssapi_error(ctx);
100
101 gss_release_oid_set(&status, &oidset);
102 return (ctx->major);
103} 116}
104 117
105/* Privileged */ 118/* Privileged */
@@ -114,6 +127,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
114} 127}
115 128
116/* Unprivileged */ 129/* Unprivileged */
130char *
131ssh_gssapi_server_mechanisms(void) {
132 gss_OID_set supported;
133
134 ssh_gssapi_supported_oids(&supported);
135 return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
136 NULL, NULL));
137}
138
139/* Unprivileged */
140int
141ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
142 const char *dummy) {
143 Gssctxt *ctx = NULL;
144 int res;
145
146 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
147 ssh_gssapi_delete_ctx(&ctx);
148
149 return (res);
150}
151
152/* Unprivileged */
117void 153void
118ssh_gssapi_supported_oids(gss_OID_set *oidset) 154ssh_gssapi_supported_oids(gss_OID_set *oidset)
119{ 155{
@@ -123,7 +159,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
123 gss_OID_set supported; 159 gss_OID_set supported;
124 160
125 gss_create_empty_oid_set(&min_status, oidset); 161 gss_create_empty_oid_set(&min_status, oidset);
126 gss_indicate_mechs(&min_status, &supported); 162
163 if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
164 return;
127 165
128 while (supported_mechs[i]->name != NULL) { 166 while (supported_mechs[i]->name != NULL) {
129 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 167 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -249,8 +287,48 @@ OM_uint32
249ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 287ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
250{ 288{
251 int i = 0; 289 int i = 0;
290 int equal = 0;
291 gss_name_t new_name = GSS_C_NO_NAME;
292 gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
293
294 if (options.gss_store_rekey && client->used && ctx->client_creds) {
295 if (client->mech->oid.length != ctx->oid->length ||
296 (memcmp(client->mech->oid.elements,
297 ctx->oid->elements, ctx->oid->length) !=0)) {
298 debug("Rekeyed credentials have different mechanism");
299 return GSS_S_COMPLETE;
300 }
301
302 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
303 ctx->client_creds, ctx->oid, &new_name,
304 NULL, NULL, NULL))) {
305 ssh_gssapi_error(ctx);
306 return (ctx->major);
307 }
308
309 ctx->major = gss_compare_name(&ctx->minor, client->name,
310 new_name, &equal);
252 311
253 gss_buffer_desc ename; 312 if (GSS_ERROR(ctx->major)) {
313 ssh_gssapi_error(ctx);
314 return (ctx->major);
315 }
316
317 if (!equal) {
318 debug("Rekeyed credentials have different name");
319 return GSS_S_COMPLETE;
320 }
321
322 debug("Marking rekeyed credentials for export");
323
324 gss_release_name(&ctx->minor, &client->name);
325 gss_release_cred(&ctx->minor, &client->creds);
326 client->name = new_name;
327 client->creds = ctx->client_creds;
328 ctx->client_creds = GSS_C_NO_CREDENTIAL;
329 client->updated = 1;
330 return GSS_S_COMPLETE;
331 }
254 332
255 client->mech = NULL; 333 client->mech = NULL;
256 334
@@ -265,6 +343,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
265 if (client->mech == NULL) 343 if (client->mech == NULL)
266 return GSS_S_FAILURE; 344 return GSS_S_FAILURE;
267 345
346 if (ctx->client_creds &&
347 (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
348 ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
349 ssh_gssapi_error(ctx);
350 return (ctx->major);
351 }
352
268 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 353 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
269 &client->displayname, NULL))) { 354 &client->displayname, NULL))) {
270 ssh_gssapi_error(ctx); 355 ssh_gssapi_error(ctx);
@@ -282,6 +367,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
282 return (ctx->major); 367 return (ctx->major);
283 } 368 }
284 369
370 gss_release_buffer(&ctx->minor, &ename);
371
285 /* We can't copy this structure, so we just move the pointer to it */ 372 /* We can't copy this structure, so we just move the pointer to it */
286 client->creds = ctx->client_creds; 373 client->creds = ctx->client_creds;
287 ctx->client_creds = GSS_C_NO_CREDENTIAL; 374 ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -329,7 +416,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
329 416
330/* Privileged */ 417/* Privileged */
331int 418int
332ssh_gssapi_userok(char *user) 419ssh_gssapi_userok(char *user, struct passwd *pw)
333{ 420{
334 OM_uint32 lmin; 421 OM_uint32 lmin;
335 422
@@ -339,9 +426,11 @@ ssh_gssapi_userok(char *user)
339 return 0; 426 return 0;
340 } 427 }
341 if (gssapi_client.mech && gssapi_client.mech->userok) 428 if (gssapi_client.mech && gssapi_client.mech->userok)
342 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 429 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
430 gssapi_client.used = 1;
431 gssapi_client.store.owner = pw;
343 return 1; 432 return 1;
344 else { 433 } else {
345 /* Destroy delegated credentials if userok fails */ 434 /* Destroy delegated credentials if userok fails */
346 gss_release_buffer(&lmin, &gssapi_client.displayname); 435 gss_release_buffer(&lmin, &gssapi_client.displayname);
347 gss_release_buffer(&lmin, &gssapi_client.exportedname); 436 gss_release_buffer(&lmin, &gssapi_client.exportedname);
@@ -354,14 +443,90 @@ ssh_gssapi_userok(char *user)
354 return (0); 443 return (0);
355} 444}
356 445
357/* Privileged */ 446/* These bits are only used for rekeying. The unpriviledged child is running
358OM_uint32 447 * as the user, the monitor is root.
359ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 448 *
449 * In the child, we want to :
450 * *) Ask the monitor to store our credentials into the store we specify
451 * *) If it succeeds, maybe do a PAM update
452 */
453
454/* Stuff for PAM */
455
456#ifdef USE_PAM
457static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
458 struct pam_response **resp, void *data)
360{ 459{
361 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 460 return (PAM_CONV_ERR);
362 gssbuf, gssmic, NULL); 461}
462#endif
363 463
364 return (ctx->major); 464void
465ssh_gssapi_rekey_creds(void) {
466 int ok;
467 int ret;
468#ifdef USE_PAM
469 pam_handle_t *pamh = NULL;
470 struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
471 char *envstr;
472#endif
473
474 if (gssapi_client.store.filename == NULL &&
475 gssapi_client.store.envval == NULL &&
476 gssapi_client.store.envvar == NULL)
477 return;
478
479 ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
480
481 if (!ok)
482 return;
483
484 debug("Rekeyed credentials stored successfully");
485
486 /* Actually managing to play with the ssh pam stack from here will
487 * be next to impossible. In any case, we may want different options
488 * for rekeying. So, use our own :)
489 */
490#ifdef USE_PAM
491 if (!use_privsep) {
492 debug("Not even going to try and do PAM with privsep disabled");
493 return;
494 }
495
496 ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
497 &pamconv, &pamh);
498 if (ret)
499 return;
500
501 xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
502 gssapi_client.store.envval);
503
504 ret = pam_putenv(pamh, envstr);
505 if (!ret)
506 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
507 pam_end(pamh, PAM_SUCCESS);
508#endif
509}
510
511int
512ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
513 int ok = 0;
514
515 /* Check we've got credentials to store */
516 if (!gssapi_client.updated)
517 return 0;
518
519 gssapi_client.updated = 0;
520
521 temporarily_use_uid(gssapi_client.store.owner);
522 if (gssapi_client.mech && gssapi_client.mech->updatecreds)
523 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
524 else
525 debug("No update function for this mechanism");
526
527 restore_uid();
528
529 return ok;
365} 530}
366 531
367#endif 532#endif
diff --git a/kex.c b/kex.c
index 616484b85..49d0fc8fd 100644
--- a/kex.c
+++ b/kex.c
@@ -51,6 +51,10 @@
51#include "roaming.h" 51#include "roaming.h"
52#include "digest.h" 52#include "digest.h"
53 53
54#ifdef GSSAPI
55#include "ssh-gss.h"
56#endif
57
54#if OPENSSL_VERSION_NUMBER >= 0x00907000L 58#if OPENSSL_VERSION_NUMBER >= 0x00907000L
55# if defined(HAVE_EVP_SHA256) 59# if defined(HAVE_EVP_SHA256)
56# define evp_ssh_sha256 EVP_sha256 60# define evp_ssh_sha256 EVP_sha256
@@ -92,6 +96,14 @@ static const struct kexalg kexalgs[] = {
92#endif 96#endif
93 { NULL, -1, -1, -1}, 97 { NULL, -1, -1, -1},
94}; 98};
99static const struct kexalg kexalg_prefixes[] = {
100#ifdef GSSAPI
101 { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
102 { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
103 { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
104#endif
105 { NULL, -1, -1, -1 },
106};
95 107
96char * 108char *
97kex_alg_list(char sep) 109kex_alg_list(char sep)
@@ -120,6 +132,10 @@ kex_alg_by_name(const char *name)
120 if (strcmp(k->name, name) == 0) 132 if (strcmp(k->name, name) == 0)
121 return k; 133 return k;
122 } 134 }
135 for (k = kexalg_prefixes; k->name != NULL; k++) {
136 if (strncmp(k->name, name, strlen(k->name)) == 0)
137 return k;
138 }
123 return NULL; 139 return NULL;
124} 140}
125 141
diff --git a/kex.h b/kex.h
index 1aa3ec26a..8fbcb2b79 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
@@ -136,6 +139,12 @@ struct Kex {
136 int flags; 139 int flags;
137 int hash_alg; 140 int hash_alg;
138 int ec_nid; 141 int ec_nid;
142#ifdef GSSAPI
143 int gss_deleg_creds;
144 int gss_trust_dns;
145 char *gss_host;
146 char *gss_client;
147#endif
139 char *client_version_string; 148 char *client_version_string;
140 char *server_version_string; 149 char *server_version_string;
141 int (*verify_host_key)(Key *); 150 int (*verify_host_key)(Key *);
@@ -168,6 +177,11 @@ void kexecdh_server(Kex *);
168void kexc25519_client(Kex *); 177void kexc25519_client(Kex *);
169void kexc25519_server(Kex *); 178void kexc25519_server(Kex *);
170 179
180#ifdef GSSAPI
181void kexgss_client(Kex *);
182void kexgss_server(Kex *);
183#endif
184
171void 185void
172kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, 186kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int,
173 BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); 187 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..809525924
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,289 @@
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 "servconf.h"
46
47extern ServerOptions options;
48
49void
50kexgss_server(Kex *kex)
51{
52 OM_uint32 maj_status, min_status;
53
54 /*
55 * Some GSSAPI implementations use the input value of ret_flags (an
56 * output variable) as a means of triggering mechanism specific
57 * features. Initializing it to zero avoids inadvertently
58 * activating this non-standard behaviour.
59 */
60
61 OM_uint32 ret_flags = 0;
62 gss_buffer_desc gssbuf, recv_tok, msg_tok;
63 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
64 Gssctxt *ctxt = NULL;
65 u_int slen, klen, kout, hashlen;
66 u_char *kbuf, *hash;
67 DH *dh;
68 int min = -1, max = -1, nbits = -1;
69 BIGNUM *shared_secret = NULL;
70 BIGNUM *dh_client_pub = NULL;
71 int type = 0;
72 gss_OID oid;
73 char *mechs;
74
75 /* Initialise GSSAPI */
76
77 /* If we're rekeying, privsep means that some of the private structures
78 * in the GSSAPI code are no longer available. This kludges them back
79 * into life
80 */
81 if (!ssh_gssapi_oid_table_ok()) {
82 mechs = ssh_gssapi_server_mechanisms();
83 free(mechs);
84 }
85
86 debug2("%s: Identifying %s", __func__, kex->name);
87 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
88 if (oid == GSS_C_NO_OID)
89 fatal("Unknown gssapi mechanism");
90
91 debug2("%s: Acquiring credentials", __func__);
92
93 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
94 fatal("Unable to acquire credentials for the server");
95
96 switch (kex->kex_type) {
97 case KEX_GSS_GRP1_SHA1:
98 dh = dh_new_group1();
99 break;
100 case KEX_GSS_GRP14_SHA1:
101 dh = dh_new_group14();
102 break;
103 case KEX_GSS_GEX_SHA1:
104 debug("Doing group exchange");
105 packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
106 min = packet_get_int();
107 nbits = packet_get_int();
108 max = packet_get_int();
109 min = MAX(DH_GRP_MIN, min);
110 max = MIN(DH_GRP_MAX, max);
111 packet_check_eom();
112 if (max < min || nbits < min || max < nbits)
113 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
114 min, nbits, max);
115 dh = PRIVSEP(choose_dh(min, nbits, max));
116 if (dh == NULL)
117 packet_disconnect("Protocol error: no matching group found");
118
119 packet_start(SSH2_MSG_KEXGSS_GROUP);
120 packet_put_bignum2(dh->p);
121 packet_put_bignum2(dh->g);
122 packet_send();
123
124 packet_write_wait();
125 break;
126 default:
127 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
128 }
129
130 dh_gen_key(dh, kex->we_need * 8);
131
132 do {
133 debug("Wait SSH2_MSG_GSSAPI_INIT");
134 type = packet_read();
135 switch(type) {
136 case SSH2_MSG_KEXGSS_INIT:
137 if (dh_client_pub != NULL)
138 fatal("Received KEXGSS_INIT after initialising");
139 recv_tok.value = packet_get_string(&slen);
140 recv_tok.length = slen;
141
142 if ((dh_client_pub = BN_new()) == NULL)
143 fatal("dh_client_pub == NULL");
144
145 packet_get_bignum2(dh_client_pub);
146
147 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
148 break;
149 case SSH2_MSG_KEXGSS_CONTINUE:
150 recv_tok.value = packet_get_string(&slen);
151 recv_tok.length = slen;
152 break;
153 default:
154 packet_disconnect(
155 "Protocol error: didn't expect packet type %d",
156 type);
157 }
158
159 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
160 &send_tok, &ret_flags));
161
162 free(recv_tok.value);
163
164 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
165 fatal("Zero length token output when incomplete");
166
167 if (dh_client_pub == NULL)
168 fatal("No client public key");
169
170 if (maj_status & GSS_S_CONTINUE_NEEDED) {
171 debug("Sending GSSAPI_CONTINUE");
172 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
173 packet_put_string(send_tok.value, send_tok.length);
174 packet_send();
175 gss_release_buffer(&min_status, &send_tok);
176 }
177 } while (maj_status & GSS_S_CONTINUE_NEEDED);
178
179 if (GSS_ERROR(maj_status)) {
180 if (send_tok.length > 0) {
181 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
182 packet_put_string(send_tok.value, send_tok.length);
183 packet_send();
184 }
185 fatal("accept_ctx died");
186 }
187
188 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
189 fatal("Mutual Authentication flag wasn't set");
190
191 if (!(ret_flags & GSS_C_INTEG_FLAG))
192 fatal("Integrity flag wasn't set");
193
194 if (!dh_pub_is_valid(dh, dh_client_pub))
195 packet_disconnect("bad client public DH value");
196
197 klen = DH_size(dh);
198 kbuf = xmalloc(klen);
199 kout = DH_compute_key(kbuf, dh_client_pub, dh);
200 if (kout < 0)
201 fatal("DH_compute_key: failed");
202
203 shared_secret = BN_new();
204 if (shared_secret == NULL)
205 fatal("kexgss_server: BN_new failed");
206
207 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
208 fatal("kexgss_server: BN_bin2bn failed");
209
210 memset(kbuf, 0, klen);
211 free(kbuf);
212
213 switch (kex->kex_type) {
214 case KEX_GSS_GRP1_SHA1:
215 case KEX_GSS_GRP14_SHA1:
216 kex_dh_hash(
217 kex->client_version_string, kex->server_version_string,
218 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
219 buffer_ptr(&kex->my), buffer_len(&kex->my),
220 NULL, 0, /* Change this if we start sending host keys */
221 dh_client_pub, dh->pub_key, shared_secret,
222 &hash, &hashlen
223 );
224 break;
225 case KEX_GSS_GEX_SHA1:
226 kexgex_hash(
227 kex->hash_alg,
228 kex->client_version_string, kex->server_version_string,
229 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
230 buffer_ptr(&kex->my), buffer_len(&kex->my),
231 NULL, 0,
232 min, nbits, max,
233 dh->p, dh->g,
234 dh_client_pub,
235 dh->pub_key,
236 shared_secret,
237 &hash, &hashlen
238 );
239 break;
240 default:
241 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
242 }
243
244 BN_clear_free(dh_client_pub);
245
246 if (kex->session_id == NULL) {
247 kex->session_id_len = hashlen;
248 kex->session_id = xmalloc(kex->session_id_len);
249 memcpy(kex->session_id, hash, kex->session_id_len);
250 }
251
252 gssbuf.value = hash;
253 gssbuf.length = hashlen;
254
255 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
256 fatal("Couldn't get MIC");
257
258 packet_start(SSH2_MSG_KEXGSS_COMPLETE);
259 packet_put_bignum2(dh->pub_key);
260 packet_put_string(msg_tok.value,msg_tok.length);
261
262 if (send_tok.length != 0) {
263 packet_put_char(1); /* true */
264 packet_put_string(send_tok.value, send_tok.length);
265 } else {
266 packet_put_char(0); /* false */
267 }
268 packet_send();
269
270 gss_release_buffer(&min_status, &send_tok);
271 gss_release_buffer(&min_status, &msg_tok);
272
273 if (gss_kex_context == NULL)
274 gss_kex_context = ctxt;
275 else
276 ssh_gssapi_delete_ctx(&ctxt);
277
278 DH_free(dh);
279
280 kex_derive_keys_bn(kex, hash, hashlen, shared_secret);
281 BN_clear_free(shared_secret);
282 kex_finish(kex);
283
284 /* If this was a rekey, then save out any delegated credentials we
285 * just exchanged. */
286 if (options.gss_store_rekey)
287 ssh_gssapi_rekey_creds();
288}
289#endif /* GSSAPI */
diff --git a/key.c b/key.c
index 914233808..7ac844c66 100644
--- a/key.c
+++ b/key.c
@@ -985,6 +985,7 @@ static const struct keytype keytypes[] = {
985 KEY_DSA_CERT_V00, 0, 1 }, 985 KEY_DSA_CERT_V00, 0, 1 },
986 { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", 986 { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT",
987 KEY_ED25519_CERT, 0, 1 }, 987 KEY_ED25519_CERT, 0, 1 },
988 { "null", "null", KEY_NULL, 0, 0 },
988 { NULL, NULL, -1, -1, 0 } 989 { NULL, NULL, -1, -1, 0 }
989}; 990};
990 991
@@ -1063,7 +1064,7 @@ key_alg_list(int certs_only, int plain_only)
1063 const struct keytype *kt; 1064 const struct keytype *kt;
1064 1065
1065 for (kt = keytypes; kt->type != -1; kt++) { 1066 for (kt = keytypes; kt->type != -1; kt++) {
1066 if (kt->name == NULL) 1067 if (kt->name == NULL || kt->type == KEY_NULL)
1067 continue; 1068 continue;
1068 if ((certs_only && !kt->cert) || (plain_only && kt->cert)) 1069 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
1069 continue; 1070 continue;
diff --git a/key.h b/key.h
index d8ad13d08..c8aeba29e 100644
--- a/key.h
+++ b/key.h
@@ -46,6 +46,7 @@ enum types {
46 KEY_ED25519_CERT, 46 KEY_ED25519_CERT,
47 KEY_RSA_CERT_V00, 47 KEY_RSA_CERT_V00,
48 KEY_DSA_CERT_V00, 48 KEY_DSA_CERT_V00,
49 KEY_NULL,
49 KEY_UNSPEC 50 KEY_UNSPEC
50}; 51};
51enum fp_type { 52enum fp_type {
diff --git a/monitor.c b/monitor.c
index 03baf1ea9..a777c4c03 100644
--- a/monitor.c
+++ b/monitor.c
@@ -181,6 +181,8 @@ int mm_answer_gss_setup_ctx(int, Buffer *);
181int mm_answer_gss_accept_ctx(int, Buffer *); 181int mm_answer_gss_accept_ctx(int, Buffer *);
182int mm_answer_gss_userok(int, Buffer *); 182int mm_answer_gss_userok(int, Buffer *);
183int mm_answer_gss_checkmic(int, Buffer *); 183int mm_answer_gss_checkmic(int, Buffer *);
184int mm_answer_gss_sign(int, Buffer *);
185int mm_answer_gss_updatecreds(int, Buffer *);
184#endif 186#endif
185 187
186#ifdef SSH_AUDIT_EVENTS 188#ifdef SSH_AUDIT_EVENTS
@@ -253,6 +255,7 @@ struct mon_table mon_dispatch_proto20[] = {
253 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, 255 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
254 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, 256 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
255 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, 257 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
258 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
256#endif 259#endif
257#ifdef JPAKE 260#ifdef JPAKE
258 {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata}, 261 {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata},
@@ -265,6 +268,12 @@ struct mon_table mon_dispatch_proto20[] = {
265}; 268};
266 269
267struct mon_table mon_dispatch_postauth20[] = { 270struct mon_table mon_dispatch_postauth20[] = {
271#ifdef GSSAPI
272 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
273 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
274 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
275 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
276#endif
268 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 277 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
269 {MONITOR_REQ_SIGN, 0, mm_answer_sign}, 278 {MONITOR_REQ_SIGN, 0, mm_answer_sign},
270 {MONITOR_REQ_PTY, 0, mm_answer_pty}, 279 {MONITOR_REQ_PTY, 0, mm_answer_pty},
@@ -373,6 +382,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
373 /* Permit requests for moduli and signatures */ 382 /* Permit requests for moduli and signatures */
374 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 383 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
375 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 384 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
385#ifdef GSSAPI
386 /* and for the GSSAPI key exchange */
387 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
388#endif
376 } else { 389 } else {
377 mon_dispatch = mon_dispatch_proto15; 390 mon_dispatch = mon_dispatch_proto15;
378 391
@@ -487,6 +500,10 @@ monitor_child_postauth(struct monitor *pmonitor)
487 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 500 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
488 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 501 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
489 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 502 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
503#ifdef GSSAPI
504 /* and for the GSSAPI key exchange */
505 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
506#endif
490 } else { 507 } else {
491 mon_dispatch = mon_dispatch_postauth15; 508 mon_dispatch = mon_dispatch_postauth15;
492 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 509 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
@@ -1856,6 +1873,13 @@ mm_get_kex(Buffer *m)
1856 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; 1873 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
1857 kex->kex[KEX_ECDH_SHA2] = kexecdh_server; 1874 kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
1858 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 1875 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1876#ifdef GSSAPI
1877 if (options.gss_keyex) {
1878 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1879 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1880 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1881 }
1882#endif
1859 kex->server = 1; 1883 kex->server = 1;
1860 kex->hostkey_type = buffer_get_int(m); 1884 kex->hostkey_type = buffer_get_int(m);
1861 kex->kex_type = buffer_get_int(m); 1885 kex->kex_type = buffer_get_int(m);
@@ -2063,6 +2087,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m)
2063 OM_uint32 major; 2087 OM_uint32 major;
2064 u_int len; 2088 u_int len;
2065 2089
2090 if (!options.gss_authentication && !options.gss_keyex)
2091 fatal("In GSSAPI monitor when GSSAPI is disabled");
2092
2066 goid.elements = buffer_get_string(m, &len); 2093 goid.elements = buffer_get_string(m, &len);
2067 goid.length = len; 2094 goid.length = len;
2068 2095
@@ -2090,6 +2117,9 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
2090 OM_uint32 flags = 0; /* GSI needs this */ 2117 OM_uint32 flags = 0; /* GSI needs this */
2091 u_int len; 2118 u_int len;
2092 2119
2120 if (!options.gss_authentication && !options.gss_keyex)
2121 fatal("In GSSAPI monitor when GSSAPI is disabled");
2122
2093 in.value = buffer_get_string(m, &len); 2123 in.value = buffer_get_string(m, &len);
2094 in.length = len; 2124 in.length = len;
2095 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); 2125 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
@@ -2107,6 +2137,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
2107 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 2137 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2108 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 2138 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2109 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 2139 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2140 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2110 } 2141 }
2111 return (0); 2142 return (0);
2112} 2143}
@@ -2118,6 +2149,9 @@ mm_answer_gss_checkmic(int sock, Buffer *m)
2118 OM_uint32 ret; 2149 OM_uint32 ret;
2119 u_int len; 2150 u_int len;
2120 2151
2152 if (!options.gss_authentication && !options.gss_keyex)
2153 fatal("In GSSAPI monitor when GSSAPI is disabled");
2154
2121 gssbuf.value = buffer_get_string(m, &len); 2155 gssbuf.value = buffer_get_string(m, &len);
2122 gssbuf.length = len; 2156 gssbuf.length = len;
2123 mic.value = buffer_get_string(m, &len); 2157 mic.value = buffer_get_string(m, &len);
@@ -2144,7 +2178,11 @@ mm_answer_gss_userok(int sock, Buffer *m)
2144{ 2178{
2145 int authenticated; 2179 int authenticated;
2146 2180
2147 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 2181 if (!options.gss_authentication && !options.gss_keyex)
2182 fatal("In GSSAPI monitor when GSSAPI is disabled");
2183
2184 authenticated = authctxt->valid &&
2185 ssh_gssapi_userok(authctxt->user, authctxt->pw);
2148 2186
2149 buffer_clear(m); 2187 buffer_clear(m);
2150 buffer_put_int(m, authenticated); 2188 buffer_put_int(m, authenticated);
@@ -2157,6 +2195,74 @@ mm_answer_gss_userok(int sock, Buffer *m)
2157 /* Monitor loop will terminate if authenticated */ 2195 /* Monitor loop will terminate if authenticated */
2158 return (authenticated); 2196 return (authenticated);
2159} 2197}
2198
2199int
2200mm_answer_gss_sign(int socket, Buffer *m)
2201{
2202 gss_buffer_desc data;
2203 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2204 OM_uint32 major, minor;
2205 u_int len;
2206
2207 if (!options.gss_authentication && !options.gss_keyex)
2208 fatal("In GSSAPI monitor when GSSAPI is disabled");
2209
2210 data.value = buffer_get_string(m, &len);
2211 data.length = len;
2212 if (data.length != 20)
2213 fatal("%s: data length incorrect: %d", __func__,
2214 (int) data.length);
2215
2216 /* Save the session ID on the first time around */
2217 if (session_id2_len == 0) {
2218 session_id2_len = data.length;
2219 session_id2 = xmalloc(session_id2_len);
2220 memcpy(session_id2, data.value, session_id2_len);
2221 }
2222 major = ssh_gssapi_sign(gsscontext, &data, &hash);
2223
2224 free(data.value);
2225
2226 buffer_clear(m);
2227 buffer_put_int(m, major);
2228 buffer_put_string(m, hash.value, hash.length);
2229
2230 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2231
2232 gss_release_buffer(&minor, &hash);
2233
2234 /* Turn on getpwnam permissions */
2235 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2236
2237 /* And credential updating, for when rekeying */
2238 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2239
2240 return (0);
2241}
2242
2243int
2244mm_answer_gss_updatecreds(int socket, Buffer *m) {
2245 ssh_gssapi_ccache store;
2246 int ok;
2247
2248 store.filename = buffer_get_string(m, NULL);
2249 store.envvar = buffer_get_string(m, NULL);
2250 store.envval = buffer_get_string(m, NULL);
2251
2252 ok = ssh_gssapi_update_creds(&store);
2253
2254 free(store.filename);
2255 free(store.envvar);
2256 free(store.envval);
2257
2258 buffer_clear(m);
2259 buffer_put_int(m, ok);
2260
2261 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2262
2263 return(0);
2264}
2265
2160#endif /* GSSAPI */ 2266#endif /* GSSAPI */
2161 2267
2162#ifdef JPAKE 2268#ifdef JPAKE
diff --git a/monitor.h b/monitor.h
index 2caa46933..315ef996d 100644
--- a/monitor.h
+++ b/monitor.h
@@ -70,6 +70,9 @@ enum monitor_reqtype {
70 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, 70 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
71 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, 71 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
72 72
73 MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
74 MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
75
73}; 76};
74 77
75struct mm_master; 78struct mm_master;
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 4ce469605..44019f32a 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1273,7 +1273,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1273} 1273}
1274 1274
1275int 1275int
1276mm_ssh_gssapi_userok(char *user) 1276mm_ssh_gssapi_userok(char *user, struct passwd *pw)
1277{ 1277{
1278 Buffer m; 1278 Buffer m;
1279 int authenticated = 0; 1279 int authenticated = 0;
@@ -1290,6 +1290,51 @@ mm_ssh_gssapi_userok(char *user)
1290 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1290 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1291 return (authenticated); 1291 return (authenticated);
1292} 1292}
1293
1294OM_uint32
1295mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1296{
1297 Buffer m;
1298 OM_uint32 major;
1299 u_int len;
1300
1301 buffer_init(&m);
1302 buffer_put_string(&m, data->value, data->length);
1303
1304 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
1305 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
1306
1307 major = buffer_get_int(&m);
1308 hash->value = buffer_get_string(&m, &len);
1309 hash->length = len;
1310
1311 buffer_free(&m);
1312
1313 return(major);
1314}
1315
1316int
1317mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1318{
1319 Buffer m;
1320 int ok;
1321
1322 buffer_init(&m);
1323
1324 buffer_put_cstring(&m, store->filename ? store->filename : "");
1325 buffer_put_cstring(&m, store->envvar ? store->envvar : "");
1326 buffer_put_cstring(&m, store->envval ? store->envval : "");
1327
1328 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m);
1329 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m);
1330
1331 ok = buffer_get_int(&m);
1332
1333 buffer_free(&m);
1334
1335 return (ok);
1336}
1337
1293#endif /* GSSAPI */ 1338#endif /* GSSAPI */
1294 1339
1295#ifdef JPAKE 1340#ifdef JPAKE
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 0c7f2e384..ec9b9b1c3 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 9c7e73d7d..cb8bcb2cf 100644
--- a/readconf.c
+++ b/readconf.c
@@ -140,6 +140,8 @@ typedef enum {
140 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 140 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
141 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 141 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
142 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 142 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
143 oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
144 oGssServerIdentity,
143 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 145 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
144 oSendEnv, oControlPath, oControlMaster, oControlPersist, 146 oSendEnv, oControlPath, oControlMaster, oControlPersist,
145 oHashKnownHosts, 147 oHashKnownHosts,
@@ -182,10 +184,19 @@ static struct {
182 { "afstokenpassing", oUnsupported }, 184 { "afstokenpassing", oUnsupported },
183#if defined(GSSAPI) 185#if defined(GSSAPI)
184 { "gssapiauthentication", oGssAuthentication }, 186 { "gssapiauthentication", oGssAuthentication },
187 { "gssapikeyexchange", oGssKeyEx },
185 { "gssapidelegatecredentials", oGssDelegateCreds }, 188 { "gssapidelegatecredentials", oGssDelegateCreds },
189 { "gssapitrustdns", oGssTrustDns },
190 { "gssapiclientidentity", oGssClientIdentity },
191 { "gssapiserveridentity", oGssServerIdentity },
192 { "gssapirenewalforcesrekey", oGssRenewalRekey },
186#else 193#else
187 { "gssapiauthentication", oUnsupported }, 194 { "gssapiauthentication", oUnsupported },
195 { "gssapikeyexchange", oUnsupported },
188 { "gssapidelegatecredentials", oUnsupported }, 196 { "gssapidelegatecredentials", oUnsupported },
197 { "gssapitrustdns", oUnsupported },
198 { "gssapiclientidentity", oUnsupported },
199 { "gssapirenewalforcesrekey", oUnsupported },
189#endif 200#endif
190 { "fallbacktorsh", oDeprecated }, 201 { "fallbacktorsh", oDeprecated },
191 { "usersh", oDeprecated }, 202 { "usersh", oDeprecated },
@@ -839,10 +850,30 @@ parse_time:
839 intptr = &options->gss_authentication; 850 intptr = &options->gss_authentication;
840 goto parse_flag; 851 goto parse_flag;
841 852
853 case oGssKeyEx:
854 intptr = &options->gss_keyex;
855 goto parse_flag;
856
842 case oGssDelegateCreds: 857 case oGssDelegateCreds:
843 intptr = &options->gss_deleg_creds; 858 intptr = &options->gss_deleg_creds;
844 goto parse_flag; 859 goto parse_flag;
845 860
861 case oGssTrustDns:
862 intptr = &options->gss_trust_dns;
863 goto parse_flag;
864
865 case oGssClientIdentity:
866 charptr = &options->gss_client_identity;
867 goto parse_string;
868
869 case oGssServerIdentity:
870 charptr = &options->gss_server_identity;
871 goto parse_string;
872
873 case oGssRenewalRekey:
874 intptr = &options->gss_renewal_rekey;
875 goto parse_flag;
876
846 case oBatchMode: 877 case oBatchMode:
847 intptr = &options->batch_mode; 878 intptr = &options->batch_mode;
848 goto parse_flag; 879 goto parse_flag;
@@ -1488,7 +1519,12 @@ initialize_options(Options * options)
1488 options->pubkey_authentication = -1; 1519 options->pubkey_authentication = -1;
1489 options->challenge_response_authentication = -1; 1520 options->challenge_response_authentication = -1;
1490 options->gss_authentication = -1; 1521 options->gss_authentication = -1;
1522 options->gss_keyex = -1;
1491 options->gss_deleg_creds = -1; 1523 options->gss_deleg_creds = -1;
1524 options->gss_trust_dns = -1;
1525 options->gss_renewal_rekey = -1;
1526 options->gss_client_identity = NULL;
1527 options->gss_server_identity = NULL;
1492 options->password_authentication = -1; 1528 options->password_authentication = -1;
1493 options->kbd_interactive_authentication = -1; 1529 options->kbd_interactive_authentication = -1;
1494 options->kbd_interactive_devices = NULL; 1530 options->kbd_interactive_devices = NULL;
@@ -1594,8 +1630,14 @@ fill_default_options(Options * options)
1594 options->challenge_response_authentication = 1; 1630 options->challenge_response_authentication = 1;
1595 if (options->gss_authentication == -1) 1631 if (options->gss_authentication == -1)
1596 options->gss_authentication = 0; 1632 options->gss_authentication = 0;
1633 if (options->gss_keyex == -1)
1634 options->gss_keyex = 0;
1597 if (options->gss_deleg_creds == -1) 1635 if (options->gss_deleg_creds == -1)
1598 options->gss_deleg_creds = 0; 1636 options->gss_deleg_creds = 0;
1637 if (options->gss_trust_dns == -1)
1638 options->gss_trust_dns = 0;
1639 if (options->gss_renewal_rekey == -1)
1640 options->gss_renewal_rekey = 0;
1599 if (options->password_authentication == -1) 1641 if (options->password_authentication == -1)
1600 options->password_authentication = 1; 1642 options->password_authentication = 1;
1601 if (options->kbd_interactive_authentication == -1) 1643 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index 2d7ea9fc4..826c6767b 100644
--- a/readconf.h
+++ b/readconf.h
@@ -54,7 +54,12 @@ typedef struct {
54 int challenge_response_authentication; 54 int challenge_response_authentication;
55 /* Try S/Key or TIS, authentication. */ 55 /* Try S/Key or TIS, authentication. */
56 int gss_authentication; /* Try GSS authentication */ 56 int gss_authentication; /* Try GSS authentication */
57 int gss_keyex; /* Try GSS key exchange */
57 int gss_deleg_creds; /* Delegate GSS credentials */ 58 int gss_deleg_creds; /* Delegate GSS credentials */
59 int gss_trust_dns; /* Trust DNS for GSS canonicalization */
60 int gss_renewal_rekey; /* Credential renewal forces rekey */
61 char *gss_client_identity; /* Principal to initiate GSSAPI with */
62 char *gss_server_identity; /* GSSAPI target principal */
58 int password_authentication; /* Try password 63 int password_authentication; /* Try password
59 * authentication. */ 64 * authentication. */
60 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 65 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
diff --git a/servconf.c b/servconf.c
index 9bcd05bf2..29209e452 100644
--- a/servconf.c
+++ b/servconf.c
@@ -108,7 +108,10 @@ initialize_server_options(ServerOptions *options)
108 options->kerberos_ticket_cleanup = -1; 108 options->kerberos_ticket_cleanup = -1;
109 options->kerberos_get_afs_token = -1; 109 options->kerberos_get_afs_token = -1;
110 options->gss_authentication=-1; 110 options->gss_authentication=-1;
111 options->gss_keyex = -1;
111 options->gss_cleanup_creds = -1; 112 options->gss_cleanup_creds = -1;
113 options->gss_strict_acceptor = -1;
114 options->gss_store_rekey = -1;
112 options->password_authentication = -1; 115 options->password_authentication = -1;
113 options->kbd_interactive_authentication = -1; 116 options->kbd_interactive_authentication = -1;
114 options->challenge_response_authentication = -1; 117 options->challenge_response_authentication = -1;
@@ -245,8 +248,14 @@ fill_default_server_options(ServerOptions *options)
245 options->kerberos_get_afs_token = 0; 248 options->kerberos_get_afs_token = 0;
246 if (options->gss_authentication == -1) 249 if (options->gss_authentication == -1)
247 options->gss_authentication = 0; 250 options->gss_authentication = 0;
251 if (options->gss_keyex == -1)
252 options->gss_keyex = 0;
248 if (options->gss_cleanup_creds == -1) 253 if (options->gss_cleanup_creds == -1)
249 options->gss_cleanup_creds = 1; 254 options->gss_cleanup_creds = 1;
255 if (options->gss_strict_acceptor == -1)
256 options->gss_strict_acceptor = 1;
257 if (options->gss_store_rekey == -1)
258 options->gss_store_rekey = 0;
250 if (options->password_authentication == -1) 259 if (options->password_authentication == -1)
251 options->password_authentication = 1; 260 options->password_authentication = 1;
252 if (options->kbd_interactive_authentication == -1) 261 if (options->kbd_interactive_authentication == -1)
@@ -343,7 +352,9 @@ typedef enum {
343 sBanner, sUseDNS, sHostbasedAuthentication, 352 sBanner, sUseDNS, sHostbasedAuthentication,
344 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, 353 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
345 sClientAliveCountMax, sAuthorizedKeysFile, 354 sClientAliveCountMax, sAuthorizedKeysFile,
346 sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, 355 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
356 sGssKeyEx, sGssStoreRekey,
357 sAcceptEnv, sPermitTunnel,
347 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 358 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
348 sUsePrivilegeSeparation, sAllowAgentForwarding, 359 sUsePrivilegeSeparation, sAllowAgentForwarding,
349 sZeroKnowledgePasswordAuthentication, sHostCertificate, 360 sZeroKnowledgePasswordAuthentication, sHostCertificate,
@@ -410,10 +421,20 @@ static struct {
410#ifdef GSSAPI 421#ifdef GSSAPI
411 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 422 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
412 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 423 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
424 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
425 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
426 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
427 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
413#else 428#else
414 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 429 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
415 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 430 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
431 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
432 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
433 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
434 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
416#endif 435#endif
436 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
437 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
417 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 438 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
418 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 439 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
419 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 440 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1094,10 +1115,22 @@ process_server_config_line(ServerOptions *options, char *line,
1094 intptr = &options->gss_authentication; 1115 intptr = &options->gss_authentication;
1095 goto parse_flag; 1116 goto parse_flag;
1096 1117
1118 case sGssKeyEx:
1119 intptr = &options->gss_keyex;
1120 goto parse_flag;
1121
1097 case sGssCleanupCreds: 1122 case sGssCleanupCreds:
1098 intptr = &options->gss_cleanup_creds; 1123 intptr = &options->gss_cleanup_creds;
1099 goto parse_flag; 1124 goto parse_flag;
1100 1125
1126 case sGssStrictAcceptor:
1127 intptr = &options->gss_strict_acceptor;
1128 goto parse_flag;
1129
1130 case sGssStoreRekey:
1131 intptr = &options->gss_store_rekey;
1132 goto parse_flag;
1133
1101 case sPasswordAuthentication: 1134 case sPasswordAuthentication:
1102 intptr = &options->password_authentication; 1135 intptr = &options->password_authentication;
1103 goto parse_flag; 1136 goto parse_flag;
@@ -2008,7 +2041,10 @@ dump_config(ServerOptions *o)
2008#endif 2041#endif
2009#ifdef GSSAPI 2042#ifdef GSSAPI
2010 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2043 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2044 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2011 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2045 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2046 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2047 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2012#endif 2048#endif
2013#ifdef JPAKE 2049#ifdef JPAKE
2014 dump_cfg_fmtint(sZeroKnowledgePasswordAuthentication, 2050 dump_cfg_fmtint(sZeroKnowledgePasswordAuthentication,
diff --git a/servconf.h b/servconf.h
index 8812c5aab..eba76ee1d 100644
--- a/servconf.h
+++ b/servconf.h
@@ -112,7 +112,10 @@ typedef struct {
112 int kerberos_get_afs_token; /* If true, try to get AFS token if 112 int kerberos_get_afs_token; /* If true, try to get AFS token if
113 * authenticated with Kerberos. */ 113 * authenticated with Kerberos. */
114 int gss_authentication; /* If true, permit GSSAPI authentication */ 114 int gss_authentication; /* If true, permit GSSAPI authentication */
115 int gss_keyex; /* If true, permit GSSAPI key exchange */
115 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 116 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
117 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
118 int gss_store_rekey;
116 int password_authentication; /* If true, permit password 119 int password_authentication; /* If true, permit password
117 * authentication. */ 120 * authentication. */
118 int kbd_interactive_authentication; /* If true, permit */ 121 int kbd_interactive_authentication; /* If true, permit */
diff --git a/ssh-gss.h b/ssh-gss.h
index 077e13ce4..885e48185 100644
--- a/ssh-gss.h
+++ b/ssh-gss.h
@@ -1,6 +1,6 @@
1/* $OpenBSD: ssh-gss.h,v 1.10 2007/06/12 08:20:00 djm Exp $ */ 1/* $OpenBSD: ssh-gss.h,v 1.10 2007/06/12 08:20:00 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);
@@ -117,16 +134,32 @@ void ssh_gssapi_build_ctx(Gssctxt **);
117void ssh_gssapi_delete_ctx(Gssctxt **); 134void ssh_gssapi_delete_ctx(Gssctxt **);
118OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); 135OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
119void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); 136void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *);
120int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); 137int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
138OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
139int ssh_gssapi_credentials_updated(Gssctxt *);
121 140
122/* In the server */ 141/* In the server */
142typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
143 const char *);
144char *ssh_gssapi_client_mechanisms(const char *, const char *);
145char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
146 const char *);
147gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
148int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
149 const char *);
123OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 150OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
124int ssh_gssapi_userok(char *name); 151int ssh_gssapi_userok(char *name, struct passwd *);
125OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 152OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
126void ssh_gssapi_do_child(char ***, u_int *); 153void ssh_gssapi_do_child(char ***, u_int *);
127void ssh_gssapi_cleanup_creds(void); 154void ssh_gssapi_cleanup_creds(void);
128void ssh_gssapi_storecreds(void); 155void ssh_gssapi_storecreds(void);
129 156
157char *ssh_gssapi_server_mechanisms(void);
158int ssh_gssapi_oid_table_ok(void);
159
160int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
161void ssh_gssapi_rekey_creds(void);
162
130#endif /* GSSAPI */ 163#endif /* GSSAPI */
131 164
132#endif /* _SSH_GSS_H */ 165#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 3cadcd767..49505ae9c 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -676,11 +676,43 @@ Specifies whether user authentication based on GSSAPI is allowed.
676The default is 676The default is
677.Dq no . 677.Dq no .
678Note that this option applies to protocol version 2 only. 678Note that this option applies to protocol version 2 only.
679.It Cm GSSAPIKeyExchange
680Specifies whether key exchange based on GSSAPI may be used. When using
681GSSAPI key exchange the server need not have a host key.
682The default is
683.Dq no .
684Note that this option applies to protocol version 2 only.
685.It Cm GSSAPIClientIdentity
686If set, specifies the GSSAPI client identity that ssh should use when
687connecting to the server. The default is unset, which means that the default
688identity will be used.
689.It Cm GSSAPIServerIdentity
690If set, specifies the GSSAPI server identity that ssh should expect when
691connecting to the server. The default is unset, which means that the
692expected GSSAPI server identity will be determined from the target
693hostname.
679.It Cm GSSAPIDelegateCredentials 694.It Cm GSSAPIDelegateCredentials
680Forward (delegate) credentials to the server. 695Forward (delegate) credentials to the server.
681The default is 696The default is
682.Dq no . 697.Dq no .
683Note that this option applies to protocol version 2 only. 698Note that this option applies to protocol version 2 connections using GSSAPI.
699.It Cm GSSAPIRenewalForcesRekey
700If set to
701.Dq yes
702then renewal of the client's GSSAPI credentials will force the rekeying of the
703ssh connection. With a compatible server, this can delegate the renewed
704credentials to a session on the server.
705The default is
706.Dq no .
707.It Cm GSSAPITrustDns
708Set to
709.Dq yes to indicate that the DNS is trusted to securely canonicalize
710the name of the host being connected to. If
711.Dq no, the hostname entered on the
712command line will be passed untouched to the GSSAPI library.
713The default is
714.Dq no .
715This option only applies to protocol version 2 connections using GSSAPI.
684.It Cm HashKnownHosts 716.It Cm HashKnownHosts
685Indicates that 717Indicates that
686.Xr ssh 1 718.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index 8acffc5c3..21a269d3c 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -160,9 +160,34 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
160{ 160{
161 Kex *kex; 161 Kex *kex;
162 162
163#ifdef GSSAPI
164 char *orig = NULL, *gss = NULL;
165 char *gss_host = NULL;
166#endif
167
163 xxx_host = host; 168 xxx_host = host;
164 xxx_hostaddr = hostaddr; 169 xxx_hostaddr = hostaddr;
165 170
171#ifdef GSSAPI
172 if (options.gss_keyex) {
173 /* Add the GSSAPI mechanisms currently supported on this
174 * client to the key exchange algorithm proposal */
175 orig = myproposal[PROPOSAL_KEX_ALGS];
176
177 if (options.gss_trust_dns)
178 gss_host = (char *)get_canonical_hostname(1);
179 else
180 gss_host = host;
181
182 gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity);
183 if (gss) {
184 debug("Offering GSSAPI proposal: %s", gss);
185 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
186 "%s,%s", gss, orig);
187 }
188 }
189#endif
190
166 if (options.ciphers == (char *)-1) { 191 if (options.ciphers == (char *)-1) {
167 logit("No valid ciphers for protocol version 2 given, using defaults."); 192 logit("No valid ciphers for protocol version 2 given, using defaults.");
168 options.ciphers = NULL; 193 options.ciphers = NULL;
@@ -198,6 +223,17 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
198 if (options.kex_algorithms != NULL) 223 if (options.kex_algorithms != NULL)
199 myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; 224 myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
200 225
226#ifdef GSSAPI
227 /* If we've got GSSAPI algorithms, then we also support the
228 * 'null' hostkey, as a last resort */
229 if (options.gss_keyex && gss) {
230 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
231 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
232 "%s,null", orig);
233 free(gss);
234 }
235#endif
236
201 if (options.rekey_limit || options.rekey_interval) 237 if (options.rekey_limit || options.rekey_interval)
202 packet_set_rekey_limits((u_int32_t)options.rekey_limit, 238 packet_set_rekey_limits((u_int32_t)options.rekey_limit,
203 (time_t)options.rekey_interval); 239 (time_t)options.rekey_interval);
@@ -210,10 +246,30 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
210 kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; 246 kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
211 kex->kex[KEX_ECDH_SHA2] = kexecdh_client; 247 kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
212 kex->kex[KEX_C25519_SHA256] = kexc25519_client; 248 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
249#ifdef GSSAPI
250 if (options.gss_keyex) {
251 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
252 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
253 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
254 }
255#endif
213 kex->client_version_string=client_version_string; 256 kex->client_version_string=client_version_string;
214 kex->server_version_string=server_version_string; 257 kex->server_version_string=server_version_string;
215 kex->verify_host_key=&verify_host_key_callback; 258 kex->verify_host_key=&verify_host_key_callback;
216 259
260#ifdef GSSAPI
261 if (options.gss_keyex) {
262 kex->gss_deleg_creds = options.gss_deleg_creds;
263 kex->gss_trust_dns = options.gss_trust_dns;
264 kex->gss_client = options.gss_client_identity;
265 if (options.gss_server_identity) {
266 kex->gss_host = options.gss_server_identity;
267 } else {
268 kex->gss_host = gss_host;
269 }
270 }
271#endif
272
217 xxx_kex = kex; 273 xxx_kex = kex;
218 274
219 dispatch_run(DISPATCH_BLOCK, &kex->done, kex); 275 dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
@@ -309,6 +365,7 @@ void input_gssapi_token(int type, u_int32_t, void *);
309void input_gssapi_hash(int type, u_int32_t, void *); 365void input_gssapi_hash(int type, u_int32_t, void *);
310void input_gssapi_error(int, u_int32_t, void *); 366void input_gssapi_error(int, u_int32_t, void *);
311void input_gssapi_errtok(int, u_int32_t, void *); 367void input_gssapi_errtok(int, u_int32_t, void *);
368int userauth_gsskeyex(Authctxt *authctxt);
312#endif 369#endif
313 370
314void userauth(Authctxt *, char *); 371void userauth(Authctxt *, char *);
@@ -324,6 +381,11 @@ static char *authmethods_get(void);
324 381
325Authmethod authmethods[] = { 382Authmethod authmethods[] = {
326#ifdef GSSAPI 383#ifdef GSSAPI
384 {"gssapi-keyex",
385 userauth_gsskeyex,
386 NULL,
387 &options.gss_authentication,
388 NULL},
327 {"gssapi-with-mic", 389 {"gssapi-with-mic",
328 userauth_gssapi, 390 userauth_gssapi,
329 NULL, 391 NULL,
@@ -627,19 +689,31 @@ userauth_gssapi(Authctxt *authctxt)
627 static u_int mech = 0; 689 static u_int mech = 0;
628 OM_uint32 min; 690 OM_uint32 min;
629 int ok = 0; 691 int ok = 0;
692 const char *gss_host;
693
694 if (options.gss_server_identity)
695 gss_host = options.gss_server_identity;
696 else if (options.gss_trust_dns)
697 gss_host = get_canonical_hostname(1);
698 else
699 gss_host = authctxt->host;
630 700
631 /* Try one GSSAPI method at a time, rather than sending them all at 701 /* Try one GSSAPI method at a time, rather than sending them all at
632 * once. */ 702 * once. */
633 703
634 if (gss_supported == NULL) 704 if (gss_supported == NULL)
635 gss_indicate_mechs(&min, &gss_supported); 705 if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
706 gss_supported = NULL;
707 return 0;
708 }
636 709
637 /* Check to see if the mechanism is usable before we offer it */ 710 /* Check to see if the mechanism is usable before we offer it */
638 while (mech < gss_supported->count && !ok) { 711 while (mech < gss_supported->count && !ok) {
639 /* My DER encoding requires length<128 */ 712 /* My DER encoding requires length<128 */
640 if (gss_supported->elements[mech].length < 128 && 713 if (gss_supported->elements[mech].length < 128 &&
641 ssh_gssapi_check_mechanism(&gssctxt, 714 ssh_gssapi_check_mechanism(&gssctxt,
642 &gss_supported->elements[mech], authctxt->host)) { 715 &gss_supported->elements[mech], gss_host,
716 options.gss_client_identity)) {
643 ok = 1; /* Mechanism works */ 717 ok = 1; /* Mechanism works */
644 } else { 718 } else {
645 mech++; 719 mech++;
@@ -736,8 +810,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt)
736{ 810{
737 Authctxt *authctxt = ctxt; 811 Authctxt *authctxt = ctxt;
738 Gssctxt *gssctxt; 812 Gssctxt *gssctxt;
739 int oidlen; 813 u_int oidlen;
740 char *oidv; 814 u_char *oidv;
741 815
742 if (authctxt == NULL) 816 if (authctxt == NULL)
743 fatal("input_gssapi_response: no authentication context"); 817 fatal("input_gssapi_response: no authentication context");
@@ -846,6 +920,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt)
846 free(msg); 920 free(msg);
847 free(lang); 921 free(lang);
848} 922}
923
924int
925userauth_gsskeyex(Authctxt *authctxt)
926{
927 Buffer b;
928 gss_buffer_desc gssbuf;
929 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
930 OM_uint32 ms;
931
932 static int attempt = 0;
933 if (attempt++ >= 1)
934 return (0);
935
936 if (gss_kex_context == NULL) {
937 debug("No valid Key exchange context");
938 return (0);
939 }
940
941 ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
942 "gssapi-keyex");
943
944 gssbuf.value = buffer_ptr(&b);
945 gssbuf.length = buffer_len(&b);
946
947 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
948 buffer_free(&b);
949 return (0);
950 }
951
952 packet_start(SSH2_MSG_USERAUTH_REQUEST);
953 packet_put_cstring(authctxt->server_user);
954 packet_put_cstring(authctxt->service);
955 packet_put_cstring(authctxt->method->name);
956 packet_put_string(mic.value, mic.length);
957 packet_send();
958
959 buffer_free(&b);
960 gss_release_buffer(&ms, &mic);
961
962 return (1);
963}
964
849#endif /* GSSAPI */ 965#endif /* GSSAPI */
850 966
851int 967int
diff --git a/sshd.c b/sshd.c
index 25380c911..fe65132e8 100644
--- a/sshd.c
+++ b/sshd.c
@@ -122,6 +122,10 @@
122#include "ssh-sandbox.h" 122#include "ssh-sandbox.h"
123#include "version.h" 123#include "version.h"
124 124
125#ifdef USE_SECURITY_SESSION_API
126#include <Security/AuthSession.h>
127#endif
128
125#ifdef LIBWRAP 129#ifdef LIBWRAP
126#include <tcpd.h> 130#include <tcpd.h>
127#include <syslog.h> 131#include <syslog.h>
@@ -1721,10 +1725,13 @@ main(int ac, char **av)
1721 logit("Disabling protocol version 1. Could not load host key"); 1725 logit("Disabling protocol version 1. Could not load host key");
1722 options.protocol &= ~SSH_PROTO_1; 1726 options.protocol &= ~SSH_PROTO_1;
1723 } 1727 }
1728#ifndef GSSAPI
1729 /* The GSSAPI key exchange can run without a host key */
1724 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { 1730 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
1725 logit("Disabling protocol version 2. Could not load host key"); 1731 logit("Disabling protocol version 2. Could not load host key");
1726 options.protocol &= ~SSH_PROTO_2; 1732 options.protocol &= ~SSH_PROTO_2;
1727 } 1733 }
1734#endif
1728 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { 1735 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
1729 logit("sshd: no hostkeys available -- exiting."); 1736 logit("sshd: no hostkeys available -- exiting.");
1730 exit(1); 1737 exit(1);
@@ -2051,6 +2058,60 @@ main(int ac, char **av)
2051 remote_ip, remote_port, 2058 remote_ip, remote_port,
2052 get_local_ipaddr(sock_in), get_local_port()); 2059 get_local_ipaddr(sock_in), get_local_port());
2053 2060
2061#ifdef USE_SECURITY_SESSION_API
2062 /*
2063 * Create a new security session for use by the new user login if
2064 * the current session is the root session or we are not launched
2065 * by inetd (eg: debugging mode or server mode). We do not
2066 * necessarily need to create a session if we are launched from
2067 * inetd because Panther xinetd will create a session for us.
2068 *
2069 * The only case where this logic will fail is if there is an
2070 * inetd running in a non-root session which is not creating
2071 * new sessions for us. Then all the users will end up in the
2072 * same session (bad).
2073 *
2074 * When the client exits, the session will be destroyed for us
2075 * automatically.
2076 *
2077 * We must create the session before any credentials are stored
2078 * (including AFS pags, which happens a few lines below).
2079 */
2080 {
2081 OSStatus err = 0;
2082 SecuritySessionId sid = 0;
2083 SessionAttributeBits sattrs = 0;
2084
2085 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2086 if (err)
2087 error("SessionGetInfo() failed with error %.8X",
2088 (unsigned) err);
2089 else
2090 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2091 (unsigned) sid, (unsigned) sattrs);
2092
2093 if (inetd_flag && !(sattrs & sessionIsRoot))
2094 debug("Running in inetd mode in a non-root session... "
2095 "assuming inetd created the session for us.");
2096 else {
2097 debug("Creating new security session...");
2098 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2099 if (err)
2100 error("SessionCreate() failed with error %.8X",
2101 (unsigned) err);
2102
2103 err = SessionGetInfo(callerSecuritySession, &sid,
2104 &sattrs);
2105 if (err)
2106 error("SessionGetInfo() failed with error %.8X",
2107 (unsigned) err);
2108 else
2109 debug("New Session ID is %.8X / Session Attributes are %.8X",
2110 (unsigned) sid, (unsigned) sattrs);
2111 }
2112 }
2113#endif
2114
2054 /* 2115 /*
2055 * We don't want to listen forever unless the other side 2116 * We don't want to listen forever unless the other side
2056 * successfully authenticates itself. So we set up an alarm which is 2117 * successfully authenticates itself. So we set up an alarm which is
@@ -2456,6 +2517,48 @@ do_ssh2_kex(void)
2456 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2517 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2457 list_hostkey_types()); 2518 list_hostkey_types());
2458 2519
2520#ifdef GSSAPI
2521 {
2522 char *orig;
2523 char *gss = NULL;
2524 char *newstr = NULL;
2525 orig = myproposal[PROPOSAL_KEX_ALGS];
2526
2527 /*
2528 * If we don't have a host key, then there's no point advertising
2529 * the other key exchange algorithms
2530 */
2531
2532 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2533 orig = NULL;
2534
2535 if (options.gss_keyex)
2536 gss = ssh_gssapi_server_mechanisms();
2537 else
2538 gss = NULL;
2539
2540 if (gss && orig)
2541 xasprintf(&newstr, "%s,%s", gss, orig);
2542 else if (gss)
2543 newstr = gss;
2544 else if (orig)
2545 newstr = orig;
2546
2547 /*
2548 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2549 * key alg, but we can't tell people about it unless its the only
2550 * host key algorithm we support
2551 */
2552 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2553 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2554
2555 if (newstr)
2556 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2557 else
2558 fatal("No supported key exchange algorithms");
2559 }
2560#endif
2561
2459 /* start key exchange */ 2562 /* start key exchange */
2460 kex = kex_setup(myproposal); 2563 kex = kex_setup(myproposal);
2461 kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; 2564 kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
@@ -2464,6 +2567,13 @@ do_ssh2_kex(void)
2464 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; 2567 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
2465 kex->kex[KEX_ECDH_SHA2] = kexecdh_server; 2568 kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
2466 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2569 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2570#ifdef GSSAPI
2571 if (options.gss_keyex) {
2572 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2573 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2574 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2575 }
2576#endif
2467 kex->server = 1; 2577 kex->server = 1;
2468 kex->client_version_string=client_version_string; 2578 kex->client_version_string=client_version_string;
2469 kex->server_version_string=server_version_string; 2579 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 3b21ea6e7..9aa9ebafa 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -493,12 +493,40 @@ Specifies whether user authentication based on GSSAPI is allowed.
493The default is 493The default is
494.Dq no . 494.Dq no .
495Note that this option applies to protocol version 2 only. 495Note that this option applies to protocol version 2 only.
496.It Cm GSSAPIKeyExchange
497Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
498doesn't rely on ssh keys to verify host identity.
499The default is
500.Dq no .
501Note that this option applies to protocol version 2 only.
496.It Cm GSSAPICleanupCredentials 502.It Cm GSSAPICleanupCredentials
497Specifies whether to automatically destroy the user's credentials cache 503Specifies whether to automatically destroy the user's credentials cache
498on logout. 504on logout.
499The default is 505The default is
500.Dq yes . 506.Dq yes .
501Note that this option applies to protocol version 2 only. 507Note that this option applies to protocol version 2 only.
508.It Cm GSSAPIStrictAcceptorCheck
509Determines whether to be strict about the identity of the GSSAPI acceptor
510a client authenticates against. If
511.Dq yes
512then the client must authenticate against the
513.Pa host
514service on the current hostname. If
515.Dq no
516then the client may authenticate against any service key stored in the
517machine's default store. This facility is provided to assist with operation
518on multi homed machines.
519The default is
520.Dq yes .
521Note that this option applies only to protocol version 2 GSSAPI connections,
522and setting it to
523.Dq no
524may only work with recent Kerberos GSSAPI libraries.
525.It Cm GSSAPIStoreCredentialsOnRekey
526Controls whether the user's GSSAPI credentials should be updated following a
527successful connection rekeying. This option can be used to accepted renewed
528or updated credentials from a compatible client. The default is
529.Dq no .
502.It Cm HostbasedAuthentication 530.It Cm HostbasedAuthentication
503Specifies whether rhosts or /etc/hosts.equiv authentication together 531Specifies whether rhosts or /etc/hosts.equiv authentication together
504with successful public key client host authentication is allowed 532with successful public key client host authentication is allowed