update version
[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 (ctx->major == GSS_S_COMPLETE) {
285                 if (options.gss_require_mic &&
286                     ((flags == NULL) || !(*flags & GSS_C_INTEG_FLAG))) {
287                         debug("GSSAPIRequireMIC true and integrity protection not supported so gssapi-with-mic fails.");
288                 } else if (ssh_gssapi_getclient(ctx, &gssapi_client)) {
289                         fatal("Couldn't convert client name");
290                 }
291         }
292
293         return (status);
294 }
295
296 /*
297  * This parses an exported name, extracting the mechanism specific portion
298  * to use for ACL checking. It verifies that the name belongs the mechanism
299  * originally selected.
300  */
301 static OM_uint32
302 ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
303 {
304         u_char *tok;
305         OM_uint32 offset;
306         OM_uint32 oidl;
307
308         tok = ename->value;
309
310         /*
311          * Check that ename is long enough for all of the fixed length
312          * header, and that the initial ID bytes are correct
313          */
314
315         if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0)
316                 return GSS_S_FAILURE;
317
318         /*
319          * Extract the OID, and check it. Here GSSAPI breaks with tradition
320          * and does use the OID type and length bytes. To confuse things
321          * there are two lengths - the first including these, and the
322          * second without.
323          */
324
325         oidl = get_u16(tok+2); /* length including next two bytes */
326         oidl = oidl-2; /* turn it into the _real_ length of the variable OID */
327
328         /*
329          * Check the BER encoding for correct type and length, that the
330          * string is long enough and that the OID matches that in our context
331          */
332         if (tok[4] != 0x06 || tok[5] != oidl ||
333             ename->length < oidl+6 ||
334             !ssh_gssapi_check_oid(ctx, tok+6, oidl))
335                 return GSS_S_FAILURE;
336
337         offset = oidl+6;
338
339         if (ename->length < offset+4)
340                 return GSS_S_FAILURE;
341
342         name->length = get_u32(tok+offset);
343         offset += 4;
344
345         if (UINT_MAX - offset < name->length)
346                 return GSS_S_FAILURE;
347         if (ename->length < offset+name->length)
348                 return GSS_S_FAILURE;
349
350         name->value = xmalloc(name->length+1);
351         memcpy(name->value, tok+offset, name->length);
352         ((char *)name->value)[name->length] = 0;
353
354         return GSS_S_COMPLETE;
355 }
356
357 /* Extract the client details from a given context. This can only reliably
358  * be called once for a context */
359
360 /* Privileged (called from accept_secure_ctx) */
361 OM_uint32
362 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
363 {
364         int i = 0;
365         int equal = 0;
366         gss_name_t new_name = GSS_C_NO_NAME;
367         gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
368
369         if (options.gss_store_rekey && client->used && ctx->client_creds) {
370                 if (client->oid.length != ctx->oid->length ||
371                     (memcmp(client->oid.elements,
372                      ctx->oid->elements, ctx->oid->length) !=0)) {
373                         debug("Rekeyed credentials have different mechanism");
374                         return GSS_S_COMPLETE;
375                 }
376
377                 if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, 
378                     ctx->client_creds, ctx->oid, &new_name, 
379                     NULL, NULL, NULL))) {
380                         ssh_gssapi_error(ctx);
381                         return (ctx->major);
382                 }
383
384                 ctx->major = gss_compare_name(&ctx->minor, client->cred_name, 
385                     new_name, &equal);
386
387                 if (GSS_ERROR(ctx->major)) {
388                         ssh_gssapi_error(ctx);
389                         return (ctx->major);
390                 }
391  
392                 if (!equal) {
393                         debug("Rekeyed credentials have different name");
394                         return GSS_S_COMPLETE;
395                 }
396
397                 debug("Marking rekeyed credentials for export");
398
399                 gss_release_name(&ctx->minor, &client->cred_name);
400                 gss_release_cred(&ctx->minor, &client->creds);
401                 client->cred_name = new_name;
402                 client->creds = ctx->client_creds;
403                 ctx->client_creds = GSS_C_NO_CREDENTIAL;
404                 client->updated = 1;
405                 return GSS_S_COMPLETE;
406         }
407
408         client->mech = NULL;
409
410         while (supported_mechs[i]->name != NULL) {
411                 if (supported_mechs[i]->oid.length == ctx->oid->length &&
412                     (memcmp(supported_mechs[i]->oid.elements,
413                     ctx->oid->elements, ctx->oid->length) == 0))
414                         client->mech = supported_mechs[i];
415                 i++;
416         }
417
418         if (client->oid.elements == NULL)
419           client->oid = *ctx->oid;
420         if (client->mech == NULL) {
421           if (ssh_gssapi_generic_mech_ptr)
422             client->mech = (ssh_gssapi_mech *) ssh_gssapi_generic_mech_ptr;
423           else return GSS_S_FAILURE;
424         }
425
426         if (ctx->client_creds &&
427             (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
428              ctx->client_creds, ctx->oid, &client->cred_name, NULL, NULL, NULL))) {
429                 ssh_gssapi_error(ctx);
430                 return (ctx->major);
431         }
432
433         if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
434             &client->displayname, NULL))) {
435                 ssh_gssapi_error(ctx);
436                 return (ctx->major);
437         }
438
439         if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
440             &ename))) {
441                 ssh_gssapi_error(ctx);
442                 return (ctx->major);
443         }
444
445         if ((client->mech->oid.elements != NULL) &&
446             (ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
447             &client->exportedname))) {
448                 return (ctx->major);
449         }
450
451         if ((ctx->major = gss_duplicate_name(&ctx->minor, ctx->client,
452                                              &client->ctx_name)))
453           return ctx->major;
454             
455         gss_release_buffer(&ctx->minor, &ename);
456
457         /* We can't copy this structure, so we just move the pointer to it */
458         client->creds = ctx->client_creds;
459         ctx->client_creds = GSS_C_NO_CREDENTIAL;
460         return (ctx->major);
461 }
462
463 /* As user - called on fatal/exit */
464 void
465 ssh_gssapi_cleanup_creds(void)
466 {
467         if (gssapi_client.store.filename != NULL) {
468                 /* Unlink probably isn't sufficient */
469                 debug("removing gssapi cred file\"%s\"",
470                     gssapi_client.store.filename);
471                 unlink(gssapi_client.store.filename);
472         }
473 }
474
475 /* As user */
476 void
477 ssh_gssapi_storecreds(void)
478 {
479         if (gssapi_client.mech && gssapi_client.mech->storecreds) {
480                 (*gssapi_client.mech->storecreds)(&gssapi_client);
481         } else
482                 debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
483 }
484
485 /* This allows GSSAPI methods to do things to the childs environment based
486  * on the passed authentication process and credentials.
487  */
488 /* As user */
489 void
490 ssh_gssapi_do_child(char ***envp, u_int *envsizep)
491 {
492
493         if (gssapi_client.store.envvar != NULL &&
494             gssapi_client.store.envval != NULL) {
495                 debug("Setting %s to %s", gssapi_client.store.envvar,
496                     gssapi_client.store.envval);
497                 child_set_env(envp, envsizep, gssapi_client.store.envvar,
498                     gssapi_client.store.envval);
499         }
500 }
501
502 /* Privileged */
503 int
504 ssh_gssapi_userok(char *user, struct passwd *pw)
505 {
506         OM_uint32 lmin;
507
508         if (gssapi_client.mech && gssapi_client.mech->userok)
509                 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
510                         gssapi_client.used = 1;
511                         gssapi_client.store.owner = pw;
512                         return 1;
513                 } else {
514                         /* Destroy delegated credentials if userok fails */
515                         gss_release_buffer(&lmin, &gssapi_client.displayname);
516                         gss_release_buffer(&lmin, &gssapi_client.exportedname);
517                         gss_release_cred(&lmin, &gssapi_client.creds);
518                         gss_release_name(&lmin, &gssapi_client.ctx_name);
519                         memset(&gssapi_client, 0, sizeof(ssh_gssapi_client));
520                         return 0;
521                 }
522         else
523                 debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
524         return (0);
525 }
526
527
528 /* Privileged */
529 int
530 ssh_gssapi_localname(char **user)
531 {
532         *user = NULL;
533         if (gssapi_client.mech && gssapi_client.mech->localname) {
534                 return((*gssapi_client.mech->localname)(&gssapi_client,user));
535         } else {
536                 debug("Unknown client authentication type");
537         }
538         return(0);
539 }
540
541 /* These bits are only used for rekeying. The unpriviledged child is running 
542  * as the user, the monitor is root.
543  *
544  * In the child, we want to :
545  *    *) Ask the monitor to store our credentials into the store we specify
546  *    *) If it succeeds, maybe do a PAM update
547  */
548
549 /* Stuff for PAM */
550
551 #ifdef USE_PAM
552 static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, 
553     struct pam_response **resp, void *data)
554 {
555         return (PAM_CONV_ERR);
556 }
557 #endif
558
559 void
560 ssh_gssapi_rekey_creds() {
561         int ok;
562         int ret;
563 #ifdef USE_PAM
564         pam_handle_t *pamh = NULL;
565         struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
566         char *envstr;
567 #endif
568
569         if (gssapi_client.store.filename == NULL && 
570             gssapi_client.store.envval == NULL &&
571             gssapi_client.store.envvar == NULL)
572                 return;
573  
574         ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
575
576         if (!ok)
577                 return;
578
579         debug("Rekeyed credentials stored successfully");
580
581         /* Actually managing to play with the ssh pam stack from here will
582          * be next to impossible. In any case, we may want different options
583          * for rekeying. So, use our own :)
584          */
585 #ifdef USE_PAM  
586         if (!use_privsep) {
587                 debug("Not even going to try and do PAM with privsep disabled");
588                 return;
589         }
590
591         ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
592             &pamconv, &pamh);
593         if (ret)
594                 return;
595
596         xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, 
597             gssapi_client.store.envval);
598
599         ret = pam_putenv(pamh, envstr);
600         if (!ret)
601                 pam_setcred(pamh, PAM_REINITIALIZE_CRED);
602         pam_end(pamh, PAM_SUCCESS);
603 #endif
604 }
605
606 int 
607 ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
608         int ok = 0;
609
610         /* Check we've got credentials to store */
611         if (!gssapi_client.updated)
612                 return 0;
613
614         gssapi_client.updated = 0;
615
616         temporarily_use_uid(gssapi_client.store.owner);
617         if (gssapi_client.mech && gssapi_client.mech->updatecreds)
618                 ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
619         else
620                 debug("No update function for this mechanism");
621
622         restore_uid();
623
624         return ok;
625 }
626
627 #endif