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