summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2018-10-20 22:54:00 +0100
commit72b1d308e6400194ef6e4e7dd45bfa48fa39b5e6 (patch)
tree2a3b57ae5446f4273804064ccc42659adfc2a3b2
parent3d246f10429fc9a37b98eabef94fe8dc7c61002b (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: 2018-10-20 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.c54
-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.c280
-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.c341
-rw-r--r--kexgsss.c300
-rw-r--r--monitor.c122
-rw-r--r--monitor.h3
-rw-r--r--monitor_wrap.c53
-rw-r--r--monitor_wrap.h4
-rw-r--r--opacket.c2
-rw-r--r--opacket.h2
-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.c133
-rw-r--r--sshd.c110
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.510
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
37 files changed, 2099 insertions, 146 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 126b2c742..70050ffb6 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -100,6 +100,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
100 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ 100 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
101 kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ 101 kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \
102 kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ 102 kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \
103 kexgssc.o \
103 platform-pledge.o platform-tracing.o platform-misc.o 104 platform-pledge.o platform-tracing.o platform-misc.o
104 105
105SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ 106SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
@@ -113,7 +114,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
113 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ 114 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
114 auth2-none.o auth2-passwd.o auth2-pubkey.o \ 115 auth2-none.o auth2-passwd.o auth2-pubkey.o \
115 monitor.o monitor_wrap.o auth-krb5.o \ 116 monitor.o monitor_wrap.o auth-krb5.o \
116 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 117 auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
117 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ 118 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
118 sftp-server.o sftp-common.o \ 119 sftp-server.o sftp-common.o \
119 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ 120 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
diff --git a/auth-krb5.c b/auth-krb5.c
index 3096f1c8e..204752e1b 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 3ca3762cc..d8e6b4a3d 100644
--- a/auth.c
+++ b/auth.c
@@ -399,7 +399,8 @@ auth_root_allowed(struct ssh *ssh, const char *method)
399 case PERMIT_NO_PASSWD: 399 case PERMIT_NO_PASSWD:
400 if (strcmp(method, "publickey") == 0 || 400 if (strcmp(method, "publickey") == 0 ||
401 strcmp(method, "hostbased") == 0 || 401 strcmp(method, "hostbased") == 0 ||
402 strcmp(method, "gssapi-with-mic") == 0) 402 strcmp(method, "gssapi-with-mic") == 0 ||
403 strcmp(method, "gssapi-keyex") == 0)
403 return 1; 404 return 1;
404 break; 405 break;
405 case PERMIT_FORCED_ONLY: 406 case PERMIT_FORCED_ONLY:
@@ -738,99 +739,6 @@ fakepw(void)
738} 739}
739 740
740/* 741/*
741 * Returns the remote DNS hostname as a string. The returned string must not
742 * be freed. NB. this will usually trigger a DNS query the first time it is
743 * called.
744 * This function does additional checks on the hostname to mitigate some
745 * attacks on legacy rhosts-style authentication.
746 * XXX is RhostsRSAAuthentication vulnerable to these?
747 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
748 */
749
750static char *
751remote_hostname(struct ssh *ssh)
752{
753 struct sockaddr_storage from;
754 socklen_t fromlen;
755 struct addrinfo hints, *ai, *aitop;
756 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
757 const char *ntop = ssh_remote_ipaddr(ssh);
758
759 /* Get IP address of client. */
760 fromlen = sizeof(from);
761 memset(&from, 0, sizeof(from));
762 if (getpeername(ssh_packet_get_connection_in(ssh),
763 (struct sockaddr *)&from, &fromlen) < 0) {
764 debug("getpeername failed: %.100s", strerror(errno));
765 return strdup(ntop);
766 }
767
768 ipv64_normalise_mapped(&from, &fromlen);
769 if (from.ss_family == AF_INET6)
770 fromlen = sizeof(struct sockaddr_in6);
771
772 debug3("Trying to reverse map address %.100s.", ntop);
773 /* Map the IP address to a host name. */
774 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
775 NULL, 0, NI_NAMEREQD) != 0) {
776 /* Host name not found. Use ip address. */
777 return strdup(ntop);
778 }
779
780 /*
781 * if reverse lookup result looks like a numeric hostname,
782 * someone is trying to trick us by PTR record like following:
783 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
784 */
785 memset(&hints, 0, sizeof(hints));
786 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
787 hints.ai_flags = AI_NUMERICHOST;
788 if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
789 logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
790 name, ntop);
791 freeaddrinfo(ai);
792 return strdup(ntop);
793 }
794
795 /* Names are stored in lowercase. */
796 lowercase(name);
797
798 /*
799 * Map it back to an IP address and check that the given
800 * address actually is an address of this host. This is
801 * necessary because anyone with access to a name server can
802 * define arbitrary names for an IP address. Mapping from
803 * name to IP address can be trusted better (but can still be
804 * fooled if the intruder has access to the name server of
805 * the domain).
806 */
807 memset(&hints, 0, sizeof(hints));
808 hints.ai_family = from.ss_family;
809 hints.ai_socktype = SOCK_STREAM;
810 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
811 logit("reverse mapping checking getaddrinfo for %.700s "
812 "[%s] failed.", name, ntop);
813 return strdup(ntop);
814 }
815 /* Look for the address from the list of addresses. */
816 for (ai = aitop; ai; ai = ai->ai_next) {
817 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
818 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
819 (strcmp(ntop, ntop2) == 0))
820 break;
821 }
822 freeaddrinfo(aitop);
823 /* If we reached the end of the list, the address was not there. */
824 if (ai == NULL) {
825 /* Address not found for the host name. */
826 logit("Address %.100s maps to %.600s, but this does not "
827 "map back to the address.", ntop, name);
828 return strdup(ntop);
829 }
830 return strdup(name);
831}
832
833/*
834 * Return the canonical name of the host in the other side of the current 742 * Return the canonical name of the host in the other side of the current
835 * connection. The host name is cached, so it is efficient to call this 743 * connection. The host name is cached, so it is efficient to call this
836 * several times. 744 * several times.
diff --git a/auth2-gss.c b/auth2-gss.c
index 9351e0428..1f12bb113 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 djm Exp $ */ 1/* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 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
@@ -54,6 +54,46 @@ static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
54static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); 54static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
55static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 55static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
56 56
57/*
58 * The 'gssapi_keyex' userauth mechanism.
59 */
60static int
61userauth_gsskeyex(struct ssh *ssh)
62{
63 Authctxt *authctxt = ssh->authctxt;
64 int r, authenticated = 0;
65 struct sshbuf *b;
66 gss_buffer_desc mic, gssbuf;
67 u_char *p;
68 size_t len;
69
70 if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
71 (r = sshpkt_get_end(ssh)) != 0)
72 fatal("%s: %s", __func__, ssh_err(r));
73 if ((b = sshbuf_new()) == NULL)
74 fatal("%s: sshbuf_new failed", __func__);
75 mic.value = p;
76 mic.length = len;
77
78 ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
79 "gssapi-keyex");
80
81 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
82 fatal("%s: sshbuf_mutable_ptr failed", __func__);
83 gssbuf.length = sshbuf_len(b);
84
85 /* gss_kex_context is NULL with privsep, so we can't check it here */
86 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
87 &gssbuf, &mic))))
88 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
89 authctxt->pw));
90
91 sshbuf_free(b);
92 free(mic.value);
93
94 return (authenticated);
95}
96
57/* 97/*
58 * We only support those mechanisms that we know about (ie ones that we know 98 * We only support those mechanisms that we know about (ie ones that we know
59 * how to check local user kuserok and the like) 99 * how to check local user kuserok and the like)
@@ -260,7 +300,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
260 if ((r = sshpkt_get_end(ssh)) != 0) 300 if ((r = sshpkt_get_end(ssh)) != 0)
261 fatal("%s: %s", __func__, ssh_err(r)); 301 fatal("%s: %s", __func__, ssh_err(r));
262 302
263 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 303 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
304 authctxt->pw));
264 305
265 if ((!use_privsep || mm_is_monitor()) && 306 if ((!use_privsep || mm_is_monitor()) &&
266 (displayname = ssh_gssapi_displayname()) != NULL) 307 (displayname = ssh_gssapi_displayname()) != NULL)
@@ -306,7 +347,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
306 gssbuf.length = sshbuf_len(b); 347 gssbuf.length = sshbuf_len(b);
307 348
308 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) 349 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
309 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 350 authenticated =
351 PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
310 else 352 else
311 logit("GSSAPI MIC check failed"); 353 logit("GSSAPI MIC check failed");
312 354
@@ -326,6 +368,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
326 return 0; 368 return 0;
327} 369}
328 370
371Authmethod method_gsskeyex = {
372 "gssapi-keyex",
373 userauth_gsskeyex,
374 &options.gss_authentication
375};
376
329Authmethod method_gssapi = { 377Authmethod method_gssapi = {
330 "gssapi-with-mic", 378 "gssapi-with-mic",
331 userauth_gssapi, 379 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index 4d19957a6..a77742819 100644
--- a/auth2.c
+++ b/auth2.c
@@ -74,6 +74,7 @@ extern Authmethod method_passwd;
74extern Authmethod method_kbdint; 74extern Authmethod method_kbdint;
75extern Authmethod method_hostbased; 75extern Authmethod method_hostbased;
76#ifdef GSSAPI 76#ifdef GSSAPI
77extern Authmethod method_gsskeyex;
77extern Authmethod method_gssapi; 78extern Authmethod method_gssapi;
78#endif 79#endif
79 80
@@ -81,6 +82,7 @@ Authmethod *authmethods[] = {
81 &method_none, 82 &method_none,
82 &method_pubkey, 83 &method_pubkey,
83#ifdef GSSAPI 84#ifdef GSSAPI
85 &method_gsskeyex,
84 &method_gssapi, 86 &method_gssapi,
85#endif 87#endif
86 &method_passwd, 88 &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 8d312cdaa..1464634b0 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
@@ -1370,9 +1374,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
1370 break; 1374 break;
1371 1375
1372 /* Do channel operations unless rekeying in progress. */ 1376 /* Do channel operations unless rekeying in progress. */
1373 if (!ssh_packet_is_rekeying(ssh)) 1377 if (!ssh_packet_is_rekeying(ssh)) {
1374 channel_after_select(ssh, readset, writeset); 1378 channel_after_select(ssh, readset, writeset);
1375 1379
1380#ifdef GSSAPI
1381 if (options.gss_renewal_rekey &&
1382 ssh_gssapi_credentials_updated(NULL)) {
1383 debug("credentials updated - forcing rekey");
1384 need_rekeying = 1;
1385 }
1386#endif
1387 }
1388
1376 /* Buffer input from the connection. */ 1389 /* Buffer input from the connection. */
1377 client_process_net_input(readset); 1390 client_process_net_input(readset);
1378 1391
diff --git a/config.h.in b/config.h.in
index 91b65db8f..209760c7c 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1845,6 +1845,9 @@
1845/* Use btmp to log bad logins */ 1845/* Use btmp to log bad logins */
1846#undef USE_BTMP 1846#undef USE_BTMP
1847 1847
1848/* platform uses an in-memory credentials cache */
1849#undef USE_CCAPI
1850
1848/* Use libedit for sftp */ 1851/* Use libedit for sftp */
1849#undef USE_LIBEDIT 1852#undef USE_LIBEDIT
1850 1853
@@ -1860,6 +1863,9 @@
1860/* Use PIPES instead of a socketpair() */ 1863/* Use PIPES instead of a socketpair() */
1861#undef USE_PIPES 1864#undef USE_PIPES
1862 1865
1866/* platform has the Security Authorization Session API */
1867#undef USE_SECURITY_SESSION_API
1868
1863/* Define if you have Solaris privileges */ 1869/* Define if you have Solaris privileges */
1864#undef USE_SOLARIS_PRIVS 1870#undef USE_SOLARIS_PRIVS
1865 1871
diff --git a/configure.ac b/configure.ac
index 7379ab358..023e7cc55 100644
--- a/configure.ac
+++ b/configure.ac
@@ -664,6 +664,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
664 [Use tunnel device compatibility to OpenBSD]) 664 [Use tunnel device compatibility to OpenBSD])
665 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 665 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
666 [Prepend the address family to IP tunnel traffic]) 666 [Prepend the address family to IP tunnel traffic])
667 AC_MSG_CHECKING([if we have the Security Authorization Session API])
668 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
669 [SessionCreate(0, 0);],
670 [ac_cv_use_security_session_api="yes"
671 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
672 [platform has the Security Authorization Session API])
673 LIBS="$LIBS -framework Security"
674 AC_MSG_RESULT([yes])],
675 [ac_cv_use_security_session_api="no"
676 AC_MSG_RESULT([no])])
677 AC_MSG_CHECKING([if we have an in-memory credentials cache])
678 AC_TRY_COMPILE(
679 [#include <Kerberos/Kerberos.h>],
680 [cc_context_t c;
681 (void) cc_initialize (&c, 0, NULL, NULL);],
682 [AC_DEFINE([USE_CCAPI], [1],
683 [platform uses an in-memory credentials cache])
684 LIBS="$LIBS -framework Security"
685 AC_MSG_RESULT([yes])
686 if test "x$ac_cv_use_security_session_api" = "xno"; then
687 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
688 fi],
689 [AC_MSG_RESULT([no])]
690 )
667 m4_pattern_allow([AU_IPv]) 691 m4_pattern_allow([AU_IPv])
668 AC_CHECK_DECL([AU_IPv4], [], 692 AC_CHECK_DECL([AU_IPv4], [],
669 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 693 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
diff --git a/gss-genr.c b/gss-genr.c
index d56257b4a..491e62cee 100644
--- a/gss-genr.c
+++ b/gss-genr.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 djm Exp $ */ 1/* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -39,14 +39,37 @@
39#include "xmalloc.h" 39#include "xmalloc.h"
40#include "ssherr.h" 40#include "ssherr.h"
41#include "sshbuf.h" 41#include "sshbuf.h"
42#include "sshkey.h"
42#include "log.h" 43#include "log.h"
43#include "ssh2.h" 44#include "ssh2.h"
45#include "cipher.h"
46#include "kex.h"
47#include "digest.h"
44 48
45#include "ssh-gss.h" 49#include "ssh-gss.h"
46 50
47extern u_char *session_id2; 51extern u_char *session_id2;
48extern u_int session_id2_len; 52extern u_int session_id2_len;
49 53
54typedef struct {
55 char *encoded;
56 gss_OID oid;
57} ssh_gss_kex_mapping;
58
59/*
60 * XXX - It would be nice to find a more elegant way of handling the
61 * XXX passing of the key exchange context to the userauth routines
62 */
63
64Gssctxt *gss_kex_context = NULL;
65
66static ssh_gss_kex_mapping *gss_enc2oid = NULL;
67
68int
69ssh_gssapi_oid_table_ok(void) {
70 return (gss_enc2oid != NULL);
71}
72
50/* sshbuf_get for gss_buffer_desc */ 73/* sshbuf_get for gss_buffer_desc */
51int 74int
52ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) 75ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
@@ -62,6 +85,143 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
62 return 0; 85 return 0;
63} 86}
64 87
88/*
89 * Return a list of the gss-group1-sha1 mechanisms supported by this program
90 *
91 * We test mechanisms to ensure that we can use them, to avoid starting
92 * a key exchange with a bad mechanism
93 */
94
95char *
96ssh_gssapi_client_mechanisms(const char *host, const char *client) {
97 gss_OID_set gss_supported;
98 OM_uint32 min_status;
99
100 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
101 return NULL;
102
103 return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
104 host, client));
105}
106
107char *
108ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
109 const char *host, const char *client) {
110 struct sshbuf *buf;
111 size_t i;
112 int r, oidpos, enclen;
113 char *mechs, *encoded;
114 u_char digest[SSH_DIGEST_MAX_LENGTH];
115 char deroid[2];
116 struct ssh_digest_ctx *md;
117
118 if (gss_enc2oid != NULL) {
119 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
120 free(gss_enc2oid[i].encoded);
121 free(gss_enc2oid);
122 }
123
124 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
125 (gss_supported->count + 1));
126
127 if ((buf = sshbuf_new()) == NULL)
128 fatal("%s: sshbuf_new failed", __func__);
129
130 oidpos = 0;
131 for (i = 0; i < gss_supported->count; i++) {
132 if (gss_supported->elements[i].length < 128 &&
133 (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
134
135 deroid[0] = SSH_GSS_OIDTYPE;
136 deroid[1] = gss_supported->elements[i].length;
137
138 if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
139 ssh_digest_update(md, deroid, 2) != 0 ||
140 ssh_digest_update(md,
141 gss_supported->elements[i].elements,
142 gss_supported->elements[i].length) != 0 ||
143 ssh_digest_final(md, digest, sizeof(digest)) != 0)
144 fatal("%s: digest failed", __func__);
145
146 encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
147 * 2);
148 enclen = __b64_ntop(digest,
149 ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
150 ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
151
152 if (oidpos != 0) {
153 if ((r = sshbuf_put_u8(buf, ',')) != 0)
154 fatal("%s: buffer error: %s",
155 __func__, ssh_err(r));
156 }
157
158 if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID,
159 sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 ||
160 (r = sshbuf_put(buf, encoded, enclen)) != 0 ||
161 (r = sshbuf_put_u8(buf, ',')) != 0 ||
162 (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID,
163 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 ||
164 (r = sshbuf_put(buf, encoded, enclen)) != 0 ||
165 (r = sshbuf_put_u8(buf, ',')) != 0 ||
166 (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID,
167 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 0 ||
168 (r = sshbuf_put(buf, encoded, enclen)) != 0)
169 fatal("%s: buffer error: %s",
170 __func__, ssh_err(r));
171
172 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
173 gss_enc2oid[oidpos].encoded = encoded;
174 oidpos++;
175 }
176 }
177 gss_enc2oid[oidpos].oid = NULL;
178 gss_enc2oid[oidpos].encoded = NULL;
179
180 if ((mechs = sshbuf_dup_string(buf)) == NULL)
181 fatal("%s: sshbuf_dup_string failed", __func__);
182
183 if (strlen(mechs) == 0) {
184 free(mechs);
185 mechs = NULL;
186 }
187
188 return (mechs);
189}
190
191gss_OID
192ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
193 int i = 0;
194
195 switch (kex_type) {
196 case KEX_GSS_GRP1_SHA1:
197 if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
198 return GSS_C_NO_OID;
199 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
200 break;
201 case KEX_GSS_GRP14_SHA1:
202 if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
203 return GSS_C_NO_OID;
204 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
205 break;
206 case KEX_GSS_GEX_SHA1:
207 if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
208 return GSS_C_NO_OID;
209 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
210 break;
211 default:
212 return GSS_C_NO_OID;
213 }
214
215 while (gss_enc2oid[i].encoded != NULL &&
216 strcmp(name, gss_enc2oid[i].encoded) != 0)
217 i++;
218
219 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
220 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
221
222 return gss_enc2oid[i].oid;
223}
224
65/* Check that the OID in a data stream matches that in the context */ 225/* Check that the OID in a data stream matches that in the context */
66int 226int
67ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 227ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -218,7 +378,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
218 } 378 }
219 379
220 ctx->major = gss_init_sec_context(&ctx->minor, 380 ctx->major = gss_init_sec_context(&ctx->minor,
221 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 381 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
222 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 382 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
223 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 383 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
224 384
@@ -248,8 +408,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
248} 408}
249 409
250OM_uint32 410OM_uint32
411ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
412{
413 gss_buffer_desc gssbuf;
414 gss_name_t gssname;
415 OM_uint32 status;
416 gss_OID_set oidset;
417
418 gssbuf.value = (void *) name;
419 gssbuf.length = strlen(gssbuf.value);
420
421 gss_create_empty_oid_set(&status, &oidset);
422 gss_add_oid_set_member(&status, ctx->oid, &oidset);
423
424 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
425 GSS_C_NT_USER_NAME, &gssname);
426
427 if (!ctx->major)
428 ctx->major = gss_acquire_cred(&ctx->minor,
429 gssname, 0, oidset, GSS_C_INITIATE,
430 &ctx->client_creds, NULL, NULL);
431
432 gss_release_name(&status, &gssname);
433 gss_release_oid_set(&status, &oidset);
434
435 if (ctx->major)
436 ssh_gssapi_error(ctx);
437
438 return(ctx->major);
439}
440
441OM_uint32
251ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 442ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
252{ 443{
444 if (ctx == NULL)
445 return -1;
446
253 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 447 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
254 GSS_C_QOP_DEFAULT, buffer, hash))) 448 GSS_C_QOP_DEFAULT, buffer, hash)))
255 ssh_gssapi_error(ctx); 449 ssh_gssapi_error(ctx);
@@ -257,6 +451,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
257 return (ctx->major); 451 return (ctx->major);
258} 452}
259 453
454/* Priviledged when used by server */
455OM_uint32
456ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
457{
458 if (ctx == NULL)
459 return -1;
460
461 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
462 gssbuf, gssmic, NULL);
463
464 return (ctx->major);
465}
466
260void 467void
261ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, 468ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
262 const char *context) 469 const char *context)
@@ -273,11 +480,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
273} 480}
274 481
275int 482int
276ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 483ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
484 const char *client)
277{ 485{
278 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 486 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
279 OM_uint32 major, minor; 487 OM_uint32 major, minor;
280 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 488 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
489 Gssctxt *intctx = NULL;
490
491 if (ctx == NULL)
492 ctx = &intctx;
281 493
282 /* RFC 4462 says we MUST NOT do SPNEGO */ 494 /* RFC 4462 says we MUST NOT do SPNEGO */
283 if (oid->length == spnego_oid.length && 495 if (oid->length == spnego_oid.length &&
@@ -287,6 +499,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
287 ssh_gssapi_build_ctx(ctx); 499 ssh_gssapi_build_ctx(ctx);
288 ssh_gssapi_set_oid(*ctx, oid); 500 ssh_gssapi_set_oid(*ctx, oid);
289 major = ssh_gssapi_import_name(*ctx, host); 501 major = ssh_gssapi_import_name(*ctx, host);
502
503 if (!GSS_ERROR(major) && client)
504 major = ssh_gssapi_client_identity(*ctx, client);
505
290 if (!GSS_ERROR(major)) { 506 if (!GSS_ERROR(major)) {
291 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 507 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
292 NULL); 508 NULL);
@@ -296,10 +512,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
296 GSS_C_NO_BUFFER); 512 GSS_C_NO_BUFFER);
297 } 513 }
298 514
299 if (GSS_ERROR(major)) 515 if (GSS_ERROR(major) || intctx != NULL)
300 ssh_gssapi_delete_ctx(ctx); 516 ssh_gssapi_delete_ctx(ctx);
301 517
302 return (!GSS_ERROR(major)); 518 return (!GSS_ERROR(major));
303} 519}
304 520
521int
522ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
523 static gss_name_t saved_name = GSS_C_NO_NAME;
524 static OM_uint32 saved_lifetime = 0;
525 static gss_OID saved_mech = GSS_C_NO_OID;
526 static gss_name_t name;
527 static OM_uint32 last_call = 0;
528 OM_uint32 lifetime, now, major, minor;
529 int equal;
530
531 now = time(NULL);
532
533 if (ctxt) {
534 debug("Rekey has happened - updating saved versions");
535
536 if (saved_name != GSS_C_NO_NAME)
537 gss_release_name(&minor, &saved_name);
538
539 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
540 &saved_name, &saved_lifetime, NULL, NULL);
541
542 if (!GSS_ERROR(major)) {
543 saved_mech = ctxt->oid;
544 saved_lifetime+= now;
545 } else {
546 /* Handle the error */
547 }
548 return 0;
549 }
550
551 if (now - last_call < 10)
552 return 0;
553
554 last_call = now;
555
556 if (saved_mech == GSS_C_NO_OID)
557 return 0;
558
559 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
560 &name, &lifetime, NULL, NULL);
561 if (major == GSS_S_CREDENTIALS_EXPIRED)
562 return 0;
563 else if (GSS_ERROR(major))
564 return 0;
565
566 major = gss_compare_name(&minor, saved_name, name, &equal);
567 gss_release_name(&minor, &name);
568 if (GSS_ERROR(major))
569 return 0;
570
571 if (equal && (saved_lifetime < lifetime + now - 10))
572 return 1;
573
574 return 0;
575}
576
305#endif /* GSSAPI */ 577#endif /* GSSAPI */
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
index a151bc1e4..90f8692f5 100644
--- a/gss-serv-krb5.c
+++ b/gss-serv-krb5.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */ 1/* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -120,8 +120,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
120 krb5_error_code problem; 120 krb5_error_code problem;
121 krb5_principal princ; 121 krb5_principal princ;
122 OM_uint32 maj_status, min_status; 122 OM_uint32 maj_status, min_status;
123 int len;
124 const char *errmsg; 123 const char *errmsg;
124 const char *new_ccname;
125 125
126 if (client->creds == NULL) { 126 if (client->creds == NULL) {
127 debug("No credentials stored"); 127 debug("No credentials stored");
@@ -180,11 +180,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
180 return; 180 return;
181 } 181 }
182 182
183 client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); 183 new_ccname = krb5_cc_get_name(krb_context, ccache);
184
184 client->store.envvar = "KRB5CCNAME"; 185 client->store.envvar = "KRB5CCNAME";
185 len = strlen(client->store.filename) + 6; 186#ifdef USE_CCAPI
186 client->store.envval = xmalloc(len); 187 xasprintf(&client->store.envval, "API:%s", new_ccname);
187 snprintf(client->store.envval, len, "FILE:%s", client->store.filename); 188 client->store.filename = NULL;
189#else
190 xasprintf(&client->store.envval, "FILE:%s", new_ccname);
191 client->store.filename = xstrdup(new_ccname);
192#endif
188 193
189#ifdef USE_PAM 194#ifdef USE_PAM
190 if (options.use_pam) 195 if (options.use_pam)
@@ -196,6 +201,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
196 return; 201 return;
197} 202}
198 203
204int
205ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
206 ssh_gssapi_client *client)
207{
208 krb5_ccache ccache = NULL;
209 krb5_principal principal = NULL;
210 char *name = NULL;
211 krb5_error_code problem;
212 OM_uint32 maj_status, min_status;
213
214 if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
215 logit("krb5_cc_resolve(): %.100s",
216 krb5_get_err_text(krb_context, problem));
217 return 0;
218 }
219
220 /* Find out who the principal in this cache is */
221 if ((problem = krb5_cc_get_principal(krb_context, ccache,
222 &principal))) {
223 logit("krb5_cc_get_principal(): %.100s",
224 krb5_get_err_text(krb_context, problem));
225 krb5_cc_close(krb_context, ccache);
226 return 0;
227 }
228
229 if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
230 logit("krb5_unparse_name(): %.100s",
231 krb5_get_err_text(krb_context, problem));
232 krb5_free_principal(krb_context, principal);
233 krb5_cc_close(krb_context, ccache);
234 return 0;
235 }
236
237
238 if (strcmp(name,client->exportedname.value)!=0) {
239 debug("Name in local credentials cache differs. Not storing");
240 krb5_free_principal(krb_context, principal);
241 krb5_cc_close(krb_context, ccache);
242 krb5_free_unparsed_name(krb_context, name);
243 return 0;
244 }
245 krb5_free_unparsed_name(krb_context, name);
246
247 /* Name matches, so lets get on with it! */
248
249 if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
250 logit("krb5_cc_initialize(): %.100s",
251 krb5_get_err_text(krb_context, problem));
252 krb5_free_principal(krb_context, principal);
253 krb5_cc_close(krb_context, ccache);
254 return 0;
255 }
256
257 krb5_free_principal(krb_context, principal);
258
259 if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
260 ccache))) {
261 logit("gss_krb5_copy_ccache() failed. Sorry!");
262 krb5_cc_close(krb_context, ccache);
263 return 0;
264 }
265
266 return 1;
267}
268
199ssh_gssapi_mech gssapi_kerberos_mech = { 269ssh_gssapi_mech gssapi_kerberos_mech = {
200 "toWM5Slw5Ew8Mqkay+al2g==", 270 "toWM5Slw5Ew8Mqkay+al2g==",
201 "Kerberos", 271 "Kerberos",
@@ -203,7 +273,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
203 NULL, 273 NULL,
204 &ssh_gssapi_krb5_userok, 274 &ssh_gssapi_krb5_userok,
205 NULL, 275 NULL,
206 &ssh_gssapi_krb5_storecreds 276 &ssh_gssapi_krb5_storecreds,
277 &ssh_gssapi_krb5_updatecreds
207}; 278};
208 279
209#endif /* KRB5 */ 280#endif /* KRB5 */
diff --git a/gss-serv.c b/gss-serv.c
index ab3a15f0f..6c087a1b1 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -44,17 +44,22 @@
44#include "session.h" 44#include "session.h"
45#include "misc.h" 45#include "misc.h"
46#include "servconf.h" 46#include "servconf.h"
47#include "uidswap.h"
47 48
48#include "ssh-gss.h" 49#include "ssh-gss.h"
50#include "monitor_wrap.h"
51
52extern ServerOptions options;
49 53
50extern ServerOptions options; 54extern ServerOptions options;
51 55
52static ssh_gssapi_client gssapi_client = 56static ssh_gssapi_client gssapi_client =
53 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 57 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
54 GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; 58 GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL,
59 {NULL, NULL, NULL, NULL, NULL}, 0, 0};
55 60
56ssh_gssapi_mech gssapi_null_mech = 61ssh_gssapi_mech gssapi_null_mech =
57 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; 62 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
58 63
59#ifdef KRB5 64#ifdef KRB5
60extern ssh_gssapi_mech gssapi_kerberos_mech; 65extern ssh_gssapi_mech gssapi_kerberos_mech;
@@ -141,6 +146,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
141} 146}
142 147
143/* Unprivileged */ 148/* Unprivileged */
149char *
150ssh_gssapi_server_mechanisms(void) {
151 if (supported_oids == NULL)
152 ssh_gssapi_prepare_supported_oids();
153 return (ssh_gssapi_kex_mechs(supported_oids,
154 &ssh_gssapi_server_check_mech, NULL, NULL));
155}
156
157/* Unprivileged */
158int
159ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
160 const char *dummy) {
161 Gssctxt *ctx = NULL;
162 int res;
163
164 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
165 ssh_gssapi_delete_ctx(&ctx);
166
167 return (res);
168}
169
170/* Unprivileged */
144void 171void
145ssh_gssapi_supported_oids(gss_OID_set *oidset) 172ssh_gssapi_supported_oids(gss_OID_set *oidset)
146{ 173{
@@ -150,7 +177,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
150 gss_OID_set supported; 177 gss_OID_set supported;
151 178
152 gss_create_empty_oid_set(&min_status, oidset); 179 gss_create_empty_oid_set(&min_status, oidset);
153 gss_indicate_mechs(&min_status, &supported); 180
181 if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
182 return;
154 183
155 while (supported_mechs[i]->name != NULL) { 184 while (supported_mechs[i]->name != NULL) {
156 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 185 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -276,8 +305,48 @@ OM_uint32
276ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 305ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
277{ 306{
278 int i = 0; 307 int i = 0;
308 int equal = 0;
309 gss_name_t new_name = GSS_C_NO_NAME;
310 gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
311
312 if (options.gss_store_rekey && client->used && ctx->client_creds) {
313 if (client->mech->oid.length != ctx->oid->length ||
314 (memcmp(client->mech->oid.elements,
315 ctx->oid->elements, ctx->oid->length) !=0)) {
316 debug("Rekeyed credentials have different mechanism");
317 return GSS_S_COMPLETE;
318 }
319
320 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
321 ctx->client_creds, ctx->oid, &new_name,
322 NULL, NULL, NULL))) {
323 ssh_gssapi_error(ctx);
324 return (ctx->major);
325 }
326
327 ctx->major = gss_compare_name(&ctx->minor, client->name,
328 new_name, &equal);
329
330 if (GSS_ERROR(ctx->major)) {
331 ssh_gssapi_error(ctx);
332 return (ctx->major);
333 }
334
335 if (!equal) {
336 debug("Rekeyed credentials have different name");
337 return GSS_S_COMPLETE;
338 }
279 339
280 gss_buffer_desc ename; 340 debug("Marking rekeyed credentials for export");
341
342 gss_release_name(&ctx->minor, &client->name);
343 gss_release_cred(&ctx->minor, &client->creds);
344 client->name = new_name;
345 client->creds = ctx->client_creds;
346 ctx->client_creds = GSS_C_NO_CREDENTIAL;
347 client->updated = 1;
348 return GSS_S_COMPLETE;
349 }
281 350
282 client->mech = NULL; 351 client->mech = NULL;
283 352
@@ -292,6 +361,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
292 if (client->mech == NULL) 361 if (client->mech == NULL)
293 return GSS_S_FAILURE; 362 return GSS_S_FAILURE;
294 363
364 if (ctx->client_creds &&
365 (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
366 ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
367 ssh_gssapi_error(ctx);
368 return (ctx->major);
369 }
370
295 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 371 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
296 &client->displayname, NULL))) { 372 &client->displayname, NULL))) {
297 ssh_gssapi_error(ctx); 373 ssh_gssapi_error(ctx);
@@ -309,6 +385,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
309 return (ctx->major); 385 return (ctx->major);
310 } 386 }
311 387
388 gss_release_buffer(&ctx->minor, &ename);
389
312 /* We can't copy this structure, so we just move the pointer to it */ 390 /* We can't copy this structure, so we just move the pointer to it */
313 client->creds = ctx->client_creds; 391 client->creds = ctx->client_creds;
314 ctx->client_creds = GSS_C_NO_CREDENTIAL; 392 ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -356,7 +434,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
356 434
357/* Privileged */ 435/* Privileged */
358int 436int
359ssh_gssapi_userok(char *user) 437ssh_gssapi_userok(char *user, struct passwd *pw)
360{ 438{
361 OM_uint32 lmin; 439 OM_uint32 lmin;
362 440
@@ -366,9 +444,11 @@ ssh_gssapi_userok(char *user)
366 return 0; 444 return 0;
367 } 445 }
368 if (gssapi_client.mech && gssapi_client.mech->userok) 446 if (gssapi_client.mech && gssapi_client.mech->userok)
369 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 447 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
448 gssapi_client.used = 1;
449 gssapi_client.store.owner = pw;
370 return 1; 450 return 1;
371 else { 451 } else {
372 /* Destroy delegated credentials if userok fails */ 452 /* Destroy delegated credentials if userok fails */
373 gss_release_buffer(&lmin, &gssapi_client.displayname); 453 gss_release_buffer(&lmin, &gssapi_client.displayname);
374 gss_release_buffer(&lmin, &gssapi_client.exportedname); 454 gss_release_buffer(&lmin, &gssapi_client.exportedname);
@@ -382,14 +462,90 @@ ssh_gssapi_userok(char *user)
382 return (0); 462 return (0);
383} 463}
384 464
385/* Privileged */ 465/* These bits are only used for rekeying. The unpriviledged child is running
386OM_uint32 466 * as the user, the monitor is root.
387ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 467 *
468 * In the child, we want to :
469 * *) Ask the monitor to store our credentials into the store we specify
470 * *) If it succeeds, maybe do a PAM update
471 */
472
473/* Stuff for PAM */
474
475#ifdef USE_PAM
476static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
477 struct pam_response **resp, void *data)
388{ 478{
389 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 479 return (PAM_CONV_ERR);
390 gssbuf, gssmic, NULL); 480}
481#endif
391 482
392 return (ctx->major); 483void
484ssh_gssapi_rekey_creds(void) {
485 int ok;
486 int ret;
487#ifdef USE_PAM
488 pam_handle_t *pamh = NULL;
489 struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
490 char *envstr;
491#endif
492
493 if (gssapi_client.store.filename == NULL &&
494 gssapi_client.store.envval == NULL &&
495 gssapi_client.store.envvar == NULL)
496 return;
497
498 ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
499
500 if (!ok)
501 return;
502
503 debug("Rekeyed credentials stored successfully");
504
505 /* Actually managing to play with the ssh pam stack from here will
506 * be next to impossible. In any case, we may want different options
507 * for rekeying. So, use our own :)
508 */
509#ifdef USE_PAM
510 if (!use_privsep) {
511 debug("Not even going to try and do PAM with privsep disabled");
512 return;
513 }
514
515 ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
516 &pamconv, &pamh);
517 if (ret)
518 return;
519
520 xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
521 gssapi_client.store.envval);
522
523 ret = pam_putenv(pamh, envstr);
524 if (!ret)
525 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
526 pam_end(pamh, PAM_SUCCESS);
527#endif
528}
529
530int
531ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
532 int ok = 0;
533
534 /* Check we've got credentials to store */
535 if (!gssapi_client.updated)
536 return 0;
537
538 gssapi_client.updated = 0;
539
540 temporarily_use_uid(gssapi_client.store.owner);
541 if (gssapi_client.mech && gssapi_client.mech->updatecreds)
542 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
543 else
544 debug("No update function for this mechanism");
545
546 restore_uid();
547
548 return ok;
393} 549}
394 550
395/* Privileged */ 551/* Privileged */
diff --git a/kex.c b/kex.c
index 25f9f66f6..fb5bfaea5 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
@@ -653,6 +669,9 @@ kex_free(struct kex *kex)
653 sshbuf_free(kex->peer); 669 sshbuf_free(kex->peer);
654 sshbuf_free(kex->my); 670 sshbuf_free(kex->my);
655 free(kex->session_id); 671 free(kex->session_id);
672#ifdef GSSAPI
673 free(kex->gss_host);
674#endif /* GSSAPI */
656 free(kex->client_version_string); 675 free(kex->client_version_string);
657 free(kex->server_version_string); 676 free(kex->server_version_string);
658 free(kex->failed_choice); 677 free(kex->failed_choice);
diff --git a/kex.h b/kex.h
index 593de1208..4e5ead839 100644
--- a/kex.h
+++ b/kex.h
@@ -100,6 +100,9 @@ enum kex_exchange {
100 KEX_DH_GEX_SHA256, 100 KEX_DH_GEX_SHA256,
101 KEX_ECDH_SHA2, 101 KEX_ECDH_SHA2,
102 KEX_C25519_SHA256, 102 KEX_C25519_SHA256,
103 KEX_GSS_GRP1_SHA1,
104 KEX_GSS_GRP14_SHA1,
105 KEX_GSS_GEX_SHA1,
103 KEX_MAX 106 KEX_MAX
104}; 107};
105 108
@@ -148,6 +151,12 @@ struct kex {
148 u_int flags; 151 u_int flags;
149 int hash_alg; 152 int hash_alg;
150 int ec_nid; 153 int ec_nid;
154#ifdef GSSAPI
155 int gss_deleg_creds;
156 int gss_trust_dns;
157 char *gss_host;
158 char *gss_client;
159#endif
151 char *client_version_string; 160 char *client_version_string;
152 char *server_version_string; 161 char *server_version_string;
153 char *failed_choice; 162 char *failed_choice;
@@ -198,6 +207,11 @@ int kexecdh_server(struct ssh *);
198int kexc25519_client(struct ssh *); 207int kexc25519_client(struct ssh *);
199int kexc25519_server(struct ssh *); 208int kexc25519_server(struct ssh *);
200 209
210#ifdef GSSAPI
211int kexgss_client(struct ssh *);
212int kexgss_server(struct ssh *);
213#endif
214
201int kex_dh_hash(int, const char *, const char *, 215int kex_dh_hash(int, const char *, const char *,
202 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, 216 const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
203 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); 217 const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *);
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..3c8ae08dd
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,341 @@
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 "sshbuf.h"
38#include "ssh2.h"
39#include "sshkey.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 const BIGNUM *pub_key, *dh_p, *dh_g;
60 BIGNUM *p = NULL;
61 BIGNUM *g = NULL;
62 u_char *kbuf;
63 u_char *serverhostkey = NULL;
64 u_char *empty = "";
65 char *msg;
66 int type = 0;
67 int first = 1;
68 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
69 u_char hash[SSH_DIGEST_MAX_LENGTH];
70 size_t hashlen;
71
72 /* Initialise our GSSAPI world */
73 ssh_gssapi_build_ctx(&ctxt);
74 if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type)
75 == GSS_C_NO_OID)
76 fatal("Couldn't identify host exchange");
77
78 if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host))
79 fatal("Couldn't import hostname");
80
81 if (ssh->kex->gss_client &&
82 ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client))
83 fatal("Couldn't acquire client credentials");
84
85 switch (ssh->kex->kex_type) {
86 case KEX_GSS_GRP1_SHA1:
87 dh = dh_new_group1();
88 break;
89 case KEX_GSS_GRP14_SHA1:
90 dh = dh_new_group14();
91 break;
92 case KEX_GSS_GEX_SHA1:
93 debug("Doing group exchange\n");
94 nbits = dh_estimate(ssh->kex->we_need * 8);
95 packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
96 packet_put_int(min);
97 packet_put_int(nbits);
98 packet_put_int(max);
99
100 packet_send();
101
102 packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
103
104 if ((p = BN_new()) == NULL)
105 fatal("BN_new() failed");
106 packet_get_bignum2(p);
107 if ((g = BN_new()) == NULL)
108 fatal("BN_new() failed");
109 packet_get_bignum2(g);
110 packet_check_eom();
111
112 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
113 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
114 min, BN_num_bits(p), max);
115
116 dh = dh_new_group(g, p);
117 break;
118 default:
119 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
120 }
121
122 /* Step 1 - e is dh->pub_key */
123 dh_gen_key(dh, ssh->kex->we_need * 8);
124 DH_get0_key(dh, &pub_key, NULL);
125 DH_get0_pqg(dh, &dh_p, NULL, &dh_g);
126
127 /* This is f, we initialise it now to make life easier */
128 dh_server_pub = BN_new();
129 if (dh_server_pub == NULL)
130 fatal("dh_server_pub == NULL");
131
132 token_ptr = GSS_C_NO_BUFFER;
133
134 do {
135 debug("Calling gss_init_sec_context");
136
137 maj_status = ssh_gssapi_init_ctx(ctxt,
138 ssh->kex->gss_deleg_creds, token_ptr, &send_tok,
139 &ret_flags);
140
141 if (GSS_ERROR(maj_status)) {
142 if (send_tok.length != 0) {
143 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
144 packet_put_string(send_tok.value,
145 send_tok.length);
146 }
147 fatal("gss_init_context failed");
148 }
149
150 /* If we've got an old receive buffer get rid of it */
151 if (token_ptr != GSS_C_NO_BUFFER)
152 free(recv_tok.value);
153
154 if (maj_status == GSS_S_COMPLETE) {
155 /* If mutual state flag is not true, kex fails */
156 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
157 fatal("Mutual authentication failed");
158
159 /* If integ avail flag is not true kex fails */
160 if (!(ret_flags & GSS_C_INTEG_FLAG))
161 fatal("Integrity check failed");
162 }
163
164 /*
165 * If we have data to send, then the last message that we
166 * received cannot have been a 'complete'.
167 */
168 if (send_tok.length != 0) {
169 if (first) {
170 packet_start(SSH2_MSG_KEXGSS_INIT);
171 packet_put_string(send_tok.value,
172 send_tok.length);
173 packet_put_bignum2(pub_key);
174 first = 0;
175 } else {
176 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
177 packet_put_string(send_tok.value,
178 send_tok.length);
179 }
180 packet_send();
181 gss_release_buffer(&min_status, &send_tok);
182
183 /* If we've sent them data, they should reply */
184 do {
185 type = packet_read();
186 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
187 debug("Received KEXGSS_HOSTKEY");
188 if (serverhostkey)
189 fatal("Server host key received more than once");
190 serverhostkey =
191 packet_get_string(&slen);
192 }
193 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
194
195 switch (type) {
196 case SSH2_MSG_KEXGSS_CONTINUE:
197 debug("Received GSSAPI_CONTINUE");
198 if (maj_status == GSS_S_COMPLETE)
199 fatal("GSSAPI Continue received from server when complete");
200 recv_tok.value = packet_get_string(&strlen);
201 recv_tok.length = strlen;
202 break;
203 case SSH2_MSG_KEXGSS_COMPLETE:
204 debug("Received GSSAPI_COMPLETE");
205 packet_get_bignum2(dh_server_pub);
206 msg_tok.value = packet_get_string(&strlen);
207 msg_tok.length = strlen;
208
209 /* Is there a token included? */
210 if (packet_get_char()) {
211 recv_tok.value=
212 packet_get_string(&strlen);
213 recv_tok.length = strlen;
214 /* If we're already complete - protocol error */
215 if (maj_status == GSS_S_COMPLETE)
216 packet_disconnect("Protocol error: received token when complete");
217 } else {
218 /* No token included */
219 if (maj_status != GSS_S_COMPLETE)
220 packet_disconnect("Protocol error: did not receive final token");
221 }
222 break;
223 case SSH2_MSG_KEXGSS_ERROR:
224 debug("Received Error");
225 maj_status = packet_get_int();
226 min_status = packet_get_int();
227 msg = packet_get_string(NULL);
228 (void) packet_get_string_ptr(NULL);
229 fatal("GSSAPI Error: \n%.400s",msg);
230 default:
231 packet_disconnect("Protocol error: didn't expect packet type %d",
232 type);
233 }
234 token_ptr = &recv_tok;
235 } else {
236 /* No data, and not complete */
237 if (maj_status != GSS_S_COMPLETE)
238 fatal("Not complete, and no token output");
239 }
240 } while (maj_status & GSS_S_CONTINUE_NEEDED);
241
242 /*
243 * We _must_ have received a COMPLETE message in reply from the
244 * server, which will have set dh_server_pub and msg_tok
245 */
246
247 if (type != SSH2_MSG_KEXGSS_COMPLETE)
248 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
249
250 /* Check f in range [1, p-1] */
251 if (!dh_pub_is_valid(dh, dh_server_pub))
252 packet_disconnect("bad server public DH value");
253
254 /* compute K=f^x mod p */
255 klen = DH_size(dh);
256 kbuf = xmalloc(klen);
257 kout = DH_compute_key(kbuf, dh_server_pub, dh);
258 if (kout < 0)
259 fatal("DH_compute_key: failed");
260
261 shared_secret = BN_new();
262 if (shared_secret == NULL)
263 fatal("kexgss_client: BN_new failed");
264
265 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
266 fatal("kexdh_client: BN_bin2bn failed");
267
268 memset(kbuf, 0, klen);
269 free(kbuf);
270
271 hashlen = sizeof(hash);
272 switch (ssh->kex->kex_type) {
273 case KEX_GSS_GRP1_SHA1:
274 case KEX_GSS_GRP14_SHA1:
275 kex_dh_hash(
276 ssh->kex->hash_alg,
277 ssh->kex->client_version_string,
278 ssh->kex->server_version_string,
279 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
280 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
281 (serverhostkey ? serverhostkey : empty), slen,
282 pub_key, /* e */
283 dh_server_pub, /* f */
284 shared_secret, /* K */
285 hash, &hashlen
286 );
287 break;
288 case KEX_GSS_GEX_SHA1:
289 kexgex_hash(
290 ssh->kex->hash_alg,
291 ssh->kex->client_version_string,
292 ssh->kex->server_version_string,
293 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
294 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
295 (serverhostkey ? serverhostkey : empty), slen,
296 min, nbits, max,
297 dh_p, dh_g,
298 pub_key,
299 dh_server_pub,
300 shared_secret,
301 hash, &hashlen
302 );
303 break;
304 default:
305 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
306 }
307
308 gssbuf.value = hash;
309 gssbuf.length = hashlen;
310
311 /* Verify that the hash matches the MIC we just got. */
312 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
313 packet_disconnect("Hash's MIC didn't verify");
314
315 free(msg_tok.value);
316
317 DH_free(dh);
318 free(serverhostkey);
319 BN_clear_free(dh_server_pub);
320
321 /* save session id */
322 if (ssh->kex->session_id == NULL) {
323 ssh->kex->session_id_len = hashlen;
324 ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
325 memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
326 }
327
328 if (ssh->kex->gss_deleg_creds)
329 ssh_gssapi_credentials_updated(ctxt);
330
331 if (gss_kex_context == NULL)
332 gss_kex_context = ctxt;
333 else
334 ssh_gssapi_delete_ctx(&ctxt);
335
336 kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
337 BN_clear_free(shared_secret);
338 return kex_send_newkeys(ssh);
339}
340
341#endif /* GSSAPI */
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..18070f1d7
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,300 @@
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 "sshbuf.h"
36#include "ssh2.h"
37#include "sshkey.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 const BIGNUM *pub_key, *dh_p, *dh_g;
72 BIGNUM *shared_secret = NULL;
73 BIGNUM *dh_client_pub = NULL;
74 int type = 0;
75 gss_OID oid;
76 char *mechs;
77 u_char hash[SSH_DIGEST_MAX_LENGTH];
78 size_t hashlen;
79
80 /* Initialise GSSAPI */
81
82 /* If we're rekeying, privsep means that some of the private structures
83 * in the GSSAPI code are no longer available. This kludges them back
84 * into life
85 */
86 if (!ssh_gssapi_oid_table_ok()) {
87 mechs = ssh_gssapi_server_mechanisms();
88 free(mechs);
89 }
90
91 debug2("%s: Identifying %s", __func__, ssh->kex->name);
92 oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type);
93 if (oid == GSS_C_NO_OID)
94 fatal("Unknown gssapi mechanism");
95
96 debug2("%s: Acquiring credentials", __func__);
97
98 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
99 fatal("Unable to acquire credentials for the server");
100
101 switch (ssh->kex->kex_type) {
102 case KEX_GSS_GRP1_SHA1:
103 dh = dh_new_group1();
104 break;
105 case KEX_GSS_GRP14_SHA1:
106 dh = dh_new_group14();
107 break;
108 case KEX_GSS_GEX_SHA1:
109 debug("Doing group exchange");
110 packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
111 min = packet_get_int();
112 nbits = packet_get_int();
113 max = packet_get_int();
114 packet_check_eom();
115 if (max < min || nbits < min || max < nbits)
116 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
117 min, nbits, max);
118 dh = PRIVSEP(choose_dh(MAX(DH_GRP_MIN, min),
119 nbits, MIN(DH_GRP_MAX, max)));
120 if (dh == NULL)
121 packet_disconnect("Protocol error: no matching group found");
122 DH_get0_pqg(dh, &dh_p, NULL, &dh_g);
123
124 packet_start(SSH2_MSG_KEXGSS_GROUP);
125 packet_put_bignum2(dh_p);
126 packet_put_bignum2(dh_g);
127 packet_send();
128
129 packet_write_wait();
130 break;
131 default:
132 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
133 }
134
135 dh_gen_key(dh, ssh->kex->we_need * 8);
136
137 do {
138 debug("Wait SSH2_MSG_GSSAPI_INIT");
139 type = packet_read();
140 switch(type) {
141 case SSH2_MSG_KEXGSS_INIT:
142 if (dh_client_pub != NULL)
143 fatal("Received KEXGSS_INIT after initialising");
144 recv_tok.value = packet_get_string(&slen);
145 recv_tok.length = slen;
146
147 if ((dh_client_pub = BN_new()) == NULL)
148 fatal("dh_client_pub == NULL");
149
150 packet_get_bignum2(dh_client_pub);
151
152 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
153 break;
154 case SSH2_MSG_KEXGSS_CONTINUE:
155 recv_tok.value = packet_get_string(&slen);
156 recv_tok.length = slen;
157 break;
158 default:
159 packet_disconnect(
160 "Protocol error: didn't expect packet type %d",
161 type);
162 }
163
164 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
165 &send_tok, &ret_flags));
166
167 free(recv_tok.value);
168
169 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
170 fatal("Zero length token output when incomplete");
171
172 if (dh_client_pub == NULL)
173 fatal("No client public key");
174
175 if (maj_status & GSS_S_CONTINUE_NEEDED) {
176 debug("Sending GSSAPI_CONTINUE");
177 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
178 packet_put_string(send_tok.value, send_tok.length);
179 packet_send();
180 gss_release_buffer(&min_status, &send_tok);
181 }
182 } while (maj_status & GSS_S_CONTINUE_NEEDED);
183
184 if (GSS_ERROR(maj_status)) {
185 if (send_tok.length > 0) {
186 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
187 packet_put_string(send_tok.value, send_tok.length);
188 packet_send();
189 }
190 fatal("accept_ctx died");
191 }
192
193 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
194 fatal("Mutual Authentication flag wasn't set");
195
196 if (!(ret_flags & GSS_C_INTEG_FLAG))
197 fatal("Integrity flag wasn't set");
198
199 if (!dh_pub_is_valid(dh, dh_client_pub))
200 packet_disconnect("bad client public DH value");
201
202 klen = DH_size(dh);
203 kbuf = xmalloc(klen);
204 kout = DH_compute_key(kbuf, dh_client_pub, dh);
205 if (kout < 0)
206 fatal("DH_compute_key: failed");
207
208 shared_secret = BN_new();
209 if (shared_secret == NULL)
210 fatal("kexgss_server: BN_new failed");
211
212 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
213 fatal("kexgss_server: BN_bin2bn failed");
214
215 memset(kbuf, 0, klen);
216 free(kbuf);
217
218 DH_get0_key(dh, &pub_key, NULL);
219 DH_get0_pqg(dh, &dh_p, NULL, &dh_g);
220
221 hashlen = sizeof(hash);
222 switch (ssh->kex->kex_type) {
223 case KEX_GSS_GRP1_SHA1:
224 case KEX_GSS_GRP14_SHA1:
225 kex_dh_hash(
226 ssh->kex->hash_alg,
227 ssh->kex->client_version_string, ssh->kex->server_version_string,
228 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
229 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
230 NULL, 0, /* Change this if we start sending host keys */
231 dh_client_pub, pub_key, shared_secret,
232 hash, &hashlen
233 );
234 break;
235 case KEX_GSS_GEX_SHA1:
236 kexgex_hash(
237 ssh->kex->hash_alg,
238 ssh->kex->client_version_string, ssh->kex->server_version_string,
239 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
240 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
241 NULL, 0,
242 min, nbits, max,
243 dh_p, dh_g,
244 dh_client_pub,
245 pub_key,
246 shared_secret,
247 hash, &hashlen
248 );
249 break;
250 default:
251 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
252 }
253
254 BN_clear_free(dh_client_pub);
255
256 if (ssh->kex->session_id == NULL) {
257 ssh->kex->session_id_len = hashlen;
258 ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
259 memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
260 }
261
262 gssbuf.value = hash;
263 gssbuf.length = hashlen;
264
265 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
266 fatal("Couldn't get MIC");
267
268 packet_start(SSH2_MSG_KEXGSS_COMPLETE);
269 packet_put_bignum2(pub_key);
270 packet_put_string(msg_tok.value,msg_tok.length);
271
272 if (send_tok.length != 0) {
273 packet_put_char(1); /* true */
274 packet_put_string(send_tok.value, send_tok.length);
275 } else {
276 packet_put_char(0); /* false */
277 }
278 packet_send();
279
280 gss_release_buffer(&min_status, &send_tok);
281 gss_release_buffer(&min_status, &msg_tok);
282
283 if (gss_kex_context == NULL)
284 gss_kex_context = ctxt;
285 else
286 ssh_gssapi_delete_ctx(&ctxt);
287
288 DH_free(dh);
289
290 kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
291 BN_clear_free(shared_secret);
292 kex_send_newkeys(ssh);
293
294 /* If this was a rekey, then save out any delegated credentials we
295 * just exchanged. */
296 if (options.gss_store_rekey)
297 ssh_gssapi_rekey_creds();
298 return 0;
299}
300#endif /* GSSAPI */
diff --git a/monitor.c b/monitor.c
index 531b2993a..eabc1e89b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -145,6 +145,8 @@ int mm_answer_gss_setup_ctx(int, struct sshbuf *);
145int mm_answer_gss_accept_ctx(int, struct sshbuf *); 145int mm_answer_gss_accept_ctx(int, struct sshbuf *);
146int mm_answer_gss_userok(int, struct sshbuf *); 146int mm_answer_gss_userok(int, struct sshbuf *);
147int mm_answer_gss_checkmic(int, struct sshbuf *); 147int mm_answer_gss_checkmic(int, struct sshbuf *);
148int mm_answer_gss_sign(int, struct sshbuf *);
149int mm_answer_gss_updatecreds(int, struct sshbuf *);
148#endif 150#endif
149 151
150#ifdef SSH_AUDIT_EVENTS 152#ifdef SSH_AUDIT_EVENTS
@@ -215,11 +217,18 @@ struct mon_table mon_dispatch_proto20[] = {
215 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, 217 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
216 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, 218 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
217 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, 219 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
220 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
218#endif 221#endif
219 {0, 0, NULL} 222 {0, 0, NULL}
220}; 223};
221 224
222struct mon_table mon_dispatch_postauth20[] = { 225struct mon_table mon_dispatch_postauth20[] = {
226#ifdef GSSAPI
227 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
228 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
229 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
230 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
231#endif
223#ifdef WITH_OPENSSL 232#ifdef WITH_OPENSSL
224 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 233 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
225#endif 234#endif
@@ -289,6 +298,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
289 /* Permit requests for moduli and signatures */ 298 /* Permit requests for moduli and signatures */
290 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 299 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
291 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 300 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
301#ifdef GSSAPI
302 /* and for the GSSAPI key exchange */
303 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
304#endif
292 305
293 /* The first few requests do not require asynchronous access */ 306 /* The first few requests do not require asynchronous access */
294 while (!authenticated) { 307 while (!authenticated) {
@@ -401,6 +414,10 @@ monitor_child_postauth(struct monitor *pmonitor)
401 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 414 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
402 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 415 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
403 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 416 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
417#ifdef GSSAPI
418 /* and for the GSSAPI key exchange */
419 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
420#endif
404 421
405 if (auth_opts->permit_pty_flag) { 422 if (auth_opts->permit_pty_flag) {
406 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); 423 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
@@ -1666,6 +1683,13 @@ monitor_apply_keystate(struct monitor *pmonitor)
1666# endif 1683# endif
1667#endif /* WITH_OPENSSL */ 1684#endif /* WITH_OPENSSL */
1668 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 1685 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1686#ifdef GSSAPI
1687 if (options.gss_keyex) {
1688 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1689 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1690 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1691 }
1692#endif
1669 kex->load_host_public_key=&get_hostkey_public_by_type; 1693 kex->load_host_public_key=&get_hostkey_public_by_type;
1670 kex->load_host_private_key=&get_hostkey_private_by_type; 1694 kex->load_host_private_key=&get_hostkey_private_by_type;
1671 kex->host_key_index=&get_hostkey_index; 1695 kex->host_key_index=&get_hostkey_index;
@@ -1756,8 +1780,8 @@ mm_answer_gss_setup_ctx(int sock, struct sshbuf *m)
1756 u_char *p; 1780 u_char *p;
1757 int r; 1781 int r;
1758 1782
1759 if (!options.gss_authentication) 1783 if (!options.gss_authentication && !options.gss_keyex)
1760 fatal("%s: GSSAPI authentication not enabled", __func__); 1784 fatal("%s: GSSAPI not enabled", __func__);
1761 1785
1762 if ((r = sshbuf_get_string(m, &p, &len)) != 0) 1786 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1763 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1787 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1789,8 +1813,8 @@ mm_answer_gss_accept_ctx(int sock, struct sshbuf *m)
1789 OM_uint32 flags = 0; /* GSI needs this */ 1813 OM_uint32 flags = 0; /* GSI needs this */
1790 int r; 1814 int r;
1791 1815
1792 if (!options.gss_authentication) 1816 if (!options.gss_authentication && !options.gss_keyex)
1793 fatal("%s: GSSAPI authentication not enabled", __func__); 1817 fatal("%s: GSSAPI not enabled", __func__);
1794 1818
1795 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) 1819 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
1796 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1820 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1810,6 +1834,7 @@ mm_answer_gss_accept_ctx(int sock, struct sshbuf *m)
1810 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 1834 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
1811 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 1835 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
1812 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 1836 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
1837 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
1813 } 1838 }
1814 return (0); 1839 return (0);
1815} 1840}
@@ -1821,8 +1846,8 @@ mm_answer_gss_checkmic(int sock, struct sshbuf *m)
1821 OM_uint32 ret; 1846 OM_uint32 ret;
1822 int r; 1847 int r;
1823 1848
1824 if (!options.gss_authentication) 1849 if (!options.gss_authentication && !options.gss_keyex)
1825 fatal("%s: GSSAPI authentication not enabled", __func__); 1850 fatal("%s: GSSAPI not enabled", __func__);
1826 1851
1827 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || 1852 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
1828 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) 1853 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
@@ -1851,10 +1876,11 @@ mm_answer_gss_userok(int sock, struct sshbuf *m)
1851 int r, authenticated; 1876 int r, authenticated;
1852 const char *displayname; 1877 const char *displayname;
1853 1878
1854 if (!options.gss_authentication) 1879 if (!options.gss_authentication && !options.gss_keyex)
1855 fatal("%s: GSSAPI authentication not enabled", __func__); 1880 fatal("%s: GSSAPI not enabled", __func__);
1856 1881
1857 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 1882 authenticated = authctxt->valid &&
1883 ssh_gssapi_userok(authctxt->user, authctxt->pw);
1858 1884
1859 sshbuf_reset(m); 1885 sshbuf_reset(m);
1860 if ((r = sshbuf_put_u32(m, authenticated)) != 0) 1886 if ((r = sshbuf_put_u32(m, authenticated)) != 0)
@@ -1871,5 +1897,83 @@ mm_answer_gss_userok(int sock, struct sshbuf *m)
1871 /* Monitor loop will terminate if authenticated */ 1897 /* Monitor loop will terminate if authenticated */
1872 return (authenticated); 1898 return (authenticated);
1873} 1899}
1900
1901int
1902mm_answer_gss_sign(int socket, struct sshbuf *m)
1903{
1904 gss_buffer_desc data;
1905 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
1906 OM_uint32 major, minor;
1907 size_t len;
1908 u_char *p;
1909 int r;
1910
1911 if (!options.gss_authentication && !options.gss_keyex)
1912 fatal("%s: GSSAPI not enabled", __func__);
1913
1914 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1915 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1916 data.value = p;
1917 data.length = len;
1918 if (data.length != 20)
1919 fatal("%s: data length incorrect: %d", __func__,
1920 (int) data.length);
1921
1922 /* Save the session ID on the first time around */
1923 if (session_id2_len == 0) {
1924 session_id2_len = data.length;
1925 session_id2 = xmalloc(session_id2_len);
1926 memcpy(session_id2, data.value, session_id2_len);
1927 }
1928 major = ssh_gssapi_sign(gsscontext, &data, &hash);
1929
1930 free(data.value);
1931
1932 sshbuf_reset(m);
1933 if ((r = sshbuf_put_u32(m, major)) != 0 ||
1934 (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
1935 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1936
1937 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
1938
1939 gss_release_buffer(&minor, &hash);
1940
1941 /* Turn on getpwnam permissions */
1942 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
1943
1944 /* And credential updating, for when rekeying */
1945 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
1946
1947 return (0);
1948}
1949
1950int
1951mm_answer_gss_updatecreds(int socket, struct sshbuf *m) {
1952 ssh_gssapi_ccache store;
1953 int r, ok;
1954
1955 if (!options.gss_authentication && !options.gss_keyex)
1956 fatal("%s: GSSAPI not enabled", __func__);
1957
1958 if ((r = sshbuf_get_cstring(m, &store.filename, NULL)) != 0 ||
1959 (r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 ||
1960 (r = sshbuf_get_cstring(m, &store.envval, NULL)) != 0)
1961 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1962
1963 ok = ssh_gssapi_update_creds(&store);
1964
1965 free(store.filename);
1966 free(store.envvar);
1967 free(store.envval);
1968
1969 sshbuf_reset(m);
1970 if ((r = sshbuf_put_u32(m, ok)) != 0)
1971 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1972
1973 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
1974
1975 return(0);
1976}
1977
1874#endif /* GSSAPI */ 1978#endif /* GSSAPI */
1875 1979
diff --git a/monitor.h b/monitor.h
index 16047299f..44fbed589 100644
--- a/monitor.h
+++ b/monitor.h
@@ -63,6 +63,9 @@ enum monitor_reqtype {
63 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, 63 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
64 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, 64 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
65 65
66 MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
67 MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
68
66}; 69};
67 70
68struct monitor { 71struct monitor {
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 732fb3476..1865a122a 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -984,7 +984,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
984} 984}
985 985
986int 986int
987mm_ssh_gssapi_userok(char *user) 987mm_ssh_gssapi_userok(char *user, struct passwd *pw)
988{ 988{
989 struct sshbuf *m; 989 struct sshbuf *m;
990 int r, authenticated = 0; 990 int r, authenticated = 0;
@@ -1003,4 +1003,55 @@ mm_ssh_gssapi_userok(char *user)
1003 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1003 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1004 return (authenticated); 1004 return (authenticated);
1005} 1005}
1006
1007OM_uint32
1008mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1009{
1010 struct sshbuf *m;
1011 OM_uint32 major;
1012 int r;
1013
1014 if ((m = sshbuf_new()) == NULL)
1015 fatal("%s: sshbuf_new failed", __func__);
1016 if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
1017 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1018
1019 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m);
1020 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m);
1021
1022 if ((r = sshbuf_get_u32(m, &major)) != 0 ||
1023 (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
1024 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1025
1026 sshbuf_free(m);
1027
1028 return(major);
1029}
1030
1031int
1032mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1033{
1034 struct sshbuf *m;
1035 int r, ok;
1036
1037 if ((m = sshbuf_new()) == NULL)
1038 fatal("%s: sshbuf_new failed", __func__);
1039 if ((r = sshbuf_put_cstring(m,
1040 store->filename ? store->filename : "")) != 0 ||
1041 (r = sshbuf_put_cstring(m,
1042 store->envvar ? store->envvar : "")) != 0 ||
1043 (r = sshbuf_put_cstring(m,
1044 store->envval ? store->envval : "")) != 0)
1045 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1046
1047 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m);
1048 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m);
1049
1050 if ((r = sshbuf_get_u32(m, &ok)) != 0)
1051 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1052 sshbuf_free(m);
1053
1054 return (ok);
1055}
1056
1006#endif /* GSSAPI */ 1057#endif /* GSSAPI */
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 644da081d..7f93144ff 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -60,8 +60,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
60OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 60OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
61OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, 61OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
62 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); 62 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
63int mm_ssh_gssapi_userok(char *user); 63int mm_ssh_gssapi_userok(char *user, struct passwd *);
64OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 64OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
65OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
66int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
65#endif 67#endif
66 68
67#ifdef USE_PAM 69#ifdef USE_PAM
diff --git a/opacket.c b/opacket.c
index e637d7a71..7672c0b59 100644
--- a/opacket.c
+++ b/opacket.c
@@ -80,7 +80,7 @@ ssh_packet_put_raw(struct ssh *ssh, const void *buf, u_int len)
80 80
81#ifdef WITH_OPENSSL 81#ifdef WITH_OPENSSL
82void 82void
83ssh_packet_put_bignum2(struct ssh *ssh, BIGNUM * value) 83ssh_packet_put_bignum2(struct ssh *ssh, const BIGNUM * value)
84{ 84{
85 int r; 85 int r;
86 86
diff --git a/opacket.h b/opacket.h
index f92fe586e..1cf66a2d3 100644
--- a/opacket.h
+++ b/opacket.h
@@ -7,7 +7,7 @@ void ssh_packet_start(struct ssh *, u_char);
7void ssh_packet_put_char(struct ssh *, int ch); 7void ssh_packet_put_char(struct ssh *, int ch);
8void ssh_packet_put_int(struct ssh *, u_int value); 8void ssh_packet_put_int(struct ssh *, u_int value);
9void ssh_packet_put_int64(struct ssh *, u_int64_t value); 9void ssh_packet_put_int64(struct ssh *, u_int64_t value);
10void ssh_packet_put_bignum2(struct ssh *, BIGNUM * value); 10void ssh_packet_put_bignum2(struct ssh *, const BIGNUM * value);
11void ssh_packet_put_ecpoint(struct ssh *, const EC_GROUP *, const EC_POINT *); 11void ssh_packet_put_ecpoint(struct ssh *, const EC_GROUP *, const EC_POINT *);
12void ssh_packet_put_string(struct ssh *, const void *buf, u_int len); 12void ssh_packet_put_string(struct ssh *, const void *buf, u_int len);
13void ssh_packet_put_cstring(struct ssh *, const char *str); 13void ssh_packet_put_cstring(struct ssh *, const char *str);
diff --git a/readconf.c b/readconf.c
index 433811521..36bc5e59a 100644
--- a/readconf.c
+++ b/readconf.c
@@ -161,6 +161,8 @@ typedef enum {
161 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 161 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
162 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 162 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
163 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 163 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
164 oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
165 oGssServerIdentity,
164 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 166 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
165 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, 167 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
166 oHashKnownHosts, 168 oHashKnownHosts,
@@ -201,10 +203,20 @@ static struct {
201 /* Sometimes-unsupported options */ 203 /* Sometimes-unsupported options */
202#if defined(GSSAPI) 204#if defined(GSSAPI)
203 { "gssapiauthentication", oGssAuthentication }, 205 { "gssapiauthentication", oGssAuthentication },
206 { "gssapikeyexchange", oGssKeyEx },
204 { "gssapidelegatecredentials", oGssDelegateCreds }, 207 { "gssapidelegatecredentials", oGssDelegateCreds },
208 { "gssapitrustdns", oGssTrustDns },
209 { "gssapiclientidentity", oGssClientIdentity },
210 { "gssapiserveridentity", oGssServerIdentity },
211 { "gssapirenewalforcesrekey", oGssRenewalRekey },
205# else 212# else
206 { "gssapiauthentication", oUnsupported }, 213 { "gssapiauthentication", oUnsupported },
214 { "gssapikeyexchange", oUnsupported },
207 { "gssapidelegatecredentials", oUnsupported }, 215 { "gssapidelegatecredentials", oUnsupported },
216 { "gssapitrustdns", oUnsupported },
217 { "gssapiclientidentity", oUnsupported },
218 { "gssapiserveridentity", oUnsupported },
219 { "gssapirenewalforcesrekey", oUnsupported },
208#endif 220#endif
209#ifdef ENABLE_PKCS11 221#ifdef ENABLE_PKCS11
210 { "smartcarddevice", oPKCS11Provider }, 222 { "smartcarddevice", oPKCS11Provider },
@@ -974,10 +986,30 @@ parse_time:
974 intptr = &options->gss_authentication; 986 intptr = &options->gss_authentication;
975 goto parse_flag; 987 goto parse_flag;
976 988
989 case oGssKeyEx:
990 intptr = &options->gss_keyex;
991 goto parse_flag;
992
977 case oGssDelegateCreds: 993 case oGssDelegateCreds:
978 intptr = &options->gss_deleg_creds; 994 intptr = &options->gss_deleg_creds;
979 goto parse_flag; 995 goto parse_flag;
980 996
997 case oGssTrustDns:
998 intptr = &options->gss_trust_dns;
999 goto parse_flag;
1000
1001 case oGssClientIdentity:
1002 charptr = &options->gss_client_identity;
1003 goto parse_string;
1004
1005 case oGssServerIdentity:
1006 charptr = &options->gss_server_identity;
1007 goto parse_string;
1008
1009 case oGssRenewalRekey:
1010 intptr = &options->gss_renewal_rekey;
1011 goto parse_flag;
1012
981 case oBatchMode: 1013 case oBatchMode:
982 intptr = &options->batch_mode; 1014 intptr = &options->batch_mode;
983 goto parse_flag; 1015 goto parse_flag;
@@ -1842,7 +1874,12 @@ initialize_options(Options * options)
1842 options->pubkey_authentication = -1; 1874 options->pubkey_authentication = -1;
1843 options->challenge_response_authentication = -1; 1875 options->challenge_response_authentication = -1;
1844 options->gss_authentication = -1; 1876 options->gss_authentication = -1;
1877 options->gss_keyex = -1;
1845 options->gss_deleg_creds = -1; 1878 options->gss_deleg_creds = -1;
1879 options->gss_trust_dns = -1;
1880 options->gss_renewal_rekey = -1;
1881 options->gss_client_identity = NULL;
1882 options->gss_server_identity = NULL;
1846 options->password_authentication = -1; 1883 options->password_authentication = -1;
1847 options->kbd_interactive_authentication = -1; 1884 options->kbd_interactive_authentication = -1;
1848 options->kbd_interactive_devices = NULL; 1885 options->kbd_interactive_devices = NULL;
@@ -1988,8 +2025,14 @@ fill_default_options(Options * options)
1988 options->challenge_response_authentication = 1; 2025 options->challenge_response_authentication = 1;
1989 if (options->gss_authentication == -1) 2026 if (options->gss_authentication == -1)
1990 options->gss_authentication = 0; 2027 options->gss_authentication = 0;
2028 if (options->gss_keyex == -1)
2029 options->gss_keyex = 0;
1991 if (options->gss_deleg_creds == -1) 2030 if (options->gss_deleg_creds == -1)
1992 options->gss_deleg_creds = 0; 2031 options->gss_deleg_creds = 0;
2032 if (options->gss_trust_dns == -1)
2033 options->gss_trust_dns = 0;
2034 if (options->gss_renewal_rekey == -1)
2035 options->gss_renewal_rekey = 0;
1993 if (options->password_authentication == -1) 2036 if (options->password_authentication == -1)
1994 options->password_authentication = 1; 2037 options->password_authentication = 1;
1995 if (options->kbd_interactive_authentication == -1) 2038 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index fc7e38251..8e4900d01 100644
--- a/readconf.h
+++ b/readconf.h
@@ -40,7 +40,12 @@ typedef struct {
40 int challenge_response_authentication; 40 int challenge_response_authentication;
41 /* Try S/Key or TIS, authentication. */ 41 /* Try S/Key or TIS, authentication. */
42 int gss_authentication; /* Try GSS authentication */ 42 int gss_authentication; /* Try GSS authentication */
43 int gss_keyex; /* Try GSS key exchange */
43 int gss_deleg_creds; /* Delegate GSS credentials */ 44 int gss_deleg_creds; /* Delegate GSS credentials */
45 int gss_trust_dns; /* Trust DNS for GSS canonicalization */
46 int gss_renewal_rekey; /* Credential renewal forces rekey */
47 char *gss_client_identity; /* Principal to initiate GSSAPI with */
48 char *gss_server_identity; /* GSSAPI target principal */
44 int password_authentication; /* Try password 49 int password_authentication; /* Try password
45 * authentication. */ 50 * authentication. */
46 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 51 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
diff --git a/servconf.c b/servconf.c
index 932d363bb..4668b8a45 100644
--- a/servconf.c
+++ b/servconf.c
@@ -124,8 +124,10 @@ initialize_server_options(ServerOptions *options)
124 options->kerberos_ticket_cleanup = -1; 124 options->kerberos_ticket_cleanup = -1;
125 options->kerberos_get_afs_token = -1; 125 options->kerberos_get_afs_token = -1;
126 options->gss_authentication=-1; 126 options->gss_authentication=-1;
127 options->gss_keyex = -1;
127 options->gss_cleanup_creds = -1; 128 options->gss_cleanup_creds = -1;
128 options->gss_strict_acceptor = -1; 129 options->gss_strict_acceptor = -1;
130 options->gss_store_rekey = -1;
129 options->password_authentication = -1; 131 options->password_authentication = -1;
130 options->kbd_interactive_authentication = -1; 132 options->kbd_interactive_authentication = -1;
131 options->challenge_response_authentication = -1; 133 options->challenge_response_authentication = -1;
@@ -337,10 +339,14 @@ fill_default_server_options(ServerOptions *options)
337 options->kerberos_get_afs_token = 0; 339 options->kerberos_get_afs_token = 0;
338 if (options->gss_authentication == -1) 340 if (options->gss_authentication == -1)
339 options->gss_authentication = 0; 341 options->gss_authentication = 0;
342 if (options->gss_keyex == -1)
343 options->gss_keyex = 0;
340 if (options->gss_cleanup_creds == -1) 344 if (options->gss_cleanup_creds == -1)
341 options->gss_cleanup_creds = 1; 345 options->gss_cleanup_creds = 1;
342 if (options->gss_strict_acceptor == -1) 346 if (options->gss_strict_acceptor == -1)
343 options->gss_strict_acceptor = 1; 347 options->gss_strict_acceptor = 1;
348 if (options->gss_store_rekey == -1)
349 options->gss_store_rekey = 0;
344 if (options->password_authentication == -1) 350 if (options->password_authentication == -1)
345 options->password_authentication = 1; 351 options->password_authentication = 1;
346 if (options->kbd_interactive_authentication == -1) 352 if (options->kbd_interactive_authentication == -1)
@@ -485,6 +491,7 @@ typedef enum {
485 sHostKeyAlgorithms, 491 sHostKeyAlgorithms,
486 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 492 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
487 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 493 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
494 sGssKeyEx, sGssStoreRekey,
488 sAcceptEnv, sSetEnv, sPermitTunnel, 495 sAcceptEnv, sSetEnv, sPermitTunnel,
489 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, 496 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
490 sUsePrivilegeSeparation, sAllowAgentForwarding, 497 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -559,12 +566,20 @@ static struct {
559#ifdef GSSAPI 566#ifdef GSSAPI
560 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 567 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
561 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 568 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
569 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
562 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 570 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
571 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
572 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
563#else 573#else
564 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 574 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
565 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 575 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
576 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
566 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 577 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
578 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
579 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
567#endif 580#endif
581 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
582 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
568 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 583 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
569 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 584 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
570 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 585 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1468,6 +1483,10 @@ process_server_config_line(ServerOptions *options, char *line,
1468 intptr = &options->gss_authentication; 1483 intptr = &options->gss_authentication;
1469 goto parse_flag; 1484 goto parse_flag;
1470 1485
1486 case sGssKeyEx:
1487 intptr = &options->gss_keyex;
1488 goto parse_flag;
1489
1471 case sGssCleanupCreds: 1490 case sGssCleanupCreds:
1472 intptr = &options->gss_cleanup_creds; 1491 intptr = &options->gss_cleanup_creds;
1473 goto parse_flag; 1492 goto parse_flag;
@@ -1476,6 +1495,10 @@ process_server_config_line(ServerOptions *options, char *line,
1476 intptr = &options->gss_strict_acceptor; 1495 intptr = &options->gss_strict_acceptor;
1477 goto parse_flag; 1496 goto parse_flag;
1478 1497
1498 case sGssStoreRekey:
1499 intptr = &options->gss_store_rekey;
1500 goto parse_flag;
1501
1479 case sPasswordAuthentication: 1502 case sPasswordAuthentication:
1480 intptr = &options->password_authentication; 1503 intptr = &options->password_authentication;
1481 goto parse_flag; 1504 goto parse_flag;
@@ -2560,7 +2583,10 @@ dump_config(ServerOptions *o)
2560#endif 2583#endif
2561#ifdef GSSAPI 2584#ifdef GSSAPI
2562 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2585 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2586 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2563 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2587 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2588 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2589 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2564#endif 2590#endif
2565 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2591 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2566 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2592 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 0175e00e8..3b76da816 100644
--- a/servconf.h
+++ b/servconf.h
@@ -125,8 +125,10 @@ typedef struct {
125 int kerberos_get_afs_token; /* If true, try to get AFS token if 125 int kerberos_get_afs_token; /* If true, try to get AFS token if
126 * authenticated with Kerberos. */ 126 * authenticated with Kerberos. */
127 int gss_authentication; /* If true, permit GSSAPI authentication */ 127 int gss_authentication; /* If true, permit GSSAPI authentication */
128 int gss_keyex; /* If true, permit GSSAPI key exchange */
128 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 129 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
129 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 130 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
131 int gss_store_rekey;
130 int password_authentication; /* If true, permit password 132 int password_authentication; /* If true, permit password
131 * authentication. */ 133 * authentication. */
132 int kbd_interactive_authentication; /* If true, permit */ 134 int kbd_interactive_authentication; /* If true, permit */
diff --git a/ssh-gss.h b/ssh-gss.h
index 36180d07a..350ce7882 100644
--- a/ssh-gss.h
+++ b/ssh-gss.h
@@ -1,6 +1,6 @@
1/* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */ 1/* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 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);
@@ -123,17 +140,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **);
123OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); 140OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
124void ssh_gssapi_buildmic(struct sshbuf *, const char *, 141void ssh_gssapi_buildmic(struct sshbuf *, const char *,
125 const char *, const char *); 142 const char *, const char *);
126int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); 143int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
144OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
145int ssh_gssapi_credentials_updated(Gssctxt *);
127 146
128/* In the server */ 147/* In the server */
148typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
149 const char *);
150char *ssh_gssapi_client_mechanisms(const char *, const char *);
151char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
152 const char *);
153gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
154int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
155 const char *);
129OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 156OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
130int ssh_gssapi_userok(char *name); 157int ssh_gssapi_userok(char *name, struct passwd *);
131OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 158OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
132void ssh_gssapi_do_child(char ***, u_int *); 159void ssh_gssapi_do_child(char ***, u_int *);
133void ssh_gssapi_cleanup_creds(void); 160void ssh_gssapi_cleanup_creds(void);
134void ssh_gssapi_storecreds(void); 161void ssh_gssapi_storecreds(void);
135const char *ssh_gssapi_displayname(void); 162const char *ssh_gssapi_displayname(void);
136 163
164char *ssh_gssapi_server_mechanisms(void);
165int ssh_gssapi_oid_table_ok(void);
166
167int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
168void ssh_gssapi_rekey_creds(void);
169
137#endif /* GSSAPI */ 170#endif /* GSSAPI */
138 171
139#endif /* _SSH_GSS_H */ 172#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 4d5b01d3e..16c79368a 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -736,10 +736,42 @@ The default is
736Specifies whether user authentication based on GSSAPI is allowed. 736Specifies whether user authentication based on GSSAPI is allowed.
737The default is 737The default is
738.Cm no . 738.Cm no .
739.It Cm GSSAPIKeyExchange
740Specifies whether key exchange based on GSSAPI may be used. When using
741GSSAPI key exchange the server need not have a host key.
742The default is
743.Cm no .
744.It Cm GSSAPIClientIdentity
745If set, specifies the GSSAPI client identity that ssh should use when
746connecting to the server. The default is unset, which means that the default
747identity will be used.
748.It Cm GSSAPIServerIdentity
749If set, specifies the GSSAPI server identity that ssh should expect when
750connecting to the server. The default is unset, which means that the
751expected GSSAPI server identity will be determined from the target
752hostname.
739.It Cm GSSAPIDelegateCredentials 753.It Cm GSSAPIDelegateCredentials
740Forward (delegate) credentials to the server. 754Forward (delegate) credentials to the server.
741The default is 755The default is
742.Cm no . 756.Cm no .
757.It Cm GSSAPIRenewalForcesRekey
758If set to
759.Cm yes
760then renewal of the client's GSSAPI credentials will force the rekeying of the
761ssh connection. With a compatible server, this can delegate the renewed
762credentials to a session on the server.
763The default is
764.Cm no .
765.It Cm GSSAPITrustDns
766Set to
767.Cm yes
768to indicate that the DNS is trusted to securely canonicalize
769the name of the host being connected to. If
770.Cm no ,
771the hostname entered on the
772command line will be passed untouched to the GSSAPI library.
773The default is
774.Cm no .
743.It Cm HashKnownHosts 775.It Cm HashKnownHosts
744Indicates that 776Indicates that
745.Xr ssh 1 777.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index 1675f3935..8c872a4fb 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
@@ -194,6 +199,35 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
194 order_hostkeyalgs(host, hostaddr, port)); 199 order_hostkeyalgs(host, hostaddr, port));
195 } 200 }
196 201
202#ifdef GSSAPI
203 if (options.gss_keyex) {
204 /* Add the GSSAPI mechanisms currently supported on this
205 * client to the key exchange algorithm proposal */
206 orig = myproposal[PROPOSAL_KEX_ALGS];
207
208 if (options.gss_server_identity)
209 gss_host = xstrdup(options.gss_server_identity);
210 else if (options.gss_trust_dns)
211 gss_host = remote_hostname(active_state);
212 else
213 gss_host = xstrdup(host);
214
215 gss = ssh_gssapi_client_mechanisms(gss_host,
216 options.gss_client_identity);
217 if (gss) {
218 debug("Offering GSSAPI proposal: %s", gss);
219 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
220 "%s,%s", gss, orig);
221
222 /* If we've got GSSAPI algorithms, then we also
223 * support the 'null' hostkey, as a last resort */
224 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
225 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
226 "%s,null", orig);
227 }
228 }
229#endif
230
197 if (options.rekey_limit || options.rekey_interval) 231 if (options.rekey_limit || options.rekey_interval)
198 packet_set_rekey_limits(options.rekey_limit, 232 packet_set_rekey_limits(options.rekey_limit,
199 options.rekey_interval); 233 options.rekey_interval);
@@ -215,15 +249,41 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
215# endif 249# endif
216#endif 250#endif
217 kex->kex[KEX_C25519_SHA256] = kexc25519_client; 251 kex->kex[KEX_C25519_SHA256] = kexc25519_client;
252#ifdef GSSAPI
253 if (options.gss_keyex) {
254 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
255 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
256 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
257 }
258#endif
218 kex->client_version_string=client_version_string; 259 kex->client_version_string=client_version_string;
219 kex->server_version_string=server_version_string; 260 kex->server_version_string=server_version_string;
220 kex->verify_host_key=&verify_host_key_callback; 261 kex->verify_host_key=&verify_host_key_callback;
221 262
263#ifdef GSSAPI
264 if (options.gss_keyex) {
265 kex->gss_deleg_creds = options.gss_deleg_creds;
266 kex->gss_trust_dns = options.gss_trust_dns;
267 kex->gss_client = options.gss_client_identity;
268 kex->gss_host = gss_host;
269 }
270#endif
271
222 ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done); 272 ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done);
223 273
224 /* remove ext-info from the KEX proposals for rekeying */ 274 /* remove ext-info from the KEX proposals for rekeying */
225 myproposal[PROPOSAL_KEX_ALGS] = 275 myproposal[PROPOSAL_KEX_ALGS] =
226 compat_kex_proposal(options.kex_algorithms); 276 compat_kex_proposal(options.kex_algorithms);
277#ifdef GSSAPI
278 /* repair myproposal after it was crumpled by the */
279 /* ext-info removal above */
280 if (gss) {
281 orig = myproposal[PROPOSAL_KEX_ALGS];
282 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
283 "%s,%s", gss, orig);
284 free(gss);
285 }
286#endif
227 if ((r = kex_prop2buf(kex->my, myproposal)) != 0) 287 if ((r = kex_prop2buf(kex->my, myproposal)) != 0)
228 fatal("kex_prop2buf: %s", ssh_err(r)); 288 fatal("kex_prop2buf: %s", ssh_err(r));
229 289
@@ -314,6 +374,7 @@ int input_gssapi_token(int type, u_int32_t, struct ssh *);
314int input_gssapi_hash(int type, u_int32_t, struct ssh *); 374int input_gssapi_hash(int type, u_int32_t, struct ssh *);
315int input_gssapi_error(int, u_int32_t, struct ssh *); 375int input_gssapi_error(int, u_int32_t, struct ssh *);
316int input_gssapi_errtok(int, u_int32_t, struct ssh *); 376int input_gssapi_errtok(int, u_int32_t, struct ssh *);
377int userauth_gsskeyex(Authctxt *authctxt);
317#endif 378#endif
318 379
319void userauth(Authctxt *, char *); 380void userauth(Authctxt *, char *);
@@ -330,6 +391,11 @@ static char *authmethods_get(void);
330 391
331Authmethod authmethods[] = { 392Authmethod authmethods[] = {
332#ifdef GSSAPI 393#ifdef GSSAPI
394 {"gssapi-keyex",
395 userauth_gsskeyex,
396 NULL,
397 &options.gss_authentication,
398 NULL},
333 {"gssapi-with-mic", 399 {"gssapi-with-mic",
334 userauth_gssapi, 400 userauth_gssapi,
335 NULL, 401 NULL,
@@ -686,25 +752,40 @@ userauth_gssapi(Authctxt *authctxt)
686 static u_int mech = 0; 752 static u_int mech = 0;
687 OM_uint32 min; 753 OM_uint32 min;
688 int r, ok = 0; 754 int r, ok = 0;
755 char *gss_host;
756
757 if (options.gss_server_identity)
758 gss_host = xstrdup(options.gss_server_identity);
759 else if (options.gss_trust_dns)
760 gss_host = remote_hostname(active_state);
761 else
762 gss_host = xstrdup(authctxt->host);
689 763
690 /* Try one GSSAPI method at a time, rather than sending them all at 764 /* Try one GSSAPI method at a time, rather than sending them all at
691 * once. */ 765 * once. */
692 766
693 if (gss_supported == NULL) 767 if (gss_supported == NULL)
694 gss_indicate_mechs(&min, &gss_supported); 768 if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
769 gss_supported = NULL;
770 free(gss_host);
771 return 0;
772 }
695 773
696 /* Check to see if the mechanism is usable before we offer it */ 774 /* Check to see if the mechanism is usable before we offer it */
697 while (mech < gss_supported->count && !ok) { 775 while (mech < gss_supported->count && !ok) {
698 /* My DER encoding requires length<128 */ 776 /* My DER encoding requires length<128 */
699 if (gss_supported->elements[mech].length < 128 && 777 if (gss_supported->elements[mech].length < 128 &&
700 ssh_gssapi_check_mechanism(&gssctxt, 778 ssh_gssapi_check_mechanism(&gssctxt,
701 &gss_supported->elements[mech], authctxt->host)) { 779 &gss_supported->elements[mech], gss_host,
780 options.gss_client_identity)) {
702 ok = 1; /* Mechanism works */ 781 ok = 1; /* Mechanism works */
703 } else { 782 } else {
704 mech++; 783 mech++;
705 } 784 }
706 } 785 }
707 786
787 free(gss_host);
788
708 if (!ok) 789 if (!ok)
709 return 0; 790 return 0;
710 791
@@ -935,6 +1016,54 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
935 free(lang); 1016 free(lang);
936 return r; 1017 return r;
937} 1018}
1019
1020int
1021userauth_gsskeyex(Authctxt *authctxt)
1022{
1023 struct ssh *ssh = active_state; /* XXX */
1024 struct sshbuf *b;
1025 gss_buffer_desc gssbuf;
1026 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
1027 OM_uint32 ms;
1028 int r;
1029
1030 static int attempt = 0;
1031 if (attempt++ >= 1)
1032 return (0);
1033
1034 if (gss_kex_context == NULL) {
1035 debug("No valid Key exchange context");
1036 return (0);
1037 }
1038
1039 if ((b = sshbuf_new()) == NULL)
1040 fatal("%s: sshbuf_new failed", __func__);
1041 ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
1042 "gssapi-keyex");
1043
1044 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
1045 fatal("%s: sshbuf_mutable_ptr failed", __func__);
1046 gssbuf.length = sshbuf_len(b);
1047
1048 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
1049 sshbuf_free(b);
1050 return (0);
1051 }
1052
1053 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1054 (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1055 (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1056 (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1057 (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
1058 (r = sshpkt_send(ssh)) != 0)
1059 fatal("%s: %s", __func__, ssh_err(r));
1060
1061 sshbuf_free(b);
1062 gss_release_buffer(&ms, &mic);
1063
1064 return (1);
1065}
1066
938#endif /* GSSAPI */ 1067#endif /* GSSAPI */
939 1068
940int 1069int
diff --git a/sshd.c b/sshd.c
index ba26287ba..539a000fd 100644
--- a/sshd.c
+++ b/sshd.c
@@ -123,6 +123,10 @@
123#include "version.h" 123#include "version.h"
124#include "ssherr.h" 124#include "ssherr.h"
125 125
126#ifdef USE_SECURITY_SESSION_API
127#include <Security/AuthSession.h>
128#endif
129
126/* Re-exec fds */ 130/* Re-exec fds */
127#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) 131#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
128#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) 132#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2)
@@ -1810,10 +1814,13 @@ main(int ac, char **av)
1810 free(fp); 1814 free(fp);
1811 } 1815 }
1812 accumulate_host_timing_secret(cfg, NULL); 1816 accumulate_host_timing_secret(cfg, NULL);
1817#ifndef GSSAPI
1818 /* The GSSAPI key exchange can run without a host key */
1813 if (!sensitive_data.have_ssh2_key) { 1819 if (!sensitive_data.have_ssh2_key) {
1814 logit("sshd: no hostkeys available -- exiting."); 1820 logit("sshd: no hostkeys available -- exiting.");
1815 exit(1); 1821 exit(1);
1816 } 1822 }
1823#endif
1817 1824
1818 /* 1825 /*
1819 * Load certificates. They are stored in an array at identical 1826 * Load certificates. They are stored in an array at identical
@@ -2104,6 +2111,60 @@ main(int ac, char **av)
2104 rdomain == NULL ? "" : "\""); 2111 rdomain == NULL ? "" : "\"");
2105 free(laddr); 2112 free(laddr);
2106 2113
2114#ifdef USE_SECURITY_SESSION_API
2115 /*
2116 * Create a new security session for use by the new user login if
2117 * the current session is the root session or we are not launched
2118 * by inetd (eg: debugging mode or server mode). We do not
2119 * necessarily need to create a session if we are launched from
2120 * inetd because Panther xinetd will create a session for us.
2121 *
2122 * The only case where this logic will fail is if there is an
2123 * inetd running in a non-root session which is not creating
2124 * new sessions for us. Then all the users will end up in the
2125 * same session (bad).
2126 *
2127 * When the client exits, the session will be destroyed for us
2128 * automatically.
2129 *
2130 * We must create the session before any credentials are stored
2131 * (including AFS pags, which happens a few lines below).
2132 */
2133 {
2134 OSStatus err = 0;
2135 SecuritySessionId sid = 0;
2136 SessionAttributeBits sattrs = 0;
2137
2138 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2139 if (err)
2140 error("SessionGetInfo() failed with error %.8X",
2141 (unsigned) err);
2142 else
2143 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2144 (unsigned) sid, (unsigned) sattrs);
2145
2146 if (inetd_flag && !(sattrs & sessionIsRoot))
2147 debug("Running in inetd mode in a non-root session... "
2148 "assuming inetd created the session for us.");
2149 else {
2150 debug("Creating new security session...");
2151 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2152 if (err)
2153 error("SessionCreate() failed with error %.8X",
2154 (unsigned) err);
2155
2156 err = SessionGetInfo(callerSecuritySession, &sid,
2157 &sattrs);
2158 if (err)
2159 error("SessionGetInfo() failed with error %.8X",
2160 (unsigned) err);
2161 else
2162 debug("New Session ID is %.8X / Session Attributes are %.8X",
2163 (unsigned) sid, (unsigned) sattrs);
2164 }
2165 }
2166#endif
2167
2107 /* 2168 /*
2108 * We don't want to listen forever unless the other side 2169 * We don't want to listen forever unless the other side
2109 * successfully authenticates itself. So we set up an alarm which is 2170 * successfully authenticates itself. So we set up an alarm which is
@@ -2287,6 +2348,48 @@ do_ssh2_kex(void)
2287 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2348 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2288 list_hostkey_types()); 2349 list_hostkey_types());
2289 2350
2351#ifdef GSSAPI
2352 {
2353 char *orig;
2354 char *gss = NULL;
2355 char *newstr = NULL;
2356 orig = myproposal[PROPOSAL_KEX_ALGS];
2357
2358 /*
2359 * If we don't have a host key, then there's no point advertising
2360 * the other key exchange algorithms
2361 */
2362
2363 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2364 orig = NULL;
2365
2366 if (options.gss_keyex)
2367 gss = ssh_gssapi_server_mechanisms();
2368 else
2369 gss = NULL;
2370
2371 if (gss && orig)
2372 xasprintf(&newstr, "%s,%s", gss, orig);
2373 else if (gss)
2374 newstr = gss;
2375 else if (orig)
2376 newstr = orig;
2377
2378 /*
2379 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2380 * key alg, but we can't tell people about it unless its the only
2381 * host key algorithm we support
2382 */
2383 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2384 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2385
2386 if (newstr)
2387 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2388 else
2389 fatal("No supported key exchange algorithms");
2390 }
2391#endif
2392
2290 /* start key exchange */ 2393 /* start key exchange */
2291 if ((r = kex_setup(active_state, myproposal)) != 0) 2394 if ((r = kex_setup(active_state, myproposal)) != 0)
2292 fatal("kex_setup: %s", ssh_err(r)); 2395 fatal("kex_setup: %s", ssh_err(r));
@@ -2304,6 +2407,13 @@ do_ssh2_kex(void)
2304# endif 2407# endif
2305#endif 2408#endif
2306 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2409 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2410#ifdef GSSAPI
2411 if (options.gss_keyex) {
2412 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2413 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2414 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2415 }
2416#endif
2307 kex->server = 1; 2417 kex->server = 1;
2308 kex->client_version_string=client_version_string; 2418 kex->client_version_string=client_version_string;
2309 kex->server_version_string=server_version_string; 2419 kex->server_version_string=server_version_string;
diff --git a/sshd_config b/sshd_config
index 19b7c91a1..2c48105f8 100644
--- a/sshd_config
+++ b/sshd_config
@@ -69,6 +69,8 @@ AuthorizedKeysFile .ssh/authorized_keys
69# GSSAPI options 69# GSSAPI options
70#GSSAPIAuthentication no 70#GSSAPIAuthentication no
71#GSSAPICleanupCredentials yes 71#GSSAPICleanupCredentials yes
72#GSSAPIStrictAcceptorCheck yes
73#GSSAPIKeyExchange no
72 74
73# Set this to 'yes' to enable PAM authentication, account processing, 75# Set this to 'yes' to enable PAM authentication, account processing,
74# and session processing. If this is enabled, PAM authentication will 76# and session processing. If this is enabled, PAM authentication will
diff --git a/sshd_config.5 b/sshd_config.5
index c6484370b..985eef5a2 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -648,6 +648,11 @@ The default is
648Specifies whether user authentication based on GSSAPI is allowed. 648Specifies whether user authentication based on GSSAPI is allowed.
649The default is 649The default is
650.Cm no . 650.Cm no .
651.It Cm GSSAPIKeyExchange
652Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
653doesn't rely on ssh keys to verify host identity.
654The default is
655.Cm no .
651.It Cm GSSAPICleanupCredentials 656.It Cm GSSAPICleanupCredentials
652Specifies whether to automatically destroy the user's credentials cache 657Specifies whether to automatically destroy the user's credentials cache
653on logout. 658on logout.
@@ -667,6 +672,11 @@ machine's default store.
667This facility is provided to assist with operation on multi homed machines. 672This facility is provided to assist with operation on multi homed machines.
668The default is 673The default is
669.Cm yes . 674.Cm yes .
675.It Cm GSSAPIStoreCredentialsOnRekey
676Controls whether the user's GSSAPI credentials should be updated following a
677successful connection rekeying. This option can be used to accepted renewed
678or updated credentials from a compatible client. The default is
679.Cm no .
670.It Cm HostbasedAcceptedKeyTypes 680.It Cm HostbasedAcceptedKeyTypes
671Specifies the key types that will be accepted for hostbased authentication 681Specifies the key types that will be accepted for hostbased authentication
672as a list of comma-separated patterns. 682as a list of comma-separated patterns.
diff --git a/sshkey.c b/sshkey.c
index 6555c5ef8..a85c185fc 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -135,6 +135,7 @@ static const struct keytype keytypes[] = {
135# endif /* OPENSSL_HAS_NISTP521 */ 135# endif /* OPENSSL_HAS_NISTP521 */
136# endif /* OPENSSL_HAS_ECC */ 136# endif /* OPENSSL_HAS_ECC */
137#endif /* WITH_OPENSSL */ 137#endif /* WITH_OPENSSL */
138 { "null", "null", NULL, KEY_NULL, 0, 0, 0 },
138 { NULL, NULL, NULL, -1, -1, 0, 0 } 139 { NULL, NULL, NULL, -1, -1, 0, 0 }
139}; 140};
140 141
@@ -223,7 +224,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
223 const struct keytype *kt; 224 const struct keytype *kt;
224 225
225 for (kt = keytypes; kt->type != -1; kt++) { 226 for (kt = keytypes; kt->type != -1; kt++) {
226 if (kt->name == NULL) 227 if (kt->name == NULL || kt->type == KEY_NULL)
227 continue; 228 continue;
228 if (!include_sigonly && kt->sigonly) 229 if (!include_sigonly && kt->sigonly)
229 continue; 230 continue;
diff --git a/sshkey.h b/sshkey.h
index f6a007fdf..f54deb0c0 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -64,6 +64,7 @@ enum sshkey_types {
64 KEY_ED25519_CERT, 64 KEY_ED25519_CERT,
65 KEY_XMSS, 65 KEY_XMSS,
66 KEY_XMSS_CERT, 66 KEY_XMSS_CERT,
67 KEY_NULL,
67 KEY_UNSPEC 68 KEY_UNSPEC
68}; 69};
69 70