diff options
Diffstat (limited to 'gss-serv.c')
-rw-r--r-- | gss-serv.c | 221 |
1 files changed, 193 insertions, 28 deletions
diff --git a/gss-serv.c b/gss-serv.c index e7b8c5223..539862d67 100644 --- a/gss-serv.c +++ b/gss-serv.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* $OpenBSD: gss-serv.c,v 1.28 2015/01/20 23:14:00 deraadt Exp $ */ | 1 | /* $OpenBSD: gss-serv.c,v 1.28 2015/01/20 23:14:00 deraadt Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. | 4 | * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. |
5 | * | 5 | * |
6 | * Redistribution and use in source and binary forms, with or without | 6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions | 7 | * modification, are permitted provided that the following conditions |
@@ -44,15 +44,21 @@ | |||
44 | #include "channels.h" | 44 | #include "channels.h" |
45 | #include "session.h" | 45 | #include "session.h" |
46 | #include "misc.h" | 46 | #include "misc.h" |
47 | #include "servconf.h" | ||
48 | #include "uidswap.h" | ||
47 | 49 | ||
48 | #include "ssh-gss.h" | 50 | #include "ssh-gss.h" |
51 | #include "monitor_wrap.h" | ||
52 | |||
53 | extern ServerOptions options; | ||
49 | 54 | ||
50 | static ssh_gssapi_client gssapi_client = | 55 | static ssh_gssapi_client gssapi_client = |
51 | { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, | 56 | { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, |
52 | GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; | 57 | GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, |
58 | {NULL, NULL, NULL, NULL, NULL}, 0, 0}; | ||
53 | 59 | ||
54 | ssh_gssapi_mech gssapi_null_mech = | 60 | ssh_gssapi_mech gssapi_null_mech = |
55 | { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; | 61 | { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; |
56 | 62 | ||
57 | #ifdef KRB5 | 63 | #ifdef KRB5 |
58 | extern ssh_gssapi_mech gssapi_kerberos_mech; | 64 | extern ssh_gssapi_mech gssapi_kerberos_mech; |
@@ -99,25 +105,32 @@ ssh_gssapi_acquire_cred(Gssctxt *ctx) | |||
99 | char lname[NI_MAXHOST]; | 105 | char lname[NI_MAXHOST]; |
100 | gss_OID_set oidset; | 106 | gss_OID_set oidset; |
101 | 107 | ||
102 | gss_create_empty_oid_set(&status, &oidset); | 108 | if (options.gss_strict_acceptor) { |
103 | gss_add_oid_set_member(&status, ctx->oid, &oidset); | 109 | gss_create_empty_oid_set(&status, &oidset); |
110 | gss_add_oid_set_member(&status, ctx->oid, &oidset); | ||
104 | 111 | ||
105 | if (gethostname(lname, sizeof(lname))) { | 112 | if (gethostname(lname, sizeof(lname))) { |
106 | gss_release_oid_set(&status, &oidset); | 113 | gss_release_oid_set(&status, &oidset); |
107 | return (-1); | 114 | return (-1); |
108 | } | 115 | } |
116 | |||
117 | if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { | ||
118 | gss_release_oid_set(&status, &oidset); | ||
119 | return (ctx->major); | ||
120 | } | ||
121 | |||
122 | if ((ctx->major = gss_acquire_cred(&ctx->minor, | ||
123 | ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, | ||
124 | NULL, NULL))) | ||
125 | ssh_gssapi_error(ctx); | ||
109 | 126 | ||
110 | if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { | ||
111 | gss_release_oid_set(&status, &oidset); | 127 | gss_release_oid_set(&status, &oidset); |
112 | return (ctx->major); | 128 | return (ctx->major); |
129 | } else { | ||
130 | ctx->name = GSS_C_NO_NAME; | ||
131 | ctx->creds = GSS_C_NO_CREDENTIAL; | ||
113 | } | 132 | } |
114 | 133 | return GSS_S_COMPLETE; | |
115 | if ((ctx->major = gss_acquire_cred(&ctx->minor, | ||
116 | ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL))) | ||
117 | ssh_gssapi_error(ctx); | ||
118 | |||
119 | gss_release_oid_set(&status, &oidset); | ||
120 | return (ctx->major); | ||
121 | } | 134 | } |
122 | 135 | ||
123 | /* Privileged */ | 136 | /* Privileged */ |
@@ -132,6 +145,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) | |||
132 | } | 145 | } |
133 | 146 | ||
134 | /* Unprivileged */ | 147 | /* Unprivileged */ |
148 | char * | ||
149 | ssh_gssapi_server_mechanisms(void) { | ||
150 | gss_OID_set supported; | ||
151 | |||
152 | ssh_gssapi_supported_oids(&supported); | ||
153 | return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech, | ||
154 | NULL, NULL)); | ||
155 | } | ||
156 | |||
157 | /* Unprivileged */ | ||
158 | int | ||
159 | ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, | ||
160 | const char *dummy) { | ||
161 | Gssctxt *ctx = NULL; | ||
162 | int res; | ||
163 | |||
164 | res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); | ||
165 | ssh_gssapi_delete_ctx(&ctx); | ||
166 | |||
167 | return (res); | ||
168 | } | ||
169 | |||
170 | /* Unprivileged */ | ||
135 | void | 171 | void |
136 | ssh_gssapi_supported_oids(gss_OID_set *oidset) | 172 | ssh_gssapi_supported_oids(gss_OID_set *oidset) |
137 | { | 173 | { |
@@ -141,7 +177,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset) | |||
141 | gss_OID_set supported; | 177 | gss_OID_set supported; |
142 | 178 | ||
143 | gss_create_empty_oid_set(&min_status, oidset); | 179 | gss_create_empty_oid_set(&min_status, oidset); |
144 | gss_indicate_mechs(&min_status, &supported); | 180 | |
181 | if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) | ||
182 | return; | ||
145 | 183 | ||
146 | while (supported_mechs[i]->name != NULL) { | 184 | while (supported_mechs[i]->name != NULL) { |
147 | if (GSS_ERROR(gss_test_oid_set_member(&min_status, | 185 | if (GSS_ERROR(gss_test_oid_set_member(&min_status, |
@@ -267,8 +305,48 @@ OM_uint32 | |||
267 | ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | 305 | ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) |
268 | { | 306 | { |
269 | int i = 0; | 307 | int i = 0; |
308 | int equal = 0; | ||
309 | gss_name_t new_name = GSS_C_NO_NAME; | ||
310 | gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; | ||
311 | |||
312 | if (options.gss_store_rekey && client->used && ctx->client_creds) { | ||
313 | if (client->mech->oid.length != ctx->oid->length || | ||
314 | (memcmp(client->mech->oid.elements, | ||
315 | ctx->oid->elements, ctx->oid->length) !=0)) { | ||
316 | debug("Rekeyed credentials have different mechanism"); | ||
317 | return GSS_S_COMPLETE; | ||
318 | } | ||
319 | |||
320 | if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, | ||
321 | ctx->client_creds, ctx->oid, &new_name, | ||
322 | NULL, NULL, NULL))) { | ||
323 | ssh_gssapi_error(ctx); | ||
324 | return (ctx->major); | ||
325 | } | ||
326 | |||
327 | ctx->major = gss_compare_name(&ctx->minor, client->name, | ||
328 | new_name, &equal); | ||
270 | 329 | ||
271 | gss_buffer_desc ename; | 330 | if (GSS_ERROR(ctx->major)) { |
331 | ssh_gssapi_error(ctx); | ||
332 | return (ctx->major); | ||
333 | } | ||
334 | |||
335 | if (!equal) { | ||
336 | debug("Rekeyed credentials have different name"); | ||
337 | return GSS_S_COMPLETE; | ||
338 | } | ||
339 | |||
340 | debug("Marking rekeyed credentials for export"); | ||
341 | |||
342 | gss_release_name(&ctx->minor, &client->name); | ||
343 | gss_release_cred(&ctx->minor, &client->creds); | ||
344 | client->name = new_name; | ||
345 | client->creds = ctx->client_creds; | ||
346 | ctx->client_creds = GSS_C_NO_CREDENTIAL; | ||
347 | client->updated = 1; | ||
348 | return GSS_S_COMPLETE; | ||
349 | } | ||
272 | 350 | ||
273 | client->mech = NULL; | 351 | client->mech = NULL; |
274 | 352 | ||
@@ -283,6 +361,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | |||
283 | if (client->mech == NULL) | 361 | if (client->mech == NULL) |
284 | return GSS_S_FAILURE; | 362 | return GSS_S_FAILURE; |
285 | 363 | ||
364 | if (ctx->client_creds && | ||
365 | (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, | ||
366 | ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { | ||
367 | ssh_gssapi_error(ctx); | ||
368 | return (ctx->major); | ||
369 | } | ||
370 | |||
286 | if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, | 371 | if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, |
287 | &client->displayname, NULL))) { | 372 | &client->displayname, NULL))) { |
288 | ssh_gssapi_error(ctx); | 373 | ssh_gssapi_error(ctx); |
@@ -300,6 +385,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | |||
300 | return (ctx->major); | 385 | return (ctx->major); |
301 | } | 386 | } |
302 | 387 | ||
388 | gss_release_buffer(&ctx->minor, &ename); | ||
389 | |||
303 | /* We can't copy this structure, so we just move the pointer to it */ | 390 | /* We can't copy this structure, so we just move the pointer to it */ |
304 | client->creds = ctx->client_creds; | 391 | client->creds = ctx->client_creds; |
305 | ctx->client_creds = GSS_C_NO_CREDENTIAL; | 392 | ctx->client_creds = GSS_C_NO_CREDENTIAL; |
@@ -347,7 +434,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep) | |||
347 | 434 | ||
348 | /* Privileged */ | 435 | /* Privileged */ |
349 | int | 436 | int |
350 | ssh_gssapi_userok(char *user) | 437 | ssh_gssapi_userok(char *user, struct passwd *pw) |
351 | { | 438 | { |
352 | OM_uint32 lmin; | 439 | OM_uint32 lmin; |
353 | 440 | ||
@@ -357,9 +444,11 @@ ssh_gssapi_userok(char *user) | |||
357 | return 0; | 444 | return 0; |
358 | } | 445 | } |
359 | if (gssapi_client.mech && gssapi_client.mech->userok) | 446 | if (gssapi_client.mech && gssapi_client.mech->userok) |
360 | if ((*gssapi_client.mech->userok)(&gssapi_client, user)) | 447 | if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { |
448 | gssapi_client.used = 1; | ||
449 | gssapi_client.store.owner = pw; | ||
361 | return 1; | 450 | return 1; |
362 | else { | 451 | } else { |
363 | /* Destroy delegated credentials if userok fails */ | 452 | /* Destroy delegated credentials if userok fails */ |
364 | gss_release_buffer(&lmin, &gssapi_client.displayname); | 453 | gss_release_buffer(&lmin, &gssapi_client.displayname); |
365 | gss_release_buffer(&lmin, &gssapi_client.exportedname); | 454 | gss_release_buffer(&lmin, &gssapi_client.exportedname); |
@@ -373,14 +462,90 @@ ssh_gssapi_userok(char *user) | |||
373 | return (0); | 462 | return (0); |
374 | } | 463 | } |
375 | 464 | ||
376 | /* Privileged */ | 465 | /* These bits are only used for rekeying. The unpriviledged child is running |
377 | OM_uint32 | 466 | * as the user, the monitor is root. |
378 | ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) | 467 | * |
468 | * In the child, we want to : | ||
469 | * *) Ask the monitor to store our credentials into the store we specify | ||
470 | * *) If it succeeds, maybe do a PAM update | ||
471 | */ | ||
472 | |||
473 | /* Stuff for PAM */ | ||
474 | |||
475 | #ifdef USE_PAM | ||
476 | static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, | ||
477 | struct pam_response **resp, void *data) | ||
379 | { | 478 | { |
380 | ctx->major = gss_verify_mic(&ctx->minor, ctx->context, | 479 | return (PAM_CONV_ERR); |
381 | gssbuf, gssmic, NULL); | 480 | } |
481 | #endif | ||
382 | 482 | ||
383 | return (ctx->major); | 483 | void |
484 | ssh_gssapi_rekey_creds(void) { | ||
485 | int ok; | ||
486 | int ret; | ||
487 | #ifdef USE_PAM | ||
488 | pam_handle_t *pamh = NULL; | ||
489 | struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; | ||
490 | char *envstr; | ||
491 | #endif | ||
492 | |||
493 | if (gssapi_client.store.filename == NULL && | ||
494 | gssapi_client.store.envval == NULL && | ||
495 | gssapi_client.store.envvar == NULL) | ||
496 | return; | ||
497 | |||
498 | ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); | ||
499 | |||
500 | if (!ok) | ||
501 | return; | ||
502 | |||
503 | debug("Rekeyed credentials stored successfully"); | ||
504 | |||
505 | /* Actually managing to play with the ssh pam stack from here will | ||
506 | * be next to impossible. In any case, we may want different options | ||
507 | * for rekeying. So, use our own :) | ||
508 | */ | ||
509 | #ifdef USE_PAM | ||
510 | if (!use_privsep) { | ||
511 | debug("Not even going to try and do PAM with privsep disabled"); | ||
512 | return; | ||
513 | } | ||
514 | |||
515 | ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, | ||
516 | &pamconv, &pamh); | ||
517 | if (ret) | ||
518 | return; | ||
519 | |||
520 | xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, | ||
521 | gssapi_client.store.envval); | ||
522 | |||
523 | ret = pam_putenv(pamh, envstr); | ||
524 | if (!ret) | ||
525 | pam_setcred(pamh, PAM_REINITIALIZE_CRED); | ||
526 | pam_end(pamh, PAM_SUCCESS); | ||
527 | #endif | ||
528 | } | ||
529 | |||
530 | int | ||
531 | ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { | ||
532 | int ok = 0; | ||
533 | |||
534 | /* Check we've got credentials to store */ | ||
535 | if (!gssapi_client.updated) | ||
536 | return 0; | ||
537 | |||
538 | gssapi_client.updated = 0; | ||
539 | |||
540 | temporarily_use_uid(gssapi_client.store.owner); | ||
541 | if (gssapi_client.mech && gssapi_client.mech->updatecreds) | ||
542 | ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); | ||
543 | else | ||
544 | debug("No update function for this mechanism"); | ||
545 | |||
546 | restore_uid(); | ||
547 | |||
548 | return ok; | ||
384 | } | 549 | } |
385 | 550 | ||
386 | #endif | 551 | #endif |