diff options
author | Simon Wilkinson <simon@sxw.org.uk> | 2014-02-09 16:09:48 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2015-11-29 17:26:06 +0000 |
commit | 233e78235070e871b658c8f289e600bd52a99711 (patch) | |
tree | 38c49e39e2a61ef635ce70062d8830d09fc963ff /gss-serv.c | |
parent | 58ddb8ad21f21f5358db0204c4ba9abf94a1ca11 (diff) |
GSSAPI key exchange support
This patch has been rejected upstream: "None of the OpenSSH developers are
in favour of adding this, and this situation has not changed for several
years. This is not a slight on Simon's patch, which is of fine quality, but
just that a) we don't trust GSSAPI implementations that much and b) we don't
like adding new KEX since they are pre-auth attack surface. This one is
particularly scary, since it requires hooks out to typically root-owned
system resources."
However, quite a lot of people rely on this in Debian, and it's better to
have it merged into the main openssh package rather than having separate
-krb5 packages (as we used to have). It seems to have a generally good
security history.
Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242
Last-Updated: 2015-11-29
Patch-Name: gssapi.patch
Diffstat (limited to 'gss-serv.c')
-rw-r--r-- | gss-serv.c | 185 |
1 files changed, 171 insertions, 14 deletions
diff --git a/gss-serv.c b/gss-serv.c index 53993d674..2f6baf70d 100644 --- a/gss-serv.c +++ b/gss-serv.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */ | 1 | /* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. | 4 | * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. |
5 | * | 5 | * |
6 | * Redistribution and use in source and binary forms, with or without | 6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions | 7 | * modification, are permitted provided that the following conditions |
@@ -45,17 +45,22 @@ | |||
45 | #include "session.h" | 45 | #include "session.h" |
46 | #include "misc.h" | 46 | #include "misc.h" |
47 | #include "servconf.h" | 47 | #include "servconf.h" |
48 | #include "uidswap.h" | ||
48 | 49 | ||
49 | #include "ssh-gss.h" | 50 | #include "ssh-gss.h" |
51 | #include "monitor_wrap.h" | ||
52 | |||
53 | extern ServerOptions options; | ||
50 | 54 | ||
51 | extern ServerOptions options; | 55 | extern ServerOptions options; |
52 | 56 | ||
53 | static ssh_gssapi_client gssapi_client = | 57 | static ssh_gssapi_client gssapi_client = |
54 | { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, | 58 | { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, |
55 | GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; | 59 | GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, |
60 | {NULL, NULL, NULL, NULL, NULL}, 0, 0}; | ||
56 | 61 | ||
57 | ssh_gssapi_mech gssapi_null_mech = | 62 | ssh_gssapi_mech gssapi_null_mech = |
58 | { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; | 63 | { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; |
59 | 64 | ||
60 | #ifdef KRB5 | 65 | #ifdef KRB5 |
61 | extern ssh_gssapi_mech gssapi_kerberos_mech; | 66 | extern ssh_gssapi_mech gssapi_kerberos_mech; |
@@ -142,6 +147,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) | |||
142 | } | 147 | } |
143 | 148 | ||
144 | /* Unprivileged */ | 149 | /* Unprivileged */ |
150 | char * | ||
151 | ssh_gssapi_server_mechanisms(void) { | ||
152 | gss_OID_set supported; | ||
153 | |||
154 | ssh_gssapi_supported_oids(&supported); | ||
155 | return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech, | ||
156 | NULL, NULL)); | ||
157 | } | ||
158 | |||
159 | /* Unprivileged */ | ||
160 | int | ||
161 | ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, | ||
162 | const char *dummy) { | ||
163 | Gssctxt *ctx = NULL; | ||
164 | int res; | ||
165 | |||
166 | res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); | ||
167 | ssh_gssapi_delete_ctx(&ctx); | ||
168 | |||
169 | return (res); | ||
170 | } | ||
171 | |||
172 | /* Unprivileged */ | ||
145 | void | 173 | void |
146 | ssh_gssapi_supported_oids(gss_OID_set *oidset) | 174 | ssh_gssapi_supported_oids(gss_OID_set *oidset) |
147 | { | 175 | { |
@@ -151,7 +179,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset) | |||
151 | gss_OID_set supported; | 179 | gss_OID_set supported; |
152 | 180 | ||
153 | gss_create_empty_oid_set(&min_status, oidset); | 181 | gss_create_empty_oid_set(&min_status, oidset); |
154 | gss_indicate_mechs(&min_status, &supported); | 182 | |
183 | if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) | ||
184 | return; | ||
155 | 185 | ||
156 | while (supported_mechs[i]->name != NULL) { | 186 | while (supported_mechs[i]->name != NULL) { |
157 | if (GSS_ERROR(gss_test_oid_set_member(&min_status, | 187 | if (GSS_ERROR(gss_test_oid_set_member(&min_status, |
@@ -277,8 +307,48 @@ OM_uint32 | |||
277 | ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | 307 | ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) |
278 | { | 308 | { |
279 | int i = 0; | 309 | int i = 0; |
310 | int equal = 0; | ||
311 | gss_name_t new_name = GSS_C_NO_NAME; | ||
312 | gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; | ||
313 | |||
314 | if (options.gss_store_rekey && client->used && ctx->client_creds) { | ||
315 | if (client->mech->oid.length != ctx->oid->length || | ||
316 | (memcmp(client->mech->oid.elements, | ||
317 | ctx->oid->elements, ctx->oid->length) !=0)) { | ||
318 | debug("Rekeyed credentials have different mechanism"); | ||
319 | return GSS_S_COMPLETE; | ||
320 | } | ||
321 | |||
322 | if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, | ||
323 | ctx->client_creds, ctx->oid, &new_name, | ||
324 | NULL, NULL, NULL))) { | ||
325 | ssh_gssapi_error(ctx); | ||
326 | return (ctx->major); | ||
327 | } | ||
328 | |||
329 | ctx->major = gss_compare_name(&ctx->minor, client->name, | ||
330 | new_name, &equal); | ||
331 | |||
332 | if (GSS_ERROR(ctx->major)) { | ||
333 | ssh_gssapi_error(ctx); | ||
334 | return (ctx->major); | ||
335 | } | ||
336 | |||
337 | if (!equal) { | ||
338 | debug("Rekeyed credentials have different name"); | ||
339 | return GSS_S_COMPLETE; | ||
340 | } | ||
280 | 341 | ||
281 | gss_buffer_desc ename; | 342 | debug("Marking rekeyed credentials for export"); |
343 | |||
344 | gss_release_name(&ctx->minor, &client->name); | ||
345 | gss_release_cred(&ctx->minor, &client->creds); | ||
346 | client->name = new_name; | ||
347 | client->creds = ctx->client_creds; | ||
348 | ctx->client_creds = GSS_C_NO_CREDENTIAL; | ||
349 | client->updated = 1; | ||
350 | return GSS_S_COMPLETE; | ||
351 | } | ||
282 | 352 | ||
283 | client->mech = NULL; | 353 | client->mech = NULL; |
284 | 354 | ||
@@ -293,6 +363,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | |||
293 | if (client->mech == NULL) | 363 | if (client->mech == NULL) |
294 | return GSS_S_FAILURE; | 364 | return GSS_S_FAILURE; |
295 | 365 | ||
366 | if (ctx->client_creds && | ||
367 | (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, | ||
368 | ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { | ||
369 | ssh_gssapi_error(ctx); | ||
370 | return (ctx->major); | ||
371 | } | ||
372 | |||
296 | if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, | 373 | if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, |
297 | &client->displayname, NULL))) { | 374 | &client->displayname, NULL))) { |
298 | ssh_gssapi_error(ctx); | 375 | ssh_gssapi_error(ctx); |
@@ -310,6 +387,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | |||
310 | return (ctx->major); | 387 | return (ctx->major); |
311 | } | 388 | } |
312 | 389 | ||
390 | gss_release_buffer(&ctx->minor, &ename); | ||
391 | |||
313 | /* We can't copy this structure, so we just move the pointer to it */ | 392 | /* We can't copy this structure, so we just move the pointer to it */ |
314 | client->creds = ctx->client_creds; | 393 | client->creds = ctx->client_creds; |
315 | ctx->client_creds = GSS_C_NO_CREDENTIAL; | 394 | ctx->client_creds = GSS_C_NO_CREDENTIAL; |
@@ -357,7 +436,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep) | |||
357 | 436 | ||
358 | /* Privileged */ | 437 | /* Privileged */ |
359 | int | 438 | int |
360 | ssh_gssapi_userok(char *user) | 439 | ssh_gssapi_userok(char *user, struct passwd *pw) |
361 | { | 440 | { |
362 | OM_uint32 lmin; | 441 | OM_uint32 lmin; |
363 | 442 | ||
@@ -367,9 +446,11 @@ ssh_gssapi_userok(char *user) | |||
367 | return 0; | 446 | return 0; |
368 | } | 447 | } |
369 | if (gssapi_client.mech && gssapi_client.mech->userok) | 448 | if (gssapi_client.mech && gssapi_client.mech->userok) |
370 | if ((*gssapi_client.mech->userok)(&gssapi_client, user)) | 449 | if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { |
450 | gssapi_client.used = 1; | ||
451 | gssapi_client.store.owner = pw; | ||
371 | return 1; | 452 | return 1; |
372 | else { | 453 | } else { |
373 | /* Destroy delegated credentials if userok fails */ | 454 | /* Destroy delegated credentials if userok fails */ |
374 | gss_release_buffer(&lmin, &gssapi_client.displayname); | 455 | gss_release_buffer(&lmin, &gssapi_client.displayname); |
375 | gss_release_buffer(&lmin, &gssapi_client.exportedname); | 456 | gss_release_buffer(&lmin, &gssapi_client.exportedname); |
@@ -383,14 +464,90 @@ ssh_gssapi_userok(char *user) | |||
383 | return (0); | 464 | return (0); |
384 | } | 465 | } |
385 | 466 | ||
386 | /* Privileged */ | 467 | /* These bits are only used for rekeying. The unpriviledged child is running |
387 | OM_uint32 | 468 | * as the user, the monitor is root. |
388 | ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) | 469 | * |
470 | * In the child, we want to : | ||
471 | * *) Ask the monitor to store our credentials into the store we specify | ||
472 | * *) If it succeeds, maybe do a PAM update | ||
473 | */ | ||
474 | |||
475 | /* Stuff for PAM */ | ||
476 | |||
477 | #ifdef USE_PAM | ||
478 | static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, | ||
479 | struct pam_response **resp, void *data) | ||
389 | { | 480 | { |
390 | ctx->major = gss_verify_mic(&ctx->minor, ctx->context, | 481 | return (PAM_CONV_ERR); |
391 | gssbuf, gssmic, NULL); | 482 | } |
483 | #endif | ||
392 | 484 | ||
393 | return (ctx->major); | 485 | void |
486 | ssh_gssapi_rekey_creds(void) { | ||
487 | int ok; | ||
488 | int ret; | ||
489 | #ifdef USE_PAM | ||
490 | pam_handle_t *pamh = NULL; | ||
491 | struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; | ||
492 | char *envstr; | ||
493 | #endif | ||
494 | |||
495 | if (gssapi_client.store.filename == NULL && | ||
496 | gssapi_client.store.envval == NULL && | ||
497 | gssapi_client.store.envvar == NULL) | ||
498 | return; | ||
499 | |||
500 | ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); | ||
501 | |||
502 | if (!ok) | ||
503 | return; | ||
504 | |||
505 | debug("Rekeyed credentials stored successfully"); | ||
506 | |||
507 | /* Actually managing to play with the ssh pam stack from here will | ||
508 | * be next to impossible. In any case, we may want different options | ||
509 | * for rekeying. So, use our own :) | ||
510 | */ | ||
511 | #ifdef USE_PAM | ||
512 | if (!use_privsep) { | ||
513 | debug("Not even going to try and do PAM with privsep disabled"); | ||
514 | return; | ||
515 | } | ||
516 | |||
517 | ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, | ||
518 | &pamconv, &pamh); | ||
519 | if (ret) | ||
520 | return; | ||
521 | |||
522 | xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, | ||
523 | gssapi_client.store.envval); | ||
524 | |||
525 | ret = pam_putenv(pamh, envstr); | ||
526 | if (!ret) | ||
527 | pam_setcred(pamh, PAM_REINITIALIZE_CRED); | ||
528 | pam_end(pamh, PAM_SUCCESS); | ||
529 | #endif | ||
530 | } | ||
531 | |||
532 | int | ||
533 | ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { | ||
534 | int ok = 0; | ||
535 | |||
536 | /* Check we've got credentials to store */ | ||
537 | if (!gssapi_client.updated) | ||
538 | return 0; | ||
539 | |||
540 | gssapi_client.updated = 0; | ||
541 | |||
542 | temporarily_use_uid(gssapi_client.store.owner); | ||
543 | if (gssapi_client.mech && gssapi_client.mech->updatecreds) | ||
544 | ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); | ||
545 | else | ||
546 | debug("No update function for this mechanism"); | ||
547 | |||
548 | restore_uid(); | ||
549 | |||
550 | return ok; | ||
394 | } | 551 | } |
395 | 552 | ||
396 | #endif | 553 | #endif |