]> andersk Git - gssapi-openssh.git/blob - openssh/gss-genr.c
- if multiple gssapi mechanisms in ssh_gssapi_mechanisms, separate them
[gssapi-openssh.git] / openssh / gss-genr.c
1 /*
2  * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved. *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions
5  * are met:
6  * 1. Redistributions of source code must retain the above copyright
7  *    notice, this list of conditions and the following disclaimer.
8  * 2. Redistributions in binary form must reproduce the above copyright
9  *    notice, this list of conditions and the following disclaimer in the
10  *    documentation and/or other materials provided with the distribution.
11  *
12  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
13  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
14  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
15  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
16  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
17  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
18  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
19  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
21  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22  */
23
24 #include "includes.h"
25
26 #ifdef GSSAPI
27
28 #include "ssh.h"
29 #include "ssh2.h"
30 #include "xmalloc.h"
31 #include "buffer.h"
32 #include "bufaux.h"
33 #include "packet.h"
34 #include "compat.h"
35 #include <openssl/evp.h>
36 #include "cipher.h"
37 #include "kex.h"
38 #include "log.h"
39 #include "compat.h"
40 #include "monitor_wrap.h"
41
42 #include <netdb.h>
43
44 #include "ssh-gss.h"
45
46 /* Assorted globals for tracking the clients identity once they've
47  * authenticated */
48  
49 gss_buffer_desc gssapi_client_name = {0,NULL}; /* Name of our client */
50 gss_cred_id_t   gssapi_client_creds = GSS_C_NO_CREDENTIAL; /* Their credentials */
51 enum ssh_gss_id gssapi_client_type = GSS_LAST_ENTRY;
52
53 unsigned char ssh1_key_digest[16]; /* used for ssh1 gssapi */
54
55 /* The mechanism name used in the list below is defined in the internet
56  * draft as the Base 64 encoding of the MD5 hash of the ASN.1 DER encoding 
57  * of the underlying GSSAPI mechanism's OID.
58  *
59  * Also from the draft, before considering adding SPNEGO, bear in mind that
60  * "mechanisms ... MUST NOT use SPNEGO as the underlying GSSAPI mechanism"
61  */
62
63 /* These must be in the same order as ssh_gss_id, in ssh-gss.h */
64
65 ssh_gssapi_mech supported_mechs[]= {
66 #ifdef KRB5
67  /* Official OID - 1.2.850.113554.1.2.2 */
68  {"Se3H81ismmOC3OE+FwYCiQ==","Kerberos",
69         {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}},
70 #endif
71 #ifdef GSI
72  /* gssapi_ssleay 1.3.6.1.4.1.3536.1.1 */
73  {"N3+k7/4wGxHyuP8Yxi4RhA==",
74   "GSI",
75   {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"}
76  },
77 #endif /* GSI */
78  {NULL,NULL,{0,0}}
79 };
80
81 char gssprefix[]=KEX_GSS_SHA1;
82
83 /* Return a list of the gss-group1-sha1-x mechanisms supported by this
84  * program.
85  *
86  * We only support the mechanisms that we've indicated in the list above,
87  * but we check that they're supported by the GSSAPI mechanism on the 
88  * machine. We also check, before including them in the list, that
89  * we have the necesary information in order to carry out the key exchange
90  * (that is, that the user has credentials, the server's creds are accessible,
91  * etc)
92  *
93  * The way that this is done is fairly nasty, as we do a lot of work that
94  * is then thrown away. This should possibly be implemented with a cache
95  * that stores the results (in an expanded Gssctxt structure), which are
96  * then used by the first calls if that key exchange mechanism is chosen.
97  */
98  
99 char * 
100 ssh_gssapi_mechanisms(int server,char *host) {
101         gss_OID_set     supported;
102         OM_uint32       maj_status, min_status;
103         Buffer          buf;
104         int             i = 0;
105         int             present;
106         int             mech_count=0;
107         char *          mechs;
108         Gssctxt *       ctx = NULL;     
109
110         if (datafellows & SSH_OLD_GSSAPI) return NULL;
111         
112         gss_indicate_mechs(&min_status, &supported);
113         
114         buffer_init(&buf);      
115
116         do {
117                 if ((maj_status=gss_test_oid_set_member(&min_status,
118                                                         &supported_mechs[i].oid,
119                                                         supported,
120                                                         &present))) {
121                         present=0;
122                 }
123                 if (present) {
124                         if ((server && 
125                              !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx,
126                                                             &supported_mechs[i].oid)))) 
127                             || (!server &&
128                                 !GSS_ERROR(ssh_gssapi_client_ctx(&ctx,
129                                                        &supported_mechs[i].oid,
130                                                        host)))) {
131                                 /* Append gss_group1_sha1_x to our list */
132                                 if (++mech_count > 1) {
133                                     buffer_append(&buf, ",", 1);
134                                 }
135                                 buffer_append(&buf, gssprefix,
136                                               strlen(gssprefix));
137                                 buffer_append(&buf, 
138                                               supported_mechs[i].enc_name,
139                                               strlen(supported_mechs[i].enc_name));
140                                 debug("GSSAPI mechanism %s (%s%s) supported",
141                                       supported_mechs[i].name, gssprefix,
142                                       supported_mechs[i].enc_name);
143                         } else {
144                             debug("no credentials for GSSAPI mechanism %s",
145                                   supported_mechs[i].name);
146                         }
147                 } else {
148                     debug("GSSAPI mechanism %s not supported",
149                           supported_mechs[i].name);
150                 }
151         } while (supported_mechs[++i].name != NULL);
152         
153         buffer_put_char(&buf,'\0');
154         
155         mechs=xmalloc(buffer_len(&buf));
156         buffer_get(&buf,mechs,buffer_len(&buf));
157         buffer_free(&buf);
158         if (strlen(mechs)==0)
159            return(NULL);
160         else
161            return(mechs);
162 }
163
164 void ssh_gssapi_supported_oids(gss_OID_set *oidset) {
165         enum ssh_gss_id i =0;
166         OM_uint32 maj_status,min_status;
167         int present;
168         gss_OID_set supported;
169         
170         gss_create_empty_oid_set(&min_status,oidset);
171         gss_indicate_mechs(&min_status, &supported);
172
173         while (supported_mechs[i].name!=NULL) {
174                 if ((maj_status=gss_test_oid_set_member(&min_status,
175                                                        &supported_mechs[i].oid,
176                                                        supported,
177                                                        &present))) {
178                         present=0;
179                 }
180                 if (present) {
181                         gss_add_oid_set_member(&min_status,
182                                                &supported_mechs[i].oid,
183                                                oidset); 
184                 }
185                 i++;
186         }
187 }       
188
189 /* Set the contexts OID from a data stream */
190 void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) { 
191   if (ctx->oid != GSS_C_NO_OID) {
192         xfree(ctx->oid->elements);
193         xfree(ctx->oid);
194   }
195   ctx->oid=xmalloc(sizeof(gss_OID_desc));
196   ctx->oid->length=len;
197   ctx->oid->elements=xmalloc(len);
198   memcpy(ctx->oid->elements,data,len);
199 }
200
201 /* Set the contexts OID */
202 void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) {  
203   ssh_gssapi_set_oid_data(ctx,oid->elements,oid->length);
204 }
205
206 /* Find out which GSS type (out of the list we define in ssh-gss.h) a
207  * particular connection is using 
208  */
209 enum ssh_gss_id ssh_gssapi_get_ctype(Gssctxt *ctxt) {
210         enum ssh_gss_id i=0;
211         
212         while(supported_mechs[i].name!=NULL) {
213            if (supported_mechs[i].oid.length == ctxt->oid->length &&
214                (memcmp(supported_mechs[i].oid.elements,
215                        ctxt->oid->elements,ctxt->oid->length) == 0))
216                return i;
217            i++;
218         }
219         return(GSS_LAST_ENTRY);
220 }
221
222 /* Set the GSS context's OID to the oid indicated by the given key exchange
223  * name. */
224 gss_OID ssh_gssapi_id_kex(Gssctxt *ctx, char *name) {
225   enum ssh_gss_id i=0;
226   
227   if (strncmp(name, gssprefix, strlen(gssprefix)-1) !=0) {
228      return(NULL);
229   }
230   
231   name+=strlen(gssprefix); /* Move to the start of the MIME string */
232   
233   while (supported_mechs[i].name!=NULL &&
234          strcmp(name,supported_mechs[i].enc_name)!=0) {
235         i++;
236   }
237
238   if (supported_mechs[i].name==NULL)
239      return (NULL);
240
241   if (ctx) ssh_gssapi_set_oid(ctx,&supported_mechs[i].oid);
242
243   debug("using GSSAPI mechanism %s (%s%s)", supported_mechs[i].name,
244         gssprefix, supported_mechs[i].enc_name);
245
246   return &supported_mechs[i].oid;
247 }
248
249
250 /* All this effort to report an error ... */
251 static void
252 ssh_gssapi_error_ex(OM_uint32 major_status,OM_uint32 minor_status,
253                     int send_packet) {
254         OM_uint32 lmaj, lmin;
255         gss_buffer_desc msg = {0,NULL};
256         OM_uint32 ctx;
257         
258         ctx = 0;
259         /* The GSSAPI error */
260         do {
261                 lmaj = gss_display_status(&lmin, major_status,
262                                           GSS_C_GSS_CODE,
263                                           GSS_C_NULL_OID,
264                                           &ctx, &msg);
265                 if (lmaj == GSS_S_COMPLETE) {
266                         debug((char *)msg.value);
267                         if (send_packet) packet_send_debug((char *)msg.value);
268                         (void) gss_release_buffer(&lmin, &msg);
269                 }
270         } while (ctx!=0);          
271
272         /* The mechanism specific error */
273         do {
274                 lmaj = gss_display_status(&lmin, minor_status,
275                                           GSS_C_MECH_CODE,
276                                           GSS_C_NULL_OID,
277                                           &ctx, &msg);
278                 if (lmaj == GSS_S_COMPLETE) {
279                         debug((char *)msg.value);
280                         if (send_packet) packet_send_debug((char *)msg.value);
281                         (void) gss_release_buffer(&lmin, &msg);
282                 }
283         } while (ctx!=0);
284 }
285
286 void
287 ssh_gssapi_error(OM_uint32 major_status,OM_uint32 minor_status) {
288     ssh_gssapi_error_ex(major_status, minor_status, 0);
289 }
290
291 void
292 ssh_gssapi_send_error(OM_uint32 major_status,OM_uint32 minor_status) {
293     ssh_gssapi_error_ex(major_status, minor_status, 1);
294 }
295
296
297
298
299 /* Initialise our GSSAPI context. We use this opaque structure to contain all
300  * of the data which both the client and server need to persist across
301  * {accept,init}_sec_context calls, so that when we do it from the userauth
302  * stuff life is a little easier
303  */
304 void
305 ssh_gssapi_build_ctx(Gssctxt **ctx)
306 {
307         *ctx=xmalloc(sizeof (Gssctxt));
308         (*ctx)->context=GSS_C_NO_CONTEXT;
309         (*ctx)->name=GSS_C_NO_NAME;
310         (*ctx)->oid=GSS_C_NO_OID;
311         (*ctx)->creds=GSS_C_NO_CREDENTIAL;
312         (*ctx)->client=GSS_C_NO_NAME;
313         (*ctx)->client_creds=GSS_C_NO_CREDENTIAL;
314 }
315
316 /* Delete our context, providing it has been built correctly */
317 void
318 ssh_gssapi_delete_ctx(Gssctxt **ctx)
319 {
320         OM_uint32 ms;
321         
322         /* Return if there's no context */
323         if ((*ctx)==NULL)
324                 return;
325                 
326 #if !defined(MECHGLUE) /* mechglue has some memory management issues */
327         if ((*ctx)->context != GSS_C_NO_CONTEXT) 
328                 gss_delete_sec_context(&ms,&(*ctx)->context,GSS_C_NO_BUFFER);
329         if ((*ctx)->name != GSS_C_NO_NAME)
330                 gss_release_name(&ms,&(*ctx)->name);
331         if ((*ctx)->oid != GSS_C_NO_OID) {
332                 xfree((*ctx)->oid->elements);
333                 xfree((*ctx)->oid);
334                 (*ctx)->oid = GSS_C_NO_OID;
335         }
336         if ((*ctx)->creds != GSS_C_NO_CREDENTIAL)
337                 gss_release_cred(&ms,&(*ctx)->creds);
338         if ((*ctx)->client != GSS_C_NO_NAME)
339                 gss_release_name(&ms,&(*ctx)->client);  
340         if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL)
341                 gss_release_cred(&ms,&(*ctx)->client_creds);
342 #endif
343         
344         xfree(*ctx);
345         *ctx=NULL; 
346 }
347
348 /* Wrapper to init_sec_context 
349  * Requires that the context contains:
350  *      oid
351  *      server name (from ssh_gssapi_import_name)
352  */
353 OM_uint32 
354 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
355                     gss_buffer_desc* send_tok, OM_uint32 *flags) 
356 {
357         OM_uint32 maj_status, min_status;
358         int deleg_flag = 0;
359         
360         if (deleg_creds) {
361                 deleg_flag=GSS_C_DELEG_FLAG;
362                 debug("Delegating credentials");
363         }
364                 
365         maj_status=gss_init_sec_context(&min_status,
366                                         GSS_C_NO_CREDENTIAL, /* def. cred */
367                                         &ctx->context,
368                                         ctx->name,
369                                         ctx->oid,
370                                         GSS_C_MUTUAL_FLAG |
371                                         GSS_C_INTEG_FLAG |
372                                         deleg_flag,
373                                         0, /* default lifetime */
374                                         NULL, /* no channel bindings */
375                                         recv_tok,
376                                         NULL,
377                                         send_tok,
378                                         flags,
379                                         NULL);
380         ctx->status=maj_status;
381         if (GSS_ERROR(maj_status)) {
382                 ssh_gssapi_error(maj_status,min_status);
383         }
384         return(maj_status);
385 }
386
387 /* Wrapper arround accept_sec_context
388  * Requires that the context contains:
389  *    oid               
390  *    credentials       (from ssh_gssapi_acquire_cred)
391  */
392 OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx,gss_buffer_desc *recv_tok,
393                                 gss_buffer_desc *send_tok, OM_uint32 *flags) 
394 {
395         OM_uint32 maj_status, min_status;
396         gss_OID mech;
397         
398         maj_status=gss_accept_sec_context(&min_status,
399                                           &ctx->context,
400                                           ctx->creds,
401                                           recv_tok,
402                                           GSS_C_NO_CHANNEL_BINDINGS,
403                                           &ctx->client,
404                                           &mech,
405                                           send_tok,
406                                           flags,
407                                           NULL,
408                                           &ctx->client_creds);
409         if (GSS_ERROR(maj_status)) {
410                 ssh_gssapi_send_error(maj_status,min_status);
411         }
412         
413         if (ctx->client_creds) {
414                 debug("Received some client credentials");
415         } else {
416                 debug("Got no client credentials");
417         }
418
419         /* FIXME: We should check that the me
420          * the one that we asked for (in ctx->oid) */
421
422         ctx->status=maj_status;
423         
424         /* Now, if we're complete and we have the right flags, then
425          * we flag the user as also having been authenticated
426          */
427         
428         if (((flags==NULL) || ((*flags & GSS_C_MUTUAL_FLAG) && 
429                                (*flags & GSS_C_INTEG_FLAG))) &&
430             (maj_status == GSS_S_COMPLETE)) {
431                 if (ssh_gssapi_getclient(ctx,&gssapi_client_type,
432                                          &gssapi_client_name,
433                                          &gssapi_client_creds))
434                         fatal("Couldn't convert client name");
435         }
436
437         return(maj_status);
438 }
439
440 /* Create a service name for the given host */
441 OM_uint32
442 ssh_gssapi_import_name(Gssctxt *ctx, const char *host) {
443         gss_buffer_desc gssbuf = {0,NULL};
444         OM_uint32 maj_status, min_status;
445         struct hostent *hostinfo = NULL;
446         char *xhost, *addr;
447         
448         /* Make a copy of the host name, in case it was returned by a
449          * previous call to gethostbyname(). */ 
450         xhost = xstrdup(host);
451
452         /* Make sure we have the FQDN. Some GSSAPI implementations don't do
453          * this for us themselves */
454         hostinfo = gethostbyname(xhost);
455         
456         /* Use local hostname when coming in on loopback interface because
457            we won't have 'localhost' credentials. */
458         if (hostinfo &&
459             hostinfo->h_addrtype == AF_INET) {
460             struct in_addr addr;
461             addr = *(struct in_addr *)(hostinfo->h_addr);
462             if (ntohl(addr.s_addr) == INADDR_LOOPBACK) {
463                 char buf[4096];
464                 if (gethostname(buf, 4096) == 0) {
465                     hostinfo = gethostbyname(buf);
466                 }
467             }
468         }
469
470         /* Go to the resolver to get the official hostname for our target.
471            WARNING: This makes us vulnerable to DNS spoofing. */
472         if ((hostinfo == NULL) || (hostinfo->h_name == NULL)) {
473                 debug("Unable to get FQDN for \"%s\"", xhost);
474         } else {
475                 addr = xmalloc(hostinfo->h_length);
476                 memcpy(addr, hostinfo->h_addr, hostinfo->h_length);
477                 hostinfo = gethostbyaddr(addr, hostinfo->h_length,
478                                          hostinfo->h_addrtype);
479                 xfree(addr);
480                 if ((hostinfo == NULL) || (hostinfo->h_name == NULL)) {
481                     debug("Unable to get FQDN for \"%s\"", xhost);
482                 } else {
483                     xfree(xhost);
484                     xhost = xstrdup(hostinfo->h_name);
485                 }
486         }
487                 
488         gssbuf.length = sizeof("host@")+strlen(xhost);
489
490         gssbuf.value = xmalloc(gssbuf.length);
491         if (gssbuf.value == NULL) {
492                 xfree(xhost);
493                 return(-1);
494         }
495         snprintf(gssbuf.value,gssbuf.length,"host@%s",xhost);
496         if ((maj_status=gss_import_name(&min_status,
497                                         &gssbuf,
498                                         GSS_C_NT_HOSTBASED_SERVICE,
499                                         &ctx->name))) {
500                 ssh_gssapi_error(maj_status,min_status);
501         }
502         
503         xfree(xhost);
504         xfree(gssbuf.value);
505         return(maj_status);
506 }
507
508 /* Acquire credentials for a server running on the current host.
509  * Requires that the context structure contains a valid OID
510  */
511  
512 /* Returns a GSSAPI error code */
513 OM_uint32
514 ssh_gssapi_acquire_cred(Gssctxt *ctx) {
515         OM_uint32 maj_status, min_status;
516         char lname[MAXHOSTNAMELEN];
517         gss_OID_set oidset;
518         
519         gss_create_empty_oid_set(&min_status,&oidset);
520         gss_add_oid_set_member(&min_status,ctx->oid,&oidset);
521         
522         if (gethostname(lname, MAXHOSTNAMELEN)) {
523                 return(-1);
524         }
525
526         if ((maj_status=ssh_gssapi_import_name(ctx,lname))) {
527                 return(maj_status);
528         }
529         if ((maj_status=gss_acquire_cred(&min_status,
530                                     ctx->name,
531                                     0,
532                                     oidset,
533                                     GSS_C_ACCEPT,
534                                     &ctx->creds,
535                                     NULL,
536                                     NULL))) {
537                 ssh_gssapi_error(maj_status,min_status);
538         }
539                                 
540         gss_release_oid_set(&min_status, &oidset);
541         return(maj_status);
542 }
543
544 /* Extract the client details from a given context. This can only reliably
545  * be called once for a context */
546
547 OM_uint32 
548 ssh_gssapi_getclient(Gssctxt *ctx, enum ssh_gss_id *type,
549                      gss_buffer_desc *name, gss_cred_id_t *creds) {
550
551         OM_uint32 maj_status,min_status;
552         
553         *type=ssh_gssapi_get_ctype(ctx);
554         if ((maj_status=gss_display_name(&min_status,ctx->client,name,NULL))) {
555                 ssh_gssapi_error(maj_status,min_status);
556         }
557         
558         /* This is icky. There appears to be no way to copy this structure,
559          * rather than the pointer to it, so we simply copy the pointer and
560          * mark the originator as empty so we don't destroy it. 
561          */
562         *creds=ctx->client_creds;
563         ctx->client_creds=GSS_C_NO_CREDENTIAL;
564         return(maj_status);
565 }
566
567 OM_uint32
568 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) {
569         OM_uint32 maj_status,min_status;
570         
571         /* ssh1 needs to exchange the hash of the keys */
572         /* will us this hash to return it */
573         if (!compat20) {
574                 if ((maj_status=gss_wrap(&min_status,ctx->context,
575                                         0,
576                                         GSS_C_QOP_DEFAULT,
577                                         buffer,
578                                         NULL,
579                                         hash)))
580                         ssh_gssapi_error(maj_status,min_status);
581         }
582         else
583
584         if ((maj_status=gss_get_mic(&min_status,ctx->context,
585                                     GSS_C_QOP_DEFAULT, buffer, hash))) {
586                 ssh_gssapi_error(maj_status,min_status);
587         }
588         
589         return(maj_status);
590 }
591
592 OM_uint32
593 ssh_gssapi_server_ctx(Gssctxt **ctx,gss_OID oid) {
594         if (*ctx) ssh_gssapi_delete_ctx(ctx);
595         ssh_gssapi_build_ctx(ctx);
596         ssh_gssapi_set_oid(*ctx,oid);
597         return(ssh_gssapi_acquire_cred(*ctx));
598 }
599
600 OM_uint32 
601 ssh_gssapi_client_ctx(Gssctxt **ctx,gss_OID oid, char *host) {
602         gss_buffer_desc token = {0,NULL};
603         OM_uint32 major,minor;
604         
605         if (*ctx) ssh_gssapi_delete_ctx(ctx);
606         ssh_gssapi_build_ctx(ctx);
607         ssh_gssapi_set_oid(*ctx,oid);
608         ssh_gssapi_import_name(*ctx,host);
609         major=ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, NULL);
610         gss_release_buffer(&minor,&token);
611         return(major);
612 }
613                                                                                         
614 #endif /* GSSAPI */
This page took 0.085213 seconds and 5 git commands to generate.