summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2017-10-04 13:54:43 +0100
commit4e70490950e5c5134df48848affaf73685bf0284 (patch)
tree59de097e770693fb1f81268e85f7802df32cb58e
parent62f54f20bf351468e0124f63cc2902ee40d9b0e9 (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: 2017-10-04 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.c49
-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.c184
-rw-r--r--kex.c19
-rw-r--r--kex.h14
-rw-r--r--kexgssc.c338
-rw-r--r--kexgsss.c295
-rw-r--r--monitor.c115
-rw-r--r--monitor.h3
-rw-r--r--monitor_wrap.c47
-rw-r--r--monitor_wrap.h4
-rw-r--r--readconf.c43
-rw-r--r--readconf.h5
-rw-r--r--servconf.c26
-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.c131
-rw-r--r--sshd.c112
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.510
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
35 files changed, 2063 insertions, 147 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 c52ce191f..f6e9fe4cd 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 platform-misc.o 96 platform-pledge.o platform-tracing.o platform-misc.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 \
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.o monitor_wrap.o auth-krb5.o \ 108 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 a44906174..6aec36052 100644
--- a/auth.c
+++ b/auth.c
@@ -395,7 +395,8 @@ auth_root_allowed(const char *method)
395 case PERMIT_NO_PASSWD: 395 case PERMIT_NO_PASSWD:
396 if (strcmp(method, "publickey") == 0 || 396 if (strcmp(method, "publickey") == 0 ||
397 strcmp(method, "hostbased") == 0 || 397 strcmp(method, "hostbased") == 0 ||
398 strcmp(method, "gssapi-with-mic") == 0) 398 strcmp(method, "gssapi-with-mic") == 0 ||
399 strcmp(method, "gssapi-keyex") == 0)
399 return 1; 400 return 1;
400 break; 401 break;
401 case PERMIT_FORCED_ONLY: 402 case PERMIT_FORCED_ONLY:
@@ -728,99 +729,6 @@ fakepw(void)
728} 729}
729 730
730/* 731/*
731 * Returns the remote DNS hostname as a string. The returned string must not
732 * be freed. NB. this will usually trigger a DNS query the first time it is
733 * called.
734 * This function does additional checks on the hostname to mitigate some
735 * attacks on legacy rhosts-style authentication.
736 * XXX is RhostsRSAAuthentication vulnerable to these?
737 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
738 */
739
740static char *
741remote_hostname(struct ssh *ssh)
742{
743 struct sockaddr_storage from;
744 socklen_t fromlen;
745 struct addrinfo hints, *ai, *aitop;
746 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
747 const char *ntop = ssh_remote_ipaddr(ssh);
748
749 /* Get IP address of client. */
750 fromlen = sizeof(from);
751 memset(&from, 0, sizeof(from));
752 if (getpeername(ssh_packet_get_connection_in(ssh),
753 (struct sockaddr *)&from, &fromlen) < 0) {
754 debug("getpeername failed: %.100s", strerror(errno));
755 return strdup(ntop);
756 }
757
758 ipv64_normalise_mapped(&from, &fromlen);
759 if (from.ss_family == AF_INET6)
760 fromlen = sizeof(struct sockaddr_in6);
761
762 debug3("Trying to reverse map address %.100s.", ntop);
763 /* Map the IP address to a host name. */
764 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
765 NULL, 0, NI_NAMEREQD) != 0) {
766 /* Host name not found. Use ip address. */
767 return strdup(ntop);
768 }
769
770 /*
771 * if reverse lookup result looks like a numeric hostname,
772 * someone is trying to trick us by PTR record like following:
773 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
774 */
775 memset(&hints, 0, sizeof(hints));
776 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
777 hints.ai_flags = AI_NUMERICHOST;
778 if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
779 logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
780 name, ntop);
781 freeaddrinfo(ai);
782 return strdup(ntop);
783 }
784
785 /* Names are stored in lowercase. */
786 lowercase(name);
787
788 /*
789 * Map it back to an IP address and check that the given
790 * address actually is an address of this host. This is
791 * necessary because anyone with access to a name server can
792 * define arbitrary names for an IP address. Mapping from
793 * name to IP address can be trusted better (but can still be
794 * fooled if the intruder has access to the name server of
795 * the domain).
796 */
797 memset(&hints, 0, sizeof(hints));
798 hints.ai_family = from.ss_family;
799 hints.ai_socktype = SOCK_STREAM;
800 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
801 logit("reverse mapping checking getaddrinfo for %.700s "
802 "[%s] failed.", name, ntop);
803 return strdup(ntop);
804 }
805 /* Look for the address from the list of addresses. */
806 for (ai = aitop; ai; ai = ai->ai_next) {
807 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
808 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
809 (strcmp(ntop, ntop2) == 0))
810 break;
811 }
812 freeaddrinfo(aitop);
813 /* If we reached the end of the list, the address was not there. */
814 if (ai == NULL) {
815 /* Address not found for the host name. */
816 logit("Address %.100s maps to %.600s, but this does not "
817 "map back to the address.", ntop, name);
818 return strdup(ntop);
819 }
820 return strdup(name);
821}
822
823/*
824 * Return the canonical name of the host in the other side of the current 732 * Return the canonical name of the host in the other side of the current
825 * connection. The host name is cached, so it is efficient to call this 733 * connection. The host name is cached, so it is efficient to call this
826 * several times. 734 * several times.
diff --git a/auth2-gss.c b/auth2-gss.c
index 589283b72..fd411d3a7 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: auth2-gss.c,v 1.26 2017/06/24 06:34:38 djm Exp $ */ 1/* $OpenBSD: auth2-gss.c,v 1.26 2017/06/24 06:34:38 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -53,6 +53,41 @@ static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
53static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); 53static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
54static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 54static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
55 55
56/*
57 * The 'gssapi_keyex' userauth mechanism.
58 */
59static int
60userauth_gsskeyex(struct ssh *ssh)
61{
62 Authctxt *authctxt = ssh->authctxt;
63 int authenticated = 0;
64 Buffer b;
65 gss_buffer_desc mic, gssbuf;
66 u_int len;
67
68 mic.value = packet_get_string(&len);
69 mic.length = len;
70
71 packet_check_eom();
72
73 ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
74 "gssapi-keyex");
75
76 gssbuf.value = buffer_ptr(&b);
77 gssbuf.length = buffer_len(&b);
78
79 /* gss_kex_context is NULL with privsep, so we can't check it here */
80 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
81 &gssbuf, &mic))))
82 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
83 authctxt->pw));
84
85 buffer_free(&b);
86 free(mic.value);
87
88 return (authenticated);
89}
90
56/* 91/*
57 * We only support those mechanisms that we know about (ie ones that we know 92 * We only support those mechanisms that we know about (ie ones that we know
58 * how to check local user kuserok and the like) 93 * how to check local user kuserok and the like)
@@ -240,7 +275,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
240 275
241 packet_check_eom(); 276 packet_check_eom();
242 277
243 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 278 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
279 authctxt->pw));
244 280
245 if ((!use_privsep || mm_is_monitor()) && 281 if ((!use_privsep || mm_is_monitor()) &&
246 (displayname = ssh_gssapi_displayname()) != NULL) 282 (displayname = ssh_gssapi_displayname()) != NULL)
@@ -281,7 +317,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
281 gssbuf.length = buffer_len(&b); 317 gssbuf.length = buffer_len(&b);
282 318
283 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) 319 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
284 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 320 authenticated =
321 PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
285 else 322 else
286 logit("GSSAPI MIC check failed"); 323 logit("GSSAPI MIC check failed");
287 324
@@ -301,6 +338,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
301 return 0; 338 return 0;
302} 339}
303 340
341Authmethod method_gsskeyex = {
342 "gssapi-keyex",
343 userauth_gsskeyex,
344 &options.gss_authentication
345};
346
304Authmethod method_gssapi = { 347Authmethod method_gssapi = {
305 "gssapi-with-mic", 348 "gssapi-with-mic",
306 userauth_gssapi, 349 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index 862e09960..54070e3a9 100644
--- a/auth2.c
+++ b/auth2.c
@@ -72,6 +72,7 @@ extern Authmethod method_passwd;
72extern Authmethod method_kbdint; 72extern Authmethod method_kbdint;
73extern Authmethod method_hostbased; 73extern Authmethod method_hostbased;
74#ifdef GSSAPI 74#ifdef GSSAPI
75extern Authmethod method_gsskeyex;
75extern Authmethod method_gssapi; 76extern Authmethod method_gssapi;
76#endif 77#endif
77 78
@@ -79,6 +80,7 @@ Authmethod *authmethods[] = {
79 &method_none, 80 &method_none,
80 &method_pubkey, 81 &method_pubkey,
81#ifdef GSSAPI 82#ifdef GSSAPI
83 &method_gsskeyex,
82 &method_gssapi, 84 &method_gssapi,
83#endif 85#endif
84 &method_passwd, 86 &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 791d336e3..0010b8337 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -112,6 +112,10 @@
112#include "ssherr.h" 112#include "ssherr.h"
113#include "hostfile.h" 113#include "hostfile.h"
114 114
115#ifdef GSSAPI
116#include "ssh-gss.h"
117#endif
118
115/* import options */ 119/* import options */
116extern Options options; 120extern Options options;
117 121
@@ -1349,9 +1353,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
1349 break; 1353 break;
1350 1354
1351 /* Do channel operations unless rekeying in progress. */ 1355 /* Do channel operations unless rekeying in progress. */
1352 if (!ssh_packet_is_rekeying(ssh)) 1356 if (!ssh_packet_is_rekeying(ssh)) {
1353 channel_after_select(ssh, readset, writeset); 1357 channel_after_select(ssh, readset, writeset);
1354 1358
1359#ifdef GSSAPI
1360 if (options.gss_renewal_rekey &&
1361 ssh_gssapi_credentials_updated(NULL)) {
1362 debug("credentials updated - forcing rekey");
1363 need_rekeying = 1;
1364 }
1365#endif
1366 }
1367
1355 /* Buffer input from the connection. */ 1368 /* Buffer input from the connection. */
1356 client_process_net_input(readset); 1369 client_process_net_input(readset);
1357 1370
diff --git a/config.h.in b/config.h.in
index 63fc548b5..0b244fd5f 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1696,6 +1696,9 @@
1696/* Use btmp to log bad logins */ 1696/* Use btmp to log bad logins */
1697#undef USE_BTMP 1697#undef USE_BTMP
1698 1698
1699/* platform uses an in-memory credentials cache */
1700#undef USE_CCAPI
1701
1699/* Use libedit for sftp */ 1702/* Use libedit for sftp */
1700#undef USE_LIBEDIT 1703#undef USE_LIBEDIT
1701 1704
@@ -1711,6 +1714,9 @@
1711/* Use PIPES instead of a socketpair() */ 1714/* Use PIPES instead of a socketpair() */
1712#undef USE_PIPES 1715#undef USE_PIPES
1713 1716
1717/* platform has the Security Authorization Session API */
1718#undef USE_SECURITY_SESSION_API
1719
1714/* Define if you have Solaris privileges */ 1720/* Define if you have Solaris privileges */
1715#undef USE_SOLARIS_PRIVS 1721#undef USE_SOLARIS_PRIVS
1716 1722
diff --git a/configure.ac b/configure.ac
index 889f50637..84bfad8c5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -621,6 +621,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
621 [Use tunnel device compatibility to OpenBSD]) 621 [Use tunnel device compatibility to OpenBSD])
622 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 622 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
623 [Prepend the address family to IP tunnel traffic]) 623 [Prepend the address family to IP tunnel traffic])
624 AC_MSG_CHECKING([if we have the Security Authorization Session API])
625 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
626 [SessionCreate(0, 0);],
627 [ac_cv_use_security_session_api="yes"
628 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
629 [platform has the Security Authorization Session API])
630 LIBS="$LIBS -framework Security"
631 AC_MSG_RESULT([yes])],
632 [ac_cv_use_security_session_api="no"
633 AC_MSG_RESULT([no])])
634 AC_MSG_CHECKING([if we have an in-memory credentials cache])
635 AC_TRY_COMPILE(
636 [#include <Kerberos/Kerberos.h>],
637 [cc_context_t c;
638 (void) cc_initialize (&c, 0, NULL, NULL);],
639 [AC_DEFINE([USE_CCAPI], [1],
640 [platform uses an in-memory credentials cache])
641 LIBS="$LIBS -framework Security"
642 AC_MSG_RESULT([yes])
643 if test "x$ac_cv_use_security_session_api" = "xno"; then
644 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
645 fi],
646 [AC_MSG_RESULT([no])]
647 )
624 m4_pattern_allow([AU_IPv]) 648 m4_pattern_allow([AU_IPv])
625 AC_CHECK_DECL([AU_IPv4], [], 649 AC_CHECK_DECL([AU_IPv4], [],
626 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 650 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
diff --git a/gss-genr.c b/gss-genr.c
index 62559ed9e..0b3ae073c 100644
--- a/gss-genr.c
+++ b/gss-genr.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-genr.c,v 1.24 2016/09/12 01:22:38 deraadt Exp $ */ 1/* $OpenBSD: gss-genr.c,v 1.24 2016/09/12 01:22:38 deraadt Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -40,12 +40,167 @@
40#include "buffer.h" 40#include "buffer.h"
41#include "log.h" 41#include "log.h"
42#include "ssh2.h" 42#include "ssh2.h"
43#include "cipher.h"
44#include "key.h"
45#include "kex.h"
46#include <openssl/evp.h>
43 47
44#include "ssh-gss.h" 48#include "ssh-gss.h"
45 49
46extern u_char *session_id2; 50extern u_char *session_id2;
47extern u_int session_id2_len; 51extern u_int session_id2_len;
48 52
53typedef struct {
54 char *encoded;
55 gss_OID oid;
56} ssh_gss_kex_mapping;
57
58/*
59 * XXX - It would be nice to find a more elegant way of handling the
60 * XXX passing of the key exchange context to the userauth routines
61 */
62
63Gssctxt *gss_kex_context = NULL;
64
65static ssh_gss_kex_mapping *gss_enc2oid = NULL;
66
67int
68ssh_gssapi_oid_table_ok(void) {
69 return (gss_enc2oid != NULL);
70}
71
72/*
73 * Return a list of the gss-group1-sha1 mechanisms supported by this program
74 *
75 * We test mechanisms to ensure that we can use them, to avoid starting
76 * a key exchange with a bad mechanism
77 */
78
79char *
80ssh_gssapi_client_mechanisms(const char *host, const char *client) {
81 gss_OID_set gss_supported;
82 OM_uint32 min_status;
83
84 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
85 return NULL;
86
87 return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
88 host, client));
89}
90
91char *
92ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
93 const char *host, const char *client) {
94 Buffer buf;
95 size_t i;
96 int oidpos, enclen;
97 char *mechs, *encoded;
98 u_char digest[EVP_MAX_MD_SIZE];
99 char deroid[2];
100 const EVP_MD *evp_md = EVP_md5();
101 EVP_MD_CTX md;
102
103 if (gss_enc2oid != NULL) {
104 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
105 free(gss_enc2oid[i].encoded);
106 free(gss_enc2oid);
107 }
108
109 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
110 (gss_supported->count + 1));
111
112 buffer_init(&buf);
113
114 oidpos = 0;
115 for (i = 0; i < gss_supported->count; i++) {
116 if (gss_supported->elements[i].length < 128 &&
117 (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
118
119 deroid[0] = SSH_GSS_OIDTYPE;
120 deroid[1] = gss_supported->elements[i].length;
121
122 EVP_DigestInit(&md, evp_md);
123 EVP_DigestUpdate(&md, deroid, 2);
124 EVP_DigestUpdate(&md,
125 gss_supported->elements[i].elements,
126 gss_supported->elements[i].length);
127 EVP_DigestFinal(&md, digest, NULL);
128
129 encoded = xmalloc(EVP_MD_size(evp_md) * 2);
130 enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
131 encoded, EVP_MD_size(evp_md) * 2);
132
133 if (oidpos != 0)
134 buffer_put_char(&buf, ',');
135
136 buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
137 sizeof(KEX_GSS_GEX_SHA1_ID) - 1);
138 buffer_append(&buf, encoded, enclen);
139 buffer_put_char(&buf, ',');
140 buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID,
141 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1);
142 buffer_append(&buf, encoded, enclen);
143 buffer_put_char(&buf, ',');
144 buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
145 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1);
146 buffer_append(&buf, encoded, enclen);
147
148 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
149 gss_enc2oid[oidpos].encoded = encoded;
150 oidpos++;
151 }
152 }
153 gss_enc2oid[oidpos].oid = NULL;
154 gss_enc2oid[oidpos].encoded = NULL;
155
156 buffer_put_char(&buf, '\0');
157
158 mechs = xmalloc(buffer_len(&buf));
159 buffer_get(&buf, mechs, buffer_len(&buf));
160 buffer_free(&buf);
161
162 if (strlen(mechs) == 0) {
163 free(mechs);
164 mechs = NULL;
165 }
166
167 return (mechs);
168}
169
170gss_OID
171ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
172 int i = 0;
173
174 switch (kex_type) {
175 case KEX_GSS_GRP1_SHA1:
176 if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
177 return GSS_C_NO_OID;
178 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
179 break;
180 case KEX_GSS_GRP14_SHA1:
181 if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
182 return GSS_C_NO_OID;
183 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
184 break;
185 case KEX_GSS_GEX_SHA1:
186 if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
187 return GSS_C_NO_OID;
188 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
189 break;
190 default:
191 return GSS_C_NO_OID;
192 }
193
194 while (gss_enc2oid[i].encoded != NULL &&
195 strcmp(name, gss_enc2oid[i].encoded) != 0)
196 i++;
197
198 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
199 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
200
201 return gss_enc2oid[i].oid;
202}
203
49/* Check that the OID in a data stream matches that in the context */ 204/* Check that the OID in a data stream matches that in the context */
50int 205int
51ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 206ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -198,7 +353,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
198 } 353 }
199 354
200 ctx->major = gss_init_sec_context(&ctx->minor, 355 ctx->major = gss_init_sec_context(&ctx->minor,
201 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 356 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
202 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 357 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
203 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 358 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
204 359
@@ -228,8 +383,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
228} 383}
229 384
230OM_uint32 385OM_uint32
386ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
387{
388 gss_buffer_desc gssbuf;
389 gss_name_t gssname;
390 OM_uint32 status;
391 gss_OID_set oidset;
392
393 gssbuf.value = (void *) name;
394 gssbuf.length = strlen(gssbuf.value);
395
396 gss_create_empty_oid_set(&status, &oidset);
397 gss_add_oid_set_member(&status, ctx->oid, &oidset);
398
399 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
400 GSS_C_NT_USER_NAME, &gssname);
401
402 if (!ctx->major)
403 ctx->major = gss_acquire_cred(&ctx->minor,
404 gssname, 0, oidset, GSS_C_INITIATE,
405 &ctx->client_creds, NULL, NULL);
406
407 gss_release_name(&status, &gssname);
408 gss_release_oid_set(&status, &oidset);
409
410 if (ctx->major)
411 ssh_gssapi_error(ctx);
412
413 return(ctx->major);
414}
415
416OM_uint32
231ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 417ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
232{ 418{
419 if (ctx == NULL)
420 return -1;
421
233 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 422 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
234 GSS_C_QOP_DEFAULT, buffer, hash))) 423 GSS_C_QOP_DEFAULT, buffer, hash)))
235 ssh_gssapi_error(ctx); 424 ssh_gssapi_error(ctx);
@@ -237,6 +426,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
237 return (ctx->major); 426 return (ctx->major);
238} 427}
239 428
429/* Priviledged when used by server */
430OM_uint32
431ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
432{
433 if (ctx == NULL)
434 return -1;
435
436 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
437 gssbuf, gssmic, NULL);
438
439 return (ctx->major);
440}
441
240void 442void
241ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, 443ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
242 const char *context) 444 const char *context)
@@ -250,11 +452,16 @@ ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
250} 452}
251 453
252int 454int
253ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 455ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
456 const char *client)
254{ 457{
255 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 458 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
256 OM_uint32 major, minor; 459 OM_uint32 major, minor;
257 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 460 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
461 Gssctxt *intctx = NULL;
462
463 if (ctx == NULL)
464 ctx = &intctx;
258 465
259 /* RFC 4462 says we MUST NOT do SPNEGO */ 466 /* RFC 4462 says we MUST NOT do SPNEGO */
260 if (oid->length == spnego_oid.length && 467 if (oid->length == spnego_oid.length &&
@@ -264,6 +471,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
264 ssh_gssapi_build_ctx(ctx); 471 ssh_gssapi_build_ctx(ctx);
265 ssh_gssapi_set_oid(*ctx, oid); 472 ssh_gssapi_set_oid(*ctx, oid);
266 major = ssh_gssapi_import_name(*ctx, host); 473 major = ssh_gssapi_import_name(*ctx, host);
474
475 if (!GSS_ERROR(major) && client)
476 major = ssh_gssapi_client_identity(*ctx, client);
477
267 if (!GSS_ERROR(major)) { 478 if (!GSS_ERROR(major)) {
268 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 479 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
269 NULL); 480 NULL);
@@ -273,10 +484,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
273 GSS_C_NO_BUFFER); 484 GSS_C_NO_BUFFER);
274 } 485 }
275 486
276 if (GSS_ERROR(major)) 487 if (GSS_ERROR(major) || intctx != NULL)
277 ssh_gssapi_delete_ctx(ctx); 488 ssh_gssapi_delete_ctx(ctx);
278 489
279 return (!GSS_ERROR(major)); 490 return (!GSS_ERROR(major));
280} 491}
281 492
493int
494ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
495 static gss_name_t saved_name = GSS_C_NO_NAME;
496 static OM_uint32 saved_lifetime = 0;
497 static gss_OID saved_mech = GSS_C_NO_OID;
498 static gss_name_t name;
499 static OM_uint32 last_call = 0;
500 OM_uint32 lifetime, now, major, minor;
501 int equal;
502
503 now = time(NULL);
504
505 if (ctxt) {
506 debug("Rekey has happened - updating saved versions");
507
508 if (saved_name != GSS_C_NO_NAME)
509 gss_release_name(&minor, &saved_name);
510
511 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
512 &saved_name, &saved_lifetime, NULL, NULL);
513
514 if (!GSS_ERROR(major)) {
515 saved_mech = ctxt->oid;
516 saved_lifetime+= now;
517 } else {
518 /* Handle the error */
519 }
520 return 0;
521 }
522
523 if (now - last_call < 10)
524 return 0;
525
526 last_call = now;
527
528 if (saved_mech == GSS_C_NO_OID)
529 return 0;
530
531 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
532 &name, &lifetime, NULL, NULL);
533 if (major == GSS_S_CREDENTIALS_EXPIRED)
534 return 0;
535 else if (GSS_ERROR(major))
536 return 0;
537
538 major = gss_compare_name(&minor, saved_name, name, &equal);
539 gss_release_name(&minor, &name);
540 if (GSS_ERROR(major))
541 return 0;
542
543 if (equal && (saved_lifetime < lifetime + now - 10))
544 return 1;
545
546 return 0;
547}
548
282#endif /* GSSAPI */ 549#endif /* GSSAPI */
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
index 795992d9f..fd8b37183 100644
--- a/gss-serv-krb5.c
+++ b/gss-serv-krb5.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv-krb5.c,v 1.8 2013/07/20 01:55:13 djm Exp $ */ 1/* $OpenBSD: gss-serv-krb5.c,v 1.8 2013/07/20 01:55:13 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -121,8 +121,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
121 krb5_error_code problem; 121 krb5_error_code problem;
122 krb5_principal princ; 122 krb5_principal princ;
123 OM_uint32 maj_status, min_status; 123 OM_uint32 maj_status, min_status;
124 int len;
125 const char *errmsg; 124 const char *errmsg;
125 const char *new_ccname;
126 126
127 if (client->creds == NULL) { 127 if (client->creds == NULL) {
128 debug("No credentials stored"); 128 debug("No credentials stored");
@@ -181,11 +181,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
181 return; 181 return;
182 } 182 }
183 183
184 client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); 184 new_ccname = krb5_cc_get_name(krb_context, ccache);
185
185 client->store.envvar = "KRB5CCNAME"; 186 client->store.envvar = "KRB5CCNAME";
186 len = strlen(client->store.filename) + 6; 187#ifdef USE_CCAPI
187 client->store.envval = xmalloc(len); 188 xasprintf(&client->store.envval, "API:%s", new_ccname);
188 snprintf(client->store.envval, len, "FILE:%s", client->store.filename); 189 client->store.filename = NULL;
190#else
191 xasprintf(&client->store.envval, "FILE:%s", new_ccname);
192 client->store.filename = xstrdup(new_ccname);
193#endif
189 194
190#ifdef USE_PAM 195#ifdef USE_PAM
191 if (options.use_pam) 196 if (options.use_pam)
@@ -197,6 +202,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
197 return; 202 return;
198} 203}
199 204
205int
206ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
207 ssh_gssapi_client *client)
208{
209 krb5_ccache ccache = NULL;
210 krb5_principal principal = NULL;
211 char *name = NULL;
212 krb5_error_code problem;
213 OM_uint32 maj_status, min_status;
214
215 if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
216 logit("krb5_cc_resolve(): %.100s",
217 krb5_get_err_text(krb_context, problem));
218 return 0;
219 }
220
221 /* Find out who the principal in this cache is */
222 if ((problem = krb5_cc_get_principal(krb_context, ccache,
223 &principal))) {
224 logit("krb5_cc_get_principal(): %.100s",
225 krb5_get_err_text(krb_context, problem));
226 krb5_cc_close(krb_context, ccache);
227 return 0;
228 }
229
230 if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
231 logit("krb5_unparse_name(): %.100s",
232 krb5_get_err_text(krb_context, problem));
233 krb5_free_principal(krb_context, principal);
234 krb5_cc_close(krb_context, ccache);
235 return 0;
236 }
237
238
239 if (strcmp(name,client->exportedname.value)!=0) {
240 debug("Name in local credentials cache differs. Not storing");
241 krb5_free_principal(krb_context, principal);
242 krb5_cc_close(krb_context, ccache);
243 krb5_free_unparsed_name(krb_context, name);
244 return 0;
245 }
246 krb5_free_unparsed_name(krb_context, name);
247
248 /* Name matches, so lets get on with it! */
249
250 if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
251 logit("krb5_cc_initialize(): %.100s",
252 krb5_get_err_text(krb_context, problem));
253 krb5_free_principal(krb_context, principal);
254 krb5_cc_close(krb_context, ccache);
255 return 0;
256 }
257
258 krb5_free_principal(krb_context, principal);
259
260 if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
261 ccache))) {
262 logit("gss_krb5_copy_ccache() failed. Sorry!");
263 krb5_cc_close(krb_context, ccache);
264 return 0;
265 }
266
267 return 1;
268}
269
200ssh_gssapi_mech gssapi_kerberos_mech = { 270ssh_gssapi_mech gssapi_kerberos_mech = {
201 "toWM5Slw5Ew8Mqkay+al2g==", 271 "toWM5Slw5Ew8Mqkay+al2g==",
202 "Kerberos", 272 "Kerberos",
@@ -204,7 +274,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
204 NULL, 274 NULL,
205 &ssh_gssapi_krb5_userok, 275 &ssh_gssapi_krb5_userok,
206 NULL, 276 NULL,
207 &ssh_gssapi_krb5_storecreds 277 &ssh_gssapi_krb5_storecreds,
278 &ssh_gssapi_krb5_updatecreds
208}; 279};
209 280
210#endif /* KRB5 */ 281#endif /* KRB5 */
diff --git a/gss-serv.c b/gss-serv.c
index 6cae720e5..967c6cfbc 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.30 2017/06/24 06:34:38 djm Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.30 2017/06/24 06:34:38 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,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
142} 147}
143 148
144/* Unprivileged */ 149/* Unprivileged */
150char *
151ssh_gssapi_server_mechanisms(void) {
152 if (supported_oids == NULL)
153 ssh_gssapi_prepare_supported_oids();
154 return (ssh_gssapi_kex_mechs(supported_oids,
155 &ssh_gssapi_server_check_mech, NULL, NULL));
156}
157
158/* Unprivileged */
159int
160ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
161 const char *dummy) {
162 Gssctxt *ctx = NULL;
163 int res;
164
165 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
166 ssh_gssapi_delete_ctx(&ctx);
167
168 return (res);
169}
170
171/* Unprivileged */
145void 172void
146ssh_gssapi_supported_oids(gss_OID_set *oidset) 173ssh_gssapi_supported_oids(gss_OID_set *oidset)
147{ 174{
@@ -151,7 +178,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
151 gss_OID_set supported; 178 gss_OID_set supported;
152 179
153 gss_create_empty_oid_set(&min_status, oidset); 180 gss_create_empty_oid_set(&min_status, oidset);
154 gss_indicate_mechs(&min_status, &supported); 181
182 if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
183 return;
155 184
156 while (supported_mechs[i]->name != NULL) { 185 while (supported_mechs[i]->name != NULL) {
157 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 186 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -277,8 +306,48 @@ OM_uint32
277ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 306ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
278{ 307{
279 int i = 0; 308 int i = 0;
309 int equal = 0;
310 gss_name_t new_name = GSS_C_NO_NAME;
311 gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
312
313 if (options.gss_store_rekey && client->used && ctx->client_creds) {
314 if (client->mech->oid.length != ctx->oid->length ||
315 (memcmp(client->mech->oid.elements,
316 ctx->oid->elements, ctx->oid->length) !=0)) {
317 debug("Rekeyed credentials have different mechanism");
318 return GSS_S_COMPLETE;
319 }
320
321 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
322 ctx->client_creds, ctx->oid, &new_name,
323 NULL, NULL, NULL))) {
324 ssh_gssapi_error(ctx);
325 return (ctx->major);
326 }
327
328 ctx->major = gss_compare_name(&ctx->minor, client->name,
329 new_name, &equal);
330
331 if (GSS_ERROR(ctx->major)) {
332 ssh_gssapi_error(ctx);
333 return (ctx->major);
334 }
335
336 if (!equal) {
337 debug("Rekeyed credentials have different name");
338 return GSS_S_COMPLETE;
339 }
280 340
281 gss_buffer_desc ename; 341 debug("Marking rekeyed credentials for export");
342
343 gss_release_name(&ctx->minor, &client->name);
344 gss_release_cred(&ctx->minor, &client->creds);
345 client->name = new_name;
346 client->creds = ctx->client_creds;
347 ctx->client_creds = GSS_C_NO_CREDENTIAL;
348 client->updated = 1;
349 return GSS_S_COMPLETE;
350 }
282 351
283 client->mech = NULL; 352 client->mech = NULL;
284 353
@@ -293,6 +362,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
293 if (client->mech == NULL) 362 if (client->mech == NULL)
294 return GSS_S_FAILURE; 363 return GSS_S_FAILURE;
295 364
365 if (ctx->client_creds &&
366 (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
367 ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
368 ssh_gssapi_error(ctx);
369 return (ctx->major);
370 }
371
296 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 372 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
297 &client->displayname, NULL))) { 373 &client->displayname, NULL))) {
298 ssh_gssapi_error(ctx); 374 ssh_gssapi_error(ctx);
@@ -310,6 +386,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
310 return (ctx->major); 386 return (ctx->major);
311 } 387 }
312 388
389 gss_release_buffer(&ctx->minor, &ename);
390
313 /* We can't copy this structure, so we just move the pointer to it */ 391 /* We can't copy this structure, so we just move the pointer to it */
314 client->creds = ctx->client_creds; 392 client->creds = ctx->client_creds;
315 ctx->client_creds = GSS_C_NO_CREDENTIAL; 393 ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -357,7 +435,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
357 435
358/* Privileged */ 436/* Privileged */
359int 437int
360ssh_gssapi_userok(char *user) 438ssh_gssapi_userok(char *user, struct passwd *pw)
361{ 439{
362 OM_uint32 lmin; 440 OM_uint32 lmin;
363 441
@@ -367,9 +445,11 @@ ssh_gssapi_userok(char *user)
367 return 0; 445 return 0;
368 } 446 }
369 if (gssapi_client.mech && gssapi_client.mech->userok) 447 if (gssapi_client.mech && gssapi_client.mech->userok)
370 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 448 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
449 gssapi_client.used = 1;
450 gssapi_client.store.owner = pw;
371 return 1; 451 return 1;
372 else { 452 } else {
373 /* Destroy delegated credentials if userok fails */ 453 /* Destroy delegated credentials if userok fails */
374 gss_release_buffer(&lmin, &gssapi_client.displayname); 454 gss_release_buffer(&lmin, &gssapi_client.displayname);
375 gss_release_buffer(&lmin, &gssapi_client.exportedname); 455 gss_release_buffer(&lmin, &gssapi_client.exportedname);
@@ -383,14 +463,90 @@ ssh_gssapi_userok(char *user)
383 return (0); 463 return (0);
384} 464}
385 465
386/* Privileged */ 466/* These bits are only used for rekeying. The unpriviledged child is running
387OM_uint32 467 * as the user, the monitor is root.
388ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 468 *
469 * In the child, we want to :
470 * *) Ask the monitor to store our credentials into the store we specify
471 * *) If it succeeds, maybe do a PAM update
472 */
473
474/* Stuff for PAM */
475
476#ifdef USE_PAM
477static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
478 struct pam_response **resp, void *data)
389{ 479{
390 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 480 return (PAM_CONV_ERR);
391 gssbuf, gssmic, NULL); 481}
482#endif
392 483
393 return (ctx->major); 484void
485ssh_gssapi_rekey_creds(void) {
486 int ok;
487 int ret;
488#ifdef USE_PAM
489 pam_handle_t *pamh = NULL;
490 struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
491 char *envstr;
492#endif
493
494 if (gssapi_client.store.filename == NULL &&
495 gssapi_client.store.envval == NULL &&
496 gssapi_client.store.envvar == NULL)
497 return;
498
499 ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
500
501 if (!ok)
502 return;
503
504 debug("Rekeyed credentials stored successfully");
505
506 /* Actually managing to play with the ssh pam stack from here will
507 * be next to impossible. In any case, we may want different options
508 * for rekeying. So, use our own :)
509 */
510#ifdef USE_PAM
511 if (!use_privsep) {
512 debug("Not even going to try and do PAM with privsep disabled");
513 return;
514 }
515
516 ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
517 &pamconv, &pamh);
518 if (ret)
519 return;
520
521 xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
522 gssapi_client.store.envval);
523
524 ret = pam_putenv(pamh, envstr);
525 if (!ret)
526 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
527 pam_end(pamh, PAM_SUCCESS);
528#endif
529}
530
531int
532ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
533 int ok = 0;
534
535 /* Check we've got credentials to store */
536 if (!gssapi_client.updated)
537 return 0;
538
539 gssapi_client.updated = 0;
540
541 temporarily_use_uid(gssapi_client.store.owner);
542 if (gssapi_client.mech && gssapi_client.mech->updatecreds)
543 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
544 else
545 debug("No update function for this mechanism");
546
547 restore_uid();
548
549 return ok;
394} 550}
395 551
396/* Privileged */ 552/* Privileged */
diff --git a/kex.c b/kex.c
index d5d5a9dae..bb1bd6616 100644
--- a/kex.c
+++ b/kex.c
@@ -54,6 +54,10 @@
54#include "sshbuf.h" 54#include "sshbuf.h"
55#include "digest.h" 55#include "digest.h"
56 56
57#ifdef GSSAPI
58#include "ssh-gss.h"
59#endif
60
57/* prototype */ 61/* prototype */
58static int kex_choose_conf(struct ssh *); 62static int kex_choose_conf(struct ssh *);
59static int kex_input_newkeys(int, u_int32_t, struct ssh *); 63static int kex_input_newkeys(int, u_int32_t, struct ssh *);
@@ -105,6 +109,14 @@ static const struct kexalg kexalgs[] = {
105#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ 109#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
106 { NULL, -1, -1, -1}, 110 { NULL, -1, -1, -1},
107}; 111};
112static const struct kexalg kexalg_prefixes[] = {
113#ifdef GSSAPI
114 { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
115 { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
116 { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
117#endif
118 { NULL, -1, -1, -1 },
119};
108 120
109char * 121char *
110kex_alg_list(char sep) 122kex_alg_list(char sep)
@@ -137,6 +149,10 @@ kex_alg_by_name(const char *name)
137 if (strcmp(k->name, name) == 0) 149 if (strcmp(k->name, name) == 0)
138 return k; 150 return k;
139 } 151 }
152 for (k = kexalg_prefixes; k->name != NULL; k++) {
153 if (strncmp(k->name, name, strlen(k->name)) == 0)
154 return k;
155 }
140 return NULL; 156 return NULL;
141} 157}
142 158
@@ -601,6 +617,9 @@ kex_free(struct kex *kex)
601 sshbuf_free(kex->peer); 617 sshbuf_free(kex->peer);
602 sshbuf_free(kex->my); 618 sshbuf_free(kex->my);
603 free(kex->session_id); 619 free(kex->session_id);
620#ifdef GSSAPI
621 free(kex->gss_host);
622#endif /* GSSAPI */
604 free(kex->client_version_string); 623 free(kex->client_version_string);
605 free(kex->server_version_string); 624 free(kex->server_version_string);
606 free(kex->failed_choice); 625 free(kex->failed_choice);
diff --git a/kex.h b/kex.h
index 01bb3986a..a708e4868 100644
--- a/kex.h
+++ b/kex.h
@@ -99,6 +99,9 @@ enum kex_exchange {
99 KEX_DH_GEX_SHA256, 99 KEX_DH_GEX_SHA256,
100 KEX_ECDH_SHA2, 100 KEX_ECDH_SHA2,
101 KEX_C25519_SHA256, 101 KEX_C25519_SHA256,
102 KEX_GSS_GRP1_SHA1,
103 KEX_GSS_GRP14_SHA1,
104 KEX_GSS_GEX_SHA1,
102 KEX_MAX 105 KEX_MAX
103}; 106};
104 107
@@ -147,6 +150,12 @@ struct kex {
147 u_int flags; 150 u_int flags;
148 int hash_alg; 151 int hash_alg;
149 int ec_nid; 152 int ec_nid;
153#ifdef GSSAPI
154 int gss_deleg_creds;
155 int gss_trust_dns;
156 char *gss_host;
157 char *gss_client;
158#endif
150 char *client_version_string; 159 char *client_version_string;
151 char *server_version_string; 160 char *server_version_string;
152 char *failed_choice; 161 char *failed_choice;
@@ -197,6 +206,11 @@ int kexecdh_server(struct ssh *);
197int kexc25519_client(struct ssh *); 206int kexc25519_client(struct ssh *);
198int kexc25519_server(struct ssh *); 207int kexc25519_server(struct ssh *);
199 208
209#ifdef GSSAPI
210int kexgss_client(struct ssh *);
211int kexgss_server(struct ssh *);
212#endif
213
200int kex_dh_hash(int, const char *, const char *, 214int kex_dh_hash(int, const char *, const char *,
201 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, 215 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
202 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); 216 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 f517da482..cabfeb8a4 100644
--- a/monitor.c
+++ b/monitor.c
@@ -157,6 +157,8 @@ int mm_answer_gss_setup_ctx(int, Buffer *);
157int mm_answer_gss_accept_ctx(int, Buffer *); 157int mm_answer_gss_accept_ctx(int, Buffer *);
158int mm_answer_gss_userok(int, Buffer *); 158int mm_answer_gss_userok(int, Buffer *);
159int mm_answer_gss_checkmic(int, Buffer *); 159int mm_answer_gss_checkmic(int, Buffer *);
160int mm_answer_gss_sign(int, Buffer *);
161int mm_answer_gss_updatecreds(int, Buffer *);
160#endif 162#endif
161 163
162#ifdef SSH_AUDIT_EVENTS 164#ifdef SSH_AUDIT_EVENTS
@@ -230,11 +232,18 @@ struct mon_table mon_dispatch_proto20[] = {
230 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, 232 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
231 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, 233 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
232 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, 234 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
235 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
233#endif 236#endif
234 {0, 0, NULL} 237 {0, 0, NULL}
235}; 238};
236 239
237struct mon_table mon_dispatch_postauth20[] = { 240struct mon_table mon_dispatch_postauth20[] = {
241#ifdef GSSAPI
242 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
243 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
244 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
245 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
246#endif
238#ifdef WITH_OPENSSL 247#ifdef WITH_OPENSSL
239 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 248 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
240#endif 249#endif
@@ -302,6 +311,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
302 /* Permit requests for moduli and signatures */ 311 /* Permit requests for moduli and signatures */
303 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 312 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
304 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 313 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
314#ifdef GSSAPI
315 /* and for the GSSAPI key exchange */
316 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
317#endif
305 318
306 /* The first few requests do not require asynchronous access */ 319 /* The first few requests do not require asynchronous access */
307 while (!authenticated) { 320 while (!authenticated) {
@@ -408,6 +421,10 @@ monitor_child_postauth(struct monitor *pmonitor)
408 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 421 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
409 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 422 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
410 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 423 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
424#ifdef GSSAPI
425 /* and for the GSSAPI key exchange */
426 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
427#endif
411 428
412 if (!no_pty_flag) { 429 if (!no_pty_flag) {
413 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); 430 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
@@ -1626,6 +1643,13 @@ monitor_apply_keystate(struct monitor *pmonitor)
1626# endif 1643# endif
1627#endif /* WITH_OPENSSL */ 1644#endif /* WITH_OPENSSL */
1628 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 1645 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1646#ifdef GSSAPI
1647 if (options.gss_keyex) {
1648 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1649 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1650 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1651 }
1652#endif
1629 kex->load_host_public_key=&get_hostkey_public_by_type; 1653 kex->load_host_public_key=&get_hostkey_public_by_type;
1630 kex->load_host_private_key=&get_hostkey_private_by_type; 1654 kex->load_host_private_key=&get_hostkey_private_by_type;
1631 kex->host_key_index=&get_hostkey_index; 1655 kex->host_key_index=&get_hostkey_index;
@@ -1714,8 +1738,8 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m)
1714 OM_uint32 major; 1738 OM_uint32 major;
1715 u_int len; 1739 u_int len;
1716 1740
1717 if (!options.gss_authentication) 1741 if (!options.gss_authentication && !options.gss_keyex)
1718 fatal("%s: GSSAPI authentication not enabled", __func__); 1742 fatal("%s: GSSAPI not enabled", __func__);
1719 1743
1720 goid.elements = buffer_get_string(m, &len); 1744 goid.elements = buffer_get_string(m, &len);
1721 goid.length = len; 1745 goid.length = len;
@@ -1744,8 +1768,8 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
1744 OM_uint32 flags = 0; /* GSI needs this */ 1768 OM_uint32 flags = 0; /* GSI needs this */
1745 u_int len; 1769 u_int len;
1746 1770
1747 if (!options.gss_authentication) 1771 if (!options.gss_authentication && !options.gss_keyex)
1748 fatal("%s: GSSAPI authentication not enabled", __func__); 1772 fatal("%s: GSSAPI not enabled", __func__);
1749 1773
1750 in.value = buffer_get_string(m, &len); 1774 in.value = buffer_get_string(m, &len);
1751 in.length = len; 1775 in.length = len;
@@ -1764,6 +1788,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
1764 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 1788 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
1765 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 1789 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
1766 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 1790 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
1791 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
1767 } 1792 }
1768 return (0); 1793 return (0);
1769} 1794}
@@ -1775,8 +1800,8 @@ mm_answer_gss_checkmic(int sock, Buffer *m)
1775 OM_uint32 ret; 1800 OM_uint32 ret;
1776 u_int len; 1801 u_int len;
1777 1802
1778 if (!options.gss_authentication) 1803 if (!options.gss_authentication && !options.gss_keyex)
1779 fatal("%s: GSSAPI authentication not enabled", __func__); 1804 fatal("%s: GSSAPI not enabled", __func__);
1780 1805
1781 gssbuf.value = buffer_get_string(m, &len); 1806 gssbuf.value = buffer_get_string(m, &len);
1782 gssbuf.length = len; 1807 gssbuf.length = len;
@@ -1805,10 +1830,11 @@ mm_answer_gss_userok(int sock, Buffer *m)
1805 int authenticated; 1830 int authenticated;
1806 const char *displayname; 1831 const char *displayname;
1807 1832
1808 if (!options.gss_authentication) 1833 if (!options.gss_authentication && !options.gss_keyex)
1809 fatal("%s: GSSAPI authentication not enabled", __func__); 1834 fatal("%s: GSSAPI not enabled", __func__);
1810 1835
1811 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 1836 authenticated = authctxt->valid &&
1837 ssh_gssapi_userok(authctxt->user, authctxt->pw);
1812 1838
1813 buffer_clear(m); 1839 buffer_clear(m);
1814 buffer_put_int(m, authenticated); 1840 buffer_put_int(m, authenticated);
@@ -1824,5 +1850,76 @@ mm_answer_gss_userok(int sock, Buffer *m)
1824 /* Monitor loop will terminate if authenticated */ 1850 /* Monitor loop will terminate if authenticated */
1825 return (authenticated); 1851 return (authenticated);
1826} 1852}
1853
1854int
1855mm_answer_gss_sign(int socket, Buffer *m)
1856{
1857 gss_buffer_desc data;
1858 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
1859 OM_uint32 major, minor;
1860 u_int len;
1861
1862 if (!options.gss_authentication && !options.gss_keyex)
1863 fatal("%s: GSSAPI not enabled", __func__);
1864
1865 data.value = buffer_get_string(m, &len);
1866 data.length = len;
1867 if (data.length != 20)
1868 fatal("%s: data length incorrect: %d", __func__,
1869 (int) data.length);
1870
1871 /* Save the session ID on the first time around */
1872 if (session_id2_len == 0) {
1873 session_id2_len = data.length;
1874 session_id2 = xmalloc(session_id2_len);
1875 memcpy(session_id2, data.value, session_id2_len);
1876 }
1877 major = ssh_gssapi_sign(gsscontext, &data, &hash);
1878
1879 free(data.value);
1880
1881 buffer_clear(m);
1882 buffer_put_int(m, major);
1883 buffer_put_string(m, hash.value, hash.length);
1884
1885 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
1886
1887 gss_release_buffer(&minor, &hash);
1888
1889 /* Turn on getpwnam permissions */
1890 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
1891
1892 /* And credential updating, for when rekeying */
1893 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
1894
1895 return (0);
1896}
1897
1898int
1899mm_answer_gss_updatecreds(int socket, Buffer *m) {
1900 ssh_gssapi_ccache store;
1901 int ok;
1902
1903 if (!options.gss_authentication && !options.gss_keyex)
1904 fatal("%s: GSSAPI not enabled", __func__);
1905
1906 store.filename = buffer_get_string(m, NULL);
1907 store.envvar = buffer_get_string(m, NULL);
1908 store.envval = buffer_get_string(m, NULL);
1909
1910 ok = ssh_gssapi_update_creds(&store);
1911
1912 free(store.filename);
1913 free(store.envvar);
1914 free(store.envval);
1915
1916 buffer_clear(m);
1917 buffer_put_int(m, ok);
1918
1919 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
1920
1921 return(0);
1922}
1923
1827#endif /* GSSAPI */ 1924#endif /* GSSAPI */
1828 1925
diff --git a/monitor.h b/monitor.h
index d68f67458..ec41404c7 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 monitor { 73struct monitor {
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 69212aaf3..0e171a6a6 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -937,7 +937,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
937} 937}
938 938
939int 939int
940mm_ssh_gssapi_userok(char *user) 940mm_ssh_gssapi_userok(char *user, struct passwd *pw)
941{ 941{
942 Buffer m; 942 Buffer m;
943 int authenticated = 0; 943 int authenticated = 0;
@@ -954,5 +954,50 @@ mm_ssh_gssapi_userok(char *user)
954 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 954 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
955 return (authenticated); 955 return (authenticated);
956} 956}
957
958OM_uint32
959mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
960{
961 Buffer m;
962 OM_uint32 major;
963 u_int len;
964
965 buffer_init(&m);
966 buffer_put_string(&m, data->value, data->length);
967
968 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
969 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
970
971 major = buffer_get_int(&m);
972 hash->value = buffer_get_string(&m, &len);
973 hash->length = len;
974
975 buffer_free(&m);
976
977 return(major);
978}
979
980int
981mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
982{
983 Buffer m;
984 int ok;
985
986 buffer_init(&m);
987
988 buffer_put_cstring(&m, store->filename ? store->filename : "");
989 buffer_put_cstring(&m, store->envvar ? store->envvar : "");
990 buffer_put_cstring(&m, store->envval ? store->envval : "");
991
992 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m);
993 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m);
994
995 ok = buffer_get_int(&m);
996
997 buffer_free(&m);
998
999 return (ok);
1000}
1001
957#endif /* GSSAPI */ 1002#endif /* GSSAPI */
958 1003
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 9e032d204..7b2e89452 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -57,8 +57,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
57OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 57OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
58OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, 58OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
59 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); 59 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
60int mm_ssh_gssapi_userok(char *user); 60int mm_ssh_gssapi_userok(char *user, struct passwd *);
61OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 61OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
62OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
63int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
62#endif 64#endif
63 65
64#ifdef USE_PAM 66#ifdef USE_PAM
diff --git a/readconf.c b/readconf.c
index f63894f9c..99e03ee1f 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,
@@ -199,10 +201,20 @@ static struct {
199 /* Sometimes-unsupported options */ 201 /* Sometimes-unsupported options */
200#if defined(GSSAPI) 202#if defined(GSSAPI)
201 { "gssapiauthentication", oGssAuthentication }, 203 { "gssapiauthentication", oGssAuthentication },
204 { "gssapikeyexchange", oGssKeyEx },
202 { "gssapidelegatecredentials", oGssDelegateCreds }, 205 { "gssapidelegatecredentials", oGssDelegateCreds },
206 { "gssapitrustdns", oGssTrustDns },
207 { "gssapiclientidentity", oGssClientIdentity },
208 { "gssapiserveridentity", oGssServerIdentity },
209 { "gssapirenewalforcesrekey", oGssRenewalRekey },
203# else 210# else
204 { "gssapiauthentication", oUnsupported }, 211 { "gssapiauthentication", oUnsupported },
212 { "gssapikeyexchange", oUnsupported },
205 { "gssapidelegatecredentials", oUnsupported }, 213 { "gssapidelegatecredentials", oUnsupported },
214 { "gssapitrustdns", oUnsupported },
215 { "gssapiclientidentity", oUnsupported },
216 { "gssapiserveridentity", oUnsupported },
217 { "gssapirenewalforcesrekey", oUnsupported },
206#endif 218#endif
207#ifdef ENABLE_PKCS11 219#ifdef ENABLE_PKCS11
208 { "smartcarddevice", oPKCS11Provider }, 220 { "smartcarddevice", oPKCS11Provider },
@@ -976,10 +988,30 @@ parse_time:
976 intptr = &options->gss_authentication; 988 intptr = &options->gss_authentication;
977 goto parse_flag; 989 goto parse_flag;
978 990
991 case oGssKeyEx:
992 intptr = &options->gss_keyex;
993 goto parse_flag;
994
979 case oGssDelegateCreds: 995 case oGssDelegateCreds:
980 intptr = &options->gss_deleg_creds; 996 intptr = &options->gss_deleg_creds;
981 goto parse_flag; 997 goto parse_flag;
982 998
999 case oGssTrustDns:
1000 intptr = &options->gss_trust_dns;
1001 goto parse_flag;
1002
1003 case oGssClientIdentity:
1004 charptr = &options->gss_client_identity;
1005 goto parse_string;
1006
1007 case oGssServerIdentity:
1008 charptr = &options->gss_server_identity;
1009 goto parse_string;
1010
1011 case oGssRenewalRekey:
1012 intptr = &options->gss_renewal_rekey;
1013 goto parse_flag;
1014
983 case oBatchMode: 1015 case oBatchMode:
984 intptr = &options->batch_mode; 1016 intptr = &options->batch_mode;
985 goto parse_flag; 1017 goto parse_flag;
@@ -1790,7 +1822,12 @@ initialize_options(Options * options)
1790 options->pubkey_authentication = -1; 1822 options->pubkey_authentication = -1;
1791 options->challenge_response_authentication = -1; 1823 options->challenge_response_authentication = -1;
1792 options->gss_authentication = -1; 1824 options->gss_authentication = -1;
1825 options->gss_keyex = -1;
1793 options->gss_deleg_creds = -1; 1826 options->gss_deleg_creds = -1;
1827 options->gss_trust_dns = -1;
1828 options->gss_renewal_rekey = -1;
1829 options->gss_client_identity = NULL;
1830 options->gss_server_identity = NULL;
1794 options->password_authentication = -1; 1831 options->password_authentication = -1;
1795 options->kbd_interactive_authentication = -1; 1832 options->kbd_interactive_authentication = -1;
1796 options->kbd_interactive_devices = NULL; 1833 options->kbd_interactive_devices = NULL;
@@ -1930,8 +1967,14 @@ fill_default_options(Options * options)
1930 options->challenge_response_authentication = 1; 1967 options->challenge_response_authentication = 1;
1931 if (options->gss_authentication == -1) 1968 if (options->gss_authentication == -1)
1932 options->gss_authentication = 0; 1969 options->gss_authentication = 0;
1970 if (options->gss_keyex == -1)
1971 options->gss_keyex = 0;
1933 if (options->gss_deleg_creds == -1) 1972 if (options->gss_deleg_creds == -1)
1934 options->gss_deleg_creds = 0; 1973 options->gss_deleg_creds = 0;
1974 if (options->gss_trust_dns == -1)
1975 options->gss_trust_dns = 0;
1976 if (options->gss_renewal_rekey == -1)
1977 options->gss_renewal_rekey = 0;
1935 if (options->password_authentication == -1) 1978 if (options->password_authentication == -1)
1936 options->password_authentication = 1; 1979 options->password_authentication = 1;
1937 if (options->kbd_interactive_authentication == -1) 1980 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index 22fe5c187..d61161a84 100644
--- a/readconf.h
+++ b/readconf.h
@@ -42,7 +42,12 @@ typedef struct {
42 int challenge_response_authentication; 42 int challenge_response_authentication;
43 /* Try S/Key or TIS, authentication. */ 43 /* Try S/Key or TIS, authentication. */
44 int gss_authentication; /* Try GSS authentication */ 44 int gss_authentication; /* Try GSS authentication */
45 int gss_keyex; /* Try GSS key exchange */
45 int gss_deleg_creds; /* Delegate GSS credentials */ 46 int gss_deleg_creds; /* Delegate GSS credentials */
47 int gss_trust_dns; /* Trust DNS for GSS canonicalization */
48 int gss_renewal_rekey; /* Credential renewal forces rekey */
49 char *gss_client_identity; /* Principal to initiate GSSAPI with */
50 char *gss_server_identity; /* GSSAPI target principal */
46 int password_authentication; /* Try password 51 int password_authentication; /* Try password
47 * authentication. */ 52 * authentication. */
48 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 53 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
diff --git a/servconf.c b/servconf.c
index 2c321a4ad..8ba745170 100644
--- a/servconf.c
+++ b/servconf.c
@@ -113,8 +113,10 @@ initialize_server_options(ServerOptions *options)
113 options->kerberos_ticket_cleanup = -1; 113 options->kerberos_ticket_cleanup = -1;
114 options->kerberos_get_afs_token = -1; 114 options->kerberos_get_afs_token = -1;
115 options->gss_authentication=-1; 115 options->gss_authentication=-1;
116 options->gss_keyex = -1;
116 options->gss_cleanup_creds = -1; 117 options->gss_cleanup_creds = -1;
117 options->gss_strict_acceptor = -1; 118 options->gss_strict_acceptor = -1;
119 options->gss_store_rekey = -1;
118 options->password_authentication = -1; 120 options->password_authentication = -1;
119 options->kbd_interactive_authentication = -1; 121 options->kbd_interactive_authentication = -1;
120 options->challenge_response_authentication = -1; 122 options->challenge_response_authentication = -1;
@@ -268,10 +270,14 @@ fill_default_server_options(ServerOptions *options)
268 options->kerberos_get_afs_token = 0; 270 options->kerberos_get_afs_token = 0;
269 if (options->gss_authentication == -1) 271 if (options->gss_authentication == -1)
270 options->gss_authentication = 0; 272 options->gss_authentication = 0;
273 if (options->gss_keyex == -1)
274 options->gss_keyex = 0;
271 if (options->gss_cleanup_creds == -1) 275 if (options->gss_cleanup_creds == -1)
272 options->gss_cleanup_creds = 1; 276 options->gss_cleanup_creds = 1;
273 if (options->gss_strict_acceptor == -1) 277 if (options->gss_strict_acceptor == -1)
274 options->gss_strict_acceptor = 1; 278 options->gss_strict_acceptor = 1;
279 if (options->gss_store_rekey == -1)
280 options->gss_store_rekey = 0;
275 if (options->password_authentication == -1) 281 if (options->password_authentication == -1)
276 options->password_authentication = 1; 282 options->password_authentication = 1;
277 if (options->kbd_interactive_authentication == -1) 283 if (options->kbd_interactive_authentication == -1)
@@ -410,6 +416,7 @@ typedef enum {
410 sHostKeyAlgorithms, 416 sHostKeyAlgorithms,
411 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 417 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
412 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 418 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
419 sGssKeyEx, sGssStoreRekey,
413 sAcceptEnv, sPermitTunnel, 420 sAcceptEnv, sPermitTunnel,
414 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 421 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
415 sUsePrivilegeSeparation, sAllowAgentForwarding, 422 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -484,12 +491,20 @@ static struct {
484#ifdef GSSAPI 491#ifdef GSSAPI
485 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 492 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
486 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 493 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
494 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
487 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 495 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
496 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
497 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
488#else 498#else
489 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 499 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
490 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 500 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
501 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
491 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 502 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
503 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
504 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
492#endif 505#endif
506 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
507 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
493 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 508 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
494 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 509 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
495 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 510 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1253,6 +1268,10 @@ process_server_config_line(ServerOptions *options, char *line,
1253 intptr = &options->gss_authentication; 1268 intptr = &options->gss_authentication;
1254 goto parse_flag; 1269 goto parse_flag;
1255 1270
1271 case sGssKeyEx:
1272 intptr = &options->gss_keyex;
1273 goto parse_flag;
1274
1256 case sGssCleanupCreds: 1275 case sGssCleanupCreds:
1257 intptr = &options->gss_cleanup_creds; 1276 intptr = &options->gss_cleanup_creds;
1258 goto parse_flag; 1277 goto parse_flag;
@@ -1261,6 +1280,10 @@ process_server_config_line(ServerOptions *options, char *line,
1261 intptr = &options->gss_strict_acceptor; 1280 intptr = &options->gss_strict_acceptor;
1262 goto parse_flag; 1281 goto parse_flag;
1263 1282
1283 case sGssStoreRekey:
1284 intptr = &options->gss_store_rekey;
1285 goto parse_flag;
1286
1264 case sPasswordAuthentication: 1287 case sPasswordAuthentication:
1265 intptr = &options->password_authentication; 1288 intptr = &options->password_authentication;
1266 goto parse_flag; 1289 goto parse_flag;
@@ -2301,7 +2324,10 @@ dump_config(ServerOptions *o)
2301#endif 2324#endif
2302#ifdef GSSAPI 2325#ifdef GSSAPI
2303 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2326 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2327 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2304 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2328 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2329 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2330 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2305#endif 2331#endif
2306 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2332 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2307 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2333 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 1dca702e6..641e93c8f 100644
--- a/servconf.h
+++ b/servconf.h
@@ -119,8 +119,10 @@ typedef struct {
119 int kerberos_get_afs_token; /* If true, try to get AFS token if 119 int kerberos_get_afs_token; /* If true, try to get AFS token if
120 * authenticated with Kerberos. */ 120 * authenticated with Kerberos. */
121 int gss_authentication; /* If true, permit GSSAPI authentication */ 121 int gss_authentication; /* If true, permit GSSAPI authentication */
122 int gss_keyex; /* If true, permit GSSAPI key exchange */
122 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 123 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
123 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 124 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
125 int gss_store_rekey;
124 int password_authentication; /* If true, permit password 126 int password_authentication; /* If true, permit password
125 * authentication. */ 127 * authentication. */
126 int kbd_interactive_authentication; /* If true, permit */ 128 int kbd_interactive_authentication; /* If true, permit */
diff --git a/ssh-gss.h b/ssh-gss.h
index 6593e422d..919660a03 100644
--- a/ssh-gss.h
+++ b/ssh-gss.h
@@ -1,6 +1,6 @@
1/* $OpenBSD: ssh-gss.h,v 1.12 2017/06/24 06:34:38 djm Exp $ */ 1/* $OpenBSD: ssh-gss.h,v 1.12 2017/06/24 06:34:38 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,17 +136,33 @@ 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);
131const char *ssh_gssapi_displayname(void); 158const char *ssh_gssapi_displayname(void);
132 159
160char *ssh_gssapi_server_mechanisms(void);
161int ssh_gssapi_oid_table_ok(void);
162
163int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
164void ssh_gssapi_rekey_creds(void);
165
133#endif /* GSSAPI */ 166#endif /* GSSAPI */
134 167
135#endif /* _SSH_GSS_H */ 168#endif /* _SSH_GSS_H */
diff --git a/ssh_config b/ssh_config
index c12f5ef52..bcb9f153d 100644
--- a/ssh_config
+++ b/ssh_config
@@ -24,6 +24,8 @@
24# HostbasedAuthentication no 24# HostbasedAuthentication no
25# GSSAPIAuthentication no 25# GSSAPIAuthentication no
26# GSSAPIDelegateCredentials no 26# GSSAPIDelegateCredentials no
27# GSSAPIKeyExchange no
28# GSSAPITrustDNS no
27# BatchMode no 29# BatchMode no
28# CheckHostIP yes 30# CheckHostIP yes
29# AddressFamily any 31# AddressFamily any
diff --git a/ssh_config.5 b/ssh_config.5
index eab8dd01c..9a06a757a 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -720,10 +720,42 @@ The default is
720Specifies whether user authentication based on GSSAPI is allowed. 720Specifies whether user authentication based on GSSAPI is allowed.
721The default is 721The default is
722.Cm no . 722.Cm no .
723.It Cm GSSAPIKeyExchange
724Specifies whether key exchange based on GSSAPI may be used. When using
725GSSAPI key exchange the server need not have a host key.
726The default is
727.Cm no .
728.It Cm GSSAPIClientIdentity
729If set, specifies the GSSAPI client identity that ssh should use when
730connecting to the server. The default is unset, which means that the default
731identity will be used.
732.It Cm GSSAPIServerIdentity
733If set, specifies the GSSAPI server identity that ssh should expect when
734connecting to the server. The default is unset, which means that the
735expected GSSAPI server identity will be determined from the target
736hostname.
723.It Cm GSSAPIDelegateCredentials 737.It Cm GSSAPIDelegateCredentials
724Forward (delegate) credentials to the server. 738Forward (delegate) credentials to the server.
725The default is 739The default is
726.Cm no . 740.Cm no .
741.It Cm GSSAPIRenewalForcesRekey
742If set to
743.Cm yes
744then renewal of the client's GSSAPI credentials will force the rekeying of the
745ssh connection. With a compatible server, this can delegate the renewed
746credentials to a session on the server.
747The default is
748.Cm no .
749.It Cm GSSAPITrustDns
750Set to
751.Cm yes
752to indicate that the DNS is trusted to securely canonicalize
753the name of the host being connected to. If
754.Cm no ,
755the hostname entered on the
756command line will be passed untouched to the GSSAPI library.
757The default is
758.Cm no .
727.It Cm HashKnownHosts 759.It Cm HashKnownHosts
728Indicates that 760Indicates that
729.Xr ssh 1 761.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index be9397e48..c22477f59 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,35 @@ 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 }
226 }
227#endif
228
195 if (options.rekey_limit || options.rekey_interval) 229 if (options.rekey_limit || options.rekey_interval)
196 packet_set_rekey_limits(options.rekey_limit, 230 packet_set_rekey_limits(options.rekey_limit,
197 options.rekey_interval); 231 options.rekey_interval);
@@ -213,15 +247,41 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
213# endif 247# endif
214#endif 248#endif
215 kex->kex[KEX_C25519_SHA256] = kexc25519_client; 249 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
250#ifdef GSSAPI
251 if (options.gss_keyex) {
252 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
253 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
254 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
255 }
256#endif
216 kex->client_version_string=client_version_string; 257 kex->client_version_string=client_version_string;
217 kex->server_version_string=server_version_string; 258 kex->server_version_string=server_version_string;
218 kex->verify_host_key=&verify_host_key_callback; 259 kex->verify_host_key=&verify_host_key_callback;
219 260
261#ifdef GSSAPI
262 if (options.gss_keyex) {
263 kex->gss_deleg_creds = options.gss_deleg_creds;
264 kex->gss_trust_dns = options.gss_trust_dns;
265 kex->gss_client = options.gss_client_identity;
266 kex->gss_host = gss_host;
267 }
268#endif
269
220 ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done); 270 ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done);
221 271
222 /* remove ext-info from the KEX proposals for rekeying */ 272 /* remove ext-info from the KEX proposals for rekeying */
223 myproposal[PROPOSAL_KEX_ALGS] = 273 myproposal[PROPOSAL_KEX_ALGS] =
224 compat_kex_proposal(options.kex_algorithms); 274 compat_kex_proposal(options.kex_algorithms);
275#ifdef GSSAPI
276 /* repair myproposal after it was crumpled by the */
277 /* ext-info removal above */
278 if (gss) {
279 orig = myproposal[PROPOSAL_KEX_ALGS];
280 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
281 "%s,%s", gss, orig);
282 free(gss);
283 }
284#endif
225 if ((r = kex_prop2buf(kex->my, myproposal)) != 0) 285 if ((r = kex_prop2buf(kex->my, myproposal)) != 0)
226 fatal("kex_prop2buf: %s", ssh_err(r)); 286 fatal("kex_prop2buf: %s", ssh_err(r));
227 287
@@ -311,6 +371,7 @@ int input_gssapi_token(int type, u_int32_t, struct ssh *);
311int input_gssapi_hash(int type, u_int32_t, struct ssh *); 371int input_gssapi_hash(int type, u_int32_t, struct ssh *);
312int input_gssapi_error(int, u_int32_t, struct ssh *); 372int input_gssapi_error(int, u_int32_t, struct ssh *);
313int input_gssapi_errtok(int, u_int32_t, struct ssh *); 373int input_gssapi_errtok(int, u_int32_t, struct ssh *);
374int userauth_gsskeyex(Authctxt *authctxt);
314#endif 375#endif
315 376
316void userauth(Authctxt *, char *); 377void userauth(Authctxt *, char *);
@@ -327,6 +388,11 @@ static char *authmethods_get(void);
327 388
328Authmethod authmethods[] = { 389Authmethod authmethods[] = {
329#ifdef GSSAPI 390#ifdef GSSAPI
391 {"gssapi-keyex",
392 userauth_gsskeyex,
393 NULL,
394 &options.gss_authentication,
395 NULL},
330 {"gssapi-with-mic", 396 {"gssapi-with-mic",
331 userauth_gssapi, 397 userauth_gssapi,
332 NULL, 398 NULL,
@@ -654,25 +720,40 @@ userauth_gssapi(Authctxt *authctxt)
654 static u_int mech = 0; 720 static u_int mech = 0;
655 OM_uint32 min; 721 OM_uint32 min;
656 int ok = 0; 722 int ok = 0;
723 char *gss_host;
724
725 if (options.gss_server_identity)
726 gss_host = xstrdup(options.gss_server_identity);
727 else if (options.gss_trust_dns)
728 gss_host = remote_hostname(active_state);
729 else
730 gss_host = xstrdup(authctxt->host);
657 731
658 /* Try one GSSAPI method at a time, rather than sending them all at 732 /* Try one GSSAPI method at a time, rather than sending them all at
659 * once. */ 733 * once. */
660 734
661 if (gss_supported == NULL) 735 if (gss_supported == NULL)
662 gss_indicate_mechs(&min, &gss_supported); 736 if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
737 gss_supported = NULL;
738 free(gss_host);
739 return 0;
740 }
663 741
664 /* Check to see if the mechanism is usable before we offer it */ 742 /* Check to see if the mechanism is usable before we offer it */
665 while (mech < gss_supported->count && !ok) { 743 while (mech < gss_supported->count && !ok) {
666 /* My DER encoding requires length<128 */ 744 /* My DER encoding requires length<128 */
667 if (gss_supported->elements[mech].length < 128 && 745 if (gss_supported->elements[mech].length < 128 &&
668 ssh_gssapi_check_mechanism(&gssctxt, 746 ssh_gssapi_check_mechanism(&gssctxt,
669 &gss_supported->elements[mech], authctxt->host)) { 747 &gss_supported->elements[mech], gss_host,
748 options.gss_client_identity)) {
670 ok = 1; /* Mechanism works */ 749 ok = 1; /* Mechanism works */
671 } else { 750 } else {
672 mech++; 751 mech++;
673 } 752 }
674 } 753 }
675 754
755 free(gss_host);
756
676 if (!ok) 757 if (!ok)
677 return 0; 758 return 0;
678 759
@@ -763,8 +844,8 @@ input_gssapi_response(int type, u_int32_t plen, struct ssh *ssh)
763{ 844{
764 Authctxt *authctxt = ssh->authctxt; 845 Authctxt *authctxt = ssh->authctxt;
765 Gssctxt *gssctxt; 846 Gssctxt *gssctxt;
766 int oidlen; 847 u_int oidlen;
767 char *oidv; 848 u_char *oidv;
768 849
769 if (authctxt == NULL) 850 if (authctxt == NULL)
770 fatal("input_gssapi_response: no authentication context"); 851 fatal("input_gssapi_response: no authentication context");
@@ -877,6 +958,48 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
877 free(lang); 958 free(lang);
878 return 0; 959 return 0;
879} 960}
961
962int
963userauth_gsskeyex(Authctxt *authctxt)
964{
965 Buffer b;
966 gss_buffer_desc gssbuf;
967 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
968 OM_uint32 ms;
969
970 static int attempt = 0;
971 if (attempt++ >= 1)
972 return (0);
973
974 if (gss_kex_context == NULL) {
975 debug("No valid Key exchange context");
976 return (0);
977 }
978
979 ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
980 "gssapi-keyex");
981
982 gssbuf.value = buffer_ptr(&b);
983 gssbuf.length = buffer_len(&b);
984
985 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
986 buffer_free(&b);
987 return (0);
988 }
989
990 packet_start(SSH2_MSG_USERAUTH_REQUEST);
991 packet_put_cstring(authctxt->server_user);
992 packet_put_cstring(authctxt->service);
993 packet_put_cstring(authctxt->method->name);
994 packet_put_string(mic.value, mic.length);
995 packet_send();
996
997 buffer_free(&b);
998 gss_release_buffer(&ms, &mic);
999
1000 return (1);
1001}
1002
880#endif /* GSSAPI */ 1003#endif /* GSSAPI */
881 1004
882int 1005int
diff --git a/sshd.c b/sshd.c
index 51a1aaf6e..45e50fac3 100644
--- a/sshd.c
+++ b/sshd.c
@@ -122,6 +122,10 @@
122#include "version.h" 122#include "version.h"
123#include "ssherr.h" 123#include "ssherr.h"
124 124
125#ifdef USE_SECURITY_SESSION_API
126#include <Security/AuthSession.h>
127#endif
128
125/* Re-exec fds */ 129/* Re-exec fds */
126#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) 130#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
127#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) 131#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2)
@@ -529,7 +533,7 @@ privsep_preauth_child(void)
529 533
530#ifdef GSSAPI 534#ifdef GSSAPI
531 /* Cache supported mechanism OIDs for later use */ 535 /* Cache supported mechanism OIDs for later use */
532 if (options.gss_authentication) 536 if (options.gss_authentication || options.gss_keyex)
533 ssh_gssapi_prepare_supported_oids(); 537 ssh_gssapi_prepare_supported_oids();
534#endif 538#endif
535 539
@@ -1708,10 +1712,13 @@ main(int ac, char **av)
1708 key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp); 1712 key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp);
1709 free(fp); 1713 free(fp);
1710 } 1714 }
1715#ifndef GSSAPI
1716 /* The GSSAPI key exchange can run without a host key */
1711 if (!sensitive_data.have_ssh2_key) { 1717 if (!sensitive_data.have_ssh2_key) {
1712 logit("sshd: no hostkeys available -- exiting."); 1718 logit("sshd: no hostkeys available -- exiting.");
1713 exit(1); 1719 exit(1);
1714 } 1720 }
1721#endif
1715 1722
1716 /* 1723 /*
1717 * Load certificates. They are stored in an array at identical 1724 * Load certificates. They are stored in an array at identical
@@ -1987,6 +1994,60 @@ main(int ac, char **av)
1987 remote_ip, remote_port, laddr, ssh_local_port(ssh)); 1994 remote_ip, remote_port, laddr, ssh_local_port(ssh));
1988 free(laddr); 1995 free(laddr);
1989 1996
1997#ifdef USE_SECURITY_SESSION_API
1998 /*
1999 * Create a new security session for use by the new user login if
2000 * the current session is the root session or we are not launched
2001 * by inetd (eg: debugging mode or server mode). We do not
2002 * necessarily need to create a session if we are launched from
2003 * inetd because Panther xinetd will create a session for us.
2004 *
2005 * The only case where this logic will fail is if there is an
2006 * inetd running in a non-root session which is not creating
2007 * new sessions for us. Then all the users will end up in the
2008 * same session (bad).
2009 *
2010 * When the client exits, the session will be destroyed for us
2011 * automatically.
2012 *
2013 * We must create the session before any credentials are stored
2014 * (including AFS pags, which happens a few lines below).
2015 */
2016 {
2017 OSStatus err = 0;
2018 SecuritySessionId sid = 0;
2019 SessionAttributeBits sattrs = 0;
2020
2021 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2022 if (err)
2023 error("SessionGetInfo() failed with error %.8X",
2024 (unsigned) err);
2025 else
2026 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2027 (unsigned) sid, (unsigned) sattrs);
2028
2029 if (inetd_flag && !(sattrs & sessionIsRoot))
2030 debug("Running in inetd mode in a non-root session... "
2031 "assuming inetd created the session for us.");
2032 else {
2033 debug("Creating new security session...");
2034 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2035 if (err)
2036 error("SessionCreate() failed with error %.8X",
2037 (unsigned) err);
2038
2039 err = SessionGetInfo(callerSecuritySession, &sid,
2040 &sattrs);
2041 if (err)
2042 error("SessionGetInfo() failed with error %.8X",
2043 (unsigned) err);
2044 else
2045 debug("New Session ID is %.8X / Session Attributes are %.8X",
2046 (unsigned) sid, (unsigned) sattrs);
2047 }
2048 }
2049#endif
2050
1990 /* 2051 /*
1991 * We don't want to listen forever unless the other side 2052 * We don't want to listen forever unless the other side
1992 * successfully authenticates itself. So we set up an alarm which is 2053 * successfully authenticates itself. So we set up an alarm which is
@@ -2170,6 +2231,48 @@ do_ssh2_kex(void)
2170 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2231 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2171 list_hostkey_types()); 2232 list_hostkey_types());
2172 2233
2234#ifdef GSSAPI
2235 {
2236 char *orig;
2237 char *gss = NULL;
2238 char *newstr = NULL;
2239 orig = myproposal[PROPOSAL_KEX_ALGS];
2240
2241 /*
2242 * If we don't have a host key, then there's no point advertising
2243 * the other key exchange algorithms
2244 */
2245
2246 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2247 orig = NULL;
2248
2249 if (options.gss_keyex)
2250 gss = ssh_gssapi_server_mechanisms();
2251 else
2252 gss = NULL;
2253
2254 if (gss && orig)
2255 xasprintf(&newstr, "%s,%s", gss, orig);
2256 else if (gss)
2257 newstr = gss;
2258 else if (orig)
2259 newstr = orig;
2260
2261 /*
2262 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2263 * key alg, but we can't tell people about it unless its the only
2264 * host key algorithm we support
2265 */
2266 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2267 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2268
2269 if (newstr)
2270 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2271 else
2272 fatal("No supported key exchange algorithms");
2273 }
2274#endif
2275
2173 /* start key exchange */ 2276 /* start key exchange */
2174 if ((r = kex_setup(active_state, myproposal)) != 0) 2277 if ((r = kex_setup(active_state, myproposal)) != 0)
2175 fatal("kex_setup: %s", ssh_err(r)); 2278 fatal("kex_setup: %s", ssh_err(r));
@@ -2187,6 +2290,13 @@ do_ssh2_kex(void)
2187# endif 2290# endif
2188#endif 2291#endif
2189 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2292 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2293#ifdef GSSAPI
2294 if (options.gss_keyex) {
2295 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2296 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2297 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2298 }
2299#endif
2190 kex->server = 1; 2300 kex->server = 1;
2191 kex->client_version_string=client_version_string; 2301 kex->client_version_string=client_version_string;
2192 kex->server_version_string=server_version_string; 2302 kex->server_version_string=server_version_string;
diff --git a/sshd_config b/sshd_config
index 4eb2e02e0..c01dd6561 100644
--- a/sshd_config
+++ b/sshd_config
@@ -70,6 +70,8 @@ AuthorizedKeysFile .ssh/authorized_keys
70# GSSAPI options 70# GSSAPI options
71#GSSAPIAuthentication no 71#GSSAPIAuthentication no
72#GSSAPICleanupCredentials yes 72#GSSAPICleanupCredentials yes
73#GSSAPIStrictAcceptorCheck yes
74#GSSAPIKeyExchange no
73 75
74# Set this to 'yes' to enable PAM authentication, account processing, 76# Set this to 'yes' to enable PAM authentication, account processing,
75# and session processing. If this is enabled, PAM authentication will 77# and session processing. If this is enabled, PAM authentication will
diff --git a/sshd_config.5 b/sshd_config.5
index 251b7467f..0dbcb8daa 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -635,6 +635,11 @@ The default is
635Specifies whether user authentication based on GSSAPI is allowed. 635Specifies whether user authentication based on GSSAPI is allowed.
636The default is 636The default is
637.Cm no . 637.Cm no .
638.It Cm GSSAPIKeyExchange
639Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
640doesn't rely on ssh keys to verify host identity.
641The default is
642.Cm no .
638.It Cm GSSAPICleanupCredentials 643.It Cm GSSAPICleanupCredentials
639Specifies whether to automatically destroy the user's credentials cache 644Specifies whether to automatically destroy the user's credentials cache
640on logout. 645on logout.
@@ -654,6 +659,11 @@ machine's default store.
654This facility is provided to assist with operation on multi homed machines. 659This facility is provided to assist with operation on multi homed machines.
655The default is 660The default is
656.Cm yes . 661.Cm yes .
662.It Cm GSSAPIStoreCredentialsOnRekey
663Controls whether the user's GSSAPI credentials should be updated following a
664successful connection rekeying. This option can be used to accepted renewed
665or updated credentials from a compatible client. The default is
666.Cm no .
657.It Cm HostbasedAcceptedKeyTypes 667.It Cm HostbasedAcceptedKeyTypes
658Specifies the key types that will be accepted for hostbased authentication 668Specifies the key types that will be accepted for hostbased authentication
659as a comma-separated pattern list. 669as a comma-separated pattern list.
diff --git a/sshkey.c b/sshkey.c
index e91c54f53..c2cf0e036 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -112,6 +112,7 @@ static const struct keytype keytypes[] = {
112# endif /* OPENSSL_HAS_NISTP521 */ 112# endif /* OPENSSL_HAS_NISTP521 */
113# endif /* OPENSSL_HAS_ECC */ 113# endif /* OPENSSL_HAS_ECC */
114#endif /* WITH_OPENSSL */ 114#endif /* WITH_OPENSSL */
115 { "null", "null", KEY_NULL, 0, 0, 0 },
115 { NULL, NULL, -1, -1, 0, 0 } 116 { NULL, NULL, -1, -1, 0, 0 }
116}; 117};
117 118
@@ -200,7 +201,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
200 const struct keytype *kt; 201 const struct keytype *kt;
201 202
202 for (kt = keytypes; kt->type != -1; kt++) { 203 for (kt = keytypes; kt->type != -1; kt++) {
203 if (kt->name == NULL) 204 if (kt->name == NULL || kt->type == KEY_NULL)
204 continue; 205 continue;
205 if (!include_sigonly && kt->sigonly) 206 if (!include_sigonly && kt->sigonly)
206 continue; 207 continue;
diff --git a/sshkey.h b/sshkey.h
index 9093eac51..b5d020cbf 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -61,6 +61,7 @@ enum sshkey_types {
61 KEY_DSA_CERT, 61 KEY_DSA_CERT,
62 KEY_ECDSA_CERT, 62 KEY_ECDSA_CERT,
63 KEY_ED25519_CERT, 63 KEY_ED25519_CERT,
64 KEY_NULL,
64 KEY_UNSPEC 65 KEY_UNSPEC
65}; 66};
66 67