allow prompting for GSS creds
[openssh.git] / gss-genr.c
1 /* $OpenBSD: gss-genr.c,v 1.20 2009/06/22 05:39:28 dtucker Exp $ */
2
3 /*
4  * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "includes.h"
28
29 #ifdef GSSAPI
30
31 #include <sys/types.h>
32 #include <sys/param.h>
33
34 #include <stdarg.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include "xmalloc.h"
39 #include "buffer.h"
40 #include "log.h"
41 #include "ssh2.h"
42 #include "cipher.h"
43 #include "key.h"
44 #include "kex.h"
45 #include "misc.h"
46 #include "ssh.h"
47 #include "readconf.h"
48 #include <openssl/evp.h>
49
50 #include "ssh-gss.h"
51
52 extern u_char *session_id2;
53 extern u_int session_id2_len;
54 extern Options options;
55
56 typedef struct {
57         char *encoded;
58         gss_OID oid;
59 } ssh_gss_kex_mapping;
60
61 /*
62  * XXX - It would be nice to find a more elegant way of handling the
63  * XXX   passing of the key exchange context to the userauth routines
64  */
65
66 Gssctxt *gss_kex_context = NULL;
67
68 static ssh_gss_kex_mapping *gss_enc2oid = NULL;
69
70 static char *gss_password = NULL;
71
72 static void
73 ssh_gssapi_cleanup_password(void)
74 {
75         if (gss_password) {
76                 memset(gss_password, 0, strlen(gss_password));
77                 xfree(gss_password);
78         }
79 }
80
81 int 
82 ssh_gssapi_oid_table_ok() {
83         return (gss_enc2oid != NULL);
84 }
85
86 /*
87  * Return a list of the gss-group1-sha1 mechanisms supported by this program
88  *
89  * We test mechanisms to ensure that we can use them, to avoid starting
90  * a key exchange with a bad mechanism
91  */
92
93 char *
94 ssh_gssapi_client_mechanisms(const char *host, const char *client) {
95         gss_OID_set gss_supported;
96         OM_uint32 maj_status, min_status;
97
98         if (options.gss_mechanism_oid) {
99                 maj_status = gss_create_empty_oid_set(&min_status,
100                                                       &gss_supported);
101                 if (!GSS_ERROR(maj_status))
102                         maj_status = gss_add_oid_set_member(&min_status,
103                                                             options.gss_mechanism_oid,
104                                                             &gss_supported);
105         } else {
106                 maj_status = gss_indicate_mechs(&min_status, &gss_supported);
107         }
108
109         if (GSS_ERROR(maj_status))
110                 return NULL;
111
112         return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
113             host, client));
114 }
115
116 char *
117 ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
118     const char *host, const char *client) {
119         Buffer buf;
120         size_t i;
121         int oidpos, enclen;
122         char *mechs, *encoded;
123         u_char digest[EVP_MAX_MD_SIZE];
124         char deroid[2];
125         const EVP_MD *evp_md = EVP_md5();
126         EVP_MD_CTX md;
127
128         if (gss_enc2oid != NULL) {
129                 for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
130                         xfree(gss_enc2oid[i].encoded);
131                 xfree(gss_enc2oid);
132         }
133
134         gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
135             (gss_supported->count + 1));
136
137         buffer_init(&buf);
138
139         oidpos = 0;
140         for (i = 0; i < gss_supported->count; i++) {
141                 if (gss_supported->elements[i].length < 128 &&
142                     (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
143
144                         deroid[0] = SSH_GSS_OIDTYPE;
145                         deroid[1] = gss_supported->elements[i].length;
146
147                         EVP_DigestInit(&md, evp_md);
148                         EVP_DigestUpdate(&md, deroid, 2);
149                         EVP_DigestUpdate(&md,
150                             gss_supported->elements[i].elements,
151                             gss_supported->elements[i].length);
152                         EVP_DigestFinal(&md, digest, NULL);
153
154                         encoded = xmalloc(EVP_MD_size(evp_md) * 2);
155                         enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
156                             encoded, EVP_MD_size(evp_md) * 2);
157
158                         if (oidpos != 0)
159                                 buffer_put_char(&buf, ',');
160
161                         buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
162                             sizeof(KEX_GSS_GEX_SHA1_ID) - 1);
163                         buffer_append(&buf, encoded, enclen);
164                         buffer_put_char(&buf, ',');
165                         buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, 
166                             sizeof(KEX_GSS_GRP1_SHA1_ID) - 1);
167                         buffer_append(&buf, encoded, enclen);
168                         buffer_put_char(&buf, ',');
169                         buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
170                             sizeof(KEX_GSS_GRP14_SHA1_ID) - 1);
171                         buffer_append(&buf, encoded, enclen);
172
173                         gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
174                         gss_enc2oid[oidpos].encoded = encoded;
175                         oidpos++;
176                 }
177         }
178         gss_enc2oid[oidpos].oid = NULL;
179         gss_enc2oid[oidpos].encoded = NULL;
180
181         buffer_put_char(&buf, '\0');
182
183         mechs = xmalloc(buffer_len(&buf));
184         buffer_get(&buf, mechs, buffer_len(&buf));
185         buffer_free(&buf);
186
187         if (strlen(mechs) == 0) {
188                 xfree(mechs);
189                 mechs = NULL;
190         }
191         
192         return (mechs);
193 }
194
195 gss_OID
196 ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
197         int i = 0;
198         
199         switch (kex_type) {
200         case KEX_GSS_GRP1_SHA1:
201                 if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
202                         return GSS_C_NO_OID;
203                 name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
204                 break;
205         case KEX_GSS_GRP14_SHA1:
206                 if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
207                         return GSS_C_NO_OID;
208                 name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
209                 break;
210         case KEX_GSS_GEX_SHA1:
211                 if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
212                         return GSS_C_NO_OID;
213                 name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
214                 break;
215         default:
216                 return GSS_C_NO_OID;
217         }
218
219         while (gss_enc2oid[i].encoded != NULL &&
220             strcmp(name, gss_enc2oid[i].encoded) != 0)
221                 i++;
222
223         if (gss_enc2oid[i].oid != NULL && ctx != NULL)
224                 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
225
226         return gss_enc2oid[i].oid;
227 }
228
229 /* Check that the OID in a data stream matches that in the context */
230 int
231 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
232 {
233         return (ctx != NULL && ctx->oid != GSS_C_NO_OID &&
234             ctx->oid->length == len &&
235             memcmp(ctx->oid->elements, data, len) == 0);
236 }
237
238 /* Set the contexts OID from a data stream */
239 void
240 ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len)
241 {
242         if (ctx->oid != GSS_C_NO_OID) {
243                 xfree(ctx->oid->elements);
244                 xfree(ctx->oid);
245         }
246         ctx->oid = xmalloc(sizeof(gss_OID_desc));
247         ctx->oid->length = len;
248         ctx->oid->elements = xmalloc(len);
249         memcpy(ctx->oid->elements, data, len);
250 }
251
252 /* Set the contexts OID */
253 void
254 ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid)
255 {
256         ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length);
257 }
258
259 /* All this effort to report an error ... */
260 void
261 ssh_gssapi_error(Gssctxt *ctxt)
262 {
263         char *s;
264
265         s = ssh_gssapi_last_error(ctxt, NULL, NULL);
266         debug("%s", s);
267         xfree(s);
268 }
269
270 char *
271 ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status,
272     OM_uint32 *minor_status)
273 {
274         OM_uint32 lmin;
275         gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
276         OM_uint32 ctx;
277         Buffer b;
278         char *ret;
279
280         buffer_init(&b);
281
282         if (major_status != NULL)
283                 *major_status = ctxt->major;
284         if (minor_status != NULL)
285                 *minor_status = ctxt->minor;
286
287         ctx = 0;
288         /* The GSSAPI error */
289         do {
290                 gss_display_status(&lmin, ctxt->major,
291                     GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg);
292
293                 buffer_append(&b, msg.value, msg.length);
294                 buffer_put_char(&b, '\n');
295
296                 gss_release_buffer(&lmin, &msg);
297         } while (ctx != 0);
298
299         /* The mechanism specific error */
300         do {
301                 gss_display_status(&lmin, ctxt->minor,
302                     GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg);
303
304                 buffer_append(&b, msg.value, msg.length);
305                 buffer_put_char(&b, '\n');
306
307                 gss_release_buffer(&lmin, &msg);
308         } while (ctx != 0);
309
310         buffer_put_char(&b, '\0');
311         ret = xmalloc(buffer_len(&b));
312         buffer_get(&b, ret, buffer_len(&b));
313         buffer_free(&b);
314         return (ret);
315 }
316
317 /*
318  * Initialise our GSSAPI context. We use this opaque structure to contain all
319  * of the data which both the client and server need to persist across
320  * {accept,init}_sec_context calls, so that when we do it from the userauth
321  * stuff life is a little easier
322  */
323 void
324 ssh_gssapi_build_ctx(Gssctxt **ctx)
325 {
326         *ctx = xcalloc(1, sizeof (Gssctxt));
327         (*ctx)->context = GSS_C_NO_CONTEXT;
328         (*ctx)->name = GSS_C_NO_NAME;
329         (*ctx)->oid = GSS_C_NO_OID;
330         (*ctx)->creds = GSS_C_NO_CREDENTIAL;
331         (*ctx)->client = GSS_C_NO_NAME;
332         (*ctx)->client_creds = GSS_C_NO_CREDENTIAL;
333 }
334
335 /* Delete our context, providing it has been built correctly */
336 void
337 ssh_gssapi_delete_ctx(Gssctxt **ctx)
338 {
339         OM_uint32 ms;
340
341         if ((*ctx) == NULL)
342                 return;
343         if ((*ctx)->context != GSS_C_NO_CONTEXT)
344                 gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER);
345         if ((*ctx)->name != GSS_C_NO_NAME)
346                 gss_release_name(&ms, &(*ctx)->name);
347         if ((*ctx)->oid != GSS_C_NO_OID) {
348                 xfree((*ctx)->oid->elements);
349                 xfree((*ctx)->oid);
350                 (*ctx)->oid = GSS_C_NO_OID;
351         }
352         if ((*ctx)->creds != GSS_C_NO_CREDENTIAL)
353                 gss_release_cred(&ms, &(*ctx)->creds);
354         if ((*ctx)->client != GSS_C_NO_NAME)
355                 gss_release_name(&ms, &(*ctx)->client);
356         if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL)
357                 gss_release_cred(&ms, &(*ctx)->client_creds);
358
359         xfree(*ctx);
360         *ctx = NULL;
361 }
362
363 /*
364  * Wrapper to init_sec_context
365  * Requires that the context contains:
366  *      oid
367  *      server name (from ssh_gssapi_import_name)
368  */
369 OM_uint32
370 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
371     gss_buffer_desc* send_tok, OM_uint32 *flags)
372 {
373         int deleg_flag = 0;
374
375         if (deleg_creds) {
376                 deleg_flag = GSS_C_DELEG_FLAG;
377                 debug("Delegating credentials");
378         }
379
380         ctx->major = gss_init_sec_context(&ctx->minor,
381             ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
382             GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
383             0, NULL, recv_tok, NULL, send_tok, flags, NULL);
384
385         if (GSS_ERROR(ctx->major))
386                 ssh_gssapi_error(ctx);
387
388         return (ctx->major);
389 }
390
391 /* Create a service name for the given host */
392 OM_uint32
393 ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
394 {
395         gss_buffer_desc gssbuf;
396         char *val;
397
398         xasprintf(&val, "host@%s", host);
399         gssbuf.value = val;
400         gssbuf.length = strlen(gssbuf.value);
401
402         if ((ctx->major = gss_import_name(&ctx->minor,
403             &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name)))
404                 ssh_gssapi_error(ctx);
405
406         xfree(gssbuf.value);
407         return (ctx->major);
408 }
409
410 OM_uint32
411 ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
412 {
413         gss_buffer_desc gssbuf;
414         gss_name_t gssname;
415         OM_uint32 status;
416         gss_OID_set oidset;
417
418         gssbuf.value = (void *) name;
419         gssbuf.length = strlen(gssbuf.value);
420
421         gss_create_empty_oid_set(&status, &oidset);
422         gss_add_oid_set_member(&status, ctx->oid, &oidset);
423
424         ctx->major = gss_import_name(&ctx->minor, &gssbuf,
425             GSS_C_NT_USER_NAME, &gssname);
426
427         if (GSS_ERROR(ctx->major)) {
428                 ssh_gssapi_error(ctx);
429                 return ctx->major;
430         }
431
432         if (options.gss_password_prompt) {
433                 char prompt[150];
434
435                 if (!gss_password) {
436                         snprintf(prompt, sizeof(prompt), "%.30s's password: ", name);
437                         gss_password = read_passphrase(prompt, 0);
438
439                         atexit(ssh_gssapi_cleanup_password);
440                 }
441
442                 gssbuf.value = gss_password;
443                 gssbuf.length = strlen(gss_password);
444
445                 ctx->major = gss_acquire_cred_with_password(&ctx->minor,
446                                                             gssname,
447                                                             &gssbuf,
448                                                             GSS_C_INDEFINITE,
449                                                             oidset,
450                                                             GSS_C_INITIATE,
451                                                             &ctx->client_creds,
452                                                             NULL,
453                                                             NULL);
454         } else {
455                 ctx->major = gss_acquire_cred(&ctx->minor,
456                                               gssname,
457                                               GSS_C_INDEFINITE,
458                                               oidset,
459                                               GSS_C_INITIATE,
460                                               &ctx->client_creds,
461                                               NULL,
462                                               NULL);
463         }
464
465         gss_release_name(&status, &gssname);
466         gss_release_oid_set(&status, &oidset);
467
468         if (GSS_ERROR(ctx->major))
469                 ssh_gssapi_error(ctx);
470
471         return ctx->major;
472 }
473
474 OM_uint32
475 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
476 {
477         if (ctx == NULL) 
478                 return -1;
479
480         if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
481             GSS_C_QOP_DEFAULT, buffer, hash)))
482                 ssh_gssapi_error(ctx);
483
484         return (ctx->major);
485 }
486
487 /* Priviledged when used by server */
488 OM_uint32
489 ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
490 {
491         if (ctx == NULL)
492                 return -1;
493
494         ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
495             gssbuf, gssmic, NULL);
496
497         return (ctx->major);
498 }
499
500 void
501 ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
502     const char *context)
503 {
504         buffer_init(b);
505         buffer_put_string(b, session_id2, session_id2_len);
506         buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST);
507         buffer_put_cstring(b, user);
508         buffer_put_cstring(b, service);
509         buffer_put_cstring(b, context);
510 }
511
512 int
513 ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, 
514     const char *client)
515 {
516         gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
517         OM_uint32 major, minor;
518         gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
519         Gssctxt *intctx = NULL;
520
521         if (ctx == NULL)
522                 ctx = &intctx;
523
524         /*
525          * RFC 4462 says we MUST NOT do SPNEGO, but we relax that if
526          * the SPNEGO mechanism was explicitly specified by the user.
527          */
528         if (options.gss_mechanism_oid == GSS_C_NO_OID &&
529             oid->length == spnego_oid.length && 
530             (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0))
531                 return 0; /* false */
532
533         ssh_gssapi_build_ctx(ctx);
534         ssh_gssapi_set_oid(*ctx, oid);
535         major = ssh_gssapi_import_name(*ctx, host);
536
537         if (!GSS_ERROR(major) && client)
538                 major = ssh_gssapi_client_identity(*ctx, client);
539
540         if (!GSS_ERROR(major)) {
541                 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 
542                     NULL);
543                 gss_release_buffer(&minor, &token);
544                 if ((*ctx)->context != GSS_C_NO_CONTEXT)
545                         gss_delete_sec_context(&minor, &(*ctx)->context,
546                             GSS_C_NO_BUFFER);
547         }
548
549         if (GSS_ERROR(major) || intctx != NULL) 
550                 ssh_gssapi_delete_ctx(ctx);
551
552         return (!GSS_ERROR(major));
553 }
554
555 int
556 ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
557         static gss_name_t saved_name = GSS_C_NO_NAME;
558         static OM_uint32 saved_lifetime = 0;
559         static gss_OID saved_mech = GSS_C_NO_OID;
560         static gss_name_t name;
561         static OM_uint32 last_call = 0;
562         OM_uint32 lifetime, now, major, minor;
563         int equal;
564         gss_cred_usage_t usage = GSS_C_INITIATE;
565         
566         now = time(NULL);
567
568         if (ctxt) {
569                 debug("Rekey has happened - updating saved versions");
570
571                 if (saved_name != GSS_C_NO_NAME)
572                         gss_release_name(&minor, &saved_name);
573
574                 major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
575                     &saved_name, &saved_lifetime, NULL, NULL);
576
577                 if (!GSS_ERROR(major)) {
578                         saved_mech = ctxt->oid;
579                         saved_lifetime+= now;
580                 } else {
581                         /* Handle the error */
582                 }
583                 return 0;
584         }
585
586         if (now - last_call < 10)
587                 return 0;
588
589         last_call = now;
590
591         if (saved_mech == GSS_C_NO_OID)
592                 return 0;
593         
594         major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, 
595             &name, &lifetime, NULL, NULL);
596         if (major == GSS_S_CREDENTIALS_EXPIRED)
597                 return 0;
598         else if (GSS_ERROR(major))
599                 return 0;
600
601         major = gss_compare_name(&minor, saved_name, name, &equal);
602         gss_release_name(&minor, &name);
603         if (GSS_ERROR(major))
604                 return 0;
605
606         if (equal && (saved_lifetime < lifetime + now - 10))
607                 return 1;
608
609         return 0;
610 }
611 #endif /* GSSAPI */