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-02-10 02:40:08 +0000
commitcd404114ded78fc51d5d9cbd458d55c9b2f67daa (patch)
treedf7a424d9301b69af906b50d550bfce6e6e2c5f3 /gss-serv.c
parent9a975a9faed7c4f334e8c8490db3e77e102f2b21 (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-02-10 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 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
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;
@@ -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 */
130char *
131ssh_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 */
140int
141ssh_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 */
117void 153void
118ssh_gssapi_supported_oids(gss_OID_set *oidset) 154ssh_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
249ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 287ssh_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 */
331int 418int
332ssh_gssapi_userok(char *user) 419ssh_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
358OM_uint32 447 * as the user, the monitor is root.
359ssh_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
457static 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); 464void
465ssh_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
511int
512ssh_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