summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2020-10-18 12:04:32 +0100
commitd1b7918f9bce6e997c7952ac795e18d09192b2a6 (patch)
tree897785ddb933a151a3d4b7f7d1b6542aee570b76
parent2b2c99658e3e8ed452e28f88f9cdbcdfb2a461cb (diff)
GSSAPI key exchange support
This patch has been rejected upstream: "None of the OpenSSH developers are in favour of adding this, and this situation has not changed for several years. This is not a slight on Simon's patch, which is of fine quality, but just that a) we don't trust GSSAPI implementations that much and b) we don't like adding new KEX since they are pre-auth attack surface. This one is particularly scary, since it requires hooks out to typically root-owned system resources." However, quite a lot of people rely on this in Debian, and it's better to have it merged into the main openssh package rather than having separate -krb5 packages (as we used to have). It seems to have a generally good security history. Author: Simon Wilkinson <simon@sxw.org.uk> Author: Colin Watson <cjwatson@debian.org> Author: Jakub Jelen <jjelen@redhat.com> Origin: other, https://github.com/openssh-gsskex/openssh-gsskex/commits/debian/master Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 Last-Updated: 2020-06-07 Patch-Name: gssapi.patch
-rw-r--r--Makefile.in3
-rw-r--r--README.md33
-rw-r--r--auth.c96
-rw-r--r--auth2-gss.c56
-rw-r--r--auth2.c2
-rw-r--r--canohost.c93
-rw-r--r--canohost.h3
-rw-r--r--clientloop.c15
-rw-r--r--configure.ac24
-rw-r--r--gss-genr.c300
-rw-r--r--gss-serv-krb5.c85
-rw-r--r--gss-serv.c186
-rw-r--r--kex.c66
-rw-r--r--kex.h29
-rw-r--r--kexdh.c10
-rw-r--r--kexgen.c2
-rw-r--r--kexgssc.c606
-rw-r--r--kexgsss.c474
-rw-r--r--monitor.c139
-rw-r--r--monitor.h2
-rw-r--r--monitor_wrap.c57
-rw-r--r--monitor_wrap.h4
-rw-r--r--readconf.c70
-rw-r--r--readconf.h6
-rw-r--r--servconf.c47
-rw-r--r--servconf.h3
-rw-r--r--session.c10
-rw-r--r--ssh-gss.h54
-rw-r--r--ssh.18
-rw-r--r--ssh.c6
-rw-r--r--ssh_config2
-rw-r--r--ssh_config.557
-rw-r--r--sshconnect2.c154
-rw-r--r--sshd.c62
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.530
-rw-r--r--sshkey.c3
-rw-r--r--sshkey.h1
38 files changed, 2640 insertions, 160 deletions
diff --git a/Makefile.in b/Makefile.in
index acfb919da..56759c388 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -107,6 +107,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
107 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ 107 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
108 kexgexc.o kexgexs.o \ 108 kexgexc.o kexgexs.o \
109 sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \ 109 sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \
110 kexgssc.o \
110 sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \ 111 sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \
111 sshbuf-io.o 112 sshbuf-io.o
112 113
@@ -123,7 +124,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
123 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ 124 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
124 auth2-none.o auth2-passwd.o auth2-pubkey.o \ 125 auth2-none.o auth2-passwd.o auth2-pubkey.o \
125 monitor.o monitor_wrap.o auth-krb5.o \ 126 monitor.o monitor_wrap.o auth-krb5.o \
126 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 127 auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
127 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ 128 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
128 sftp-server.o sftp-common.o \ 129 sftp-server.o sftp-common.o \
129 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ 130 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
diff --git a/README.md b/README.md
index 28fb43d2a..5b73d24c0 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,36 @@
1Portable OpenSSH with GSSAPI Key Exchange patches
2=================================================
3
4Currently, there are two branches with gssapi key exchange related
5patches:
6
7 * fedora/master: Changes that are shipped in Fedora
8 * debian/master: Changes that are shipped in Debian
9
10The target is to converge to a shared repository with single master
11branch from where we could build releases for both OSes.
12
13
14What is in:
15
16 * The original patch implementing missing parts of RFC4462 by Simon Wilkinson
17 adapted to the current OpenSSH versions and with several fixes
18 * New methods for GSSAPI Kex from IETF draft [1] from Jakub Jelen
19
20
21Missing kerberos-related parts:
22
23 * .k5login and .kusers support available in Fedora [2] [3].
24 * Improved handling of kerberos ccache location [4]
25
26
27[1] https://tools.ietf.org/html/draft-ietf-curdle-gss-keyex-sha2-08
28[2] https://src.fedoraproject.org/rpms/openssh/blob/master/f/openssh-6.6p1-kuserok.patch
29[3] https://src.fedoraproject.org/rpms/openssh/blob/master/f/openssh-6.6p1-GSSAPIEnablek5users.patch
30[4] https://bugzilla.mindrot.org/show_bug.cgi?id=2775
31
32-------------------------------------------------------------------------------
33
1# Portable OpenSSH 34# Portable OpenSSH
2 35
3[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh) 36[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh)
diff --git a/auth.c b/auth.c
index 9a5498b66..3d31ec860 100644
--- a/auth.c
+++ b/auth.c
@@ -400,7 +400,8 @@ auth_root_allowed(struct ssh *ssh, const char *method)
400 case PERMIT_NO_PASSWD: 400 case PERMIT_NO_PASSWD:
401 if (strcmp(method, "publickey") == 0 || 401 if (strcmp(method, "publickey") == 0 ||
402 strcmp(method, "hostbased") == 0 || 402 strcmp(method, "hostbased") == 0 ||
403 strcmp(method, "gssapi-with-mic") == 0) 403 strcmp(method, "gssapi-with-mic") == 0 ||
404 strcmp(method, "gssapi-keyex") == 0)
404 return 1; 405 return 1;
405 break; 406 break;
406 case PERMIT_FORCED_ONLY: 407 case PERMIT_FORCED_ONLY:
@@ -725,99 +726,6 @@ fakepw(void)
725} 726}
726 727
727/* 728/*
728 * Returns the remote DNS hostname as a string. The returned string must not
729 * be freed. NB. this will usually trigger a DNS query the first time it is
730 * called.
731 * This function does additional checks on the hostname to mitigate some
732 * attacks on legacy rhosts-style authentication.
733 * XXX is RhostsRSAAuthentication vulnerable to these?
734 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
735 */
736
737static char *
738remote_hostname(struct ssh *ssh)
739{
740 struct sockaddr_storage from;
741 socklen_t fromlen;
742 struct addrinfo hints, *ai, *aitop;
743 char name[NI_MAXHOST], ntop2[NI_MAXHOST];
744 const char *ntop = ssh_remote_ipaddr(ssh);
745
746 /* Get IP address of client. */
747 fromlen = sizeof(from);
748 memset(&from, 0, sizeof(from));
749 if (getpeername(ssh_packet_get_connection_in(ssh),
750 (struct sockaddr *)&from, &fromlen) == -1) {
751 debug("getpeername failed: %.100s", strerror(errno));
752 return xstrdup(ntop);
753 }
754
755 ipv64_normalise_mapped(&from, &fromlen);
756 if (from.ss_family == AF_INET6)
757 fromlen = sizeof(struct sockaddr_in6);
758
759 debug3("Trying to reverse map address %.100s.", ntop);
760 /* Map the IP address to a host name. */
761 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
762 NULL, 0, NI_NAMEREQD) != 0) {
763 /* Host name not found. Use ip address. */
764 return xstrdup(ntop);
765 }
766
767 /*
768 * if reverse lookup result looks like a numeric hostname,
769 * someone is trying to trick us by PTR record like following:
770 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
771 */
772 memset(&hints, 0, sizeof(hints));
773 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
774 hints.ai_flags = AI_NUMERICHOST;
775 if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
776 logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
777 name, ntop);
778 freeaddrinfo(ai);
779 return xstrdup(ntop);
780 }
781
782 /* Names are stored in lowercase. */
783 lowercase(name);
784
785 /*
786 * Map it back to an IP address and check that the given
787 * address actually is an address of this host. This is
788 * necessary because anyone with access to a name server can
789 * define arbitrary names for an IP address. Mapping from
790 * name to IP address can be trusted better (but can still be
791 * fooled if the intruder has access to the name server of
792 * the domain).
793 */
794 memset(&hints, 0, sizeof(hints));
795 hints.ai_family = from.ss_family;
796 hints.ai_socktype = SOCK_STREAM;
797 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
798 logit("reverse mapping checking getaddrinfo for %.700s "
799 "[%s] failed.", name, ntop);
800 return xstrdup(ntop);
801 }
802 /* Look for the address from the list of addresses. */
803 for (ai = aitop; ai; ai = ai->ai_next) {
804 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
805 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
806 (strcmp(ntop, ntop2) == 0))
807 break;
808 }
809 freeaddrinfo(aitop);
810 /* If we reached the end of the list, the address was not there. */
811 if (ai == NULL) {
812 /* Address not found for the host name. */
813 logit("Address %.100s maps to %.600s, but this does not "
814 "map back to the address.", ntop, name);
815 return xstrdup(ntop);
816 }
817 return xstrdup(name);
818}
819
820/*
821 * Return the canonical name of the host in the other side of the current 729 * Return the canonical name of the host in the other side of the current
822 * connection. The host name is cached, so it is efficient to call this 730 * connection. The host name is cached, so it is efficient to call this
823 * several times. 731 * several times.
diff --git a/auth2-gss.c b/auth2-gss.c
index 9351e0428..d6446c0cf 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
@@ -55,6 +55,48 @@ static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct 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/* 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 = NULL;
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
74 if ((b = sshbuf_new()) == NULL)
75 fatal("%s: sshbuf_new failed", __func__);
76
77 mic.value = p;
78 mic.length = len;
79
80 ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
81 "gssapi-keyex");
82
83 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
84 fatal("%s: sshbuf_mutable_ptr failed", __func__);
85 gssbuf.length = sshbuf_len(b);
86
87 /* gss_kex_context is NULL with privsep, so we can't check it here */
88 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
89 &gssbuf, &mic))))
90 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
91 authctxt->pw, 1));
92
93 sshbuf_free(b);
94 free(mic.value);
95
96 return (authenticated);
97}
98
99/*
58 * We only support those mechanisms that we know about (ie ones that we know 100 * We only support those mechanisms that we know about (ie ones that we know
59 * how to check local user kuserok and the like) 101 * how to check local user kuserok and the like)
60 */ 102 */
@@ -260,7 +302,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
260 if ((r = sshpkt_get_end(ssh)) != 0) 302 if ((r = sshpkt_get_end(ssh)) != 0)
261 fatal("%s: %s", __func__, ssh_err(r)); 303 fatal("%s: %s", __func__, ssh_err(r));
262 304
263 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 305 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
306 authctxt->pw, 1));
264 307
265 if ((!use_privsep || mm_is_monitor()) && 308 if ((!use_privsep || mm_is_monitor()) &&
266 (displayname = ssh_gssapi_displayname()) != NULL) 309 (displayname = ssh_gssapi_displayname()) != NULL)
@@ -306,7 +349,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
306 gssbuf.length = sshbuf_len(b); 349 gssbuf.length = sshbuf_len(b);
307 350
308 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) 351 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
309 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); 352 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
353 authctxt->pw, 0));
310 else 354 else
311 logit("GSSAPI MIC check failed"); 355 logit("GSSAPI MIC check failed");
312 356
@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
326 return 0; 370 return 0;
327} 371}
328 372
373Authmethod method_gsskeyex = {
374 "gssapi-keyex",
375 userauth_gsskeyex,
376 &options.gss_authentication
377};
378
329Authmethod method_gssapi = { 379Authmethod method_gssapi = {
330 "gssapi-with-mic", 380 "gssapi-with-mic",
331 userauth_gssapi, 381 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index 242a7adbe..9fa1404b3 100644
--- a/auth2.c
+++ b/auth2.c
@@ -73,6 +73,7 @@ extern Authmethod method_passwd;
73extern Authmethod method_kbdint; 73extern Authmethod method_kbdint;
74extern Authmethod method_hostbased; 74extern Authmethod method_hostbased;
75#ifdef GSSAPI 75#ifdef GSSAPI
76extern Authmethod method_gsskeyex;
76extern Authmethod method_gssapi; 77extern Authmethod method_gssapi;
77#endif 78#endif
78 79
@@ -80,6 +81,7 @@ Authmethod *authmethods[] = {
80 &method_none, 81 &method_none,
81 &method_pubkey, 82 &method_pubkey,
82#ifdef GSSAPI 83#ifdef GSSAPI
84 &method_gsskeyex,
83 &method_gssapi, 85 &method_gssapi,
84#endif 86#endif
85 &method_passwd, 87 &method_passwd,
diff --git a/canohost.c b/canohost.c
index abea9c6e6..8e81b5193 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) == -1) {
62 debug("getpeername failed: %.100s", strerror(errno));
63 return xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(ntop);
127 }
128 return xstrdup(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 60b46d161..2cebea29f 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
@@ -1368,9 +1372,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
1368 break; 1372 break;
1369 1373
1370 /* Do channel operations unless rekeying in progress. */ 1374 /* Do channel operations unless rekeying in progress. */
1371 if (!ssh_packet_is_rekeying(ssh)) 1375 if (!ssh_packet_is_rekeying(ssh)) {
1372 channel_after_select(ssh, readset, writeset); 1376 channel_after_select(ssh, readset, writeset);
1373 1377
1378#ifdef GSSAPI
1379 if (options.gss_renewal_rekey &&
1380 ssh_gssapi_credentials_updated(NULL)) {
1381 debug("credentials updated - forcing rekey");
1382 need_rekeying = 1;
1383 }
1384#endif
1385 }
1386
1374 /* Buffer input from the connection. */ 1387 /* Buffer input from the connection. */
1375 client_process_net_input(ssh, readset); 1388 client_process_net_input(ssh, readset);
1376 1389
diff --git a/configure.ac b/configure.ac
index 7005a503e..c8a96deb4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -679,6 +679,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
679 [Use tunnel device compatibility to OpenBSD]) 679 [Use tunnel device compatibility to OpenBSD])
680 AC_DEFINE([SSH_TUN_PREPEND_AF], [1], 680 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
681 [Prepend the address family to IP tunnel traffic]) 681 [Prepend the address family to IP tunnel traffic])
682 AC_MSG_CHECKING([if we have the Security Authorization Session API])
683 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
684 [SessionCreate(0, 0);],
685 [ac_cv_use_security_session_api="yes"
686 AC_DEFINE([USE_SECURITY_SESSION_API], [1],
687 [platform has the Security Authorization Session API])
688 LIBS="$LIBS -framework Security"
689 AC_MSG_RESULT([yes])],
690 [ac_cv_use_security_session_api="no"
691 AC_MSG_RESULT([no])])
692 AC_MSG_CHECKING([if we have an in-memory credentials cache])
693 AC_TRY_COMPILE(
694 [#include <Kerberos/Kerberos.h>],
695 [cc_context_t c;
696 (void) cc_initialize (&c, 0, NULL, NULL);],
697 [AC_DEFINE([USE_CCAPI], [1],
698 [platform uses an in-memory credentials cache])
699 LIBS="$LIBS -framework Security"
700 AC_MSG_RESULT([yes])
701 if test "x$ac_cv_use_security_session_api" = "xno"; then
702 AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
703 fi],
704 [AC_MSG_RESULT([no])]
705 )
682 m4_pattern_allow([AU_IPv]) 706 m4_pattern_allow([AU_IPv])
683 AC_CHECK_DECL([AU_IPv4], [], 707 AC_CHECK_DECL([AU_IPv4], [],
684 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) 708 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
diff --git a/gss-genr.c b/gss-genr.c
index d56257b4a..763a63ffa 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,36 @@
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 "sshkey.h"
46#include "kex.h"
47#include "digest.h"
48#include "packet.h"
44 49
45#include "ssh-gss.h" 50#include "ssh-gss.h"
46 51
47extern u_char *session_id2; 52extern u_char *session_id2;
48extern u_int session_id2_len; 53extern u_int session_id2_len;
49 54
55typedef struct {
56 char *encoded;
57 gss_OID oid;
58} ssh_gss_kex_mapping;
59
60/*
61 * XXX - It would be nice to find a more elegant way of handling the
62 * XXX passing of the key exchange context to the userauth routines
63 */
64
65Gssctxt *gss_kex_context = NULL;
66
67static ssh_gss_kex_mapping *gss_enc2oid = NULL;
68
69int
70ssh_gssapi_oid_table_ok(void) {
71 return (gss_enc2oid != NULL);
72}
73
50/* sshbuf_get for gss_buffer_desc */ 74/* sshbuf_get for gss_buffer_desc */
51int 75int
52ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) 76ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
@@ -62,6 +86,162 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
62 return 0; 86 return 0;
63} 87}
64 88
89/* sshpkt_get of gss_buffer_desc */
90int
91ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *ssh, gss_buffer_desc *g)
92{
93 int r;
94 u_char *p;
95 size_t len;
96
97 if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
98 return r;
99 g->value = p;
100 g->length = len;
101 return 0;
102}
103
104/*
105 * Return a list of the gss-group1-sha1 mechanisms supported by this program
106 *
107 * We test mechanisms to ensure that we can use them, to avoid starting
108 * a key exchange with a bad mechanism
109 */
110
111char *
112ssh_gssapi_client_mechanisms(const char *host, const char *client,
113 const char *kex) {
114 gss_OID_set gss_supported = NULL;
115 OM_uint32 min_status;
116
117 if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
118 return NULL;
119
120 return ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
121 host, client, kex);
122}
123
124char *
125ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
126 const char *host, const char *client, const char *kex) {
127 struct sshbuf *buf = NULL;
128 size_t i;
129 int r = SSH_ERR_ALLOC_FAIL;
130 int oidpos, enclen;
131 char *mechs, *encoded;
132 u_char digest[SSH_DIGEST_MAX_LENGTH];
133 char deroid[2];
134 struct ssh_digest_ctx *md = NULL;
135 char *s, *cp, *p;
136
137 if (gss_enc2oid != NULL) {
138 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
139 free(gss_enc2oid[i].encoded);
140 free(gss_enc2oid);
141 }
142
143 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
144 (gss_supported->count + 1));
145
146 if ((buf = sshbuf_new()) == NULL)
147 fatal("%s: sshbuf_new failed", __func__);
148
149 oidpos = 0;
150 s = cp = xstrdup(kex);
151 for (i = 0; i < gss_supported->count; i++) {
152 if (gss_supported->elements[i].length < 128 &&
153 (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
154
155 deroid[0] = SSH_GSS_OIDTYPE;
156 deroid[1] = gss_supported->elements[i].length;
157
158 if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
159 (r = ssh_digest_update(md, deroid, 2)) != 0 ||
160 (r = ssh_digest_update(md,
161 gss_supported->elements[i].elements,
162 gss_supported->elements[i].length)) != 0 ||
163 (r = ssh_digest_final(md, digest, sizeof(digest))) != 0)
164 fatal("%s: digest failed: %s", __func__,
165 ssh_err(r));
166 ssh_digest_free(md);
167 md = NULL;
168
169 encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
170 * 2);
171 enclen = __b64_ntop(digest,
172 ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
173 ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
174
175 cp = strncpy(s, kex, strlen(kex));
176 for ((p = strsep(&cp, ",")); p && *p != '\0';
177 (p = strsep(&cp, ","))) {
178 if (sshbuf_len(buf) != 0 &&
179 (r = sshbuf_put_u8(buf, ',')) != 0)
180 fatal("%s: sshbuf_put_u8 error: %s",
181 __func__, ssh_err(r));
182 if ((r = sshbuf_put(buf, p, strlen(p))) != 0 ||
183 (r = sshbuf_put(buf, encoded, enclen)) != 0)
184 fatal("%s: sshbuf_put error: %s",
185 __func__, ssh_err(r));
186 }
187
188 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
189 gss_enc2oid[oidpos].encoded = encoded;
190 oidpos++;
191 }
192 }
193 free(s);
194 gss_enc2oid[oidpos].oid = NULL;
195 gss_enc2oid[oidpos].encoded = NULL;
196
197 if ((mechs = sshbuf_dup_string(buf)) == NULL)
198 fatal("%s: sshbuf_dup_string failed", __func__);
199
200 sshbuf_free(buf);
201
202 if (strlen(mechs) == 0) {
203 free(mechs);
204 mechs = NULL;
205 }
206
207 return (mechs);
208}
209
210gss_OID
211ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
212 int i = 0;
213
214#define SKIP_KEX_NAME(type) \
215 case type: \
216 if (strlen(name) < sizeof(type##_ID)) \
217 return GSS_C_NO_OID; \
218 name += sizeof(type##_ID) - 1; \
219 break;
220
221 switch (kex_type) {
222 SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1)
223 SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1)
224 SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256)
225 SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512)
226 SKIP_KEX_NAME(KEX_GSS_GEX_SHA1)
227 SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256)
228 SKIP_KEX_NAME(KEX_GSS_C25519_SHA256)
229 default:
230 return GSS_C_NO_OID;
231 }
232
233#undef SKIP_KEX_NAME
234
235 while (gss_enc2oid[i].encoded != NULL &&
236 strcmp(name, gss_enc2oid[i].encoded) != 0)
237 i++;
238
239 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
240 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
241
242 return gss_enc2oid[i].oid;
243}
244
65/* Check that the OID in a data stream matches that in the context */ 245/* Check that the OID in a data stream matches that in the context */
66int 246int
67ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 247ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -218,7 +398,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
218 } 398 }
219 399
220 ctx->major = gss_init_sec_context(&ctx->minor, 400 ctx->major = gss_init_sec_context(&ctx->minor,
221 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 401 ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
222 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 402 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
223 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 403 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
224 404
@@ -248,8 +428,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
248} 428}
249 429
250OM_uint32 430OM_uint32
431ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
432{
433 gss_buffer_desc gssbuf;
434 gss_name_t gssname;
435 OM_uint32 status;
436 gss_OID_set oidset;
437
438 gssbuf.value = (void *) name;
439 gssbuf.length = strlen(gssbuf.value);
440
441 gss_create_empty_oid_set(&status, &oidset);
442 gss_add_oid_set_member(&status, ctx->oid, &oidset);
443
444 ctx->major = gss_import_name(&ctx->minor, &gssbuf,
445 GSS_C_NT_USER_NAME, &gssname);
446
447 if (!ctx->major)
448 ctx->major = gss_acquire_cred(&ctx->minor,
449 gssname, 0, oidset, GSS_C_INITIATE,
450 &ctx->client_creds, NULL, NULL);
451
452 gss_release_name(&status, &gssname);
453 gss_release_oid_set(&status, &oidset);
454
455 if (ctx->major)
456 ssh_gssapi_error(ctx);
457
458 return(ctx->major);
459}
460
461OM_uint32
251ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 462ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
252{ 463{
464 if (ctx == NULL)
465 return -1;
466
253 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 467 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
254 GSS_C_QOP_DEFAULT, buffer, hash))) 468 GSS_C_QOP_DEFAULT, buffer, hash)))
255 ssh_gssapi_error(ctx); 469 ssh_gssapi_error(ctx);
@@ -257,6 +471,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
257 return (ctx->major); 471 return (ctx->major);
258} 472}
259 473
474/* Priviledged when used by server */
475OM_uint32
476ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
477{
478 if (ctx == NULL)
479 return -1;
480
481 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
482 gssbuf, gssmic, NULL);
483
484 return (ctx->major);
485}
486
260void 487void
261ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, 488ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
262 const char *context) 489 const char *context)
@@ -273,11 +500,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
273} 500}
274 501
275int 502int
276ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 503ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
504 const char *client)
277{ 505{
278 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 506 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
279 OM_uint32 major, minor; 507 OM_uint32 major, minor;
280 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 508 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
509 Gssctxt *intctx = NULL;
510
511 if (ctx == NULL)
512 ctx = &intctx;
281 513
282 /* RFC 4462 says we MUST NOT do SPNEGO */ 514 /* RFC 4462 says we MUST NOT do SPNEGO */
283 if (oid->length == spnego_oid.length && 515 if (oid->length == spnego_oid.length &&
@@ -287,6 +519,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
287 ssh_gssapi_build_ctx(ctx); 519 ssh_gssapi_build_ctx(ctx);
288 ssh_gssapi_set_oid(*ctx, oid); 520 ssh_gssapi_set_oid(*ctx, oid);
289 major = ssh_gssapi_import_name(*ctx, host); 521 major = ssh_gssapi_import_name(*ctx, host);
522
523 if (!GSS_ERROR(major) && client)
524 major = ssh_gssapi_client_identity(*ctx, client);
525
290 if (!GSS_ERROR(major)) { 526 if (!GSS_ERROR(major)) {
291 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 527 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
292 NULL); 528 NULL);
@@ -296,10 +532,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
296 GSS_C_NO_BUFFER); 532 GSS_C_NO_BUFFER);
297 } 533 }
298 534
299 if (GSS_ERROR(major)) 535 if (GSS_ERROR(major) || intctx != NULL)
300 ssh_gssapi_delete_ctx(ctx); 536 ssh_gssapi_delete_ctx(ctx);
301 537
302 return (!GSS_ERROR(major)); 538 return (!GSS_ERROR(major));
303} 539}
304 540
541int
542ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
543 static gss_name_t saved_name = GSS_C_NO_NAME;
544 static OM_uint32 saved_lifetime = 0;
545 static gss_OID saved_mech = GSS_C_NO_OID;
546 static gss_name_t name;
547 static OM_uint32 last_call = 0;
548 OM_uint32 lifetime, now, major, minor;
549 int equal;
550
551 now = time(NULL);
552
553 if (ctxt) {
554 debug("Rekey has happened - updating saved versions");
555
556 if (saved_name != GSS_C_NO_NAME)
557 gss_release_name(&minor, &saved_name);
558
559 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
560 &saved_name, &saved_lifetime, NULL, NULL);
561
562 if (!GSS_ERROR(major)) {
563 saved_mech = ctxt->oid;
564 saved_lifetime+= now;
565 } else {
566 /* Handle the error */
567 }
568 return 0;
569 }
570
571 if (now - last_call < 10)
572 return 0;
573
574 last_call = now;
575
576 if (saved_mech == GSS_C_NO_OID)
577 return 0;
578
579 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
580 &name, &lifetime, NULL, NULL);
581 if (major == GSS_S_CREDENTIALS_EXPIRED)
582 return 0;
583 else if (GSS_ERROR(major))
584 return 0;
585
586 major = gss_compare_name(&minor, saved_name, name, &equal);
587 gss_release_name(&minor, &name);
588 if (GSS_ERROR(major))
589 return 0;
590
591 if (equal && (saved_lifetime < lifetime + now - 10))
592 return 1;
593
594 return 0;
595}
596
305#endif /* GSSAPI */ 597#endif /* GSSAPI */
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
index a151bc1e4..ef9beb67c 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 b5d4bb2d1..55f4d4bda 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.32 2020/03/13 03:17:07 djm Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.32 2020/03/13 03:17:07 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -44,17 +44,19 @@
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"
49 51
50extern ServerOptions options; 52extern ServerOptions options;
51 53
52static ssh_gssapi_client gssapi_client = 54static ssh_gssapi_client gssapi_client =
53 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 55 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL,
54 GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; 56 GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, 0, 0};
55 57
56ssh_gssapi_mech gssapi_null_mech = 58ssh_gssapi_mech gssapi_null_mech =
57 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; 59 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
58 60
59#ifdef KRB5 61#ifdef KRB5
60extern ssh_gssapi_mech gssapi_kerberos_mech; 62extern ssh_gssapi_mech gssapi_kerberos_mech;
@@ -141,6 +143,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
141} 143}
142 144
143/* Unprivileged */ 145/* Unprivileged */
146char *
147ssh_gssapi_server_mechanisms(void) {
148 if (supported_oids == NULL)
149 ssh_gssapi_prepare_supported_oids();
150 return (ssh_gssapi_kex_mechs(supported_oids,
151 &ssh_gssapi_server_check_mech, NULL, NULL,
152 options.gss_kex_algorithms));
153}
154
155/* Unprivileged */
156int
157ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
158 const char *dummy) {
159 Gssctxt *ctx = NULL;
160 int res;
161
162 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
163 ssh_gssapi_delete_ctx(&ctx);
164
165 return (res);
166}
167
168/* Unprivileged */
144void 169void
145ssh_gssapi_supported_oids(gss_OID_set *oidset) 170ssh_gssapi_supported_oids(gss_OID_set *oidset)
146{ 171{
@@ -150,7 +175,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
150 gss_OID_set supported; 175 gss_OID_set supported;
151 176
152 gss_create_empty_oid_set(&min_status, oidset); 177 gss_create_empty_oid_set(&min_status, oidset);
153 gss_indicate_mechs(&min_status, &supported); 178
179 if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
180 return;
154 181
155 while (supported_mechs[i]->name != NULL) { 182 while (supported_mechs[i]->name != NULL) {
156 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 183 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
@@ -276,8 +303,48 @@ OM_uint32
276ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 303ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
277{ 304{
278 int i = 0; 305 int i = 0;
306 int equal = 0;
307 gss_name_t new_name = GSS_C_NO_NAME;
308 gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
309
310 if (options.gss_store_rekey && client->used && ctx->client_creds) {
311 if (client->mech->oid.length != ctx->oid->length ||
312 (memcmp(client->mech->oid.elements,
313 ctx->oid->elements, ctx->oid->length) !=0)) {
314 debug("Rekeyed credentials have different mechanism");
315 return GSS_S_COMPLETE;
316 }
317
318 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
319 ctx->client_creds, ctx->oid, &new_name,
320 NULL, NULL, NULL))) {
321 ssh_gssapi_error(ctx);
322 return (ctx->major);
323 }
324
325 ctx->major = gss_compare_name(&ctx->minor, client->name,
326 new_name, &equal);
327
328 if (GSS_ERROR(ctx->major)) {
329 ssh_gssapi_error(ctx);
330 return (ctx->major);
331 }
332
333 if (!equal) {
334 debug("Rekeyed credentials have different name");
335 return GSS_S_COMPLETE;
336 }
279 337
280 gss_buffer_desc ename; 338 debug("Marking rekeyed credentials for export");
339
340 gss_release_name(&ctx->minor, &client->name);
341 gss_release_cred(&ctx->minor, &client->creds);
342 client->name = new_name;
343 client->creds = ctx->client_creds;
344 ctx->client_creds = GSS_C_NO_CREDENTIAL;
345 client->updated = 1;
346 return GSS_S_COMPLETE;
347 }
281 348
282 client->mech = NULL; 349 client->mech = NULL;
283 350
@@ -292,6 +359,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
292 if (client->mech == NULL) 359 if (client->mech == NULL)
293 return GSS_S_FAILURE; 360 return GSS_S_FAILURE;
294 361
362 if (ctx->client_creds &&
363 (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
364 ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
365 ssh_gssapi_error(ctx);
366 return (ctx->major);
367 }
368
295 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 369 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
296 &client->displayname, NULL))) { 370 &client->displayname, NULL))) {
297 ssh_gssapi_error(ctx); 371 ssh_gssapi_error(ctx);
@@ -309,6 +383,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
309 return (ctx->major); 383 return (ctx->major);
310 } 384 }
311 385
386 gss_release_buffer(&ctx->minor, &ename);
387
312 /* We can't copy this structure, so we just move the pointer to it */ 388 /* We can't copy this structure, so we just move the pointer to it */
313 client->creds = ctx->client_creds; 389 client->creds = ctx->client_creds;
314 ctx->client_creds = GSS_C_NO_CREDENTIAL; 390 ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -356,19 +432,23 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
356 432
357/* Privileged */ 433/* Privileged */
358int 434int
359ssh_gssapi_userok(char *user) 435ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
360{ 436{
361 OM_uint32 lmin; 437 OM_uint32 lmin;
362 438
439 (void) kex; /* used in privilege separation */
440
363 if (gssapi_client.exportedname.length == 0 || 441 if (gssapi_client.exportedname.length == 0 ||
364 gssapi_client.exportedname.value == NULL) { 442 gssapi_client.exportedname.value == NULL) {
365 debug("No suitable client data"); 443 debug("No suitable client data");
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#ifdef USE_PAM
487 int ret;
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 aecb9394d..751cfc710 100644
--- a/kex.c
+++ b/kex.c
@@ -57,11 +57,16 @@
57#include "misc.h" 57#include "misc.h"
58#include "dispatch.h" 58#include "dispatch.h"
59#include "monitor.h" 59#include "monitor.h"
60#include "xmalloc.h"
60 61
61#include "ssherr.h" 62#include "ssherr.h"
62#include "sshbuf.h" 63#include "sshbuf.h"
63#include "digest.h" 64#include "digest.h"
64 65
66#ifdef GSSAPI
67#include "ssh-gss.h"
68#endif
69
65/* prototype */ 70/* prototype */
66static int kex_choose_conf(struct ssh *); 71static int kex_choose_conf(struct ssh *);
67static int kex_input_newkeys(int, u_int32_t, struct ssh *); 72static int kex_input_newkeys(int, u_int32_t, struct ssh *);
@@ -115,15 +120,28 @@ static const struct kexalg kexalgs[] = {
115#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ 120#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
116 { NULL, 0, -1, -1}, 121 { NULL, 0, -1, -1},
117}; 122};
123static const struct kexalg gss_kexalgs[] = {
124#ifdef GSSAPI
125 { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
126 { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
127 { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
128 { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
129 { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
130 { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256,
131 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
132 { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
133#endif
134 { NULL, 0, -1, -1},
135};
118 136
119char * 137static char *
120kex_alg_list(char sep) 138kex_alg_list_internal(char sep, const struct kexalg *algs)
121{ 139{
122 char *ret = NULL, *tmp; 140 char *ret = NULL, *tmp;
123 size_t nlen, rlen = 0; 141 size_t nlen, rlen = 0;
124 const struct kexalg *k; 142 const struct kexalg *k;
125 143
126 for (k = kexalgs; k->name != NULL; k++) { 144 for (k = algs; k->name != NULL; k++) {
127 if (ret != NULL) 145 if (ret != NULL)
128 ret[rlen++] = sep; 146 ret[rlen++] = sep;
129 nlen = strlen(k->name); 147 nlen = strlen(k->name);
@@ -138,6 +156,18 @@ kex_alg_list(char sep)
138 return ret; 156 return ret;
139} 157}
140 158
159char *
160kex_alg_list(char sep)
161{
162 return kex_alg_list_internal(sep, kexalgs);
163}
164
165char *
166kex_gss_alg_list(char sep)
167{
168 return kex_alg_list_internal(sep, gss_kexalgs);
169}
170
141static const struct kexalg * 171static const struct kexalg *
142kex_alg_by_name(const char *name) 172kex_alg_by_name(const char *name)
143{ 173{
@@ -147,6 +177,10 @@ kex_alg_by_name(const char *name)
147 if (strcmp(k->name, name) == 0) 177 if (strcmp(k->name, name) == 0)
148 return k; 178 return k;
149 } 179 }
180 for (k = gss_kexalgs; k->name != NULL; k++) {
181 if (strncmp(k->name, name, strlen(k->name)) == 0)
182 return k;
183 }
150 return NULL; 184 return NULL;
151} 185}
152 186
@@ -315,6 +349,29 @@ kex_assemble_names(char **listp, const char *def, const char *all)
315 return r; 349 return r;
316} 350}
317 351
352/* Validate GSS KEX method name list */
353int
354kex_gss_names_valid(const char *names)
355{
356 char *s, *cp, *p;
357
358 if (names == NULL || *names == '\0')
359 return 0;
360 s = cp = xstrdup(names);
361 for ((p = strsep(&cp, ",")); p && *p != '\0';
362 (p = strsep(&cp, ","))) {
363 if (strncmp(p, "gss-", 4) != 0
364 || kex_alg_by_name(p) == NULL) {
365 error("Unsupported KEX algorithm \"%.100s\"", p);
366 free(s);
367 return 0;
368 }
369 }
370 debug3("gss kex names ok: [%s]", names);
371 free(s);
372 return 1;
373}
374
318/* put algorithm proposal into buffer */ 375/* put algorithm proposal into buffer */
319int 376int
320kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) 377kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
@@ -697,6 +754,9 @@ kex_free(struct kex *kex)
697 sshbuf_free(kex->server_version); 754 sshbuf_free(kex->server_version);
698 sshbuf_free(kex->client_pub); 755 sshbuf_free(kex->client_pub);
699 free(kex->session_id); 756 free(kex->session_id);
757#ifdef GSSAPI
758 free(kex->gss_host);
759#endif /* GSSAPI */
700 free(kex->failed_choice); 760 free(kex->failed_choice);
701 free(kex->hostkey_alg); 761 free(kex->hostkey_alg);
702 free(kex->name); 762 free(kex->name);
diff --git a/kex.h b/kex.h
index a5ae6ac05..fe7141414 100644
--- a/kex.h
+++ b/kex.h
@@ -102,6 +102,15 @@ enum kex_exchange {
102 KEX_ECDH_SHA2, 102 KEX_ECDH_SHA2,
103 KEX_C25519_SHA256, 103 KEX_C25519_SHA256,
104 KEX_KEM_SNTRUP4591761X25519_SHA512, 104 KEX_KEM_SNTRUP4591761X25519_SHA512,
105#ifdef GSSAPI
106 KEX_GSS_GRP1_SHA1,
107 KEX_GSS_GRP14_SHA1,
108 KEX_GSS_GRP14_SHA256,
109 KEX_GSS_GRP16_SHA512,
110 KEX_GSS_GEX_SHA1,
111 KEX_GSS_NISTP256_SHA256,
112 KEX_GSS_C25519_SHA256,
113#endif
105 KEX_MAX 114 KEX_MAX
106}; 115};
107 116
@@ -153,6 +162,12 @@ struct kex {
153 u_int flags; 162 u_int flags;
154 int hash_alg; 163 int hash_alg;
155 int ec_nid; 164 int ec_nid;
165#ifdef GSSAPI
166 int gss_deleg_creds;
167 int gss_trust_dns;
168 char *gss_host;
169 char *gss_client;
170#endif
156 char *failed_choice; 171 char *failed_choice;
157 int (*verify_host_key)(struct sshkey *, struct ssh *); 172 int (*verify_host_key)(struct sshkey *, struct ssh *);
158 struct sshkey *(*load_host_public_key)(int, int, struct ssh *); 173 struct sshkey *(*load_host_public_key)(int, int, struct ssh *);
@@ -174,8 +189,10 @@ struct kex {
174 189
175int kex_names_valid(const char *); 190int kex_names_valid(const char *);
176char *kex_alg_list(char); 191char *kex_alg_list(char);
192char *kex_gss_alg_list(char);
177char *kex_names_cat(const char *, const char *); 193char *kex_names_cat(const char *, const char *);
178int kex_assemble_names(char **, const char *, const char *); 194int kex_assemble_names(char **, const char *, const char *);
195int kex_gss_names_valid(const char *);
179 196
180int kex_exchange_identification(struct ssh *, int, const char *); 197int kex_exchange_identification(struct ssh *, int, const char *);
181 198
@@ -202,6 +219,12 @@ int kexgex_client(struct ssh *);
202int kexgex_server(struct ssh *); 219int kexgex_server(struct ssh *);
203int kex_gen_client(struct ssh *); 220int kex_gen_client(struct ssh *);
204int kex_gen_server(struct ssh *); 221int kex_gen_server(struct ssh *);
222#if defined(GSSAPI) && defined(WITH_OPENSSL)
223int kexgssgex_client(struct ssh *);
224int kexgssgex_server(struct ssh *);
225int kexgss_client(struct ssh *);
226int kexgss_server(struct ssh *);
227#endif
205 228
206int kex_dh_keypair(struct kex *); 229int kex_dh_keypair(struct kex *);
207int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, 230int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
@@ -234,6 +257,12 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *,
234 const BIGNUM *, const u_char *, size_t, 257 const BIGNUM *, const u_char *, size_t,
235 u_char *, size_t *); 258 u_char *, size_t *);
236 259
260int kex_gen_hash(int hash_alg, const struct sshbuf *client_version,
261 const struct sshbuf *server_version, const struct sshbuf *client_kexinit,
262 const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob,
263 const struct sshbuf *client_pub, const struct sshbuf *server_pub,
264 const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen);
265
237void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) 266void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
238 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) 267 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
239 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); 268 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
diff --git a/kexdh.c b/kexdh.c
index 6e0159f9f..d024a8b9a 100644
--- a/kexdh.c
+++ b/kexdh.c
@@ -49,13 +49,23 @@ kex_dh_keygen(struct kex *kex)
49{ 49{
50 switch (kex->kex_type) { 50 switch (kex->kex_type) {
51 case KEX_DH_GRP1_SHA1: 51 case KEX_DH_GRP1_SHA1:
52#ifdef GSSAPI
53 case KEX_GSS_GRP1_SHA1:
54#endif
52 kex->dh = dh_new_group1(); 55 kex->dh = dh_new_group1();
53 break; 56 break;
54 case KEX_DH_GRP14_SHA1: 57 case KEX_DH_GRP14_SHA1:
55 case KEX_DH_GRP14_SHA256: 58 case KEX_DH_GRP14_SHA256:
59#ifdef GSSAPI
60 case KEX_GSS_GRP14_SHA1:
61 case KEX_GSS_GRP14_SHA256:
62#endif
56 kex->dh = dh_new_group14(); 63 kex->dh = dh_new_group14();
57 break; 64 break;
58 case KEX_DH_GRP16_SHA512: 65 case KEX_DH_GRP16_SHA512:
66#ifdef GSSAPI
67 case KEX_GSS_GRP16_SHA512:
68#endif
59 kex->dh = dh_new_group16(); 69 kex->dh = dh_new_group16();
60 break; 70 break;
61 case KEX_DH_GRP18_SHA512: 71 case KEX_DH_GRP18_SHA512:
diff --git a/kexgen.c b/kexgen.c
index 69348b964..c0e8c2f44 100644
--- a/kexgen.c
+++ b/kexgen.c
@@ -44,7 +44,7 @@
44static int input_kex_gen_init(int, u_int32_t, struct ssh *); 44static int input_kex_gen_init(int, u_int32_t, struct ssh *);
45static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh); 45static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh);
46 46
47static int 47int
48kex_gen_hash( 48kex_gen_hash(
49 int hash_alg, 49 int hash_alg,
50 const struct sshbuf *client_version, 50 const struct sshbuf *client_version,
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..f6e1405eb
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,606 @@
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#if defined(GSSAPI) && defined(WITH_OPENSSL)
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#include "ssherr.h"
47
48#include "ssh-gss.h"
49
50int
51kexgss_client(struct ssh *ssh)
52{
53 struct kex *kex = ssh->kex;
54 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
55 recv_tok = GSS_C_EMPTY_BUFFER,
56 gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
57 Gssctxt *ctxt;
58 OM_uint32 maj_status, min_status, ret_flags;
59 struct sshbuf *server_blob = NULL;
60 struct sshbuf *shared_secret = NULL;
61 struct sshbuf *server_host_key_blob = NULL;
62 struct sshbuf *empty = NULL;
63 u_char *msg;
64 int type = 0;
65 int first = 1;
66 u_char hash[SSH_DIGEST_MAX_LENGTH];
67 size_t hashlen;
68 u_char c;
69 int r;
70
71 /* Initialise our GSSAPI world */
72 ssh_gssapi_build_ctx(&ctxt);
73 if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
74 == GSS_C_NO_OID)
75 fatal("Couldn't identify host exchange");
76
77 if (ssh_gssapi_import_name(ctxt, kex->gss_host))
78 fatal("Couldn't import hostname");
79
80 if (kex->gss_client &&
81 ssh_gssapi_client_identity(ctxt, kex->gss_client))
82 fatal("Couldn't acquire client credentials");
83
84 /* Step 1 */
85 switch (kex->kex_type) {
86 case KEX_GSS_GRP1_SHA1:
87 case KEX_GSS_GRP14_SHA1:
88 case KEX_GSS_GRP14_SHA256:
89 case KEX_GSS_GRP16_SHA512:
90 r = kex_dh_keypair(kex);
91 break;
92 case KEX_GSS_NISTP256_SHA256:
93 r = kex_ecdh_keypair(kex);
94 break;
95 case KEX_GSS_C25519_SHA256:
96 r = kex_c25519_keypair(kex);
97 break;
98 default:
99 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
100 }
101 if (r != 0)
102 return r;
103
104 token_ptr = GSS_C_NO_BUFFER;
105
106 do {
107 debug("Calling gss_init_sec_context");
108
109 maj_status = ssh_gssapi_init_ctx(ctxt,
110 kex->gss_deleg_creds, token_ptr, &send_tok,
111 &ret_flags);
112
113 if (GSS_ERROR(maj_status)) {
114 /* XXX Useles code: Missing send? */
115 if (send_tok.length != 0) {
116 if ((r = sshpkt_start(ssh,
117 SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
118 (r = sshpkt_put_string(ssh, send_tok.value,
119 send_tok.length)) != 0)
120 fatal("sshpkt failed: %s", ssh_err(r));
121 }
122 fatal("gss_init_context failed");
123 }
124
125 /* If we've got an old receive buffer get rid of it */
126 if (token_ptr != GSS_C_NO_BUFFER)
127 gss_release_buffer(&min_status, &recv_tok);
128
129 if (maj_status == GSS_S_COMPLETE) {
130 /* If mutual state flag is not true, kex fails */
131 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
132 fatal("Mutual authentication failed");
133
134 /* If integ avail flag is not true kex fails */
135 if (!(ret_flags & GSS_C_INTEG_FLAG))
136 fatal("Integrity check failed");
137 }
138
139 /*
140 * If we have data to send, then the last message that we
141 * received cannot have been a 'complete'.
142 */
143 if (send_tok.length != 0) {
144 if (first) {
145 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
146 (r = sshpkt_put_string(ssh, send_tok.value,
147 send_tok.length)) != 0 ||
148 (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
149 fatal("failed to construct packet: %s", ssh_err(r));
150 first = 0;
151 } else {
152 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
153 (r = sshpkt_put_string(ssh, send_tok.value,
154 send_tok.length)) != 0)
155 fatal("failed to construct packet: %s", ssh_err(r));
156 }
157 if ((r = sshpkt_send(ssh)) != 0)
158 fatal("failed to send packet: %s", ssh_err(r));
159 gss_release_buffer(&min_status, &send_tok);
160
161 /* If we've sent them data, they should reply */
162 do {
163 type = ssh_packet_read(ssh);
164 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
165 debug("Received KEXGSS_HOSTKEY");
166 if (server_host_key_blob)
167 fatal("Server host key received more than once");
168 if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
169 fatal("Failed to read server host key: %s", ssh_err(r));
170 }
171 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
172
173 switch (type) {
174 case SSH2_MSG_KEXGSS_CONTINUE:
175 debug("Received GSSAPI_CONTINUE");
176 if (maj_status == GSS_S_COMPLETE)
177 fatal("GSSAPI Continue received from server when complete");
178 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
179 &recv_tok)) != 0 ||
180 (r = sshpkt_get_end(ssh)) != 0)
181 fatal("Failed to read token: %s", ssh_err(r));
182 break;
183 case SSH2_MSG_KEXGSS_COMPLETE:
184 debug("Received GSSAPI_COMPLETE");
185 if (msg_tok.value != NULL)
186 fatal("Received GSSAPI_COMPLETE twice?");
187 if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
188 (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
189 &msg_tok)) != 0)
190 fatal("Failed to read message: %s", ssh_err(r));
191
192 /* Is there a token included? */
193 if ((r = sshpkt_get_u8(ssh, &c)) != 0)
194 fatal("sshpkt failed: %s", ssh_err(r));
195 if (c) {
196 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
197 ssh, &recv_tok)) != 0)
198 fatal("Failed to read token: %s", ssh_err(r));
199 /* If we're already complete - protocol error */
200 if (maj_status == GSS_S_COMPLETE)
201 sshpkt_disconnect(ssh, "Protocol error: received token when complete");
202 } else {
203 /* No token included */
204 if (maj_status != GSS_S_COMPLETE)
205 sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
206 }
207 if ((r = sshpkt_get_end(ssh)) != 0) {
208 fatal("Expecting end of packet.");
209 }
210 break;
211 case SSH2_MSG_KEXGSS_ERROR:
212 debug("Received Error");
213 if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
214 (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
215 (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
216 (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
217 (r = sshpkt_get_end(ssh)) != 0)
218 fatal("sshpkt_get failed: %s", ssh_err(r));
219 fatal("GSSAPI Error: \n%.400s", msg);
220 default:
221 sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
222 type);
223 }
224 token_ptr = &recv_tok;
225 } else {
226 /* No data, and not complete */
227 if (maj_status != GSS_S_COMPLETE)
228 fatal("Not complete, and no token output");
229 }
230 } while (maj_status & GSS_S_CONTINUE_NEEDED);
231
232 /*
233 * We _must_ have received a COMPLETE message in reply from the
234 * server, which will have set server_blob and msg_tok
235 */
236
237 if (type != SSH2_MSG_KEXGSS_COMPLETE)
238 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
239
240 /* compute shared secret */
241 switch (kex->kex_type) {
242 case KEX_GSS_GRP1_SHA1:
243 case KEX_GSS_GRP14_SHA1:
244 case KEX_GSS_GRP14_SHA256:
245 case KEX_GSS_GRP16_SHA512:
246 r = kex_dh_dec(kex, server_blob, &shared_secret);
247 break;
248 case KEX_GSS_C25519_SHA256:
249 if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80)
250 fatal("The received key has MSB of last octet set!");
251 r = kex_c25519_dec(kex, server_blob, &shared_secret);
252 break;
253 case KEX_GSS_NISTP256_SHA256:
254 if (sshbuf_len(server_blob) != 65)
255 fatal("The received NIST-P256 key did not match"
256 "expected length (expected 65, got %zu)", sshbuf_len(server_blob));
257
258 if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
259 fatal("The received NIST-P256 key does not have first octet 0x04");
260
261 r = kex_ecdh_dec(kex, server_blob, &shared_secret);
262 break;
263 default:
264 r = SSH_ERR_INVALID_ARGUMENT;
265 break;
266 }
267 if (r != 0)
268 goto out;
269
270 if ((empty = sshbuf_new()) == NULL) {
271 r = SSH_ERR_ALLOC_FAIL;
272 goto out;
273 }
274
275 hashlen = sizeof(hash);
276 if ((r = kex_gen_hash(
277 kex->hash_alg,
278 kex->client_version,
279 kex->server_version,
280 kex->my,
281 kex->peer,
282 (server_host_key_blob ? server_host_key_blob : empty),
283 kex->client_pub,
284 server_blob,
285 shared_secret,
286 hash, &hashlen)) != 0)
287 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
288
289 gssbuf.value = hash;
290 gssbuf.length = hashlen;
291
292 /* Verify that the hash matches the MIC we just got. */
293 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
294 sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
295
296 gss_release_buffer(&min_status, &msg_tok);
297
298 if (kex->gss_deleg_creds)
299 ssh_gssapi_credentials_updated(ctxt);
300
301 if (gss_kex_context == NULL)
302 gss_kex_context = ctxt;
303 else
304 ssh_gssapi_delete_ctx(&ctxt);
305
306 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
307 r = kex_send_newkeys(ssh);
308
309out:
310 explicit_bzero(hash, sizeof(hash));
311 explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
312 sshbuf_free(empty);
313 sshbuf_free(server_host_key_blob);
314 sshbuf_free(server_blob);
315 sshbuf_free(shared_secret);
316 sshbuf_free(kex->client_pub);
317 kex->client_pub = NULL;
318 return r;
319}
320
321int
322kexgssgex_client(struct ssh *ssh)
323{
324 struct kex *kex = ssh->kex;
325 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
326 recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
327 msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
328 Gssctxt *ctxt;
329 OM_uint32 maj_status, min_status, ret_flags;
330 struct sshbuf *shared_secret = NULL;
331 BIGNUM *p = NULL;
332 BIGNUM *g = NULL;
333 struct sshbuf *buf = NULL;
334 struct sshbuf *server_host_key_blob = NULL;
335 struct sshbuf *server_blob = NULL;
336 BIGNUM *dh_server_pub = NULL;
337 u_char *msg;
338 int type = 0;
339 int first = 1;
340 u_char hash[SSH_DIGEST_MAX_LENGTH];
341 size_t hashlen;
342 const BIGNUM *pub_key, *dh_p, *dh_g;
343 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
344 struct sshbuf *empty = NULL;
345 u_char c;
346 int r;
347
348 /* Initialise our GSSAPI world */
349 ssh_gssapi_build_ctx(&ctxt);
350 if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
351 == GSS_C_NO_OID)
352 fatal("Couldn't identify host exchange");
353
354 if (ssh_gssapi_import_name(ctxt, kex->gss_host))
355 fatal("Couldn't import hostname");
356
357 if (kex->gss_client &&
358 ssh_gssapi_client_identity(ctxt, kex->gss_client))
359 fatal("Couldn't acquire client credentials");
360
361 debug("Doing group exchange");
362 nbits = dh_estimate(kex->dh_need * 8);
363
364 kex->min = DH_GRP_MIN;
365 kex->max = DH_GRP_MAX;
366 kex->nbits = nbits;
367 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
368 (r = sshpkt_put_u32(ssh, min)) != 0 ||
369 (r = sshpkt_put_u32(ssh, nbits)) != 0 ||
370 (r = sshpkt_put_u32(ssh, max)) != 0 ||
371 (r = sshpkt_send(ssh)) != 0)
372 fatal("Failed to construct a packet: %s", ssh_err(r));
373
374 if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0)
375 fatal("Error: %s", ssh_err(r));
376
377 if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
378 (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
379 (r = sshpkt_get_end(ssh)) != 0)
380 fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
381
382 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
383 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
384 min, BN_num_bits(p), max);
385
386 if ((kex->dh = dh_new_group(g, p)) == NULL)
387 fatal("dn_new_group() failed");
388 p = g = NULL; /* belong to kex->dh now */
389
390 if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
391 goto out;
392 DH_get0_key(kex->dh, &pub_key, NULL);
393
394 token_ptr = GSS_C_NO_BUFFER;
395
396 do {
397 /* Step 2 - call GSS_Init_sec_context() */
398 debug("Calling gss_init_sec_context");
399
400 maj_status = ssh_gssapi_init_ctx(ctxt,
401 kex->gss_deleg_creds, token_ptr, &send_tok,
402 &ret_flags);
403
404 if (GSS_ERROR(maj_status)) {
405 /* XXX Useles code: Missing send? */
406 if (send_tok.length != 0) {
407 if ((r = sshpkt_start(ssh,
408 SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
409 (r = sshpkt_put_string(ssh, send_tok.value,
410 send_tok.length)) != 0)
411 fatal("sshpkt failed: %s", ssh_err(r));
412 }
413 fatal("gss_init_context failed");
414 }
415
416 /* If we've got an old receive buffer get rid of it */
417 if (token_ptr != GSS_C_NO_BUFFER)
418 gss_release_buffer(&min_status, &recv_tok);
419
420 if (maj_status == GSS_S_COMPLETE) {
421 /* If mutual state flag is not true, kex fails */
422 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
423 fatal("Mutual authentication failed");
424
425 /* If integ avail flag is not true kex fails */
426 if (!(ret_flags & GSS_C_INTEG_FLAG))
427 fatal("Integrity check failed");
428 }
429
430 /*
431 * If we have data to send, then the last message that we
432 * received cannot have been a 'complete'.
433 */
434 if (send_tok.length != 0) {
435 if (first) {
436 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
437 (r = sshpkt_put_string(ssh, send_tok.value,
438 send_tok.length)) != 0 ||
439 (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
440 fatal("sshpkt failed: %s", ssh_err(r));
441 first = 0;
442 } else {
443 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
444 (r = sshpkt_put_string(ssh,send_tok.value,
445 send_tok.length)) != 0)
446 fatal("sshpkt failed: %s", ssh_err(r));
447 }
448 if ((r = sshpkt_send(ssh)) != 0)
449 fatal("sshpkt_send failed: %s", ssh_err(r));
450 gss_release_buffer(&min_status, &send_tok);
451
452 /* If we've sent them data, they should reply */
453 do {
454 type = ssh_packet_read(ssh);
455 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
456 debug("Received KEXGSS_HOSTKEY");
457 if (server_host_key_blob)
458 fatal("Server host key received more than once");
459 if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
460 fatal("sshpkt failed: %s", ssh_err(r));
461 }
462 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
463
464 switch (type) {
465 case SSH2_MSG_KEXGSS_CONTINUE:
466 debug("Received GSSAPI_CONTINUE");
467 if (maj_status == GSS_S_COMPLETE)
468 fatal("GSSAPI Continue received from server when complete");
469 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
470 &recv_tok)) != 0 ||
471 (r = sshpkt_get_end(ssh)) != 0)
472 fatal("sshpkt failed: %s", ssh_err(r));
473 break;
474 case SSH2_MSG_KEXGSS_COMPLETE:
475 debug("Received GSSAPI_COMPLETE");
476 if (msg_tok.value != NULL)
477 fatal("Received GSSAPI_COMPLETE twice?");
478 if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
479 (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
480 &msg_tok)) != 0)
481 fatal("sshpkt failed: %s", ssh_err(r));
482
483 /* Is there a token included? */
484 if ((r = sshpkt_get_u8(ssh, &c)) != 0)
485 fatal("sshpkt failed: %s", ssh_err(r));
486 if (c) {
487 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
488 ssh, &recv_tok)) != 0 ||
489 (r = sshpkt_get_end(ssh)) != 0)
490 fatal("sshpkt failed: %s", ssh_err(r));
491 /* If we're already complete - protocol error */
492 if (maj_status == GSS_S_COMPLETE)
493 sshpkt_disconnect(ssh, "Protocol error: received token when complete");
494 } else {
495 /* No token included */
496 if (maj_status != GSS_S_COMPLETE)
497 sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
498 }
499 break;
500 case SSH2_MSG_KEXGSS_ERROR:
501 debug("Received Error");
502 if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
503 (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
504 (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
505 (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
506 (r = sshpkt_get_end(ssh)) != 0)
507 fatal("sshpkt failed: %s", ssh_err(r));
508 fatal("GSSAPI Error: \n%.400s", msg);
509 default:
510 sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
511 type);
512 }
513 token_ptr = &recv_tok;
514 } else {
515 /* No data, and not complete */
516 if (maj_status != GSS_S_COMPLETE)
517 fatal("Not complete, and no token output");
518 }
519 } while (maj_status & GSS_S_CONTINUE_NEEDED);
520
521 /*
522 * We _must_ have received a COMPLETE message in reply from the
523 * server, which will have set dh_server_pub and msg_tok
524 */
525
526 if (type != SSH2_MSG_KEXGSS_COMPLETE)
527 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
528
529 /* 7. C verifies that the key Q_S is valid */
530 /* 8. C computes shared secret */
531 if ((buf = sshbuf_new()) == NULL ||
532 (r = sshbuf_put_stringb(buf, server_blob)) != 0 ||
533 (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0)
534 goto out;
535 sshbuf_free(buf);
536 buf = NULL;
537
538 if ((shared_secret = sshbuf_new()) == NULL) {
539 r = SSH_ERR_ALLOC_FAIL;
540 goto out;
541 }
542
543 if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
544 goto out;
545 if ((empty = sshbuf_new()) == NULL) {
546 r = SSH_ERR_ALLOC_FAIL;
547 goto out;
548 }
549
550 DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
551 hashlen = sizeof(hash);
552 if ((r = kexgex_hash(
553 kex->hash_alg,
554 kex->client_version,
555 kex->server_version,
556 kex->my,
557 kex->peer,
558 (server_host_key_blob ? server_host_key_blob : empty),
559 kex->min, kex->nbits, kex->max,
560 dh_p, dh_g,
561 pub_key,
562 dh_server_pub,
563 sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
564 hash, &hashlen)) != 0)
565 fatal("Failed to calculate hash: %s", ssh_err(r));
566
567 gssbuf.value = hash;
568 gssbuf.length = hashlen;
569
570 /* Verify that the hash matches the MIC we just got. */
571 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
572 sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
573
574 gss_release_buffer(&min_status, &msg_tok);
575
576 /* save session id */
577 if (kex->session_id == NULL) {
578 kex->session_id_len = hashlen;
579 kex->session_id = xmalloc(kex->session_id_len);
580 memcpy(kex->session_id, hash, kex->session_id_len);
581 }
582
583 if (kex->gss_deleg_creds)
584 ssh_gssapi_credentials_updated(ctxt);
585
586 if (gss_kex_context == NULL)
587 gss_kex_context = ctxt;
588 else
589 ssh_gssapi_delete_ctx(&ctxt);
590
591 /* Finally derive the keys and send them */
592 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
593 r = kex_send_newkeys(ssh);
594out:
595 sshbuf_free(buf);
596 sshbuf_free(server_blob);
597 sshbuf_free(empty);
598 explicit_bzero(hash, sizeof(hash));
599 DH_free(kex->dh);
600 kex->dh = NULL;
601 BN_clear_free(dh_server_pub);
602 sshbuf_free(shared_secret);
603 sshbuf_free(server_host_key_blob);
604 return r;
605}
606#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..60bc02deb
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,474 @@
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#if defined(GSSAPI) && defined(WITH_OPENSSL)
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" /* servconf.h needs misc.h for struct ForwardOptions */
46#include "servconf.h"
47#include "ssh-gss.h"
48#include "digest.h"
49#include "ssherr.h"
50
51extern ServerOptions options;
52
53int
54kexgss_server(struct ssh *ssh)
55{
56 struct kex *kex = ssh->kex;
57 OM_uint32 maj_status, min_status;
58
59 /*
60 * Some GSSAPI implementations use the input value of ret_flags (an
61 * output variable) as a means of triggering mechanism specific
62 * features. Initializing it to zero avoids inadvertently
63 * activating this non-standard behaviour.
64 */
65
66 OM_uint32 ret_flags = 0;
67 gss_buffer_desc gssbuf, recv_tok, msg_tok;
68 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
69 Gssctxt *ctxt = NULL;
70 struct sshbuf *shared_secret = NULL;
71 struct sshbuf *client_pubkey = NULL;
72 struct sshbuf *server_pubkey = NULL;
73 struct sshbuf *empty = sshbuf_new();
74 int type = 0;
75 gss_OID oid;
76 char *mechs;
77 u_char hash[SSH_DIGEST_MAX_LENGTH];
78 size_t hashlen;
79 int r;
80
81 /* Initialise GSSAPI */
82
83 /* If we're rekeying, privsep means that some of the private structures
84 * in the GSSAPI code are no longer available. This kludges them back
85 * into life
86 */
87 if (!ssh_gssapi_oid_table_ok()) {
88 mechs = ssh_gssapi_server_mechanisms();
89 free(mechs);
90 }
91
92 debug2("%s: Identifying %s", __func__, kex->name);
93 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
94 if (oid == GSS_C_NO_OID)
95 fatal("Unknown gssapi mechanism");
96
97 debug2("%s: Acquiring credentials", __func__);
98
99 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
100 fatal("Unable to acquire credentials for the server");
101
102 do {
103 debug("Wait SSH2_MSG_KEXGSS_INIT");
104 type = ssh_packet_read(ssh);
105 switch(type) {
106 case SSH2_MSG_KEXGSS_INIT:
107 if (client_pubkey != NULL)
108 fatal("Received KEXGSS_INIT after initialising");
109 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
110 &recv_tok)) != 0 ||
111 (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
112 (r = sshpkt_get_end(ssh)) != 0)
113 fatal("sshpkt failed: %s", ssh_err(r));
114
115 switch (kex->kex_type) {
116 case KEX_GSS_GRP1_SHA1:
117 case KEX_GSS_GRP14_SHA1:
118 case KEX_GSS_GRP14_SHA256:
119 case KEX_GSS_GRP16_SHA512:
120 r = kex_dh_enc(kex, client_pubkey, &server_pubkey,
121 &shared_secret);
122 break;
123 case KEX_GSS_NISTP256_SHA256:
124 r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
125 &shared_secret);
126 break;
127 case KEX_GSS_C25519_SHA256:
128 r = kex_c25519_enc(kex, client_pubkey, &server_pubkey,
129 &shared_secret);
130 break;
131 default:
132 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
133 }
134 if (r != 0)
135 goto out;
136
137 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
138 break;
139 case SSH2_MSG_KEXGSS_CONTINUE:
140 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
141 &recv_tok)) != 0 ||
142 (r = sshpkt_get_end(ssh)) != 0)
143 fatal("sshpkt failed: %s", ssh_err(r));
144 break;
145 default:
146 sshpkt_disconnect(ssh,
147 "Protocol error: didn't expect packet type %d",
148 type);
149 }
150
151 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
152 &send_tok, &ret_flags));
153
154 gss_release_buffer(&min_status, &recv_tok);
155
156 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
157 fatal("Zero length token output when incomplete");
158
159 if (client_pubkey == NULL)
160 fatal("No client public key");
161
162 if (maj_status & GSS_S_CONTINUE_NEEDED) {
163 debug("Sending GSSAPI_CONTINUE");
164 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
165 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
166 (r = sshpkt_send(ssh)) != 0)
167 fatal("sshpkt failed: %s", ssh_err(r));
168 gss_release_buffer(&min_status, &send_tok);
169 }
170 } while (maj_status & GSS_S_CONTINUE_NEEDED);
171
172 if (GSS_ERROR(maj_status)) {
173 if (send_tok.length > 0) {
174 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
175 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
176 (r = sshpkt_send(ssh)) != 0)
177 fatal("sshpkt failed: %s", ssh_err(r));
178 }
179 fatal("accept_ctx died");
180 }
181
182 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
183 fatal("Mutual Authentication flag wasn't set");
184
185 if (!(ret_flags & GSS_C_INTEG_FLAG))
186 fatal("Integrity flag wasn't set");
187
188 hashlen = sizeof(hash);
189 if ((r = kex_gen_hash(
190 kex->hash_alg,
191 kex->client_version,
192 kex->server_version,
193 kex->peer,
194 kex->my,
195 empty,
196 client_pubkey,
197 server_pubkey,
198 shared_secret,
199 hash, &hashlen)) != 0)
200 goto out;
201
202 gssbuf.value = hash;
203 gssbuf.length = hashlen;
204
205 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
206 fatal("Couldn't get MIC");
207
208 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
209 (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 ||
210 (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
211 fatal("sshpkt failed: %s", ssh_err(r));
212
213 if (send_tok.length != 0) {
214 if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
215 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
216 fatal("sshpkt failed: %s", ssh_err(r));
217 } else {
218 if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
219 fatal("sshpkt failed: %s", ssh_err(r));
220 }
221 if ((r = sshpkt_send(ssh)) != 0)
222 fatal("sshpkt_send failed: %s", ssh_err(r));
223
224 gss_release_buffer(&min_status, &send_tok);
225 gss_release_buffer(&min_status, &msg_tok);
226
227 if (gss_kex_context == NULL)
228 gss_kex_context = ctxt;
229 else
230 ssh_gssapi_delete_ctx(&ctxt);
231
232 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
233 r = kex_send_newkeys(ssh);
234
235 /* If this was a rekey, then save out any delegated credentials we
236 * just exchanged. */
237 if (options.gss_store_rekey)
238 ssh_gssapi_rekey_creds();
239out:
240 sshbuf_free(empty);
241 explicit_bzero(hash, sizeof(hash));
242 sshbuf_free(shared_secret);
243 sshbuf_free(client_pubkey);
244 sshbuf_free(server_pubkey);
245 return r;
246}
247
248int
249kexgssgex_server(struct ssh *ssh)
250{
251 struct kex *kex = ssh->kex;
252 OM_uint32 maj_status, min_status;
253
254 /*
255 * Some GSSAPI implementations use the input value of ret_flags (an
256 * output variable) as a means of triggering mechanism specific
257 * features. Initializing it to zero avoids inadvertently
258 * activating this non-standard behaviour.
259 */
260
261 OM_uint32 ret_flags = 0;
262 gss_buffer_desc gssbuf, recv_tok, msg_tok;
263 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
264 Gssctxt *ctxt = NULL;
265 struct sshbuf *shared_secret = NULL;
266 int type = 0;
267 gss_OID oid;
268 char *mechs;
269 u_char hash[SSH_DIGEST_MAX_LENGTH];
270 size_t hashlen;
271 BIGNUM *dh_client_pub = NULL;
272 const BIGNUM *pub_key, *dh_p, *dh_g;
273 int min = -1, max = -1, nbits = -1;
274 int cmin = -1, cmax = -1; /* client proposal */
275 struct sshbuf *empty = sshbuf_new();
276 int r;
277
278 /* Initialise GSSAPI */
279
280 /* If we're rekeying, privsep means that some of the private structures
281 * in the GSSAPI code are no longer available. This kludges them back
282 * into life
283 */
284 if (!ssh_gssapi_oid_table_ok())
285 if ((mechs = ssh_gssapi_server_mechanisms()))
286 free(mechs);
287
288 debug2("%s: Identifying %s", __func__, kex->name);
289 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
290 if (oid == GSS_C_NO_OID)
291 fatal("Unknown gssapi mechanism");
292
293 debug2("%s: Acquiring credentials", __func__);
294
295 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
296 fatal("Unable to acquire credentials for the server");
297
298 /* 5. S generates an ephemeral key pair (do the allocations early) */
299 debug("Doing group exchange");
300 ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ);
301 /* store client proposal to provide valid signature */
302 if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
303 (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
304 (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
305 (r = sshpkt_get_end(ssh)) != 0)
306 fatal("sshpkt failed: %s", ssh_err(r));
307 kex->nbits = nbits;
308 kex->min = cmin;
309 kex->max = cmax;
310 min = MAX(DH_GRP_MIN, cmin);
311 max = MIN(DH_GRP_MAX, cmax);
312 nbits = MAXIMUM(DH_GRP_MIN, nbits);
313 nbits = MINIMUM(DH_GRP_MAX, nbits);
314 if (max < min || nbits < min || max < nbits)
315 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
316 min, nbits, max);
317 kex->dh = PRIVSEP(choose_dh(min, nbits, max));
318 if (kex->dh == NULL) {
319 sshpkt_disconnect(ssh, "Protocol error: no matching group found");
320 fatal("Protocol error: no matching group found");
321 }
322
323 DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
324 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
325 (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
326 (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
327 (r = sshpkt_send(ssh)) != 0)
328 fatal("sshpkt failed: %s", ssh_err(r));
329
330 if ((r = ssh_packet_write_wait(ssh)) != 0)
331 fatal("ssh_packet_write_wait: %s", ssh_err(r));
332
333 /* Compute our exchange value in parallel with the client */
334 if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
335 goto out;
336
337 do {
338 debug("Wait SSH2_MSG_GSSAPI_INIT");
339 type = ssh_packet_read(ssh);
340 switch(type) {
341 case SSH2_MSG_KEXGSS_INIT:
342 if (dh_client_pub != NULL)
343 fatal("Received KEXGSS_INIT after initialising");
344 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
345 &recv_tok)) != 0 ||
346 (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 ||
347 (r = sshpkt_get_end(ssh)) != 0)
348 fatal("sshpkt failed: %s", ssh_err(r));
349
350 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
351 break;
352 case SSH2_MSG_KEXGSS_CONTINUE:
353 if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
354 &recv_tok)) != 0 ||
355 (r = sshpkt_get_end(ssh)) != 0)
356 fatal("sshpkt failed: %s", ssh_err(r));
357 break;
358 default:
359 sshpkt_disconnect(ssh,
360 "Protocol error: didn't expect packet type %d",
361 type);
362 }
363
364 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
365 &send_tok, &ret_flags));
366
367 gss_release_buffer(&min_status, &recv_tok);
368
369 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
370 fatal("Zero length token output when incomplete");
371
372 if (dh_client_pub == NULL)
373 fatal("No client public key");
374
375 if (maj_status & GSS_S_CONTINUE_NEEDED) {
376 debug("Sending GSSAPI_CONTINUE");
377 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
378 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
379 (r = sshpkt_send(ssh)) != 0)
380 fatal("sshpkt failed: %s", ssh_err(r));
381 gss_release_buffer(&min_status, &send_tok);
382 }
383 } while (maj_status & GSS_S_CONTINUE_NEEDED);
384
385 if (GSS_ERROR(maj_status)) {
386 if (send_tok.length > 0) {
387 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
388 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
389 (r = sshpkt_send(ssh)) != 0)
390 fatal("sshpkt failed: %s", ssh_err(r));
391 }
392 fatal("accept_ctx died");
393 }
394
395 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
396 fatal("Mutual Authentication flag wasn't set");
397
398 if (!(ret_flags & GSS_C_INTEG_FLAG))
399 fatal("Integrity flag wasn't set");
400
401 /* calculate shared secret */
402 if ((shared_secret = sshbuf_new()) == NULL) {
403 r = SSH_ERR_ALLOC_FAIL;
404 goto out;
405 }
406 if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0)
407 goto out;
408
409 DH_get0_key(kex->dh, &pub_key, NULL);
410 DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
411 hashlen = sizeof(hash);
412 if ((r = kexgex_hash(
413 kex->hash_alg,
414 kex->client_version,
415 kex->server_version,
416 kex->peer,
417 kex->my,
418 empty,
419 cmin, nbits, cmax,
420 dh_p, dh_g,
421 dh_client_pub,
422 pub_key,
423 sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
424 hash, &hashlen)) != 0)
425 fatal("kexgex_hash failed: %s", ssh_err(r));
426
427 gssbuf.value = hash;
428 gssbuf.length = hashlen;
429
430 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
431 fatal("Couldn't get MIC");
432
433 if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
434 (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
435 (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
436 fatal("sshpkt failed: %s", ssh_err(r));
437
438 if (send_tok.length != 0) {
439 if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
440 (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
441 fatal("sshpkt failed: %s", ssh_err(r));
442 } else {
443 if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
444 fatal("sshpkt failed: %s", ssh_err(r));
445 }
446 if ((r = sshpkt_send(ssh)) != 0)
447 fatal("sshpkt failed: %s", ssh_err(r));
448
449 gss_release_buffer(&min_status, &send_tok);
450 gss_release_buffer(&min_status, &msg_tok);
451
452 if (gss_kex_context == NULL)
453 gss_kex_context = ctxt;
454 else
455 ssh_gssapi_delete_ctx(&ctxt);
456
457 /* Finally derive the keys and send them */
458 if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
459 r = kex_send_newkeys(ssh);
460
461 /* If this was a rekey, then save out any delegated credentials we
462 * just exchanged. */
463 if (options.gss_store_rekey)
464 ssh_gssapi_rekey_creds();
465out:
466 sshbuf_free(empty);
467 explicit_bzero(hash, sizeof(hash));
468 DH_free(kex->dh);
469 kex->dh = NULL;
470 BN_clear_free(dh_client_pub);
471 sshbuf_free(shared_secret);
472 return r;
473}
474#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
diff --git a/monitor.c b/monitor.c
index 4cf79dfc9..11868952b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -148,6 +148,8 @@ int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *);
148int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *); 148int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *);
149int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *); 149int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *);
150int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *); 150int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *);
151int mm_answer_gss_sign(struct ssh *, int, struct sshbuf *);
152int mm_answer_gss_updatecreds(struct ssh *, int, struct sshbuf *);
151#endif 153#endif
152 154
153#ifdef SSH_AUDIT_EVENTS 155#ifdef SSH_AUDIT_EVENTS
@@ -220,11 +222,18 @@ struct mon_table mon_dispatch_proto20[] = {
220 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, 222 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
221 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, 223 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
222 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, 224 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
225 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
223#endif 226#endif
224 {0, 0, NULL} 227 {0, 0, NULL}
225}; 228};
226 229
227struct mon_table mon_dispatch_postauth20[] = { 230struct mon_table mon_dispatch_postauth20[] = {
231#ifdef GSSAPI
232 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
233 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
234 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
235 {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
236#endif
228#ifdef WITH_OPENSSL 237#ifdef WITH_OPENSSL
229 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 238 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
230#endif 239#endif
@@ -293,6 +302,10 @@ monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor)
293 /* Permit requests for moduli and signatures */ 302 /* Permit requests for moduli and signatures */
294 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 303 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
295 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 304 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
305#ifdef GSSAPI
306 /* and for the GSSAPI key exchange */
307 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
308#endif
296 309
297 /* The first few requests do not require asynchronous access */ 310 /* The first few requests do not require asynchronous access */
298 while (!authenticated) { 311 while (!authenticated) {
@@ -406,6 +419,10 @@ monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor)
406 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 419 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
407 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 420 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
408 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 421 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
422#ifdef GSSAPI
423 /* and for the GSSAPI key exchange */
424 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
425#endif
409 426
410 if (auth_opts->permit_pty_flag) { 427 if (auth_opts->permit_pty_flag) {
411 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); 428 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
@@ -1725,6 +1742,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
1725# ifdef OPENSSL_HAS_ECC 1742# ifdef OPENSSL_HAS_ECC
1726 kex->kex[KEX_ECDH_SHA2] = kex_gen_server; 1743 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
1727# endif 1744# endif
1745# ifdef GSSAPI
1746 if (options.gss_keyex) {
1747 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1748 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1749 kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
1750 kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
1751 kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
1752 kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
1753 kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
1754 }
1755# endif
1728#endif /* WITH_OPENSSL */ 1756#endif /* WITH_OPENSSL */
1729 kex->kex[KEX_C25519_SHA256] = kex_gen_server; 1757 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
1730 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; 1758 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
@@ -1818,8 +1846,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1818 u_char *p; 1846 u_char *p;
1819 int r; 1847 int r;
1820 1848
1821 if (!options.gss_authentication) 1849 if (!options.gss_authentication && !options.gss_keyex)
1822 fatal("%s: GSSAPI authentication not enabled", __func__); 1850 fatal("%s: GSSAPI not enabled", __func__);
1823 1851
1824 if ((r = sshbuf_get_string(m, &p, &len)) != 0) 1852 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1825 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1853 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1851,8 +1879,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1851 OM_uint32 flags = 0; /* GSI needs this */ 1879 OM_uint32 flags = 0; /* GSI needs this */
1852 int r; 1880 int r;
1853 1881
1854 if (!options.gss_authentication) 1882 if (!options.gss_authentication && !options.gss_keyex)
1855 fatal("%s: GSSAPI authentication not enabled", __func__); 1883 fatal("%s: GSSAPI not enabled", __func__);
1856 1884
1857 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) 1885 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
1858 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1886 fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1872,6 +1900,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
1872 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 1900 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
1873 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 1901 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
1874 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 1902 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
1903 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
1875 } 1904 }
1876 return (0); 1905 return (0);
1877} 1906}
@@ -1883,8 +1912,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
1883 OM_uint32 ret; 1912 OM_uint32 ret;
1884 int r; 1913 int r;
1885 1914
1886 if (!options.gss_authentication) 1915 if (!options.gss_authentication && !options.gss_keyex)
1887 fatal("%s: GSSAPI authentication not enabled", __func__); 1916 fatal("%s: GSSAPI not enabled", __func__);
1888 1917
1889 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || 1918 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
1890 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) 1919 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
@@ -1910,13 +1939,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
1910int 1939int
1911mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) 1940mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1912{ 1941{
1913 int r, authenticated; 1942 int r, authenticated, kex;
1914 const char *displayname; 1943 const char *displayname;
1915 1944
1916 if (!options.gss_authentication) 1945 if (!options.gss_authentication && !options.gss_keyex)
1917 fatal("%s: GSSAPI authentication not enabled", __func__); 1946 fatal("%s: GSSAPI not enabled", __func__);
1918 1947
1919 authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); 1948 if ((r = sshbuf_get_u32(m, &kex)) != 0)
1949 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1950
1951 authenticated = authctxt->valid &&
1952 ssh_gssapi_userok(authctxt->user, authctxt->pw, kex);
1920 1953
1921 sshbuf_reset(m); 1954 sshbuf_reset(m);
1922 if ((r = sshbuf_put_u32(m, authenticated)) != 0) 1955 if ((r = sshbuf_put_u32(m, authenticated)) != 0)
@@ -1925,7 +1958,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1925 debug3("%s: sending result %d", __func__, authenticated); 1958 debug3("%s: sending result %d", __func__, authenticated);
1926 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); 1959 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
1927 1960
1928 auth_method = "gssapi-with-mic"; 1961 if (kex) {
1962 auth_method = "gssapi-keyex";
1963 } else {
1964 auth_method = "gssapi-with-mic";
1965 }
1929 1966
1930 if ((displayname = ssh_gssapi_displayname()) != NULL) 1967 if ((displayname = ssh_gssapi_displayname()) != NULL)
1931 auth2_record_info(authctxt, "%s", displayname); 1968 auth2_record_info(authctxt, "%s", displayname);
@@ -1933,5 +1970,85 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
1933 /* Monitor loop will terminate if authenticated */ 1970 /* Monitor loop will terminate if authenticated */
1934 return (authenticated); 1971 return (authenticated);
1935} 1972}
1973
1974int
1975mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m)
1976{
1977 gss_buffer_desc data;
1978 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
1979 OM_uint32 major, minor;
1980 size_t len;
1981 u_char *p = NULL;
1982 int r;
1983
1984 if (!options.gss_authentication && !options.gss_keyex)
1985 fatal("%s: GSSAPI not enabled", __func__);
1986
1987 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
1988 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1989 data.value = p;
1990 data.length = len;
1991 /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */
1992 if (data.length != 20 && data.length != 32 && data.length != 64)
1993 fatal("%s: data length incorrect: %d", __func__,
1994 (int) data.length);
1995
1996 /* Save the session ID on the first time around */
1997 if (session_id2_len == 0) {
1998 session_id2_len = data.length;
1999 session_id2 = xmalloc(session_id2_len);
2000 memcpy(session_id2, data.value, session_id2_len);
2001 }
2002 major = ssh_gssapi_sign(gsscontext, &data, &hash);
2003
2004 free(data.value);
2005
2006 sshbuf_reset(m);
2007
2008 if ((r = sshbuf_put_u32(m, major)) != 0 ||
2009 (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
2010 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2011
2012 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2013
2014 gss_release_buffer(&minor, &hash);
2015
2016 /* Turn on getpwnam permissions */
2017 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2018
2019 /* And credential updating, for when rekeying */
2020 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2021
2022 return (0);
2023}
2024
2025int
2026mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) {
2027 ssh_gssapi_ccache store;
2028 int r, ok;
2029
2030 if (!options.gss_authentication && !options.gss_keyex)
2031 fatal("%s: GSSAPI not enabled", __func__);
2032
2033 if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 ||
2034 (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 ||
2035 (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0)
2036 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2037
2038 ok = ssh_gssapi_update_creds(&store);
2039
2040 free(store.filename);
2041 free(store.envvar);
2042 free(store.envval);
2043
2044 sshbuf_reset(m);
2045 if ((r = sshbuf_put_u32(m, ok)) != 0)
2046 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2047
2048 mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2049
2050 return(0);
2051}
2052
1936#endif /* GSSAPI */ 2053#endif /* GSSAPI */
1937 2054
diff --git a/monitor.h b/monitor.h
index 683e5e071..2b1a2d590 100644
--- a/monitor.h
+++ b/monitor.h
@@ -63,6 +63,8 @@ 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,
66}; 68};
67 69
68struct ssh; 70struct ssh;
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 5e38d83eb..0e78cd006 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -993,13 +993,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
993} 993}
994 994
995int 995int
996mm_ssh_gssapi_userok(char *user) 996mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
997{ 997{
998 struct sshbuf *m; 998 struct sshbuf *m;
999 int r, authenticated = 0; 999 int r, authenticated = 0;
1000 1000
1001 if ((m = sshbuf_new()) == NULL) 1001 if ((m = sshbuf_new()) == NULL)
1002 fatal("%s: sshbuf_new failed", __func__); 1002 fatal("%s: sshbuf_new failed", __func__);
1003 if ((r = sshbuf_put_u32(m, kex)) != 0)
1004 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1003 1005
1004 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); 1006 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m);
1005 mm_request_receive_expect(pmonitor->m_recvfd, 1007 mm_request_receive_expect(pmonitor->m_recvfd,
@@ -1012,4 +1014,57 @@ mm_ssh_gssapi_userok(char *user)
1012 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1014 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1013 return (authenticated); 1015 return (authenticated);
1014} 1016}
1017
1018OM_uint32
1019mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1020{
1021 struct sshbuf *m;
1022 OM_uint32 major;
1023 int r;
1024
1025 if ((m = sshbuf_new()) == NULL)
1026 fatal("%s: sshbuf_new failed", __func__);
1027 if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
1028 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1029
1030 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m);
1031 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m);
1032
1033 if ((r = sshbuf_get_u32(m, &major)) != 0 ||
1034 (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
1035 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1036
1037 sshbuf_free(m);
1038
1039 return (major);
1040}
1041
1042int
1043mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
1044{
1045 struct sshbuf *m;
1046 int r, ok;
1047
1048 if ((m = sshbuf_new()) == NULL)
1049 fatal("%s: sshbuf_new failed", __func__);
1050
1051 if ((r = sshbuf_put_cstring(m,
1052 store->filename ? store->filename : "")) != 0 ||
1053 (r = sshbuf_put_cstring(m,
1054 store->envvar ? store->envvar : "")) != 0 ||
1055 (r = sshbuf_put_cstring(m,
1056 store->envval ? store->envval : "")) != 0)
1057 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1058
1059 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m);
1060 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m);
1061
1062 if ((r = sshbuf_get_u32(m, &ok)) != 0)
1063 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1064
1065 sshbuf_free(m);
1066
1067 return (ok);
1068}
1069
1015#endif /* GSSAPI */ 1070#endif /* GSSAPI */
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 0db38c206..75aef1c74 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -65,8 +65,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
65OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 65OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
66OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, 66OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
67 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); 67 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
68int mm_ssh_gssapi_userok(char *user); 68int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex);
69OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 69OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
70OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
71int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
70#endif 72#endif
71 73
72#ifdef USE_PAM 74#ifdef USE_PAM
diff --git a/readconf.c b/readconf.c
index 554efd7c9..57dae55d1 100644
--- a/readconf.c
+++ b/readconf.c
@@ -67,6 +67,7 @@
67#include "uidswap.h" 67#include "uidswap.h"
68#include "myproposal.h" 68#include "myproposal.h"
69#include "digest.h" 69#include "digest.h"
70#include "ssh-gss.h"
70 71
71/* Format of the configuration file: 72/* Format of the configuration file:
72 73
@@ -160,6 +161,8 @@ typedef enum {
160 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 161 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
161 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 162 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
162 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 163 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
164 oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
165 oGssServerIdentity, oGssKexAlgorithms,
163 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 166 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
164 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, 167 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
165 oHashKnownHosts, 168 oHashKnownHosts,
@@ -204,10 +207,22 @@ static struct {
204 /* Sometimes-unsupported options */ 207 /* Sometimes-unsupported options */
205#if defined(GSSAPI) 208#if defined(GSSAPI)
206 { "gssapiauthentication", oGssAuthentication }, 209 { "gssapiauthentication", oGssAuthentication },
210 { "gssapikeyexchange", oGssKeyEx },
207 { "gssapidelegatecredentials", oGssDelegateCreds }, 211 { "gssapidelegatecredentials", oGssDelegateCreds },
212 { "gssapitrustdns", oGssTrustDns },
213 { "gssapiclientidentity", oGssClientIdentity },
214 { "gssapiserveridentity", oGssServerIdentity },
215 { "gssapirenewalforcesrekey", oGssRenewalRekey },
216 { "gssapikexalgorithms", oGssKexAlgorithms },
208# else 217# else
209 { "gssapiauthentication", oUnsupported }, 218 { "gssapiauthentication", oUnsupported },
219 { "gssapikeyexchange", oUnsupported },
210 { "gssapidelegatecredentials", oUnsupported }, 220 { "gssapidelegatecredentials", oUnsupported },
221 { "gssapitrustdns", oUnsupported },
222 { "gssapiclientidentity", oUnsupported },
223 { "gssapiserveridentity", oUnsupported },
224 { "gssapirenewalforcesrekey", oUnsupported },
225 { "gssapikexalgorithms", oUnsupported },
211#endif 226#endif
212#ifdef ENABLE_PKCS11 227#ifdef ENABLE_PKCS11
213 { "pkcs11provider", oPKCS11Provider }, 228 { "pkcs11provider", oPKCS11Provider },
@@ -1068,10 +1083,42 @@ parse_time:
1068 intptr = &options->gss_authentication; 1083 intptr = &options->gss_authentication;
1069 goto parse_flag; 1084 goto parse_flag;
1070 1085
1086 case oGssKeyEx:
1087 intptr = &options->gss_keyex;
1088 goto parse_flag;
1089
1071 case oGssDelegateCreds: 1090 case oGssDelegateCreds:
1072 intptr = &options->gss_deleg_creds; 1091 intptr = &options->gss_deleg_creds;
1073 goto parse_flag; 1092 goto parse_flag;
1074 1093
1094 case oGssTrustDns:
1095 intptr = &options->gss_trust_dns;
1096 goto parse_flag;
1097
1098 case oGssClientIdentity:
1099 charptr = &options->gss_client_identity;
1100 goto parse_string;
1101
1102 case oGssServerIdentity:
1103 charptr = &options->gss_server_identity;
1104 goto parse_string;
1105
1106 case oGssRenewalRekey:
1107 intptr = &options->gss_renewal_rekey;
1108 goto parse_flag;
1109
1110 case oGssKexAlgorithms:
1111 arg = strdelim(&s);
1112 if (!arg || *arg == '\0')
1113 fatal("%.200s line %d: Missing argument.",
1114 filename, linenum);
1115 if (!kex_gss_names_valid(arg))
1116 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1117 filename, linenum, arg ? arg : "<NONE>");
1118 if (*activep && options->gss_kex_algorithms == NULL)
1119 options->gss_kex_algorithms = xstrdup(arg);
1120 break;
1121
1075 case oBatchMode: 1122 case oBatchMode:
1076 intptr = &options->batch_mode; 1123 intptr = &options->batch_mode;
1077 goto parse_flag; 1124 goto parse_flag;
@@ -1976,7 +2023,13 @@ initialize_options(Options * options)
1976 options->pubkey_authentication = -1; 2023 options->pubkey_authentication = -1;
1977 options->challenge_response_authentication = -1; 2024 options->challenge_response_authentication = -1;
1978 options->gss_authentication = -1; 2025 options->gss_authentication = -1;
2026 options->gss_keyex = -1;
1979 options->gss_deleg_creds = -1; 2027 options->gss_deleg_creds = -1;
2028 options->gss_trust_dns = -1;
2029 options->gss_renewal_rekey = -1;
2030 options->gss_client_identity = NULL;
2031 options->gss_server_identity = NULL;
2032 options->gss_kex_algorithms = NULL;
1980 options->password_authentication = -1; 2033 options->password_authentication = -1;
1981 options->kbd_interactive_authentication = -1; 2034 options->kbd_interactive_authentication = -1;
1982 options->kbd_interactive_devices = NULL; 2035 options->kbd_interactive_devices = NULL;
@@ -2125,8 +2178,18 @@ fill_default_options(Options * options)
2125 options->challenge_response_authentication = 1; 2178 options->challenge_response_authentication = 1;
2126 if (options->gss_authentication == -1) 2179 if (options->gss_authentication == -1)
2127 options->gss_authentication = 0; 2180 options->gss_authentication = 0;
2181 if (options->gss_keyex == -1)
2182 options->gss_keyex = 0;
2128 if (options->gss_deleg_creds == -1) 2183 if (options->gss_deleg_creds == -1)
2129 options->gss_deleg_creds = 0; 2184 options->gss_deleg_creds = 0;
2185 if (options->gss_trust_dns == -1)
2186 options->gss_trust_dns = 0;
2187 if (options->gss_renewal_rekey == -1)
2188 options->gss_renewal_rekey = 0;
2189#ifdef GSSAPI
2190 if (options->gss_kex_algorithms == NULL)
2191 options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
2192#endif
2130 if (options->password_authentication == -1) 2193 if (options->password_authentication == -1)
2131 options->password_authentication = 1; 2194 options->password_authentication = 1;
2132 if (options->kbd_interactive_authentication == -1) 2195 if (options->kbd_interactive_authentication == -1)
@@ -2776,7 +2839,14 @@ dump_client_config(Options *o, const char *host)
2776 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); 2839 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
2777#ifdef GSSAPI 2840#ifdef GSSAPI
2778 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); 2841 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
2842 dump_cfg_fmtint(oGssKeyEx, o->gss_keyex);
2779 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); 2843 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
2844 dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns);
2845 dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
2846 dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
2847 dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
2848 dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ?
2849 o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX);
2780#endif /* GSSAPI */ 2850#endif /* GSSAPI */
2781 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); 2851 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
2782 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); 2852 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
diff --git a/readconf.h b/readconf.h
index d6a15550d..3803eeddf 100644
--- a/readconf.h
+++ b/readconf.h
@@ -41,7 +41,13 @@ typedef struct {
41 int challenge_response_authentication; 41 int challenge_response_authentication;
42 /* Try S/Key or TIS, authentication. */ 42 /* Try S/Key or TIS, authentication. */
43 int gss_authentication; /* Try GSS authentication */ 43 int gss_authentication; /* Try GSS authentication */
44 int gss_keyex; /* Try GSS key exchange */
44 int gss_deleg_creds; /* Delegate GSS credentials */ 45 int gss_deleg_creds; /* Delegate GSS credentials */
46 int gss_trust_dns; /* Trust DNS for GSS canonicalization */
47 int gss_renewal_rekey; /* Credential renewal forces rekey */
48 char *gss_client_identity; /* Principal to initiate GSSAPI with */
49 char *gss_server_identity; /* GSSAPI target principal */
50 char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
45 int password_authentication; /* Try password 51 int password_authentication; /* Try password
46 * authentication. */ 52 * authentication. */
47 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 53 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
diff --git a/servconf.c b/servconf.c
index f08e37477..ded8f4a87 100644
--- a/servconf.c
+++ b/servconf.c
@@ -70,6 +70,7 @@
70#include "auth.h" 70#include "auth.h"
71#include "myproposal.h" 71#include "myproposal.h"
72#include "digest.h" 72#include "digest.h"
73#include "ssh-gss.h"
73 74
74static void add_listen_addr(ServerOptions *, const char *, 75static void add_listen_addr(ServerOptions *, const char *,
75 const char *, int); 76 const char *, int);
@@ -134,8 +135,11 @@ initialize_server_options(ServerOptions *options)
134 options->kerberos_ticket_cleanup = -1; 135 options->kerberos_ticket_cleanup = -1;
135 options->kerberos_get_afs_token = -1; 136 options->kerberos_get_afs_token = -1;
136 options->gss_authentication=-1; 137 options->gss_authentication=-1;
138 options->gss_keyex = -1;
137 options->gss_cleanup_creds = -1; 139 options->gss_cleanup_creds = -1;
138 options->gss_strict_acceptor = -1; 140 options->gss_strict_acceptor = -1;
141 options->gss_store_rekey = -1;
142 options->gss_kex_algorithms = NULL;
139 options->password_authentication = -1; 143 options->password_authentication = -1;
140 options->kbd_interactive_authentication = -1; 144 options->kbd_interactive_authentication = -1;
141 options->challenge_response_authentication = -1; 145 options->challenge_response_authentication = -1;
@@ -376,10 +380,18 @@ fill_default_server_options(ServerOptions *options)
376 options->kerberos_get_afs_token = 0; 380 options->kerberos_get_afs_token = 0;
377 if (options->gss_authentication == -1) 381 if (options->gss_authentication == -1)
378 options->gss_authentication = 0; 382 options->gss_authentication = 0;
383 if (options->gss_keyex == -1)
384 options->gss_keyex = 0;
379 if (options->gss_cleanup_creds == -1) 385 if (options->gss_cleanup_creds == -1)
380 options->gss_cleanup_creds = 1; 386 options->gss_cleanup_creds = 1;
381 if (options->gss_strict_acceptor == -1) 387 if (options->gss_strict_acceptor == -1)
382 options->gss_strict_acceptor = 1; 388 options->gss_strict_acceptor = 1;
389 if (options->gss_store_rekey == -1)
390 options->gss_store_rekey = 0;
391#ifdef GSSAPI
392 if (options->gss_kex_algorithms == NULL)
393 options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
394#endif
383 if (options->password_authentication == -1) 395 if (options->password_authentication == -1)
384 options->password_authentication = 1; 396 options->password_authentication = 1;
385 if (options->kbd_interactive_authentication == -1) 397 if (options->kbd_interactive_authentication == -1)
@@ -523,6 +535,7 @@ typedef enum {
523 sHostKeyAlgorithms, 535 sHostKeyAlgorithms,
524 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, 536 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
525 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, 537 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
538 sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey,
526 sAcceptEnv, sSetEnv, sPermitTunnel, 539 sAcceptEnv, sSetEnv, sPermitTunnel,
527 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, 540 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
528 sUsePrivilegeSeparation, sAllowAgentForwarding, 541 sUsePrivilegeSeparation, sAllowAgentForwarding,
@@ -600,12 +613,22 @@ static struct {
600#ifdef GSSAPI 613#ifdef GSSAPI
601 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 614 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
602 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 615 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
616 { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
603 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, 617 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
618 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
619 { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
620 { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL },
604#else 621#else
605 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 622 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
606 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 623 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
624 { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
607 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, 625 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
626 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
627 { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
628 { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL },
608#endif 629#endif
630 { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
631 { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
609 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 632 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
610 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 633 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
611 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 634 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
@@ -1557,6 +1580,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
1557 intptr = &options->gss_authentication; 1580 intptr = &options->gss_authentication;
1558 goto parse_flag; 1581 goto parse_flag;
1559 1582
1583 case sGssKeyEx:
1584 intptr = &options->gss_keyex;
1585 goto parse_flag;
1586
1560 case sGssCleanupCreds: 1587 case sGssCleanupCreds:
1561 intptr = &options->gss_cleanup_creds; 1588 intptr = &options->gss_cleanup_creds;
1562 goto parse_flag; 1589 goto parse_flag;
@@ -1565,6 +1592,22 @@ process_server_config_line_depth(ServerOptions *options, char *line,
1565 intptr = &options->gss_strict_acceptor; 1592 intptr = &options->gss_strict_acceptor;
1566 goto parse_flag; 1593 goto parse_flag;
1567 1594
1595 case sGssStoreRekey:
1596 intptr = &options->gss_store_rekey;
1597 goto parse_flag;
1598
1599 case sGssKexAlgorithms:
1600 arg = strdelim(&cp);
1601 if (!arg || *arg == '\0')
1602 fatal("%.200s line %d: Missing argument.",
1603 filename, linenum);
1604 if (!kex_gss_names_valid(arg))
1605 fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
1606 filename, linenum, arg ? arg : "<NONE>");
1607 if (*activep && options->gss_kex_algorithms == NULL)
1608 options->gss_kex_algorithms = xstrdup(arg);
1609 break;
1610
1568 case sPasswordAuthentication: 1611 case sPasswordAuthentication:
1569 intptr = &options->password_authentication; 1612 intptr = &options->password_authentication;
1570 goto parse_flag; 1613 goto parse_flag;
@@ -2808,6 +2851,10 @@ dump_config(ServerOptions *o)
2808#ifdef GSSAPI 2851#ifdef GSSAPI
2809 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 2852 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2810 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 2853 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2854 dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2855 dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2856 dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2857 dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
2811#endif 2858#endif
2812 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 2859 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
2813 dump_cfg_fmtint(sKbdInteractiveAuthentication, 2860 dump_cfg_fmtint(sKbdInteractiveAuthentication,
diff --git a/servconf.h b/servconf.h
index 1df8f3db8..f10908e5b 100644
--- a/servconf.h
+++ b/servconf.h
@@ -138,8 +138,11 @@ typedef struct {
138 int kerberos_get_afs_token; /* If true, try to get AFS token if 138 int kerberos_get_afs_token; /* If true, try to get AFS token if
139 * authenticated with Kerberos. */ 139 * authenticated with Kerberos. */
140 int gss_authentication; /* If true, permit GSSAPI authentication */ 140 int gss_authentication; /* If true, permit GSSAPI authentication */
141 int gss_keyex; /* If true, permit GSSAPI key exchange */
141 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 142 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
142 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ 143 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
144 int gss_store_rekey;
145 char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
143 int password_authentication; /* If true, permit password 146 int password_authentication; /* If true, permit password
144 * authentication. */ 147 * authentication. */
145 int kbd_interactive_authentication; /* If true, permit */ 148 int kbd_interactive_authentication; /* If true, permit */
diff --git a/session.c b/session.c
index 27ca8a104..857f17b3c 100644
--- a/session.c
+++ b/session.c
@@ -2685,13 +2685,19 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
2685 2685
2686#ifdef KRB5 2686#ifdef KRB5
2687 if (options.kerberos_ticket_cleanup && 2687 if (options.kerberos_ticket_cleanup &&
2688 authctxt->krb5_ctx) 2688 authctxt->krb5_ctx) {
2689 temporarily_use_uid(authctxt->pw);
2689 krb5_cleanup_proc(authctxt); 2690 krb5_cleanup_proc(authctxt);
2691 restore_uid();
2692 }
2690#endif 2693#endif
2691 2694
2692#ifdef GSSAPI 2695#ifdef GSSAPI
2693 if (options.gss_cleanup_creds) 2696 if (options.gss_cleanup_creds) {
2697 temporarily_use_uid(authctxt->pw);
2694 ssh_gssapi_cleanup_creds(); 2698 ssh_gssapi_cleanup_creds();
2699 restore_uid();
2700 }
2695#endif 2701#endif
2696 2702
2697 /* remove agent socket */ 2703 /* remove agent socket */
diff --git a/ssh-gss.h b/ssh-gss.h
index 36180d07a..50d80bbca 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,34 @@
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_GRP14_SHA256_ID "gss-group14-sha256-"
74#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-"
75#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
76#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-"
77#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-"
78
79#define GSS_KEX_DEFAULT_KEX \
80 KEX_GSS_GRP14_SHA256_ID "," \
81 KEX_GSS_GRP16_SHA512_ID "," \
82 KEX_GSS_NISTP256_SHA256_ID "," \
83 KEX_GSS_C25519_SHA256_ID "," \
84 KEX_GSS_GRP14_SHA1_ID "," \
85 KEX_GSS_GEX_SHA1_ID
86
64typedef struct { 87typedef struct {
65 char *filename; 88 char *filename;
66 char *envvar; 89 char *envvar;
67 char *envval; 90 char *envval;
91 struct passwd *owner;
68 void *data; 92 void *data;
69} ssh_gssapi_ccache; 93} ssh_gssapi_ccache;
70 94
@@ -72,8 +96,11 @@ typedef struct {
72 gss_buffer_desc displayname; 96 gss_buffer_desc displayname;
73 gss_buffer_desc exportedname; 97 gss_buffer_desc exportedname;
74 gss_cred_id_t creds; 98 gss_cred_id_t creds;
99 gss_name_t name;
75 struct ssh_gssapi_mech_struct *mech; 100 struct ssh_gssapi_mech_struct *mech;
76 ssh_gssapi_ccache store; 101 ssh_gssapi_ccache store;
102 int used;
103 int updated;
77} ssh_gssapi_client; 104} ssh_gssapi_client;
78 105
79typedef struct ssh_gssapi_mech_struct { 106typedef struct ssh_gssapi_mech_struct {
@@ -84,6 +111,7 @@ typedef struct ssh_gssapi_mech_struct {
84 int (*userok) (ssh_gssapi_client *, char *); 111 int (*userok) (ssh_gssapi_client *, char *);
85 int (*localname) (ssh_gssapi_client *, char **); 112 int (*localname) (ssh_gssapi_client *, char **);
86 void (*storecreds) (ssh_gssapi_client *); 113 void (*storecreds) (ssh_gssapi_client *);
114 int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
87} ssh_gssapi_mech; 115} ssh_gssapi_mech;
88 116
89typedef struct { 117typedef struct {
@@ -94,10 +122,11 @@ typedef struct {
94 gss_OID oid; /* client */ 122 gss_OID oid; /* client */
95 gss_cred_id_t creds; /* server */ 123 gss_cred_id_t creds; /* server */
96 gss_name_t client; /* server */ 124 gss_name_t client; /* server */
97 gss_cred_id_t client_creds; /* server */ 125 gss_cred_id_t client_creds; /* both */
98} Gssctxt; 126} Gssctxt;
99 127
100extern ssh_gssapi_mech *supported_mechs[]; 128extern ssh_gssapi_mech *supported_mechs[];
129extern Gssctxt *gss_kex_context;
101 130
102int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); 131int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
103void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); 132void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
@@ -109,6 +138,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *);
109 138
110struct sshbuf; 139struct sshbuf;
111int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *); 140int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *);
141int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *);
112 142
113OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *); 143OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *);
114OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int, 144OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int,
@@ -123,17 +153,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **);
123OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); 153OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
124void ssh_gssapi_buildmic(struct sshbuf *, const char *, 154void ssh_gssapi_buildmic(struct sshbuf *, const char *,
125 const char *, const char *); 155 const char *, const char *);
126int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); 156int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
157OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
158int ssh_gssapi_credentials_updated(Gssctxt *);
127 159
128/* In the server */ 160/* In the server */
161typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
162 const char *);
163char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *);
164char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
165 const char *, const char *);
166gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
167int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
168 const char *);
129OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 169OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
130int ssh_gssapi_userok(char *name); 170int ssh_gssapi_userok(char *name, struct passwd *, int kex);
131OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 171OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
132void ssh_gssapi_do_child(char ***, u_int *); 172void ssh_gssapi_do_child(char ***, u_int *);
133void ssh_gssapi_cleanup_creds(void); 173void ssh_gssapi_cleanup_creds(void);
134void ssh_gssapi_storecreds(void); 174void ssh_gssapi_storecreds(void);
135const char *ssh_gssapi_displayname(void); 175const char *ssh_gssapi_displayname(void);
136 176
177char *ssh_gssapi_server_mechanisms(void);
178int ssh_gssapi_oid_table_ok(void);
179
180int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
181void ssh_gssapi_rekey_creds(void);
182
137#endif /* GSSAPI */ 183#endif /* GSSAPI */
138 184
139#endif /* _SSH_GSS_H */ 185#endif /* _SSH_GSS_H */
diff --git a/ssh.1 b/ssh.1
index 555317887..be8e964f0 100644
--- a/ssh.1
+++ b/ssh.1
@@ -506,7 +506,13 @@ For full details of the options listed below, and their possible values, see
506.It GatewayPorts 506.It GatewayPorts
507.It GlobalKnownHostsFile 507.It GlobalKnownHostsFile
508.It GSSAPIAuthentication 508.It GSSAPIAuthentication
509.It GSSAPIKeyExchange
510.It GSSAPIClientIdentity
509.It GSSAPIDelegateCredentials 511.It GSSAPIDelegateCredentials
512.It GSSAPIKexAlgorithms
513.It GSSAPIRenewalForcesRekey
514.It GSSAPIServerIdentity
515.It GSSAPITrustDns
510.It HashKnownHosts 516.It HashKnownHosts
511.It Host 517.It Host
512.It HostbasedAuthentication 518.It HostbasedAuthentication
@@ -582,6 +588,8 @@ flag),
582(supported message integrity codes), 588(supported message integrity codes),
583.Ar kex 589.Ar kex
584(key exchange algorithms), 590(key exchange algorithms),
591.Ar kex-gss
592(GSSAPI key exchange algorithms),
585.Ar key 593.Ar key
586(key types), 594(key types),
587.Ar key-cert 595.Ar key-cert
diff --git a/ssh.c b/ssh.c
index f34ca0d71..bb98a7e2d 100644
--- a/ssh.c
+++ b/ssh.c
@@ -801,6 +801,8 @@ main(int ac, char **av)
801 else if (strcmp(optarg, "kex") == 0 || 801 else if (strcmp(optarg, "kex") == 0 ||
802 strcasecmp(optarg, "KexAlgorithms") == 0) 802 strcasecmp(optarg, "KexAlgorithms") == 0)
803 cp = kex_alg_list('\n'); 803 cp = kex_alg_list('\n');
804 else if (strcmp(optarg, "kex-gss") == 0)
805 cp = kex_gss_alg_list('\n');
804 else if (strcmp(optarg, "key") == 0) 806 else if (strcmp(optarg, "key") == 0)
805 cp = sshkey_alg_list(0, 0, 0, '\n'); 807 cp = sshkey_alg_list(0, 0, 0, '\n');
806 else if (strcmp(optarg, "key-cert") == 0) 808 else if (strcmp(optarg, "key-cert") == 0)
@@ -826,8 +828,8 @@ main(int ac, char **av)
826 } else if (strcmp(optarg, "help") == 0) { 828 } else if (strcmp(optarg, "help") == 0) {
827 cp = xstrdup( 829 cp = xstrdup(
828 "cipher\ncipher-auth\ncompression\nkex\n" 830 "cipher\ncipher-auth\ncompression\nkex\n"
829 "key\nkey-cert\nkey-plain\nkey-sig\nmac\n" 831 "kex-gss\nkey\nkey-cert\nkey-plain\n"
830 "protocol-version\nsig"); 832 "key-sig\nmac\nprotocol-version\nsig");
831 } 833 }
832 if (cp == NULL) 834 if (cp == NULL)
833 fatal("Unsupported query \"%s\"", optarg); 835 fatal("Unsupported query \"%s\"", optarg);
diff --git a/ssh_config b/ssh_config
index 842ea866c..52aae8692 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 6be1f1aa2..bd86d000c 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -779,10 +779,67 @@ The default is
779Specifies whether user authentication based on GSSAPI is allowed. 779Specifies whether user authentication based on GSSAPI is allowed.
780The default is 780The default is
781.Cm no . 781.Cm no .
782.It Cm GSSAPIClientIdentity
783If set, specifies the GSSAPI client identity that ssh should use when
784connecting to the server. The default is unset, which means that the default
785identity will be used.
782.It Cm GSSAPIDelegateCredentials 786.It Cm GSSAPIDelegateCredentials
783Forward (delegate) credentials to the server. 787Forward (delegate) credentials to the server.
784The default is 788The default is
785.Cm no . 789.Cm no .
790.It Cm GSSAPIKeyExchange
791Specifies whether key exchange based on GSSAPI may be used. When using
792GSSAPI key exchange the server need not have a host key.
793The default is
794.Dq no .
795.It Cm GSSAPIRenewalForcesRekey
796If set to
797.Dq yes
798then renewal of the client's GSSAPI credentials will force the rekeying of the
799ssh connection. With a compatible server, this will delegate the renewed
800credentials to a session on the server.
801.Pp
802Checks are made to ensure that credentials are only propagated when the new
803credentials match the old ones on the originating client and where the
804receiving server still has the old set in its cache.
805.Pp
806The default is
807.Dq no .
808.Pp
809For this to work
810.Cm GSSAPIKeyExchange
811needs to be enabled in the server and also used by the client.
812.It Cm GSSAPIServerIdentity
813If set, specifies the GSSAPI server identity that ssh should expect when
814connecting to the server. The default is unset, which means that the
815expected GSSAPI server identity will be determined from the target
816hostname.
817.It Cm GSSAPITrustDns
818Set to
819.Dq yes
820to indicate that the DNS is trusted to securely canonicalize
821the name of the host being connected to. If
822.Dq no ,
823the hostname entered on the
824command line will be passed untouched to the GSSAPI library.
825The default is
826.Dq no .
827.It Cm GSSAPIKexAlgorithms
828The list of key exchange algorithms that are offered for GSSAPI
829key exchange. Possible values are
830.Bd -literal -offset 3n
831gss-gex-sha1-,
832gss-group1-sha1-,
833gss-group14-sha1-,
834gss-group14-sha256-,
835gss-group16-sha512-,
836gss-nistp256-sha256-,
837gss-curve25519-sha256-
838.Ed
839.Pp
840The default is
841.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,gss-curve25519-sha256-,gss-gex-sha1-,gss-group14-sha1- .
842This option only applies to connections using GSSAPI.
786.It Cm HashKnownHosts 843.It Cm HashKnownHosts
787Indicates that 844Indicates that
788.Xr ssh 1 845.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index f64aae66a..c47fc31a6 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -80,8 +80,6 @@
80#endif 80#endif
81 81
82/* import */ 82/* import */
83extern char *client_version_string;
84extern char *server_version_string;
85extern Options options; 83extern Options options;
86 84
87/* 85/*
@@ -210,6 +208,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
210 char *s, *all_key; 208 char *s, *all_key;
211 int r, use_known_hosts_order = 0; 209 int r, use_known_hosts_order = 0;
212 210
211#if defined(GSSAPI) && defined(WITH_OPENSSL)
212 char *orig = NULL, *gss = NULL;
213 char *gss_host = NULL;
214#endif
215
213 xxx_host = host; 216 xxx_host = host;
214 xxx_hostaddr = hostaddr; 217 xxx_hostaddr = hostaddr;
215 218
@@ -253,6 +256,41 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
253 compat_pkalg_proposal(options.hostkeyalgorithms); 256 compat_pkalg_proposal(options.hostkeyalgorithms);
254 } 257 }
255 258
259#if defined(GSSAPI) && defined(WITH_OPENSSL)
260 if (options.gss_keyex) {
261 /* Add the GSSAPI mechanisms currently supported on this
262 * client to the key exchange algorithm proposal */
263 orig = myproposal[PROPOSAL_KEX_ALGS];
264
265 if (options.gss_server_identity) {
266 gss_host = xstrdup(options.gss_server_identity);
267 } else if (options.gss_trust_dns) {
268 gss_host = remote_hostname(ssh);
269 /* Fall back to specified host if we are using proxy command
270 * and can not use DNS on that socket */
271 if (strcmp(gss_host, "UNKNOWN") == 0) {
272 gss_host = xstrdup(host);
273 }
274 } else {
275 gss_host = xstrdup(host);
276 }
277
278 gss = ssh_gssapi_client_mechanisms(gss_host,
279 options.gss_client_identity, options.gss_kex_algorithms);
280 if (gss) {
281 debug("Offering GSSAPI proposal: %s", gss);
282 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
283 "%s,%s", gss, orig);
284
285 /* If we've got GSSAPI algorithms, then we also support the
286 * 'null' hostkey, as a last resort */
287 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
288 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
289 "%s,null", orig);
290 }
291 }
292#endif
293
256 if (options.rekey_limit || options.rekey_interval) 294 if (options.rekey_limit || options.rekey_interval)
257 ssh_packet_set_rekey_limits(ssh, options.rekey_limit, 295 ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
258 options.rekey_interval); 296 options.rekey_interval);
@@ -271,16 +309,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
271# ifdef OPENSSL_HAS_ECC 309# ifdef OPENSSL_HAS_ECC
272 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; 310 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
273# endif 311# endif
274#endif 312# ifdef GSSAPI
313 if (options.gss_keyex) {
314 ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
315 ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
316 ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
317 ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
318 ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
319 ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
320 ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
321 }
322# endif
323#endif /* WITH_OPENSSL */
275 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; 324 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
276 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; 325 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client;
277 ssh->kex->verify_host_key=&verify_host_key_callback; 326 ssh->kex->verify_host_key=&verify_host_key_callback;
278 327
328#if defined(GSSAPI) && defined(WITH_OPENSSL)
329 if (options.gss_keyex) {
330 ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
331 ssh->kex->gss_trust_dns = options.gss_trust_dns;
332 ssh->kex->gss_client = options.gss_client_identity;
333 ssh->kex->gss_host = gss_host;
334 }
335#endif
336
279 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); 337 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
280 338
281 /* remove ext-info from the KEX proposals for rekeying */ 339 /* remove ext-info from the KEX proposals for rekeying */
282 myproposal[PROPOSAL_KEX_ALGS] = 340 myproposal[PROPOSAL_KEX_ALGS] =
283 compat_kex_proposal(options.kex_algorithms); 341 compat_kex_proposal(options.kex_algorithms);
342#if defined(GSSAPI) && defined(WITH_OPENSSL)
343 /* repair myproposal after it was crumpled by the */
344 /* ext-info removal above */
345 if (gss) {
346 orig = myproposal[PROPOSAL_KEX_ALGS];
347 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
348 "%s,%s", gss, orig);
349 free(gss);
350 }
351#endif
284 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) 352 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
285 fatal("kex_prop2buf: %s", ssh_err(r)); 353 fatal("kex_prop2buf: %s", ssh_err(r));
286 354
@@ -377,6 +445,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *);
377static int input_gssapi_token(int type, u_int32_t, struct ssh *); 445static int input_gssapi_token(int type, u_int32_t, struct ssh *);
378static int input_gssapi_error(int, u_int32_t, struct ssh *); 446static int input_gssapi_error(int, u_int32_t, struct ssh *);
379static int input_gssapi_errtok(int, u_int32_t, struct ssh *); 447static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
448static int userauth_gsskeyex(struct ssh *);
380#endif 449#endif
381 450
382void userauth(struct ssh *, char *); 451void userauth(struct ssh *, char *);
@@ -393,6 +462,11 @@ static char *authmethods_get(void);
393 462
394Authmethod authmethods[] = { 463Authmethod authmethods[] = {
395#ifdef GSSAPI 464#ifdef GSSAPI
465 {"gssapi-keyex",
466 userauth_gsskeyex,
467 NULL,
468 &options.gss_keyex,
469 NULL},
396 {"gssapi-with-mic", 470 {"gssapi-with-mic",
397 userauth_gssapi, 471 userauth_gssapi,
398 userauth_gssapi_cleanup, 472 userauth_gssapi_cleanup,
@@ -763,12 +837,31 @@ userauth_gssapi(struct ssh *ssh)
763 OM_uint32 min; 837 OM_uint32 min;
764 int r, ok = 0; 838 int r, ok = 0;
765 gss_OID mech = NULL; 839 gss_OID mech = NULL;
840 char *gss_host;
841
842 if (options.gss_server_identity) {
843 gss_host = xstrdup(options.gss_server_identity);
844 } else if (options.gss_trust_dns) {
845 gss_host = remote_hostname(ssh);
846 /* Fall back to specified host if we are using proxy command
847 * and can not use DNS on that socket */
848 if (strcmp(gss_host, "UNKNOWN") == 0) {
849 gss_host = authctxt->host;
850 }
851 } else {
852 gss_host = xstrdup(authctxt->host);
853 }
766 854
767 /* Try one GSSAPI method at a time, rather than sending them all at 855 /* Try one GSSAPI method at a time, rather than sending them all at
768 * once. */ 856 * once. */
769 857
770 if (authctxt->gss_supported_mechs == NULL) 858 if (authctxt->gss_supported_mechs == NULL)
771 gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); 859 if (GSS_ERROR(gss_indicate_mechs(&min,
860 &authctxt->gss_supported_mechs))) {
861 authctxt->gss_supported_mechs = NULL;
862 free(gss_host);
863 return 0;
864 }
772 865
773 /* Check to see whether the mechanism is usable before we offer it */ 866 /* Check to see whether the mechanism is usable before we offer it */
774 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && 867 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
@@ -777,13 +870,15 @@ userauth_gssapi(struct ssh *ssh)
777 elements[authctxt->mech_tried]; 870 elements[authctxt->mech_tried];
778 /* My DER encoding requires length<128 */ 871 /* My DER encoding requires length<128 */
779 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, 872 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
780 mech, authctxt->host)) { 873 mech, gss_host, options.gss_client_identity)) {
781 ok = 1; /* Mechanism works */ 874 ok = 1; /* Mechanism works */
782 } else { 875 } else {
783 authctxt->mech_tried++; 876 authctxt->mech_tried++;
784 } 877 }
785 } 878 }
786 879
880 free(gss_host);
881
787 if (!ok || mech == NULL) 882 if (!ok || mech == NULL)
788 return 0; 883 return 0;
789 884
@@ -1023,6 +1118,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
1023 free(lang); 1118 free(lang);
1024 return r; 1119 return r;
1025} 1120}
1121
1122int
1123userauth_gsskeyex(struct ssh *ssh)
1124{
1125 struct sshbuf *b = NULL;
1126 Authctxt *authctxt = ssh->authctxt;
1127 gss_buffer_desc gssbuf;
1128 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
1129 OM_uint32 ms;
1130 int r;
1131
1132 static int attempt = 0;
1133 if (attempt++ >= 1)
1134 return (0);
1135
1136 if (gss_kex_context == NULL) {
1137 debug("No valid Key exchange context");
1138 return (0);
1139 }
1140
1141 if ((b = sshbuf_new()) == NULL)
1142 fatal("%s: sshbuf_new failed", __func__);
1143
1144 ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
1145 "gssapi-keyex");
1146
1147 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
1148 fatal("%s: sshbuf_mutable_ptr failed", __func__);
1149 gssbuf.length = sshbuf_len(b);
1150
1151 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
1152 sshbuf_free(b);
1153 return (0);
1154 }
1155
1156 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1157 (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1158 (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1159 (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1160 (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
1161 (r = sshpkt_send(ssh)) != 0)
1162 fatal("%s: %s", __func__, ssh_err(r));
1163
1164 sshbuf_free(b);
1165 gss_release_buffer(&ms, &mic);
1166
1167 return (1);
1168}
1169
1026#endif /* GSSAPI */ 1170#endif /* GSSAPI */
1027 1171
1028static int 1172static int
diff --git a/sshd.c b/sshd.c
index 8aa7f3df6..8c5d5822e 100644
--- a/sshd.c
+++ b/sshd.c
@@ -816,8 +816,8 @@ notify_hostkeys(struct ssh *ssh)
816 } 816 }
817 debug3("%s: sent %u hostkeys", __func__, nkeys); 817 debug3("%s: sent %u hostkeys", __func__, nkeys);
818 if (nkeys == 0) 818 if (nkeys == 0)
819 fatal("%s: no hostkeys", __func__); 819 debug3("%s: no hostkeys", __func__);
820 if ((r = sshpkt_send(ssh)) != 0) 820 else if ((r = sshpkt_send(ssh)) != 0)
821 sshpkt_fatal(ssh, r, "%s: send", __func__); 821 sshpkt_fatal(ssh, r, "%s: send", __func__);
822 sshbuf_free(buf); 822 sshbuf_free(buf);
823} 823}
@@ -1901,7 +1901,8 @@ main(int ac, char **av)
1901 free(fp); 1901 free(fp);
1902 } 1902 }
1903 accumulate_host_timing_secret(cfg, NULL); 1903 accumulate_host_timing_secret(cfg, NULL);
1904 if (!sensitive_data.have_ssh2_key) { 1904 /* The GSSAPI key exchange can run without a host key */
1905 if (!sensitive_data.have_ssh2_key && !options.gss_keyex) {
1905 logit("sshd: no hostkeys available -- exiting."); 1906 logit("sshd: no hostkeys available -- exiting.");
1906 exit(1); 1907 exit(1);
1907 } 1908 }
@@ -2393,6 +2394,48 @@ do_ssh2_kex(struct ssh *ssh)
2393 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( 2394 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
2394 list_hostkey_types()); 2395 list_hostkey_types());
2395 2396
2397#if defined(GSSAPI) && defined(WITH_OPENSSL)
2398 {
2399 char *orig;
2400 char *gss = NULL;
2401 char *newstr = NULL;
2402 orig = myproposal[PROPOSAL_KEX_ALGS];
2403
2404 /*
2405 * If we don't have a host key, then there's no point advertising
2406 * the other key exchange algorithms
2407 */
2408
2409 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2410 orig = NULL;
2411
2412 if (options.gss_keyex)
2413 gss = ssh_gssapi_server_mechanisms();
2414 else
2415 gss = NULL;
2416
2417 if (gss && orig)
2418 xasprintf(&newstr, "%s,%s", gss, orig);
2419 else if (gss)
2420 newstr = gss;
2421 else if (orig)
2422 newstr = orig;
2423
2424 /*
2425 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2426 * key alg, but we can't tell people about it unless its the only
2427 * host key algorithm we support
2428 */
2429 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2430 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2431
2432 if (newstr)
2433 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2434 else
2435 fatal("No supported key exchange algorithms");
2436 }
2437#endif
2438
2396 /* start key exchange */ 2439 /* start key exchange */
2397 if ((r = kex_setup(ssh, myproposal)) != 0) 2440 if ((r = kex_setup(ssh, myproposal)) != 0)
2398 fatal("kex_setup: %s", ssh_err(r)); 2441 fatal("kex_setup: %s", ssh_err(r));
@@ -2408,7 +2451,18 @@ do_ssh2_kex(struct ssh *ssh)
2408# ifdef OPENSSL_HAS_ECC 2451# ifdef OPENSSL_HAS_ECC
2409 kex->kex[KEX_ECDH_SHA2] = kex_gen_server; 2452 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
2410# endif 2453# endif
2411#endif 2454# ifdef GSSAPI
2455 if (options.gss_keyex) {
2456 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2457 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2458 kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
2459 kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
2460 kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
2461 kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
2462 kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
2463 }
2464# endif
2465#endif /* WITH_OPENSSL */
2412 kex->kex[KEX_C25519_SHA256] = kex_gen_server; 2466 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
2413 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; 2467 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
2414 kex->load_host_public_key=&get_hostkey_public_by_type; 2468 kex->load_host_public_key=&get_hostkey_public_by_type;
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 6fa421cae..eabbe9e73 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -644,6 +644,11 @@ Specifies whether to automatically destroy the user's credentials cache
644on logout. 644on logout.
645The default is 645The default is
646.Cm yes . 646.Cm yes .
647.It Cm GSSAPIKeyExchange
648Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
649doesn't rely on ssh keys to verify host identity.
650The default is
651.Cm no .
647.It Cm GSSAPIStrictAcceptorCheck 652.It Cm GSSAPIStrictAcceptorCheck
648Determines whether to be strict about the identity of the GSSAPI acceptor 653Determines whether to be strict about the identity of the GSSAPI acceptor
649a client authenticates against. 654a client authenticates against.
@@ -658,6 +663,31 @@ machine's default store.
658This facility is provided to assist with operation on multi homed machines. 663This facility is provided to assist with operation on multi homed machines.
659The default is 664The default is
660.Cm yes . 665.Cm yes .
666.It Cm GSSAPIStoreCredentialsOnRekey
667Controls whether the user's GSSAPI credentials should be updated following a
668successful connection rekeying. This option can be used to accepted renewed
669or updated credentials from a compatible client. The default is
670.Dq no .
671.Pp
672For this to work
673.Cm GSSAPIKeyExchange
674needs to be enabled in the server and also used by the client.
675.It Cm GSSAPIKexAlgorithms
676The list of key exchange algorithms that are accepted by GSSAPI
677key exchange. Possible values are
678.Bd -literal -offset 3n
679gss-gex-sha1-,
680gss-group1-sha1-,
681gss-group14-sha1-,
682gss-group14-sha256-,
683gss-group16-sha512-,
684gss-nistp256-sha256-,
685gss-curve25519-sha256-
686.Ed
687.Pp
688The default is
689.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,gss-curve25519-sha256-,gss-gex-sha1-,gss-group14-sha1- .
690This option only applies to connections using GSSAPI.
661.It Cm HostbasedAcceptedKeyTypes 691.It Cm HostbasedAcceptedKeyTypes
662Specifies the key types that will be accepted for hostbased authentication 692Specifies the key types that will be accepted for hostbased authentication
663as a list of comma-separated patterns. 693as a list of comma-separated patterns.
diff --git a/sshkey.c b/sshkey.c
index ac451f1a8..b88282e19 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -156,6 +156,7 @@ static const struct keytype keytypes[] = {
156 KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 }, 156 KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 },
157# endif /* OPENSSL_HAS_ECC */ 157# endif /* OPENSSL_HAS_ECC */
158#endif /* WITH_OPENSSL */ 158#endif /* WITH_OPENSSL */
159 { "null", "null", NULL, KEY_NULL, 0, 0, 0 },
159 { NULL, NULL, NULL, -1, -1, 0, 0 } 160 { NULL, NULL, NULL, -1, -1, 0, 0 }
160}; 161};
161 162
@@ -257,7 +258,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
257 const struct keytype *kt; 258 const struct keytype *kt;
258 259
259 for (kt = keytypes; kt->type != -1; kt++) { 260 for (kt = keytypes; kt->type != -1; kt++) {
260 if (kt->name == NULL) 261 if (kt->name == NULL || kt->type == KEY_NULL)
261 continue; 262 continue;
262 if (!include_sigonly && kt->sigonly) 263 if (!include_sigonly && kt->sigonly)
263 continue; 264 continue;
diff --git a/sshkey.h b/sshkey.h
index 2d8b62497..dc1c10597 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -69,6 +69,7 @@ enum sshkey_types {
69 KEY_ECDSA_SK_CERT, 69 KEY_ECDSA_SK_CERT,
70 KEY_ED25519_SK, 70 KEY_ED25519_SK,
71 KEY_ED25519_SK_CERT, 71 KEY_ED25519_SK_CERT,
72 KEY_NULL,
72 KEY_UNSPEC 73 KEY_UNSPEC
73}; 74};
74 75