diff options
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 |