source: trunk/third/openssh/gss-genr.c @ 18763

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