summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2016-08-07 12:18:35 +0100
commiteecddf8b72fcad83ccca43b1badb03782704f6b7 (patch)
treefd0046825c8d42bd267afa7839d5603b130cf847
parenta8ed8d256b2e2c05b0c15565a7938028c5192277 (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: 2016-08-07 Patch-Name: gssapi.patch
-rw-r--r--ChangeLog.gssapi113
-rw-r--r--Makefile.in3
-rw-r--r--auth-krb5.c17
-rw-r--r--auth.c96
-rw-r--r--auth2-gss.c48
-rw-r--r--auth2.c2
-rw-r--r--canohost.c93
-rw-r--r--canohost.h3
-rw-r--r--clientloop.c15
-rw-r--r--config.h.in6
-rw-r--r--configure.ac24
-rw-r--r--gss-genr.c275
-rw-r--r--gss-serv-krb5.c85
-rw-r--r--gss-serv.c185
-rw-r--r--kex.c19
-rw-r--r--kex.h14
-rw-r--r--kexgssc.c338
-rw-r--r--kexgsss.c295
-rw-r--r--monitor.c108
-rw-r--r--monitor.h3
-rw-r--r--monitor_wrap.c47
-rw-r--r--monitor_wrap.h4
-rw-r--r--readconf.c42
-rw-r--r--readconf.h5
-rw-r--r--servconf.c28
-rw-r--r--servconf.h2
-rw-r--r--ssh-gss.h41
-rw-r--r--ssh_config2
-rw-r--r--ssh_config.532
-rw-r--r--sshconnect2.c122
-rw-r--r--sshd.c110
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.510
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
35 files changed, 2054 insertions, 139 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 12991cd9f..51817dfe0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -92,6 +92,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
92 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ 92 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
93 kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ 93 kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \
94 kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ 94 kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \
95 kexgssc.o \
95 platform-pledge.o platform-tracing.o 96 platform-pledge.o platform-tracing.o
96 97
97SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ 98SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
@@ -105,7 +106,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
105 auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ 106 auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
106 auth2-none.o auth2-passwd.o auth2-pubkey.o \ 107 auth2-none.o auth2-passwd.o auth2-pubkey.o \
107 monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \ 108 monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \
108 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 109 auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
109 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ 110 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
110 sftp-server.o sftp-common.o \ 111 sftp-server.o sftp-common.o \
111 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ 112 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
diff --git a/auth-krb5.c b/auth-krb5.c
index a5a81ed2e..38e7fee21 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/auth.c b/auth.c
index 24527dd7c..f56dcc6cf 100644
--- a/auth.c
+++ b/auth.c
@@ -363,7 +363,8 @@ auth_root_allowed(const char *method)
363 case PERMIT_NO_PASSWD: 363 case PERMIT_NO_PASSWD:
364 if (strcmp(method, "publickey") == 0 || 364 if (strcmp(method, "publickey") == 0 ||
365 strcmp(method, "hostbased") == 0 || 365 strcmp(method, "hostbased") == 0 ||
366 strcmp(method, "gssapi-with-mic") == 0) 366 strcmp(method, "gssapi-with-mic") == 0 ||
367 strcmp(method, "gssapi-keyex") == 0)
367 return 1; 368 return 1;
368 break; 369 break;
369 case PERMIT_FORCED_ONLY: 370 case PERMIT_FORCED_ONLY:
@@ -786,99 +787,6 @@ fakepw(void)
786} 787}
787 788
788/* 789/*
789 * Returns the remote DNS hostname as a string. The returned string must not
790 * be freed. NB. this will usually trigger a DNS query the first time it is
791 * called.
792 * This function does additional checks on the hostname to mitigate some
793 * attacks on legacy rhosts-style authentication.
794 * XXX is RhostsRSAAuthentication vulnerable to these?
795 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
796 */
797
798static char *
799remote_hostname(struct ssh *ssh)
800{
801 struct sockaddr_storage from;
802 socklen_t fromlen;
803 struct addrinfo hints, *ai, *aitop;
804 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
805 const char *ntop = ssh_remote_ipaddr(ssh);
806
807 /* Get IP address of client. */
808 fromlen = sizeof(from);
809 memset(&from, 0, sizeof(from));
810 if (getpeername(ssh_packet_get_connection_in(ssh),
811 (struct sockaddr *)&from, &fromlen) < 0) {
812 debug("getpeername failed: %.100s", strerror(errno));
813 return strdup(ntop);
814 }
815
816 ipv64_normalise_mapped(&from, &fromlen);
817 if (from.ss_family == AF_INET6)
818 fromlen = sizeof(struct sockaddr_in6);
819
820 debug3("Trying to reverse map address %.100s.", ntop);
821 /* Map the IP address to a host name. */
822 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
823 NULL, 0, NI_NAMEREQD) != 0) {
824 /* Host name not found. Use ip address. */
825 return strdup(ntop);
826 }
827
828 /*
829 * if reverse lookup result looks like a numeric hostname,
830 * someone is trying to trick us by PTR record like following:
831 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
832 */
833 memset(&hints, 0, sizeof(hints));
834 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
835 hints.ai_flags = AI_NUMERICHOST;
836 if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
837 logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
838 name, ntop);
839 freeaddrinfo(ai);
840 return strdup(ntop);
841 }
842
843 /* Names are stored in lowercase. */
844 lowercase(name);
845
846 /*
847 * Map it back to an IP address and check that the given
848 * address actually is an address of this host. This is
849 * necessary because anyone with access to a name server can
850 * define arbitrary names for an IP address. Mapping from
851 * name to IP address can be trusted better (but can still be
852 * fooled if the intruder has access to the name server of
853 * the domain).
854 */
855 memset(&hints, 0, sizeof(hints));
856 hints.ai_family = from.ss_family;
857 hints.ai_socktype = SOCK_STREAM;
858 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
859 logit("reverse mapping checking getaddrinfo for %.700s "
860 "[%s] failed.", name, ntop);
861 return strdup(ntop);
862 }
863 /* Look for the address from the list of addresses. */
864 for (ai = aitop; ai; ai = ai->ai_next) {
865 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
866 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
867 (strcmp(ntop, ntop2) == 0))
868 break;
869 }
870 freeaddrinfo(aitop);
871 /* If we reached the end of the list, the address was not there. */
872 if (ai == NULL) {
873 /* Address not found for the host name. */
874 logit("Address %.100s maps to %.600s, but this does not "
875 "map back to the address.", ntop, name);
876 return strdup(ntop);
877 }
878 return strdup(name);
879}
880
881/*
882 * Return the canonical name of the host in the other side of the current 790 * Return the canonical name of the host in the other side of the current
883 * connection. The host name is cached, so it is efficient to call this 791 * connection. The host name is cached, so it is efficient to call this
884 * several times. 792 * several times.
diff --git a/auth2-gss.c b/auth2-gss.c
index 1ca835773..3b5036dfd 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 markus Exp $ */ 1/* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 markus Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -53,6 +53,40 @@ static int input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
53static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); 53static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
54static int input_gssapi_errtok(int, u_int32_t, void *); 54static int input_gssapi_errtok(int, u_int32_t, void *);
55 55
56/*
57 * The 'gssapi_keyex' userauth mechanism.
58 */
59static int
60userauth_gsskeyex(Authctxt *authctxt)
61{
62 int authenticated = 0;
63 Buffer b;
64 gss_buffer_desc mic, gssbuf;
65 u_int len;
66
67 mic.value = packet_get_string(&len);
68 mic.length = len;
69
70 packet_check_eom();
71
72 ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
73 "gssapi-keyex");
74
75 gssbuf.value = buffer_ptr(&b);
76 gssbuf.length = buffer_len(&b);
77
78 /* gss_kex_context is NULL with privsep, so we can't check it here */
79 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
80 &gssbuf, &mic))))
81 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
82 authctxt->pw));
83
84 buffer_free(&b);
85 free(mic.value);
86
87 return (authenticated);
88}
89
56/* 90/*
57 * We only support those mechanisms that we know about (ie ones that we know 91 * We only support those mechanisms that we know about (ie ones that we know
58 * how to check local user kuserok and the like) 92 * how to check local user kuserok and the like)
@@ -238,7 +272,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
238 272
239 packet_check_eom(); 273 packet_check_eom();
240 274
241 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 275 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
276 authctxt->pw));
242 277
243 authctxt->postponed = 0; 278 authctxt->postponed = 0;
244 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 279 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
@@ -274,7 +309,8 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
274 gssbuf.length = buffer_len(&b); 309 gssbuf.length = buffer_len(&b);
275 310
276 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) 311 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
277 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 312 authenticated =
313 PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
278 else 314 else
279 logit("GSSAPI MIC check failed"); 315 logit("GSSAPI MIC check failed");
280 316
@@ -290,6 +326,12 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
290 return 0; 326 return 0;
291} 327}
292 328
329Authmethod method_gsskeyex = {
330 "gssapi-keyex",
331 userauth_gsskeyex,
332 &options.gss_authentication
333};
334
293Authmethod method_gssapi = { 335Authmethod method_gssapi = {
294 "gssapi-with-mic", 336 "gssapi-with-mic",
295 userauth_gssapi, 337 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index 9108b8612..ce0d37601 100644
--- a/auth2.c
+++ b/auth2.c
@@ -70,6 +70,7 @@ extern Authmethod method_passwd;
70extern Authmethod method_kbdint; 70extern Authmethod method_kbdint;
71extern Authmethod method_hostbased; 71extern Authmethod method_hostbased;
72#ifdef GSSAPI 72#ifdef GSSAPI
73extern Authmethod method_gsskeyex;
73extern Authmethod method_gssapi; 74extern Authmethod method_gssapi;
74#endif 75#endif
75 76
@@ -77,6 +78,7 @@ Authmethod *authmethods[] = {
77 &method_none, 78 &method_none,
78 &method_pubkey, 79 &method_pubkey,
79#ifdef GSSAPI 80#ifdef GSSAPI
81 &method_gsskeyex,
80 &method_gssapi, 82 &method_gssapi,
81#endif 83#endif
82 &method_passwd, 84 &method_passwd,
diff --git a/canohost.c b/canohost.c
index f71a08568..404731d24 100644
--- a/canohost.c
+++ b/canohost.c
@@ -35,6 +35,99 @@
35#include "canohost.h" 35#include "canohost.h"
36#include "misc.h" 36#include "misc.h"
37 37
38/*
39 * Returns the remote DNS hostname as a string. The returned string must not
40 * be freed. NB. this will usually trigger a DNS query the first time it is
41 * called.
42 * This function does additional checks on the hostname to mitigate some
43 * attacks on legacy rhosts-style authentication.
44 * XXX is RhostsRSAAuthentication vulnerable to these?
45 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
46 */
47
48char *
49remote_hostname(struct ssh *ssh)
50{
51 struct sockaddr_storage from;
52 socklen_t fromlen;
53 struct addrinfo hints, *ai, *aitop;
54 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
55 const char *ntop = ssh_remote_ipaddr(ssh);
56
57 /* Get IP address of client. */
58 fromlen = sizeof(from);
59 memset(&from, 0, sizeof(from));
60 if (getpeername(ssh_packet_get_connection_in(ssh),
61 (struct sockaddr *)&from, &fromlen) < 0) {
62 debug("getpeername failed: %.100s", strerror(errno));
63 return strdup(ntop);
64 }
65
66 ipv64_normalise_mapped(&from, &fromlen);
67 if (from.ss_family == AF_INET6)
68 fromlen = sizeof(struct sockaddr_in6);
69
70 debug3("Trying to reverse map address %.100s.", ntop);
71 /* Map the IP address to a host name. */
72 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
73 NULL, 0, NI_NAMEREQD) != 0) {
74 /* Host name not found. Use ip address. */
75 return strdup(ntop);
76 }
77
78 /*
79 * if reverse lookup result looks like a numeric hostname,
80 * someone is trying to trick us by PTR record like following:
81 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
82 */
83 memset(&hints, 0, sizeof(hints));
84 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
85 hints.ai_flags = AI_NUMERICHOST;
86 if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
87 logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
88 name, ntop);
89 freeaddrinfo(ai);
90 return strdup(ntop);
91 }
92
93 /* Names are stored in lowercase. */
94 lowercase(name);
95
96 /*
97 * Map it back to an IP address and check that the given
98 * address actually is an address of this host. This is
99 * necessary because anyone with access to a name server can
100 * define arbitrary names for an IP address. Mapping from
101 * name to IP address can be trusted better (but can still be
102 * fooled if the intruder has access to the name server of
103 * the domain).
104 */
105 memset(&hints, 0, sizeof(hints));
106 hints.ai_family = from.ss_family;
107 hints.ai_socktype = SOCK_STREAM;
108 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
109 logit("reverse mapping checking getaddrinfo for %.700s "
110 "[%s] failed.", name, ntop);
111 return strdup(ntop);
112 }
113 /* Look for the address from the list of addresses. */
114 for (ai = aitop; ai; ai = ai->ai_next) {
115 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
116 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
117 (strcmp(ntop, ntop2) == 0))
118 break;
119 }
120 freeaddrinfo(aitop);
121 /* If we reached the end of the list, the address was not there. */
122 if (ai == NULL) {
123 /* Address not found for the host name. */
124 logit("Address %.100s maps to %.600s, but this does not "
125 "map back to the address.", ntop, name);
126 return strdup(ntop);
127 }
128 return strdup(name);
129}
130
38void 131void
39ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len) 132ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
40{ 133{
diff --git a/canohost.h b/canohost.h
index 26d62855a..0cadc9f18 100644
--- a/canohost.h
+++ b/canohost.h
@@ -15,6 +15,9 @@
15#ifndef _CANOHOST_H 15#ifndef _CANOHOST_H
16#define _CANOHOST_H 16#define _CANOHOST_H
17 17
18struct ssh;
19
20char *remote_hostname(struct ssh *);
18char *get_peer_ipaddr(int); 21char *get_peer_ipaddr(int);
19int get_peer_port(int); 22int get_peer_port(int);
20char *get_local_ipaddr(int); 23char *get_local_ipaddr(int);
diff --git a/clientloop.c b/clientloop.c
index 2c44f5d19..421241f74 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -114,6 +114,10 @@
114#include "ssherr.h" 114#include "ssherr.h"
115#include "hostfile.h" 115#include "hostfile.h"
116 116
117#ifdef GSSAPI
118#include "ssh-gss.h"
119#endif
120
117/* import options */ 121/* import options */
118extern Options options; 122extern Options options;
119 123
@@ -1666,9 +1670,18 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
1666 break; 1670 break;
1667 1671
1668 /* Do channel operations unless rekeying in progress. */ 1672 /* Do channel operations unless rekeying in progress. */
1669 if (!ssh_packet_is_rekeying(active_state)) 1673 if (!ssh_packet_is_rekeying(active_state)) {
1670 channel_after_select(readset, writeset); 1674 channel_after_select(readset, writeset);
1671 1675
1676#ifdef GSSAPI
1677 if (options.gss_renewal_rekey &&
1678 ssh_gssapi_credentials_updated(NULL)) {
1679 debug("credentials updated - forcing rekey");
1680 need_rekeying = 1;
1681 }
1682#endif
1683 }
1684
1672 /* Buffer input from the connection. */ 1685 /* Buffer input from the connection. */
1673 client_process_net_input(readset); 1686 client_process_net_input(readset);
1674 1687
diff --git a/config.h.in b/config.h.in
index 39d018f4c..d7caf9aa4 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1668,6 +1668,9 @@
1668/* Use btmp to log bad logins */ 1668/* Use btmp to log bad logins */
1669#undef USE_BTMP 1669#undef USE_BTMP
1670 1670
1671/* platform uses an in-memory credentials cache */
1672#undef USE_CCAPI
1673
1671/* Use libedit for sftp */ 1674/* Use libedit for sftp */
1672#undef USE_LIBEDIT 1675#undef USE_LIBEDIT
1673 1676
@@ -1683,6 +1686,9 @@
1683/* Use PIPES instead of a socketpair() */ 1686/* Use PIPES instead of a socketpair() */
1684#undef USE_PIPES 1687#undef USE_PIPES
1685 1688
1689/* platform has the Security Authorization Session API */
1690#undef USE_SECURITY_SESSION_API
1691
1686/* Define if you have Solaris privileges */ 1692/* Define if you have Solaris privileges */
1687#undef USE_SOLARIS_PRIVS 1693#undef USE_SOLARIS_PRIVS
1688 1694
diff --git a/configure.ac b/configure.ac
index 373d21b34..894ec3b7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -632,6 +632,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
632 [Use tunnel device compatibility to OpenBSD]) 632 [Use tunnel device compatibility to OpenBSD])
633 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 633 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
634 [Prepend the address family to IP tunnel traffic]) 634 [Prepend the address family to IP tunnel traffic])
635 AC_MSG_CHECKING([if we have the Security Authorization Session API])
636 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
637 [SessionCreate(0, 0);],
638 [ac_cv_use_security_session_api="yes"
639 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
640 [platform has the Security Authorization Session API])
641 LIBS="$LIBS -framework Security"
642 AC_MSG_RESULT([yes])],
643 [ac_cv_use_security_session_api="no"
644 AC_MSG_RESULT([no])])
645 AC_MSG_CHECKING([if we have an in-memory credentials cache])
646 AC_TRY_COMPILE(
647 [#include <Kerberos/Kerberos.h>],
648 [cc_context_t c;
649 (void) cc_initialize (&c, 0, NULL, NULL);],
650 [AC_DEFINE([USE_CCAPI], [1],
651 [platform uses an in-memory credentials cache])
652 LIBS="$LIBS -framework Security"
653 AC_MSG_RESULT([yes])
654 if test "x$ac_cv_use_security_session_api" = "xno"; then
655 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
656 fi],
657 [AC_MSG_RESULT([no])]
658 )
635 m4_pattern_allow([AU_IPv]) 659 m4_pattern_allow([AU_IPv])
636 AC_CHECK_DECL([AU_IPv4], [], 660 AC_CHECK_DECL([AU_IPv4], [],
637 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 661 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
diff --git a/gss-genr.c b/gss-genr.c
index d617d600a..b4eca3feb 100644
--- a/gss-genr.c
+++ b/gss-genr.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt Exp $ */ 1/* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -41,12 +41,167 @@
41#include "buffer.h" 41#include "buffer.h"
42#include "log.h" 42#include "log.h"
43#include "ssh2.h" 43#include "ssh2.h"
44#include "cipher.h"
45#include "key.h"
46#include "kex.h"
47#include <openssl/evp.h>
44 48
45#include "ssh-gss.h" 49#include "ssh-gss.h"
46 50
47extern u_char *session_id2; 51extern u_char *session_id2;
48extern u_int session_id2_len; 52extern u_int session_id2_len;
49 53
54typedef struct {
55 char *encoded;
56 gss_OID oid;
57} ssh_gss_kex_mapping;
58
59/*
60 * XXX - It would be nice to find a more elegant way of handling the
61 * XXX passing of the key exchange context to the userauth routines
62 */
63
64Gssctxt *gss_kex_context = NULL;
65
66static ssh_gss_kex_mapping *gss_enc2oid = NULL;
67
68int
69ssh_gssapi_oid_table_ok(void) {
70 return (gss_enc2oid != NULL);
71}
72
73/*
74 * Return a list of the gss-group1-sha1 mechanisms supported by this program
75 *
76 * We test mechanisms to ensure that we can use them, to avoid starting
77 * a key exchange with a bad mechanism
78 */
79
80char *
81ssh_gssapi_client_mechanisms(const char *host, const char *client) {
82 gss_OID_set gss_supported;
83 OM_uint32 min_status;
84
85 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
86 return NULL;
87
88 return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
89 host, client));
90}
91
92char *
93ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
94 const char *host, const char *client) {
95 Buffer buf;
96 size_t i;
97 int oidpos, enclen;
98 char *mechs, *encoded;
99 u_char digest[EVP_MAX_MD_SIZE];
100 char deroid[2];
101 const EVP_MD *evp_md = EVP_md5();
102 EVP_MD_CTX md;
103
104 if (gss_enc2oid != NULL) {
105 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
106 free(gss_enc2oid[i].encoded);
107 free(gss_enc2oid);
108 }
109
110 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
111 (gss_supported->count + 1));
112
113 buffer_init(&buf);
114
115 oidpos = 0;
116 for (i = 0; i < gss_supported->count; i++) {
117 if (gss_supported->elements[i].length < 128 &&
118 (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
119
120 deroid[0] = SSH_GSS_OIDTYPE;
121 deroid[1] = gss_supported->elements[i].length;
122
123 EVP_DigestInit(&md, evp_md);
124 EVP_DigestUpdate(&md, deroid, 2);
125 EVP_DigestUpdate(&md,
126 gss_supported->elements[i].elements,
127 gss_supported->elements[i].length);
128 EVP_DigestFinal(&md, digest, NULL);
129
130 encoded = xmalloc(EVP_MD_size(evp_md) * 2);
131 enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
132 encoded, EVP_MD_size(evp_md) * 2);
133
134 if (oidpos != 0)
135 buffer_put_char(&buf, ',');
136
137 buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
138 sizeof(KEX_GSS_GEX_SHA1_ID) - 1);
139 buffer_append(&buf, encoded, enclen);
140 buffer_put_char(&buf, ',');
141 buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID,
142 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1);
143 buffer_append(&buf, encoded, enclen);
144 buffer_put_char(&buf, ',');
145 buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
146 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1);
147 buffer_append(&buf, encoded, enclen);
148
149 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
150 gss_enc2oid[oidpos].encoded = encoded;
151 oidpos++;
152 }
153 }
154 gss_enc2oid[oidpos].oid = NULL;
155 gss_enc2oid[oidpos].encoded = NULL;
156
157 buffer_put_char(&buf, '\0');
158
159 mechs = xmalloc(buffer_len(&buf));
160 buffer_get(&buf, mechs, buffer_len(&buf));
161 buffer_free(&buf);
162
163 if (strlen(mechs) == 0) {
164 free(mechs);
165 mechs = NULL;
166 }
167
168 return (mechs);
169}
170
171gss_OID
172ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
173 int i = 0;
174
175 switch (kex_type) {
176 case KEX_GSS_GRP1_SHA1:
177 if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
178 return GSS_C_NO_OID;
179 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
180 break;
181 case KEX_GSS_GRP14_SHA1:
182 if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
183 return GSS_C_NO_OID;
184 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
185 break;
186 case KEX_GSS_GEX_SHA1:
187 if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
188 return GSS_C_NO_OID;
189 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
190 break;
191 default:
192 return GSS_C_NO_OID;
193 }
194
195 while (gss_enc2oid[i].encoded != NULL &&
196 strcmp(name, gss_enc2oid[i].encoded) != 0)
197 i++;
198
199 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
200 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
201
202 return gss_enc2oid[i].oid;
203}
204
50/* Check that the OID in a data stream matches that in the context */ 205/* Check that the OID in a data stream matches that in the context */
51int 206int
52ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 207ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -199,7 +354,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
199 } 354 }
200 355
201 ctx->major = gss_init_sec_context(&ctx->minor, 356 ctx->major = gss_init_sec_context(&ctx->minor,
202 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 357 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
203 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 358 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
204 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 359 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
205 360
@@ -229,8 +384,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
229} 384}
230 385
231OM_uint32 386OM_uint32
387ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
388{
389 gss_buffer_desc gssbuf;
390 gss_name_t gssname;
391 OM_uint32 status;
392 gss_OID_set oidset;
393
394 gssbuf.value = (void *) name;
395 gssbuf.length = strlen(gssbuf.value);
396
397 gss_create_empty_oid_set(&status, &oidset);
398 gss_add_oid_set_member(&status, ctx->oid, &oidset);
399
400 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
401 GSS_C_NT_USER_NAME, &gssname);
402
403 if (!ctx->major)
404 ctx->major = gss_acquire_cred(&ctx->minor,
405 gssname, 0, oidset, GSS_C_INITIATE,
406 &ctx->client_creds, NULL, NULL);
407
408 gss_release_name(&status, &gssname);
409 gss_release_oid_set(&status, &oidset);
410
411 if (ctx->major)
412 ssh_gssapi_error(ctx);
413
414 return(ctx->major);
415}
416
417OM_uint32
232ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 418ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
233{ 419{
420 if (ctx == NULL)
421 return -1;
422
234 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 423 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
235 GSS_C_QOP_DEFAULT, buffer, hash))) 424 GSS_C_QOP_DEFAULT, buffer, hash)))
236 ssh_gssapi_error(ctx); 425 ssh_gssapi_error(ctx);
@@ -238,6 +427,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
238 return (ctx->major); 427 return (ctx->major);
239} 428}
240 429
430/* Priviledged when used by server */
431OM_uint32
432ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
433{
434 if (ctx == NULL)
435 return -1;
436
437 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
438 gssbuf, gssmic, NULL);
439
440 return (ctx->major);
441}
442
241void 443void
242ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, 444ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
243 const char *context) 445 const char *context)
@@ -251,11 +453,16 @@ ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
251} 453}
252 454
253int 455int
254ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 456ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
457 const char *client)
255{ 458{
256 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 459 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
257 OM_uint32 major, minor; 460 OM_uint32 major, minor;
258 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 461 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
462 Gssctxt *intctx = NULL;
463
464 if (ctx == NULL)
465 ctx = &intctx;
259 466
260 /* RFC 4462 says we MUST NOT do SPNEGO */ 467 /* RFC 4462 says we MUST NOT do SPNEGO */
261 if (oid->length == spnego_oid.length && 468 if (oid->length == spnego_oid.length &&
@@ -265,6 +472,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
265 ssh_gssapi_build_ctx(ctx); 472 ssh_gssapi_build_ctx(ctx);
266 ssh_gssapi_set_oid(*ctx, oid); 473 ssh_gssapi_set_oid(*ctx, oid);
267 major = ssh_gssapi_import_name(*ctx, host); 474 major = ssh_gssapi_import_name(*ctx, host);
475
476 if (!GSS_ERROR(major) && client)
477 major = ssh_gssapi_client_identity(*ctx, client);
478
268 if (!GSS_ERROR(major)) { 479 if (!GSS_ERROR(major)) {
269 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 480 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
270 NULL); 481 NULL);
@@ -274,10 +485,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
274 GSS_C_NO_BUFFER); 485 GSS_C_NO_BUFFER);
275 } 486 }
276 487
277 if (GSS_ERROR(major)) 488 if (GSS_ERROR(major) || intctx != NULL)
278 ssh_gssapi_delete_ctx(ctx); 489 ssh_gssapi_delete_ctx(ctx);
279 490
280 return (!GSS_ERROR(major)); 491 return (!GSS_ERROR(major));
281} 492}
282 493
494int
495ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
496 static gss_name_t saved_name = GSS_C_NO_NAME;
497 static OM_uint32 saved_lifetime = 0;
498 static gss_OID saved_mech = GSS_C_NO_OID;
499 static gss_name_t name;
500 static OM_uint32 last_call = 0;
501 OM_uint32 lifetime, now, major, minor;
502 int equal;
503
504 now = time(NULL);
505
506 if (ctxt) {
507 debug("Rekey has happened - updating saved versions");
508
509 if (saved_name != GSS_C_NO_NAME)
510 gss_release_name(&minor, &saved_name);
511
512 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
513 &saved_name, &saved_lifetime, NULL, NULL);
514
515 if (!GSS_ERROR(major)) {
516 saved_mech = ctxt->oid;
517 saved_lifetime+= now;
518 } else {
519 /* Handle the error */
520 }
521 return 0;
522 }
523
524 if (now - last_call < 10)
525 return 0;
526
527 last_call = now;
528
529 if (saved_mech == GSS_C_NO_OID)
530 return 0;
531
532 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
533 &name, &lifetime, NULL, NULL);
534 if (major == GSS_S_CREDENTIALS_EXPIRED)
535 return 0;
536 else if (GSS_ERROR(major))
537 return 0;
538
539 major = gss_compare_name(&minor, saved_name, name, &equal);
540 gss_release_name(&minor, &name);
541 if (GSS_ERROR(major))
542 return 0;
543
544 if (equal && (saved_lifetime < lifetime + now - 10))
545 return 1;
546
547 return 0;
548}
549
283#endif /* GSSAPI */ 550#endif /* GSSAPI */
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
index 795992d9f..fd8b37183 100644
--- a/gss-serv-krb5.c
+++ b/gss-serv-krb5.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv-krb5.c,v 1.8 2013/07/20 01:55:13 djm Exp $ */ 1/* $OpenBSD: gss-serv-krb5.c,v 1.8 2013/07/20 01:55:13 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -121,8 +121,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
121 krb5_error_code problem; 121 krb5_error_code problem;
122 krb5_principal princ; 122 krb5_principal princ;
123 OM_uint32 maj_status, min_status; 123 OM_uint32 maj_status, min_status;
124 int len;
125 const char *errmsg; 124 const char *errmsg;
125 const char *new_ccname;
126 126
127 if (client->creds == NULL) { 127 if (client->creds == NULL) {
128 debug("No credentials stored"); 128 debug("No credentials stored");
@@ -181,11 +181,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
181 return; 181 return;
182 } 182 }
183 183
184 client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); 184 new_ccname = krb5_cc_get_name(krb_context, ccache);
185
185 client->store.envvar = "KRB5CCNAME"; 186 client->store.envvar = "KRB5CCNAME";
186 len = strlen(client->store.filename) + 6; 187#ifdef USE_CCAPI
187 client->store.envval = xmalloc(len); 188 xasprintf(&client->store.envval, "API:%s", new_ccname);
188 snprintf(client->store.envval, len, "FILE:%s", client->store.filename); 189 client->store.filename = NULL;
190#else
191 xasprintf(&client->store.envval, "FILE:%s", new_ccname);
192 client->store.filename = xstrdup(new_ccname);
193#endif
189 194
190#ifdef USE_PAM 195#ifdef USE_PAM
191 if (options.use_pam) 196 if (options.use_pam)
@@ -197,6 +202,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
197 return; 202 return;
198} 203}
199 204
205int
206ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
207 ssh_gssapi_client *client)
208{
209 krb5_ccache ccache = NULL;
210 krb5_principal principal = NULL;
211 char *name = NULL;
212 krb5_error_code problem;
213 OM_uint32 maj_status, min_status;
214
215 if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
216 logit("krb5_cc_resolve(): %.100s",
217 krb5_get_err_text(krb_context, problem));
218 return 0;
219 }
220
221 /* Find out who the principal in this cache is */
222 if ((problem = krb5_cc_get_principal(krb_context, ccache,
223 &principal))) {
224 logit("krb5_cc_get_principal(): %.100s",
225 krb5_get_err_text(krb_context, problem));
226 krb5_cc_close(krb_context, ccache);
227 return 0;
228 }
229
230 if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
231 logit("krb5_unparse_name(): %.100s",
232 krb5_get_err_text(krb_context, problem));
233 krb5_free_principal(krb_context, principal);
234 krb5_cc_close(krb_context, ccache);
235 return 0;
236 }
237
238
239 if (strcmp(name,client->exportedname.value)!=0) {
240 debug("Name in local credentials cache differs. Not storing");
241 krb5_free_principal(krb_context, principal);
242 krb5_cc_close(krb_context, ccache);
243 krb5_free_unparsed_name(krb_context, name);
244 return 0;
245 }
246 krb5_free_unparsed_name(krb_context, name);
247
248 /* Name matches, so lets get on with it! */
249
250 if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
251 logit("krb5_cc_initialize(): %.100s",
252 krb5_get_err_text(krb_context, problem));
253 krb5_free_principal(krb_context, principal);
254 krb5_cc_close(krb_context, ccache);
255 return 0;
256 }
257
258 krb5_free_principal(krb_context, principal);
259
260 if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
261 ccache))) {
262 logit("gss_krb5_copy_ccache() failed. Sorry!");
263 krb5_cc_close(krb_context, ccache);
264 return 0;
265 }
266
267 return 1;
268}
269
200ssh_gssapi_mech gssapi_kerberos_mech = { 270ssh_gssapi_mech gssapi_kerberos_mech = {
201 "toWM5Slw5Ew8Mqkay+al2g==", 271 "toWM5Slw5Ew8Mqkay+al2g==",
202 "Kerberos", 272 "Kerberos",
@@ -204,7 +274,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
204 NULL, 274 NULL,
205 &ssh_gssapi_krb5_userok, 275 &ssh_gssapi_krb5_userok,
206 NULL, 276 NULL,
207 &ssh_gssapi_krb5_storecreds 277 &ssh_gssapi_krb5_storecreds,
278 &ssh_gssapi_krb5_updatecreds
208}; 279};
209 280
210#endif /* KRB5 */ 281#endif /* KRB5 */
diff --git a/gss-serv.c b/gss-serv.c
index 53993d674..2f6baf70d 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -45,17 +45,22 @@
45#include "session.h" 45#include "session.h"
46#include "misc.h" 46#include "misc.h"
47#include "servconf.h" 47#include "servconf.h"
48#include "uidswap.h"
48 49
49#include "ssh-gss.h" 50#include "ssh-gss.h"
51#include "monitor_wrap.h"
52
53extern ServerOptions options;
50 54
51extern ServerOptions options; 55extern ServerOptions options;
52 56
53static ssh_gssapi_client gssapi_client = 57static ssh_gssapi_client gssapi_client =
54 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 58 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
55 GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; 59 GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL,
60 {NULL, NULL, NULL, NULL, NULL}, 0, 0};
56 61
57ssh_gssapi_mech gssapi_null_mech = 62ssh_gssapi_mech gssapi_null_mech =
58 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; 63 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
59 64
60#ifdef KRB5 65#ifdef KRB5
61extern ssh_gssapi_mech gssapi_kerberos_mech; 66extern ssh_gssapi_mech gssapi_kerberos_mech;
@@ -142,6 +147,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
142} 147}
143 148
144/* Unprivileged */ 149/* Unprivileged */
150char *
151ssh_gssapi_server_mechanisms(void) {
152 gss_OID_set supported;
153
154 ssh_gssapi_supported_oids(&supported);
155 return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
156 NULL, NULL));
157}
158
159/* Unprivileged */
160int
161ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
162 const char *dummy) {
163 Gssctxt *ctx = NULL;
164 int res;
165
166 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
167 ssh_gssapi_delete_ctx(&ctx);
168
169 return (res);
170}
171
172/* Unprivileged */
145void 173void
146ssh_gssapi_supported_oids(gss_OID_set *oidset) 174ssh_gssapi_supported_oids(gss_OID_set *oidset)
147{ 175{
@@ -151,7 +179,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
151 gss_OID_set supported; 179 gss_OID_set supported;
152 180
153 gss_create_empty_oid_set(&min_status, oidset); 181 gss_create_empty_oid_set(&min_status, oidset);
154 gss_indicate_mechs(&min_status, &supported); 182
183 if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
184 return;
155 185
156 while (supported_mechs[i]->name != NULL) { 186 while (supported_mechs[i]->name != NULL) {
157 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 187 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -277,8 +307,48 @@ OM_uint32
277ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 307ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
278{ 308{
279 int i = 0; 309 int i = 0;
310 int equal = 0;
311 gss_name_t new_name = GSS_C_NO_NAME;
312 gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
313
314 if (options.gss_store_rekey && client->used && ctx->client_creds) {
315 if (client->mech->oid.length != ctx->oid->length ||
316 (memcmp(client->mech->oid.elements,
317 ctx->oid->elements, ctx->oid->length) !=0)) {
318 debug("Rekeyed credentials have different mechanism");
319 return GSS_S_COMPLETE;
320 }
321
322 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
323 ctx->client_creds, ctx->oid, &new_name,
324 NULL, NULL, NULL))) {
325 ssh_gssapi_error(ctx);
326 return (ctx->major);
327 }
328
329 ctx->major = gss_compare_name(&ctx->minor, client->name,
330 new_name, &equal);
331
332 if (GSS_ERROR(ctx->major)) {
333 ssh_gssapi_error(ctx);
334 return (ctx->major);
335 }
336
337 if (!equal) {
338 debug("Rekeyed credentials have different name");
339 return GSS_S_COMPLETE;
340 }
280 341
281 gss_buffer_desc ename; 342 debug("Marking rekeyed credentials for export");
343
344 gss_release_name(&ctx->minor, &client->name);
345 gss_release_cred(&ctx->minor, &client->creds);
346 client->name = new_name;
347 client->creds = ctx->client_creds;
348 ctx->client_creds = GSS_C_NO_CREDENTIAL;
349 client->updated = 1;
350 return GSS_S_COMPLETE;
351 }
282 352
283 client->mech = NULL; 353 client->mech = NULL;
284 354
@@ -293,6 +363,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
293 if (client->mech == NULL) 363 if (client->mech == NULL)
294 return GSS_S_FAILURE; 364 return GSS_S_FAILURE;
295 365
366 if (ctx->client_creds &&
367 (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
368 ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
369 ssh_gssapi_error(ctx);
370 return (ctx->major);
371 }
372
296 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 373 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
297 &client->displayname, NULL))) { 374 &client->displayname, NULL))) {
298 ssh_gssapi_error(ctx); 375 ssh_gssapi_error(ctx);
@@ -310,6 +387,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
310 return (ctx->major); 387 return (ctx->major);
311 } 388 }
312 389
390 gss_release_buffer(&ctx->minor, &ename);
391
313 /* We can't copy this structure, so we just move the pointer to it */ 392 /* We can't copy this structure, so we just move the pointer to it */
314 client->creds = ctx->client_creds; 393 client->creds = ctx->client_creds;
315 ctx->client_creds = GSS_C_NO_CREDENTIAL; 394 ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -357,7 +436,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
357 436
358/* Privileged */ 437/* Privileged */
359int 438int
360ssh_gssapi_userok(char *user) 439ssh_gssapi_userok(char *user, struct passwd *pw)
361{ 440{
362 OM_uint32 lmin; 441 OM_uint32 lmin;
363 442
@@ -367,9 +446,11 @@ ssh_gssapi_userok(char *user)
367 return 0; 446 return 0;
368 } 447 }
369 if (gssapi_client.mech && gssapi_client.mech->userok) 448 if (gssapi_client.mech && gssapi_client.mech->userok)
370 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 449 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
450 gssapi_client.used = 1;
451 gssapi_client.store.owner = pw;
371 return 1; 452 return 1;
372 else { 453 } else {
373 /* Destroy delegated credentials if userok fails */ 454 /* Destroy delegated credentials if userok fails */
374 gss_release_buffer(&lmin, &gssapi_client.displayname); 455 gss_release_buffer(&lmin, &gssapi_client.displayname);
375 gss_release_buffer(&lmin, &gssapi_client.exportedname); 456 gss_release_buffer(&lmin, &gssapi_client.exportedname);
@@ -383,14 +464,90 @@ ssh_gssapi_userok(char *user)
383 return (0); 464 return (0);
384} 465}
385 466
386/* Privileged */ 467/* These bits are only used for rekeying. The unpriviledged child is running
387OM_uint32 468 * as the user, the monitor is root.
388ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 469 *
470 * In the child, we want to :
471 * *) Ask the monitor to store our credentials into the store we specify
472 * *) If it succeeds, maybe do a PAM update
473 */
474
475/* Stuff for PAM */
476
477#ifdef USE_PAM
478static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
479 struct pam_response **resp, void *data)
389{ 480{
390 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 481 return (PAM_CONV_ERR);
391 gssbuf, gssmic, NULL); 482}
483#endif
392 484
393 return (ctx->major); 485void
486ssh_gssapi_rekey_creds(void) {
487 int ok;
488 int ret;
489#ifdef USE_PAM
490 pam_handle_t *pamh = NULL;
491 struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
492 char *envstr;
493#endif
494
495 if (gssapi_client.store.filename == NULL &&
496 gssapi_client.store.envval == NULL &&
497 gssapi_client.store.envvar == NULL)
498 return;
499
500 ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
501
502 if (!ok)
503 return;
504
505 debug("Rekeyed credentials stored successfully");
506
507 /* Actually managing to play with the ssh pam stack from here will
508 * be next to impossible. In any case, we may want different options
509 * for rekeying. So, use our own :)
510 */
511#ifdef USE_PAM
512 if (!use_privsep) {
513 debug("Not even going to try and do PAM with privsep disabled");
514 return;
515 }
516
517 ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
518 &pamconv, &pamh);
519 if (ret)
520 return;
521
522 xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
523 gssapi_client.store.envval);
524
525 ret = pam_putenv(pamh, envstr);
526 if (!ret)
527 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
528 pam_end(pamh, PAM_SUCCESS);
529#endif
530}
531
532int
533ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
534 int ok = 0;
535
536 /* Check we've got credentials to store */
537 if (!gssapi_client.updated)
538 return 0;
539
540 gssapi_client.updated = 0;
541
542 temporarily_use_uid(gssapi_client.store.owner);
543 if (gssapi_client.mech && gssapi_client.mech->updatecreds)
544 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
545 else
546 debug("No update function for this mechanism");
547
548 restore_uid();
549
550 return ok;
394} 551}
395 552
396#endif 553#endif
diff --git a/kex.c b/kex.c
index 50c7a0f9b..c17d652c9 100644
--- a/kex.c
+++ b/kex.c
@@ -55,6 +55,10 @@
55#include "sshbuf.h" 55#include "sshbuf.h"
56#include "digest.h" 56#include "digest.h"
57 57
58#ifdef GSSAPI
59#include "ssh-gss.h"
60#endif
61
58#if OPENSSL_VERSION_NUMBER >= 0x00907000L 62#if OPENSSL_VERSION_NUMBER >= 0x00907000L
59# if defined(HAVE_EVP_SHA256) 63# if defined(HAVE_EVP_SHA256)
60# define evp_ssh_sha256 EVP_sha256 64# define evp_ssh_sha256 EVP_sha256
@@ -113,6 +117,14 @@ static const struct kexalg kexalgs[] = {
113#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ 117#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
114 { NULL, -1, -1, -1}, 118 { NULL, -1, -1, -1},
115}; 119};
120static const struct kexalg kexalg_prefixes[] = {
121#ifdef GSSAPI
122 { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
123 { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
124 { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
125#endif
126 { NULL, -1, -1, -1 },
127};
116 128
117char * 129char *
118kex_alg_list(char sep) 130kex_alg_list(char sep)
@@ -145,6 +157,10 @@ kex_alg_by_name(const char *name)
145 if (strcmp(k->name, name) == 0) 157 if (strcmp(k->name, name) == 0)
146 return k; 158 return k;
147 } 159 }
160 for (k = kexalg_prefixes; k->name != NULL; k++) {
161 if (strncmp(k->name, name, strlen(k->name)) == 0)
162 return k;
163 }
148 return NULL; 164 return NULL;
149} 165}
150 166
@@ -587,6 +603,9 @@ kex_free(struct kex *kex)
587 sshbuf_free(kex->peer); 603 sshbuf_free(kex->peer);
588 sshbuf_free(kex->my); 604 sshbuf_free(kex->my);
589 free(kex->session_id); 605 free(kex->session_id);
606#ifdef GSSAPI
607 free(kex->gss_host);
608#endif /* GSSAPI */
590 free(kex->client_version_string); 609 free(kex->client_version_string);
591 free(kex->server_version_string); 610 free(kex->server_version_string);
592 free(kex->failed_choice); 611 free(kex->failed_choice);
diff --git a/kex.h b/kex.h
index c35195568..8ed459a76 100644
--- a/kex.h
+++ b/kex.h
@@ -98,6 +98,9 @@ enum kex_exchange {
98 KEX_DH_GEX_SHA256, 98 KEX_DH_GEX_SHA256,
99 KEX_ECDH_SHA2, 99 KEX_ECDH_SHA2,
100 KEX_C25519_SHA256, 100 KEX_C25519_SHA256,
101 KEX_GSS_GRP1_SHA1,
102 KEX_GSS_GRP14_SHA1,
103 KEX_GSS_GEX_SHA1,
101 KEX_MAX 104 KEX_MAX
102}; 105};
103 106
@@ -146,6 +149,12 @@ struct kex {
146 u_int flags; 149 u_int flags;
147 int hash_alg; 150 int hash_alg;
148 int ec_nid; 151 int ec_nid;
152#ifdef GSSAPI
153 int gss_deleg_creds;
154 int gss_trust_dns;
155 char *gss_host;
156 char *gss_client;
157#endif
149 char *client_version_string; 158 char *client_version_string;
150 char *server_version_string; 159 char *server_version_string;
151 char *failed_choice; 160 char *failed_choice;
@@ -196,6 +205,11 @@ int kexecdh_server(struct ssh *);
196int kexc25519_client(struct ssh *); 205int kexc25519_client(struct ssh *);
197int kexc25519_server(struct ssh *); 206int kexc25519_server(struct ssh *);
198 207
208#ifdef GSSAPI
209int kexgss_client(struct ssh *);
210int kexgss_server(struct ssh *);
211#endif
212
199int kex_dh_hash(int, const char *, const char *, 213int kex_dh_hash(int, const char *, const char *,
200 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, 214 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
201 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); 215 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *);
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..10447f2b0
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,338 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include "includes.h"
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include <string.h>
35
36#include "xmalloc.h"
37#include "buffer.h"
38#include "ssh2.h"
39#include "key.h"
40#include "cipher.h"
41#include "kex.h"
42#include "log.h"
43#include "packet.h"
44#include "dh.h"
45#include "digest.h"
46
47#include "ssh-gss.h"
48
49int
50kexgss_client(struct ssh *ssh) {
51 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
52 gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr;
53 Gssctxt *ctxt;
54 OM_uint32 maj_status, min_status, ret_flags;
55 u_int klen, kout, slen = 0, strlen;
56 DH *dh;
57 BIGNUM *dh_server_pub = NULL;
58 BIGNUM *shared_secret = NULL;
59 BIGNUM *p = NULL;
60 BIGNUM *g = NULL;
61 u_char *kbuf;
62 u_char *serverhostkey = NULL;
63 u_char *empty = "";
64 char *msg;
65 int type = 0;
66 int first = 1;
67 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
68 u_char hash[SSH_DIGEST_MAX_LENGTH];
69 size_t hashlen;
70
71 /* Initialise our GSSAPI world */
72 ssh_gssapi_build_ctx(&ctxt);
73 if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type)
74 == GSS_C_NO_OID)
75 fatal("Couldn't identify host exchange");
76
77 if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host))
78 fatal("Couldn't import hostname");
79
80 if (ssh->kex->gss_client &&
81 ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client))
82 fatal("Couldn't acquire client credentials");
83
84 switch (ssh->kex->kex_type) {
85 case KEX_GSS_GRP1_SHA1:
86 dh = dh_new_group1();
87 break;
88 case KEX_GSS_GRP14_SHA1:
89 dh = dh_new_group14();
90 break;
91 case KEX_GSS_GEX_SHA1:
92 debug("Doing group exchange\n");
93 nbits = dh_estimate(ssh->kex->we_need * 8);
94 packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
95 packet_put_int(min);
96 packet_put_int(nbits);
97 packet_put_int(max);
98
99 packet_send();
100
101 packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
102
103 if ((p = BN_new()) == NULL)
104 fatal("BN_new() failed");
105 packet_get_bignum2(p);
106 if ((g = BN_new()) == NULL)
107 fatal("BN_new() failed");
108 packet_get_bignum2(g);
109 packet_check_eom();
110
111 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
112 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
113 min, BN_num_bits(p), max);
114
115 dh = dh_new_group(g, p);
116 break;
117 default:
118 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
119 }
120
121 /* Step 1 - e is dh->pub_key */
122 dh_gen_key(dh, ssh->kex->we_need * 8);
123
124 /* This is f, we initialise it now to make life easier */
125 dh_server_pub = BN_new();
126 if (dh_server_pub == NULL)
127 fatal("dh_server_pub == NULL");
128
129 token_ptr = GSS_C_NO_BUFFER;
130
131 do {
132 debug("Calling gss_init_sec_context");
133
134 maj_status = ssh_gssapi_init_ctx(ctxt,
135 ssh->kex->gss_deleg_creds, token_ptr, &send_tok,
136 &ret_flags);
137
138 if (GSS_ERROR(maj_status)) {
139 if (send_tok.length != 0) {
140 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
141 packet_put_string(send_tok.value,
142 send_tok.length);
143 }
144 fatal("gss_init_context failed");
145 }
146
147 /* If we've got an old receive buffer get rid of it */
148 if (token_ptr != GSS_C_NO_BUFFER)
149 free(recv_tok.value);
150
151 if (maj_status == GSS_S_COMPLETE) {
152 /* If mutual state flag is not true, kex fails */
153 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
154 fatal("Mutual authentication failed");
155
156 /* If integ avail flag is not true kex fails */
157 if (!(ret_flags & GSS_C_INTEG_FLAG))
158 fatal("Integrity check failed");
159 }
160
161 /*
162 * If we have data to send, then the last message that we
163 * received cannot have been a 'complete'.
164 */
165 if (send_tok.length != 0) {
166 if (first) {
167 packet_start(SSH2_MSG_KEXGSS_INIT);
168 packet_put_string(send_tok.value,
169 send_tok.length);
170 packet_put_bignum2(dh->pub_key);
171 first = 0;
172 } else {
173 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
174 packet_put_string(send_tok.value,
175 send_tok.length);
176 }
177 packet_send();
178 gss_release_buffer(&min_status, &send_tok);
179
180 /* If we've sent them data, they should reply */
181 do {
182 type = packet_read();
183 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
184 debug("Received KEXGSS_HOSTKEY");
185 if (serverhostkey)
186 fatal("Server host key received more than once");
187 serverhostkey =
188 packet_get_string(&slen);
189 }
190 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
191
192 switch (type) {
193 case SSH2_MSG_KEXGSS_CONTINUE:
194 debug("Received GSSAPI_CONTINUE");
195 if (maj_status == GSS_S_COMPLETE)
196 fatal("GSSAPI Continue received from server when complete");
197 recv_tok.value = packet_get_string(&strlen);
198 recv_tok.length = strlen;
199 break;
200 case SSH2_MSG_KEXGSS_COMPLETE:
201 debug("Received GSSAPI_COMPLETE");
202 packet_get_bignum2(dh_server_pub);
203 msg_tok.value = packet_get_string(&strlen);
204 msg_tok.length = strlen;
205
206 /* Is there a token included? */
207 if (packet_get_char()) {
208 recv_tok.value=
209 packet_get_string(&strlen);
210 recv_tok.length = strlen;
211 /* If we're already complete - protocol error */
212 if (maj_status == GSS_S_COMPLETE)
213 packet_disconnect("Protocol error: received token when complete");
214 } else {
215 /* No token included */
216 if (maj_status != GSS_S_COMPLETE)
217 packet_disconnect("Protocol error: did not receive final token");
218 }
219 break;
220 case SSH2_MSG_KEXGSS_ERROR:
221 debug("Received Error");
222 maj_status = packet_get_int();
223 min_status = packet_get_int();
224 msg = packet_get_string(NULL);
225 (void) packet_get_string_ptr(NULL);
226 fatal("GSSAPI Error: \n%.400s",msg);
227 default:
228 packet_disconnect("Protocol error: didn't expect packet type %d",
229 type);
230 }
231 token_ptr = &recv_tok;
232 } else {
233 /* No data, and not complete */
234 if (maj_status != GSS_S_COMPLETE)
235 fatal("Not complete, and no token output");
236 }
237 } while (maj_status & GSS_S_CONTINUE_NEEDED);
238
239 /*
240 * We _must_ have received a COMPLETE message in reply from the
241 * server, which will have set dh_server_pub and msg_tok
242 */
243
244 if (type != SSH2_MSG_KEXGSS_COMPLETE)
245 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
246
247 /* Check f in range [1, p-1] */
248 if (!dh_pub_is_valid(dh, dh_server_pub))
249 packet_disconnect("bad server public DH value");
250
251 /* compute K=f^x mod p */
252 klen = DH_size(dh);
253 kbuf = xmalloc(klen);
254 kout = DH_compute_key(kbuf, dh_server_pub, dh);
255 if (kout < 0)
256 fatal("DH_compute_key: failed");
257
258 shared_secret = BN_new();
259 if (shared_secret == NULL)
260 fatal("kexgss_client: BN_new failed");
261
262 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
263 fatal("kexdh_client: BN_bin2bn failed");
264
265 memset(kbuf, 0, klen);
266 free(kbuf);
267
268 hashlen = sizeof(hash);
269 switch (ssh->kex->kex_type) {
270 case KEX_GSS_GRP1_SHA1:
271 case KEX_GSS_GRP14_SHA1:
272 kex_dh_hash(
273 ssh->kex->hash_alg,
274 ssh->kex->client_version_string,
275 ssh->kex->server_version_string,
276 buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my),
277 buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer),
278 (serverhostkey ? serverhostkey : empty), slen,
279 dh->pub_key, /* e */
280 dh_server_pub, /* f */
281 shared_secret, /* K */
282 hash, &hashlen
283 );
284 break;
285 case KEX_GSS_GEX_SHA1:
286 kexgex_hash(
287 ssh->kex->hash_alg,
288 ssh->kex->client_version_string,
289 ssh->kex->server_version_string,
290 buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my),
291 buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer),
292 (serverhostkey ? serverhostkey : empty), slen,
293 min, nbits, max,
294 dh->p, dh->g,
295 dh->pub_key,
296 dh_server_pub,
297 shared_secret,
298 hash, &hashlen
299 );
300 break;
301 default:
302 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
303 }
304
305 gssbuf.value = hash;
306 gssbuf.length = hashlen;
307
308 /* Verify that the hash matches the MIC we just got. */
309 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
310 packet_disconnect("Hash's MIC didn't verify");
311
312 free(msg_tok.value);
313
314 DH_free(dh);
315 free(serverhostkey);
316 BN_clear_free(dh_server_pub);
317
318 /* save session id */
319 if (ssh->kex->session_id == NULL) {
320 ssh->kex->session_id_len = hashlen;
321 ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
322 memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
323 }
324
325 if (ssh->kex->gss_deleg_creds)
326 ssh_gssapi_credentials_updated(ctxt);
327
328 if (gss_kex_context == NULL)
329 gss_kex_context = ctxt;
330 else
331 ssh_gssapi_delete_ctx(&ctxt);
332
333 kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
334 BN_clear_free(shared_secret);
335 return kex_send_newkeys(ssh);
336}
337
338#endif /* GSSAPI */
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..38ca082ba
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,295 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include <string.h>
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include "xmalloc.h"
35#include "buffer.h"
36#include "ssh2.h"
37#include "key.h"
38#include "cipher.h"
39#include "kex.h"
40#include "log.h"
41#include "packet.h"
42#include "dh.h"
43#include "ssh-gss.h"
44#include "monitor_wrap.h"
45#include "misc.h"
46#include "servconf.h"
47#include "digest.h"
48
49extern ServerOptions options;
50
51int
52kexgss_server(struct ssh *ssh)
53{
54 OM_uint32 maj_status, min_status;
55
56 /*
57 * Some GSSAPI implementations use the input value of ret_flags (an
58 * output variable) as a means of triggering mechanism specific
59 * features. Initializing it to zero avoids inadvertently
60 * activating this non-standard behaviour.
61 */
62
63 OM_uint32 ret_flags = 0;
64 gss_buffer_desc gssbuf, recv_tok, msg_tok;
65 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
66 Gssctxt *ctxt = NULL;
67 u_int slen, klen, kout;
68 u_char *kbuf;
69 DH *dh;
70 int min = -1, max = -1, nbits = -1;
71 BIGNUM *shared_secret = NULL;
72 BIGNUM *dh_client_pub = NULL;
73 int type = 0;
74 gss_OID oid;
75 char *mechs;
76 u_char hash[SSH_DIGEST_MAX_LENGTH];
77 size_t hashlen;
78
79 /* Initialise GSSAPI */
80
81 /* If we're rekeying, privsep means that some of the private structures
82 * in the GSSAPI code are no longer available. This kludges them back
83 * into life
84 */
85 if (!ssh_gssapi_oid_table_ok()) {
86 mechs = ssh_gssapi_server_mechanisms();
87 free(mechs);
88 }
89
90 debug2("%s: Identifying %s", __func__, ssh->kex->name);
91 oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type);
92 if (oid == GSS_C_NO_OID)
93 fatal("Unknown gssapi mechanism");
94
95 debug2("%s: Acquiring credentials", __func__);
96
97 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
98 fatal("Unable to acquire credentials for the server");
99
100 switch (ssh->kex->kex_type) {
101 case KEX_GSS_GRP1_SHA1:
102 dh = dh_new_group1();
103 break;
104 case KEX_GSS_GRP14_SHA1:
105 dh = dh_new_group14();
106 break;
107 case KEX_GSS_GEX_SHA1:
108 debug("Doing group exchange");
109 packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
110 min = packet_get_int();
111 nbits = packet_get_int();
112 max = packet_get_int();
113 packet_check_eom();
114 if (max < min || nbits < min || max < nbits)
115 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
116 min, nbits, max);
117 dh = PRIVSEP(choose_dh(MAX(DH_GRP_MIN, min),
118 nbits, MIN(DH_GRP_MAX, max)));
119 if (dh == NULL)
120 packet_disconnect("Protocol error: no matching group found");
121
122 packet_start(SSH2_MSG_KEXGSS_GROUP);
123 packet_put_bignum2(dh->p);
124 packet_put_bignum2(dh->g);
125 packet_send();
126
127 packet_write_wait();
128 break;
129 default:
130 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
131 }
132
133 dh_gen_key(dh, ssh->kex->we_need * 8);
134
135 do {
136 debug("Wait SSH2_MSG_GSSAPI_INIT");
137 type = packet_read();
138 switch(type) {
139 case SSH2_MSG_KEXGSS_INIT:
140 if (dh_client_pub != NULL)
141 fatal("Received KEXGSS_INIT after initialising");
142 recv_tok.value = packet_get_string(&slen);
143 recv_tok.length = slen;
144
145 if ((dh_client_pub = BN_new()) == NULL)
146 fatal("dh_client_pub == NULL");
147
148 packet_get_bignum2(dh_client_pub);
149
150 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
151 break;
152 case SSH2_MSG_KEXGSS_CONTINUE:
153 recv_tok.value = packet_get_string(&slen);
154 recv_tok.length = slen;
155 break;
156 default:
157 packet_disconnect(
158 "Protocol error: didn't expect packet type %d",
159 type);
160 }
161
162 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
163 &send_tok, &ret_flags));
164
165 free(recv_tok.value);
166
167 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
168 fatal("Zero length token output when incomplete");
169
170 if (dh_client_pub == NULL)
171 fatal("No client public key");
172
173 if (maj_status & GSS_S_CONTINUE_NEEDED) {
174 debug("Sending GSSAPI_CONTINUE");
175 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
176 packet_put_string(send_tok.value, send_tok.length);
177 packet_send();
178 gss_release_buffer(&min_status, &send_tok);
179 }
180 } while (maj_status & GSS_S_CONTINUE_NEEDED);
181
182 if (GSS_ERROR(maj_status)) {
183 if (send_tok.length > 0) {
184 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
185 packet_put_string(send_tok.value, send_tok.length);
186 packet_send();
187 }
188 fatal("accept_ctx died");
189 }
190
191 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
192 fatal("Mutual Authentication flag wasn't set");
193
194 if (!(ret_flags & GSS_C_INTEG_FLAG))
195 fatal("Integrity flag wasn't set");
196
197 if (!dh_pub_is_valid(dh, dh_client_pub))
198 packet_disconnect("bad client public DH value");
199
200 klen = DH_size(dh);
201 kbuf = xmalloc(klen);
202 kout = DH_compute_key(kbuf, dh_client_pub, dh);
203 if (kout < 0)
204 fatal("DH_compute_key: failed");
205
206 shared_secret = BN_new();
207 if (shared_secret == NULL)
208 fatal("kexgss_server: BN_new failed");
209
210 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
211 fatal("kexgss_server: BN_bin2bn failed");
212
213 memset(kbuf, 0, klen);
214 free(kbuf);
215
216 hashlen = sizeof(hash);
217 switch (ssh->kex->kex_type) {
218 case KEX_GSS_GRP1_SHA1:
219 case KEX_GSS_GRP14_SHA1:
220 kex_dh_hash(
221 ssh->kex->hash_alg,
222 ssh->kex->client_version_string, ssh->kex->server_version_string,
223 buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer),
224 buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my),
225 NULL, 0, /* Change this if we start sending host keys */
226 dh_client_pub, dh->pub_key, shared_secret,
227 hash, &hashlen
228 );
229 break;
230 case KEX_GSS_GEX_SHA1:
231 kexgex_hash(
232 ssh->kex->hash_alg,
233 ssh->kex->client_version_string, ssh->kex->server_version_string,
234 buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer),
235 buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my),
236 NULL, 0,
237 min, nbits, max,
238 dh->p, dh->g,
239 dh_client_pub,
240 dh->pub_key,
241 shared_secret,
242 hash, &hashlen
243 );
244 break;
245 default:
246 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
247 }
248
249 BN_clear_free(dh_client_pub);
250
251 if (ssh->kex->session_id == NULL) {
252 ssh->kex->session_id_len = hashlen;
253 ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
254 memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
255 }
256
257 gssbuf.value = hash;
258 gssbuf.length = hashlen;
259
260 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
261 fatal("Couldn't get MIC");
262
263 packet_start(SSH2_MSG_KEXGSS_COMPLETE);
264 packet_put_bignum2(dh->pub_key);
265 packet_put_string(msg_tok.value,msg_tok.length);
266
267 if (send_tok.length != 0) {
268 packet_put_char(1); /* true */
269 packet_put_string(send_tok.value, send_tok.length);
270 } else {
271 packet_put_char(0); /* false */
272 }
273 packet_send();
274
275 gss_release_buffer(&min_status, &send_tok);
276 gss_release_buffer(&min_status, &msg_tok);
277
278 if (gss_kex_context == NULL)
279 gss_kex_context = ctxt;
280 else
281 ssh_gssapi_delete_ctx(&ctxt);
282
283 DH_free(dh);
284
285 kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
286 BN_clear_free(shared_secret);
287 kex_send_newkeys(ssh);
288
289 /* If this was a rekey, then save out any delegated credentials we
290 * just exchanged. */
291 if (options.gss_store_rekey)
292 ssh_gssapi_rekey_creds();
293 return 0;
294}
295#endif /* GSSAPI */
diff --git a/monitor.c b/monitor.c
index cb57bd066..05bb48a8e 100644
--- a/monitor.c
+++ b/monitor.c
@@ -158,6 +158,8 @@ int mm_answer_gss_setup_ctx(int, Buffer *);
158int mm_answer_gss_accept_ctx(int, Buffer *); 158int mm_answer_gss_accept_ctx(int, Buffer *);
159int mm_answer_gss_userok(int, Buffer *); 159int mm_answer_gss_userok(int, Buffer *);
160int mm_answer_gss_checkmic(int, Buffer *); 160int mm_answer_gss_checkmic(int, Buffer *);
161int mm_answer_gss_sign(int, Buffer *);
162int mm_answer_gss_updatecreds(int, Buffer *);
161#endif 163#endif
162 164
163#ifdef SSH_AUDIT_EVENTS 165#ifdef SSH_AUDIT_EVENTS
@@ -235,11 +237,18 @@ struct mon_table mon_dispatch_proto20[] = {
235 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, 237 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
236 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, 238 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
237 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, 239 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
240 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
238#endif 241#endif
239 {0, 0, NULL} 242 {0, 0, NULL}
240}; 243};
241 244
242struct mon_table mon_dispatch_postauth20[] = { 245struct mon_table mon_dispatch_postauth20[] = {
246#ifdef GSSAPI
247 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
248 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
249 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
250 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
251#endif
243#ifdef WITH_OPENSSL 252#ifdef WITH_OPENSSL
244 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 253 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
245#endif 254#endif
@@ -354,6 +363,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
354 /* Permit requests for moduli and signatures */ 363 /* Permit requests for moduli and signatures */
355 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 364 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
356 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 365 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
366#ifdef GSSAPI
367 /* and for the GSSAPI key exchange */
368 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
369#endif
357 } else { 370 } else {
358 mon_dispatch = mon_dispatch_proto15; 371 mon_dispatch = mon_dispatch_proto15;
359 372
@@ -462,6 +475,10 @@ monitor_child_postauth(struct monitor *pmonitor)
462 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 475 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
463 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 476 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
464 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 477 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
478#ifdef GSSAPI
479 /* and for the GSSAPI key exchange */
480 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
481#endif
465 } else { 482 } else {
466 mon_dispatch = mon_dispatch_postauth15; 483 mon_dispatch = mon_dispatch_postauth15;
467 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 484 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
@@ -1876,6 +1893,13 @@ monitor_apply_keystate(struct monitor *pmonitor)
1876# endif 1893# endif
1877#endif /* WITH_OPENSSL */ 1894#endif /* WITH_OPENSSL */
1878 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 1895 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1896#ifdef GSSAPI
1897 if (options.gss_keyex) {
1898 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1899 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1900 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1901 }
1902#endif
1879 kex->load_host_public_key=&get_hostkey_public_by_type; 1903 kex->load_host_public_key=&get_hostkey_public_by_type;
1880 kex->load_host_private_key=&get_hostkey_private_by_type; 1904 kex->load_host_private_key=&get_hostkey_private_by_type;
1881 kex->host_key_index=&get_hostkey_index; 1905 kex->host_key_index=&get_hostkey_index;
@@ -1975,6 +1999,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m)
1975 OM_uint32 major; 1999 OM_uint32 major;
1976 u_int len; 2000 u_int len;
1977 2001
2002 if (!options.gss_authentication && !options.gss_keyex)
2003 fatal("In GSSAPI monitor when GSSAPI is disabled");
2004
1978 goid.elements = buffer_get_string(m, &len); 2005 goid.elements = buffer_get_string(m, &len);
1979 goid.length = len; 2006 goid.length = len;
1980 2007
@@ -2002,6 +2029,9 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
2002 OM_uint32 flags = 0; /* GSI needs this */ 2029 OM_uint32 flags = 0; /* GSI needs this */
2003 u_int len; 2030 u_int len;
2004 2031
2032 if (!options.gss_authentication && !options.gss_keyex)
2033 fatal("In GSSAPI monitor when GSSAPI is disabled");
2034
2005 in.value = buffer_get_string(m, &len); 2035 in.value = buffer_get_string(m, &len);
2006 in.length = len; 2036 in.length = len;
2007 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); 2037 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
@@ -2019,6 +2049,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
2019 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 2049 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2020 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 2050 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2021 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 2051 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2052 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2022 } 2053 }
2023 return (0); 2054 return (0);
2024} 2055}
@@ -2030,6 +2061,9 @@ mm_answer_gss_checkmic(int sock, Buffer *m)
2030 OM_uint32 ret; 2061 OM_uint32 ret;
2031 u_int len; 2062 u_int len;
2032 2063
2064 if (!options.gss_authentication && !options.gss_keyex)
2065 fatal("In GSSAPI monitor when GSSAPI is disabled");
2066
2033 gssbuf.value = buffer_get_string(m, &len); 2067 gssbuf.value = buffer_get_string(m, &len);
2034 gssbuf.length = len; 2068 gssbuf.length = len;
2035 mic.value = buffer_get_string(m, &len); 2069 mic.value = buffer_get_string(m, &len);
@@ -2056,7 +2090,11 @@ mm_answer_gss_userok(int sock, Buffer *m)
2056{ 2090{
2057 int authenticated; 2091 int authenticated;
2058 2092
2059 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 2093 if (!options.gss_authentication && !options.gss_keyex)
2094 fatal("In GSSAPI monitor when GSSAPI is disabled");
2095
2096 authenticated = authctxt->valid &&
2097 ssh_gssapi_userok(authctxt->user, authctxt->pw);
2060 2098
2061 buffer_clear(m); 2099 buffer_clear(m);
2062 buffer_put_int(m, authenticated); 2100 buffer_put_int(m, authenticated);
@@ -2069,5 +2107,73 @@ mm_answer_gss_userok(int sock, Buffer *m)
2069 /* Monitor loop will terminate if authenticated */ 2107 /* Monitor loop will terminate if authenticated */
2070 return (authenticated); 2108 return (authenticated);
2071} 2109}
2110
2111int
2112mm_answer_gss_sign(int socket, Buffer *m)
2113{
2114 gss_buffer_desc data;
2115 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2116 OM_uint32 major, minor;
2117 u_int len;
2118
2119 if (!options.gss_authentication && !options.gss_keyex)
2120 fatal("In GSSAPI monitor when GSSAPI is disabled");
2121
2122 data.value = buffer_get_string(m, &len);
2123 data.length = len;
2124 if (data.length != 20)
2125 fatal("%s: data length incorrect: %d", __func__,
2126 (int) data.length);
2127
2128 /* Save the session ID on the first time around */
2129 if (session_id2_len == 0) {
2130 session_id2_len = data.length;
2131 session_id2 = xmalloc(session_id2_len);
2132 memcpy(session_id2, data.value, session_id2_len);
2133 }
2134 major = ssh_gssapi_sign(gsscontext, &data, &hash);
2135
2136 free(data.value);
2137
2138 buffer_clear(m);
2139 buffer_put_int(m, major);
2140 buffer_put_string(m, hash.value, hash.length);
2141
2142 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2143
2144 gss_release_buffer(&minor, &hash);
2145
2146 /* Turn on getpwnam permissions */
2147 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2148
2149 /* And credential updating, for when rekeying */
2150 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2151
2152 return (0);
2153}
2154
2155int
2156mm_answer_gss_updatecreds(int socket, Buffer *m) {
2157 ssh_gssapi_ccache store;
2158 int ok;
2159
2160 store.filename = buffer_get_string(m, NULL);
2161 store.envvar = buffer_get_string(m, NULL);
2162 store.envval = buffer_get_string(m, NULL);
2163
2164 ok = ssh_gssapi_update_creds(&store);
2165
2166 free(store.filename);
2167 free(store.envvar);
2168 free(store.envval);
2169
2170 buffer_clear(m);
2171 buffer_put_int(m, ok);
2172
2173 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2174
2175 return(0);
2176}
2177
2072#endif /* GSSAPI */ 2178#endif /* GSSAPI */
2073 2179
diff --git a/monitor.h b/monitor.h
index 93b8b66dd..bc50ade1f 100644
--- a/monitor.h
+++ b/monitor.h
@@ -65,6 +65,9 @@ enum monitor_reqtype {
65 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, 65 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
66 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, 66 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
67 67
68 MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
69 MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
70
68}; 71};
69 72
70struct mm_master; 73struct mm_master;
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 99dc13b61..5a9f1b52d 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1073,7 +1073,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1073} 1073}
1074 1074
1075int 1075int
1076mm_ssh_gssapi_userok(char *user) 1076mm_ssh_gssapi_userok(char *user, struct passwd *pw)
1077{ 1077{
1078 Buffer m; 1078 Buffer m;
1079 int authenticated = 0; 1079 int authenticated = 0;
@@ -1090,5 +1090,50 @@ mm_ssh_gssapi_userok(char *user)
1090 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1090 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1091 return (authenticated); 1091 return (authenticated);
1092} 1092}
1093
1094OM_uint32
1095mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1096{
1097 Buffer m;
1098 OM_uint32 major;
1099 u_int len;
1100
1101 buffer_init(&m);
1102 buffer_put_string(&m, data->value, data->length);
1103
1104 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
1105 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
1106
1107 major = buffer_get_int(&m);
1108 hash->value = buffer_get_string(&m, &len);
1109 hash->length = len;
1110
1111 buffer_free(&m);
1112
1113 return(major);
1114}
1115
1116int
1117mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1118{
1119 Buffer m;
1120 int ok;
1121
1122 buffer_init(&m);
1123
1124 buffer_put_cstring(&m, store->filename ? store->filename : "");
1125 buffer_put_cstring(&m, store->envvar ? store->envvar : "");
1126 buffer_put_cstring(&m, store->envval ? store->envval : "");
1127
1128 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m);
1129 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m);
1130
1131 ok = buffer_get_int(&m);
1132
1133 buffer_free(&m);
1134
1135 return (ok);
1136}
1137
1093#endif /* GSSAPI */ 1138#endif /* GSSAPI */
1094 1139
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 9fd02b30c..b5414c298 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -60,8 +60,10 @@ BIGNUM *mm_auth_rsa_generate_challenge(Key *);
60OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 60OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
61OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, 61OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
62 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); 62 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
63int mm_ssh_gssapi_userok(char *user); 63int mm_ssh_gssapi_userok(char *user, struct passwd *);
64OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 64OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
65OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
66int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
65#endif 67#endif
66 68
67#ifdef USE_PAM 69#ifdef USE_PAM
diff --git a/readconf.c b/readconf.c
index c177202b1..e019195e7 100644
--- a/readconf.c
+++ b/readconf.c
@@ -160,6 +160,8 @@ typedef enum {
160 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 160 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
161 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 161 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
162 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 162 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
163 oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
164 oGssServerIdentity,
163 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 165 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
164 oSendEnv, oControlPath, oControlMaster, oControlPersist, 166 oSendEnv, oControlPath, oControlMaster, oControlPersist,
165 oHashKnownHosts, 167 oHashKnownHosts,
@@ -205,10 +207,19 @@ static struct {
205 { "afstokenpassing", oUnsupported }, 207 { "afstokenpassing", oUnsupported },
206#if defined(GSSAPI) 208#if defined(GSSAPI)
207 { "gssapiauthentication", oGssAuthentication }, 209 { "gssapiauthentication", oGssAuthentication },
210 { "gssapikeyexchange", oGssKeyEx },
208 { "gssapidelegatecredentials", oGssDelegateCreds }, 211 { "gssapidelegatecredentials", oGssDelegateCreds },
212 { "gssapitrustdns", oGssTrustDns },
213 { "gssapiclientidentity", oGssClientIdentity },
214 { "gssapiserveridentity", oGssServerIdentity },
215 { "gssapirenewalforcesrekey", oGssRenewalRekey },
209#else 216#else
210 { "gssapiauthentication", oUnsupported }, 217 { "gssapiauthentication", oUnsupported },
218 { "gssapikeyexchange", oUnsupported },
211 { "gssapidelegatecredentials", oUnsupported }, 219 { "gssapidelegatecredentials", oUnsupported },
220 { "gssapitrustdns", oUnsupported },
221 { "gssapiclientidentity", oUnsupported },
222 { "gssapirenewalforcesrekey", oUnsupported },
212#endif 223#endif
213 { "fallbacktorsh", oDeprecated }, 224 { "fallbacktorsh", oDeprecated },
214 { "usersh", oDeprecated }, 225 { "usersh", oDeprecated },
@@ -962,10 +973,30 @@ parse_time:
962 intptr = &options->gss_authentication; 973 intptr = &options->gss_authentication;
963 goto parse_flag; 974 goto parse_flag;
964 975
976 case oGssKeyEx:
977 intptr = &options->gss_keyex;
978 goto parse_flag;
979
965 case oGssDelegateCreds: 980 case oGssDelegateCreds:
966 intptr = &options->gss_deleg_creds; 981 intptr = &options->gss_deleg_creds;
967 goto parse_flag; 982 goto parse_flag;
968 983
984 case oGssTrustDns:
985 intptr = &options->gss_trust_dns;
986 goto parse_flag;
987
988 case oGssClientIdentity:
989 charptr = &options->gss_client_identity;
990 goto parse_string;
991
992 case oGssServerIdentity:
993 charptr = &options->gss_server_identity;
994 goto parse_string;
995
996 case oGssRenewalRekey:
997 intptr = &options->gss_renewal_rekey;
998 goto parse_flag;
999
969 case oBatchMode: 1000 case oBatchMode:
970 intptr = &options->batch_mode; 1001 intptr = &options->batch_mode;
971 goto parse_flag; 1002 goto parse_flag;
@@ -1777,7 +1808,12 @@ initialize_options(Options * options)
1777 options->pubkey_authentication = -1; 1808 options->pubkey_authentication = -1;
1778 options->challenge_response_authentication = -1; 1809 options->challenge_response_authentication = -1;
1779 options->gss_authentication = -1; 1810 options->gss_authentication = -1;
1811 options->gss_keyex = -1;
1780 options->gss_deleg_creds = -1; 1812 options->gss_deleg_creds = -1;
1813 options->gss_trust_dns = -1;
1814 options->gss_renewal_rekey = -1;
1815 options->gss_client_identity = NULL;
1816 options->gss_server_identity = NULL;
1781 options->password_authentication = -1; 1817 options->password_authentication = -1;
1782 options->kbd_interactive_authentication = -1; 1818 options->kbd_interactive_authentication = -1;
1783 options->kbd_interactive_devices = NULL; 1819 options->kbd_interactive_devices = NULL;
@@ -1921,8 +1957,14 @@ fill_default_options(Options * options)
1921 options->challenge_response_authentication = 1; 1957 options->challenge_response_authentication = 1;
1922 if (options->gss_authentication == -1) 1958 if (options->gss_authentication == -1)
1923 options->gss_authentication = 0; 1959 options->gss_authentication = 0;
1960 if (options->gss_keyex == -1)
1961 options->gss_keyex = 0;
1924 if (options->gss_deleg_creds == -1) 1962 if (options->gss_deleg_creds == -1)
1925 options->gss_deleg_creds = 0; 1963 options->gss_deleg_creds = 0;
1964 if (options->gss_trust_dns == -1)
1965 options->gss_trust_dns = 0;
1966 if (options->gss_renewal_rekey == -1)
1967 options->gss_renewal_rekey = 0;
1926 if (options->password_authentication == -1) 1968 if (options->password_authentication == -1)
1927 options->password_authentication = 1; 1969 options->password_authentication = 1;
1928 if (options->kbd_interactive_authentication == -1) 1970 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index cef55f71c..fd3d7c75d 100644
--- a/readconf.h
+++ b/readconf.h
@@ -45,7 +45,12 @@ typedef struct {
45 int challenge_response_authentication; 45 int challenge_response_authentication;
46 /* Try S/Key or TIS, authentication. */ 46 /* Try S/Key or TIS, authentication. */
47 int gss_authentication; /* Try GSS authentication */ 47 int gss_authentication; /* Try GSS authentication */
48 int gss_keyex; /* Try GSS key exchange */
48 int gss_deleg_creds; /* Delegate GSS credentials */ 49 int gss_deleg_creds; /* Delegate GSS credentials */
50 int gss_trust_dns; /* Trust DNS for GSS canonicalization */
51 int gss_renewal_rekey; /* Credential renewal forces rekey */
52 char *gss_client_identity; /* Principal to initiate GSSAPI with */
53 char *gss_server_identity; /* GSSAPI target principal */
49 int password_authentication; /* Try password 54 int password_authentication; /* Try password
50 * authentication. */ 55 * authentication. */
51 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 56 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
diff --git a/servconf.c b/servconf.c
index 873b0d02a..9b0628181 100644
--- a/servconf.c
+++ b/servconf.c
@@ -117,8 +117,10 @@ initialize_server_options(ServerOptions *options)
117 options->kerberos_ticket_cleanup = -1; 117 options->kerberos_ticket_cleanup = -1;
118 options->kerberos_get_afs_token = -1; 118 options->kerberos_get_afs_token = -1;
119 options->gss_authentication=-1; 119 options->gss_authentication=-1;
120 options->gss_keyex = -1;
120 options->gss_cleanup_creds = -1; 121 options->gss_cleanup_creds = -1;
121 options->gss_strict_acceptor = -1; 122 options->gss_strict_acceptor = -1;
123 options->gss_store_rekey = -1;
122 options->password_authentication = -1; 124 options->password_authentication = -1;
123 options->kbd_interactive_authentication = -1; 125 options->kbd_interactive_authentication = -1;
124 options->challenge_response_authentication = -1; 126 options->challenge_response_authentication = -1;
@@ -287,10 +289,14 @@ fill_default_server_options(ServerOptions *options)
287 options->kerberos_get_afs_token = 0; 289 options->kerberos_get_afs_token = 0;
288 if (options->gss_authentication == -1) 290 if (options->gss_authentication == -1)
289 options->gss_authentication = 0; 291 options->gss_authentication = 0;
292 if (options->gss_keyex == -1)
293 options->gss_keyex = 0;
290 if (options->gss_cleanup_creds == -1) 294 if (options->gss_cleanup_creds == -1)
291 options->gss_cleanup_creds = 1; 295 options->gss_cleanup_creds = 1;
292 if (options->gss_strict_acceptor == -1) 296 if (options->gss_strict_acceptor == -1)
293 options->gss_strict_acceptor = 0; 297 options->gss_strict_acceptor = 1;
298 if (options->gss_store_rekey == -1)
299 options->gss_store_rekey = 0;
294 if (options->password_authentication == -1) 300 if (options->password_authentication == -1)
295 options->password_authentication = 1; 301 options->password_authentication = 1;
296 if (options->kbd_interactive_authentication == -1) 302 if (options->kbd_interactive_authentication == -1)
@@ -427,6 +433,7 @@ typedef enum {
427 sHostKeyAlgorithms, 433 sHostKeyAlgorithms,
428 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 434 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
429 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 435 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
436 sGssKeyEx, sGssStoreRekey,
430 sAcceptEnv, sPermitTunnel, 437 sAcceptEnv, sPermitTunnel,
431 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 438 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
432 sUsePrivilegeSeparation, sAllowAgentForwarding, 439 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -500,12 +507,20 @@ static struct {
500#ifdef GSSAPI 507#ifdef GSSAPI
501 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 508 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
502 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 509 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
510 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
503 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 511 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
512 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
513 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
504#else 514#else
505 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 515 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
506 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 516 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
517 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
507 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 518 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
519 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
520 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
508#endif 521#endif
522 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
523 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
509 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 524 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
510 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 525 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
511 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 526 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1251,6 +1266,10 @@ process_server_config_line(ServerOptions *options, char *line,
1251 intptr = &options->gss_authentication; 1266 intptr = &options->gss_authentication;
1252 goto parse_flag; 1267 goto parse_flag;
1253 1268
1269 case sGssKeyEx:
1270 intptr = &options->gss_keyex;
1271 goto parse_flag;
1272
1254 case sGssCleanupCreds: 1273 case sGssCleanupCreds:
1255 intptr = &options->gss_cleanup_creds; 1274 intptr = &options->gss_cleanup_creds;
1256 goto parse_flag; 1275 goto parse_flag;
@@ -1259,6 +1278,10 @@ process_server_config_line(ServerOptions *options, char *line,
1259 intptr = &options->gss_strict_acceptor; 1278 intptr = &options->gss_strict_acceptor;
1260 goto parse_flag; 1279 goto parse_flag;
1261 1280
1281 case sGssStoreRekey:
1282 intptr = &options->gss_store_rekey;
1283 goto parse_flag;
1284
1262 case sPasswordAuthentication: 1285 case sPasswordAuthentication:
1263 intptr = &options->password_authentication; 1286 intptr = &options->password_authentication;
1264 goto parse_flag; 1287 goto parse_flag;
@@ -2308,7 +2331,10 @@ dump_config(ServerOptions *o)
2308#endif 2331#endif
2309#ifdef GSSAPI 2332#ifdef GSSAPI
2310 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2333 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2334 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2311 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2335 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2336 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2337 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2312#endif 2338#endif
2313 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2339 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2314 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2340 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index f4137af7d..778ba1742 100644
--- a/servconf.h
+++ b/servconf.h
@@ -118,8 +118,10 @@ typedef struct {
118 int kerberos_get_afs_token; /* If true, try to get AFS token if 118 int kerberos_get_afs_token; /* If true, try to get AFS token if
119 * authenticated with Kerberos. */ 119 * authenticated with Kerberos. */
120 int gss_authentication; /* If true, permit GSSAPI authentication */ 120 int gss_authentication; /* If true, permit GSSAPI authentication */
121 int gss_keyex; /* If true, permit GSSAPI key exchange */
121 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 122 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
122 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 123 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
124 int gss_store_rekey;
123 int password_authentication; /* If true, permit password 125 int password_authentication; /* If true, permit password
124 * authentication. */ 126 * authentication. */
125 int kbd_interactive_authentication; /* If true, permit */ 127 int kbd_interactive_authentication; /* If true, permit */
diff --git a/ssh-gss.h b/ssh-gss.h
index a99d7f08b..914701bcf 100644
--- a/ssh-gss.h
+++ b/ssh-gss.h
@@ -1,6 +1,6 @@
1/* $OpenBSD: ssh-gss.h,v 1.11 2014/02/26 20:28:44 djm Exp $ */ 1/* $OpenBSD: ssh-gss.h,v 1.11 2014/02/26 20:28:44 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 3 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
@@ -61,10 +61,22 @@
61 61
62#define SSH_GSS_OIDTYPE 0x06 62#define SSH_GSS_OIDTYPE 0x06
63 63
64#define SSH2_MSG_KEXGSS_INIT 30
65#define SSH2_MSG_KEXGSS_CONTINUE 31
66#define SSH2_MSG_KEXGSS_COMPLETE 32
67#define SSH2_MSG_KEXGSS_HOSTKEY 33
68#define SSH2_MSG_KEXGSS_ERROR 34
69#define SSH2_MSG_KEXGSS_GROUPREQ 40
70#define SSH2_MSG_KEXGSS_GROUP 41
71#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
72#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-"
73#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
74
64typedef struct { 75typedef struct {
65 char *filename; 76 char *filename;
66 char *envvar; 77 char *envvar;
67 char *envval; 78 char *envval;
79 struct passwd *owner;
68 void *data; 80 void *data;
69} ssh_gssapi_ccache; 81} ssh_gssapi_ccache;
70 82
@@ -72,8 +84,11 @@ typedef struct {
72 gss_buffer_desc displayname; 84 gss_buffer_desc displayname;
73 gss_buffer_desc exportedname; 85 gss_buffer_desc exportedname;
74 gss_cred_id_t creds; 86 gss_cred_id_t creds;
87 gss_name_t name;
75 struct ssh_gssapi_mech_struct *mech; 88 struct ssh_gssapi_mech_struct *mech;
76 ssh_gssapi_ccache store; 89 ssh_gssapi_ccache store;
90 int used;
91 int updated;
77} ssh_gssapi_client; 92} ssh_gssapi_client;
78 93
79typedef struct ssh_gssapi_mech_struct { 94typedef struct ssh_gssapi_mech_struct {
@@ -84,6 +99,7 @@ typedef struct ssh_gssapi_mech_struct {
84 int (*userok) (ssh_gssapi_client *, char *); 99 int (*userok) (ssh_gssapi_client *, char *);
85 int (*localname) (ssh_gssapi_client *, char **); 100 int (*localname) (ssh_gssapi_client *, char **);
86 void (*storecreds) (ssh_gssapi_client *); 101 void (*storecreds) (ssh_gssapi_client *);
102 int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
87} ssh_gssapi_mech; 103} ssh_gssapi_mech;
88 104
89typedef struct { 105typedef struct {
@@ -94,10 +110,11 @@ typedef struct {
94 gss_OID oid; /* client */ 110 gss_OID oid; /* client */
95 gss_cred_id_t creds; /* server */ 111 gss_cred_id_t creds; /* server */
96 gss_name_t client; /* server */ 112 gss_name_t client; /* server */
97 gss_cred_id_t client_creds; /* server */ 113 gss_cred_id_t client_creds; /* both */
98} Gssctxt; 114} Gssctxt;
99 115
100extern ssh_gssapi_mech *supported_mechs[]; 116extern ssh_gssapi_mech *supported_mechs[];
117extern Gssctxt *gss_kex_context;
101 118
102int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); 119int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
103void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); 120void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
@@ -119,16 +136,32 @@ void ssh_gssapi_build_ctx(Gssctxt **);
119void ssh_gssapi_delete_ctx(Gssctxt **); 136void ssh_gssapi_delete_ctx(Gssctxt **);
120OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); 137OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
121void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); 138void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *);
122int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); 139int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
140OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
141int ssh_gssapi_credentials_updated(Gssctxt *);
123 142
124/* In the server */ 143/* In the server */
144typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
145 const char *);
146char *ssh_gssapi_client_mechanisms(const char *, const char *);
147char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
148 const char *);
149gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
150int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
151 const char *);
125OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 152OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
126int ssh_gssapi_userok(char *name); 153int ssh_gssapi_userok(char *name, struct passwd *);
127OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 154OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
128void ssh_gssapi_do_child(char ***, u_int *); 155void ssh_gssapi_do_child(char ***, u_int *);
129void ssh_gssapi_cleanup_creds(void); 156void ssh_gssapi_cleanup_creds(void);
130void ssh_gssapi_storecreds(void); 157void ssh_gssapi_storecreds(void);
131 158
159char *ssh_gssapi_server_mechanisms(void);
160int ssh_gssapi_oid_table_ok(void);
161
162int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
163void ssh_gssapi_rekey_creds(void);
164
132#endif /* GSSAPI */ 165#endif /* GSSAPI */
133 166
134#endif /* _SSH_GSS_H */ 167#endif /* _SSH_GSS_H */
diff --git a/ssh_config b/ssh_config
index 90fb63f0b..4e879cd20 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 7630e7bcb..707d0e1c3 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -826,10 +826,42 @@ The default is
826Specifies whether user authentication based on GSSAPI is allowed. 826Specifies whether user authentication based on GSSAPI is allowed.
827The default is 827The default is
828.Dq no . 828.Dq no .
829.It Cm GSSAPIKeyExchange
830Specifies whether key exchange based on GSSAPI may be used. When using
831GSSAPI key exchange the server need not have a host key.
832The default is
833.Dq no .
834.It Cm GSSAPIClientIdentity
835If set, specifies the GSSAPI client identity that ssh should use when
836connecting to the server. The default is unset, which means that the default
837identity will be used.
838.It Cm GSSAPIServerIdentity
839If set, specifies the GSSAPI server identity that ssh should expect when
840connecting to the server. The default is unset, which means that the
841expected GSSAPI server identity will be determined from the target
842hostname.
829.It Cm GSSAPIDelegateCredentials 843.It Cm GSSAPIDelegateCredentials
830Forward (delegate) credentials to the server. 844Forward (delegate) credentials to the server.
831The default is 845The default is
832.Dq no . 846.Dq no .
847.It Cm GSSAPIRenewalForcesRekey
848If set to
849.Dq yes
850then renewal of the client's GSSAPI credentials will force the rekeying of the
851ssh connection. With a compatible server, this can delegate the renewed
852credentials to a session on the server.
853The default is
854.Dq no .
855.It Cm GSSAPITrustDns
856Set to
857.Dq yes
858to indicate that the DNS is trusted to securely canonicalize
859the name of the host being connected to. If
860.Dq no ,
861the hostname entered on the
862command line will be passed untouched to the GSSAPI library.
863The default is
864.Dq no .
833.It Cm HashKnownHosts 865.It Cm HashKnownHosts
834Indicates that 866Indicates that
835.Xr ssh 1 867.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index fae8b0f2c..34b9d304e 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -162,6 +162,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
162 struct kex *kex; 162 struct kex *kex;
163 int r; 163 int r;
164 164
165#ifdef GSSAPI
166 char *orig = NULL, *gss = NULL;
167 char *gss_host = NULL;
168#endif
169
165 xxx_host = host; 170 xxx_host = host;
166 xxx_hostaddr = hostaddr; 171 xxx_hostaddr = hostaddr;
167 172
@@ -192,6 +197,36 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
192 order_hostkeyalgs(host, hostaddr, port)); 197 order_hostkeyalgs(host, hostaddr, port));
193 } 198 }
194 199
200#ifdef GSSAPI
201 if (options.gss_keyex) {
202 /* Add the GSSAPI mechanisms currently supported on this
203 * client to the key exchange algorithm proposal */
204 orig = myproposal[PROPOSAL_KEX_ALGS];
205
206 if (options.gss_server_identity)
207 gss_host = xstrdup(options.gss_server_identity);
208 else if (options.gss_trust_dns)
209 gss_host = remote_hostname(active_state);
210 else
211 gss_host = xstrdup(host);
212
213 gss = ssh_gssapi_client_mechanisms(gss_host,
214 options.gss_client_identity);
215 if (gss) {
216 debug("Offering GSSAPI proposal: %s", gss);
217 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
218 "%s,%s", gss, orig);
219
220 /* If we've got GSSAPI algorithms, then we also
221 * support the 'null' hostkey, as a last resort */
222 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
223 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
224 "%s,null", orig);
225 free(gss);
226 }
227 }
228#endif
229
195 if (options.rekey_limit || options.rekey_interval) 230 if (options.rekey_limit || options.rekey_interval)
196 packet_set_rekey_limits((u_int32_t)options.rekey_limit, 231 packet_set_rekey_limits((u_int32_t)options.rekey_limit,
197 (time_t)options.rekey_interval); 232 (time_t)options.rekey_interval);
@@ -213,10 +248,26 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
213# endif 248# endif
214#endif 249#endif
215 kex->kex[KEX_C25519_SHA256] = kexc25519_client; 250 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
251#ifdef GSSAPI
252 if (options.gss_keyex) {
253 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
254 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
255 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
256 }
257#endif
216 kex->client_version_string=client_version_string; 258 kex->client_version_string=client_version_string;
217 kex->server_version_string=server_version_string; 259 kex->server_version_string=server_version_string;
218 kex->verify_host_key=&verify_host_key_callback; 260 kex->verify_host_key=&verify_host_key_callback;
219 261
262#ifdef GSSAPI
263 if (options.gss_keyex) {
264 kex->gss_deleg_creds = options.gss_deleg_creds;
265 kex->gss_trust_dns = options.gss_trust_dns;
266 kex->gss_client = options.gss_client_identity;
267 kex->gss_host = gss_host;
268 }
269#endif
270
220 dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); 271 dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
221 272
222 /* remove ext-info from the KEX proposals for rekeying */ 273 /* remove ext-info from the KEX proposals for rekeying */
@@ -311,6 +362,7 @@ int input_gssapi_token(int type, u_int32_t, void *);
311int input_gssapi_hash(int type, u_int32_t, void *); 362int input_gssapi_hash(int type, u_int32_t, void *);
312int input_gssapi_error(int, u_int32_t, void *); 363int input_gssapi_error(int, u_int32_t, void *);
313int input_gssapi_errtok(int, u_int32_t, void *); 364int input_gssapi_errtok(int, u_int32_t, void *);
365int userauth_gsskeyex(Authctxt *authctxt);
314#endif 366#endif
315 367
316void userauth(Authctxt *, char *); 368void userauth(Authctxt *, char *);
@@ -326,6 +378,11 @@ static char *authmethods_get(void);
326 378
327Authmethod authmethods[] = { 379Authmethod authmethods[] = {
328#ifdef GSSAPI 380#ifdef GSSAPI
381 {"gssapi-keyex",
382 userauth_gsskeyex,
383 NULL,
384 &options.gss_authentication,
385 NULL},
329 {"gssapi-with-mic", 386 {"gssapi-with-mic",
330 userauth_gssapi, 387 userauth_gssapi,
331 NULL, 388 NULL,
@@ -650,25 +707,40 @@ userauth_gssapi(Authctxt *authctxt)
650 static u_int mech = 0; 707 static u_int mech = 0;
651 OM_uint32 min; 708 OM_uint32 min;
652 int ok = 0; 709 int ok = 0;
710 char *gss_host;
711
712 if (options.gss_server_identity)
713 gss_host = xstrdup(options.gss_server_identity);
714 else if (options.gss_trust_dns)
715 gss_host = remote_hostname(active_state);
716 else
717 gss_host = xstrdup(authctxt->host);
653 718
654 /* Try one GSSAPI method at a time, rather than sending them all at 719 /* Try one GSSAPI method at a time, rather than sending them all at
655 * once. */ 720 * once. */
656 721
657 if (gss_supported == NULL) 722 if (gss_supported == NULL)
658 gss_indicate_mechs(&min, &gss_supported); 723 if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
724 gss_supported = NULL;
725 free(gss_host);
726 return 0;
727 }
659 728
660 /* Check to see if the mechanism is usable before we offer it */ 729 /* Check to see if the mechanism is usable before we offer it */
661 while (mech < gss_supported->count && !ok) { 730 while (mech < gss_supported->count && !ok) {
662 /* My DER encoding requires length<128 */ 731 /* My DER encoding requires length<128 */
663 if (gss_supported->elements[mech].length < 128 && 732 if (gss_supported->elements[mech].length < 128 &&
664 ssh_gssapi_check_mechanism(&gssctxt, 733 ssh_gssapi_check_mechanism(&gssctxt,
665 &gss_supported->elements[mech], authctxt->host)) { 734 &gss_supported->elements[mech], gss_host,
735 options.gss_client_identity)) {
666 ok = 1; /* Mechanism works */ 736 ok = 1; /* Mechanism works */
667 } else { 737 } else {
668 mech++; 738 mech++;
669 } 739 }
670 } 740 }
671 741
742 free(gss_host);
743
672 if (!ok) 744 if (!ok)
673 return 0; 745 return 0;
674 746
@@ -759,8 +831,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt)
759{ 831{
760 Authctxt *authctxt = ctxt; 832 Authctxt *authctxt = ctxt;
761 Gssctxt *gssctxt; 833 Gssctxt *gssctxt;
762 int oidlen; 834 u_int oidlen;
763 char *oidv; 835 u_char *oidv;
764 836
765 if (authctxt == NULL) 837 if (authctxt == NULL)
766 fatal("input_gssapi_response: no authentication context"); 838 fatal("input_gssapi_response: no authentication context");
@@ -873,6 +945,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt)
873 free(lang); 945 free(lang);
874 return 0; 946 return 0;
875} 947}
948
949int
950userauth_gsskeyex(Authctxt *authctxt)
951{
952 Buffer b;
953 gss_buffer_desc gssbuf;
954 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
955 OM_uint32 ms;
956
957 static int attempt = 0;
958 if (attempt++ >= 1)
959 return (0);
960
961 if (gss_kex_context == NULL) {
962 debug("No valid Key exchange context");
963 return (0);
964 }
965
966 ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
967 "gssapi-keyex");
968
969 gssbuf.value = buffer_ptr(&b);
970 gssbuf.length = buffer_len(&b);
971
972 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
973 buffer_free(&b);
974 return (0);
975 }
976
977 packet_start(SSH2_MSG_USERAUTH_REQUEST);
978 packet_put_cstring(authctxt->server_user);
979 packet_put_cstring(authctxt->service);
980 packet_put_cstring(authctxt->method->name);
981 packet_put_string(mic.value, mic.length);
982 packet_send();
983
984 buffer_free(&b);
985 gss_release_buffer(&ms, &mic);
986
987 return (1);
988}
989
876#endif /* GSSAPI */ 990#endif /* GSSAPI */
877 991
878int 992int
diff --git a/sshd.c b/sshd.c
index 799c7711f..ebb88c776 100644
--- a/sshd.c
+++ b/sshd.c
@@ -125,6 +125,10 @@
125#include "version.h" 125#include "version.h"
126#include "ssherr.h" 126#include "ssherr.h"
127 127
128#ifdef USE_SECURITY_SESSION_API
129#include <Security/AuthSession.h>
130#endif
131
128#ifndef O_NOCTTY 132#ifndef O_NOCTTY
129#define O_NOCTTY 0 133#define O_NOCTTY 0
130#endif 134#endif
@@ -1892,10 +1896,13 @@ main(int ac, char **av)
1892 logit("Disabling protocol version 1. Could not load host key"); 1896 logit("Disabling protocol version 1. Could not load host key");
1893 options.protocol &= ~SSH_PROTO_1; 1897 options.protocol &= ~SSH_PROTO_1;
1894 } 1898 }
1899#ifndef GSSAPI
1900 /* The GSSAPI key exchange can run without a host key */
1895 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { 1901 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
1896 logit("Disabling protocol version 2. Could not load host key"); 1902 logit("Disabling protocol version 2. Could not load host key");
1897 options.protocol &= ~SSH_PROTO_2; 1903 options.protocol &= ~SSH_PROTO_2;
1898 } 1904 }
1905#endif
1899 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { 1906 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
1900 logit("sshd: no hostkeys available -- exiting."); 1907 logit("sshd: no hostkeys available -- exiting.");
1901 exit(1); 1908 exit(1);
@@ -2207,6 +2214,60 @@ main(int ac, char **av)
2207 remote_ip, remote_port, laddr, ssh_local_port(ssh)); 2214 remote_ip, remote_port, laddr, ssh_local_port(ssh));
2208 free(laddr); 2215 free(laddr);
2209 2216
2217#ifdef USE_SECURITY_SESSION_API
2218 /*
2219 * Create a new security session for use by the new user login if
2220 * the current session is the root session or we are not launched
2221 * by inetd (eg: debugging mode or server mode). We do not
2222 * necessarily need to create a session if we are launched from
2223 * inetd because Panther xinetd will create a session for us.
2224 *
2225 * The only case where this logic will fail is if there is an
2226 * inetd running in a non-root session which is not creating
2227 * new sessions for us. Then all the users will end up in the
2228 * same session (bad).
2229 *
2230 * When the client exits, the session will be destroyed for us
2231 * automatically.
2232 *
2233 * We must create the session before any credentials are stored
2234 * (including AFS pags, which happens a few lines below).
2235 */
2236 {
2237 OSStatus err = 0;
2238 SecuritySessionId sid = 0;
2239 SessionAttributeBits sattrs = 0;
2240
2241 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2242 if (err)
2243 error("SessionGetInfo() failed with error %.8X",
2244 (unsigned) err);
2245 else
2246 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2247 (unsigned) sid, (unsigned) sattrs);
2248
2249 if (inetd_flag && !(sattrs & sessionIsRoot))
2250 debug("Running in inetd mode in a non-root session... "
2251 "assuming inetd created the session for us.");
2252 else {
2253 debug("Creating new security session...");
2254 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2255 if (err)
2256 error("SessionCreate() failed with error %.8X",
2257 (unsigned) err);
2258
2259 err = SessionGetInfo(callerSecuritySession, &sid,
2260 &sattrs);
2261 if (err)
2262 error("SessionGetInfo() failed with error %.8X",
2263 (unsigned) err);
2264 else
2265 debug("New Session ID is %.8X / Session Attributes are %.8X",
2266 (unsigned) sid, (unsigned) sattrs);
2267 }
2268 }
2269#endif
2270
2210 /* 2271 /*
2211 * We don't want to listen forever unless the other side 2272 * We don't want to listen forever unless the other side
2212 * successfully authenticates itself. So we set up an alarm which is 2273 * successfully authenticates itself. So we set up an alarm which is
@@ -2631,6 +2692,48 @@ do_ssh2_kex(void)
2631 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2692 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2632 list_hostkey_types()); 2693 list_hostkey_types());
2633 2694
2695#ifdef GSSAPI
2696 {
2697 char *orig;
2698 char *gss = NULL;
2699 char *newstr = NULL;
2700 orig = myproposal[PROPOSAL_KEX_ALGS];
2701
2702 /*
2703 * If we don't have a host key, then there's no point advertising
2704 * the other key exchange algorithms
2705 */
2706
2707 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2708 orig = NULL;
2709
2710 if (options.gss_keyex)
2711 gss = ssh_gssapi_server_mechanisms();
2712 else
2713 gss = NULL;
2714
2715 if (gss && orig)
2716 xasprintf(&newstr, "%s,%s", gss, orig);
2717 else if (gss)
2718 newstr = gss;
2719 else if (orig)
2720 newstr = orig;
2721
2722 /*
2723 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2724 * key alg, but we can't tell people about it unless its the only
2725 * host key algorithm we support
2726 */
2727 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2728 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2729
2730 if (newstr)
2731 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2732 else
2733 fatal("No supported key exchange algorithms");
2734 }
2735#endif
2736
2634 /* start key exchange */ 2737 /* start key exchange */
2635 if ((r = kex_setup(active_state, myproposal)) != 0) 2738 if ((r = kex_setup(active_state, myproposal)) != 0)
2636 fatal("kex_setup: %s", ssh_err(r)); 2739 fatal("kex_setup: %s", ssh_err(r));
@@ -2648,6 +2751,13 @@ do_ssh2_kex(void)
2648# endif 2751# endif
2649#endif 2752#endif
2650 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2753 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2754#ifdef GSSAPI
2755 if (options.gss_keyex) {
2756 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2757 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2758 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2759 }
2760#endif
2651 kex->server = 1; 2761 kex->server = 1;
2652 kex->client_version_string=client_version_string; 2762 kex->client_version_string=client_version_string;
2653 kex->server_version_string=server_version_string; 2763 kex->server_version_string=server_version_string;
diff --git a/sshd_config b/sshd_config
index 75ae8e739..3fe3e017e 100644
--- a/sshd_config
+++ b/sshd_config
@@ -83,6 +83,8 @@ AuthorizedKeysFile .ssh/authorized_keys
83# GSSAPI options 83# GSSAPI options
84#GSSAPIAuthentication no 84#GSSAPIAuthentication no
85#GSSAPICleanupCredentials yes 85#GSSAPICleanupCredentials yes
86#GSSAPIStrictAcceptorCheck yes
87#GSSAPIKeyExchange no
86 88
87# Set this to 'yes' to enable PAM authentication, account processing, 89# Set this to 'yes' to enable PAM authentication, account processing,
88# and session processing. If this is enabled, PAM authentication will 90# and session processing. If this is enabled, PAM authentication will
diff --git a/sshd_config.5 b/sshd_config.5
index 1bc26ec4d..3b4cba9ae 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -632,6 +632,11 @@ The default is
632Specifies whether user authentication based on GSSAPI is allowed. 632Specifies whether user authentication based on GSSAPI is allowed.
633The default is 633The default is
634.Dq no . 634.Dq no .
635.It Cm GSSAPIKeyExchange
636Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
637doesn't rely on ssh keys to verify host identity.
638The default is
639.Dq no .
635.It Cm GSSAPICleanupCredentials 640.It Cm GSSAPICleanupCredentials
636Specifies whether to automatically destroy the user's credentials cache 641Specifies whether to automatically destroy the user's credentials cache
637on logout. 642on logout.
@@ -652,6 +657,11 @@ machine's default store.
652This facility is provided to assist with operation on multi homed machines. 657This facility is provided to assist with operation on multi homed machines.
653The default is 658The default is
654.Dq yes . 659.Dq yes .
660.It Cm GSSAPIStoreCredentialsOnRekey
661Controls whether the user's GSSAPI credentials should be updated following a
662successful connection rekeying. This option can be used to accepted renewed
663or updated credentials from a compatible client. The default is
664.Dq no .
655.It Cm HostbasedAcceptedKeyTypes 665.It Cm HostbasedAcceptedKeyTypes
656Specifies the key types that will be accepted for hostbased authentication 666Specifies the key types that will be accepted for hostbased authentication
657as a comma-separated pattern list. 667as a comma-separated pattern list.
diff --git a/sshkey.c b/sshkey.c
index c9f04cd67..558bbbef6 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -115,6 +115,7 @@ static const struct keytype keytypes[] = {
115# endif /* OPENSSL_HAS_NISTP521 */ 115# endif /* OPENSSL_HAS_NISTP521 */
116# endif /* OPENSSL_HAS_ECC */ 116# endif /* OPENSSL_HAS_ECC */
117#endif /* WITH_OPENSSL */ 117#endif /* WITH_OPENSSL */
118 { "null", "null", KEY_NULL, 0, 0, 0 },
118 { NULL, NULL, -1, -1, 0, 0 } 119 { NULL, NULL, -1, -1, 0, 0 }
119}; 120};
120 121
@@ -203,7 +204,7 @@ key_alg_list(int certs_only, int plain_only)
203 const struct keytype *kt; 204 const struct keytype *kt;
204 205
205 for (kt = keytypes; kt->type != -1; kt++) { 206 for (kt = keytypes; kt->type != -1; kt++) {
206 if (kt->name == NULL || kt->sigonly) 207 if (kt->name == NULL || kt->sigonly || kt->type == KEY_NULL)
207 continue; 208 continue;
208 if ((certs_only && !kt->cert) || (plain_only && kt->cert)) 209 if ((certs_only && !kt->cert) || (plain_only && kt->cert))
209 continue; 210 continue;
diff --git a/sshkey.h b/sshkey.h
index 8c3d866bf..e0caa37a7 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -62,6 +62,7 @@ enum sshkey_types {
62 KEY_DSA_CERT, 62 KEY_DSA_CERT,
63 KEY_ECDSA_CERT, 63 KEY_ECDSA_CERT,
64 KEY_ED25519_CERT, 64 KEY_ED25519_CERT,
65 KEY_NULL,
65 KEY_UNSPEC 66 KEY_UNSPEC
66}; 67};
67 68