allow prompting for GSS creds
[openssh.git] / gss-serv.c
1 /* $OpenBSD: gss-serv.c,v 1.23 2011/08/01 19:18:15 markus 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 "openbsd-compat/sys-queue.h"
39 #include "xmalloc.h"
40 #include "buffer.h"
41 #include "key.h"
42 #include "hostfile.h"
43 #include "auth.h"
44 #include "log.h"
45 #include "channels.h"
46 #include "session.h"
47 #include "misc.h"
48 #include "servconf.h"
49 #include "uidswap.h"
50
51 #include "ssh-gss.h"
52 #include "monitor_wrap.h"
53
54 extern ServerOptions options;
55
56 static ssh_gssapi_client gssapi_client =
57   { {0, NULL}, GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
58     GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, GSS_C_NO_NAME, NULL, {NULL, NULL, NULL}, 0, 0};
59
60 ssh_gssapi_mech gssapi_null_mech =
61     { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
62 /* Generic GSS-API support*/
63 #ifdef HAVE_GSS_USEROK
64 static int ssh_gssapi_generic_userok(
65                                  ssh_gssapi_client *client, char *user) {
66   if (gss_userok(client->ctx_name, user)) {
67     debug("userok succeded for %s", user);
68     return 1;
69   } else {
70     debug("userok failed for %s", user);
71     return 0;
72   }
73 }
74 #endif
75
76 static int
77 ssh_gssapi_generic_localname(ssh_gssapi_client *client,
78                           char **localname) {
79   #ifdef HAVE_GSS_LOCALNAME
80   gss_buffer_desc lbuffer;
81   OM_uint32 major, minor;
82   *localname = NULL;
83   major = gss_localname(&minor, client->ctx_name, NULL, &lbuffer);
84   if (GSS_ERROR(major))
85     return 0;
86   if (lbuffer.value == NULL)
87     return 0;
88   *localname = xmalloc(lbuffer.length+1);
89   if (*localname) {
90     memcpy(*localname, lbuffer.value, lbuffer.length);
91     (*localname)[lbuffer.length] = '\0';
92   }
93   gss_release_buffer(&minor, &lbuffer);
94   if (*localname)
95     return 1;
96   return 0;
97   #else
98   debug("No generic gss_localname");
99   return 0;
100   #endif
101       }
102
103 #ifdef HAVE_GSS_USEROK
104 static ssh_gssapi_mech ssh_gssapi_generic_mech = {
105   NULL, NULL,
106   {0, NULL},
107   NULL, /* dochild */
108   ssh_gssapi_generic_userok,
109   ssh_gssapi_generic_localname,
110   NULL,
111   NULL};
112 static const ssh_gssapi_mech *ssh_gssapi_generic_mech_ptr = &ssh_gssapi_generic_mech;
113 #else /*HAVE_GSS_USEROK*/
114 static const ssh_gssapi_mech *ssh_gssapi_generic_mech_ptr = NULL;
115 #endif
116
117 #ifdef KRB5
118 extern ssh_gssapi_mech gssapi_kerberos_mech;
119 #endif
120
121
122 ssh_gssapi_mech* supported_mechs[]= {
123 #ifdef KRB5
124         &gssapi_kerberos_mech,
125 #endif
126         &gssapi_null_mech,
127 };
128
129
130 /*
131  * Acquire credentials for a server running on the current host.
132  * Requires that the context structure contains a valid OID
133  */
134
135 /* Returns a GSSAPI error code */
136 /* Privileged (called from ssh_gssapi_server_ctx) */
137 static OM_uint32
138 ssh_gssapi_acquire_cred(Gssctxt *ctx)
139 {
140         OM_uint32 status;
141         char lname[MAXHOSTNAMELEN];
142         gss_OID_set oidset;
143
144         if (options.gss_strict_acceptor) {
145                 gss_create_empty_oid_set(&status, &oidset);
146                 gss_add_oid_set_member(&status, ctx->oid, &oidset);
147
148                 if (gethostname(lname, MAXHOSTNAMELEN)) {
149                         gss_release_oid_set(&status, &oidset);
150                         return (-1);
151                 }
152
153                 if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
154                         gss_release_oid_set(&status, &oidset);
155                         return (ctx->major);
156                 }
157
158                 if ((ctx->major = gss_acquire_cred(&ctx->minor,
159                     ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, 
160                     NULL, NULL)))
161                         ssh_gssapi_error(ctx);
162
163                 gss_release_oid_set(&status, &oidset);
164                 return (ctx->major);
165         } else {
166                 ctx->name = GSS_C_NO_NAME;
167                 ctx->creds = GSS_C_NO_CREDENTIAL;
168         }
169         return GSS_S_COMPLETE;
170 }
171
172 /* Privileged */
173 OM_uint32
174 ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
175 {
176         if (*ctx)
177                 ssh_gssapi_delete_ctx(ctx);
178         ssh_gssapi_build_ctx(ctx);
179         ssh_gssapi_set_oid(*ctx, oid);
180         return (ssh_gssapi_acquire_cred(*ctx));
181 }
182
183 /* Unprivileged */
184 char *
185 ssh_gssapi_server_mechanisms() {
186         gss_OID_set     supported;
187
188         ssh_gssapi_supported_oids(&supported);
189         return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
190             NULL, NULL));
191 }
192
193 /* Unprivileged */
194 int
195 ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
196     const char *dummy) {
197         Gssctxt *ctx = NULL;
198         int res;
199  
200         res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
201         ssh_gssapi_delete_ctx(&ctx);
202
203         return (res);
204 }
205
206 /* Unprivileged */
207 void
208 ssh_gssapi_supported_oids(gss_OID_set *oidset)
209 {
210         int i = 0;
211         OM_uint32 min_status;
212         int present;
213         gss_OID_set supported;
214         /* If we have a generic mechanism all OIDs supported */
215         if (ssh_gssapi_generic_mech_ptr) {
216           gss_OID_desc except_oids[3];
217           gss_OID_set_desc except_attrs;
218           except_oids[0] = *GSS_C_MA_MECH_NEGO;
219           except_oids[1] = *GSS_C_MA_NOT_MECH;
220           except_oids[2] = *GSS_C_MA_DEPRECATED;
221
222           except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]);
223           except_attrs.elements = except_oids;
224
225           if (!GSS_ERROR(gss_indicate_mechs_by_attrs(&min_status,
226                                                      GSS_C_NO_OID_SET,
227                                                      &except_attrs,
228                                                      GSS_C_NO_OID_SET,
229                                                      oidset)))
230             return;
231         }
232
233         gss_create_empty_oid_set(&min_status, oidset);
234
235         if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
236                 return;
237
238         while (supported_mechs[i]->name != NULL) {
239                 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
240                     &supported_mechs[i]->oid, supported, &present)))
241                         present = 0;
242                 if (present)
243                         gss_add_oid_set_member(&min_status,
244                             &supported_mechs[i]->oid, oidset);
245                 i++;
246         }
247
248         gss_release_oid_set(&min_status, &supported);
249 }
250
251
252 /* Wrapper around accept_sec_context
253  * Requires that the context contains:
254  *    oid
255  *    credentials       (from ssh_gssapi_acquire_cred)
256  */
257 /* Privileged */
258 OM_uint32
259 ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok,
260     gss_buffer_desc *send_tok, OM_uint32 *flags)
261 {
262         OM_uint32 status;
263         gss_OID mech;
264
265         ctx->major = gss_accept_sec_context(&ctx->minor,
266             &ctx->context, ctx->creds, recv_tok,
267             GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech,
268             send_tok, flags, NULL, &ctx->client_creds);
269
270         if (GSS_ERROR(ctx->major))
271                 ssh_gssapi_error(ctx);
272
273         if (ctx->client_creds)
274                 debug("Received some client credentials");
275         else
276                 debug("Got no client credentials");
277
278         status = ctx->major;
279
280         /* Now, if we're complete and we have the right flags, then
281          * we flag the user as also having been authenticated
282          */
283
284         if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
285             (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
286                 if (ssh_gssapi_getclient(ctx, &gssapi_client))
287                         fatal("Couldn't convert client name");
288         }
289
290         return (status);
291 }
292
293 /*
294  * This parses an exported name, extracting the mechanism specific portion
295  * to use for ACL checking. It verifies that the name belongs the mechanism
296  * originally selected.
297  */
298 static OM_uint32
299 ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
300 {
301         u_char *tok;
302         OM_uint32 offset;
303         OM_uint32 oidl;
304
305         tok = ename->value;
306
307         /*
308          * Check that ename is long enough for all of the fixed length
309          * header, and that the initial ID bytes are correct
310          */
311
312         if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0)
313                 return GSS_S_FAILURE;
314
315         /*
316          * Extract the OID, and check it. Here GSSAPI breaks with tradition
317          * and does use the OID type and length bytes. To confuse things
318          * there are two lengths - the first including these, and the
319          * second without.
320          */
321
322         oidl = get_u16(tok+2); /* length including next two bytes */
323         oidl = oidl-2; /* turn it into the _real_ length of the variable OID */
324
325         /*
326          * Check the BER encoding for correct type and length, that the
327          * string is long enough and that the OID matches that in our context
328          */
329         if (tok[4] != 0x06 || tok[5] != oidl ||
330             ename->length < oidl+6 ||
331             !ssh_gssapi_check_oid(ctx, tok+6, oidl))
332                 return GSS_S_FAILURE;
333
334         offset = oidl+6;
335
336         if (ename->length < offset+4)
337                 return GSS_S_FAILURE;
338
339         name->length = get_u32(tok+offset);
340         offset += 4;
341
342         if (UINT_MAX - offset < name->length)
343                 return GSS_S_FAILURE;
344         if (ename->length < offset+name->length)
345                 return GSS_S_FAILURE;
346
347         name->value = xmalloc(name->length+1);
348         memcpy(name->value, tok+offset, name->length);
349         ((char *)name->value)[name->length] = 0;
350
351         return GSS_S_COMPLETE;
352 }
353
354 /* Extract the client details from a given context. This can only reliably
355  * be called once for a context */
356
357 /* Privileged (called from accept_secure_ctx) */
358 OM_uint32
359 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
360 {
361         int i = 0;
362         int equal = 0;
363         gss_name_t new_name = GSS_C_NO_NAME;
364         gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
365
366         if (options.gss_store_rekey && client->used && ctx->client_creds) {
367                 if (client->oid.length != ctx->oid->length ||
368                     (memcmp(client->oid.elements,
369                      ctx->oid->elements, ctx->oid->length) !=0)) {
370                         debug("Rekeyed credentials have different mechanism");
371                         return GSS_S_COMPLETE;
372                 }
373
374                 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, 
375                     ctx->client_creds, ctx->oid, &new_name, 
376                     NULL, NULL, NULL))) {
377                         ssh_gssapi_error(ctx);
378                         return (ctx->major);
379                 }
380
381                 ctx->major = gss_compare_name(&ctx->minor, client->cred_name, 
382                     new_name, &equal);
383
384                 if (GSS_ERROR(ctx->major)) {
385                         ssh_gssapi_error(ctx);
386                         return (ctx->major);
387                 }
388  
389                 if (!equal) {
390                         debug("Rekeyed credentials have different name");
391                         return GSS_S_COMPLETE;
392                 }
393
394                 debug("Marking rekeyed credentials for export");
395
396                 gss_release_name(&ctx->minor, &client->cred_name);
397                 gss_release_cred(&ctx->minor, &client->creds);
398                 client->cred_name = new_name;
399                 client->creds = ctx->client_creds;
400                 ctx->client_creds = GSS_C_NO_CREDENTIAL;
401                 client->updated = 1;
402                 return GSS_S_COMPLETE;
403         }
404
405         client->mech = NULL;
406
407         while (supported_mechs[i]->name != NULL) {
408                 if (supported_mechs[i]->oid.length == ctx->oid->length &&
409                     (memcmp(supported_mechs[i]->oid.elements,
410                     ctx->oid->elements, ctx->oid->length) == 0))
411                         client->mech = supported_mechs[i];
412                 i++;
413         }
414
415         if (client->oid.elements == NULL)
416           client->oid = *ctx->oid;
417         if (client->mech == NULL) {
418           if (ssh_gssapi_generic_mech_ptr)
419             client->mech = (ssh_gssapi_mech *) ssh_gssapi_generic_mech_ptr;
420           else return GSS_S_FAILURE;
421         }
422
423         if (ctx->client_creds &&
424             (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
425              ctx->client_creds, ctx->oid, &client->cred_name, NULL, NULL, NULL))) {
426                 ssh_gssapi_error(ctx);
427                 return (ctx->major);
428         }
429
430         if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
431             &client->displayname, NULL))) {
432                 ssh_gssapi_error(ctx);
433                 return (ctx->major);
434         }
435
436         if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
437             &ename))) {
438                 ssh_gssapi_error(ctx);
439                 return (ctx->major);
440         }
441
442         if ((client->mech->oid.elements != NULL) &&
443             (ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
444             &client->exportedname))) {
445                 return (ctx->major);
446         }
447
448         if ((ctx->major = gss_duplicate_name(&ctx->minor, ctx->client,
449                                              &client->ctx_name)))
450           return ctx->major;
451             
452         gss_release_buffer(&ctx->minor, &ename);
453
454         /* We can't copy this structure, so we just move the pointer to it */
455         client->creds = ctx->client_creds;
456         ctx->client_creds = GSS_C_NO_CREDENTIAL;
457         return (ctx->major);
458 }
459
460 /* As user - called on fatal/exit */
461 void
462 ssh_gssapi_cleanup_creds(void)
463 {
464         if (gssapi_client.store.filename != NULL) {
465                 /* Unlink probably isn't sufficient */
466                 debug("removing gssapi cred file\"%s\"",
467                     gssapi_client.store.filename);
468                 unlink(gssapi_client.store.filename);
469         }
470 }
471
472 /* As user */
473 void
474 ssh_gssapi_storecreds(void)
475 {
476         if (gssapi_client.mech && gssapi_client.mech->storecreds) {
477                 (*gssapi_client.mech->storecreds)(&gssapi_client);
478         } else
479                 debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
480 }
481
482 /* This allows GSSAPI methods to do things to the childs environment based
483  * on the passed authentication process and credentials.
484  */
485 /* As user */
486 void
487 ssh_gssapi_do_child(char ***envp, u_int *envsizep)
488 {
489
490         if (gssapi_client.store.envvar != NULL &&
491             gssapi_client.store.envval != NULL) {
492                 debug("Setting %s to %s", gssapi_client.store.envvar,
493                     gssapi_client.store.envval);
494                 child_set_env(envp, envsizep, gssapi_client.store.envvar,
495                     gssapi_client.store.envval);
496         }
497 }
498
499 /* Privileged */
500 int
501 ssh_gssapi_userok(char *user, struct passwd *pw)
502 {
503         OM_uint32 lmin;
504
505         if (gssapi_client.mech && gssapi_client.mech->userok)
506                 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
507                         gssapi_client.used = 1;
508                         gssapi_client.store.owner = pw;
509                         return 1;
510                 } else {
511                         /* Destroy delegated credentials if userok fails */
512                         gss_release_buffer(&lmin, &gssapi_client.displayname);
513                         gss_release_buffer(&lmin, &gssapi_client.exportedname);
514                         gss_release_cred(&lmin, &gssapi_client.creds);
515                         gss_release_name(&lmin, &gssapi_client.ctx_name);
516                         memset(&gssapi_client, 0, sizeof(ssh_gssapi_client));
517                         return 0;
518                 }
519         else
520                 debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
521         return (0);
522 }
523
524 /* These bits are only used for rekeying. The unpriviledged child is running 
525  * as the user, the monitor is root.
526  *
527  * In the child, we want to :
528  *    *) Ask the monitor to store our credentials into the store we specify
529  *    *) If it succeeds, maybe do a PAM update
530  */
531
532 /* Stuff for PAM */
533
534 #ifdef USE_PAM
535 static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, 
536     struct pam_response **resp, void *data)
537 {
538         return (PAM_CONV_ERR);
539 }
540 #endif
541
542 void
543 ssh_gssapi_rekey_creds() {
544         int ok;
545         int ret;
546 #ifdef USE_PAM
547         pam_handle_t *pamh = NULL;
548         struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
549         char *envstr;
550 #endif
551
552         if (gssapi_client.store.filename == NULL && 
553             gssapi_client.store.envval == NULL &&
554             gssapi_client.store.envvar == NULL)
555                 return;
556  
557         ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
558
559         if (!ok)
560                 return;
561
562         debug("Rekeyed credentials stored successfully");
563
564         /* Actually managing to play with the ssh pam stack from here will
565          * be next to impossible. In any case, we may want different options
566          * for rekeying. So, use our own :)
567          */
568 #ifdef USE_PAM  
569         if (!use_privsep) {
570                 debug("Not even going to try and do PAM with privsep disabled");
571                 return;
572         }
573
574         ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
575             &pamconv, &pamh);
576         if (ret)
577                 return;
578
579         xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, 
580             gssapi_client.store.envval);
581
582         ret = pam_putenv(pamh, envstr);
583         if (!ret)
584                 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
585         pam_end(pamh, PAM_SUCCESS);
586 #endif
587 }
588
589 int 
590 ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
591         int ok = 0;
592
593         /* Check we've got credentials to store */
594         if (!gssapi_client.updated)
595                 return 0;
596
597         gssapi_client.updated = 0;
598
599         temporarily_use_uid(gssapi_client.store.owner);
600         if (gssapi_client.mech && gssapi_client.mech->updatecreds)
601                 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
602         else
603                 debug("No update function for this mechanism");
604
605         restore_uid();
606
607         return ok;
608 }
609
610 #endif