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