summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog.gssapi69
-rw-r--r--Makefile.in4
-rw-r--r--auth-krb5.c17
-rw-r--r--auth.h1
-rw-r--r--auth2-gss.c41
-rw-r--r--auth2.c7
-rw-r--r--configure.ac26
-rw-r--r--gss-genr.c170
-rw-r--r--gss-serv-krb5.c14
-rw-r--r--gss-serv.c38
-rw-r--r--kex.c18
-rw-r--r--kex.h13
-rw-r--r--kexgssc.c319
-rw-r--r--kexgsss.c271
-rw-r--r--key.c2
-rw-r--r--key.h1
-rw-r--r--monitor.c59
-rw-r--r--monitor.h1
-rw-r--r--monitor_wrap.c23
-rw-r--r--monitor_wrap.h1
-rw-r--r--readconf.c20
-rw-r--r--readconf.h2
-rw-r--r--servconf.c22
-rw-r--r--servconf.h2
-rw-r--r--ssh-gss.h19
-rw-r--r--ssh_config2
-rw-r--r--ssh_config.517
-rw-r--r--sshconnect2.c105
-rw-r--r--sshd.c108
-rw-r--r--sshd_config2
-rw-r--r--sshd_config.523
31 files changed, 1391 insertions, 26 deletions
diff --git a/ChangeLog.gssapi b/ChangeLog.gssapi
new file mode 100644
index 000000000..010612c4c
--- /dev/null
+++ b/ChangeLog.gssapi
@@ -0,0 +1,69 @@
120070317
2 - [ gss-serv-krb5.c ]
3 Remove C99ism, where new_ccname was being declared in the middle of a
4 function
5
620061220
7 - [ servconf.c ]
8 Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and
9 documented, behaviour. Reported by Dan Watson.
10
1120060910
12 - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c
13 ssh-gss.h ]
14 add support for gss-group14-sha1 key exchange mechanisms
15 - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ]
16 Add GSSAPIStrictAcceptorCheck option to allow the disabling of
17 acceptor principal checking on multi-homed machines.
18 <Bugzilla #928>
19 - [ sshd_config ssh_config ]
20 Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample
21 configuration files
22 - [ kexgss.c kegsss.c sshconnect2.c sshd.c ]
23 Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf()
24 Limit length of error messages displayed by client
25
2620060909
27 - [ gss-genr.c gss-serv.c ]
28 move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server
29 only, where they belong
30 <Bugzilla #1225>
31
3220060829
33 - [ gss-serv-krb5.c ]
34 Fix CCAPI credentials cache name when creating KRB5CCNAME environment
35 variable
36
3720060828
38 - [ gss-genr.c ]
39 Avoid Heimdal context freeing problem
40 <Fixed upstream 20060829>
41
4220060818
43 - [ gss-genr.c ssh-gss.h sshconnect2.c ]
44 Make sure that SPENGO is disabled
45 <Bugzilla #1218 - Fixed upstream 20060818>
46
4720060421
48 - [ gssgenr.c, sshconnect2.c ]
49 a few type changes (signed versus unsigned, int versus size_t) to
50 fix compiler errors/warnings
51 (from jbasney AT ncsa.uiuc.edu)
52 - [ kexgssc.c, sshconnect2.c ]
53 fix uninitialized variable warnings
54 (from jbasney AT ncsa.uiuc.edu)
55 - [ gssgenr.c ]
56 pass oid to gss_display_status (helpful when using GSSAPI mechglue)
57 (from jbasney AT ncsa.uiuc.edu)
58 <Bugzilla #1220 >
59 - [ gss-serv-krb5.c ]
60 #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H
61 (from jbasney AT ncsa.uiuc.edu)
62 <Fixed upstream 20060304>
63 - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c
64 add client-side GssapiKeyExchange option
65 (from jbasney AT ncsa.uiuc.edu)
66 - [ sshconnect2.c ]
67 add support for GssapiTrustDns option for gssapi-with-mic
68 (from jbasney AT ncsa.uiuc.edu)
69 <gssapi-with-mic support is Bugzilla #1008>
diff --git a/Makefile.in b/Makefile.in
index 2486edc95..1e77a67b5 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -71,7 +71,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
71 atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ 71 atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
72 monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ 72 monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \
73 kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ 73 kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \
74 entropy.o scard-opensc.o gss-genr.o umac.o 74 entropy.o scard-opensc.o gss-genr.o umac.o kexgssc.o
75 75
76SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ 76SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
77 sshconnect.o sshconnect1.o sshconnect2.o 77 sshconnect.o sshconnect1.o sshconnect2.o
@@ -84,7 +84,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
84 auth2-none.o auth2-passwd.o auth2-pubkey.o \ 84 auth2-none.o auth2-passwd.o auth2-pubkey.o \
85 monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ 85 monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \
86 auth-krb5.o \ 86 auth-krb5.o \
87 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 87 auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o\
88 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ 88 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
89 audit.o audit-bsm.o platform.o 89 audit.o audit-bsm.o platform.o
90 90
diff --git a/auth-krb5.c b/auth-krb5.c
index 868288126..38164fda8 100644
--- a/auth-krb5.c
+++ b/auth-krb5.c
@@ -166,8 +166,13 @@ auth_krb5_password(Authctxt *authctxt, const char *password)
166 166
167 len = strlen(authctxt->krb5_ticket_file) + 6; 167 len = strlen(authctxt->krb5_ticket_file) + 6;
168 authctxt->krb5_ccname = xmalloc(len); 168 authctxt->krb5_ccname = xmalloc(len);
169#ifdef USE_CCAPI
170 snprintf(authctxt->krb5_ccname, len, "API:%s",
171 authctxt->krb5_ticket_file);
172#else
169 snprintf(authctxt->krb5_ccname, len, "FILE:%s", 173 snprintf(authctxt->krb5_ccname, len, "FILE:%s",
170 authctxt->krb5_ticket_file); 174 authctxt->krb5_ticket_file);
175#endif
171 176
172#ifdef USE_PAM 177#ifdef USE_PAM
173 if (options.use_pam) 178 if (options.use_pam)
@@ -219,15 +224,22 @@ krb5_cleanup_proc(Authctxt *authctxt)
219#ifndef HEIMDAL 224#ifndef HEIMDAL
220krb5_error_code 225krb5_error_code
221ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { 226ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
222 int tmpfd, ret; 227 int ret;
223 char ccname[40]; 228 char ccname[40];
224 mode_t old_umask; 229 mode_t old_umask;
230#ifdef USE_CCAPI
231 char cctemplate[] = "API:krb5cc_%d";
232#else
233 char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX";
234 int tmpfd;
235#endif
225 236
226 ret = snprintf(ccname, sizeof(ccname), 237 ret = snprintf(ccname, sizeof(ccname),
227 "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); 238 cctemplate, geteuid());
228 if (ret < 0 || (size_t)ret >= sizeof(ccname)) 239 if (ret < 0 || (size_t)ret >= sizeof(ccname))
229 return ENOMEM; 240 return ENOMEM;
230 241
242#ifndef USE_CCAPI
231 old_umask = umask(0177); 243 old_umask = umask(0177);
232 tmpfd = mkstemp(ccname + strlen("FILE:")); 244 tmpfd = mkstemp(ccname + strlen("FILE:"));
233 umask(old_umask); 245 umask(old_umask);
@@ -242,6 +254,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
242 return errno; 254 return errno;
243 } 255 }
244 close(tmpfd); 256 close(tmpfd);
257#endif
245 258
246 return (krb5_cc_resolve(ctx, ccname, ccache)); 259 return (krb5_cc_resolve(ctx, ccname, ccache));
247} 260}
diff --git a/auth.h b/auth.h
index 8c554b6a6..ee8a2b95c 100644
--- a/auth.h
+++ b/auth.h
@@ -53,6 +53,7 @@ struct Authctxt {
53 int valid; /* user exists and is allowed to login */ 53 int valid; /* user exists and is allowed to login */
54 int attempt; 54 int attempt;
55 int failures; 55 int failures;
56 int server_caused_failure;
56 int force_pwchange; 57 int force_pwchange;
57 char *user; /* username sent by the client */ 58 char *user; /* username sent by the client */
58 char *service; 59 char *service;
diff --git a/auth2-gss.c b/auth2-gss.c
index c77c841a3..50bdc6452 100644
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -52,6 +52,39 @@ static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
52static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); 52static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
53static void input_gssapi_errtok(int, u_int32_t, void *); 53static void input_gssapi_errtok(int, u_int32_t, void *);
54 54
55/*
56 * The 'gssapi_keyex' userauth mechanism.
57 */
58static int
59userauth_gsskeyex(Authctxt *authctxt)
60{
61 int authenticated = 0;
62 Buffer b;
63 gss_buffer_desc mic, gssbuf;
64 u_int len;
65
66 mic.value = packet_get_string(&len);
67 mic.length = len;
68
69 packet_check_eom();
70
71 ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
72 "gssapi-keyex");
73
74 gssbuf.value = buffer_ptr(&b);
75 gssbuf.length = buffer_len(&b);
76
77 /* gss_kex_context is NULL with privsep, so we can't check it here */
78 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
79 &gssbuf, &mic))))
80 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
81
82 buffer_free(&b);
83 xfree(mic.value);
84
85 return (authenticated);
86}
87
55/* 88/*
56 * We only support those mechanisms that we know about (ie ones that we know 89 * We only support those mechanisms that we know about (ie ones that we know
57 * how to check local user kuserok and the like) 90 * how to check local user kuserok and the like)
@@ -102,6 +135,7 @@ userauth_gssapi(Authctxt *authctxt)
102 135
103 if (!present) { 136 if (!present) {
104 xfree(doid); 137 xfree(doid);
138 authctxt->server_caused_failure = 1;
105 return (0); 139 return (0);
106 } 140 }
107 141
@@ -109,6 +143,7 @@ userauth_gssapi(Authctxt *authctxt)
109 if (ctxt != NULL) 143 if (ctxt != NULL)
110 ssh_gssapi_delete_ctx(&ctxt); 144 ssh_gssapi_delete_ctx(&ctxt);
111 xfree(doid); 145 xfree(doid);
146 authctxt->server_caused_failure = 1;
112 return (0); 147 return (0);
113 } 148 }
114 149
@@ -292,6 +327,12 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
292 userauth_finish(authctxt, authenticated, "gssapi-with-mic"); 327 userauth_finish(authctxt, authenticated, "gssapi-with-mic");
293} 328}
294 329
330Authmethod method_gsskeyex = {
331 "gssapi-keyex",
332 userauth_gsskeyex,
333 &options.gss_authentication
334};
335
295Authmethod method_gssapi = { 336Authmethod method_gssapi = {
296 "gssapi-with-mic", 337 "gssapi-with-mic",
297 userauth_gssapi, 338 userauth_gssapi,
diff --git a/auth2.c b/auth2.c
index bded8c2f8..828a7bc1a 100644
--- a/auth2.c
+++ b/auth2.c
@@ -64,6 +64,7 @@ extern Authmethod method_passwd;
64extern Authmethod method_kbdint; 64extern Authmethod method_kbdint;
65extern Authmethod method_hostbased; 65extern Authmethod method_hostbased;
66#ifdef GSSAPI 66#ifdef GSSAPI
67extern Authmethod method_gsskeyex;
67extern Authmethod method_gssapi; 68extern Authmethod method_gssapi;
68#endif 69#endif
69 70
@@ -71,6 +72,7 @@ Authmethod *authmethods[] = {
71 &method_none, 72 &method_none,
72 &method_pubkey, 73 &method_pubkey,
73#ifdef GSSAPI 74#ifdef GSSAPI
75 &method_gsskeyex,
74 &method_gssapi, 76 &method_gssapi,
75#endif 77#endif
76 &method_passwd, 78 &method_passwd,
@@ -195,6 +197,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
195#endif 197#endif
196 198
197 authctxt->postponed = 0; 199 authctxt->postponed = 0;
200 authctxt->server_caused_failure = 0;
198 201
199 /* try to authenticate user */ 202 /* try to authenticate user */
200 m = authmethod_lookup(method); 203 m = authmethod_lookup(method);
@@ -265,7 +268,9 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
265 /* now we can break out */ 268 /* now we can break out */
266 authctxt->success = 1; 269 authctxt->success = 1;
267 } else { 270 } else {
268 if (authctxt->failures++ > options.max_authtries) { 271 /* Dont count server configuration issues against the client */
272 if (!authctxt->server_caused_failure &&
273 authctxt->failures++ > options.max_authtries) {
269#ifdef SSH_AUDIT_EVENTS 274#ifdef SSH_AUDIT_EVENTS
270 PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); 275 PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES));
271#endif 276#endif
diff --git a/configure.ac b/configure.ac
index f1052b079..a9eb8e76d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -412,7 +412,31 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
412 [Use tunnel device compatibility to OpenBSD]) 412 [Use tunnel device compatibility to OpenBSD])
413 AC_DEFINE(SSH_TUN_PREPEND_AF, 1, 413 AC_DEFINE(SSH_TUN_PREPEND_AF, 1,
414 [Prepend the address family to IP tunnel traffic]) 414 [Prepend the address family to IP tunnel traffic])
415 ;; 415 AC_MSG_CHECKING(if we have the Security Authorization Session API)
416 AC_TRY_COMPILE([#include <Security/AuthSession.h>],
417 [SessionCreate(0, 0);],
418 [ac_cv_use_security_session_api="yes"
419 AC_DEFINE(USE_SECURITY_SESSION_API, 1,
420 [platform has the Security Authorization Session API])
421 LIBS="$LIBS -framework Security"
422 AC_MSG_RESULT(yes)],
423 [ac_cv_use_security_session_api="no"
424 AC_MSG_RESULT(no)])
425 AC_MSG_CHECKING(if we have an in-memory credentials cache)
426 AC_TRY_COMPILE(
427 [#include <Kerberos/Kerberos.h>],
428 [cc_context_t c;
429 (void) cc_initialize (&c, 0, NULL, NULL);],
430 [AC_DEFINE(USE_CCAPI, 1,
431 [platform uses an in-memory credentials cache])
432 LIBS="$LIBS -framework Security"
433 AC_MSG_RESULT(yes)
434 if test "x$ac_cv_use_security_session_api" = "xno"; then
435 AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***)
436 fi],
437 [AC_MSG_RESULT(no)]
438 )
439 ;;
416*-*-dragonfly*) 440*-*-dragonfly*)
417 SSHDLIBS="$SSHDLIBS -lcrypt" 441 SSHDLIBS="$SSHDLIBS -lcrypt"
418 ;; 442 ;;
diff --git a/gss-genr.c b/gss-genr.c
index e9190575d..822a08212 100644
--- a/gss-genr.c
+++ b/gss-genr.c
@@ -39,12 +39,160 @@
39#include "buffer.h" 39#include "buffer.h"
40#include "log.h" 40#include "log.h"
41#include "ssh2.h" 41#include "ssh2.h"
42#include "cipher.h"
43#include "key.h"
44#include "kex.h"
45#include <openssl/evp.h>
42 46
43#include "ssh-gss.h" 47#include "ssh-gss.h"
44 48
45extern u_char *session_id2; 49extern u_char *session_id2;
46extern u_int session_id2_len; 50extern u_int session_id2_len;
47 51
52typedef struct {
53 char *encoded;
54 gss_OID oid;
55} ssh_gss_kex_mapping;
56
57/*
58 * XXX - It would be nice to find a more elegant way of handling the
59 * XXX passing of the key exchange context to the userauth routines
60 */
61
62Gssctxt *gss_kex_context = NULL;
63
64static ssh_gss_kex_mapping *gss_enc2oid = NULL;
65
66int
67ssh_gssapi_oid_table_ok() {
68 return (gss_enc2oid != NULL);
69}
70
71/*
72 * Return a list of the gss-group1-sha1 mechanisms supported by this program
73 *
74 * We test mechanisms to ensure that we can use them, to avoid starting
75 * a key exchange with a bad mechanism
76 */
77
78char *
79ssh_gssapi_client_mechanisms(const char *host) {
80 gss_OID_set gss_supported;
81 OM_uint32 min_status;
82
83 gss_indicate_mechs(&min_status, &gss_supported);
84
85 return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
86 host));
87}
88
89char *
90ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
91 const char *data) {
92 Buffer buf;
93 size_t i;
94 int oidpos, enclen;
95 char *mechs, *encoded;
96 u_char digest[EVP_MAX_MD_SIZE];
97 char deroid[2];
98 const EVP_MD *evp_md = EVP_md5();
99 EVP_MD_CTX md;
100
101 if (gss_enc2oid != NULL) {
102 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
103 xfree(gss_enc2oid[i].encoded);
104 xfree(gss_enc2oid);
105 }
106
107 gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
108 (gss_supported->count + 1));
109
110 buffer_init(&buf);
111
112 oidpos = 0;
113 for (i = 0; i < gss_supported->count; i++) {
114 if (gss_supported->elements[i].length < 128 &&
115 (*check)(NULL, &(gss_supported->elements[i]), data)) {
116
117 deroid[0] = SSH_GSS_OIDTYPE;
118 deroid[1] = gss_supported->elements[i].length;
119
120 EVP_DigestInit(&md, evp_md);
121 EVP_DigestUpdate(&md, deroid, 2);
122 EVP_DigestUpdate(&md,
123 gss_supported->elements[i].elements,
124 gss_supported->elements[i].length);
125 EVP_DigestFinal(&md, digest, NULL);
126
127 encoded = xmalloc(EVP_MD_size(evp_md) * 2);
128 enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
129 encoded, EVP_MD_size(evp_md) * 2);
130
131 if (oidpos != 0)
132 buffer_put_char(&buf, ',');
133
134 buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
135 sizeof(KEX_GSS_GEX_SHA1_ID) - 1);
136 buffer_append(&buf, encoded, enclen);
137 buffer_put_char(&buf, ',');
138 buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID,
139 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1);
140 buffer_append(&buf, encoded, enclen);
141 buffer_put_char(&buf, ',');
142 buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
143 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1);
144 buffer_append(&buf, encoded, enclen);
145
146 gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
147 gss_enc2oid[oidpos].encoded = encoded;
148 oidpos++;
149 }
150 }
151 gss_enc2oid[oidpos].oid = NULL;
152 gss_enc2oid[oidpos].encoded = NULL;
153
154 buffer_put_char(&buf, '\0');
155
156 mechs = xmalloc(buffer_len(&buf));
157 buffer_get(&buf, mechs, buffer_len(&buf));
158 buffer_free(&buf);
159
160 if (strlen(mechs) == 0) {
161 xfree(mechs);
162 mechs = NULL;
163 }
164
165 return (mechs);
166}
167
168gss_OID
169ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
170 int i = 0;
171
172 switch (kex_type) {
173 case KEX_GSS_GRP1_SHA1:
174 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
175 break;
176 case KEX_GSS_GRP14_SHA1:
177 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
178 break;
179 case KEX_GSS_GEX_SHA1:
180 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
181 break;
182 default:
183 return GSS_C_NO_OID;
184 }
185
186 while (gss_enc2oid[i].encoded != NULL &&
187 strcmp(name, gss_enc2oid[i].encoded) != 0)
188 i++;
189
190 if (gss_enc2oid[i].oid != NULL && ctx != NULL)
191 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
192
193 return gss_enc2oid[i].oid;
194}
195
48/* Check that the OID in a data stream matches that in the context */ 196/* Check that the OID in a data stream matches that in the context */
49int 197int
50ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 198ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
@@ -229,6 +377,9 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
229OM_uint32 377OM_uint32
230ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 378ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
231{ 379{
380 if (ctx == NULL)
381 return -1;
382
232 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 383 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
233 GSS_C_QOP_DEFAULT, buffer, hash))) 384 GSS_C_QOP_DEFAULT, buffer, hash)))
234 ssh_gssapi_error(ctx); 385 ssh_gssapi_error(ctx);
@@ -236,6 +387,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
236 return (ctx->major); 387 return (ctx->major);
237} 388}
238 389
390/* Priviledged when used by server */
391OM_uint32
392ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
393{
394 if (ctx == NULL)
395 return -1;
396
397 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
398 gssbuf, gssmic, NULL);
399
400 return (ctx->major);
401}
402
239void 403void
240ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, 404ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
241 const char *context) 405 const char *context)
@@ -254,6 +418,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
254 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 418 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
255 OM_uint32 major, minor; 419 OM_uint32 major, minor;
256 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 420 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
421 Gssctxt *intctx = NULL;
422
423 if (ctx == NULL)
424 ctx = &intctx;
257 425
258 /* RFC 4462 says we MUST NOT do SPNEGO */ 426 /* RFC 4462 says we MUST NOT do SPNEGO */
259 if (oid->length == spnego_oid.length && 427 if (oid->length == spnego_oid.length &&
@@ -272,7 +440,7 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
272 GSS_C_NO_BUFFER); 440 GSS_C_NO_BUFFER);
273 } 441 }
274 442
275 if (GSS_ERROR(major)) 443 if (GSS_ERROR(major) || intctx != NULL)
276 ssh_gssapi_delete_ctx(ctx); 444 ssh_gssapi_delete_ctx(ctx);
277 445
278 return (!GSS_ERROR(major)); 446 return (!GSS_ERROR(major));
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
index 5a625acb8..b400081f6 100644
--- a/gss-serv-krb5.c
+++ b/gss-serv-krb5.c
@@ -120,6 +120,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
120 krb5_principal princ; 120 krb5_principal princ;
121 OM_uint32 maj_status, min_status; 121 OM_uint32 maj_status, min_status;
122 int len; 122 int len;
123 const char *new_ccname;
123 124
124 if (client->creds == NULL) { 125 if (client->creds == NULL) {
125 debug("No credentials stored"); 126 debug("No credentials stored");
@@ -168,11 +169,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
168 return; 169 return;
169 } 170 }
170 171
171 client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); 172 new_ccname = krb5_cc_get_name(krb_context, ccache);
173
172 client->store.envvar = "KRB5CCNAME"; 174 client->store.envvar = "KRB5CCNAME";
173 len = strlen(client->store.filename) + 6; 175#ifdef USE_CCAPI
174 client->store.envval = xmalloc(len); 176 xasprintf(&client->store.envval, "API:%s", new_ccname);
175 snprintf(client->store.envval, len, "FILE:%s", client->store.filename); 177 client->store.filename = NULL;
178#else
179 xasprintf(&client->store.envval, "FILE:%s", new_ccname);
180 client->store.filename = xstrdup(new_ccname);
181#endif
176 182
177#ifdef USE_PAM 183#ifdef USE_PAM
178 if (options.use_pam) 184 if (options.use_pam)
diff --git a/gss-serv.c b/gss-serv.c
index bc498fd47..e157ec515 100644
--- a/gss-serv.c
+++ b/gss-serv.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: gss-serv.c,v 1.21 2007/06/12 08:20:00 djm Exp $ */ 1/* $OpenBSD: gss-serv.c,v 1.21 2007/06/12 08:20:00 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 4 * Copyright (c) 2001-2006 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,8 +44,12 @@
44#include "channels.h" 44#include "channels.h"
45#include "session.h" 45#include "session.h"
46#include "misc.h" 46#include "misc.h"
47#include "servconf.h"
47 48
48#include "ssh-gss.h" 49#include "ssh-gss.h"
50#include "monitor_wrap.h"
51
52extern ServerOptions options;
49 53
50static ssh_gssapi_client gssapi_client = 54static ssh_gssapi_client gssapi_client =
51 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 55 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
@@ -113,6 +117,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
113} 117}
114 118
115/* Unprivileged */ 119/* Unprivileged */
120char *
121ssh_gssapi_server_mechanisms() {
122 gss_OID_set supported;
123
124 ssh_gssapi_supported_oids(&supported);
125 return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
126 NULL));
127}
128
129/* Unprivileged */
130int
131ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data) {
132 Gssctxt *ctx = NULL;
133 int res;
134
135 res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
136 ssh_gssapi_delete_ctx(&ctx);
137
138 return (res);
139}
140
141/* Unprivileged */
116void 142void
117ssh_gssapi_supported_oids(gss_OID_set *oidset) 143ssh_gssapi_supported_oids(gss_OID_set *oidset)
118{ 144{
@@ -351,14 +377,4 @@ ssh_gssapi_userok(char *user)
351 return (0); 377 return (0);
352} 378}
353 379
354/* Privileged */
355OM_uint32
356ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
357{
358 ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
359 gssbuf, gssmic, NULL);
360
361 return (ctx->major);
362}
363
364#endif 380#endif
diff --git a/kex.c b/kex.c
index 332fadf6e..5c8361bac 100644
--- a/kex.c
+++ b/kex.c
@@ -49,6 +49,10 @@
49#include "dispatch.h" 49#include "dispatch.h"
50#include "monitor.h" 50#include "monitor.h"
51 51
52#ifdef GSSAPI
53#include "ssh-gss.h"
54#endif
55
52#define KEX_COOKIE_LEN 16 56#define KEX_COOKIE_LEN 16
53 57
54#if OPENSSL_VERSION_NUMBER >= 0x00907000L 58#if OPENSSL_VERSION_NUMBER >= 0x00907000L
@@ -327,6 +331,20 @@ choose_kex(Kex *k, char *client, char *server)
327 k->kex_type = KEX_DH_GEX_SHA256; 331 k->kex_type = KEX_DH_GEX_SHA256;
328 k->evp_md = evp_ssh_sha256(); 332 k->evp_md = evp_ssh_sha256();
329#endif 333#endif
334#ifdef GSSAPI
335 } else if (strncmp(k->name, KEX_GSS_GEX_SHA1_ID,
336 sizeof(KEX_GSS_GEX_SHA1_ID) - 1) == 0) {
337 k->kex_type = KEX_GSS_GEX_SHA1;
338 k->evp_md = EVP_sha1();
339 } else if (strncmp(k->name, KEX_GSS_GRP1_SHA1_ID,
340 sizeof(KEX_GSS_GRP1_SHA1_ID) - 1) == 0) {
341 k->kex_type = KEX_GSS_GRP1_SHA1;
342 k->evp_md = EVP_sha1();
343 } else if (strncmp(k->name, KEX_GSS_GRP14_SHA1_ID,
344 sizeof(KEX_GSS_GRP14_SHA1_ID) - 1) == 0) {
345 k->kex_type = KEX_GSS_GRP14_SHA1;
346 k->evp_md = EVP_sha1();
347#endif
330 } else 348 } else
331 fatal("bad kex alg %s", k->name); 349 fatal("bad kex alg %s", k->name);
332} 350}
diff --git a/kex.h b/kex.h
index 8e29c90e9..bd763a074 100644
--- a/kex.h
+++ b/kex.h
@@ -64,6 +64,9 @@ enum kex_exchange {
64 KEX_DH_GRP14_SHA1, 64 KEX_DH_GRP14_SHA1,
65 KEX_DH_GEX_SHA1, 65 KEX_DH_GEX_SHA1,
66 KEX_DH_GEX_SHA256, 66 KEX_DH_GEX_SHA256,
67 KEX_GSS_GRP1_SHA1,
68 KEX_GSS_GRP14_SHA1,
69 KEX_GSS_GEX_SHA1,
67 KEX_MAX 70 KEX_MAX
68}; 71};
69 72
@@ -119,6 +122,11 @@ struct Kex {
119 sig_atomic_t done; 122 sig_atomic_t done;
120 int flags; 123 int flags;
121 const EVP_MD *evp_md; 124 const EVP_MD *evp_md;
125#ifdef GSSAPI
126 int gss_deleg_creds;
127 int gss_trust_dns;
128 char *gss_host;
129#endif
122 char *client_version_string; 130 char *client_version_string;
123 char *server_version_string; 131 char *server_version_string;
124 int (*verify_host_key)(Key *); 132 int (*verify_host_key)(Key *);
@@ -141,6 +149,11 @@ void kexdh_server(Kex *);
141void kexgex_client(Kex *); 149void kexgex_client(Kex *);
142void kexgex_server(Kex *); 150void kexgex_server(Kex *);
143 151
152#ifdef GSSAPI
153void kexgss_client(Kex *);
154void kexgss_server(Kex *);
155#endif
156
144void 157void
145kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, 158kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int,
146 BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); 159 BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *);
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..7c4a56f45
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,319 @@
1/*
2 * Copyright (c) 2001-2006 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include "includes.h"
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include <string.h>
35
36#include "xmalloc.h"
37#include "buffer.h"
38#include "ssh2.h"
39#include "key.h"
40#include "cipher.h"
41#include "kex.h"
42#include "log.h"
43#include "packet.h"
44#include "dh.h"
45
46#include "ssh-gss.h"
47
48void
49kexgss_client(Kex *kex) {
50 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
51 gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr;
52 Gssctxt *ctxt;
53 OM_uint32 maj_status, min_status, ret_flags;
54 u_int klen, kout, slen = 0, hashlen, strlen;
55 DH *dh;
56 BIGNUM *dh_server_pub = NULL;
57 BIGNUM *shared_secret = NULL;
58 BIGNUM *p = NULL;
59 BIGNUM *g = NULL;
60 u_char *kbuf, *hash;
61 u_char *serverhostkey = NULL;
62 char *msg;
63 char *lang;
64 int type = 0;
65 int first = 1;
66 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
67
68 /* Initialise our GSSAPI world */
69 ssh_gssapi_build_ctx(&ctxt);
70 if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
71 == GSS_C_NO_OID)
72 fatal("Couldn't identify host exchange");
73
74 if (ssh_gssapi_import_name(ctxt, kex->gss_host))
75 fatal("Couldn't import hostname");
76
77 switch (kex->kex_type) {
78 case KEX_GSS_GRP1_SHA1:
79 dh = dh_new_group1();
80 break;
81 case KEX_GSS_GRP14_SHA1:
82 dh = dh_new_group14();
83 break;
84 case KEX_GSS_GEX_SHA1:
85 debug("Doing group exchange\n");
86 nbits = dh_estimate(kex->we_need * 8);
87 packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
88 packet_put_int(min);
89 packet_put_int(nbits);
90 packet_put_int(max);
91
92 packet_send();
93
94 packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
95
96 if ((p = BN_new()) == NULL)
97 fatal("BN_new() failed");
98 packet_get_bignum2(p);
99 if ((g = BN_new()) == NULL)
100 fatal("BN_new() failed");
101 packet_get_bignum2(g);
102 packet_check_eom();
103
104 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
105 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
106 min, BN_num_bits(p), max);
107
108 dh = dh_new_group(g, p);
109 break;
110 default:
111 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
112 }
113
114 /* Step 1 - e is dh->pub_key */
115 dh_gen_key(dh, kex->we_need * 8);
116
117 /* This is f, we initialise it now to make life easier */
118 dh_server_pub = BN_new();
119 if (dh_server_pub == NULL)
120 fatal("dh_server_pub == NULL");
121
122 token_ptr = GSS_C_NO_BUFFER;
123
124 do {
125 debug("Calling gss_init_sec_context");
126
127 maj_status = ssh_gssapi_init_ctx(ctxt,
128 kex->gss_deleg_creds, token_ptr, &send_tok,
129 &ret_flags);
130
131 if (GSS_ERROR(maj_status)) {
132 if (send_tok.length != 0) {
133 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
134 packet_put_string(send_tok.value,
135 send_tok.length);
136 }
137 fatal("gss_init_context failed");
138 }
139
140 /* If we've got an old receive buffer get rid of it */
141 if (token_ptr != GSS_C_NO_BUFFER)
142 xfree(recv_tok.value);
143
144 if (maj_status == GSS_S_COMPLETE) {
145 /* If mutual state flag is not true, kex fails */
146 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
147 fatal("Mutual authentication failed");
148
149 /* If integ avail flag is not true kex fails */
150 if (!(ret_flags & GSS_C_INTEG_FLAG))
151 fatal("Integrity check failed");
152 }
153
154 /*
155 * If we have data to send, then the last message that we
156 * received cannot have been a 'complete'.
157 */
158 if (send_tok.length != 0) {
159 if (first) {
160 packet_start(SSH2_MSG_KEXGSS_INIT);
161 packet_put_string(send_tok.value,
162 send_tok.length);
163 packet_put_bignum2(dh->pub_key);
164 first = 0;
165 } else {
166 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
167 packet_put_string(send_tok.value,
168 send_tok.length);
169 }
170 packet_send();
171 gss_release_buffer(&min_status, &send_tok);
172
173 /* If we've sent them data, they should reply */
174 do {
175 type = packet_read();
176 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
177 debug("Received KEXGSS_HOSTKEY");
178 if (serverhostkey)
179 fatal("Server host key received more than once");
180 serverhostkey =
181 packet_get_string(&slen);
182 }
183 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
184
185 switch (type) {
186 case SSH2_MSG_KEXGSS_CONTINUE:
187 debug("Received GSSAPI_CONTINUE");
188 if (maj_status == GSS_S_COMPLETE)
189 fatal("GSSAPI Continue received from server when complete");
190 recv_tok.value = packet_get_string(&strlen);
191 recv_tok.length = strlen;
192 break;
193 case SSH2_MSG_KEXGSS_COMPLETE:
194 debug("Received GSSAPI_COMPLETE");
195 packet_get_bignum2(dh_server_pub);
196 msg_tok.value = packet_get_string(&strlen);
197 msg_tok.length = strlen;
198
199 /* Is there a token included? */
200 if (packet_get_char()) {
201 recv_tok.value=
202 packet_get_string(&strlen);
203 recv_tok.length = strlen;
204 /* If we're already complete - protocol error */
205 if (maj_status == GSS_S_COMPLETE)
206 packet_disconnect("Protocol error: received token when complete");
207 } else {
208 /* No token included */
209 if (maj_status != GSS_S_COMPLETE)
210 packet_disconnect("Protocol error: did not receive final token");
211 }
212 break;
213 case SSH2_MSG_KEXGSS_ERROR:
214 debug("Received Error");
215 maj_status = packet_get_int();
216 min_status = packet_get_int();
217 msg = packet_get_string(NULL);
218 lang = packet_get_string(NULL);
219 fatal("GSSAPI Error: \n%.400s",msg);
220 default:
221 packet_disconnect("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 dh_server_pub 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 /* Check f in range [1, p-1] */
241 if (!dh_pub_is_valid(dh, dh_server_pub))
242 packet_disconnect("bad server public DH value");
243
244 /* compute K=f^x mod p */
245 klen = DH_size(dh);
246 kbuf = xmalloc(klen);
247 kout = DH_compute_key(kbuf, dh_server_pub, dh);
248
249 shared_secret = BN_new();
250 BN_bin2bn(kbuf,kout, shared_secret);
251 memset(kbuf, 0, klen);
252 xfree(kbuf);
253
254 switch (kex->kex_type) {
255 case KEX_GSS_GRP1_SHA1:
256 case KEX_GSS_GRP14_SHA1:
257 kex_dh_hash( kex->client_version_string,
258 kex->server_version_string,
259 buffer_ptr(&kex->my), buffer_len(&kex->my),
260 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
261 serverhostkey, slen, /* server host key */
262 dh->pub_key, /* e */
263 dh_server_pub, /* f */
264 shared_secret, /* K */
265 &hash, &hashlen
266 );
267 break;
268 case KEX_GSS_GEX_SHA1:
269 kexgex_hash(
270 kex->evp_md,
271 kex->client_version_string,
272 kex->server_version_string,
273 buffer_ptr(&kex->my), buffer_len(&kex->my),
274 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
275 serverhostkey, slen,
276 min, nbits, max,
277 dh->p, dh->g,
278 dh->pub_key,
279 dh_server_pub,
280 shared_secret,
281 &hash, &hashlen
282 );
283 break;
284 default:
285 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
286 }
287
288 gssbuf.value = hash;
289 gssbuf.length = hashlen;
290
291 /* Verify that the hash matches the MIC we just got. */
292 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
293 packet_disconnect("Hash's MIC didn't verify");
294
295 xfree(msg_tok.value);
296
297 DH_free(dh);
298 if (serverhostkey)
299 xfree(serverhostkey);
300 BN_clear_free(dh_server_pub);
301
302 /* save session id */
303 if (kex->session_id == NULL) {
304 kex->session_id_len = hashlen;
305 kex->session_id = xmalloc(kex->session_id_len);
306 memcpy(kex->session_id, hash, kex->session_id_len);
307 }
308
309 if (gss_kex_context == NULL)
310 gss_kex_context = ctxt;
311 else
312 ssh_gssapi_delete_ctx(&ctxt);
313
314 kex_derive_keys(kex, hash, hashlen, shared_secret);
315 BN_clear_free(shared_secret);
316 kex_finish(kex);
317}
318
319#endif /* GSSAPI */
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..3ca23bbb2
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,271 @@
1/*
2 * Copyright (c) 2001-2006 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include <string.h>
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include "xmalloc.h"
35#include "buffer.h"
36#include "ssh2.h"
37#include "key.h"
38#include "cipher.h"
39#include "kex.h"
40#include "log.h"
41#include "packet.h"
42#include "dh.h"
43#include "ssh-gss.h"
44#include "monitor_wrap.h"
45
46void
47kexgss_server(Kex *kex)
48{
49 OM_uint32 maj_status, min_status;
50
51 /*
52 * Some GSSAPI implementations use the input value of ret_flags (an
53 * output variable) as a means of triggering mechanism specific
54 * features. Initializing it to zero avoids inadvertently
55 * activating this non-standard behaviour.
56 */
57
58 OM_uint32 ret_flags = 0;
59 gss_buffer_desc gssbuf, recv_tok, msg_tok;
60 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
61 Gssctxt *ctxt = NULL;
62 u_int slen, klen, kout, hashlen;
63 u_char *kbuf, *hash;
64 DH *dh;
65 int min = -1, max = -1, nbits = -1;
66 BIGNUM *shared_secret = NULL;
67 BIGNUM *dh_client_pub = NULL;
68 int type = 0;
69 gss_OID oid;
70
71 /* Initialise GSSAPI */
72
73 /* If we're rekeying, privsep means that some of the private structures
74 * in the GSSAPI code are no longer available. This kludges them back
75 * into life
76 */
77 if (!ssh_gssapi_oid_table_ok())
78 ssh_gssapi_server_mechanisms();
79
80 debug2("%s: Identifying %s", __func__, kex->name);
81 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
82 if (oid == GSS_C_NO_OID)
83 fatal("Unknown gssapi mechanism");
84
85 debug2("%s: Acquiring credentials", __func__);
86
87 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
88 fatal("Unable to acquire credentials for the server");
89
90 switch (kex->kex_type) {
91 case KEX_GSS_GRP1_SHA1:
92 dh = dh_new_group1();
93 break;
94 case KEX_GSS_GRP14_SHA1:
95 dh = dh_new_group14();
96 break;
97 case KEX_GSS_GEX_SHA1:
98 debug("Doing group exchange");
99 packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
100 min = packet_get_int();
101 nbits = packet_get_int();
102 max = packet_get_int();
103 min = MAX(DH_GRP_MIN, min);
104 max = MIN(DH_GRP_MAX, max);
105 packet_check_eom();
106 if (max < min || nbits < min || max < nbits)
107 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
108 min, nbits, max);
109 dh = PRIVSEP(choose_dh(min, nbits, max));
110 if (dh == NULL)
111 packet_disconnect("Protocol error: no matching group found");
112
113 packet_start(SSH2_MSG_KEXGSS_GROUP);
114 packet_put_bignum2(dh->p);
115 packet_put_bignum2(dh->g);
116 packet_send();
117
118 packet_write_wait();
119 break;
120 default:
121 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
122 }
123
124 dh_gen_key(dh, kex->we_need * 8);
125
126 do {
127 debug("Wait SSH2_MSG_GSSAPI_INIT");
128 type = packet_read();
129 switch(type) {
130 case SSH2_MSG_KEXGSS_INIT:
131 if (dh_client_pub != NULL)
132 fatal("Received KEXGSS_INIT after initialising");
133 recv_tok.value = packet_get_string(&slen);
134 recv_tok.length = slen;
135
136 if ((dh_client_pub = BN_new()) == NULL)
137 fatal("dh_client_pub == NULL");
138
139 packet_get_bignum2(dh_client_pub);
140
141 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
142 break;
143 case SSH2_MSG_KEXGSS_CONTINUE:
144 recv_tok.value = packet_get_string(&slen);
145 recv_tok.length = slen;
146 break;
147 default:
148 packet_disconnect(
149 "Protocol error: didn't expect packet type %d",
150 type);
151 }
152
153 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
154 &send_tok, &ret_flags));
155
156 xfree(recv_tok.value);
157
158 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
159 fatal("Zero length token output when incomplete");
160
161 if (dh_client_pub == NULL)
162 fatal("No client public key");
163
164 if (maj_status & GSS_S_CONTINUE_NEEDED) {
165 debug("Sending GSSAPI_CONTINUE");
166 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
167 packet_put_string(send_tok.value, send_tok.length);
168 packet_send();
169 gss_release_buffer(&min_status, &send_tok);
170 }
171 } while (maj_status & GSS_S_CONTINUE_NEEDED);
172
173 if (GSS_ERROR(maj_status)) {
174 if (send_tok.length > 0) {
175 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
176 packet_put_string(send_tok.value, send_tok.length);
177 packet_send();
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 if (!dh_pub_is_valid(dh, dh_client_pub))
189 packet_disconnect("bad client public DH value");
190
191 klen = DH_size(dh);
192 kbuf = xmalloc(klen);
193 kout = DH_compute_key(kbuf, dh_client_pub, dh);
194
195 shared_secret = BN_new();
196 BN_bin2bn(kbuf, kout, shared_secret);
197 memset(kbuf, 0, klen);
198 xfree(kbuf);
199
200 switch (kex->kex_type) {
201 case KEX_GSS_GRP1_SHA1:
202 case KEX_GSS_GRP14_SHA1:
203 kex_dh_hash(
204 kex->client_version_string, kex->server_version_string,
205 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
206 buffer_ptr(&kex->my), buffer_len(&kex->my),
207 NULL, 0, /* Change this if we start sending host keys */
208 dh_client_pub, dh->pub_key, shared_secret,
209 &hash, &hashlen
210 );
211 break;
212 case KEX_GSS_GEX_SHA1:
213 kexgex_hash(
214 kex->evp_md,
215 kex->client_version_string, kex->server_version_string,
216 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
217 buffer_ptr(&kex->my), buffer_len(&kex->my),
218 NULL, 0,
219 min, nbits, max,
220 dh->p, dh->g,
221 dh_client_pub,
222 dh->pub_key,
223 shared_secret,
224 &hash, &hashlen
225 );
226 break;
227 default:
228 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
229 }
230
231 BN_free(dh_client_pub);
232
233 if (kex->session_id == NULL) {
234 kex->session_id_len = hashlen;
235 kex->session_id = xmalloc(kex->session_id_len);
236 memcpy(kex->session_id, hash, kex->session_id_len);
237 }
238
239 gssbuf.value = hash;
240 gssbuf.length = hashlen;
241
242 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
243 fatal("Couldn't get MIC");
244
245 packet_start(SSH2_MSG_KEXGSS_COMPLETE);
246 packet_put_bignum2(dh->pub_key);
247 packet_put_string(msg_tok.value,msg_tok.length);
248
249 if (send_tok.length != 0) {
250 packet_put_char(1); /* true */
251 packet_put_string(send_tok.value, send_tok.length);
252 } else {
253 packet_put_char(0); /* false */
254 }
255 packet_send();
256
257 gss_release_buffer(&min_status, &send_tok);
258 gss_release_buffer(&min_status, &msg_tok);
259
260 if (gss_kex_context == NULL)
261 gss_kex_context = ctxt;
262 else
263 ssh_gssapi_delete_ctx(&ctxt);
264
265 DH_free(dh);
266
267 kex_derive_keys(kex, hash, hashlen, shared_secret);
268 BN_clear_free(shared_secret);
269 kex_finish(kex);
270}
271#endif /* GSSAPI */
diff --git a/key.c b/key.c
index 8fef9b40f..06b15d65c 100644
--- a/key.c
+++ b/key.c
@@ -648,6 +648,8 @@ key_type_from_name(char *name)
648 return KEY_RSA; 648 return KEY_RSA;
649 } else if (strcmp(name, "ssh-dss") == 0) { 649 } else if (strcmp(name, "ssh-dss") == 0) {
650 return KEY_DSA; 650 return KEY_DSA;
651 } else if (strcmp(name, "null") == 0) {
652 return KEY_NULL;
651 } 653 }
652 debug2("key_type_from_name: unknown key type '%s'", name); 654 debug2("key_type_from_name: unknown key type '%s'", name);
653 return KEY_UNSPEC; 655 return KEY_UNSPEC;
diff --git a/key.h b/key.h
index 6873dd793..40576f3d7 100644
--- a/key.h
+++ b/key.h
@@ -34,6 +34,7 @@ enum types {
34 KEY_RSA1, 34 KEY_RSA1,
35 KEY_RSA, 35 KEY_RSA,
36 KEY_DSA, 36 KEY_DSA,
37 KEY_NULL,
37 KEY_UNSPEC 38 KEY_UNSPEC
38}; 39};
39enum fp_type { 40enum fp_type {
diff --git a/monitor.c b/monitor.c
index 08c7ea3cb..d512d0b36 100644
--- a/monitor.c
+++ b/monitor.c
@@ -163,6 +163,7 @@ int mm_answer_gss_setup_ctx(int, Buffer *);
163int mm_answer_gss_accept_ctx(int, Buffer *); 163int mm_answer_gss_accept_ctx(int, Buffer *);
164int mm_answer_gss_userok(int, Buffer *); 164int mm_answer_gss_userok(int, Buffer *);
165int mm_answer_gss_checkmic(int, Buffer *); 165int mm_answer_gss_checkmic(int, Buffer *);
166int mm_answer_gss_sign(int, Buffer *);
166#endif 167#endif
167 168
168#ifdef SSH_AUDIT_EVENTS 169#ifdef SSH_AUDIT_EVENTS
@@ -232,11 +233,17 @@ struct mon_table mon_dispatch_proto20[] = {
232 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, 233 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
233 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, 234 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
234 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, 235 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
236 {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
235#endif 237#endif
236 {0, 0, NULL} 238 {0, 0, NULL}
237}; 239};
238 240
239struct mon_table mon_dispatch_postauth20[] = { 241struct mon_table mon_dispatch_postauth20[] = {
242#ifdef GSSAPI
243 {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
244 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
245 {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
246#endif
240 {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, 247 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
241 {MONITOR_REQ_SIGN, 0, mm_answer_sign}, 248 {MONITOR_REQ_SIGN, 0, mm_answer_sign},
242 {MONITOR_REQ_PTY, 0, mm_answer_pty}, 249 {MONITOR_REQ_PTY, 0, mm_answer_pty},
@@ -341,6 +348,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
341 /* Permit requests for moduli and signatures */ 348 /* Permit requests for moduli and signatures */
342 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 349 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
343 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 350 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
351#ifdef GSSAPI
352 /* and for the GSSAPI key exchange */
353 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
354#endif
344 } else { 355 } else {
345 mon_dispatch = mon_dispatch_proto15; 356 mon_dispatch = mon_dispatch_proto15;
346 357
@@ -418,6 +429,10 @@ monitor_child_postauth(struct monitor *pmonitor)
418 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); 429 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
419 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); 430 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
420 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 431 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
432#ifdef GSSAPI
433 /* and for the GSSAPI key exchange */
434 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
435#endif
421 } else { 436 } else {
422 mon_dispatch = mon_dispatch_postauth15; 437 mon_dispatch = mon_dispatch_postauth15;
423 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); 438 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
@@ -1664,6 +1679,11 @@ mm_get_kex(Buffer *m)
1664 kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; 1679 kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
1665 kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; 1680 kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
1666 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; 1681 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
1682#ifdef GSSAPI
1683 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
1684 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
1685 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
1686#endif
1667 kex->server = 1; 1687 kex->server = 1;
1668 kex->hostkey_type = buffer_get_int(m); 1688 kex->hostkey_type = buffer_get_int(m);
1669 kex->kex_type = buffer_get_int(m); 1689 kex->kex_type = buffer_get_int(m);
@@ -1905,6 +1925,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m)
1905 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); 1925 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
1906 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); 1926 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
1907 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); 1927 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
1928 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
1908 } 1929 }
1909 return (0); 1930 return (0);
1910} 1931}
@@ -1955,4 +1976,42 @@ mm_answer_gss_userok(int sock, Buffer *m)
1955 /* Monitor loop will terminate if authenticated */ 1976 /* Monitor loop will terminate if authenticated */
1956 return (authenticated); 1977 return (authenticated);
1957} 1978}
1979
1980int
1981mm_answer_gss_sign(int socket, Buffer *m)
1982{
1983 gss_buffer_desc data;
1984 gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
1985 OM_uint32 major, minor;
1986 u_int len;
1987
1988 data.value = buffer_get_string(m, &len);
1989 data.length = len;
1990 if (data.length != 20)
1991 fatal("%s: data length incorrect: %d", __func__, data.length);
1992
1993 /* Save the session ID on the first time around */
1994 if (session_id2_len == 0) {
1995 session_id2_len = data.length;
1996 session_id2 = xmalloc(session_id2_len);
1997 memcpy(session_id2, data.value, session_id2_len);
1998 }
1999 major = ssh_gssapi_sign(gsscontext, &data, &hash);
2000
2001 xfree(data.value);
2002
2003 buffer_clear(m);
2004 buffer_put_int(m, major);
2005 buffer_put_string(m, hash.value, hash.length);
2006
2007 mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2008
2009 gss_release_buffer(&minor, &hash);
2010
2011 /* Turn on getpwnam permissions */
2012 monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2013
2014 return (0);
2015}
2016
1958#endif /* GSSAPI */ 2017#endif /* GSSAPI */
diff --git a/monitor.h b/monitor.h
index 464009ad8..5bf7d01c0 100644
--- a/monitor.h
+++ b/monitor.h
@@ -53,6 +53,7 @@ enum monitor_reqtype {
53 MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP, 53 MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP,
54 MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK, 54 MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK,
55 MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC, 55 MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC,
56 MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN,
56 MONITOR_REQ_PAM_START, 57 MONITOR_REQ_PAM_START,
57 MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT, 58 MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT,
58 MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX, 59 MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX,
diff --git a/monitor_wrap.c b/monitor_wrap.c
index edf2814e5..eff912562 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1236,4 +1236,27 @@ mm_ssh_gssapi_userok(char *user)
1236 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); 1236 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
1237 return (authenticated); 1237 return (authenticated);
1238} 1238}
1239
1240OM_uint32
1241mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
1242{
1243 Buffer m;
1244 OM_uint32 major;
1245 u_int len;
1246
1247 buffer_init(&m);
1248 buffer_put_string(&m, data->value, data->length);
1249
1250 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
1251 mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
1252
1253 major = buffer_get_int(&m);
1254 hash->value = buffer_get_string(&m, &len);
1255 hash->length = len;
1256
1257 buffer_free(&m);
1258
1259 return(major);
1260}
1261
1239#endif /* GSSAPI */ 1262#endif /* GSSAPI */
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 329189c2a..2a7bf7c87 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -59,6 +59,7 @@ OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
59 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); 59 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
60int mm_ssh_gssapi_userok(char *user); 60int mm_ssh_gssapi_userok(char *user);
61OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 61OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
62OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
62#endif 63#endif
63 64
64#ifdef USE_PAM 65#ifdef USE_PAM
diff --git a/readconf.c b/readconf.c
index d57d4551d..3f82345f6 100644
--- a/readconf.c
+++ b/readconf.c
@@ -127,6 +127,8 @@ typedef enum {
127 oClearAllForwardings, oNoHostAuthenticationForLocalhost, 127 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
128 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, 128 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
129 oAddressFamily, oGssAuthentication, oGssDelegateCreds, 129 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
130 oGssKeyEx,
131 oGssTrustDns,
130 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 132 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
131 oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, 133 oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
132 oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, 134 oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
@@ -163,10 +165,14 @@ static struct {
163 { "afstokenpassing", oUnsupported }, 165 { "afstokenpassing", oUnsupported },
164#if defined(GSSAPI) 166#if defined(GSSAPI)
165 { "gssapiauthentication", oGssAuthentication }, 167 { "gssapiauthentication", oGssAuthentication },
168 { "gssapikeyexchange", oGssKeyEx },
166 { "gssapidelegatecredentials", oGssDelegateCreds }, 169 { "gssapidelegatecredentials", oGssDelegateCreds },
170 { "gssapitrustdns", oGssTrustDns },
167#else 171#else
168 { "gssapiauthentication", oUnsupported }, 172 { "gssapiauthentication", oUnsupported },
173 { "gssapikeyexchange", oUnsupported },
169 { "gssapidelegatecredentials", oUnsupported }, 174 { "gssapidelegatecredentials", oUnsupported },
175 { "gssapitrustdns", oUnsupported },
170#endif 176#endif
171 { "fallbacktorsh", oDeprecated }, 177 { "fallbacktorsh", oDeprecated },
172 { "usersh", oDeprecated }, 178 { "usersh", oDeprecated },
@@ -441,10 +447,18 @@ parse_flag:
441 intptr = &options->gss_authentication; 447 intptr = &options->gss_authentication;
442 goto parse_flag; 448 goto parse_flag;
443 449
450 case oGssKeyEx:
451 intptr = &options->gss_keyex;
452 goto parse_flag;
453
444 case oGssDelegateCreds: 454 case oGssDelegateCreds:
445 intptr = &options->gss_deleg_creds; 455 intptr = &options->gss_deleg_creds;
446 goto parse_flag; 456 goto parse_flag;
447 457
458 case oGssTrustDns:
459 intptr = &options->gss_trust_dns;
460 goto parse_flag;
461
448 case oBatchMode: 462 case oBatchMode:
449 intptr = &options->batch_mode; 463 intptr = &options->batch_mode;
450 goto parse_flag; 464 goto parse_flag;
@@ -1010,7 +1024,9 @@ initialize_options(Options * options)
1010 options->pubkey_authentication = -1; 1024 options->pubkey_authentication = -1;
1011 options->challenge_response_authentication = -1; 1025 options->challenge_response_authentication = -1;
1012 options->gss_authentication = -1; 1026 options->gss_authentication = -1;
1027 options->gss_keyex = -1;
1013 options->gss_deleg_creds = -1; 1028 options->gss_deleg_creds = -1;
1029 options->gss_trust_dns = -1;
1014 options->password_authentication = -1; 1030 options->password_authentication = -1;
1015 options->kbd_interactive_authentication = -1; 1031 options->kbd_interactive_authentication = -1;
1016 options->kbd_interactive_devices = NULL; 1032 options->kbd_interactive_devices = NULL;
@@ -1099,8 +1115,12 @@ fill_default_options(Options * options)
1099 options->challenge_response_authentication = 1; 1115 options->challenge_response_authentication = 1;
1100 if (options->gss_authentication == -1) 1116 if (options->gss_authentication == -1)
1101 options->gss_authentication = 0; 1117 options->gss_authentication = 0;
1118 if (options->gss_keyex == -1)
1119 options->gss_keyex = 0;
1102 if (options->gss_deleg_creds == -1) 1120 if (options->gss_deleg_creds == -1)
1103 options->gss_deleg_creds = 0; 1121 options->gss_deleg_creds = 0;
1122 if (options->gss_trust_dns == -1)
1123 options->gss_trust_dns = 0;
1104 if (options->password_authentication == -1) 1124 if (options->password_authentication == -1)
1105 options->password_authentication = 1; 1125 options->password_authentication = 1;
1106 if (options->kbd_interactive_authentication == -1) 1126 if (options->kbd_interactive_authentication == -1)
diff --git a/readconf.h b/readconf.h
index d484f258e..a3d302420 100644
--- a/readconf.h
+++ b/readconf.h
@@ -44,7 +44,9 @@ typedef struct {
44 int challenge_response_authentication; 44 int challenge_response_authentication;
45 /* Try S/Key or TIS, authentication. */ 45 /* Try S/Key or TIS, authentication. */
46 int gss_authentication; /* Try GSS authentication */ 46 int gss_authentication; /* Try GSS authentication */
47 int gss_keyex; /* Try GSS key exchange */
47 int gss_deleg_creds; /* Delegate GSS credentials */ 48 int gss_deleg_creds; /* Delegate GSS credentials */
49 int gss_trust_dns; /* Trust DNS for GSS canonicalization */
48 int password_authentication; /* Try password 50 int password_authentication; /* Try password
49 * authentication. */ 51 * authentication. */
50 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 52 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
diff --git a/servconf.c b/servconf.c
index 1a7545171..f56cc1803 100644
--- a/servconf.c
+++ b/servconf.c
@@ -90,7 +90,9 @@ initialize_server_options(ServerOptions *options)
90 options->kerberos_ticket_cleanup = -1; 90 options->kerberos_ticket_cleanup = -1;
91 options->kerberos_get_afs_token = -1; 91 options->kerberos_get_afs_token = -1;
92 options->gss_authentication=-1; 92 options->gss_authentication=-1;
93 options->gss_keyex = -1;
93 options->gss_cleanup_creds = -1; 94 options->gss_cleanup_creds = -1;
95 options->gss_strict_acceptor = -1;
94 options->password_authentication = -1; 96 options->password_authentication = -1;
95 options->kbd_interactive_authentication = -1; 97 options->kbd_interactive_authentication = -1;
96 options->challenge_response_authentication = -1; 98 options->challenge_response_authentication = -1;
@@ -204,8 +206,12 @@ fill_default_server_options(ServerOptions *options)
204 options->kerberos_get_afs_token = 0; 206 options->kerberos_get_afs_token = 0;
205 if (options->gss_authentication == -1) 207 if (options->gss_authentication == -1)
206 options->gss_authentication = 0; 208 options->gss_authentication = 0;
209 if (options->gss_keyex == -1)
210 options->gss_keyex = 0;
207 if (options->gss_cleanup_creds == -1) 211 if (options->gss_cleanup_creds == -1)
208 options->gss_cleanup_creds = 1; 212 options->gss_cleanup_creds = 1;
213 if (options->gss_strict_acceptor == -1)
214 options->gss_strict_acceptor = 1;
209 if (options->password_authentication == -1) 215 if (options->password_authentication == -1)
210 options->password_authentication = 1; 216 options->password_authentication = 1;
211 if (options->kbd_interactive_authentication == -1) 217 if (options->kbd_interactive_authentication == -1)
@@ -290,7 +296,9 @@ typedef enum {
290 sBanner, sUseDNS, sHostbasedAuthentication, 296 sBanner, sUseDNS, sHostbasedAuthentication,
291 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, 297 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
292 sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, 298 sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
293 sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, 299 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
300 sGssKeyEx,
301 sAcceptEnv, sPermitTunnel,
294 sMatch, sPermitOpen, sForceCommand, 302 sMatch, sPermitOpen, sForceCommand,
295 sUsePrivilegeSeparation, 303 sUsePrivilegeSeparation,
296 sDeprecated, sUnsupported 304 sDeprecated, sUnsupported
@@ -351,9 +359,13 @@ static struct {
351#ifdef GSSAPI 359#ifdef GSSAPI
352 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, 360 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
353 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, 361 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
362 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
363 { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
354#else 364#else
355 { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, 365 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
356 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, 366 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
367 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
368 { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
357#endif 369#endif
358 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, 370 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
359 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 371 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
@@ -871,10 +883,18 @@ parse_flag:
871 intptr = &options->gss_authentication; 883 intptr = &options->gss_authentication;
872 goto parse_flag; 884 goto parse_flag;
873 885
886 case sGssKeyEx:
887 intptr = &options->gss_keyex;
888 goto parse_flag;
889
874 case sGssCleanupCreds: 890 case sGssCleanupCreds:
875 intptr = &options->gss_cleanup_creds; 891 intptr = &options->gss_cleanup_creds;
876 goto parse_flag; 892 goto parse_flag;
877 893
894 case sGssStrictAcceptor:
895 intptr = &options->gss_strict_acceptor;
896 goto parse_flag;
897
878 case sPasswordAuthentication: 898 case sPasswordAuthentication:
879 intptr = &options->password_authentication; 899 intptr = &options->password_authentication;
880 goto parse_flag; 900 goto parse_flag;
diff --git a/servconf.h b/servconf.h
index 8a5b950ea..257de1c8b 100644
--- a/servconf.h
+++ b/servconf.h
@@ -87,7 +87,9 @@ typedef struct {
87 int kerberos_get_afs_token; /* If true, try to get AFS token if 87 int kerberos_get_afs_token; /* If true, try to get AFS token if
88 * authenticated with Kerberos. */ 88 * authenticated with Kerberos. */
89 int gss_authentication; /* If true, permit GSSAPI authentication */ 89 int gss_authentication; /* If true, permit GSSAPI authentication */
90 int gss_keyex; /* If true, permit GSSAPI key exchange */
90 int gss_cleanup_creds; /* If true, destroy cred cache on logout */ 91 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
92 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
91 int password_authentication; /* If true, permit password 93 int password_authentication; /* If true, permit password
92 * authentication. */ 94 * authentication. */
93 int kbd_interactive_authentication; /* If true, permit */ 95 int kbd_interactive_authentication; /* If true, permit */
diff --git a/ssh-gss.h b/ssh-gss.h
index c29a1b7e7..4e9e357b5 100644
--- a/ssh-gss.h
+++ b/ssh-gss.h
@@ -60,6 +60,17 @@
60 60
61#define SSH_GSS_OIDTYPE 0x06 61#define SSH_GSS_OIDTYPE 0x06
62 62
63#define SSH2_MSG_KEXGSS_INIT 30
64#define SSH2_MSG_KEXGSS_CONTINUE 31
65#define SSH2_MSG_KEXGSS_COMPLETE 32
66#define SSH2_MSG_KEXGSS_HOSTKEY 33
67#define SSH2_MSG_KEXGSS_ERROR 34
68#define SSH2_MSG_KEXGSS_GROUPREQ 40
69#define SSH2_MSG_KEXGSS_GROUP 41
70#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
71#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-"
72#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
73
63typedef struct { 74typedef struct {
64 char *filename; 75 char *filename;
65 char *envvar; 76 char *envvar;
@@ -97,6 +108,7 @@ typedef struct {
97} Gssctxt; 108} Gssctxt;
98 109
99extern ssh_gssapi_mech *supported_mechs[]; 110extern ssh_gssapi_mech *supported_mechs[];
111extern Gssctxt *gss_kex_context;
100 112
101int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); 113int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
102void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); 114void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
@@ -119,6 +131,11 @@ void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *);
119int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); 131int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
120 132
121/* In the server */ 133/* In the server */
134typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *);
135char *ssh_gssapi_client_mechanisms(const char *host);
136char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *);
137gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
138int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *);
122OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); 139OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
123int ssh_gssapi_userok(char *name); 140int ssh_gssapi_userok(char *name);
124OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); 141OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
@@ -126,6 +143,8 @@ void ssh_gssapi_do_child(char ***, u_int *);
126void ssh_gssapi_cleanup_creds(void); 143void ssh_gssapi_cleanup_creds(void);
127void ssh_gssapi_storecreds(void); 144void ssh_gssapi_storecreds(void);
128 145
146char *ssh_gssapi_server_mechanisms(void);
147int ssh_gssapi_oid_table_ok();
129#endif /* GSSAPI */ 148#endif /* GSSAPI */
130 149
131#endif /* _SSH_GSS_H */ 150#endif /* _SSH_GSS_H */
diff --git a/ssh_config b/ssh_config
index 8cb0698a8..5e3cd7426 100644
--- a/ssh_config
+++ b/ssh_config
@@ -26,6 +26,8 @@
26# HostbasedAuthentication no 26# HostbasedAuthentication no
27# GSSAPIAuthentication no 27# GSSAPIAuthentication no
28# GSSAPIDelegateCredentials no 28# GSSAPIDelegateCredentials no
29# GSSAPIKeyExchange no
30# GSSAPITrustDNS no
29# BatchMode no 31# BatchMode no
30# CheckHostIP yes 32# CheckHostIP yes
31# AddressFamily any 33# AddressFamily any
diff --git a/ssh_config.5 b/ssh_config.5
index 95af3976a..53596e63a 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -477,11 +477,28 @@ Specifies whether user authentication based on GSSAPI is allowed.
477The default is 477The default is
478.Dq no . 478.Dq no .
479Note that this option applies to protocol version 2 only. 479Note that this option applies to protocol version 2 only.
480.It Cm GSSAPIKeyExchange
481Specifies whether key exchange based on GSSAPI may be used. When using
482GSSAPI key exchange the server need not have a host key.
483The default is
484.Dq no .
485Note that this option applies to protocol version 2 only.
480.It Cm GSSAPIDelegateCredentials 486.It Cm GSSAPIDelegateCredentials
481Forward (delegate) credentials to the server. 487Forward (delegate) credentials to the server.
482The default is 488The default is
483.Dq no . 489.Dq no .
484Note that this option applies to protocol version 2 only. 490Note that this option applies to protocol version 2 only.
491.It Cm GSSAPITrustDns
492Set to
493.Dq yes
494to indicate that the DNS is trusted to securely canonicalize
495the name of the host being connected to. If
496.Dq no ,
497the hostname entered on the
498command line will be passed untouched to the GSSAPI library.
499The default is
500.Dq no .
501This option only applies to protocol version 2 connections using GSSAPI.
485.It Cm HashKnownHosts 502.It Cm HashKnownHosts
486Indicates that 503Indicates that
487.Xr ssh 1 504.Xr ssh 1
diff --git a/sshconnect2.c b/sshconnect2.c
index 208df078c..a28b06502 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -99,9 +99,34 @@ ssh_kex2(char *host, struct sockaddr *hostaddr)
99{ 99{
100 Kex *kex; 100 Kex *kex;
101 101
102#ifdef GSSAPI
103 char *orig = NULL, *gss = NULL;
104 char *gss_host = NULL;
105#endif
106
102 xxx_host = host; 107 xxx_host = host;
103 xxx_hostaddr = hostaddr; 108 xxx_hostaddr = hostaddr;
104 109
110#ifdef GSSAPI
111 if (options.gss_keyex) {
112 /* Add the GSSAPI mechanisms currently supported on this
113 * client to the key exchange algorithm proposal */
114 orig = myproposal[PROPOSAL_KEX_ALGS];
115
116 if (options.gss_trust_dns)
117 gss_host = (char *)get_canonical_hostname(1);
118 else
119 gss_host = host;
120
121 gss = ssh_gssapi_client_mechanisms(gss_host);
122 if (gss) {
123 debug("Offering GSSAPI proposal: %s", gss);
124 xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
125 "%s,%s", gss, orig);
126 }
127 }
128#endif
129
105 if (options.ciphers == (char *)-1) { 130 if (options.ciphers == (char *)-1) {
106 logit("No valid ciphers for protocol version 2 given, using defaults."); 131 logit("No valid ciphers for protocol version 2 given, using defaults.");
107 options.ciphers = NULL; 132 options.ciphers = NULL;
@@ -129,6 +154,16 @@ ssh_kex2(char *host, struct sockaddr *hostaddr)
129 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = 154 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
130 options.hostkeyalgorithms; 155 options.hostkeyalgorithms;
131 156
157#ifdef GSSAPI
158 /* If we've got GSSAPI algorithms, then we also support the
159 * 'null' hostkey, as a last resort */
160 if (options.gss_keyex && gss) {
161 orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
162 xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
163 "%s,null", orig);
164 }
165#endif
166
132 if (options.rekey_limit) 167 if (options.rekey_limit)
133 packet_set_rekey_limit(options.rekey_limit); 168 packet_set_rekey_limit(options.rekey_limit);
134 169
@@ -138,10 +173,21 @@ ssh_kex2(char *host, struct sockaddr *hostaddr)
138 kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; 173 kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
139 kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 174 kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
140 kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; 175 kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
176#ifdef GSSAPI
177 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
178 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
179 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
180#endif
141 kex->client_version_string=client_version_string; 181 kex->client_version_string=client_version_string;
142 kex->server_version_string=server_version_string; 182 kex->server_version_string=server_version_string;
143 kex->verify_host_key=&verify_host_key_callback; 183 kex->verify_host_key=&verify_host_key_callback;
144 184
185#ifdef GSSAPI
186 kex->gss_deleg_creds = options.gss_deleg_creds;
187 kex->gss_trust_dns = options.gss_trust_dns;
188 kex->gss_host = gss_host;
189#endif
190
145 xxx_kex = kex; 191 xxx_kex = kex;
146 192
147 dispatch_run(DISPATCH_BLOCK, &kex->done, kex); 193 dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
@@ -224,6 +270,7 @@ void input_gssapi_token(int type, u_int32_t, void *);
224void input_gssapi_hash(int type, u_int32_t, void *); 270void input_gssapi_hash(int type, u_int32_t, void *);
225void input_gssapi_error(int, u_int32_t, void *); 271void input_gssapi_error(int, u_int32_t, void *);
226void input_gssapi_errtok(int, u_int32_t, void *); 272void input_gssapi_errtok(int, u_int32_t, void *);
273int userauth_gsskeyex(Authctxt *authctxt);
227#endif 274#endif
228 275
229void userauth(Authctxt *, char *); 276void userauth(Authctxt *, char *);
@@ -239,6 +286,10 @@ static char *authmethods_get(void);
239 286
240Authmethod authmethods[] = { 287Authmethod authmethods[] = {
241#ifdef GSSAPI 288#ifdef GSSAPI
289 {"gssapi-keyex",
290 userauth_gsskeyex,
291 &options.gss_authentication,
292 NULL},
242 {"gssapi-with-mic", 293 {"gssapi-with-mic",
243 userauth_gssapi, 294 userauth_gssapi,
244 &options.gss_authentication, 295 &options.gss_authentication,
@@ -501,6 +552,12 @@ userauth_gssapi(Authctxt *authctxt)
501 static u_int mech = 0; 552 static u_int mech = 0;
502 OM_uint32 min; 553 OM_uint32 min;
503 int ok = 0; 554 int ok = 0;
555 char *gss_host = NULL;
556
557 if (options.gss_trust_dns)
558 gss_host = (char *)get_canonical_hostname(1);
559 else
560 gss_host = (char *)authctxt->host;
504 561
505 /* Try one GSSAPI method at a time, rather than sending them all at 562 /* Try one GSSAPI method at a time, rather than sending them all at
506 * once. */ 563 * once. */
@@ -513,7 +570,7 @@ userauth_gssapi(Authctxt *authctxt)
513 /* My DER encoding requires length<128 */ 570 /* My DER encoding requires length<128 */
514 if (gss_supported->elements[mech].length < 128 && 571 if (gss_supported->elements[mech].length < 128 &&
515 ssh_gssapi_check_mechanism(&gssctxt, 572 ssh_gssapi_check_mechanism(&gssctxt,
516 &gss_supported->elements[mech], authctxt->host)) { 573 &gss_supported->elements[mech], gss_host)) {
517 ok = 1; /* Mechanism works */ 574 ok = 1; /* Mechanism works */
518 } else { 575 } else {
519 mech++; 576 mech++;
@@ -609,8 +666,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt)
609{ 666{
610 Authctxt *authctxt = ctxt; 667 Authctxt *authctxt = ctxt;
611 Gssctxt *gssctxt; 668 Gssctxt *gssctxt;
612 int oidlen; 669 u_int oidlen;
613 char *oidv; 670 u_char *oidv;
614 671
615 if (authctxt == NULL) 672 if (authctxt == NULL)
616 fatal("input_gssapi_response: no authentication context"); 673 fatal("input_gssapi_response: no authentication context");
@@ -717,6 +774,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt)
717 xfree(msg); 774 xfree(msg);
718 xfree(lang); 775 xfree(lang);
719} 776}
777
778int
779userauth_gsskeyex(Authctxt *authctxt)
780{
781 Buffer b;
782 gss_buffer_desc gssbuf;
783 gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
784 OM_uint32 ms;
785
786 static int attempt = 0;
787 if (attempt++ >= 1)
788 return (0);
789
790 if (gss_kex_context == NULL) {
791 debug("No valid Key exchange context");
792 return (0);
793 }
794
795 ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
796 "gssapi-keyex");
797
798 gssbuf.value = buffer_ptr(&b);
799 gssbuf.length = buffer_len(&b);
800
801 if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
802 buffer_free(&b);
803 return (0);
804 }
805
806 packet_start(SSH2_MSG_USERAUTH_REQUEST);
807 packet_put_cstring(authctxt->server_user);
808 packet_put_cstring(authctxt->service);
809 packet_put_cstring(authctxt->method->name);
810 packet_put_string(mic.value, mic.length);
811 packet_send();
812
813 buffer_free(&b);
814 gss_release_buffer(&ms, &mic);
815
816 return (1);
817}
818
720#endif /* GSSAPI */ 819#endif /* GSSAPI */
721 820
722int 821int
diff --git a/sshd.c b/sshd.c
index 04778ea99..efa3f4c13 100644
--- a/sshd.c
+++ b/sshd.c
@@ -117,6 +117,10 @@
117#include "monitor_fdpass.h" 117#include "monitor_fdpass.h"
118#include "version.h" 118#include "version.h"
119 119
120#ifdef USE_SECURITY_SESSION_API
121#include <Security/AuthSession.h>
122#endif
123
120#ifdef LIBWRAP 124#ifdef LIBWRAP
121#include <tcpd.h> 125#include <tcpd.h>
122#include <syslog.h> 126#include <syslog.h>
@@ -1481,10 +1485,13 @@ main(int ac, char **av)
1481 logit("Disabling protocol version 1. Could not load host key"); 1485 logit("Disabling protocol version 1. Could not load host key");
1482 options.protocol &= ~SSH_PROTO_1; 1486 options.protocol &= ~SSH_PROTO_1;
1483 } 1487 }
1488#ifndef GSSAPI
1489 /* The GSSAPI key exchange can run without a host key */
1484 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { 1490 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
1485 logit("Disabling protocol version 2. Could not load host key"); 1491 logit("Disabling protocol version 2. Could not load host key");
1486 options.protocol &= ~SSH_PROTO_2; 1492 options.protocol &= ~SSH_PROTO_2;
1487 } 1493 }
1494#endif
1488 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { 1495 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
1489 logit("sshd: no hostkeys available -- exiting."); 1496 logit("sshd: no hostkeys available -- exiting.");
1490 exit(1); 1497 exit(1);
@@ -1759,6 +1766,60 @@ main(int ac, char **av)
1759 /* Log the connection. */ 1766 /* Log the connection. */
1760 verbose("Connection from %.500s port %d", remote_ip, remote_port); 1767 verbose("Connection from %.500s port %d", remote_ip, remote_port);
1761 1768
1769#ifdef USE_SECURITY_SESSION_API
1770 /*
1771 * Create a new security session for use by the new user login if
1772 * the current session is the root session or we are not launched
1773 * by inetd (eg: debugging mode or server mode). We do not
1774 * necessarily need to create a session if we are launched from
1775 * inetd because Panther xinetd will create a session for us.
1776 *
1777 * The only case where this logic will fail is if there is an
1778 * inetd running in a non-root session which is not creating
1779 * new sessions for us. Then all the users will end up in the
1780 * same session (bad).
1781 *
1782 * When the client exits, the session will be destroyed for us
1783 * automatically.
1784 *
1785 * We must create the session before any credentials are stored
1786 * (including AFS pags, which happens a few lines below).
1787 */
1788 {
1789 OSStatus err = 0;
1790 SecuritySessionId sid = 0;
1791 SessionAttributeBits sattrs = 0;
1792
1793 err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
1794 if (err)
1795 error("SessionGetInfo() failed with error %.8X",
1796 (unsigned) err);
1797 else
1798 debug("Current Session ID is %.8X / Session Attributes are %.8X",
1799 (unsigned) sid, (unsigned) sattrs);
1800
1801 if (inetd_flag && !(sattrs & sessionIsRoot))
1802 debug("Running in inetd mode in a non-root session... "
1803 "assuming inetd created the session for us.");
1804 else {
1805 debug("Creating new security session...");
1806 err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
1807 if (err)
1808 error("SessionCreate() failed with error %.8X",
1809 (unsigned) err);
1810
1811 err = SessionGetInfo(callerSecuritySession, &sid,
1812 &sattrs);
1813 if (err)
1814 error("SessionGetInfo() failed with error %.8X",
1815 (unsigned) err);
1816 else
1817 debug("New Session ID is %.8X / Session Attributes are %.8X",
1818 (unsigned) sid, (unsigned) sattrs);
1819 }
1820 }
1821#endif
1822
1762 /* 1823 /*
1763 * We don't want to listen forever unless the other side 1824 * We don't want to listen forever unless the other side
1764 * successfully authenticates itself. So we set up an alarm which is 1825 * successfully authenticates itself. So we set up an alarm which is
@@ -2117,12 +2178,59 @@ do_ssh2_kex(void)
2117 2178
2118 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); 2179 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types();
2119 2180
2181#ifdef GSSAPI
2182 {
2183 char *orig;
2184 char *gss = NULL;
2185 char *newstr = NULL;
2186 orig = myproposal[PROPOSAL_KEX_ALGS];
2187
2188 /*
2189 * If we don't have a host key, then there's no point advertising
2190 * the other key exchange algorithms
2191 */
2192
2193 if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2194 orig = NULL;
2195
2196 if (options.gss_keyex)
2197 gss = ssh_gssapi_server_mechanisms();
2198 else
2199 gss = NULL;
2200
2201 if (gss && orig)
2202 xasprintf(&newstr, "%s,%s", gss, orig);
2203 else if (gss)
2204 newstr = gss;
2205 else if (orig)
2206 newstr = orig;
2207
2208 /*
2209 * If we've got GSSAPI mechanisms, then we've got the 'null' host
2210 * key alg, but we can't tell people about it unless its the only
2211 * host key algorithm we support
2212 */
2213 if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
2214 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
2215
2216 if (newstr)
2217 myproposal[PROPOSAL_KEX_ALGS] = newstr;
2218 else
2219 fatal("No supported key exchange algorithms");
2220 }
2221#endif
2222
2120 /* start key exchange */ 2223 /* start key exchange */
2121 kex = kex_setup(myproposal); 2224 kex = kex_setup(myproposal);
2122 kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; 2225 kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
2123 kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; 2226 kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
2124 kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; 2227 kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
2125 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; 2228 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
2229#ifdef GSSAPI
2230 kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2231 kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2232 kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2233#endif
2126 kex->server = 1; 2234 kex->server = 1;
2127 kex->client_version_string=client_version_string; 2235 kex->client_version_string=client_version_string;
2128 kex->server_version_string=server_version_string; 2236 kex->server_version_string=server_version_string;
diff --git a/sshd_config b/sshd_config
index 3393cec50..aa1e4abdf 100644
--- a/sshd_config
+++ b/sshd_config
@@ -72,6 +72,8 @@ Protocol 2
72# GSSAPI options 72# GSSAPI options
73#GSSAPIAuthentication no 73#GSSAPIAuthentication no
74#GSSAPICleanupCredentials yes 74#GSSAPICleanupCredentials yes
75#GSSAPIStrictAcceptorCheck yes
76#GSSAPIKeyExchange no
75 77
76# Set this to 'yes' to enable PAM authentication, account processing, 78# Set this to 'yes' to enable PAM authentication, account processing,
77# and session processing. If this is enabled, PAM authentication will 79# and session processing. If this is enabled, PAM authentication will
diff --git a/sshd_config.5 b/sshd_config.5
index 7882f8bcf..5e1c7943c 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -318,12 +318,35 @@ Specifies whether user authentication based on GSSAPI is allowed.
318The default is 318The default is
319.Dq no . 319.Dq no .
320Note that this option applies to protocol version 2 only. 320Note that this option applies to protocol version 2 only.
321.It Cm GSSAPIKeyExchange
322Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
323doesn't rely on ssh keys to verify host identity.
324The default is
325.Dq no .
326Note that this option applies to protocol version 2 only.
321.It Cm GSSAPICleanupCredentials 327.It Cm GSSAPICleanupCredentials
322Specifies whether to automatically destroy the user's credentials cache 328Specifies whether to automatically destroy the user's credentials cache
323on logout. 329on logout.
324The default is 330The default is
325.Dq yes . 331.Dq yes .
326Note that this option applies to protocol version 2 only. 332Note that this option applies to protocol version 2 only.
333.It Cm GSSAPIStrictAcceptorCheck
334Determines whether to be strict about the identity of the GSSAPI acceptor
335a client authenticates against. If
336.Dq yes
337then the client must authenticate against the
338.Pa host
339service on the current hostname. If
340.Dq no
341then the client may authenticate against any service key stored in the
342machine's default store. This facility is provided to assist with operation
343on multi homed machines.
344The default is
345.Dq yes .
346Note that this option applies only to protocol version 2 GSSAPI connections,
347and setting it to
348.Dq no
349may only work with recent Kerberos GSSAPI libraries.
327.It Cm HostbasedAuthentication 350.It Cm HostbasedAuthentication
328Specifies whether rhosts or /etc/hosts.equiv authentication together 351Specifies whether rhosts or /etc/hosts.equiv authentication together
329with successful public key client host authentication is allowed 352with successful public key client host authentication is allowed