diff options
Diffstat (limited to 'debian/patches/gssapi.patch')
-rw-r--r-- | debian/patches/gssapi.patch | 3045 |
1 files changed, 3045 insertions, 0 deletions
diff --git a/debian/patches/gssapi.patch b/debian/patches/gssapi.patch new file mode 100644 index 000000000..112b31fdf --- /dev/null +++ b/debian/patches/gssapi.patch | |||
@@ -0,0 +1,3045 @@ | |||
1 | Description: GSSAPI key exchange support | ||
2 | This patch has been rejected upstream: "None of the OpenSSH developers are | ||
3 | in favour of adding this, and this situation has not changed for several | ||
4 | years. This is not a slight on Simon's patch, which is of fine quality, | ||
5 | but just that a) we don't trust GSSAPI implementations that much and b) we | ||
6 | don't like adding new KEX since they are pre-auth attack surface. This one | ||
7 | is particularly scary, since it requires hooks out to typically root-owned | ||
8 | system resources." | ||
9 | . | ||
10 | However, quite a lot of people rely on this in Debian, and it's better to | ||
11 | have it merged into the main openssh package rather than having separate | ||
12 | -krb5 packages (as we used to have). It seems to have a generally good | ||
13 | security history. | ||
14 | Author: Simon Wilkinson <simon@sxw.org.uk> | ||
15 | Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 | ||
16 | Last-Updated: 2010-02-27 | ||
17 | |||
18 | Index: b/ChangeLog.gssapi | ||
19 | =================================================================== | ||
20 | --- /dev/null | ||
21 | +++ b/ChangeLog.gssapi | ||
22 | @@ -0,0 +1,113 @@ | ||
23 | +20110101 | ||
24 | + - Finally update for OpenSSH 5.6p1 | ||
25 | + - Add GSSAPIServerIdentity option from Jim Basney | ||
26 | + | ||
27 | +20100308 | ||
28 | + - [ Makefile.in, key.c, key.h ] | ||
29 | + Updates for OpenSSH 5.4p1 | ||
30 | + - [ servconf.c ] | ||
31 | + Include GSSAPI options in the sshd -T configuration dump, and flag | ||
32 | + some older configuration options as being unsupported. Thanks to Colin | ||
33 | + Watson. | ||
34 | + - | ||
35 | + | ||
36 | +20100124 | ||
37 | + - [ sshconnect2.c ] | ||
38 | + Adapt to deal with additional element in Authmethod structure. Thanks to | ||
39 | + Colin Watson | ||
40 | + | ||
41 | +20090615 | ||
42 | + - [ gss-genr.c gss-serv.c kexgssc.c kexgsss.c monitor.c sshconnect2.c | ||
43 | + sshd.c ] | ||
44 | + Fix issues identified by Greg Hudson following a code review | ||
45 | + Check return value of gss_indicate_mechs | ||
46 | + Protect GSSAPI calls in monitor, so they can only be used if enabled | ||
47 | + Check return values of bignum functions in key exchange | ||
48 | + Use BN_clear_free to clear other side's DH value | ||
49 | + Make ssh_gssapi_id_kex more robust | ||
50 | + Only configure kex table pointers if GSSAPI is enabled | ||
51 | + Don't leak mechanism list, or gss mechanism list | ||
52 | + Cast data.length before printing | ||
53 | + If serverkey isn't provided, use an empty string, rather than NULL | ||
54 | + | ||
55 | +20090201 | ||
56 | + - [ gss-genr.c gss-serv.c kex.h kexgssc.c readconf.c readconf.h ssh-gss.h | ||
57 | + ssh_config.5 sshconnet2.c ] | ||
58 | + Add support for the GSSAPIClientIdentity option, which allows the user | ||
59 | + to specify which GSSAPI identity to use to contact a given server | ||
60 | + | ||
61 | +20080404 | ||
62 | + - [ gss-serv.c ] | ||
63 | + Add code to actually implement GSSAPIStrictAcceptCheck, which had somehow | ||
64 | + been omitted from a previous version of this patch. Reported by Borislav | ||
65 | + Stoichkov | ||
66 | + | ||
67 | +20070317 | ||
68 | + - [ gss-serv-krb5.c ] | ||
69 | + Remove C99ism, where new_ccname was being declared in the middle of a | ||
70 | + function | ||
71 | + | ||
72 | +20061220 | ||
73 | + - [ servconf.c ] | ||
74 | + Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and | ||
75 | + documented, behaviour. Reported by Dan Watson. | ||
76 | + | ||
77 | +20060910 | ||
78 | + - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c | ||
79 | + ssh-gss.h ] | ||
80 | + add support for gss-group14-sha1 key exchange mechanisms | ||
81 | + - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ] | ||
82 | + Add GSSAPIStrictAcceptorCheck option to allow the disabling of | ||
83 | + acceptor principal checking on multi-homed machines. | ||
84 | + <Bugzilla #928> | ||
85 | + - [ sshd_config ssh_config ] | ||
86 | + Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample | ||
87 | + configuration files | ||
88 | + - [ kexgss.c kegsss.c sshconnect2.c sshd.c ] | ||
89 | + Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf() | ||
90 | + Limit length of error messages displayed by client | ||
91 | + | ||
92 | +20060909 | ||
93 | + - [ gss-genr.c gss-serv.c ] | ||
94 | + move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server | ||
95 | + only, where they belong | ||
96 | + <Bugzilla #1225> | ||
97 | + | ||
98 | +20060829 | ||
99 | + - [ gss-serv-krb5.c ] | ||
100 | + Fix CCAPI credentials cache name when creating KRB5CCNAME environment | ||
101 | + variable | ||
102 | + | ||
103 | +20060828 | ||
104 | + - [ gss-genr.c ] | ||
105 | + Avoid Heimdal context freeing problem | ||
106 | + <Fixed upstream 20060829> | ||
107 | + | ||
108 | +20060818 | ||
109 | + - [ gss-genr.c ssh-gss.h sshconnect2.c ] | ||
110 | + Make sure that SPENGO is disabled | ||
111 | + <Bugzilla #1218 - Fixed upstream 20060818> | ||
112 | + | ||
113 | +20060421 | ||
114 | + - [ gssgenr.c, sshconnect2.c ] | ||
115 | + a few type changes (signed versus unsigned, int versus size_t) to | ||
116 | + fix compiler errors/warnings | ||
117 | + (from jbasney AT ncsa.uiuc.edu) | ||
118 | + - [ kexgssc.c, sshconnect2.c ] | ||
119 | + fix uninitialized variable warnings | ||
120 | + (from jbasney AT ncsa.uiuc.edu) | ||
121 | + - [ gssgenr.c ] | ||
122 | + pass oid to gss_display_status (helpful when using GSSAPI mechglue) | ||
123 | + (from jbasney AT ncsa.uiuc.edu) | ||
124 | + <Bugzilla #1220 > | ||
125 | + - [ gss-serv-krb5.c ] | ||
126 | + #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H | ||
127 | + (from jbasney AT ncsa.uiuc.edu) | ||
128 | + <Fixed upstream 20060304> | ||
129 | + - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c | ||
130 | + add client-side GssapiKeyExchange option | ||
131 | + (from jbasney AT ncsa.uiuc.edu) | ||
132 | + - [ sshconnect2.c ] | ||
133 | + add support for GssapiTrustDns option for gssapi-with-mic | ||
134 | + (from jbasney AT ncsa.uiuc.edu) | ||
135 | + <gssapi-with-mic support is Bugzilla #1008> | ||
136 | Index: b/Makefile.in | ||
137 | =================================================================== | ||
138 | --- a/Makefile.in | ||
139 | +++ b/Makefile.in | ||
140 | @@ -74,6 +74,7 @@ | ||
141 | atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ | ||
142 | monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ | ||
143 | kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \ | ||
144 | + kexgssc.o \ | ||
145 | msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o jpake.o \ | ||
146 | schnorr.o ssh-pkcs11.o | ||
147 | |||
148 | @@ -90,7 +91,7 @@ | ||
149 | auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-jpake.o \ | ||
150 | monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \ | ||
151 | auth-krb5.o \ | ||
152 | - auth2-gss.o gss-serv.o gss-serv-krb5.o \ | ||
153 | + auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o\ | ||
154 | loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ | ||
155 | sftp-server.o sftp-common.o \ | ||
156 | roaming_common.o roaming_serv.o | ||
157 | Index: b/auth-krb5.c | ||
158 | =================================================================== | ||
159 | --- a/auth-krb5.c | ||
160 | +++ b/auth-krb5.c | ||
161 | @@ -170,8 +170,13 @@ | ||
162 | |||
163 | len = strlen(authctxt->krb5_ticket_file) + 6; | ||
164 | authctxt->krb5_ccname = xmalloc(len); | ||
165 | +#ifdef USE_CCAPI | ||
166 | + snprintf(authctxt->krb5_ccname, len, "API:%s", | ||
167 | + authctxt->krb5_ticket_file); | ||
168 | +#else | ||
169 | snprintf(authctxt->krb5_ccname, len, "FILE:%s", | ||
170 | authctxt->krb5_ticket_file); | ||
171 | +#endif | ||
172 | |||
173 | #ifdef USE_PAM | ||
174 | if (options.use_pam) | ||
175 | @@ -226,15 +231,22 @@ | ||
176 | #ifndef HEIMDAL | ||
177 | krb5_error_code | ||
178 | ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { | ||
179 | - int tmpfd, ret; | ||
180 | + int ret; | ||
181 | char ccname[40]; | ||
182 | mode_t old_umask; | ||
183 | +#ifdef USE_CCAPI | ||
184 | + char cctemplate[] = "API:krb5cc_%d"; | ||
185 | +#else | ||
186 | + char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; | ||
187 | + int tmpfd; | ||
188 | +#endif | ||
189 | |||
190 | ret = snprintf(ccname, sizeof(ccname), | ||
191 | - "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); | ||
192 | + cctemplate, geteuid()); | ||
193 | if (ret < 0 || (size_t)ret >= sizeof(ccname)) | ||
194 | return ENOMEM; | ||
195 | |||
196 | +#ifndef USE_CCAPI | ||
197 | old_umask = umask(0177); | ||
198 | tmpfd = mkstemp(ccname + strlen("FILE:")); | ||
199 | umask(old_umask); | ||
200 | @@ -249,6 +261,7 @@ | ||
201 | return errno; | ||
202 | } | ||
203 | close(tmpfd); | ||
204 | +#endif | ||
205 | |||
206 | return (krb5_cc_resolve(ctx, ccname, ccache)); | ||
207 | } | ||
208 | Index: b/auth.h | ||
209 | =================================================================== | ||
210 | --- a/auth.h | ||
211 | +++ b/auth.h | ||
212 | @@ -53,6 +53,7 @@ | ||
213 | int valid; /* user exists and is allowed to login */ | ||
214 | int attempt; | ||
215 | int failures; | ||
216 | + int server_caused_failure; | ||
217 | int force_pwchange; | ||
218 | char *user; /* username sent by the client */ | ||
219 | char *service; | ||
220 | Index: b/auth2-gss.c | ||
221 | =================================================================== | ||
222 | --- a/auth2-gss.c | ||
223 | +++ b/auth2-gss.c | ||
224 | @@ -1,7 +1,7 @@ | ||
225 | /* $OpenBSD: auth2-gss.c,v 1.16 2007/10/29 00:52:45 dtucker Exp $ */ | ||
226 | |||
227 | /* | ||
228 | - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. | ||
229 | + * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. | ||
230 | * | ||
231 | * Redistribution and use in source and binary forms, with or without | ||
232 | * modification, are permitted provided that the following conditions | ||
233 | @@ -52,6 +52,40 @@ | ||
234 | static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); | ||
235 | static void input_gssapi_errtok(int, u_int32_t, void *); | ||
236 | |||
237 | +/* | ||
238 | + * The 'gssapi_keyex' userauth mechanism. | ||
239 | + */ | ||
240 | +static int | ||
241 | +userauth_gsskeyex(Authctxt *authctxt) | ||
242 | +{ | ||
243 | + int authenticated = 0; | ||
244 | + Buffer b; | ||
245 | + gss_buffer_desc mic, gssbuf; | ||
246 | + u_int len; | ||
247 | + | ||
248 | + mic.value = packet_get_string(&len); | ||
249 | + mic.length = len; | ||
250 | + | ||
251 | + packet_check_eom(); | ||
252 | + | ||
253 | + ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, | ||
254 | + "gssapi-keyex"); | ||
255 | + | ||
256 | + gssbuf.value = buffer_ptr(&b); | ||
257 | + gssbuf.length = buffer_len(&b); | ||
258 | + | ||
259 | + /* gss_kex_context is NULL with privsep, so we can't check it here */ | ||
260 | + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, | ||
261 | + &gssbuf, &mic)))) | ||
262 | + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, | ||
263 | + authctxt->pw)); | ||
264 | + | ||
265 | + buffer_free(&b); | ||
266 | + xfree(mic.value); | ||
267 | + | ||
268 | + return (authenticated); | ||
269 | +} | ||
270 | + | ||
271 | /* | ||
272 | * We only support those mechanisms that we know about (ie ones that we know | ||
273 | * how to check local user kuserok and the like) | ||
274 | @@ -102,6 +136,7 @@ | ||
275 | |||
276 | if (!present) { | ||
277 | xfree(doid); | ||
278 | + authctxt->server_caused_failure = 1; | ||
279 | return (0); | ||
280 | } | ||
281 | |||
282 | @@ -109,6 +144,7 @@ | ||
283 | if (ctxt != NULL) | ||
284 | ssh_gssapi_delete_ctx(&ctxt); | ||
285 | xfree(doid); | ||
286 | + authctxt->server_caused_failure = 1; | ||
287 | return (0); | ||
288 | } | ||
289 | |||
290 | @@ -242,7 +278,8 @@ | ||
291 | |||
292 | packet_check_eom(); | ||
293 | |||
294 | - authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); | ||
295 | + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, | ||
296 | + authctxt->pw)); | ||
297 | |||
298 | authctxt->postponed = 0; | ||
299 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); | ||
300 | @@ -277,7 +314,8 @@ | ||
301 | gssbuf.length = buffer_len(&b); | ||
302 | |||
303 | if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) | ||
304 | - authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); | ||
305 | + authenticated = | ||
306 | + PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); | ||
307 | else | ||
308 | logit("GSSAPI MIC check failed"); | ||
309 | |||
310 | @@ -292,6 +330,12 @@ | ||
311 | userauth_finish(authctxt, authenticated, "gssapi-with-mic"); | ||
312 | } | ||
313 | |||
314 | +Authmethod method_gsskeyex = { | ||
315 | + "gssapi-keyex", | ||
316 | + userauth_gsskeyex, | ||
317 | + &options.gss_authentication | ||
318 | +}; | ||
319 | + | ||
320 | Authmethod method_gssapi = { | ||
321 | "gssapi-with-mic", | ||
322 | userauth_gssapi, | ||
323 | Index: b/auth2.c | ||
324 | =================================================================== | ||
325 | --- a/auth2.c | ||
326 | +++ b/auth2.c | ||
327 | @@ -69,6 +69,7 @@ | ||
328 | extern Authmethod method_kbdint; | ||
329 | extern Authmethod method_hostbased; | ||
330 | #ifdef GSSAPI | ||
331 | +extern Authmethod method_gsskeyex; | ||
332 | extern Authmethod method_gssapi; | ||
333 | #endif | ||
334 | #ifdef JPAKE | ||
335 | @@ -79,6 +80,7 @@ | ||
336 | &method_none, | ||
337 | &method_pubkey, | ||
338 | #ifdef GSSAPI | ||
339 | + &method_gsskeyex, | ||
340 | &method_gssapi, | ||
341 | #endif | ||
342 | #ifdef JPAKE | ||
343 | @@ -274,6 +276,7 @@ | ||
344 | #endif | ||
345 | |||
346 | authctxt->postponed = 0; | ||
347 | + authctxt->server_caused_failure = 0; | ||
348 | |||
349 | /* try to authenticate user */ | ||
350 | m = authmethod_lookup(method); | ||
351 | @@ -346,7 +349,8 @@ | ||
352 | } else { | ||
353 | |||
354 | /* Allow initial try of "none" auth without failure penalty */ | ||
355 | - if (authctxt->attempt > 1 || strcmp(method, "none") != 0) | ||
356 | + if (!authctxt->server_caused_failure && | ||
357 | + (authctxt->attempt > 1 || strcmp(method, "none") != 0)) | ||
358 | authctxt->failures++; | ||
359 | if (authctxt->failures >= options.max_authtries) { | ||
360 | #ifdef SSH_AUDIT_EVENTS | ||
361 | Index: b/clientloop.c | ||
362 | =================================================================== | ||
363 | --- a/clientloop.c | ||
364 | +++ b/clientloop.c | ||
365 | @@ -111,6 +111,10 @@ | ||
366 | #include "msg.h" | ||
367 | #include "roaming.h" | ||
368 | |||
369 | +#ifdef GSSAPI | ||
370 | +#include "ssh-gss.h" | ||
371 | +#endif | ||
372 | + | ||
373 | /* import options */ | ||
374 | extern Options options; | ||
375 | |||
376 | @@ -1483,6 +1487,15 @@ | ||
377 | /* Do channel operations unless rekeying in progress. */ | ||
378 | if (!rekeying) { | ||
379 | channel_after_select(readset, writeset); | ||
380 | + | ||
381 | +#ifdef GSSAPI | ||
382 | + if (options.gss_renewal_rekey && | ||
383 | + ssh_gssapi_credentials_updated(GSS_C_NO_CONTEXT)) { | ||
384 | + debug("credentials updated - forcing rekey"); | ||
385 | + need_rekeying = 1; | ||
386 | + } | ||
387 | +#endif | ||
388 | + | ||
389 | if (need_rekeying || packet_need_rekeying()) { | ||
390 | debug("need rekeying"); | ||
391 | xxx_kex->done = 0; | ||
392 | Index: b/configure.ac | ||
393 | =================================================================== | ||
394 | --- a/configure.ac | ||
395 | +++ b/configure.ac | ||
396 | @@ -514,6 +514,30 @@ | ||
397 | [Use tunnel device compatibility to OpenBSD]) | ||
398 | AC_DEFINE(SSH_TUN_PREPEND_AF, 1, | ||
399 | [Prepend the address family to IP tunnel traffic]) | ||
400 | + AC_MSG_CHECKING(if we have the Security Authorization Session API) | ||
401 | + AC_TRY_COMPILE([#include <Security/AuthSession.h>], | ||
402 | + [SessionCreate(0, 0);], | ||
403 | + [ac_cv_use_security_session_api="yes" | ||
404 | + AC_DEFINE(USE_SECURITY_SESSION_API, 1, | ||
405 | + [platform has the Security Authorization Session API]) | ||
406 | + LIBS="$LIBS -framework Security" | ||
407 | + AC_MSG_RESULT(yes)], | ||
408 | + [ac_cv_use_security_session_api="no" | ||
409 | + AC_MSG_RESULT(no)]) | ||
410 | + AC_MSG_CHECKING(if we have an in-memory credentials cache) | ||
411 | + AC_TRY_COMPILE( | ||
412 | + [#include <Kerberos/Kerberos.h>], | ||
413 | + [cc_context_t c; | ||
414 | + (void) cc_initialize (&c, 0, NULL, NULL);], | ||
415 | + [AC_DEFINE(USE_CCAPI, 1, | ||
416 | + [platform uses an in-memory credentials cache]) | ||
417 | + LIBS="$LIBS -framework Security" | ||
418 | + AC_MSG_RESULT(yes) | ||
419 | + if test "x$ac_cv_use_security_session_api" = "xno"; then | ||
420 | + AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) | ||
421 | + fi], | ||
422 | + [AC_MSG_RESULT(no)] | ||
423 | + ) | ||
424 | m4_pattern_allow(AU_IPv) | ||
425 | AC_CHECK_DECL(AU_IPv4, [], | ||
426 | AC_DEFINE(AU_IPv4, 0, [System only supports IPv4 audit records]) | ||
427 | Index: b/gss-genr.c | ||
428 | =================================================================== | ||
429 | --- a/gss-genr.c | ||
430 | +++ b/gss-genr.c | ||
431 | @@ -1,7 +1,7 @@ | ||
432 | /* $OpenBSD: gss-genr.c,v 1.20 2009/06/22 05:39:28 dtucker Exp $ */ | ||
433 | |||
434 | /* | ||
435 | - * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. | ||
436 | + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. | ||
437 | * | ||
438 | * Redistribution and use in source and binary forms, with or without | ||
439 | * modification, are permitted provided that the following conditions | ||
440 | @@ -39,12 +39,167 @@ | ||
441 | #include "buffer.h" | ||
442 | #include "log.h" | ||
443 | #include "ssh2.h" | ||
444 | +#include "cipher.h" | ||
445 | +#include "key.h" | ||
446 | +#include "kex.h" | ||
447 | +#include <openssl/evp.h> | ||
448 | |||
449 | #include "ssh-gss.h" | ||
450 | |||
451 | extern u_char *session_id2; | ||
452 | extern u_int session_id2_len; | ||
453 | |||
454 | +typedef struct { | ||
455 | + char *encoded; | ||
456 | + gss_OID oid; | ||
457 | +} ssh_gss_kex_mapping; | ||
458 | + | ||
459 | +/* | ||
460 | + * XXX - It would be nice to find a more elegant way of handling the | ||
461 | + * XXX passing of the key exchange context to the userauth routines | ||
462 | + */ | ||
463 | + | ||
464 | +Gssctxt *gss_kex_context = NULL; | ||
465 | + | ||
466 | +static ssh_gss_kex_mapping *gss_enc2oid = NULL; | ||
467 | + | ||
468 | +int | ||
469 | +ssh_gssapi_oid_table_ok() { | ||
470 | + return (gss_enc2oid != NULL); | ||
471 | +} | ||
472 | + | ||
473 | +/* | ||
474 | + * Return a list of the gss-group1-sha1 mechanisms supported by this program | ||
475 | + * | ||
476 | + * We test mechanisms to ensure that we can use them, to avoid starting | ||
477 | + * a key exchange with a bad mechanism | ||
478 | + */ | ||
479 | + | ||
480 | +char * | ||
481 | +ssh_gssapi_client_mechanisms(const char *host, const char *client) { | ||
482 | + gss_OID_set gss_supported; | ||
483 | + OM_uint32 min_status; | ||
484 | + | ||
485 | + if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) | ||
486 | + return NULL; | ||
487 | + | ||
488 | + return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, | ||
489 | + host, client)); | ||
490 | +} | ||
491 | + | ||
492 | +char * | ||
493 | +ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, | ||
494 | + const char *host, const char *client) { | ||
495 | + Buffer buf; | ||
496 | + size_t i; | ||
497 | + int oidpos, enclen; | ||
498 | + char *mechs, *encoded; | ||
499 | + u_char digest[EVP_MAX_MD_SIZE]; | ||
500 | + char deroid[2]; | ||
501 | + const EVP_MD *evp_md = EVP_md5(); | ||
502 | + EVP_MD_CTX md; | ||
503 | + | ||
504 | + if (gss_enc2oid != NULL) { | ||
505 | + for (i = 0; gss_enc2oid[i].encoded != NULL; i++) | ||
506 | + xfree(gss_enc2oid[i].encoded); | ||
507 | + xfree(gss_enc2oid); | ||
508 | + } | ||
509 | + | ||
510 | + gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * | ||
511 | + (gss_supported->count + 1)); | ||
512 | + | ||
513 | + buffer_init(&buf); | ||
514 | + | ||
515 | + oidpos = 0; | ||
516 | + for (i = 0; i < gss_supported->count; i++) { | ||
517 | + if (gss_supported->elements[i].length < 128 && | ||
518 | + (*check)(NULL, &(gss_supported->elements[i]), host, client)) { | ||
519 | + | ||
520 | + deroid[0] = SSH_GSS_OIDTYPE; | ||
521 | + deroid[1] = gss_supported->elements[i].length; | ||
522 | + | ||
523 | + EVP_DigestInit(&md, evp_md); | ||
524 | + EVP_DigestUpdate(&md, deroid, 2); | ||
525 | + EVP_DigestUpdate(&md, | ||
526 | + gss_supported->elements[i].elements, | ||
527 | + gss_supported->elements[i].length); | ||
528 | + EVP_DigestFinal(&md, digest, NULL); | ||
529 | + | ||
530 | + encoded = xmalloc(EVP_MD_size(evp_md) * 2); | ||
531 | + enclen = __b64_ntop(digest, EVP_MD_size(evp_md), | ||
532 | + encoded, EVP_MD_size(evp_md) * 2); | ||
533 | + | ||
534 | + if (oidpos != 0) | ||
535 | + buffer_put_char(&buf, ','); | ||
536 | + | ||
537 | + buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, | ||
538 | + sizeof(KEX_GSS_GEX_SHA1_ID) - 1); | ||
539 | + buffer_append(&buf, encoded, enclen); | ||
540 | + buffer_put_char(&buf, ','); | ||
541 | + buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, | ||
542 | + sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); | ||
543 | + buffer_append(&buf, encoded, enclen); | ||
544 | + buffer_put_char(&buf, ','); | ||
545 | + buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, | ||
546 | + sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); | ||
547 | + buffer_append(&buf, encoded, enclen); | ||
548 | + | ||
549 | + gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); | ||
550 | + gss_enc2oid[oidpos].encoded = encoded; | ||
551 | + oidpos++; | ||
552 | + } | ||
553 | + } | ||
554 | + gss_enc2oid[oidpos].oid = NULL; | ||
555 | + gss_enc2oid[oidpos].encoded = NULL; | ||
556 | + | ||
557 | + buffer_put_char(&buf, '\0'); | ||
558 | + | ||
559 | + mechs = xmalloc(buffer_len(&buf)); | ||
560 | + buffer_get(&buf, mechs, buffer_len(&buf)); | ||
561 | + buffer_free(&buf); | ||
562 | + | ||
563 | + if (strlen(mechs) == 0) { | ||
564 | + xfree(mechs); | ||
565 | + mechs = NULL; | ||
566 | + } | ||
567 | + | ||
568 | + return (mechs); | ||
569 | +} | ||
570 | + | ||
571 | +gss_OID | ||
572 | +ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { | ||
573 | + int i = 0; | ||
574 | + | ||
575 | + switch (kex_type) { | ||
576 | + case KEX_GSS_GRP1_SHA1: | ||
577 | + if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) | ||
578 | + return GSS_C_NO_OID; | ||
579 | + name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; | ||
580 | + break; | ||
581 | + case KEX_GSS_GRP14_SHA1: | ||
582 | + if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) | ||
583 | + return GSS_C_NO_OID; | ||
584 | + name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; | ||
585 | + break; | ||
586 | + case KEX_GSS_GEX_SHA1: | ||
587 | + if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) | ||
588 | + return GSS_C_NO_OID; | ||
589 | + name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; | ||
590 | + break; | ||
591 | + default: | ||
592 | + return GSS_C_NO_OID; | ||
593 | + } | ||
594 | + | ||
595 | + while (gss_enc2oid[i].encoded != NULL && | ||
596 | + strcmp(name, gss_enc2oid[i].encoded) != 0) | ||
597 | + i++; | ||
598 | + | ||
599 | + if (gss_enc2oid[i].oid != NULL && ctx != NULL) | ||
600 | + ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); | ||
601 | + | ||
602 | + return gss_enc2oid[i].oid; | ||
603 | +} | ||
604 | + | ||
605 | /* Check that the OID in a data stream matches that in the context */ | ||
606 | int | ||
607 | ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) | ||
608 | @@ -197,7 +352,7 @@ | ||
609 | } | ||
610 | |||
611 | ctx->major = gss_init_sec_context(&ctx->minor, | ||
612 | - GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, | ||
613 | + ctx->client_creds, &ctx->context, ctx->name, ctx->oid, | ||
614 | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, | ||
615 | 0, NULL, recv_tok, NULL, send_tok, flags, NULL); | ||
616 | |||
617 | @@ -227,8 +382,42 @@ | ||
618 | } | ||
619 | |||
620 | OM_uint32 | ||
621 | +ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) | ||
622 | +{ | ||
623 | + gss_buffer_desc gssbuf; | ||
624 | + gss_name_t gssname; | ||
625 | + OM_uint32 status; | ||
626 | + gss_OID_set oidset; | ||
627 | + | ||
628 | + gssbuf.value = (void *) name; | ||
629 | + gssbuf.length = strlen(gssbuf.value); | ||
630 | + | ||
631 | + gss_create_empty_oid_set(&status, &oidset); | ||
632 | + gss_add_oid_set_member(&status, ctx->oid, &oidset); | ||
633 | + | ||
634 | + ctx->major = gss_import_name(&ctx->minor, &gssbuf, | ||
635 | + GSS_C_NT_USER_NAME, &gssname); | ||
636 | + | ||
637 | + if (!ctx->major) | ||
638 | + ctx->major = gss_acquire_cred(&ctx->minor, | ||
639 | + gssname, 0, oidset, GSS_C_INITIATE, | ||
640 | + &ctx->client_creds, NULL, NULL); | ||
641 | + | ||
642 | + gss_release_name(&status, &gssname); | ||
643 | + gss_release_oid_set(&status, &oidset); | ||
644 | + | ||
645 | + if (ctx->major) | ||
646 | + ssh_gssapi_error(ctx); | ||
647 | + | ||
648 | + return(ctx->major); | ||
649 | +} | ||
650 | + | ||
651 | +OM_uint32 | ||
652 | ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) | ||
653 | { | ||
654 | + if (ctx == NULL) | ||
655 | + return -1; | ||
656 | + | ||
657 | if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, | ||
658 | GSS_C_QOP_DEFAULT, buffer, hash))) | ||
659 | ssh_gssapi_error(ctx); | ||
660 | @@ -236,6 +425,19 @@ | ||
661 | return (ctx->major); | ||
662 | } | ||
663 | |||
664 | +/* Priviledged when used by server */ | ||
665 | +OM_uint32 | ||
666 | +ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) | ||
667 | +{ | ||
668 | + if (ctx == NULL) | ||
669 | + return -1; | ||
670 | + | ||
671 | + ctx->major = gss_verify_mic(&ctx->minor, ctx->context, | ||
672 | + gssbuf, gssmic, NULL); | ||
673 | + | ||
674 | + return (ctx->major); | ||
675 | +} | ||
676 | + | ||
677 | void | ||
678 | ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, | ||
679 | const char *context) | ||
680 | @@ -249,11 +451,16 @@ | ||
681 | } | ||
682 | |||
683 | int | ||
684 | -ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) | ||
685 | +ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, | ||
686 | + const char *client) | ||
687 | { | ||
688 | gss_buffer_desc token = GSS_C_EMPTY_BUFFER; | ||
689 | OM_uint32 major, minor; | ||
690 | gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; | ||
691 | + Gssctxt *intctx = NULL; | ||
692 | + | ||
693 | + if (ctx == NULL) | ||
694 | + ctx = &intctx; | ||
695 | |||
696 | /* RFC 4462 says we MUST NOT do SPNEGO */ | ||
697 | if (oid->length == spnego_oid.length && | ||
698 | @@ -263,6 +470,10 @@ | ||
699 | ssh_gssapi_build_ctx(ctx); | ||
700 | ssh_gssapi_set_oid(*ctx, oid); | ||
701 | major = ssh_gssapi_import_name(*ctx, host); | ||
702 | + | ||
703 | + if (!GSS_ERROR(major) && client) | ||
704 | + major = ssh_gssapi_client_identity(*ctx, client); | ||
705 | + | ||
706 | if (!GSS_ERROR(major)) { | ||
707 | major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, | ||
708 | NULL); | ||
709 | @@ -272,10 +483,67 @@ | ||
710 | GSS_C_NO_BUFFER); | ||
711 | } | ||
712 | |||
713 | - if (GSS_ERROR(major)) | ||
714 | + if (GSS_ERROR(major) || intctx != NULL) | ||
715 | ssh_gssapi_delete_ctx(ctx); | ||
716 | |||
717 | return (!GSS_ERROR(major)); | ||
718 | } | ||
719 | |||
720 | +int | ||
721 | +ssh_gssapi_credentials_updated(Gssctxt *ctxt) { | ||
722 | + static gss_name_t saved_name = GSS_C_NO_NAME; | ||
723 | + static OM_uint32 saved_lifetime = 0; | ||
724 | + static gss_OID saved_mech = GSS_C_NO_OID; | ||
725 | + static gss_name_t name; | ||
726 | + static OM_uint32 last_call = 0; | ||
727 | + OM_uint32 lifetime, now, major, minor; | ||
728 | + int equal; | ||
729 | + gss_cred_usage_t usage = GSS_C_INITIATE; | ||
730 | + | ||
731 | + now = time(NULL); | ||
732 | + | ||
733 | + if (ctxt) { | ||
734 | + debug("Rekey has happened - updating saved versions"); | ||
735 | + | ||
736 | + if (saved_name != GSS_C_NO_NAME) | ||
737 | + gss_release_name(&minor, &saved_name); | ||
738 | + | ||
739 | + major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, | ||
740 | + &saved_name, &saved_lifetime, NULL, NULL); | ||
741 | + | ||
742 | + if (!GSS_ERROR(major)) { | ||
743 | + saved_mech = ctxt->oid; | ||
744 | + saved_lifetime+= now; | ||
745 | + } else { | ||
746 | + /* Handle the error */ | ||
747 | + } | ||
748 | + return 0; | ||
749 | + } | ||
750 | + | ||
751 | + if (now - last_call < 10) | ||
752 | + return 0; | ||
753 | + | ||
754 | + last_call = now; | ||
755 | + | ||
756 | + if (saved_mech == GSS_C_NO_OID) | ||
757 | + return 0; | ||
758 | + | ||
759 | + major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, | ||
760 | + &name, &lifetime, NULL, NULL); | ||
761 | + if (major == GSS_S_CREDENTIALS_EXPIRED) | ||
762 | + return 0; | ||
763 | + else if (GSS_ERROR(major)) | ||
764 | + return 0; | ||
765 | + | ||
766 | + major = gss_compare_name(&minor, saved_name, name, &equal); | ||
767 | + gss_release_name(&minor, &name); | ||
768 | + if (GSS_ERROR(major)) | ||
769 | + return 0; | ||
770 | + | ||
771 | + if (equal && (saved_lifetime < lifetime + now - 10)) | ||
772 | + return 1; | ||
773 | + | ||
774 | + return 0; | ||
775 | +} | ||
776 | + | ||
777 | #endif /* GSSAPI */ | ||
778 | Index: b/gss-serv-krb5.c | ||
779 | =================================================================== | ||
780 | --- a/gss-serv-krb5.c | ||
781 | +++ b/gss-serv-krb5.c | ||
782 | @@ -1,7 +1,7 @@ | ||
783 | /* $OpenBSD: gss-serv-krb5.c,v 1.7 2006/08/03 03:34:42 deraadt Exp $ */ | ||
784 | |||
785 | /* | ||
786 | - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. | ||
787 | + * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. | ||
788 | * | ||
789 | * Redistribution and use in source and binary forms, with or without | ||
790 | * modification, are permitted provided that the following conditions | ||
791 | @@ -120,6 +120,7 @@ | ||
792 | krb5_principal princ; | ||
793 | OM_uint32 maj_status, min_status; | ||
794 | int len; | ||
795 | + const char *new_ccname; | ||
796 | |||
797 | if (client->creds == NULL) { | ||
798 | debug("No credentials stored"); | ||
799 | @@ -168,11 +169,16 @@ | ||
800 | return; | ||
801 | } | ||
802 | |||
803 | - client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); | ||
804 | + new_ccname = krb5_cc_get_name(krb_context, ccache); | ||
805 | + | ||
806 | client->store.envvar = "KRB5CCNAME"; | ||
807 | - len = strlen(client->store.filename) + 6; | ||
808 | - client->store.envval = xmalloc(len); | ||
809 | - snprintf(client->store.envval, len, "FILE:%s", client->store.filename); | ||
810 | +#ifdef USE_CCAPI | ||
811 | + xasprintf(&client->store.envval, "API:%s", new_ccname); | ||
812 | + client->store.filename = NULL; | ||
813 | +#else | ||
814 | + xasprintf(&client->store.envval, "FILE:%s", new_ccname); | ||
815 | + client->store.filename = xstrdup(new_ccname); | ||
816 | +#endif | ||
817 | |||
818 | #ifdef USE_PAM | ||
819 | if (options.use_pam) | ||
820 | @@ -184,6 +190,71 @@ | ||
821 | return; | ||
822 | } | ||
823 | |||
824 | +int | ||
825 | +ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, | ||
826 | + ssh_gssapi_client *client) | ||
827 | +{ | ||
828 | + krb5_ccache ccache = NULL; | ||
829 | + krb5_principal principal = NULL; | ||
830 | + char *name = NULL; | ||
831 | + krb5_error_code problem; | ||
832 | + OM_uint32 maj_status, min_status; | ||
833 | + | ||
834 | + if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { | ||
835 | + logit("krb5_cc_resolve(): %.100s", | ||
836 | + krb5_get_err_text(krb_context, problem)); | ||
837 | + return 0; | ||
838 | + } | ||
839 | + | ||
840 | + /* Find out who the principal in this cache is */ | ||
841 | + if ((problem = krb5_cc_get_principal(krb_context, ccache, | ||
842 | + &principal))) { | ||
843 | + logit("krb5_cc_get_principal(): %.100s", | ||
844 | + krb5_get_err_text(krb_context, problem)); | ||
845 | + krb5_cc_close(krb_context, ccache); | ||
846 | + return 0; | ||
847 | + } | ||
848 | + | ||
849 | + if ((problem = krb5_unparse_name(krb_context, principal, &name))) { | ||
850 | + logit("krb5_unparse_name(): %.100s", | ||
851 | + krb5_get_err_text(krb_context, problem)); | ||
852 | + krb5_free_principal(krb_context, principal); | ||
853 | + krb5_cc_close(krb_context, ccache); | ||
854 | + return 0; | ||
855 | + } | ||
856 | + | ||
857 | + | ||
858 | + if (strcmp(name,client->exportedname.value)!=0) { | ||
859 | + debug("Name in local credentials cache differs. Not storing"); | ||
860 | + krb5_free_principal(krb_context, principal); | ||
861 | + krb5_cc_close(krb_context, ccache); | ||
862 | + krb5_free_unparsed_name(krb_context, name); | ||
863 | + return 0; | ||
864 | + } | ||
865 | + krb5_free_unparsed_name(krb_context, name); | ||
866 | + | ||
867 | + /* Name matches, so lets get on with it! */ | ||
868 | + | ||
869 | + if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { | ||
870 | + logit("krb5_cc_initialize(): %.100s", | ||
871 | + krb5_get_err_text(krb_context, problem)); | ||
872 | + krb5_free_principal(krb_context, principal); | ||
873 | + krb5_cc_close(krb_context, ccache); | ||
874 | + return 0; | ||
875 | + } | ||
876 | + | ||
877 | + krb5_free_principal(krb_context, principal); | ||
878 | + | ||
879 | + if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, | ||
880 | + ccache))) { | ||
881 | + logit("gss_krb5_copy_ccache() failed. Sorry!"); | ||
882 | + krb5_cc_close(krb_context, ccache); | ||
883 | + return 0; | ||
884 | + } | ||
885 | + | ||
886 | + return 1; | ||
887 | +} | ||
888 | + | ||
889 | ssh_gssapi_mech gssapi_kerberos_mech = { | ||
890 | "toWM5Slw5Ew8Mqkay+al2g==", | ||
891 | "Kerberos", | ||
892 | @@ -191,7 +262,8 @@ | ||
893 | NULL, | ||
894 | &ssh_gssapi_krb5_userok, | ||
895 | NULL, | ||
896 | - &ssh_gssapi_krb5_storecreds | ||
897 | + &ssh_gssapi_krb5_storecreds, | ||
898 | + &ssh_gssapi_krb5_updatecreds | ||
899 | }; | ||
900 | |||
901 | #endif /* KRB5 */ | ||
902 | Index: b/gss-serv.c | ||
903 | =================================================================== | ||
904 | --- a/gss-serv.c | ||
905 | +++ b/gss-serv.c | ||
906 | @@ -1,7 +1,7 @@ | ||
907 | /* $OpenBSD: gss-serv.c,v 1.22 2008/05/08 12:02:23 djm Exp $ */ | ||
908 | |||
909 | /* | ||
910 | - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. | ||
911 | + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. | ||
912 | * | ||
913 | * Redistribution and use in source and binary forms, with or without | ||
914 | * modification, are permitted provided that the following conditions | ||
915 | @@ -45,15 +45,20 @@ | ||
916 | #include "channels.h" | ||
917 | #include "session.h" | ||
918 | #include "misc.h" | ||
919 | +#include "servconf.h" | ||
920 | +#include "uidswap.h" | ||
921 | |||
922 | #include "ssh-gss.h" | ||
923 | +#include "monitor_wrap.h" | ||
924 | + | ||
925 | +extern ServerOptions options; | ||
926 | |||
927 | static ssh_gssapi_client gssapi_client = | ||
928 | { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, | ||
929 | - GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL}}; | ||
930 | + GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, {NULL, NULL, NULL}, 0, 0}; | ||
931 | |||
932 | ssh_gssapi_mech gssapi_null_mech = | ||
933 | - { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; | ||
934 | + { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; | ||
935 | |||
936 | #ifdef KRB5 | ||
937 | extern ssh_gssapi_mech gssapi_kerberos_mech; | ||
938 | @@ -81,25 +86,32 @@ | ||
939 | char lname[MAXHOSTNAMELEN]; | ||
940 | gss_OID_set oidset; | ||
941 | |||
942 | - gss_create_empty_oid_set(&status, &oidset); | ||
943 | - gss_add_oid_set_member(&status, ctx->oid, &oidset); | ||
944 | + if (options.gss_strict_acceptor) { | ||
945 | + gss_create_empty_oid_set(&status, &oidset); | ||
946 | + gss_add_oid_set_member(&status, ctx->oid, &oidset); | ||
947 | + | ||
948 | + if (gethostname(lname, MAXHOSTNAMELEN)) { | ||
949 | + gss_release_oid_set(&status, &oidset); | ||
950 | + return (-1); | ||
951 | + } | ||
952 | |||
953 | - if (gethostname(lname, MAXHOSTNAMELEN)) { | ||
954 | - gss_release_oid_set(&status, &oidset); | ||
955 | - return (-1); | ||
956 | - } | ||
957 | + if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { | ||
958 | + gss_release_oid_set(&status, &oidset); | ||
959 | + return (ctx->major); | ||
960 | + } | ||
961 | + | ||
962 | + if ((ctx->major = gss_acquire_cred(&ctx->minor, | ||
963 | + ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, | ||
964 | + NULL, NULL))) | ||
965 | + ssh_gssapi_error(ctx); | ||
966 | |||
967 | - if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { | ||
968 | gss_release_oid_set(&status, &oidset); | ||
969 | return (ctx->major); | ||
970 | + } else { | ||
971 | + ctx->name = GSS_C_NO_NAME; | ||
972 | + ctx->creds = GSS_C_NO_CREDENTIAL; | ||
973 | } | ||
974 | - | ||
975 | - if ((ctx->major = gss_acquire_cred(&ctx->minor, | ||
976 | - ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL))) | ||
977 | - ssh_gssapi_error(ctx); | ||
978 | - | ||
979 | - gss_release_oid_set(&status, &oidset); | ||
980 | - return (ctx->major); | ||
981 | + return GSS_S_COMPLETE; | ||
982 | } | ||
983 | |||
984 | /* Privileged */ | ||
985 | @@ -114,6 +126,29 @@ | ||
986 | } | ||
987 | |||
988 | /* Unprivileged */ | ||
989 | +char * | ||
990 | +ssh_gssapi_server_mechanisms() { | ||
991 | + gss_OID_set supported; | ||
992 | + | ||
993 | + ssh_gssapi_supported_oids(&supported); | ||
994 | + return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech, | ||
995 | + NULL, NULL)); | ||
996 | +} | ||
997 | + | ||
998 | +/* Unprivileged */ | ||
999 | +int | ||
1000 | +ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, | ||
1001 | + const char *dummy) { | ||
1002 | + Gssctxt *ctx = NULL; | ||
1003 | + int res; | ||
1004 | + | ||
1005 | + res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); | ||
1006 | + ssh_gssapi_delete_ctx(&ctx); | ||
1007 | + | ||
1008 | + return (res); | ||
1009 | +} | ||
1010 | + | ||
1011 | +/* Unprivileged */ | ||
1012 | void | ||
1013 | ssh_gssapi_supported_oids(gss_OID_set *oidset) | ||
1014 | { | ||
1015 | @@ -123,7 +158,9 @@ | ||
1016 | gss_OID_set supported; | ||
1017 | |||
1018 | gss_create_empty_oid_set(&min_status, oidset); | ||
1019 | - gss_indicate_mechs(&min_status, &supported); | ||
1020 | + | ||
1021 | + if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) | ||
1022 | + return; | ||
1023 | |||
1024 | while (supported_mechs[i]->name != NULL) { | ||
1025 | if (GSS_ERROR(gss_test_oid_set_member(&min_status, | ||
1026 | @@ -247,8 +284,48 @@ | ||
1027 | ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | ||
1028 | { | ||
1029 | int i = 0; | ||
1030 | + int equal = 0; | ||
1031 | + gss_name_t new_name = GSS_C_NO_NAME; | ||
1032 | + gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; | ||
1033 | + | ||
1034 | + if (options.gss_store_rekey && client->used && ctx->client_creds) { | ||
1035 | + if (client->mech->oid.length != ctx->oid->length || | ||
1036 | + (memcmp(client->mech->oid.elements, | ||
1037 | + ctx->oid->elements, ctx->oid->length) !=0)) { | ||
1038 | + debug("Rekeyed credentials have different mechanism"); | ||
1039 | + return GSS_S_COMPLETE; | ||
1040 | + } | ||
1041 | + | ||
1042 | + if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, | ||
1043 | + ctx->client_creds, ctx->oid, &new_name, | ||
1044 | + NULL, NULL, NULL))) { | ||
1045 | + ssh_gssapi_error(ctx); | ||
1046 | + return (ctx->major); | ||
1047 | + } | ||
1048 | + | ||
1049 | + ctx->major = gss_compare_name(&ctx->minor, client->name, | ||
1050 | + new_name, &equal); | ||
1051 | |||
1052 | - gss_buffer_desc ename; | ||
1053 | + if (GSS_ERROR(ctx->major)) { | ||
1054 | + ssh_gssapi_error(ctx); | ||
1055 | + return (ctx->major); | ||
1056 | + } | ||
1057 | + | ||
1058 | + if (!equal) { | ||
1059 | + debug("Rekeyed credentials have different name"); | ||
1060 | + return GSS_S_COMPLETE; | ||
1061 | + } | ||
1062 | + | ||
1063 | + debug("Marking rekeyed credentials for export"); | ||
1064 | + | ||
1065 | + gss_release_name(&ctx->minor, &client->name); | ||
1066 | + gss_release_cred(&ctx->minor, &client->creds); | ||
1067 | + client->name = new_name; | ||
1068 | + client->creds = ctx->client_creds; | ||
1069 | + ctx->client_creds = GSS_C_NO_CREDENTIAL; | ||
1070 | + client->updated = 1; | ||
1071 | + return GSS_S_COMPLETE; | ||
1072 | + } | ||
1073 | |||
1074 | client->mech = NULL; | ||
1075 | |||
1076 | @@ -263,6 +340,13 @@ | ||
1077 | if (client->mech == NULL) | ||
1078 | return GSS_S_FAILURE; | ||
1079 | |||
1080 | + if (ctx->client_creds && | ||
1081 | + (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, | ||
1082 | + ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { | ||
1083 | + ssh_gssapi_error(ctx); | ||
1084 | + return (ctx->major); | ||
1085 | + } | ||
1086 | + | ||
1087 | if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, | ||
1088 | &client->displayname, NULL))) { | ||
1089 | ssh_gssapi_error(ctx); | ||
1090 | @@ -280,6 +364,8 @@ | ||
1091 | return (ctx->major); | ||
1092 | } | ||
1093 | |||
1094 | + gss_release_buffer(&ctx->minor, &ename); | ||
1095 | + | ||
1096 | /* We can't copy this structure, so we just move the pointer to it */ | ||
1097 | client->creds = ctx->client_creds; | ||
1098 | ctx->client_creds = GSS_C_NO_CREDENTIAL; | ||
1099 | @@ -327,7 +413,7 @@ | ||
1100 | |||
1101 | /* Privileged */ | ||
1102 | int | ||
1103 | -ssh_gssapi_userok(char *user) | ||
1104 | +ssh_gssapi_userok(char *user, struct passwd *pw) | ||
1105 | { | ||
1106 | OM_uint32 lmin; | ||
1107 | |||
1108 | @@ -337,9 +423,11 @@ | ||
1109 | return 0; | ||
1110 | } | ||
1111 | if (gssapi_client.mech && gssapi_client.mech->userok) | ||
1112 | - if ((*gssapi_client.mech->userok)(&gssapi_client, user)) | ||
1113 | + if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { | ||
1114 | + gssapi_client.used = 1; | ||
1115 | + gssapi_client.store.owner = pw; | ||
1116 | return 1; | ||
1117 | - else { | ||
1118 | + } else { | ||
1119 | /* Destroy delegated credentials if userok fails */ | ||
1120 | gss_release_buffer(&lmin, &gssapi_client.displayname); | ||
1121 | gss_release_buffer(&lmin, &gssapi_client.exportedname); | ||
1122 | @@ -352,14 +440,90 @@ | ||
1123 | return (0); | ||
1124 | } | ||
1125 | |||
1126 | -/* Privileged */ | ||
1127 | -OM_uint32 | ||
1128 | -ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) | ||
1129 | +/* These bits are only used for rekeying. The unpriviledged child is running | ||
1130 | + * as the user, the monitor is root. | ||
1131 | + * | ||
1132 | + * In the child, we want to : | ||
1133 | + * *) Ask the monitor to store our credentials into the store we specify | ||
1134 | + * *) If it succeeds, maybe do a PAM update | ||
1135 | + */ | ||
1136 | + | ||
1137 | +/* Stuff for PAM */ | ||
1138 | + | ||
1139 | +#ifdef USE_PAM | ||
1140 | +static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, | ||
1141 | + struct pam_response **resp, void *data) | ||
1142 | { | ||
1143 | - ctx->major = gss_verify_mic(&ctx->minor, ctx->context, | ||
1144 | - gssbuf, gssmic, NULL); | ||
1145 | + return (PAM_CONV_ERR); | ||
1146 | +} | ||
1147 | +#endif | ||
1148 | |||
1149 | - return (ctx->major); | ||
1150 | +void | ||
1151 | +ssh_gssapi_rekey_creds() { | ||
1152 | + int ok; | ||
1153 | + int ret; | ||
1154 | +#ifdef USE_PAM | ||
1155 | + pam_handle_t *pamh = NULL; | ||
1156 | + struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; | ||
1157 | + char *envstr; | ||
1158 | +#endif | ||
1159 | + | ||
1160 | + if (gssapi_client.store.filename == NULL && | ||
1161 | + gssapi_client.store.envval == NULL && | ||
1162 | + gssapi_client.store.envvar == NULL) | ||
1163 | + return; | ||
1164 | + | ||
1165 | + ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); | ||
1166 | + | ||
1167 | + if (!ok) | ||
1168 | + return; | ||
1169 | + | ||
1170 | + debug("Rekeyed credentials stored successfully"); | ||
1171 | + | ||
1172 | + /* Actually managing to play with the ssh pam stack from here will | ||
1173 | + * be next to impossible. In any case, we may want different options | ||
1174 | + * for rekeying. So, use our own :) | ||
1175 | + */ | ||
1176 | +#ifdef USE_PAM | ||
1177 | + if (!use_privsep) { | ||
1178 | + debug("Not even going to try and do PAM with privsep disabled"); | ||
1179 | + return; | ||
1180 | + } | ||
1181 | + | ||
1182 | + ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, | ||
1183 | + &pamconv, &pamh); | ||
1184 | + if (ret) | ||
1185 | + return; | ||
1186 | + | ||
1187 | + xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, | ||
1188 | + gssapi_client.store.envval); | ||
1189 | + | ||
1190 | + ret = pam_putenv(pamh, envstr); | ||
1191 | + if (!ret) | ||
1192 | + pam_setcred(pamh, PAM_REINITIALIZE_CRED); | ||
1193 | + pam_end(pamh, PAM_SUCCESS); | ||
1194 | +#endif | ||
1195 | +} | ||
1196 | + | ||
1197 | +int | ||
1198 | +ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { | ||
1199 | + int ok = 0; | ||
1200 | + | ||
1201 | + /* Check we've got credentials to store */ | ||
1202 | + if (!gssapi_client.updated) | ||
1203 | + return 0; | ||
1204 | + | ||
1205 | + gssapi_client.updated = 0; | ||
1206 | + | ||
1207 | + temporarily_use_uid(gssapi_client.store.owner); | ||
1208 | + if (gssapi_client.mech && gssapi_client.mech->updatecreds) | ||
1209 | + ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); | ||
1210 | + else | ||
1211 | + debug("No update function for this mechanism"); | ||
1212 | + | ||
1213 | + restore_uid(); | ||
1214 | + | ||
1215 | + return ok; | ||
1216 | } | ||
1217 | |||
1218 | #endif | ||
1219 | Index: b/kex.c | ||
1220 | =================================================================== | ||
1221 | --- a/kex.c | ||
1222 | +++ b/kex.c | ||
1223 | @@ -50,6 +50,10 @@ | ||
1224 | #include "monitor.h" | ||
1225 | #include "roaming.h" | ||
1226 | |||
1227 | +#ifdef GSSAPI | ||
1228 | +#include "ssh-gss.h" | ||
1229 | +#endif | ||
1230 | + | ||
1231 | #if OPENSSL_VERSION_NUMBER >= 0x00907000L | ||
1232 | # if defined(HAVE_EVP_SHA256) | ||
1233 | # define evp_ssh_sha256 EVP_sha256 | ||
1234 | @@ -358,6 +362,20 @@ | ||
1235 | k->kex_type = KEX_ECDH_SHA2; | ||
1236 | k->evp_md = kex_ecdh_name_to_evpmd(k->name); | ||
1237 | #endif | ||
1238 | +#ifdef GSSAPI | ||
1239 | + } else if (strncmp(k->name, KEX_GSS_GEX_SHA1_ID, | ||
1240 | + sizeof(KEX_GSS_GEX_SHA1_ID) - 1) == 0) { | ||
1241 | + k->kex_type = KEX_GSS_GEX_SHA1; | ||
1242 | + k->evp_md = EVP_sha1(); | ||
1243 | + } else if (strncmp(k->name, KEX_GSS_GRP1_SHA1_ID, | ||
1244 | + sizeof(KEX_GSS_GRP1_SHA1_ID) - 1) == 0) { | ||
1245 | + k->kex_type = KEX_GSS_GRP1_SHA1; | ||
1246 | + k->evp_md = EVP_sha1(); | ||
1247 | + } else if (strncmp(k->name, KEX_GSS_GRP14_SHA1_ID, | ||
1248 | + sizeof(KEX_GSS_GRP14_SHA1_ID) - 1) == 0) { | ||
1249 | + k->kex_type = KEX_GSS_GRP14_SHA1; | ||
1250 | + k->evp_md = EVP_sha1(); | ||
1251 | +#endif | ||
1252 | } else | ||
1253 | fatal("bad kex alg %s", k->name); | ||
1254 | } | ||
1255 | Index: b/kex.h | ||
1256 | =================================================================== | ||
1257 | --- a/kex.h | ||
1258 | +++ b/kex.h | ||
1259 | @@ -73,6 +73,9 @@ | ||
1260 | KEX_DH_GEX_SHA1, | ||
1261 | KEX_DH_GEX_SHA256, | ||
1262 | KEX_ECDH_SHA2, | ||
1263 | + KEX_GSS_GRP1_SHA1, | ||
1264 | + KEX_GSS_GRP14_SHA1, | ||
1265 | + KEX_GSS_GEX_SHA1, | ||
1266 | KEX_MAX | ||
1267 | }; | ||
1268 | |||
1269 | @@ -129,6 +132,12 @@ | ||
1270 | sig_atomic_t done; | ||
1271 | int flags; | ||
1272 | const EVP_MD *evp_md; | ||
1273 | +#ifdef GSSAPI | ||
1274 | + int gss_deleg_creds; | ||
1275 | + int gss_trust_dns; | ||
1276 | + char *gss_host; | ||
1277 | + char *gss_client; | ||
1278 | +#endif | ||
1279 | char *client_version_string; | ||
1280 | char *server_version_string; | ||
1281 | int (*verify_host_key)(Key *); | ||
1282 | @@ -156,6 +165,11 @@ | ||
1283 | void kexecdh_client(Kex *); | ||
1284 | void kexecdh_server(Kex *); | ||
1285 | |||
1286 | +#ifdef GSSAPI | ||
1287 | +void kexgss_client(Kex *); | ||
1288 | +void kexgss_server(Kex *); | ||
1289 | +#endif | ||
1290 | + | ||
1291 | void | ||
1292 | kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, | ||
1293 | BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); | ||
1294 | Index: b/kexgssc.c | ||
1295 | =================================================================== | ||
1296 | --- /dev/null | ||
1297 | +++ b/kexgssc.c | ||
1298 | @@ -0,0 +1,334 @@ | ||
1299 | +/* | ||
1300 | + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. | ||
1301 | + * | ||
1302 | + * Redistribution and use in source and binary forms, with or without | ||
1303 | + * modification, are permitted provided that the following conditions | ||
1304 | + * are met: | ||
1305 | + * 1. Redistributions of source code must retain the above copyright | ||
1306 | + * notice, this list of conditions and the following disclaimer. | ||
1307 | + * 2. Redistributions in binary form must reproduce the above copyright | ||
1308 | + * notice, this list of conditions and the following disclaimer in the | ||
1309 | + * documentation and/or other materials provided with the distribution. | ||
1310 | + * | ||
1311 | + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR | ||
1312 | + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
1313 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
1314 | + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
1315 | + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
1316 | + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
1317 | + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
1318 | + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
1319 | + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
1320 | + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
1321 | + */ | ||
1322 | + | ||
1323 | +#include "includes.h" | ||
1324 | + | ||
1325 | +#ifdef GSSAPI | ||
1326 | + | ||
1327 | +#include "includes.h" | ||
1328 | + | ||
1329 | +#include <openssl/crypto.h> | ||
1330 | +#include <openssl/bn.h> | ||
1331 | + | ||
1332 | +#include <string.h> | ||
1333 | + | ||
1334 | +#include "xmalloc.h" | ||
1335 | +#include "buffer.h" | ||
1336 | +#include "ssh2.h" | ||
1337 | +#include "key.h" | ||
1338 | +#include "cipher.h" | ||
1339 | +#include "kex.h" | ||
1340 | +#include "log.h" | ||
1341 | +#include "packet.h" | ||
1342 | +#include "dh.h" | ||
1343 | + | ||
1344 | +#include "ssh-gss.h" | ||
1345 | + | ||
1346 | +void | ||
1347 | +kexgss_client(Kex *kex) { | ||
1348 | + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | ||
1349 | + gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; | ||
1350 | + Gssctxt *ctxt; | ||
1351 | + OM_uint32 maj_status, min_status, ret_flags; | ||
1352 | + u_int klen, kout, slen = 0, hashlen, strlen; | ||
1353 | + DH *dh; | ||
1354 | + BIGNUM *dh_server_pub = NULL; | ||
1355 | + BIGNUM *shared_secret = NULL; | ||
1356 | + BIGNUM *p = NULL; | ||
1357 | + BIGNUM *g = NULL; | ||
1358 | + u_char *kbuf, *hash; | ||
1359 | + u_char *serverhostkey = NULL; | ||
1360 | + u_char *empty = ""; | ||
1361 | + char *msg; | ||
1362 | + char *lang; | ||
1363 | + int type = 0; | ||
1364 | + int first = 1; | ||
1365 | + int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; | ||
1366 | + | ||
1367 | + /* Initialise our GSSAPI world */ | ||
1368 | + ssh_gssapi_build_ctx(&ctxt); | ||
1369 | + if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) | ||
1370 | + == GSS_C_NO_OID) | ||
1371 | + fatal("Couldn't identify host exchange"); | ||
1372 | + | ||
1373 | + if (ssh_gssapi_import_name(ctxt, kex->gss_host)) | ||
1374 | + fatal("Couldn't import hostname"); | ||
1375 | + | ||
1376 | + if (kex->gss_client && | ||
1377 | + ssh_gssapi_client_identity(ctxt, kex->gss_client)) | ||
1378 | + fatal("Couldn't acquire client credentials"); | ||
1379 | + | ||
1380 | + switch (kex->kex_type) { | ||
1381 | + case KEX_GSS_GRP1_SHA1: | ||
1382 | + dh = dh_new_group1(); | ||
1383 | + break; | ||
1384 | + case KEX_GSS_GRP14_SHA1: | ||
1385 | + dh = dh_new_group14(); | ||
1386 | + break; | ||
1387 | + case KEX_GSS_GEX_SHA1: | ||
1388 | + debug("Doing group exchange\n"); | ||
1389 | + nbits = dh_estimate(kex->we_need * 8); | ||
1390 | + packet_start(SSH2_MSG_KEXGSS_GROUPREQ); | ||
1391 | + packet_put_int(min); | ||
1392 | + packet_put_int(nbits); | ||
1393 | + packet_put_int(max); | ||
1394 | + | ||
1395 | + packet_send(); | ||
1396 | + | ||
1397 | + packet_read_expect(SSH2_MSG_KEXGSS_GROUP); | ||
1398 | + | ||
1399 | + if ((p = BN_new()) == NULL) | ||
1400 | + fatal("BN_new() failed"); | ||
1401 | + packet_get_bignum2(p); | ||
1402 | + if ((g = BN_new()) == NULL) | ||
1403 | + fatal("BN_new() failed"); | ||
1404 | + packet_get_bignum2(g); | ||
1405 | + packet_check_eom(); | ||
1406 | + | ||
1407 | + if (BN_num_bits(p) < min || BN_num_bits(p) > max) | ||
1408 | + fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", | ||
1409 | + min, BN_num_bits(p), max); | ||
1410 | + | ||
1411 | + dh = dh_new_group(g, p); | ||
1412 | + break; | ||
1413 | + default: | ||
1414 | + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
1415 | + } | ||
1416 | + | ||
1417 | + /* Step 1 - e is dh->pub_key */ | ||
1418 | + dh_gen_key(dh, kex->we_need * 8); | ||
1419 | + | ||
1420 | + /* This is f, we initialise it now to make life easier */ | ||
1421 | + dh_server_pub = BN_new(); | ||
1422 | + if (dh_server_pub == NULL) | ||
1423 | + fatal("dh_server_pub == NULL"); | ||
1424 | + | ||
1425 | + token_ptr = GSS_C_NO_BUFFER; | ||
1426 | + | ||
1427 | + do { | ||
1428 | + debug("Calling gss_init_sec_context"); | ||
1429 | + | ||
1430 | + maj_status = ssh_gssapi_init_ctx(ctxt, | ||
1431 | + kex->gss_deleg_creds, token_ptr, &send_tok, | ||
1432 | + &ret_flags); | ||
1433 | + | ||
1434 | + if (GSS_ERROR(maj_status)) { | ||
1435 | + if (send_tok.length != 0) { | ||
1436 | + packet_start(SSH2_MSG_KEXGSS_CONTINUE); | ||
1437 | + packet_put_string(send_tok.value, | ||
1438 | + send_tok.length); | ||
1439 | + } | ||
1440 | + fatal("gss_init_context failed"); | ||
1441 | + } | ||
1442 | + | ||
1443 | + /* If we've got an old receive buffer get rid of it */ | ||
1444 | + if (token_ptr != GSS_C_NO_BUFFER) | ||
1445 | + xfree(recv_tok.value); | ||
1446 | + | ||
1447 | + if (maj_status == GSS_S_COMPLETE) { | ||
1448 | + /* If mutual state flag is not true, kex fails */ | ||
1449 | + if (!(ret_flags & GSS_C_MUTUAL_FLAG)) | ||
1450 | + fatal("Mutual authentication failed"); | ||
1451 | + | ||
1452 | + /* If integ avail flag is not true kex fails */ | ||
1453 | + if (!(ret_flags & GSS_C_INTEG_FLAG)) | ||
1454 | + fatal("Integrity check failed"); | ||
1455 | + } | ||
1456 | + | ||
1457 | + /* | ||
1458 | + * If we have data to send, then the last message that we | ||
1459 | + * received cannot have been a 'complete'. | ||
1460 | + */ | ||
1461 | + if (send_tok.length != 0) { | ||
1462 | + if (first) { | ||
1463 | + packet_start(SSH2_MSG_KEXGSS_INIT); | ||
1464 | + packet_put_string(send_tok.value, | ||
1465 | + send_tok.length); | ||
1466 | + packet_put_bignum2(dh->pub_key); | ||
1467 | + first = 0; | ||
1468 | + } else { | ||
1469 | + packet_start(SSH2_MSG_KEXGSS_CONTINUE); | ||
1470 | + packet_put_string(send_tok.value, | ||
1471 | + send_tok.length); | ||
1472 | + } | ||
1473 | + packet_send(); | ||
1474 | + gss_release_buffer(&min_status, &send_tok); | ||
1475 | + | ||
1476 | + /* If we've sent them data, they should reply */ | ||
1477 | + do { | ||
1478 | + type = packet_read(); | ||
1479 | + if (type == SSH2_MSG_KEXGSS_HOSTKEY) { | ||
1480 | + debug("Received KEXGSS_HOSTKEY"); | ||
1481 | + if (serverhostkey) | ||
1482 | + fatal("Server host key received more than once"); | ||
1483 | + serverhostkey = | ||
1484 | + packet_get_string(&slen); | ||
1485 | + } | ||
1486 | + } while (type == SSH2_MSG_KEXGSS_HOSTKEY); | ||
1487 | + | ||
1488 | + switch (type) { | ||
1489 | + case SSH2_MSG_KEXGSS_CONTINUE: | ||
1490 | + debug("Received GSSAPI_CONTINUE"); | ||
1491 | + if (maj_status == GSS_S_COMPLETE) | ||
1492 | + fatal("GSSAPI Continue received from server when complete"); | ||
1493 | + recv_tok.value = packet_get_string(&strlen); | ||
1494 | + recv_tok.length = strlen; | ||
1495 | + break; | ||
1496 | + case SSH2_MSG_KEXGSS_COMPLETE: | ||
1497 | + debug("Received GSSAPI_COMPLETE"); | ||
1498 | + packet_get_bignum2(dh_server_pub); | ||
1499 | + msg_tok.value = packet_get_string(&strlen); | ||
1500 | + msg_tok.length = strlen; | ||
1501 | + | ||
1502 | + /* Is there a token included? */ | ||
1503 | + if (packet_get_char()) { | ||
1504 | + recv_tok.value= | ||
1505 | + packet_get_string(&strlen); | ||
1506 | + recv_tok.length = strlen; | ||
1507 | + /* If we're already complete - protocol error */ | ||
1508 | + if (maj_status == GSS_S_COMPLETE) | ||
1509 | + packet_disconnect("Protocol error: received token when complete"); | ||
1510 | + } else { | ||
1511 | + /* No token included */ | ||
1512 | + if (maj_status != GSS_S_COMPLETE) | ||
1513 | + packet_disconnect("Protocol error: did not receive final token"); | ||
1514 | + } | ||
1515 | + break; | ||
1516 | + case SSH2_MSG_KEXGSS_ERROR: | ||
1517 | + debug("Received Error"); | ||
1518 | + maj_status = packet_get_int(); | ||
1519 | + min_status = packet_get_int(); | ||
1520 | + msg = packet_get_string(NULL); | ||
1521 | + lang = packet_get_string(NULL); | ||
1522 | + fatal("GSSAPI Error: \n%.400s",msg); | ||
1523 | + default: | ||
1524 | + packet_disconnect("Protocol error: didn't expect packet type %d", | ||
1525 | + type); | ||
1526 | + } | ||
1527 | + token_ptr = &recv_tok; | ||
1528 | + } else { | ||
1529 | + /* No data, and not complete */ | ||
1530 | + if (maj_status != GSS_S_COMPLETE) | ||
1531 | + fatal("Not complete, and no token output"); | ||
1532 | + } | ||
1533 | + } while (maj_status & GSS_S_CONTINUE_NEEDED); | ||
1534 | + | ||
1535 | + /* | ||
1536 | + * We _must_ have received a COMPLETE message in reply from the | ||
1537 | + * server, which will have set dh_server_pub and msg_tok | ||
1538 | + */ | ||
1539 | + | ||
1540 | + if (type != SSH2_MSG_KEXGSS_COMPLETE) | ||
1541 | + fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); | ||
1542 | + | ||
1543 | + /* Check f in range [1, p-1] */ | ||
1544 | + if (!dh_pub_is_valid(dh, dh_server_pub)) | ||
1545 | + packet_disconnect("bad server public DH value"); | ||
1546 | + | ||
1547 | + /* compute K=f^x mod p */ | ||
1548 | + klen = DH_size(dh); | ||
1549 | + kbuf = xmalloc(klen); | ||
1550 | + kout = DH_compute_key(kbuf, dh_server_pub, dh); | ||
1551 | + if (kout < 0) | ||
1552 | + fatal("DH_compute_key: failed"); | ||
1553 | + | ||
1554 | + shared_secret = BN_new(); | ||
1555 | + if (shared_secret == NULL) | ||
1556 | + fatal("kexgss_client: BN_new failed"); | ||
1557 | + | ||
1558 | + if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) | ||
1559 | + fatal("kexdh_client: BN_bin2bn failed"); | ||
1560 | + | ||
1561 | + memset(kbuf, 0, klen); | ||
1562 | + xfree(kbuf); | ||
1563 | + | ||
1564 | + switch (kex->kex_type) { | ||
1565 | + case KEX_GSS_GRP1_SHA1: | ||
1566 | + case KEX_GSS_GRP14_SHA1: | ||
1567 | + kex_dh_hash( kex->client_version_string, | ||
1568 | + kex->server_version_string, | ||
1569 | + buffer_ptr(&kex->my), buffer_len(&kex->my), | ||
1570 | + buffer_ptr(&kex->peer), buffer_len(&kex->peer), | ||
1571 | + (serverhostkey ? serverhostkey : empty), slen, | ||
1572 | + dh->pub_key, /* e */ | ||
1573 | + dh_server_pub, /* f */ | ||
1574 | + shared_secret, /* K */ | ||
1575 | + &hash, &hashlen | ||
1576 | + ); | ||
1577 | + break; | ||
1578 | + case KEX_GSS_GEX_SHA1: | ||
1579 | + kexgex_hash( | ||
1580 | + kex->evp_md, | ||
1581 | + kex->client_version_string, | ||
1582 | + kex->server_version_string, | ||
1583 | + buffer_ptr(&kex->my), buffer_len(&kex->my), | ||
1584 | + buffer_ptr(&kex->peer), buffer_len(&kex->peer), | ||
1585 | + (serverhostkey ? serverhostkey : empty), slen, | ||
1586 | + min, nbits, max, | ||
1587 | + dh->p, dh->g, | ||
1588 | + dh->pub_key, | ||
1589 | + dh_server_pub, | ||
1590 | + shared_secret, | ||
1591 | + &hash, &hashlen | ||
1592 | + ); | ||
1593 | + break; | ||
1594 | + default: | ||
1595 | + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
1596 | + } | ||
1597 | + | ||
1598 | + gssbuf.value = hash; | ||
1599 | + gssbuf.length = hashlen; | ||
1600 | + | ||
1601 | + /* Verify that the hash matches the MIC we just got. */ | ||
1602 | + if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) | ||
1603 | + packet_disconnect("Hash's MIC didn't verify"); | ||
1604 | + | ||
1605 | + xfree(msg_tok.value); | ||
1606 | + | ||
1607 | + DH_free(dh); | ||
1608 | + if (serverhostkey) | ||
1609 | + xfree(serverhostkey); | ||
1610 | + BN_clear_free(dh_server_pub); | ||
1611 | + | ||
1612 | + /* save session id */ | ||
1613 | + if (kex->session_id == NULL) { | ||
1614 | + kex->session_id_len = hashlen; | ||
1615 | + kex->session_id = xmalloc(kex->session_id_len); | ||
1616 | + memcpy(kex->session_id, hash, kex->session_id_len); | ||
1617 | + } | ||
1618 | + | ||
1619 | + if (kex->gss_deleg_creds) | ||
1620 | + ssh_gssapi_credentials_updated(ctxt); | ||
1621 | + | ||
1622 | + if (gss_kex_context == NULL) | ||
1623 | + gss_kex_context = ctxt; | ||
1624 | + else | ||
1625 | + ssh_gssapi_delete_ctx(&ctxt); | ||
1626 | + | ||
1627 | + kex_derive_keys(kex, hash, hashlen, shared_secret); | ||
1628 | + BN_clear_free(shared_secret); | ||
1629 | + kex_finish(kex); | ||
1630 | +} | ||
1631 | + | ||
1632 | +#endif /* GSSAPI */ | ||
1633 | Index: b/kexgsss.c | ||
1634 | =================================================================== | ||
1635 | --- /dev/null | ||
1636 | +++ b/kexgsss.c | ||
1637 | @@ -0,0 +1,288 @@ | ||
1638 | +/* | ||
1639 | + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. | ||
1640 | + * | ||
1641 | + * Redistribution and use in source and binary forms, with or without | ||
1642 | + * modification, are permitted provided that the following conditions | ||
1643 | + * are met: | ||
1644 | + * 1. Redistributions of source code must retain the above copyright | ||
1645 | + * notice, this list of conditions and the following disclaimer. | ||
1646 | + * 2. Redistributions in binary form must reproduce the above copyright | ||
1647 | + * notice, this list of conditions and the following disclaimer in the | ||
1648 | + * documentation and/or other materials provided with the distribution. | ||
1649 | + * | ||
1650 | + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR | ||
1651 | + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
1652 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
1653 | + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
1654 | + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
1655 | + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
1656 | + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
1657 | + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
1658 | + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
1659 | + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
1660 | + */ | ||
1661 | + | ||
1662 | +#include "includes.h" | ||
1663 | + | ||
1664 | +#ifdef GSSAPI | ||
1665 | + | ||
1666 | +#include <string.h> | ||
1667 | + | ||
1668 | +#include <openssl/crypto.h> | ||
1669 | +#include <openssl/bn.h> | ||
1670 | + | ||
1671 | +#include "xmalloc.h" | ||
1672 | +#include "buffer.h" | ||
1673 | +#include "ssh2.h" | ||
1674 | +#include "key.h" | ||
1675 | +#include "cipher.h" | ||
1676 | +#include "kex.h" | ||
1677 | +#include "log.h" | ||
1678 | +#include "packet.h" | ||
1679 | +#include "dh.h" | ||
1680 | +#include "ssh-gss.h" | ||
1681 | +#include "monitor_wrap.h" | ||
1682 | +#include "servconf.h" | ||
1683 | + | ||
1684 | +extern ServerOptions options; | ||
1685 | + | ||
1686 | +void | ||
1687 | +kexgss_server(Kex *kex) | ||
1688 | +{ | ||
1689 | + OM_uint32 maj_status, min_status; | ||
1690 | + | ||
1691 | + /* | ||
1692 | + * Some GSSAPI implementations use the input value of ret_flags (an | ||
1693 | + * output variable) as a means of triggering mechanism specific | ||
1694 | + * features. Initializing it to zero avoids inadvertently | ||
1695 | + * activating this non-standard behaviour. | ||
1696 | + */ | ||
1697 | + | ||
1698 | + OM_uint32 ret_flags = 0; | ||
1699 | + gss_buffer_desc gssbuf, recv_tok, msg_tok; | ||
1700 | + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | ||
1701 | + Gssctxt *ctxt = NULL; | ||
1702 | + u_int slen, klen, kout, hashlen; | ||
1703 | + u_char *kbuf, *hash; | ||
1704 | + DH *dh; | ||
1705 | + int min = -1, max = -1, nbits = -1; | ||
1706 | + BIGNUM *shared_secret = NULL; | ||
1707 | + BIGNUM *dh_client_pub = NULL; | ||
1708 | + int type = 0; | ||
1709 | + gss_OID oid; | ||
1710 | + char *mechs; | ||
1711 | + | ||
1712 | + /* Initialise GSSAPI */ | ||
1713 | + | ||
1714 | + /* If we're rekeying, privsep means that some of the private structures | ||
1715 | + * in the GSSAPI code are no longer available. This kludges them back | ||
1716 | + * into life | ||
1717 | + */ | ||
1718 | + if (!ssh_gssapi_oid_table_ok()) | ||
1719 | + if ((mechs = ssh_gssapi_server_mechanisms())) | ||
1720 | + xfree(mechs); | ||
1721 | + | ||
1722 | + debug2("%s: Identifying %s", __func__, kex->name); | ||
1723 | + oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); | ||
1724 | + if (oid == GSS_C_NO_OID) | ||
1725 | + fatal("Unknown gssapi mechanism"); | ||
1726 | + | ||
1727 | + debug2("%s: Acquiring credentials", __func__); | ||
1728 | + | ||
1729 | + if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) | ||
1730 | + fatal("Unable to acquire credentials for the server"); | ||
1731 | + | ||
1732 | + switch (kex->kex_type) { | ||
1733 | + case KEX_GSS_GRP1_SHA1: | ||
1734 | + dh = dh_new_group1(); | ||
1735 | + break; | ||
1736 | + case KEX_GSS_GRP14_SHA1: | ||
1737 | + dh = dh_new_group14(); | ||
1738 | + break; | ||
1739 | + case KEX_GSS_GEX_SHA1: | ||
1740 | + debug("Doing group exchange"); | ||
1741 | + packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); | ||
1742 | + min = packet_get_int(); | ||
1743 | + nbits = packet_get_int(); | ||
1744 | + max = packet_get_int(); | ||
1745 | + min = MAX(DH_GRP_MIN, min); | ||
1746 | + max = MIN(DH_GRP_MAX, max); | ||
1747 | + packet_check_eom(); | ||
1748 | + if (max < min || nbits < min || max < nbits) | ||
1749 | + fatal("GSS_GEX, bad parameters: %d !< %d !< %d", | ||
1750 | + min, nbits, max); | ||
1751 | + dh = PRIVSEP(choose_dh(min, nbits, max)); | ||
1752 | + if (dh == NULL) | ||
1753 | + packet_disconnect("Protocol error: no matching group found"); | ||
1754 | + | ||
1755 | + packet_start(SSH2_MSG_KEXGSS_GROUP); | ||
1756 | + packet_put_bignum2(dh->p); | ||
1757 | + packet_put_bignum2(dh->g); | ||
1758 | + packet_send(); | ||
1759 | + | ||
1760 | + packet_write_wait(); | ||
1761 | + break; | ||
1762 | + default: | ||
1763 | + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
1764 | + } | ||
1765 | + | ||
1766 | + dh_gen_key(dh, kex->we_need * 8); | ||
1767 | + | ||
1768 | + do { | ||
1769 | + debug("Wait SSH2_MSG_GSSAPI_INIT"); | ||
1770 | + type = packet_read(); | ||
1771 | + switch(type) { | ||
1772 | + case SSH2_MSG_KEXGSS_INIT: | ||
1773 | + if (dh_client_pub != NULL) | ||
1774 | + fatal("Received KEXGSS_INIT after initialising"); | ||
1775 | + recv_tok.value = packet_get_string(&slen); | ||
1776 | + recv_tok.length = slen; | ||
1777 | + | ||
1778 | + if ((dh_client_pub = BN_new()) == NULL) | ||
1779 | + fatal("dh_client_pub == NULL"); | ||
1780 | + | ||
1781 | + packet_get_bignum2(dh_client_pub); | ||
1782 | + | ||
1783 | + /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ | ||
1784 | + break; | ||
1785 | + case SSH2_MSG_KEXGSS_CONTINUE: | ||
1786 | + recv_tok.value = packet_get_string(&slen); | ||
1787 | + recv_tok.length = slen; | ||
1788 | + break; | ||
1789 | + default: | ||
1790 | + packet_disconnect( | ||
1791 | + "Protocol error: didn't expect packet type %d", | ||
1792 | + type); | ||
1793 | + } | ||
1794 | + | ||
1795 | + maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, | ||
1796 | + &send_tok, &ret_flags)); | ||
1797 | + | ||
1798 | + xfree(recv_tok.value); | ||
1799 | + | ||
1800 | + if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) | ||
1801 | + fatal("Zero length token output when incomplete"); | ||
1802 | + | ||
1803 | + if (dh_client_pub == NULL) | ||
1804 | + fatal("No client public key"); | ||
1805 | + | ||
1806 | + if (maj_status & GSS_S_CONTINUE_NEEDED) { | ||
1807 | + debug("Sending GSSAPI_CONTINUE"); | ||
1808 | + packet_start(SSH2_MSG_KEXGSS_CONTINUE); | ||
1809 | + packet_put_string(send_tok.value, send_tok.length); | ||
1810 | + packet_send(); | ||
1811 | + gss_release_buffer(&min_status, &send_tok); | ||
1812 | + } | ||
1813 | + } while (maj_status & GSS_S_CONTINUE_NEEDED); | ||
1814 | + | ||
1815 | + if (GSS_ERROR(maj_status)) { | ||
1816 | + if (send_tok.length > 0) { | ||
1817 | + packet_start(SSH2_MSG_KEXGSS_CONTINUE); | ||
1818 | + packet_put_string(send_tok.value, send_tok.length); | ||
1819 | + packet_send(); | ||
1820 | + } | ||
1821 | + fatal("accept_ctx died"); | ||
1822 | + } | ||
1823 | + | ||
1824 | + if (!(ret_flags & GSS_C_MUTUAL_FLAG)) | ||
1825 | + fatal("Mutual Authentication flag wasn't set"); | ||
1826 | + | ||
1827 | + if (!(ret_flags & GSS_C_INTEG_FLAG)) | ||
1828 | + fatal("Integrity flag wasn't set"); | ||
1829 | + | ||
1830 | + if (!dh_pub_is_valid(dh, dh_client_pub)) | ||
1831 | + packet_disconnect("bad client public DH value"); | ||
1832 | + | ||
1833 | + klen = DH_size(dh); | ||
1834 | + kbuf = xmalloc(klen); | ||
1835 | + kout = DH_compute_key(kbuf, dh_client_pub, dh); | ||
1836 | + if (kout < 0) | ||
1837 | + fatal("DH_compute_key: failed"); | ||
1838 | + | ||
1839 | + shared_secret = BN_new(); | ||
1840 | + if (shared_secret == NULL) | ||
1841 | + fatal("kexgss_server: BN_new failed"); | ||
1842 | + | ||
1843 | + if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) | ||
1844 | + fatal("kexgss_server: BN_bin2bn failed"); | ||
1845 | + | ||
1846 | + memset(kbuf, 0, klen); | ||
1847 | + xfree(kbuf); | ||
1848 | + | ||
1849 | + switch (kex->kex_type) { | ||
1850 | + case KEX_GSS_GRP1_SHA1: | ||
1851 | + case KEX_GSS_GRP14_SHA1: | ||
1852 | + kex_dh_hash( | ||
1853 | + kex->client_version_string, kex->server_version_string, | ||
1854 | + buffer_ptr(&kex->peer), buffer_len(&kex->peer), | ||
1855 | + buffer_ptr(&kex->my), buffer_len(&kex->my), | ||
1856 | + NULL, 0, /* Change this if we start sending host keys */ | ||
1857 | + dh_client_pub, dh->pub_key, shared_secret, | ||
1858 | + &hash, &hashlen | ||
1859 | + ); | ||
1860 | + break; | ||
1861 | + case KEX_GSS_GEX_SHA1: | ||
1862 | + kexgex_hash( | ||
1863 | + kex->evp_md, | ||
1864 | + kex->client_version_string, kex->server_version_string, | ||
1865 | + buffer_ptr(&kex->peer), buffer_len(&kex->peer), | ||
1866 | + buffer_ptr(&kex->my), buffer_len(&kex->my), | ||
1867 | + NULL, 0, | ||
1868 | + min, nbits, max, | ||
1869 | + dh->p, dh->g, | ||
1870 | + dh_client_pub, | ||
1871 | + dh->pub_key, | ||
1872 | + shared_secret, | ||
1873 | + &hash, &hashlen | ||
1874 | + ); | ||
1875 | + break; | ||
1876 | + default: | ||
1877 | + fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
1878 | + } | ||
1879 | + | ||
1880 | + BN_clear_free(dh_client_pub); | ||
1881 | + | ||
1882 | + if (kex->session_id == NULL) { | ||
1883 | + kex->session_id_len = hashlen; | ||
1884 | + kex->session_id = xmalloc(kex->session_id_len); | ||
1885 | + memcpy(kex->session_id, hash, kex->session_id_len); | ||
1886 | + } | ||
1887 | + | ||
1888 | + gssbuf.value = hash; | ||
1889 | + gssbuf.length = hashlen; | ||
1890 | + | ||
1891 | + if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) | ||
1892 | + fatal("Couldn't get MIC"); | ||
1893 | + | ||
1894 | + packet_start(SSH2_MSG_KEXGSS_COMPLETE); | ||
1895 | + packet_put_bignum2(dh->pub_key); | ||
1896 | + packet_put_string(msg_tok.value,msg_tok.length); | ||
1897 | + | ||
1898 | + if (send_tok.length != 0) { | ||
1899 | + packet_put_char(1); /* true */ | ||
1900 | + packet_put_string(send_tok.value, send_tok.length); | ||
1901 | + } else { | ||
1902 | + packet_put_char(0); /* false */ | ||
1903 | + } | ||
1904 | + packet_send(); | ||
1905 | + | ||
1906 | + gss_release_buffer(&min_status, &send_tok); | ||
1907 | + gss_release_buffer(&min_status, &msg_tok); | ||
1908 | + | ||
1909 | + if (gss_kex_context == NULL) | ||
1910 | + gss_kex_context = ctxt; | ||
1911 | + else | ||
1912 | + ssh_gssapi_delete_ctx(&ctxt); | ||
1913 | + | ||
1914 | + DH_free(dh); | ||
1915 | + | ||
1916 | + kex_derive_keys(kex, hash, hashlen, shared_secret); | ||
1917 | + BN_clear_free(shared_secret); | ||
1918 | + kex_finish(kex); | ||
1919 | + | ||
1920 | + /* If this was a rekey, then save out any delegated credentials we | ||
1921 | + * just exchanged. */ | ||
1922 | + if (options.gss_store_rekey) | ||
1923 | + ssh_gssapi_rekey_creds(); | ||
1924 | +} | ||
1925 | +#endif /* GSSAPI */ | ||
1926 | Index: b/key.c | ||
1927 | =================================================================== | ||
1928 | --- a/key.c | ||
1929 | +++ b/key.c | ||
1930 | @@ -971,6 +971,8 @@ | ||
1931 | } | ||
1932 | break; | ||
1933 | #endif /* OPENSSL_HAS_ECC */ | ||
1934 | + case KEY_NULL: | ||
1935 | + return "null"; | ||
1936 | } | ||
1937 | return "ssh-unknown"; | ||
1938 | } | ||
1939 | @@ -1276,6 +1278,8 @@ | ||
1940 | strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) { | ||
1941 | return KEY_ECDSA_CERT; | ||
1942 | #endif | ||
1943 | + } else if (strcmp(name, "null") == 0) { | ||
1944 | + return KEY_NULL; | ||
1945 | } | ||
1946 | |||
1947 | debug2("key_type_from_name: unknown key type '%s'", name); | ||
1948 | Index: b/key.h | ||
1949 | =================================================================== | ||
1950 | --- a/key.h | ||
1951 | +++ b/key.h | ||
1952 | @@ -44,6 +44,7 @@ | ||
1953 | KEY_ECDSA_CERT, | ||
1954 | KEY_RSA_CERT_V00, | ||
1955 | KEY_DSA_CERT_V00, | ||
1956 | + KEY_NULL, | ||
1957 | KEY_UNSPEC | ||
1958 | }; | ||
1959 | enum fp_type { | ||
1960 | Index: b/monitor.c | ||
1961 | =================================================================== | ||
1962 | --- a/monitor.c | ||
1963 | +++ b/monitor.c | ||
1964 | @@ -172,6 +172,8 @@ | ||
1965 | int mm_answer_gss_accept_ctx(int, Buffer *); | ||
1966 | int mm_answer_gss_userok(int, Buffer *); | ||
1967 | int mm_answer_gss_checkmic(int, Buffer *); | ||
1968 | +int mm_answer_gss_sign(int, Buffer *); | ||
1969 | +int mm_answer_gss_updatecreds(int, Buffer *); | ||
1970 | #endif | ||
1971 | |||
1972 | #ifdef SSH_AUDIT_EVENTS | ||
1973 | @@ -241,6 +243,7 @@ | ||
1974 | {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, | ||
1975 | {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, | ||
1976 | {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, | ||
1977 | + {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, | ||
1978 | #endif | ||
1979 | #ifdef JPAKE | ||
1980 | {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata}, | ||
1981 | @@ -253,6 +256,12 @@ | ||
1982 | }; | ||
1983 | |||
1984 | struct mon_table mon_dispatch_postauth20[] = { | ||
1985 | +#ifdef GSSAPI | ||
1986 | + {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, | ||
1987 | + {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, | ||
1988 | + {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, | ||
1989 | + {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds}, | ||
1990 | +#endif | ||
1991 | {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, | ||
1992 | {MONITOR_REQ_SIGN, 0, mm_answer_sign}, | ||
1993 | {MONITOR_REQ_PTY, 0, mm_answer_pty}, | ||
1994 | @@ -357,6 +366,10 @@ | ||
1995 | /* Permit requests for moduli and signatures */ | ||
1996 | monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); | ||
1997 | monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); | ||
1998 | +#ifdef GSSAPI | ||
1999 | + /* and for the GSSAPI key exchange */ | ||
2000 | + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); | ||
2001 | +#endif | ||
2002 | } else { | ||
2003 | mon_dispatch = mon_dispatch_proto15; | ||
2004 | |||
2005 | @@ -443,6 +456,10 @@ | ||
2006 | monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); | ||
2007 | monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); | ||
2008 | monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); | ||
2009 | +#ifdef GSSAPI | ||
2010 | + /* and for the GSSAPI key exchange */ | ||
2011 | + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); | ||
2012 | +#endif | ||
2013 | } else { | ||
2014 | mon_dispatch = mon_dispatch_postauth15; | ||
2015 | monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); | ||
2016 | @@ -1692,6 +1709,13 @@ | ||
2017 | kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; | ||
2018 | kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; | ||
2019 | kex->kex[KEX_ECDH_SHA2] = kexecdh_server; | ||
2020 | +#ifdef GSSAPI | ||
2021 | + if (options.gss_keyex) { | ||
2022 | + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; | ||
2023 | + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; | ||
2024 | + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; | ||
2025 | + } | ||
2026 | +#endif | ||
2027 | kex->server = 1; | ||
2028 | kex->hostkey_type = buffer_get_int(m); | ||
2029 | kex->kex_type = buffer_get_int(m); | ||
2030 | @@ -1898,6 +1922,9 @@ | ||
2031 | OM_uint32 major; | ||
2032 | u_int len; | ||
2033 | |||
2034 | + if (!options.gss_authentication && !options.gss_keyex) | ||
2035 | + fatal("In GSSAPI monitor when GSSAPI is disabled"); | ||
2036 | + | ||
2037 | goid.elements = buffer_get_string(m, &len); | ||
2038 | goid.length = len; | ||
2039 | |||
2040 | @@ -1925,6 +1952,9 @@ | ||
2041 | OM_uint32 flags = 0; /* GSI needs this */ | ||
2042 | u_int len; | ||
2043 | |||
2044 | + if (!options.gss_authentication && !options.gss_keyex) | ||
2045 | + fatal("In GSSAPI monitor when GSSAPI is disabled"); | ||
2046 | + | ||
2047 | in.value = buffer_get_string(m, &len); | ||
2048 | in.length = len; | ||
2049 | major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); | ||
2050 | @@ -1942,6 +1972,7 @@ | ||
2051 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); | ||
2052 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); | ||
2053 | monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); | ||
2054 | + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); | ||
2055 | } | ||
2056 | return (0); | ||
2057 | } | ||
2058 | @@ -1953,6 +1984,9 @@ | ||
2059 | OM_uint32 ret; | ||
2060 | u_int len; | ||
2061 | |||
2062 | + if (!options.gss_authentication && !options.gss_keyex) | ||
2063 | + fatal("In GSSAPI monitor when GSSAPI is disabled"); | ||
2064 | + | ||
2065 | gssbuf.value = buffer_get_string(m, &len); | ||
2066 | gssbuf.length = len; | ||
2067 | mic.value = buffer_get_string(m, &len); | ||
2068 | @@ -1979,7 +2013,11 @@ | ||
2069 | { | ||
2070 | int authenticated; | ||
2071 | |||
2072 | - authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); | ||
2073 | + if (!options.gss_authentication && !options.gss_keyex) | ||
2074 | + fatal("In GSSAPI monitor when GSSAPI is disabled"); | ||
2075 | + | ||
2076 | + authenticated = authctxt->valid && | ||
2077 | + ssh_gssapi_userok(authctxt->user, authctxt->pw); | ||
2078 | |||
2079 | buffer_clear(m); | ||
2080 | buffer_put_int(m, authenticated); | ||
2081 | @@ -1992,6 +2030,74 @@ | ||
2082 | /* Monitor loop will terminate if authenticated */ | ||
2083 | return (authenticated); | ||
2084 | } | ||
2085 | + | ||
2086 | +int | ||
2087 | +mm_answer_gss_sign(int socket, Buffer *m) | ||
2088 | +{ | ||
2089 | + gss_buffer_desc data; | ||
2090 | + gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; | ||
2091 | + OM_uint32 major, minor; | ||
2092 | + u_int len; | ||
2093 | + | ||
2094 | + if (!options.gss_authentication && !options.gss_keyex) | ||
2095 | + fatal("In GSSAPI monitor when GSSAPI is disabled"); | ||
2096 | + | ||
2097 | + data.value = buffer_get_string(m, &len); | ||
2098 | + data.length = len; | ||
2099 | + if (data.length != 20) | ||
2100 | + fatal("%s: data length incorrect: %d", __func__, | ||
2101 | + (int) data.length); | ||
2102 | + | ||
2103 | + /* Save the session ID on the first time around */ | ||
2104 | + if (session_id2_len == 0) { | ||
2105 | + session_id2_len = data.length; | ||
2106 | + session_id2 = xmalloc(session_id2_len); | ||
2107 | + memcpy(session_id2, data.value, session_id2_len); | ||
2108 | + } | ||
2109 | + major = ssh_gssapi_sign(gsscontext, &data, &hash); | ||
2110 | + | ||
2111 | + xfree(data.value); | ||
2112 | + | ||
2113 | + buffer_clear(m); | ||
2114 | + buffer_put_int(m, major); | ||
2115 | + buffer_put_string(m, hash.value, hash.length); | ||
2116 | + | ||
2117 | + mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); | ||
2118 | + | ||
2119 | + gss_release_buffer(&minor, &hash); | ||
2120 | + | ||
2121 | + /* Turn on getpwnam permissions */ | ||
2122 | + monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); | ||
2123 | + | ||
2124 | + /* And credential updating, for when rekeying */ | ||
2125 | + monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1); | ||
2126 | + | ||
2127 | + return (0); | ||
2128 | +} | ||
2129 | + | ||
2130 | +int | ||
2131 | +mm_answer_gss_updatecreds(int socket, Buffer *m) { | ||
2132 | + ssh_gssapi_ccache store; | ||
2133 | + int ok; | ||
2134 | + | ||
2135 | + store.filename = buffer_get_string(m, NULL); | ||
2136 | + store.envvar = buffer_get_string(m, NULL); | ||
2137 | + store.envval = buffer_get_string(m, NULL); | ||
2138 | + | ||
2139 | + ok = ssh_gssapi_update_creds(&store); | ||
2140 | + | ||
2141 | + xfree(store.filename); | ||
2142 | + xfree(store.envvar); | ||
2143 | + xfree(store.envval); | ||
2144 | + | ||
2145 | + buffer_clear(m); | ||
2146 | + buffer_put_int(m, ok); | ||
2147 | + | ||
2148 | + mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); | ||
2149 | + | ||
2150 | + return(0); | ||
2151 | +} | ||
2152 | + | ||
2153 | #endif /* GSSAPI */ | ||
2154 | |||
2155 | #ifdef JPAKE | ||
2156 | Index: b/monitor.h | ||
2157 | =================================================================== | ||
2158 | --- a/monitor.h | ||
2159 | +++ b/monitor.h | ||
2160 | @@ -53,6 +53,8 @@ | ||
2161 | MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP, | ||
2162 | MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK, | ||
2163 | MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC, | ||
2164 | + MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN, | ||
2165 | + MONITOR_REQ_GSSUPCREDS, MONITOR_ANS_GSSUPCREDS, | ||
2166 | MONITOR_REQ_PAM_START, | ||
2167 | MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT, | ||
2168 | MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX, | ||
2169 | Index: b/monitor_wrap.c | ||
2170 | =================================================================== | ||
2171 | --- a/monitor_wrap.c | ||
2172 | +++ b/monitor_wrap.c | ||
2173 | @@ -1232,7 +1232,7 @@ | ||
2174 | } | ||
2175 | |||
2176 | int | ||
2177 | -mm_ssh_gssapi_userok(char *user) | ||
2178 | +mm_ssh_gssapi_userok(char *user, struct passwd *pw) | ||
2179 | { | ||
2180 | Buffer m; | ||
2181 | int authenticated = 0; | ||
2182 | @@ -1249,6 +1249,51 @@ | ||
2183 | debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); | ||
2184 | return (authenticated); | ||
2185 | } | ||
2186 | + | ||
2187 | +OM_uint32 | ||
2188 | +mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) | ||
2189 | +{ | ||
2190 | + Buffer m; | ||
2191 | + OM_uint32 major; | ||
2192 | + u_int len; | ||
2193 | + | ||
2194 | + buffer_init(&m); | ||
2195 | + buffer_put_string(&m, data->value, data->length); | ||
2196 | + | ||
2197 | + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m); | ||
2198 | + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); | ||
2199 | + | ||
2200 | + major = buffer_get_int(&m); | ||
2201 | + hash->value = buffer_get_string(&m, &len); | ||
2202 | + hash->length = len; | ||
2203 | + | ||
2204 | + buffer_free(&m); | ||
2205 | + | ||
2206 | + return(major); | ||
2207 | +} | ||
2208 | + | ||
2209 | +int | ||
2210 | +mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) | ||
2211 | +{ | ||
2212 | + Buffer m; | ||
2213 | + int ok; | ||
2214 | + | ||
2215 | + buffer_init(&m); | ||
2216 | + | ||
2217 | + buffer_put_cstring(&m, store->filename ? store->filename : ""); | ||
2218 | + buffer_put_cstring(&m, store->envvar ? store->envvar : ""); | ||
2219 | + buffer_put_cstring(&m, store->envval ? store->envval : ""); | ||
2220 | + | ||
2221 | + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m); | ||
2222 | + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m); | ||
2223 | + | ||
2224 | + ok = buffer_get_int(&m); | ||
2225 | + | ||
2226 | + buffer_free(&m); | ||
2227 | + | ||
2228 | + return (ok); | ||
2229 | +} | ||
2230 | + | ||
2231 | #endif /* GSSAPI */ | ||
2232 | |||
2233 | #ifdef JPAKE | ||
2234 | Index: b/monitor_wrap.h | ||
2235 | =================================================================== | ||
2236 | --- a/monitor_wrap.h | ||
2237 | +++ b/monitor_wrap.h | ||
2238 | @@ -57,8 +57,10 @@ | ||
2239 | OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); | ||
2240 | OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, | ||
2241 | gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); | ||
2242 | -int mm_ssh_gssapi_userok(char *user); | ||
2243 | +int mm_ssh_gssapi_userok(char *user, struct passwd *); | ||
2244 | OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); | ||
2245 | +OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); | ||
2246 | +int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *); | ||
2247 | #endif | ||
2248 | |||
2249 | #ifdef USE_PAM | ||
2250 | Index: b/readconf.c | ||
2251 | =================================================================== | ||
2252 | --- a/readconf.c | ||
2253 | +++ b/readconf.c | ||
2254 | @@ -129,6 +129,8 @@ | ||
2255 | oClearAllForwardings, oNoHostAuthenticationForLocalhost, | ||
2256 | oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, | ||
2257 | oAddressFamily, oGssAuthentication, oGssDelegateCreds, | ||
2258 | + oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, | ||
2259 | + oGssServerIdentity, | ||
2260 | oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, | ||
2261 | oSendEnv, oControlPath, oControlMaster, oControlPersist, | ||
2262 | oHashKnownHosts, | ||
2263 | @@ -169,10 +171,19 @@ | ||
2264 | { "afstokenpassing", oUnsupported }, | ||
2265 | #if defined(GSSAPI) | ||
2266 | { "gssapiauthentication", oGssAuthentication }, | ||
2267 | + { "gssapikeyexchange", oGssKeyEx }, | ||
2268 | { "gssapidelegatecredentials", oGssDelegateCreds }, | ||
2269 | + { "gssapitrustdns", oGssTrustDns }, | ||
2270 | + { "gssapiclientidentity", oGssClientIdentity }, | ||
2271 | + { "gssapiserveridentity", oGssServerIdentity }, | ||
2272 | + { "gssapirenewalforcesrekey", oGssRenewalRekey }, | ||
2273 | #else | ||
2274 | { "gssapiauthentication", oUnsupported }, | ||
2275 | + { "gssapikeyexchange", oUnsupported }, | ||
2276 | { "gssapidelegatecredentials", oUnsupported }, | ||
2277 | + { "gssapitrustdns", oUnsupported }, | ||
2278 | + { "gssapiclientidentity", oUnsupported }, | ||
2279 | + { "gssapirenewalforcesrekey", oUnsupported }, | ||
2280 | #endif | ||
2281 | { "fallbacktorsh", oDeprecated }, | ||
2282 | { "usersh", oDeprecated }, | ||
2283 | @@ -479,10 +490,30 @@ | ||
2284 | intptr = &options->gss_authentication; | ||
2285 | goto parse_flag; | ||
2286 | |||
2287 | + case oGssKeyEx: | ||
2288 | + intptr = &options->gss_keyex; | ||
2289 | + goto parse_flag; | ||
2290 | + | ||
2291 | case oGssDelegateCreds: | ||
2292 | intptr = &options->gss_deleg_creds; | ||
2293 | goto parse_flag; | ||
2294 | |||
2295 | + case oGssTrustDns: | ||
2296 | + intptr = &options->gss_trust_dns; | ||
2297 | + goto parse_flag; | ||
2298 | + | ||
2299 | + case oGssClientIdentity: | ||
2300 | + charptr = &options->gss_client_identity; | ||
2301 | + goto parse_string; | ||
2302 | + | ||
2303 | + case oGssServerIdentity: | ||
2304 | + charptr = &options->gss_server_identity; | ||
2305 | + goto parse_string; | ||
2306 | + | ||
2307 | + case oGssRenewalRekey: | ||
2308 | + intptr = &options->gss_renewal_rekey; | ||
2309 | + goto parse_flag; | ||
2310 | + | ||
2311 | case oBatchMode: | ||
2312 | intptr = &options->batch_mode; | ||
2313 | goto parse_flag; | ||
2314 | @@ -1092,7 +1123,12 @@ | ||
2315 | options->pubkey_authentication = -1; | ||
2316 | options->challenge_response_authentication = -1; | ||
2317 | options->gss_authentication = -1; | ||
2318 | + options->gss_keyex = -1; | ||
2319 | options->gss_deleg_creds = -1; | ||
2320 | + options->gss_trust_dns = -1; | ||
2321 | + options->gss_renewal_rekey = -1; | ||
2322 | + options->gss_client_identity = NULL; | ||
2323 | + options->gss_server_identity = NULL; | ||
2324 | options->password_authentication = -1; | ||
2325 | options->kbd_interactive_authentication = -1; | ||
2326 | options->kbd_interactive_devices = NULL; | ||
2327 | @@ -1193,8 +1229,14 @@ | ||
2328 | options->challenge_response_authentication = 1; | ||
2329 | if (options->gss_authentication == -1) | ||
2330 | options->gss_authentication = 0; | ||
2331 | + if (options->gss_keyex == -1) | ||
2332 | + options->gss_keyex = 0; | ||
2333 | if (options->gss_deleg_creds == -1) | ||
2334 | options->gss_deleg_creds = 0; | ||
2335 | + if (options->gss_trust_dns == -1) | ||
2336 | + options->gss_trust_dns = 0; | ||
2337 | + if (options->gss_renewal_rekey == -1) | ||
2338 | + options->gss_renewal_rekey = 0; | ||
2339 | if (options->password_authentication == -1) | ||
2340 | options->password_authentication = 1; | ||
2341 | if (options->kbd_interactive_authentication == -1) | ||
2342 | Index: b/readconf.h | ||
2343 | =================================================================== | ||
2344 | --- a/readconf.h | ||
2345 | +++ b/readconf.h | ||
2346 | @@ -46,7 +46,12 @@ | ||
2347 | int challenge_response_authentication; | ||
2348 | /* Try S/Key or TIS, authentication. */ | ||
2349 | int gss_authentication; /* Try GSS authentication */ | ||
2350 | + int gss_keyex; /* Try GSS key exchange */ | ||
2351 | int gss_deleg_creds; /* Delegate GSS credentials */ | ||
2352 | + int gss_trust_dns; /* Trust DNS for GSS canonicalization */ | ||
2353 | + int gss_renewal_rekey; /* Credential renewal forces rekey */ | ||
2354 | + char *gss_client_identity; /* Principal to initiate GSSAPI with */ | ||
2355 | + char *gss_server_identity; /* GSSAPI target principal */ | ||
2356 | int password_authentication; /* Try password | ||
2357 | * authentication. */ | ||
2358 | int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ | ||
2359 | Index: b/servconf.c | ||
2360 | =================================================================== | ||
2361 | --- a/servconf.c | ||
2362 | +++ b/servconf.c | ||
2363 | @@ -97,7 +97,10 @@ | ||
2364 | options->kerberos_ticket_cleanup = -1; | ||
2365 | options->kerberos_get_afs_token = -1; | ||
2366 | options->gss_authentication=-1; | ||
2367 | + options->gss_keyex = -1; | ||
2368 | options->gss_cleanup_creds = -1; | ||
2369 | + options->gss_strict_acceptor = -1; | ||
2370 | + options->gss_store_rekey = -1; | ||
2371 | options->password_authentication = -1; | ||
2372 | options->kbd_interactive_authentication = -1; | ||
2373 | options->challenge_response_authentication = -1; | ||
2374 | @@ -226,8 +229,14 @@ | ||
2375 | options->kerberos_get_afs_token = 0; | ||
2376 | if (options->gss_authentication == -1) | ||
2377 | options->gss_authentication = 0; | ||
2378 | + if (options->gss_keyex == -1) | ||
2379 | + options->gss_keyex = 0; | ||
2380 | if (options->gss_cleanup_creds == -1) | ||
2381 | options->gss_cleanup_creds = 1; | ||
2382 | + if (options->gss_strict_acceptor == -1) | ||
2383 | + options->gss_strict_acceptor = 1; | ||
2384 | + if (options->gss_store_rekey == -1) | ||
2385 | + options->gss_store_rekey = 0; | ||
2386 | if (options->password_authentication == -1) | ||
2387 | options->password_authentication = 1; | ||
2388 | if (options->kbd_interactive_authentication == -1) | ||
2389 | @@ -322,7 +331,9 @@ | ||
2390 | sBanner, sUseDNS, sHostbasedAuthentication, | ||
2391 | sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, | ||
2392 | sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, | ||
2393 | - sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, | ||
2394 | + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, | ||
2395 | + sGssKeyEx, sGssStoreRekey, | ||
2396 | + sAcceptEnv, sPermitTunnel, | ||
2397 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, | ||
2398 | sUsePrivilegeSeparation, sAllowAgentForwarding, | ||
2399 | sZeroKnowledgePasswordAuthentication, sHostCertificate, | ||
2400 | @@ -386,10 +397,20 @@ | ||
2401 | #ifdef GSSAPI | ||
2402 | { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, | ||
2403 | { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, | ||
2404 | + { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL }, | ||
2405 | + { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, | ||
2406 | + { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, | ||
2407 | + { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, | ||
2408 | #else | ||
2409 | { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, | ||
2410 | { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, | ||
2411 | + { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL }, | ||
2412 | + { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, | ||
2413 | + { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, | ||
2414 | + { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, | ||
2415 | #endif | ||
2416 | + { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, | ||
2417 | + { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, | ||
2418 | { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, | ||
2419 | { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, | ||
2420 | { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, | ||
2421 | @@ -944,10 +965,22 @@ | ||
2422 | intptr = &options->gss_authentication; | ||
2423 | goto parse_flag; | ||
2424 | |||
2425 | + case sGssKeyEx: | ||
2426 | + intptr = &options->gss_keyex; | ||
2427 | + goto parse_flag; | ||
2428 | + | ||
2429 | case sGssCleanupCreds: | ||
2430 | intptr = &options->gss_cleanup_creds; | ||
2431 | goto parse_flag; | ||
2432 | |||
2433 | + case sGssStrictAcceptor: | ||
2434 | + intptr = &options->gss_strict_acceptor; | ||
2435 | + goto parse_flag; | ||
2436 | + | ||
2437 | + case sGssStoreRekey: | ||
2438 | + intptr = &options->gss_store_rekey; | ||
2439 | + goto parse_flag; | ||
2440 | + | ||
2441 | case sPasswordAuthentication: | ||
2442 | intptr = &options->password_authentication; | ||
2443 | goto parse_flag; | ||
2444 | @@ -1704,7 +1737,10 @@ | ||
2445 | #endif | ||
2446 | #ifdef GSSAPI | ||
2447 | dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); | ||
2448 | + dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); | ||
2449 | dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); | ||
2450 | + dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); | ||
2451 | + dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); | ||
2452 | #endif | ||
2453 | #ifdef JPAKE | ||
2454 | dump_cfg_fmtint(sZeroKnowledgePasswordAuthentication, | ||
2455 | Index: b/servconf.h | ||
2456 | =================================================================== | ||
2457 | --- a/servconf.h | ||
2458 | +++ b/servconf.h | ||
2459 | @@ -97,7 +97,10 @@ | ||
2460 | int kerberos_get_afs_token; /* If true, try to get AFS token if | ||
2461 | * authenticated with Kerberos. */ | ||
2462 | int gss_authentication; /* If true, permit GSSAPI authentication */ | ||
2463 | + int gss_keyex; /* If true, permit GSSAPI key exchange */ | ||
2464 | int gss_cleanup_creds; /* If true, destroy cred cache on logout */ | ||
2465 | + int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ | ||
2466 | + int gss_store_rekey; | ||
2467 | int password_authentication; /* If true, permit password | ||
2468 | * authentication. */ | ||
2469 | int kbd_interactive_authentication; /* If true, permit */ | ||
2470 | Index: b/ssh-gss.h | ||
2471 | =================================================================== | ||
2472 | --- a/ssh-gss.h | ||
2473 | +++ b/ssh-gss.h | ||
2474 | @@ -1,6 +1,6 @@ | ||
2475 | /* $OpenBSD: ssh-gss.h,v 1.10 2007/06/12 08:20:00 djm Exp $ */ | ||
2476 | /* | ||
2477 | - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. | ||
2478 | + * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. | ||
2479 | * | ||
2480 | * Redistribution and use in source and binary forms, with or without | ||
2481 | * modification, are permitted provided that the following conditions | ||
2482 | @@ -60,10 +60,22 @@ | ||
2483 | |||
2484 | #define SSH_GSS_OIDTYPE 0x06 | ||
2485 | |||
2486 | +#define SSH2_MSG_KEXGSS_INIT 30 | ||
2487 | +#define SSH2_MSG_KEXGSS_CONTINUE 31 | ||
2488 | +#define SSH2_MSG_KEXGSS_COMPLETE 32 | ||
2489 | +#define SSH2_MSG_KEXGSS_HOSTKEY 33 | ||
2490 | +#define SSH2_MSG_KEXGSS_ERROR 34 | ||
2491 | +#define SSH2_MSG_KEXGSS_GROUPREQ 40 | ||
2492 | +#define SSH2_MSG_KEXGSS_GROUP 41 | ||
2493 | +#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" | ||
2494 | +#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" | ||
2495 | +#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" | ||
2496 | + | ||
2497 | typedef struct { | ||
2498 | char *filename; | ||
2499 | char *envvar; | ||
2500 | char *envval; | ||
2501 | + struct passwd *owner; | ||
2502 | void *data; | ||
2503 | } ssh_gssapi_ccache; | ||
2504 | |||
2505 | @@ -71,8 +83,11 @@ | ||
2506 | gss_buffer_desc displayname; | ||
2507 | gss_buffer_desc exportedname; | ||
2508 | gss_cred_id_t creds; | ||
2509 | + gss_name_t name; | ||
2510 | struct ssh_gssapi_mech_struct *mech; | ||
2511 | ssh_gssapi_ccache store; | ||
2512 | + int used; | ||
2513 | + int updated; | ||
2514 | } ssh_gssapi_client; | ||
2515 | |||
2516 | typedef struct ssh_gssapi_mech_struct { | ||
2517 | @@ -83,6 +98,7 @@ | ||
2518 | int (*userok) (ssh_gssapi_client *, char *); | ||
2519 | int (*localname) (ssh_gssapi_client *, char **); | ||
2520 | void (*storecreds) (ssh_gssapi_client *); | ||
2521 | + int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); | ||
2522 | } ssh_gssapi_mech; | ||
2523 | |||
2524 | typedef struct { | ||
2525 | @@ -93,10 +109,11 @@ | ||
2526 | gss_OID oid; /* client */ | ||
2527 | gss_cred_id_t creds; /* server */ | ||
2528 | gss_name_t client; /* server */ | ||
2529 | - gss_cred_id_t client_creds; /* server */ | ||
2530 | + gss_cred_id_t client_creds; /* both */ | ||
2531 | } Gssctxt; | ||
2532 | |||
2533 | extern ssh_gssapi_mech *supported_mechs[]; | ||
2534 | +extern Gssctxt *gss_kex_context; | ||
2535 | |||
2536 | int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); | ||
2537 | void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); | ||
2538 | @@ -116,16 +133,30 @@ | ||
2539 | void ssh_gssapi_delete_ctx(Gssctxt **); | ||
2540 | OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); | ||
2541 | void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *); | ||
2542 | -int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); | ||
2543 | +int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *); | ||
2544 | +OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *); | ||
2545 | +int ssh_gssapi_credentials_updated(Gssctxt *); | ||
2546 | |||
2547 | /* In the server */ | ||
2548 | +typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, | ||
2549 | + const char *); | ||
2550 | +char *ssh_gssapi_client_mechanisms(const char *, const char *); | ||
2551 | +char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, | ||
2552 | + const char *); | ||
2553 | +gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); | ||
2554 | +int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, | ||
2555 | + const char *); | ||
2556 | OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); | ||
2557 | -int ssh_gssapi_userok(char *name); | ||
2558 | +int ssh_gssapi_userok(char *name, struct passwd *); | ||
2559 | OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); | ||
2560 | void ssh_gssapi_do_child(char ***, u_int *); | ||
2561 | void ssh_gssapi_cleanup_creds(void); | ||
2562 | void ssh_gssapi_storecreds(void); | ||
2563 | |||
2564 | +char *ssh_gssapi_server_mechanisms(void); | ||
2565 | +int ssh_gssapi_oid_table_ok(); | ||
2566 | + | ||
2567 | +int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); | ||
2568 | #endif /* GSSAPI */ | ||
2569 | |||
2570 | #endif /* _SSH_GSS_H */ | ||
2571 | Index: b/ssh_config | ||
2572 | =================================================================== | ||
2573 | --- a/ssh_config | ||
2574 | +++ b/ssh_config | ||
2575 | @@ -26,6 +26,8 @@ | ||
2576 | # HostbasedAuthentication no | ||
2577 | # GSSAPIAuthentication no | ||
2578 | # GSSAPIDelegateCredentials no | ||
2579 | +# GSSAPIKeyExchange no | ||
2580 | +# GSSAPITrustDNS no | ||
2581 | # BatchMode no | ||
2582 | # CheckHostIP yes | ||
2583 | # AddressFamily any | ||
2584 | Index: b/ssh_config.5 | ||
2585 | =================================================================== | ||
2586 | --- a/ssh_config.5 | ||
2587 | +++ b/ssh_config.5 | ||
2588 | @@ -508,11 +508,43 @@ | ||
2589 | The default is | ||
2590 | .Dq no . | ||
2591 | Note that this option applies to protocol version 2 only. | ||
2592 | +.It Cm GSSAPIKeyExchange | ||
2593 | +Specifies whether key exchange based on GSSAPI may be used. When using | ||
2594 | +GSSAPI key exchange the server need not have a host key. | ||
2595 | +The default is | ||
2596 | +.Dq no . | ||
2597 | +Note that this option applies to protocol version 2 only. | ||
2598 | +.It Cm GSSAPIClientIdentity | ||
2599 | +If set, specifies the GSSAPI client identity that ssh should use when | ||
2600 | +connecting to the server. The default is unset, which means that the default | ||
2601 | +identity will be used. | ||
2602 | +.It Cm GSSAPIServerIdentity | ||
2603 | +If set, specifies the GSSAPI server identity that ssh should expect when | ||
2604 | +connecting to the server. The default is unset, which means that the | ||
2605 | +expected GSSAPI server identity will be determined from the target | ||
2606 | +hostname. | ||
2607 | .It Cm GSSAPIDelegateCredentials | ||
2608 | Forward (delegate) credentials to the server. | ||
2609 | The default is | ||
2610 | .Dq no . | ||
2611 | -Note that this option applies to protocol version 2 only. | ||
2612 | +Note that this option applies to protocol version 2 connections using GSSAPI. | ||
2613 | +.It Cm GSSAPIRenewalForcesRekey | ||
2614 | +If set to | ||
2615 | +.Dq yes | ||
2616 | +then renewal of the client's GSSAPI credentials will force the rekeying of the | ||
2617 | +ssh connection. With a compatible server, this can delegate the renewed | ||
2618 | +credentials to a session on the server. | ||
2619 | +The default is | ||
2620 | +.Dq no . | ||
2621 | +.It Cm GSSAPITrustDns | ||
2622 | +Set to | ||
2623 | +.Dq yes to indicate that the DNS is trusted to securely canonicalize | ||
2624 | +the name of the host being connected to. If | ||
2625 | +.Dq no, the hostname entered on the | ||
2626 | +command line will be passed untouched to the GSSAPI library. | ||
2627 | +The default is | ||
2628 | +.Dq no . | ||
2629 | +This option only applies to protocol version 2 connections using GSSAPI. | ||
2630 | .It Cm HashKnownHosts | ||
2631 | Indicates that | ||
2632 | .Xr ssh 1 | ||
2633 | Index: b/sshconnect2.c | ||
2634 | =================================================================== | ||
2635 | --- a/sshconnect2.c | ||
2636 | +++ b/sshconnect2.c | ||
2637 | @@ -159,9 +159,34 @@ | ||
2638 | { | ||
2639 | Kex *kex; | ||
2640 | |||
2641 | +#ifdef GSSAPI | ||
2642 | + char *orig = NULL, *gss = NULL; | ||
2643 | + char *gss_host = NULL; | ||
2644 | +#endif | ||
2645 | + | ||
2646 | xxx_host = host; | ||
2647 | xxx_hostaddr = hostaddr; | ||
2648 | |||
2649 | +#ifdef GSSAPI | ||
2650 | + if (options.gss_keyex) { | ||
2651 | + /* Add the GSSAPI mechanisms currently supported on this | ||
2652 | + * client to the key exchange algorithm proposal */ | ||
2653 | + orig = myproposal[PROPOSAL_KEX_ALGS]; | ||
2654 | + | ||
2655 | + if (options.gss_trust_dns) | ||
2656 | + gss_host = (char *)get_canonical_hostname(1); | ||
2657 | + else | ||
2658 | + gss_host = host; | ||
2659 | + | ||
2660 | + gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); | ||
2661 | + if (gss) { | ||
2662 | + debug("Offering GSSAPI proposal: %s", gss); | ||
2663 | + xasprintf(&myproposal[PROPOSAL_KEX_ALGS], | ||
2664 | + "%s,%s", gss, orig); | ||
2665 | + } | ||
2666 | + } | ||
2667 | +#endif | ||
2668 | + | ||
2669 | if (options.ciphers == (char *)-1) { | ||
2670 | logit("No valid ciphers for protocol version 2 given, using defaults."); | ||
2671 | options.ciphers = NULL; | ||
2672 | @@ -196,6 +221,17 @@ | ||
2673 | if (options.kex_algorithms != NULL) | ||
2674 | myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; | ||
2675 | |||
2676 | +#ifdef GSSAPI | ||
2677 | + /* If we've got GSSAPI algorithms, then we also support the | ||
2678 | + * 'null' hostkey, as a last resort */ | ||
2679 | + if (options.gss_keyex && gss) { | ||
2680 | + orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; | ||
2681 | + xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], | ||
2682 | + "%s,null", orig); | ||
2683 | + xfree(gss); | ||
2684 | + } | ||
2685 | +#endif | ||
2686 | + | ||
2687 | if (options.rekey_limit) | ||
2688 | packet_set_rekey_limit((u_int32_t)options.rekey_limit); | ||
2689 | |||
2690 | @@ -206,10 +242,30 @@ | ||
2691 | kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; | ||
2692 | kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; | ||
2693 | kex->kex[KEX_ECDH_SHA2] = kexecdh_client; | ||
2694 | +#ifdef GSSAPI | ||
2695 | + if (options.gss_keyex) { | ||
2696 | + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; | ||
2697 | + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; | ||
2698 | + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; | ||
2699 | + } | ||
2700 | +#endif | ||
2701 | kex->client_version_string=client_version_string; | ||
2702 | kex->server_version_string=server_version_string; | ||
2703 | kex->verify_host_key=&verify_host_key_callback; | ||
2704 | |||
2705 | +#ifdef GSSAPI | ||
2706 | + if (options.gss_keyex) { | ||
2707 | + kex->gss_deleg_creds = options.gss_deleg_creds; | ||
2708 | + kex->gss_trust_dns = options.gss_trust_dns; | ||
2709 | + kex->gss_client = options.gss_client_identity; | ||
2710 | + if (options.gss_server_identity) { | ||
2711 | + kex->gss_host = options.gss_server_identity; | ||
2712 | + } else { | ||
2713 | + kex->gss_host = gss_host; | ||
2714 | + } | ||
2715 | + } | ||
2716 | +#endif | ||
2717 | + | ||
2718 | xxx_kex = kex; | ||
2719 | |||
2720 | dispatch_run(DISPATCH_BLOCK, &kex->done, kex); | ||
2721 | @@ -304,6 +360,7 @@ | ||
2722 | void input_gssapi_hash(int type, u_int32_t, void *); | ||
2723 | void input_gssapi_error(int, u_int32_t, void *); | ||
2724 | void input_gssapi_errtok(int, u_int32_t, void *); | ||
2725 | +int userauth_gsskeyex(Authctxt *authctxt); | ||
2726 | #endif | ||
2727 | |||
2728 | void userauth(Authctxt *, char *); | ||
2729 | @@ -319,6 +376,11 @@ | ||
2730 | |||
2731 | Authmethod authmethods[] = { | ||
2732 | #ifdef GSSAPI | ||
2733 | + {"gssapi-keyex", | ||
2734 | + userauth_gsskeyex, | ||
2735 | + NULL, | ||
2736 | + &options.gss_authentication, | ||
2737 | + NULL}, | ||
2738 | {"gssapi-with-mic", | ||
2739 | userauth_gssapi, | ||
2740 | NULL, | ||
2741 | @@ -625,19 +687,31 @@ | ||
2742 | static u_int mech = 0; | ||
2743 | OM_uint32 min; | ||
2744 | int ok = 0; | ||
2745 | + const char *gss_host; | ||
2746 | + | ||
2747 | + if (options.gss_server_identity) | ||
2748 | + gss_host = options.gss_server_identity; | ||
2749 | + else if (options.gss_trust_dns) | ||
2750 | + gss_host = get_canonical_hostname(1); | ||
2751 | + else | ||
2752 | + gss_host = authctxt->host; | ||
2753 | |||
2754 | /* Try one GSSAPI method at a time, rather than sending them all at | ||
2755 | * once. */ | ||
2756 | |||
2757 | if (gss_supported == NULL) | ||
2758 | - gss_indicate_mechs(&min, &gss_supported); | ||
2759 | + if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { | ||
2760 | + gss_supported = NULL; | ||
2761 | + return 0; | ||
2762 | + } | ||
2763 | |||
2764 | /* Check to see if the mechanism is usable before we offer it */ | ||
2765 | while (mech < gss_supported->count && !ok) { | ||
2766 | /* My DER encoding requires length<128 */ | ||
2767 | if (gss_supported->elements[mech].length < 128 && | ||
2768 | ssh_gssapi_check_mechanism(&gssctxt, | ||
2769 | - &gss_supported->elements[mech], authctxt->host)) { | ||
2770 | + &gss_supported->elements[mech], gss_host, | ||
2771 | + options.gss_client_identity)) { | ||
2772 | ok = 1; /* Mechanism works */ | ||
2773 | } else { | ||
2774 | mech++; | ||
2775 | @@ -734,8 +808,8 @@ | ||
2776 | { | ||
2777 | Authctxt *authctxt = ctxt; | ||
2778 | Gssctxt *gssctxt; | ||
2779 | - int oidlen; | ||
2780 | - char *oidv; | ||
2781 | + u_int oidlen; | ||
2782 | + u_char *oidv; | ||
2783 | |||
2784 | if (authctxt == NULL) | ||
2785 | fatal("input_gssapi_response: no authentication context"); | ||
2786 | @@ -845,6 +919,48 @@ | ||
2787 | xfree(msg); | ||
2788 | xfree(lang); | ||
2789 | } | ||
2790 | + | ||
2791 | +int | ||
2792 | +userauth_gsskeyex(Authctxt *authctxt) | ||
2793 | +{ | ||
2794 | + Buffer b; | ||
2795 | + gss_buffer_desc gssbuf; | ||
2796 | + gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; | ||
2797 | + OM_uint32 ms; | ||
2798 | + | ||
2799 | + static int attempt = 0; | ||
2800 | + if (attempt++ >= 1) | ||
2801 | + return (0); | ||
2802 | + | ||
2803 | + if (gss_kex_context == NULL) { | ||
2804 | + debug("No valid Key exchange context"); | ||
2805 | + return (0); | ||
2806 | + } | ||
2807 | + | ||
2808 | + ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, | ||
2809 | + "gssapi-keyex"); | ||
2810 | + | ||
2811 | + gssbuf.value = buffer_ptr(&b); | ||
2812 | + gssbuf.length = buffer_len(&b); | ||
2813 | + | ||
2814 | + if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { | ||
2815 | + buffer_free(&b); | ||
2816 | + return (0); | ||
2817 | + } | ||
2818 | + | ||
2819 | + packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
2820 | + packet_put_cstring(authctxt->server_user); | ||
2821 | + packet_put_cstring(authctxt->service); | ||
2822 | + packet_put_cstring(authctxt->method->name); | ||
2823 | + packet_put_string(mic.value, mic.length); | ||
2824 | + packet_send(); | ||
2825 | + | ||
2826 | + buffer_free(&b); | ||
2827 | + gss_release_buffer(&ms, &mic); | ||
2828 | + | ||
2829 | + return (1); | ||
2830 | +} | ||
2831 | + | ||
2832 | #endif /* GSSAPI */ | ||
2833 | |||
2834 | int | ||
2835 | Index: b/sshd.c | ||
2836 | =================================================================== | ||
2837 | --- a/sshd.c | ||
2838 | +++ b/sshd.c | ||
2839 | @@ -120,6 +120,10 @@ | ||
2840 | #include "roaming.h" | ||
2841 | #include "version.h" | ||
2842 | |||
2843 | +#ifdef USE_SECURITY_SESSION_API | ||
2844 | +#include <Security/AuthSession.h> | ||
2845 | +#endif | ||
2846 | + | ||
2847 | #ifdef LIBWRAP | ||
2848 | #include <tcpd.h> | ||
2849 | #include <syslog.h> | ||
2850 | @@ -1590,10 +1594,13 @@ | ||
2851 | logit("Disabling protocol version 1. Could not load host key"); | ||
2852 | options.protocol &= ~SSH_PROTO_1; | ||
2853 | } | ||
2854 | +#ifndef GSSAPI | ||
2855 | + /* The GSSAPI key exchange can run without a host key */ | ||
2856 | if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { | ||
2857 | logit("Disabling protocol version 2. Could not load host key"); | ||
2858 | options.protocol &= ~SSH_PROTO_2; | ||
2859 | } | ||
2860 | +#endif | ||
2861 | if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { | ||
2862 | logit("sshd: no hostkeys available -- exiting."); | ||
2863 | exit(1); | ||
2864 | @@ -1922,6 +1929,60 @@ | ||
2865 | /* Log the connection. */ | ||
2866 | verbose("Connection from %.500s port %d", remote_ip, remote_port); | ||
2867 | |||
2868 | +#ifdef USE_SECURITY_SESSION_API | ||
2869 | + /* | ||
2870 | + * Create a new security session for use by the new user login if | ||
2871 | + * the current session is the root session or we are not launched | ||
2872 | + * by inetd (eg: debugging mode or server mode). We do not | ||
2873 | + * necessarily need to create a session if we are launched from | ||
2874 | + * inetd because Panther xinetd will create a session for us. | ||
2875 | + * | ||
2876 | + * The only case where this logic will fail is if there is an | ||
2877 | + * inetd running in a non-root session which is not creating | ||
2878 | + * new sessions for us. Then all the users will end up in the | ||
2879 | + * same session (bad). | ||
2880 | + * | ||
2881 | + * When the client exits, the session will be destroyed for us | ||
2882 | + * automatically. | ||
2883 | + * | ||
2884 | + * We must create the session before any credentials are stored | ||
2885 | + * (including AFS pags, which happens a few lines below). | ||
2886 | + */ | ||
2887 | + { | ||
2888 | + OSStatus err = 0; | ||
2889 | + SecuritySessionId sid = 0; | ||
2890 | + SessionAttributeBits sattrs = 0; | ||
2891 | + | ||
2892 | + err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); | ||
2893 | + if (err) | ||
2894 | + error("SessionGetInfo() failed with error %.8X", | ||
2895 | + (unsigned) err); | ||
2896 | + else | ||
2897 | + debug("Current Session ID is %.8X / Session Attributes are %.8X", | ||
2898 | + (unsigned) sid, (unsigned) sattrs); | ||
2899 | + | ||
2900 | + if (inetd_flag && !(sattrs & sessionIsRoot)) | ||
2901 | + debug("Running in inetd mode in a non-root session... " | ||
2902 | + "assuming inetd created the session for us."); | ||
2903 | + else { | ||
2904 | + debug("Creating new security session..."); | ||
2905 | + err = SessionCreate(0, sessionHasTTY | sessionIsRemote); | ||
2906 | + if (err) | ||
2907 | + error("SessionCreate() failed with error %.8X", | ||
2908 | + (unsigned) err); | ||
2909 | + | ||
2910 | + err = SessionGetInfo(callerSecuritySession, &sid, | ||
2911 | + &sattrs); | ||
2912 | + if (err) | ||
2913 | + error("SessionGetInfo() failed with error %.8X", | ||
2914 | + (unsigned) err); | ||
2915 | + else | ||
2916 | + debug("New Session ID is %.8X / Session Attributes are %.8X", | ||
2917 | + (unsigned) sid, (unsigned) sattrs); | ||
2918 | + } | ||
2919 | + } | ||
2920 | +#endif | ||
2921 | + | ||
2922 | /* | ||
2923 | * We don't want to listen forever unless the other side | ||
2924 | * successfully authenticates itself. So we set up an alarm which is | ||
2925 | @@ -2303,6 +2364,48 @@ | ||
2926 | |||
2927 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); | ||
2928 | |||
2929 | +#ifdef GSSAPI | ||
2930 | + { | ||
2931 | + char *orig; | ||
2932 | + char *gss = NULL; | ||
2933 | + char *newstr = NULL; | ||
2934 | + orig = myproposal[PROPOSAL_KEX_ALGS]; | ||
2935 | + | ||
2936 | + /* | ||
2937 | + * If we don't have a host key, then there's no point advertising | ||
2938 | + * the other key exchange algorithms | ||
2939 | + */ | ||
2940 | + | ||
2941 | + if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) | ||
2942 | + orig = NULL; | ||
2943 | + | ||
2944 | + if (options.gss_keyex) | ||
2945 | + gss = ssh_gssapi_server_mechanisms(); | ||
2946 | + else | ||
2947 | + gss = NULL; | ||
2948 | + | ||
2949 | + if (gss && orig) | ||
2950 | + xasprintf(&newstr, "%s,%s", gss, orig); | ||
2951 | + else if (gss) | ||
2952 | + newstr = gss; | ||
2953 | + else if (orig) | ||
2954 | + newstr = orig; | ||
2955 | + | ||
2956 | + /* | ||
2957 | + * If we've got GSSAPI mechanisms, then we've got the 'null' host | ||
2958 | + * key alg, but we can't tell people about it unless its the only | ||
2959 | + * host key algorithm we support | ||
2960 | + */ | ||
2961 | + if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) | ||
2962 | + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; | ||
2963 | + | ||
2964 | + if (newstr) | ||
2965 | + myproposal[PROPOSAL_KEX_ALGS] = newstr; | ||
2966 | + else | ||
2967 | + fatal("No supported key exchange algorithms"); | ||
2968 | + } | ||
2969 | +#endif | ||
2970 | + | ||
2971 | /* start key exchange */ | ||
2972 | kex = kex_setup(myproposal); | ||
2973 | kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; | ||
2974 | @@ -2310,6 +2413,13 @@ | ||
2975 | kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; | ||
2976 | kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; | ||
2977 | kex->kex[KEX_ECDH_SHA2] = kexecdh_server; | ||
2978 | +#ifdef GSSAPI | ||
2979 | + if (options.gss_keyex) { | ||
2980 | + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; | ||
2981 | + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; | ||
2982 | + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; | ||
2983 | + } | ||
2984 | +#endif | ||
2985 | kex->server = 1; | ||
2986 | kex->client_version_string=client_version_string; | ||
2987 | kex->server_version_string=server_version_string; | ||
2988 | Index: b/sshd_config | ||
2989 | =================================================================== | ||
2990 | --- a/sshd_config | ||
2991 | +++ b/sshd_config | ||
2992 | @@ -72,6 +72,8 @@ | ||
2993 | # GSSAPI options | ||
2994 | #GSSAPIAuthentication no | ||
2995 | #GSSAPICleanupCredentials yes | ||
2996 | +#GSSAPIStrictAcceptorCheck yes | ||
2997 | +#GSSAPIKeyExchange no | ||
2998 | |||
2999 | # Set this to 'yes' to enable PAM authentication, account processing, | ||
3000 | # and session processing. If this is enabled, PAM authentication will | ||
3001 | Index: b/sshd_config.5 | ||
3002 | =================================================================== | ||
3003 | --- a/sshd_config.5 | ||
3004 | +++ b/sshd_config.5 | ||
3005 | @@ -423,12 +423,40 @@ | ||
3006 | The default is | ||
3007 | .Dq no . | ||
3008 | Note that this option applies to protocol version 2 only. | ||
3009 | +.It Cm GSSAPIKeyExchange | ||
3010 | +Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange | ||
3011 | +doesn't rely on ssh keys to verify host identity. | ||
3012 | +The default is | ||
3013 | +.Dq no . | ||
3014 | +Note that this option applies to protocol version 2 only. | ||
3015 | .It Cm GSSAPICleanupCredentials | ||
3016 | Specifies whether to automatically destroy the user's credentials cache | ||
3017 | on logout. | ||
3018 | The default is | ||
3019 | .Dq yes . | ||
3020 | Note that this option applies to protocol version 2 only. | ||
3021 | +.It Cm GSSAPIStrictAcceptorCheck | ||
3022 | +Determines whether to be strict about the identity of the GSSAPI acceptor | ||
3023 | +a client authenticates against. If | ||
3024 | +.Dq yes | ||
3025 | +then the client must authenticate against the | ||
3026 | +.Pa host | ||
3027 | +service on the current hostname. If | ||
3028 | +.Dq no | ||
3029 | +then the client may authenticate against any service key stored in the | ||
3030 | +machine's default store. This facility is provided to assist with operation | ||
3031 | +on multi homed machines. | ||
3032 | +The default is | ||
3033 | +.Dq yes . | ||
3034 | +Note that this option applies only to protocol version 2 GSSAPI connections, | ||
3035 | +and setting it to | ||
3036 | +.Dq no | ||
3037 | +may only work with recent Kerberos GSSAPI libraries. | ||
3038 | +.It Cm GSSAPIStoreCredentialsOnRekey | ||
3039 | +Controls whether the user's GSSAPI credentials should be updated following a | ||
3040 | +successful connection rekeying. This option can be used to accepted renewed | ||
3041 | +or updated credentials from a compatible client. The default is | ||
3042 | +.Dq no . | ||
3043 | .It Cm HostbasedAuthentication | ||
3044 | Specifies whether rhosts or /etc/hosts.equiv authentication together | ||
3045 | with successful public key client host authentication is allowed | ||