summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.c277
-rw-r--r--gss-serv-krb5.c85
-rw-r--r--gss-serv.c184
-rw-r--r--kex.c19
-rw-r--r--kex.h14
-rw-r--r--kexgssc.c338
-rw-r--r--kexgsss.c295
-rw-r--r--monitor.c122
-rw-r--r--monitor.h3
-rw-r--r--monitor_wrap.c53
-rw-r--r--monitor_wrap.h4
-rw-r--r--readconf.c43
-rw-r--r--readconf.h5
-rw-r--r--servconf.c26
-rw-r--r--servconf.h2
-rw-r--r--ssh-gss.h41
-rw-r--r--ssh_config2
-rw-r--r--ssh_config.532
-rw-r--r--sshconnect2.c133
-rw-r--r--sshd.c112
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.510
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
35 files changed, 2087 insertions, 145 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 2385c62a8..6175c6063 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 9a3bc96f1..80eb78c48 100644
--- a/auth.c
+++ b/auth.c
@@ -395,7 +395,8 @@ auth_root_allowed(struct ssh *ssh, const char *method)
395 case PERMIT_NO_PASSWD: 395 case PERMIT_NO_PASSWD:
396 if (strcmp(method, "publickey") == 0 || 396 if (strcmp(method, "publickey") == 0 ||
397 strcmp(method, "hostbased") == 0 || 397 strcmp(method, "hostbased") == 0 ||
398 strcmp(method, "gssapi-with-mic") == 0) 398 strcmp(method, "gssapi-with-mic") == 0 ||
399 strcmp(method, "gssapi-keyex") == 0)
399 return 1; 400 return 1;
400 break; 401 break;
401 case PERMIT_FORCED_ONLY: 402 case PERMIT_FORCED_ONLY:
@@ -734,99 +735,6 @@ fakepw(void)
734} 735}
735 736
736/* 737/*
737 * Returns the remote DNS hostname as a string. The returned string must not
738 * be freed. NB. this will usually trigger a DNS query the first time it is
739 * called.
740 * This function does additional checks on the hostname to mitigate some
741 * attacks on legacy rhosts-style authentication.
742 * XXX is RhostsRSAAuthentication vulnerable to these?
743 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
744 */
745
746static char *
747remote_hostname(struct ssh *ssh)
748{
749 struct sockaddr_storage from;
750 socklen_t fromlen;
751 struct addrinfo hints, *ai, *aitop;
752 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
753 const char *ntop = ssh_remote_ipaddr(ssh);
754
755 /* Get IP address of client. */
756 fromlen = sizeof(from);
757 memset(&from, 0, sizeof(from));
758 if (getpeername(ssh_packet_get_connection_in(ssh),
759 (struct sockaddr *)&from, &fromlen) < 0) {
760 debug("getpeername failed: %.100s", strerror(errno));
761 return strdup(ntop);
762 }
763
764 ipv64_normalise_mapped(&from, &fromlen);
765 if (from.ss_family == AF_INET6)
766 fromlen = sizeof(struct sockaddr_in6);
767
768 debug3("Trying to reverse map address %.100s.", ntop);
769 /* Map the IP address to a host name. */
770 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
771 NULL, 0, NI_NAMEREQD) != 0) {
772 /* Host name not found. Use ip address. */
773 return strdup(ntop);
774 }
775
776 /*
777 * if reverse lookup result looks like a numeric hostname,
778 * someone is trying to trick us by PTR record like following:
779 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
780 */
781 memset(&hints, 0, sizeof(hints));
782 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
783 hints.ai_flags = AI_NUMERICHOST;
784 if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
785 logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
786 name, ntop);
787 freeaddrinfo(ai);
788 return strdup(ntop);
789 }
790
791 /* Names are stored in lowercase. */
792 lowercase(name);
793
794 /*
795 * Map it back to an IP address and check that the given
796 * address actually is an address of this host. This is
797 * necessary because anyone with access to a name server can
798 * define arbitrary names for an IP address. Mapping from
799 * name to IP address can be trusted better (but can still be
800 * fooled if the intruder has access to the name server of
801 * the domain).
802 */
803 memset(&hints, 0, sizeof(hints));
804 hints.ai_family = from.ss_family;
805 hints.ai_socktype = SOCK_STREAM;
806 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
807 logit("reverse mapping checking getaddrinfo for %.700s "
808 "[%s] failed.", name, ntop);
809 return strdup(ntop);
810 }
811 /* Look for the address from the list of addresses. */
812 for (ai = aitop; ai; ai = ai->ai_next) {
813 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
814 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
815 (strcmp(ntop, ntop2) == 0))
816 break;
817 }
818 freeaddrinfo(aitop);
819 /* If we reached the end of the list, the address was not there. */
820 if (ai == NULL) {
821 /* Address not found for the host name. */
822 logit("Address %.100s maps to %.600s, but this does not "
823 "map back to the address.", ntop, name);
824 return strdup(ntop);
825 }
826 return strdup(name);
827}
828
829/*
830 * Return the canonical name of the host in the other side of the current 738 * Return the canonical name of the host in the other side of the current
831 * connection. The host name is cached, so it is efficient to call this 739 * connection. The host name is cached, so it is efficient to call this
832 * several times. 740 * 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 ab8795895..96efe164c 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 ad35cb7ba..e69c5141f 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
@@ -1357,9 +1361,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
1357 break; 1361 break;
1358 1362
1359 /* Do channel operations unless rekeying in progress. */ 1363 /* Do channel operations unless rekeying in progress. */
1360 if (!ssh_packet_is_rekeying(ssh)) 1364 if (!ssh_packet_is_rekeying(ssh)) {
1361 channel_after_select(ssh, readset, writeset); 1365 channel_after_select(ssh, readset, writeset);
1362 1366
1367#ifdef GSSAPI
1368 if (options.gss_renewal_rekey &&
1369 ssh_gssapi_credentials_updated(NULL)) {
1370 debug("credentials updated - forcing rekey");
1371 need_rekeying = 1;
1372 }
1373#endif
1374 }
1375
1363 /* Buffer input from the connection. */ 1376 /* Buffer input from the connection. */
1364 client_process_net_input(readset); 1377 client_process_net_input(readset);
1365 1378
diff --git a/config.h.in b/config.h.in
index 7940b4c86..93295da07 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1749,6 +1749,9 @@
1749/* Use btmp to log bad logins */ 1749/* Use btmp to log bad logins */
1750#undef USE_BTMP 1750#undef USE_BTMP
1751 1751
1752/* platform uses an in-memory credentials cache */
1753#undef USE_CCAPI
1754
1752/* Use libedit for sftp */ 1755/* Use libedit for sftp */
1753#undef USE_LIBEDIT 1756#undef USE_LIBEDIT
1754 1757
@@ -1764,6 +1767,9 @@
1764/* Use PIPES instead of a socketpair() */ 1767/* Use PIPES instead of a socketpair() */
1765#undef USE_PIPES 1768#undef USE_PIPES
1766 1769
1770/* platform has the Security Authorization Session API */
1771#undef USE_SECURITY_SESSION_API
1772
1767/* Define if you have Solaris privileges */ 1773/* Define if you have Solaris privileges */
1768#undef USE_SOLARIS_PRIVS 1774#undef USE_SOLARIS_PRIVS
1769 1775
diff --git a/configure.ac b/configure.ac
index 83e530750..82428b241 100644
--- a/configure.ac
+++ b/configure.ac
@@ -673,6 +673,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
673 [Use tunnel device compatibility to OpenBSD]) 673 [Use tunnel device compatibility to OpenBSD])
674 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 674 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
675 [Prepend the address family to IP tunnel traffic]) 675 [Prepend the address family to IP tunnel traffic])
676 AC_MSG_CHECKING([if we have the Security Authorization Session API])
677 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
678 [SessionCreate(0, 0);],
679 [ac_cv_use_security_session_api="yes"
680 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
681 [platform has the Security Authorization Session API])
682 LIBS="$LIBS -framework Security"
683 AC_MSG_RESULT([yes])],
684 [ac_cv_use_security_session_api="no"
685 AC_MSG_RESULT([no])])
686 AC_MSG_CHECKING([if we have an in-memory credentials cache])
687 AC_TRY_COMPILE(
688 [#include <Kerberos/Kerberos.h>],
689 [cc_context_t c;
690 (void) cc_initialize (&c, 0, NULL, NULL);],
691 [AC_DEFINE([USE_CCAPI], [1],
692 [platform uses an in-memory credentials cache])
693 LIBS="$LIBS -framework Security"
694 AC_MSG_RESULT([yes])
695 if test "x$ac_cv_use_security_session_api" = "xno"; then
696 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
697 fi],
698 [AC_MSG_RESULT([no])]
699 )
676 m4_pattern_allow([AU_IPv]) 700 m4_pattern_allow([AU_IPv])
677 AC_CHECK_DECL([AU_IPv4], [], 701 AC_CHECK_DECL([AU_IPv4], [],
678 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 702 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
diff --git a/gss-genr.c b/gss-genr.c
index d56257b4a..285fc29a5 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
@@ -41,12 +41,34 @@
41#include "sshbuf.h" 41#include "sshbuf.h"
42#include "log.h" 42#include "log.h"
43#include "ssh2.h" 43#include "ssh2.h"
44#include "cipher.h"
45#include "kex.h"
46#include <openssl/evp.h>
44 47
45#include "ssh-gss.h" 48#include "ssh-gss.h"
46 49
47extern u_char *session_id2; 50extern u_char *session_id2;
48extern u_int session_id2_len; 51extern u_int session_id2_len;
49 52
53typedef struct {
54 char *encoded;
55 gss_OID oid;
56} ssh_gss_kex_mapping;
57
58/*
59 * XXX - It would be nice to find a more elegant way of handling the
60 * XXX passing of the key exchange context to the userauth routines
61 */
62
63Gssctxt *gss_kex_context = NULL;
64
65static ssh_gss_kex_mapping *gss_enc2oid = NULL;
66
67int
68ssh_gssapi_oid_table_ok(void) {
69 return (gss_enc2oid != NULL);
70}
71
50/* sshbuf_get for gss_buffer_desc */ 72/* sshbuf_get for gss_buffer_desc */
51int 73int
52ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) 74ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
@@ -62,6 +84,141 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
62 return 0; 84 return 0;
63} 85}
64 86
87/*
88 * Return a list of the gss-group1-sha1 mechanisms supported by this program
89 *
90 * We test mechanisms to ensure that we can use them, to avoid starting
91 * a key exchange with a bad mechanism
92 */
93
94char *
95ssh_gssapi_client_mechanisms(const char *host, const char *client) {
96 gss_OID_set gss_supported;
97 OM_uint32 min_status;
98
99 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
100 return NULL;
101
102 return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
103 host, client));
104}
105
106char *
107ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
108 const char *host, const char *client) {
109 struct sshbuf *buf;
110 size_t i;
111 int r, oidpos, enclen;
112 char *mechs, *encoded;
113 u_char digest[EVP_MAX_MD_SIZE];
114 char deroid[2];
115 const EVP_MD *evp_md = EVP_md5();
116 EVP_MD_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 EVP_DigestInit(&md, evp_md);
139 EVP_DigestUpdate(&md, deroid, 2);
140 EVP_DigestUpdate(&md,
141 gss_supported->elements[i].elements,
142 gss_supported->elements[i].length);
143 EVP_DigestFinal(&md, digest, NULL);
144
145 encoded = xmalloc(EVP_MD_size(evp_md) * 2);
146 enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
147 encoded, EVP_MD_size(evp_md) * 2);
148
149 if (oidpos != 0) {
150 if ((r = sshbuf_put_u8(buf, ',')) != 0)
151 fatal("%s: buffer error: %s",
152 __func__, ssh_err(r));
153 }
154
155 if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID,
156 sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 ||
157 (r = sshbuf_put(buf, encoded, enclen)) != 0 ||
158 (r = sshbuf_put_u8(buf, ',')) != 0 ||
159 (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID,
160 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 ||
161 (r = sshbuf_put(buf, encoded, enclen)) != 0 ||
162 (r = sshbuf_put_u8(buf, ',')) != 0 ||
163 (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID,
164 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 0 ||
165 (r = sshbuf_put(buf, encoded, enclen)) != 0)
166 fatal("%s: buffer error: %s",
167 __func__, ssh_err(r));
168
169 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
170 gss_enc2oid[oidpos].encoded = encoded;
171 oidpos++;
172 }
173 }
174 gss_enc2oid[oidpos].oid = NULL;
175 gss_enc2oid[oidpos].encoded = NULL;
176
177 if ((mechs = sshbuf_dup_string(buf)) == NULL)
178 fatal("%s: sshbuf_dup_string failed", __func__);
179
180 if (strlen(mechs) == 0) {
181 free(mechs);
182 mechs = NULL;
183 }
184
185 return (mechs);
186}
187
188gss_OID
189ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
190 int i = 0;
191
192 switch (kex_type) {
193 case KEX_GSS_GRP1_SHA1:
194 if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
195 return GSS_C_NO_OID;
196 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
197 break;
198 case KEX_GSS_GRP14_SHA1:
199 if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
200 return GSS_C_NO_OID;
201 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
202 break;
203 case KEX_GSS_GEX_SHA1:
204 if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
205 return GSS_C_NO_OID;
206 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
207 break;
208 default:
209 return GSS_C_NO_OID;
210 }
211
212 while (gss_enc2oid[i].encoded != NULL &&
213 strcmp(name, gss_enc2oid[i].encoded) != 0)
214 i++;
215
216 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
217 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
218
219 return gss_enc2oid[i].oid;
220}
221
65/* Check that the OID in a data stream matches that in the context */ 222/* Check that the OID in a data stream matches that in the context */
66int 223int
67ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 224ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -218,7 +375,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
218 } 375 }
219 376
220 ctx->major = gss_init_sec_context(&ctx->minor, 377 ctx->major = gss_init_sec_context(&ctx->minor,
221 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 378 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
222 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 379 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
223 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 380 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
224 381
@@ -248,8 +405,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
248} 405}
249 406
250OM_uint32 407OM_uint32
408ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
409{
410 gss_buffer_desc gssbuf;
411 gss_name_t gssname;
412 OM_uint32 status;
413 gss_OID_set oidset;
414
415 gssbuf.value = (void *) name;
416 gssbuf.length = strlen(gssbuf.value);
417
418 gss_create_empty_oid_set(&status, &oidset);
419 gss_add_oid_set_member(&status, ctx->oid, &oidset);
420
421 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
422 GSS_C_NT_USER_NAME, &gssname);
423
424 if (!ctx->major)
425 ctx->major = gss_acquire_cred(&ctx->minor,
426 gssname, 0, oidset, GSS_C_INITIATE,
427 &ctx->client_creds, NULL, NULL);
428
429 gss_release_name(&status, &gssname);
430 gss_release_oid_set(&status, &oidset);
431
432 if (ctx->major)
433 ssh_gssapi_error(ctx);
434
435 return(ctx->major);
436}
437
438OM_uint32
251ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 439ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
252{ 440{
441 if (ctx == NULL)
442 return -1;
443
253 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 444 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
254 GSS_C_QOP_DEFAULT, buffer, hash))) 445 GSS_C_QOP_DEFAULT, buffer, hash)))
255 ssh_gssapi_error(ctx); 446 ssh_gssapi_error(ctx);
@@ -257,6 +448,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
257 return (ctx->major); 448 return (ctx->major);
258} 449}
259 450
451/* Priviledged when used by server */
452OM_uint32
453ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
454{
455 if (ctx == NULL)
456 return -1;
457
458 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
459 gssbuf, gssmic, NULL);
460
461 return (ctx->major);
462}
463
260void 464void
261ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, 465ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
262 const char *context) 466 const char *context)
@@ -273,11 +477,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
273} 477}
274 478
275int 479int
276ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 480ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
481 const char *client)
277{ 482{
278 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 483 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
279 OM_uint32 major, minor; 484 OM_uint32 major, minor;
280 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 485 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
486 Gssctxt *intctx = NULL;
487
488 if (ctx == NULL)
489 ctx = &intctx;
281 490
282 /* RFC 4462 says we MUST NOT do SPNEGO */ 491 /* RFC 4462 says we MUST NOT do SPNEGO */
283 if (oid->length == spnego_oid.length && 492 if (oid->length == spnego_oid.length &&
@@ -287,6 +496,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
287 ssh_gssapi_build_ctx(ctx); 496 ssh_gssapi_build_ctx(ctx);
288 ssh_gssapi_set_oid(*ctx, oid); 497 ssh_gssapi_set_oid(*ctx, oid);
289 major = ssh_gssapi_import_name(*ctx, host); 498 major = ssh_gssapi_import_name(*ctx, host);
499
500 if (!GSS_ERROR(major) && client)
501 major = ssh_gssapi_client_identity(*ctx, client);
502
290 if (!GSS_ERROR(major)) { 503 if (!GSS_ERROR(major)) {
291 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 504 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
292 NULL); 505 NULL);
@@ -296,10 +509,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
296 GSS_C_NO_BUFFER); 509 GSS_C_NO_BUFFER);
297 } 510 }
298 511
299 if (GSS_ERROR(major)) 512 if (GSS_ERROR(major) || intctx != NULL)
300 ssh_gssapi_delete_ctx(ctx); 513 ssh_gssapi_delete_ctx(ctx);
301 514
302 return (!GSS_ERROR(major)); 515 return (!GSS_ERROR(major));
303} 516}
304 517
518int
519ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
520 static gss_name_t saved_name = GSS_C_NO_NAME;
521 static OM_uint32 saved_lifetime = 0;
522 static gss_OID saved_mech = GSS_C_NO_OID;
523 static gss_name_t name;
524 static OM_uint32 last_call = 0;
525 OM_uint32 lifetime, now, major, minor;
526 int equal;
527
528 now = time(NULL);
529
530 if (ctxt) {
531 debug("Rekey has happened - updating saved versions");
532
533 if (saved_name != GSS_C_NO_NAME)
534 gss_release_name(&minor, &saved_name);
535
536 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
537 &saved_name, &saved_lifetime, NULL, NULL);
538
539 if (!GSS_ERROR(major)) {
540 saved_mech = ctxt->oid;
541 saved_lifetime+= now;
542 } else {
543 /* Handle the error */
544 }
545 return 0;
546 }
547
548 if (now - last_call < 10)
549 return 0;
550
551 last_call = now;
552
553 if (saved_mech == GSS_C_NO_OID)
554 return 0;
555
556 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
557 &name, &lifetime, NULL, NULL);
558 if (major == GSS_S_CREDENTIALS_EXPIRED)
559 return 0;
560 else if (GSS_ERROR(major))
561 return 0;
562
563 major = gss_compare_name(&minor, saved_name, name, &equal);
564 gss_release_name(&minor, &name);
565 if (GSS_ERROR(major))
566 return 0;
567
568 if (equal && (saved_lifetime < lifetime + now - 10))
569 return 1;
570
571 return 0;
572}
573
305#endif /* GSSAPI */ 574#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..953c0a248
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,338 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include "includes.h"
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include <string.h>
35
36#include "xmalloc.h"
37#include "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 BIGNUM *p = NULL;
60 BIGNUM *g = NULL;
61 u_char *kbuf;
62 u_char *serverhostkey = NULL;
63 u_char *empty = "";
64 char *msg;
65 int type = 0;
66 int first = 1;
67 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
68 u_char hash[SSH_DIGEST_MAX_LENGTH];
69 size_t hashlen;
70
71 /* Initialise our GSSAPI world */
72 ssh_gssapi_build_ctx(&ctxt);
73 if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type)
74 == GSS_C_NO_OID)
75 fatal("Couldn't identify host exchange");
76
77 if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host))
78 fatal("Couldn't import hostname");
79
80 if (ssh->kex->gss_client &&
81 ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client))
82 fatal("Couldn't acquire client credentials");
83
84 switch (ssh->kex->kex_type) {
85 case KEX_GSS_GRP1_SHA1:
86 dh = dh_new_group1();
87 break;
88 case KEX_GSS_GRP14_SHA1:
89 dh = dh_new_group14();
90 break;
91 case KEX_GSS_GEX_SHA1:
92 debug("Doing group exchange\n");
93 nbits = dh_estimate(ssh->kex->we_need * 8);
94 packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
95 packet_put_int(min);
96 packet_put_int(nbits);
97 packet_put_int(max);
98
99 packet_send();
100
101 packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
102
103 if ((p = BN_new()) == NULL)
104 fatal("BN_new() failed");
105 packet_get_bignum2(p);
106 if ((g = BN_new()) == NULL)
107 fatal("BN_new() failed");
108 packet_get_bignum2(g);
109 packet_check_eom();
110
111 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
112 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
113 min, BN_num_bits(p), max);
114
115 dh = dh_new_group(g, p);
116 break;
117 default:
118 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
119 }
120
121 /* Step 1 - e is dh->pub_key */
122 dh_gen_key(dh, ssh->kex->we_need * 8);
123
124 /* This is f, we initialise it now to make life easier */
125 dh_server_pub = BN_new();
126 if (dh_server_pub == NULL)
127 fatal("dh_server_pub == NULL");
128
129 token_ptr = GSS_C_NO_BUFFER;
130
131 do {
132 debug("Calling gss_init_sec_context");
133
134 maj_status = ssh_gssapi_init_ctx(ctxt,
135 ssh->kex->gss_deleg_creds, token_ptr, &send_tok,
136 &ret_flags);
137
138 if (GSS_ERROR(maj_status)) {
139 if (send_tok.length != 0) {
140 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
141 packet_put_string(send_tok.value,
142 send_tok.length);
143 }
144 fatal("gss_init_context failed");
145 }
146
147 /* If we've got an old receive buffer get rid of it */
148 if (token_ptr != GSS_C_NO_BUFFER)
149 free(recv_tok.value);
150
151 if (maj_status == GSS_S_COMPLETE) {
152 /* If mutual state flag is not true, kex fails */
153 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
154 fatal("Mutual authentication failed");
155
156 /* If integ avail flag is not true kex fails */
157 if (!(ret_flags & GSS_C_INTEG_FLAG))
158 fatal("Integrity check failed");
159 }
160
161 /*
162 * If we have data to send, then the last message that we
163 * received cannot have been a 'complete'.
164 */
165 if (send_tok.length != 0) {
166 if (first) {
167 packet_start(SSH2_MSG_KEXGSS_INIT);
168 packet_put_string(send_tok.value,
169 send_tok.length);
170 packet_put_bignum2(dh->pub_key);
171 first = 0;
172 } else {
173 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
174 packet_put_string(send_tok.value,
175 send_tok.length);
176 }
177 packet_send();
178 gss_release_buffer(&min_status, &send_tok);
179
180 /* If we've sent them data, they should reply */
181 do {
182 type = packet_read();
183 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
184 debug("Received KEXGSS_HOSTKEY");
185 if (serverhostkey)
186 fatal("Server host key received more than once");
187 serverhostkey =
188 packet_get_string(&slen);
189 }
190 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
191
192 switch (type) {
193 case SSH2_MSG_KEXGSS_CONTINUE:
194 debug("Received GSSAPI_CONTINUE");
195 if (maj_status == GSS_S_COMPLETE)
196 fatal("GSSAPI Continue received from server when complete");
197 recv_tok.value = packet_get_string(&strlen);
198 recv_tok.length = strlen;
199 break;
200 case SSH2_MSG_KEXGSS_COMPLETE:
201 debug("Received GSSAPI_COMPLETE");
202 packet_get_bignum2(dh_server_pub);
203 msg_tok.value = packet_get_string(&strlen);
204 msg_tok.length = strlen;
205
206 /* Is there a token included? */
207 if (packet_get_char()) {
208 recv_tok.value=
209 packet_get_string(&strlen);
210 recv_tok.length = strlen;
211 /* If we're already complete - protocol error */
212 if (maj_status == GSS_S_COMPLETE)
213 packet_disconnect("Protocol error: received token when complete");
214 } else {
215 /* No token included */
216 if (maj_status != GSS_S_COMPLETE)
217 packet_disconnect("Protocol error: did not receive final token");
218 }
219 break;
220 case SSH2_MSG_KEXGSS_ERROR:
221 debug("Received Error");
222 maj_status = packet_get_int();
223 min_status = packet_get_int();
224 msg = packet_get_string(NULL);
225 (void) packet_get_string_ptr(NULL);
226 fatal("GSSAPI Error: \n%.400s",msg);
227 default:
228 packet_disconnect("Protocol error: didn't expect packet type %d",
229 type);
230 }
231 token_ptr = &recv_tok;
232 } else {
233 /* No data, and not complete */
234 if (maj_status != GSS_S_COMPLETE)
235 fatal("Not complete, and no token output");
236 }
237 } while (maj_status & GSS_S_CONTINUE_NEEDED);
238
239 /*
240 * We _must_ have received a COMPLETE message in reply from the
241 * server, which will have set dh_server_pub and msg_tok
242 */
243
244 if (type != SSH2_MSG_KEXGSS_COMPLETE)
245 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
246
247 /* Check f in range [1, p-1] */
248 if (!dh_pub_is_valid(dh, dh_server_pub))
249 packet_disconnect("bad server public DH value");
250
251 /* compute K=f^x mod p */
252 klen = DH_size(dh);
253 kbuf = xmalloc(klen);
254 kout = DH_compute_key(kbuf, dh_server_pub, dh);
255 if (kout < 0)
256 fatal("DH_compute_key: failed");
257
258 shared_secret = BN_new();
259 if (shared_secret == NULL)
260 fatal("kexgss_client: BN_new failed");
261
262 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
263 fatal("kexdh_client: BN_bin2bn failed");
264
265 memset(kbuf, 0, klen);
266 free(kbuf);
267
268 hashlen = sizeof(hash);
269 switch (ssh->kex->kex_type) {
270 case KEX_GSS_GRP1_SHA1:
271 case KEX_GSS_GRP14_SHA1:
272 kex_dh_hash(
273 ssh->kex->hash_alg,
274 ssh->kex->client_version_string,
275 ssh->kex->server_version_string,
276 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
277 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
278 (serverhostkey ? serverhostkey : empty), slen,
279 dh->pub_key, /* e */
280 dh_server_pub, /* f */
281 shared_secret, /* K */
282 hash, &hashlen
283 );
284 break;
285 case KEX_GSS_GEX_SHA1:
286 kexgex_hash(
287 ssh->kex->hash_alg,
288 ssh->kex->client_version_string,
289 ssh->kex->server_version_string,
290 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
291 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
292 (serverhostkey ? serverhostkey : empty), slen,
293 min, nbits, max,
294 dh->p, dh->g,
295 dh->pub_key,
296 dh_server_pub,
297 shared_secret,
298 hash, &hashlen
299 );
300 break;
301 default:
302 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
303 }
304
305 gssbuf.value = hash;
306 gssbuf.length = hashlen;
307
308 /* Verify that the hash matches the MIC we just got. */
309 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
310 packet_disconnect("Hash's MIC didn't verify");
311
312 free(msg_tok.value);
313
314 DH_free(dh);
315 free(serverhostkey);
316 BN_clear_free(dh_server_pub);
317
318 /* save session id */
319 if (ssh->kex->session_id == NULL) {
320 ssh->kex->session_id_len = hashlen;
321 ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
322 memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
323 }
324
325 if (ssh->kex->gss_deleg_creds)
326 ssh_gssapi_credentials_updated(ctxt);
327
328 if (gss_kex_context == NULL)
329 gss_kex_context = ctxt;
330 else
331 ssh_gssapi_delete_ctx(&ctxt);
332
333 kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
334 BN_clear_free(shared_secret);
335 return kex_send_newkeys(ssh);
336}
337
338#endif /* GSSAPI */
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..31ec6a890
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,295 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include <string.h>
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include "xmalloc.h"
35#include "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 BIGNUM *shared_secret = NULL;
72 BIGNUM *dh_client_pub = NULL;
73 int type = 0;
74 gss_OID oid;
75 char *mechs;
76 u_char hash[SSH_DIGEST_MAX_LENGTH];
77 size_t hashlen;
78
79 /* Initialise GSSAPI */
80
81 /* If we're rekeying, privsep means that some of the private structures
82 * in the GSSAPI code are no longer available. This kludges them back
83 * into life
84 */
85 if (!ssh_gssapi_oid_table_ok()) {
86 mechs = ssh_gssapi_server_mechanisms();
87 free(mechs);
88 }
89
90 debug2("%s: Identifying %s", __func__, ssh->kex->name);
91 oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type);
92 if (oid == GSS_C_NO_OID)
93 fatal("Unknown gssapi mechanism");
94
95 debug2("%s: Acquiring credentials", __func__);
96
97 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
98 fatal("Unable to acquire credentials for the server");
99
100 switch (ssh->kex->kex_type) {
101 case KEX_GSS_GRP1_SHA1:
102 dh = dh_new_group1();
103 break;
104 case KEX_GSS_GRP14_SHA1:
105 dh = dh_new_group14();
106 break;
107 case KEX_GSS_GEX_SHA1:
108 debug("Doing group exchange");
109 packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
110 min = packet_get_int();
111 nbits = packet_get_int();
112 max = packet_get_int();
113 packet_check_eom();
114 if (max < min || nbits < min || max < nbits)
115 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
116 min, nbits, max);
117 dh = PRIVSEP(choose_dh(MAX(DH_GRP_MIN, min),
118 nbits, MIN(DH_GRP_MAX, max)));
119 if (dh == NULL)
120 packet_disconnect("Protocol error: no matching group found");
121
122 packet_start(SSH2_MSG_KEXGSS_GROUP);
123 packet_put_bignum2(dh->p);
124 packet_put_bignum2(dh->g);
125 packet_send();
126
127 packet_write_wait();
128 break;
129 default:
130 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
131 }
132
133 dh_gen_key(dh, ssh->kex->we_need * 8);
134
135 do {
136 debug("Wait SSH2_MSG_GSSAPI_INIT");
137 type = packet_read();
138 switch(type) {
139 case SSH2_MSG_KEXGSS_INIT:
140 if (dh_client_pub != NULL)
141 fatal("Received KEXGSS_INIT after initialising");
142 recv_tok.value = packet_get_string(&slen);
143 recv_tok.length = slen;
144
145 if ((dh_client_pub = BN_new()) == NULL)
146 fatal("dh_client_pub == NULL");
147
148 packet_get_bignum2(dh_client_pub);
149
150 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
151 break;
152 case SSH2_MSG_KEXGSS_CONTINUE:
153 recv_tok.value = packet_get_string(&slen);
154 recv_tok.length = slen;
155 break;
156 default:
157 packet_disconnect(
158 "Protocol error: didn't expect packet type %d",
159 type);
160 }
161
162 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
163 &send_tok, &ret_flags));
164
165 free(recv_tok.value);
166
167 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
168 fatal("Zero length token output when incomplete");
169
170 if (dh_client_pub == NULL)
171 fatal("No client public key");
172
173 if (maj_status & GSS_S_CONTINUE_NEEDED) {
174 debug("Sending GSSAPI_CONTINUE");
175 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
176 packet_put_string(send_tok.value, send_tok.length);
177 packet_send();
178 gss_release_buffer(&min_status, &send_tok);
179 }
180 } while (maj_status & GSS_S_CONTINUE_NEEDED);
181
182 if (GSS_ERROR(maj_status)) {
183 if (send_tok.length > 0) {
184 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
185 packet_put_string(send_tok.value, send_tok.length);
186 packet_send();
187 }
188 fatal("accept_ctx died");
189 }
190
191 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
192 fatal("Mutual Authentication flag wasn't set");
193
194 if (!(ret_flags & GSS_C_INTEG_FLAG))
195 fatal("Integrity flag wasn't set");
196
197 if (!dh_pub_is_valid(dh, dh_client_pub))
198 packet_disconnect("bad client public DH value");
199
200 klen = DH_size(dh);
201 kbuf = xmalloc(klen);
202 kout = DH_compute_key(kbuf, dh_client_pub, dh);
203 if (kout < 0)
204 fatal("DH_compute_key: failed");
205
206 shared_secret = BN_new();
207 if (shared_secret == NULL)
208 fatal("kexgss_server: BN_new failed");
209
210 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
211 fatal("kexgss_server: BN_bin2bn failed");
212
213 memset(kbuf, 0, klen);
214 free(kbuf);
215
216 hashlen = sizeof(hash);
217 switch (ssh->kex->kex_type) {
218 case KEX_GSS_GRP1_SHA1:
219 case KEX_GSS_GRP14_SHA1:
220 kex_dh_hash(
221 ssh->kex->hash_alg,
222 ssh->kex->client_version_string, ssh->kex->server_version_string,
223 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
224 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
225 NULL, 0, /* Change this if we start sending host keys */
226 dh_client_pub, dh->pub_key, shared_secret,
227 hash, &hashlen
228 );
229 break;
230 case KEX_GSS_GEX_SHA1:
231 kexgex_hash(
232 ssh->kex->hash_alg,
233 ssh->kex->client_version_string, ssh->kex->server_version_string,
234 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
235 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
236 NULL, 0,
237 min, nbits, max,
238 dh->p, dh->g,
239 dh_client_pub,
240 dh->pub_key,
241 shared_secret,
242 hash, &hashlen
243 );
244 break;
245 default:
246 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
247 }
248
249 BN_clear_free(dh_client_pub);
250
251 if (ssh->kex->session_id == NULL) {
252 ssh->kex->session_id_len = hashlen;
253 ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
254 memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
255 }
256
257 gssbuf.value = hash;
258 gssbuf.length = hashlen;
259
260 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
261 fatal("Couldn't get MIC");
262
263 packet_start(SSH2_MSG_KEXGSS_COMPLETE);
264 packet_put_bignum2(dh->pub_key);
265 packet_put_string(msg_tok.value,msg_tok.length);
266
267 if (send_tok.length != 0) {
268 packet_put_char(1); /* true */
269 packet_put_string(send_tok.value, send_tok.length);
270 } else {
271 packet_put_char(0); /* false */
272 }
273 packet_send();
274
275 gss_release_buffer(&min_status, &send_tok);
276 gss_release_buffer(&min_status, &msg_tok);
277
278 if (gss_kex_context == NULL)
279 gss_kex_context = ctxt;
280 else
281 ssh_gssapi_delete_ctx(&ctxt);
282
283 DH_free(dh);
284
285 kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
286 BN_clear_free(shared_secret);
287 kex_send_newkeys(ssh);
288
289 /* If this was a rekey, then save out any delegated credentials we
290 * just exchanged. */
291 if (options.gss_store_rekey)
292 ssh_gssapi_rekey_creds();
293 return 0;
294}
295#endif /* GSSAPI */
diff --git a/monitor.c b/monitor.c
index d4b4b0471..4e574a2ae 100644
--- a/monitor.c
+++ b/monitor.c
@@ -143,6 +143,8 @@ int mm_answer_gss_setup_ctx(int, struct sshbuf *);
143int mm_answer_gss_accept_ctx(int, struct sshbuf *); 143int mm_answer_gss_accept_ctx(int, struct sshbuf *);
144int mm_answer_gss_userok(int, struct sshbuf *); 144int mm_answer_gss_userok(int, struct sshbuf *);
145int mm_answer_gss_checkmic(int, struct sshbuf *); 145int mm_answer_gss_checkmic(int, struct sshbuf *);
146int mm_answer_gss_sign(int, struct sshbuf *);
147int mm_answer_gss_updatecreds(int, struct sshbuf *);
146#endif 148#endif
147 149
148#ifdef SSH_AUDIT_EVENTS 150#ifdef SSH_AUDIT_EVENTS
@@ -213,11 +215,18 @@ struct mon_table mon_dispatch_proto20[] = {
213 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, 215 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
214 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, 216 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
215 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, 217 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
218 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
216#endif 219#endif
217 {0, 0, NULL} 220 {0, 0, NULL}
218}; 221};
219 222
220struct mon_table mon_dispatch_postauth20[] = { 223struct mon_table mon_dispatch_postauth20[] = {
224#ifdef GSSAPI
225 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
226 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
227 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
228 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
229#endif
221#ifdef WITH_OPENSSL 230#ifdef WITH_OPENSSL
222 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 231 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
223#endif 232#endif
@@ -287,6 +296,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
287 /* Permit requests for moduli and signatures */ 296 /* Permit requests for moduli and signatures */
288 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 297 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
289 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 298 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
299#ifdef GSSAPI
300 /* and for the GSSAPI key exchange */
301 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
302#endif
290 303
291 /* The first few requests do not require asynchronous access */ 304 /* The first few requests do not require asynchronous access */
292 while (!authenticated) { 305 while (!authenticated) {
@@ -399,6 +412,10 @@ monitor_child_postauth(struct monitor *pmonitor)
399 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 412 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
400 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 413 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
401 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 414 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
415#ifdef GSSAPI
416 /* and for the GSSAPI key exchange */
417 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
418#endif
402 419
403 if (auth_opts->permit_pty_flag) { 420 if (auth_opts->permit_pty_flag) {
404 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); 421 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
@@ -1662,6 +1679,13 @@ monitor_apply_keystate(struct monitor *pmonitor)
1662# endif 1679# endif
1663#endif /* WITH_OPENSSL */ 1680#endif /* WITH_OPENSSL */
1664 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 1681 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
1682#ifdef GSSAPI
1683 if (options.gss_keyex) {
1684 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1685 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1686 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1687 }
1688#endif
1665 kex->load_host_public_key=&get_hostkey_public_by_type; 1689 kex->load_host_public_key=&get_hostkey_public_by_type;
1666 kex->load_host_private_key=&get_hostkey_private_by_type; 1690 kex->load_host_private_key=&get_hostkey_private_by_type;
1667 kex->host_key_index=&get_hostkey_index; 1691 kex->host_key_index=&get_hostkey_index;
@@ -1752,8 +1776,8 @@ mm_answer_gss_setup_ctx(int sock, struct sshbuf *m)
1752 u_char *p; 1776 u_char *p;
1753 int r; 1777 int r;
1754 1778
1755 if (!options.gss_authentication) 1779 if (!options.gss_authentication && !options.gss_keyex)
1756 fatal("%s: GSSAPI authentication not enabled", __func__); 1780 fatal("%s: GSSAPI not enabled", __func__);
1757 1781
1758 if ((r = sshbuf_get_string(m, &p, &len)) != 0) 1782 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1759 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1783 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1785,8 +1809,8 @@ mm_answer_gss_accept_ctx(int sock, struct sshbuf *m)
1785 OM_uint32 flags = 0; /* GSI needs this */ 1809 OM_uint32 flags = 0; /* GSI needs this */
1786 int r; 1810 int r;
1787 1811
1788 if (!options.gss_authentication) 1812 if (!options.gss_authentication && !options.gss_keyex)
1789 fatal("%s: GSSAPI authentication not enabled", __func__); 1813 fatal("%s: GSSAPI not enabled", __func__);
1790 1814
1791 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) 1815 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
1792 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1816 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1806,6 +1830,7 @@ mm_answer_gss_accept_ctx(int sock, struct sshbuf *m)
1806 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 1830 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
1807 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 1831 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
1808 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 1832 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
1833 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
1809 } 1834 }
1810 return (0); 1835 return (0);
1811} 1836}
@@ -1817,8 +1842,8 @@ mm_answer_gss_checkmic(int sock, struct sshbuf *m)
1817 OM_uint32 ret; 1842 OM_uint32 ret;
1818 int r; 1843 int r;
1819 1844
1820 if (!options.gss_authentication) 1845 if (!options.gss_authentication && !options.gss_keyex)
1821 fatal("%s: GSSAPI authentication not enabled", __func__); 1846 fatal("%s: GSSAPI not enabled", __func__);
1822 1847
1823 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || 1848 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
1824 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) 1849 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
@@ -1847,10 +1872,11 @@ mm_answer_gss_userok(int sock, struct sshbuf *m)
1847 int r, authenticated; 1872 int r, authenticated;
1848 const char *displayname; 1873 const char *displayname;
1849 1874
1850 if (!options.gss_authentication) 1875 if (!options.gss_authentication && !options.gss_keyex)
1851 fatal("%s: GSSAPI authentication not enabled", __func__); 1876 fatal("%s: GSSAPI not enabled", __func__);
1852 1877
1853 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 1878 authenticated = authctxt->valid &&
1879 ssh_gssapi_userok(authctxt->user, authctxt->pw);
1854 1880
1855 sshbuf_reset(m); 1881 sshbuf_reset(m);
1856 if ((r = sshbuf_put_u32(m, authenticated)) != 0) 1882 if ((r = sshbuf_put_u32(m, authenticated)) != 0)
@@ -1867,5 +1893,83 @@ mm_answer_gss_userok(int sock, struct sshbuf *m)
1867 /* Monitor loop will terminate if authenticated */ 1893 /* Monitor loop will terminate if authenticated */
1868 return (authenticated); 1894 return (authenticated);
1869} 1895}
1896
1897int
1898mm_answer_gss_sign(int socket, struct sshbuf *m)
1899{
1900 gss_buffer_desc data;
1901 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
1902 OM_uint32 major, minor;
1903 size_t len;
1904 u_char *p;
1905 int r;
1906
1907 if (!options.gss_authentication && !options.gss_keyex)
1908 fatal("%s: GSSAPI not enabled", __func__);
1909
1910 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1911 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1912 data.value = p;
1913 data.length = len;
1914 if (data.length != 20)
1915 fatal("%s: data length incorrect: %d", __func__,
1916 (int) data.length);
1917
1918 /* Save the session ID on the first time around */
1919 if (session_id2_len == 0) {
1920 session_id2_len = data.length;
1921 session_id2 = xmalloc(session_id2_len);
1922 memcpy(session_id2, data.value, session_id2_len);
1923 }
1924 major = ssh_gssapi_sign(gsscontext, &data, &hash);
1925
1926 free(data.value);
1927
1928 sshbuf_reset(m);
1929 if ((r = sshbuf_put_u32(m, major)) != 0 ||
1930 (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
1931 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1932
1933 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
1934
1935 gss_release_buffer(&minor, &hash);
1936
1937 /* Turn on getpwnam permissions */
1938 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
1939
1940 /* And credential updating, for when rekeying */
1941 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
1942
1943 return (0);
1944}
1945
1946int
1947mm_answer_gss_updatecreds(int socket, struct sshbuf *m) {
1948 ssh_gssapi_ccache store;
1949 int r, ok;
1950
1951 if (!options.gss_authentication && !options.gss_keyex)
1952 fatal("%s: GSSAPI not enabled", __func__);
1953
1954 if ((r = sshbuf_get_cstring(m, &store.filename, NULL)) != 0 ||
1955 (r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 ||
1956 (r = sshbuf_get_cstring(m, &store.envval, NULL)) != 0)
1957 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1958
1959 ok = ssh_gssapi_update_creds(&store);
1960
1961 free(store.filename);
1962 free(store.envvar);
1963 free(store.envval);
1964
1965 sshbuf_reset(m);
1966 if ((r = sshbuf_put_u32(m, ok)) != 0)
1967 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1968
1969 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
1970
1971 return(0);
1972}
1973
1870#endif /* GSSAPI */ 1974#endif /* GSSAPI */
1871 1975
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/readconf.c b/readconf.c
index db5f2d547..4ad3c75fe 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 },
@@ -973,10 +985,30 @@ parse_time:
973 intptr = &options->gss_authentication; 985 intptr = &options->gss_authentication;
974 goto parse_flag; 986 goto parse_flag;
975 987
988 case oGssKeyEx:
989 intptr = &options->gss_keyex;
990 goto parse_flag;
991
976 case oGssDelegateCreds: 992 case oGssDelegateCreds:
977 intptr = &options->gss_deleg_creds; 993 intptr = &options->gss_deleg_creds;
978 goto parse_flag; 994 goto parse_flag;
979 995
996 case oGssTrustDns:
997 intptr = &options->gss_trust_dns;
998 goto parse_flag;
999
1000 case oGssClientIdentity:
1001 charptr = &options->gss_client_identity;
1002 goto parse_string;
1003
1004 case oGssServerIdentity:
1005 charptr = &options->gss_server_identity;
1006 goto parse_string;
1007
1008 case oGssRenewalRekey:
1009 intptr = &options->gss_renewal_rekey;
1010 goto parse_flag;
1011
980 case oBatchMode: 1012 case oBatchMode:
981 intptr = &options->batch_mode; 1013 intptr = &options->batch_mode;
982 goto parse_flag; 1014 goto parse_flag;
@@ -1817,7 +1849,12 @@ initialize_options(Options * options)
1817 options->pubkey_authentication = -1; 1849 options->pubkey_authentication = -1;
1818 options->challenge_response_authentication = -1; 1850 options->challenge_response_authentication = -1;
1819 options->gss_authentication = -1; 1851 options->gss_authentication = -1;
1852 options->gss_keyex = -1;
1820 options->gss_deleg_creds = -1; 1853 options->gss_deleg_creds = -1;
1854 options->gss_trust_dns = -1;
1855 options->gss_renewal_rekey = -1;
1856 options->gss_client_identity = NULL;
1857 options->gss_server_identity = NULL;
1821 options->password_authentication = -1; 1858 options->password_authentication = -1;
1822 options->kbd_interactive_authentication = -1; 1859 options->kbd_interactive_authentication = -1;
1823 options->kbd_interactive_devices = NULL; 1860 options->kbd_interactive_devices = NULL;
@@ -1962,8 +1999,14 @@ fill_default_options(Options * options)
1962 options->challenge_response_authentication = 1; 1999 options->challenge_response_authentication = 1;
1963 if (options->gss_authentication == -1) 2000 if (options->gss_authentication == -1)
1964 options->gss_authentication = 0; 2001 options->gss_authentication = 0;
2002 if (options->gss_keyex == -1)
2003 options->gss_keyex = 0;
1965 if (options->gss_deleg_creds == -1) 2004 if (options->gss_deleg_creds == -1)
1966 options->gss_deleg_creds = 0; 2005 options->gss_deleg_creds = 0;
2006 if (options->gss_trust_dns == -1)
2007 options->gss_trust_dns = 0;
2008 if (options->gss_renewal_rekey == -1)
2009 options->gss_renewal_rekey = 0;
1967 if (options->password_authentication == -1) 2010 if (options->password_authentication == -1)
1968 options->password_authentication = 1; 2011 options->password_authentication = 1;
1969 if (options->kbd_interactive_authentication == -1) 2012 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index c56887816..5ea0c296b 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 c0f6af0be..e1ae07fb7 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;
@@ -333,10 +335,14 @@ fill_default_server_options(ServerOptions *options)
333 options->kerberos_get_afs_token = 0; 335 options->kerberos_get_afs_token = 0;
334 if (options->gss_authentication == -1) 336 if (options->gss_authentication == -1)
335 options->gss_authentication = 0; 337 options->gss_authentication = 0;
338 if (options->gss_keyex == -1)
339 options->gss_keyex = 0;
336 if (options->gss_cleanup_creds == -1) 340 if (options->gss_cleanup_creds == -1)
337 options->gss_cleanup_creds = 1; 341 options->gss_cleanup_creds = 1;
338 if (options->gss_strict_acceptor == -1) 342 if (options->gss_strict_acceptor == -1)
339 options->gss_strict_acceptor = 1; 343 options->gss_strict_acceptor = 1;
344 if (options->gss_store_rekey == -1)
345 options->gss_store_rekey = 0;
340 if (options->password_authentication == -1) 346 if (options->password_authentication == -1)
341 options->password_authentication = 1; 347 options->password_authentication = 1;
342 if (options->kbd_interactive_authentication == -1) 348 if (options->kbd_interactive_authentication == -1)
@@ -481,6 +487,7 @@ typedef enum {
481 sHostKeyAlgorithms, 487 sHostKeyAlgorithms,
482 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 488 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
483 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 489 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
490 sGssKeyEx, sGssStoreRekey,
484 sAcceptEnv, sSetEnv, sPermitTunnel, 491 sAcceptEnv, sSetEnv, sPermitTunnel,
485 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, 492 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
486 sUsePrivilegeSeparation, sAllowAgentForwarding, 493 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -555,12 +562,20 @@ static struct {
555#ifdef GSSAPI 562#ifdef GSSAPI
556 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 563 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
557 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 564 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
565 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
558 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 566 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
567 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
568 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
559#else 569#else
560 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 570 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
561 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 571 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
572 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
562 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 573 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
574 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
575 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
563#endif 576#endif
577 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
578 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
564 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 579 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
565 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 580 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
566 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 581 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1459,6 +1474,10 @@ process_server_config_line(ServerOptions *options, char *line,
1459 intptr = &options->gss_authentication; 1474 intptr = &options->gss_authentication;
1460 goto parse_flag; 1475 goto parse_flag;
1461 1476
1477 case sGssKeyEx:
1478 intptr = &options->gss_keyex;
1479 goto parse_flag;
1480
1462 case sGssCleanupCreds: 1481 case sGssCleanupCreds:
1463 intptr = &options->gss_cleanup_creds; 1482 intptr = &options->gss_cleanup_creds;
1464 goto parse_flag; 1483 goto parse_flag;
@@ -1467,6 +1486,10 @@ process_server_config_line(ServerOptions *options, char *line,
1467 intptr = &options->gss_strict_acceptor; 1486 intptr = &options->gss_strict_acceptor;
1468 goto parse_flag; 1487 goto parse_flag;
1469 1488
1489 case sGssStoreRekey:
1490 intptr = &options->gss_store_rekey;
1491 goto parse_flag;
1492
1470 case sPasswordAuthentication: 1493 case sPasswordAuthentication:
1471 intptr = &options->password_authentication; 1494 intptr = &options->password_authentication;
1472 goto parse_flag; 1495 goto parse_flag;
@@ -2551,7 +2574,10 @@ dump_config(ServerOptions *o)
2551#endif 2574#endif
2552#ifdef GSSAPI 2575#ifdef GSSAPI
2553 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2576 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2577 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2554 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2578 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2579 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2580 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2555#endif 2581#endif
2556 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2582 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2557 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2583 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 557521d73..9b117fe27 100644
--- a/servconf.h
+++ b/servconf.h
@@ -124,8 +124,10 @@ typedef struct {
124 int kerberos_get_afs_token; /* If true, try to get AFS token if 124 int kerberos_get_afs_token; /* If true, try to get AFS token if
125 * authenticated with Kerberos. */ 125 * authenticated with Kerberos. */
126 int gss_authentication; /* If true, permit GSSAPI authentication */ 126 int gss_authentication; /* If true, permit GSSAPI authentication */
127 int gss_keyex; /* If true, permit GSSAPI key exchange */
127 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 128 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
128 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 129 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
130 int gss_store_rekey;
129 int password_authentication; /* If true, permit password 131 int password_authentication; /* If true, permit password
130 * authentication. */ 132 * authentication. */
131 int kbd_interactive_authentication; /* If true, permit */ 133 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 f499396a3..5b99921b4 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -718,10 +718,42 @@ The default is
718Specifies whether user authentication based on GSSAPI is allowed. 718Specifies whether user authentication based on GSSAPI is allowed.
719The default is 719The default is
720.Cm no . 720.Cm no .
721.It Cm GSSAPIKeyExchange
722Specifies whether key exchange based on GSSAPI may be used. When using
723GSSAPI key exchange the server need not have a host key.
724The default is
725.Cm no .
726.It Cm GSSAPIClientIdentity
727If set, specifies the GSSAPI client identity that ssh should use when
728connecting to the server. The default is unset, which means that the default
729identity will be used.
730.It Cm GSSAPIServerIdentity
731If set, specifies the GSSAPI server identity that ssh should expect when
732connecting to the server. The default is unset, which means that the
733expected GSSAPI server identity will be determined from the target
734hostname.
721.It Cm GSSAPIDelegateCredentials 735.It Cm GSSAPIDelegateCredentials
722Forward (delegate) credentials to the server. 736Forward (delegate) credentials to the server.
723The default is 737The default is
724.Cm no . 738.Cm no .
739.It Cm GSSAPIRenewalForcesRekey
740If set to
741.Cm yes
742then renewal of the client's GSSAPI credentials will force the rekeying of the
743ssh connection. With a compatible server, this can delegate the renewed
744credentials to a session on the server.
745The default is
746.Cm no .
747.It Cm GSSAPITrustDns
748Set to
749.Cm yes
750to indicate that the DNS is trusted to securely canonicalize
751the name of the host being connected to. If
752.Cm no ,
753the hostname entered on the
754command line will be passed untouched to the GSSAPI library.
755The default is
756.Cm no .
725.It Cm HashKnownHosts 757.It Cm HashKnownHosts
726Indicates that 758Indicates that
727.Xr ssh 1 759.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index 10e4f0a08..c6a1b1271 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,
@@ -657,25 +723,40 @@ userauth_gssapi(Authctxt *authctxt)
657 static u_int mech = 0; 723 static u_int mech = 0;
658 OM_uint32 min; 724 OM_uint32 min;
659 int r, ok = 0; 725 int r, ok = 0;
726 char *gss_host;
727
728 if (options.gss_server_identity)
729 gss_host = xstrdup(options.gss_server_identity);
730 else if (options.gss_trust_dns)
731 gss_host = remote_hostname(active_state);
732 else
733 gss_host = xstrdup(authctxt->host);
660 734
661 /* Try one GSSAPI method at a time, rather than sending them all at 735 /* Try one GSSAPI method at a time, rather than sending them all at
662 * once. */ 736 * once. */
663 737
664 if (gss_supported == NULL) 738 if (gss_supported == NULL)
665 gss_indicate_mechs(&min, &gss_supported); 739 if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
740 gss_supported = NULL;
741 free(gss_host);
742 return 0;
743 }
666 744
667 /* Check to see if the mechanism is usable before we offer it */ 745 /* Check to see if the mechanism is usable before we offer it */
668 while (mech < gss_supported->count && !ok) { 746 while (mech < gss_supported->count && !ok) {
669 /* My DER encoding requires length<128 */ 747 /* My DER encoding requires length<128 */
670 if (gss_supported->elements[mech].length < 128 && 748 if (gss_supported->elements[mech].length < 128 &&
671 ssh_gssapi_check_mechanism(&gssctxt, 749 ssh_gssapi_check_mechanism(&gssctxt,
672 &gss_supported->elements[mech], authctxt->host)) { 750 &gss_supported->elements[mech], gss_host,
751 options.gss_client_identity)) {
673 ok = 1; /* Mechanism works */ 752 ok = 1; /* Mechanism works */
674 } else { 753 } else {
675 mech++; 754 mech++;
676 } 755 }
677 } 756 }
678 757
758 free(gss_host);
759
679 if (!ok) 760 if (!ok)
680 return 0; 761 return 0;
681 762
@@ -906,6 +987,54 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
906 free(lang); 987 free(lang);
907 return r; 988 return r;
908} 989}
990
991int
992userauth_gsskeyex(Authctxt *authctxt)
993{
994 struct ssh *ssh = active_state; /* XXX */
995 struct sshbuf *b;
996 gss_buffer_desc gssbuf;
997 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
998 OM_uint32 ms;
999 int r;
1000
1001 static int attempt = 0;
1002 if (attempt++ >= 1)
1003 return (0);
1004
1005 if (gss_kex_context == NULL) {
1006 debug("No valid Key exchange context");
1007 return (0);
1008 }
1009
1010 if ((b = sshbuf_new()) == NULL)
1011 fatal("%s: sshbuf_new failed", __func__);
1012 ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
1013 "gssapi-keyex");
1014
1015 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
1016 fatal("%s: sshbuf_mutable_ptr failed", __func__);
1017 gssbuf.length = sshbuf_len(b);
1018
1019 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
1020 sshbuf_free(b);
1021 return (0);
1022 }
1023
1024 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1025 (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1026 (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1027 (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1028 (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
1029 (r = sshpkt_send(ssh)) != 0)
1030 fatal("%s: %s", __func__, ssh_err(r));
1031
1032 sshbuf_free(b);
1033 gss_release_buffer(&ms, &mic);
1034
1035 return (1);
1036}
1037
909#endif /* GSSAPI */ 1038#endif /* GSSAPI */
910 1039
911int 1040int
diff --git a/sshd.c b/sshd.c
index a738c3ab6..2e453cdf8 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)
@@ -536,7 +540,7 @@ privsep_preauth_child(void)
536 540
537#ifdef GSSAPI 541#ifdef GSSAPI
538 /* Cache supported mechanism OIDs for later use */ 542 /* Cache supported mechanism OIDs for later use */
539 if (options.gss_authentication) 543 if (options.gss_authentication || options.gss_keyex)
540 ssh_gssapi_prepare_supported_oids(); 544 ssh_gssapi_prepare_supported_oids();
541#endif 545#endif
542 546
@@ -1811,10 +1815,13 @@ main(int ac, char **av)
1811 free(fp); 1815 free(fp);
1812 } 1816 }
1813 accumulate_host_timing_secret(cfg, NULL); 1817 accumulate_host_timing_secret(cfg, NULL);
1818#ifndef GSSAPI
1819 /* The GSSAPI key exchange can run without a host key */
1814 if (!sensitive_data.have_ssh2_key) { 1820 if (!sensitive_data.have_ssh2_key) {
1815 logit("sshd: no hostkeys available -- exiting."); 1821 logit("sshd: no hostkeys available -- exiting.");
1816 exit(1); 1822 exit(1);
1817 } 1823 }
1824#endif
1818 1825
1819 /* 1826 /*
1820 * Load certificates. They are stored in an array at identical 1827 * Load certificates. They are stored in an array at identical
@@ -2105,6 +2112,60 @@ main(int ac, char **av)
2105 rdomain == NULL ? "" : "\""); 2112 rdomain == NULL ? "" : "\"");
2106 free(laddr); 2113 free(laddr);
2107 2114
2115#ifdef USE_SECURITY_SESSION_API
2116 /*
2117 * Create a new security session for use by the new user login if
2118 * the current session is the root session or we are not launched
2119 * by inetd (eg: debugging mode or server mode). We do not
2120 * necessarily need to create a session if we are launched from
2121 * inetd because Panther xinetd will create a session for us.
2122 *
2123 * The only case where this logic will fail is if there is an
2124 * inetd running in a non-root session which is not creating
2125 * new sessions for us. Then all the users will end up in the
2126 * same session (bad).
2127 *
2128 * When the client exits, the session will be destroyed for us
2129 * automatically.
2130 *
2131 * We must create the session before any credentials are stored
2132 * (including AFS pags, which happens a few lines below).
2133 */
2134 {
2135 OSStatus err = 0;
2136 SecuritySessionId sid = 0;
2137 SessionAttributeBits sattrs = 0;
2138
2139 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2140 if (err)
2141 error("SessionGetInfo() failed with error %.8X",
2142 (unsigned) err);
2143 else
2144 debug("Current Session ID is %.8X / Session Attributes are %.8X",
2145 (unsigned) sid, (unsigned) sattrs);
2146
2147 if (inetd_flag && !(sattrs & sessionIsRoot))
2148 debug("Running in inetd mode in a non-root session... "
2149 "assuming inetd created the session for us.");
2150 else {
2151 debug("Creating new security session...");
2152 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2153 if (err)
2154 error("SessionCreate() failed with error %.8X",
2155 (unsigned) err);
2156
2157 err = SessionGetInfo(callerSecuritySession, &sid,
2158 &sattrs);
2159 if (err)
2160 error("SessionGetInfo() failed with error %.8X",
2161 (unsigned) err);
2162 else
2163 debug("New Session ID is %.8X / Session Attributes are %.8X",
2164 (unsigned) sid, (unsigned) sattrs);
2165 }
2166 }
2167#endif
2168
2108 /* 2169 /*
2109 * We don't want to listen forever unless the other side 2170 * We don't want to listen forever unless the other side
2110 * successfully authenticates itself. So we set up an alarm which is 2171 * successfully authenticates itself. So we set up an alarm which is
@@ -2288,6 +2349,48 @@ do_ssh2_kex(void)
2288 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2349 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2289 list_hostkey_types()); 2350 list_hostkey_types());
2290 2351
2352#ifdef GSSAPI
2353 {
2354 char *orig;
2355 char *gss = NULL;
2356 char *newstr = NULL;
2357 orig = myproposal[PROPOSAL_KEX_ALGS];
2358
2359 /*
2360 * If we don't have a host key, then there's no point advertising
2361 * the other key exchange algorithms
2362 */
2363
2364 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2365 orig = NULL;
2366
2367 if (options.gss_keyex)
2368 gss = ssh_gssapi_server_mechanisms();
2369 else
2370 gss = NULL;
2371
2372 if (gss && orig)
2373 xasprintf(&newstr, "%s,%s", gss, orig);
2374 else if (gss)
2375 newstr = gss;
2376 else if (orig)
2377 newstr = orig;
2378
2379 /*
2380 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2381 * key alg, but we can't tell people about it unless its the only
2382 * host key algorithm we support
2383 */
2384 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2385 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2386
2387 if (newstr)
2388 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2389 else
2390 fatal("No supported key exchange algorithms");
2391 }
2392#endif
2393
2291 /* start key exchange */ 2394 /* start key exchange */
2292 if ((r = kex_setup(active_state, myproposal)) != 0) 2395 if ((r = kex_setup(active_state, myproposal)) != 0)
2293 fatal("kex_setup: %s", ssh_err(r)); 2396 fatal("kex_setup: %s", ssh_err(r));
@@ -2305,6 +2408,13 @@ do_ssh2_kex(void)
2305# endif 2408# endif
2306#endif 2409#endif
2307 kex->kex[KEX_C25519_SHA256] = kexc25519_server; 2410 kex->kex[KEX_C25519_SHA256] = kexc25519_server;
2411#ifdef GSSAPI
2412 if (options.gss_keyex) {
2413 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2414 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2415 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2416 }
2417#endif
2308 kex->server = 1; 2418 kex->server = 1;
2309 kex->client_version_string=client_version_string; 2419 kex->client_version_string=client_version_string;
2310 kex->server_version_string=server_version_string; 2420 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 e1b54ba20..a0ac717c7 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -637,6 +637,11 @@ The default is
637Specifies whether user authentication based on GSSAPI is allowed. 637Specifies whether user authentication based on GSSAPI is allowed.
638The default is 638The default is
639.Cm no . 639.Cm no .
640.It Cm GSSAPIKeyExchange
641Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
642doesn't rely on ssh keys to verify host identity.
643The default is
644.Cm no .
640.It Cm GSSAPICleanupCredentials 645.It Cm GSSAPICleanupCredentials
641Specifies whether to automatically destroy the user's credentials cache 646Specifies whether to automatically destroy the user's credentials cache
642on logout. 647on logout.
@@ -656,6 +661,11 @@ machine's default store.
656This facility is provided to assist with operation on multi homed machines. 661This facility is provided to assist with operation on multi homed machines.
657The default is 662The default is
658.Cm yes . 663.Cm yes .
664.It Cm GSSAPIStoreCredentialsOnRekey
665Controls whether the user's GSSAPI credentials should be updated following a
666successful connection rekeying. This option can be used to accepted renewed
667or updated credentials from a compatible client. The default is
668.Cm no .
659.It Cm HostbasedAcceptedKeyTypes 669.It Cm HostbasedAcceptedKeyTypes
660Specifies the key types that will be accepted for hostbased authentication 670Specifies the key types that will be accepted for hostbased authentication
661as a list of comma-separated patterns. 671as a list of comma-separated patterns.
diff --git a/sshkey.c b/sshkey.c
index 72c08c7e0..91e99a262 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -140,6 +140,7 @@ static const struct keytype keytypes[] = {
140# endif /* OPENSSL_HAS_NISTP521 */ 140# endif /* OPENSSL_HAS_NISTP521 */
141# endif /* OPENSSL_HAS_ECC */ 141# endif /* OPENSSL_HAS_ECC */
142#endif /* WITH_OPENSSL */ 142#endif /* WITH_OPENSSL */
143 { "null", "null", NULL, KEY_NULL, 0, 0, 0 },
143 { NULL, NULL, NULL, -1, -1, 0, 0 } 144 { NULL, NULL, NULL, -1, -1, 0, 0 }
144}; 145};
145 146
@@ -228,7 +229,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
228 const struct keytype *kt; 229 const struct keytype *kt;
229 230
230 for (kt = keytypes; kt->type != -1; kt++) { 231 for (kt = keytypes; kt->type != -1; kt++) {
231 if (kt->name == NULL) 232 if (kt->name == NULL || kt->type == KEY_NULL)
232 continue; 233 continue;
233 if (!include_sigonly && kt->sigonly) 234 if (!include_sigonly && kt->sigonly)
234 continue; 235 continue;
diff --git a/sshkey.h b/sshkey.h
index 9060b2ecb..0cbdcfd74 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -63,6 +63,7 @@ enum sshkey_types {
63 KEY_ED25519_CERT, 63 KEY_ED25519_CERT,
64 KEY_XMSS, 64 KEY_XMSS,
65 KEY_XMSS_CERT, 65 KEY_XMSS_CERT,
66 KEY_NULL,
66 KEY_UNSPEC 67 KEY_UNSPEC
67}; 68};
68 69