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 95348e251..97f366fdf 100644 --- a/gss-serv.c +++ b/gss-serv.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* $OpenBSD: gss-serv.c,v 1.24 2013/07/20 01:55:13 djm Exp $ */ | 1 | /* $OpenBSD: gss-serv.c,v 1.24 2013/07/20 01:55:13 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,15 +45,21 @@ | |||
45 | #include "channels.h" | 45 | #include "channels.h" |
46 | #include "session.h" | 46 | #include "session.h" |
47 | #include "misc.h" | 47 | #include "misc.h" |
48 | #include "servconf.h" | ||
49 | #include "uidswap.h" | ||
48 | 50 | ||
49 | #include "ssh-gss.h" | 51 | #include "ssh-gss.h" |
52 | #include "monitor_wrap.h" | ||
53 | |||
54 | extern ServerOptions options; | ||
50 | 55 | ||
51 | static ssh_gssapi_client gssapi_client = | 56 | static ssh_gssapi_client gssapi_client = |
52 | { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, | 57 | { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, |
53 | GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; | 58 | GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, |
59 | {NULL, NULL, NULL, NULL, NULL}, 0, 0}; | ||
54 | 60 | ||
55 | ssh_gssapi_mech gssapi_null_mech = | 61 | ssh_gssapi_mech gssapi_null_mech = |
56 | { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; | 62 | { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; |
57 | 63 | ||
58 | #ifdef KRB5 | 64 | #ifdef KRB5 |
59 | extern ssh_gssapi_mech gssapi_kerberos_mech; | 65 | extern ssh_gssapi_mech gssapi_kerberos_mech; |
@@ -81,25 +87,32 @@ ssh_gssapi_acquire_cred(Gssctxt *ctx) | |||
81 | char lname[MAXHOSTNAMELEN]; | 87 | char lname[MAXHOSTNAMELEN]; |
82 | gss_OID_set oidset; | 88 | gss_OID_set oidset; |
83 | 89 | ||
84 | gss_create_empty_oid_set(&status, &oidset); | 90 | if (options.gss_strict_acceptor) { |
85 | gss_add_oid_set_member(&status, ctx->oid, &oidset); | 91 | gss_create_empty_oid_set(&status, &oidset); |
92 | gss_add_oid_set_member(&status, ctx->oid, &oidset); | ||
86 | 93 | ||
87 | if (gethostname(lname, MAXHOSTNAMELEN)) { | 94 | if (gethostname(lname, MAXHOSTNAMELEN)) { |
88 | gss_release_oid_set(&status, &oidset); | 95 | gss_release_oid_set(&status, &oidset); |
89 | return (-1); | 96 | return (-1); |
90 | } | 97 | } |
98 | |||
99 | if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { | ||
100 | gss_release_oid_set(&status, &oidset); | ||
101 | return (ctx->major); | ||
102 | } | ||
103 | |||
104 | if ((ctx->major = gss_acquire_cred(&ctx->minor, | ||
105 | ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, | ||
106 | NULL, NULL))) | ||
107 | ssh_gssapi_error(ctx); | ||
91 | 108 | ||
92 | if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { | ||
93 | gss_release_oid_set(&status, &oidset); | 109 | gss_release_oid_set(&status, &oidset); |
94 | return (ctx->major); | 110 | return (ctx->major); |
111 | } else { | ||
112 | ctx->name = GSS_C_NO_NAME; | ||
113 | ctx->creds = GSS_C_NO_CREDENTIAL; | ||
95 | } | 114 | } |
96 | 115 | return GSS_S_COMPLETE; | |
97 | if ((ctx->major = gss_acquire_cred(&ctx->minor, | ||
98 | ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL))) | ||
99 | ssh_gssapi_error(ctx); | ||
100 | |||
101 | gss_release_oid_set(&status, &oidset); | ||
102 | return (ctx->major); | ||
103 | } | 116 | } |
104 | 117 | ||
105 | /* Privileged */ | 118 | /* Privileged */ |
@@ -114,6 +127,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) | |||
114 | } | 127 | } |
115 | 128 | ||
116 | /* Unprivileged */ | 129 | /* Unprivileged */ |
130 | char * | ||
131 | ssh_gssapi_server_mechanisms() { | ||
132 | gss_OID_set supported; | ||
133 | |||
134 | ssh_gssapi_supported_oids(&supported); | ||
135 | return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech, | ||
136 | NULL, NULL)); | ||
137 | } | ||
138 | |||
139 | /* Unprivileged */ | ||
140 | int | ||
141 | ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, | ||
142 | const char *dummy) { | ||
143 | Gssctxt *ctx = NULL; | ||
144 | int res; | ||
145 | |||
146 | res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); | ||
147 | ssh_gssapi_delete_ctx(&ctx); | ||
148 | |||
149 | return (res); | ||
150 | } | ||
151 | |||
152 | /* Unprivileged */ | ||
117 | void | 153 | void |
118 | ssh_gssapi_supported_oids(gss_OID_set *oidset) | 154 | ssh_gssapi_supported_oids(gss_OID_set *oidset) |
119 | { | 155 | { |
@@ -123,7 +159,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset) | |||
123 | gss_OID_set supported; | 159 | gss_OID_set supported; |
124 | 160 | ||
125 | gss_create_empty_oid_set(&min_status, oidset); | 161 | gss_create_empty_oid_set(&min_status, oidset); |
126 | gss_indicate_mechs(&min_status, &supported); | 162 | |
163 | if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) | ||
164 | return; | ||
127 | 165 | ||
128 | while (supported_mechs[i]->name != NULL) { | 166 | while (supported_mechs[i]->name != NULL) { |
129 | if (GSS_ERROR(gss_test_oid_set_member(&min_status, | 167 | if (GSS_ERROR(gss_test_oid_set_member(&min_status, |
@@ -249,8 +287,48 @@ OM_uint32 | |||
249 | ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | 287 | ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) |
250 | { | 288 | { |
251 | int i = 0; | 289 | int i = 0; |
290 | int equal = 0; | ||
291 | gss_name_t new_name = GSS_C_NO_NAME; | ||
292 | gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; | ||
293 | |||
294 | if (options.gss_store_rekey && client->used && ctx->client_creds) { | ||
295 | if (client->mech->oid.length != ctx->oid->length || | ||
296 | (memcmp(client->mech->oid.elements, | ||
297 | ctx->oid->elements, ctx->oid->length) !=0)) { | ||
298 | debug("Rekeyed credentials have different mechanism"); | ||
299 | return GSS_S_COMPLETE; | ||
300 | } | ||
301 | |||
302 | if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, | ||
303 | ctx->client_creds, ctx->oid, &new_name, | ||
304 | NULL, NULL, NULL))) { | ||
305 | ssh_gssapi_error(ctx); | ||
306 | return (ctx->major); | ||
307 | } | ||
308 | |||
309 | ctx->major = gss_compare_name(&ctx->minor, client->name, | ||
310 | new_name, &equal); | ||
252 | 311 | ||
253 | gss_buffer_desc ename; | 312 | if (GSS_ERROR(ctx->major)) { |
313 | ssh_gssapi_error(ctx); | ||
314 | return (ctx->major); | ||
315 | } | ||
316 | |||
317 | if (!equal) { | ||
318 | debug("Rekeyed credentials have different name"); | ||
319 | return GSS_S_COMPLETE; | ||
320 | } | ||
321 | |||
322 | debug("Marking rekeyed credentials for export"); | ||
323 | |||
324 | gss_release_name(&ctx->minor, &client->name); | ||
325 | gss_release_cred(&ctx->minor, &client->creds); | ||
326 | client->name = new_name; | ||
327 | client->creds = ctx->client_creds; | ||
328 | ctx->client_creds = GSS_C_NO_CREDENTIAL; | ||
329 | client->updated = 1; | ||
330 | return GSS_S_COMPLETE; | ||
331 | } | ||
254 | 332 | ||
255 | client->mech = NULL; | 333 | client->mech = NULL; |
256 | 334 | ||
@@ -265,6 +343,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | |||
265 | if (client->mech == NULL) | 343 | if (client->mech == NULL) |
266 | return GSS_S_FAILURE; | 344 | return GSS_S_FAILURE; |
267 | 345 | ||
346 | if (ctx->client_creds && | ||
347 | (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, | ||
348 | ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { | ||
349 | ssh_gssapi_error(ctx); | ||
350 | return (ctx->major); | ||
351 | } | ||
352 | |||
268 | if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, | 353 | if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, |
269 | &client->displayname, NULL))) { | 354 | &client->displayname, NULL))) { |
270 | ssh_gssapi_error(ctx); | 355 | ssh_gssapi_error(ctx); |
@@ -282,6 +367,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) | |||
282 | return (ctx->major); | 367 | return (ctx->major); |
283 | } | 368 | } |
284 | 369 | ||
370 | gss_release_buffer(&ctx->minor, &ename); | ||
371 | |||
285 | /* We can't copy this structure, so we just move the pointer to it */ | 372 | /* We can't copy this structure, so we just move the pointer to it */ |
286 | client->creds = ctx->client_creds; | 373 | client->creds = ctx->client_creds; |
287 | ctx->client_creds = GSS_C_NO_CREDENTIAL; | 374 | ctx->client_creds = GSS_C_NO_CREDENTIAL; |
@@ -329,7 +416,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep) | |||
329 | 416 | ||
330 | /* Privileged */ | 417 | /* Privileged */ |
331 | int | 418 | int |
332 | ssh_gssapi_userok(char *user) | 419 | ssh_gssapi_userok(char *user, struct passwd *pw) |
333 | { | 420 | { |
334 | OM_uint32 lmin; | 421 | OM_uint32 lmin; |
335 | 422 | ||
@@ -339,9 +426,11 @@ ssh_gssapi_userok(char *user) | |||
339 | return 0; | 426 | return 0; |
340 | } | 427 | } |
341 | if (gssapi_client.mech && gssapi_client.mech->userok) | 428 | if (gssapi_client.mech && gssapi_client.mech->userok) |
342 | if ((*gssapi_client.mech->userok)(&gssapi_client, user)) | 429 | if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { |
430 | gssapi_client.used = 1; | ||
431 | gssapi_client.store.owner = pw; | ||
343 | return 1; | 432 | return 1; |
344 | else { | 433 | } else { |
345 | /* Destroy delegated credentials if userok fails */ | 434 | /* Destroy delegated credentials if userok fails */ |
346 | gss_release_buffer(&lmin, &gssapi_client.displayname); | 435 | gss_release_buffer(&lmin, &gssapi_client.displayname); |
347 | gss_release_buffer(&lmin, &gssapi_client.exportedname); | 436 | gss_release_buffer(&lmin, &gssapi_client.exportedname); |
@@ -354,14 +443,90 @@ ssh_gssapi_userok(char *user) | |||
354 | return (0); | 443 | return (0); |
355 | } | 444 | } |
356 | 445 | ||
357 | /* Privileged */ | 446 | /* These bits are only used for rekeying. The unpriviledged child is running |
358 | OM_uint32 | 447 | * as the user, the monitor is root. |
359 | ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) | 448 | * |
449 | * In the child, we want to : | ||
450 | * *) Ask the monitor to store our credentials into the store we specify | ||
451 | * *) If it succeeds, maybe do a PAM update | ||
452 | */ | ||
453 | |||
454 | /* Stuff for PAM */ | ||
455 | |||
456 | #ifdef USE_PAM | ||
457 | static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, | ||
458 | struct pam_response **resp, void *data) | ||
360 | { | 459 | { |
361 | ctx->major = gss_verify_mic(&ctx->minor, ctx->context, | 460 | return (PAM_CONV_ERR); |
362 | gssbuf, gssmic, NULL); | 461 | } |
462 | #endif | ||
363 | 463 | ||
364 | return (ctx->major); | 464 | void |
465 | ssh_gssapi_rekey_creds() { | ||
466 | int ok; | ||
467 | int ret; | ||
468 | #ifdef USE_PAM | ||
469 | pam_handle_t *pamh = NULL; | ||
470 | struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; | ||
471 | char *envstr; | ||
472 | #endif | ||
473 | |||
474 | if (gssapi_client.store.filename == NULL && | ||
475 | gssapi_client.store.envval == NULL && | ||
476 | gssapi_client.store.envvar == NULL) | ||
477 | return; | ||
478 | |||
479 | ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); | ||
480 | |||
481 | if (!ok) | ||
482 | return; | ||
483 | |||
484 | debug("Rekeyed credentials stored successfully"); | ||
485 | |||
486 | /* Actually managing to play with the ssh pam stack from here will | ||
487 | * be next to impossible. In any case, we may want different options | ||
488 | * for rekeying. So, use our own :) | ||
489 | */ | ||
490 | #ifdef USE_PAM | ||
491 | if (!use_privsep) { | ||
492 | debug("Not even going to try and do PAM with privsep disabled"); | ||
493 | return; | ||
494 | } | ||
495 | |||
496 | ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, | ||
497 | &pamconv, &pamh); | ||
498 | if (ret) | ||
499 | return; | ||
500 | |||
501 | xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, | ||
502 | gssapi_client.store.envval); | ||
503 | |||
504 | ret = pam_putenv(pamh, envstr); | ||
505 | if (!ret) | ||
506 | pam_setcred(pamh, PAM_REINITIALIZE_CRED); | ||
507 | pam_end(pamh, PAM_SUCCESS); | ||
508 | #endif | ||
509 | } | ||
510 | |||
511 | int | ||
512 | ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { | ||
513 | int ok = 0; | ||
514 | |||
515 | /* Check we've got credentials to store */ | ||
516 | if (!gssapi_client.updated) | ||
517 | return 0; | ||
518 | |||
519 | gssapi_client.updated = 0; | ||
520 | |||
521 | temporarily_use_uid(gssapi_client.store.owner); | ||
522 | if (gssapi_client.mech && gssapi_client.mech->updatecreds) | ||
523 | ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); | ||
524 | else | ||
525 | debug("No update function for this mechanism"); | ||
526 | |||
527 | restore_uid(); | ||
528 | |||
529 | return ok; | ||
365 | } | 530 | } |
366 | 531 | ||
367 | #endif | 532 | #endif |