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

Revision 22574, 12.4 KB checked in by ghudson, 18 years ago (diff)
Merge with OpenSSH 4.2p1. Merge work was done in the svn repository in /afs/dev.mit.edu/project/openssh, and was based on patches from Simon Wilkinson and the krb5 team to add GSSAPI key exchange support and compatibility with OpenSSH 3.5.
Line 
1/*      $OpenBSD: gss-genr.c,v 1.4 2005/07/17 07:17:55 djm Exp $        */
2
3/*
4 * Copyright (c) 2001-2003 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 "xmalloc.h"
32#include "bufaux.h"
33#include "compat.h"
34#include "log.h"
35#include "monitor_wrap.h"
36#include "ssh2.h"
37#include <openssl/evp.h>
38
39#include "ssh-gss.h"
40
41extern u_char *session_id2;
42extern u_int session_id2_len;
43
44typedef struct {
45        char *encoded;
46        gss_OID oid;
47} ssh_gss_kex_mapping;
48
49/*
50 * XXX - It would be nice to find a more elegant way of handling the
51 * XXX   passing of the key exchange context to the userauth routines
52 */
53
54Gssctxt *gss_kex_context = NULL;
55
56static ssh_gss_kex_mapping *gss_enc2oid = NULL;
57
58/*
59 * Return a list of the gss-group1-sha1 mechanisms supported by this program
60 *
61 * We test mechanisms to ensure that we can use them, to avoid starting
62 * a key exchange with a bad mechanism
63 */
64
65
66char *
67ssh_gssapi_client_mechanisms(const char *host) {
68        gss_OID_set gss_supported;
69        OM_uint32 min_status;
70
71        gss_indicate_mechs(&min_status, &gss_supported);
72
73        return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
74            (void *)host));
75}
76
77char *
78ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
79    void *data) {
80        Buffer buf;
81        int i, oidpos, enclen;
82        char *mechs, *encoded;
83        char digest[EVP_MAX_MD_SIZE];
84        char deroid[2];
85        const EVP_MD *evp_md = EVP_md5();
86        EVP_MD_CTX md;
87
88        if (gss_enc2oid != NULL) {
89                for (i=0;gss_enc2oid[i].encoded!=NULL;i++)
90                        xfree(gss_enc2oid[i].encoded);
91                xfree(gss_enc2oid);
92        }
93
94        if (datafellows & SSH_BUG_GSSAPI_BER) {
95                gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping)
96                                        *((gss_supported->count*2)+1));
97        } else {
98                gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping)
99                                        *(gss_supported->count+1));
100        }
101
102        buffer_init(&buf);
103
104        oidpos = 0;
105        for (i=0;i<gss_supported->count;i++) {
106                gss_enc2oid[oidpos].encoded = NULL;
107       
108                if (gss_supported->elements[i].length<128 &&
109                    (*check)(&(gss_supported->elements[i]), data)) {
110
111                        /* Earlier versions of this code interpreted the
112                         * spec incorrectly with regard to OID encoding. They
113                         * also mis-encoded the krb5 OID. The following
114                         * _temporary_ code interfaces with these broken
115                         * servers */
116
117                        if (datafellows & SSH_BUG_GSSAPI_BER) {
118                                char *bodge = NULL;
119                                gss_OID_desc krb5oid = {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
120                                gss_OID_desc gsioid = {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"};
121                               
122                                if (gss_supported->elements[i].length == krb5oid.length &&
123                                    memcmp(gss_supported->elements[i].elements,
124                                           krb5oid.elements, krb5oid.length) == 0) {
125                                        bodge="Se3H81ismmOC3OE+FwYCiQ==";
126                                }
127                               
128                                if (gss_supported->elements[i].length == gsioid.length &&
129                                    memcmp(gss_supported->elements[i].elements,
130                                           gsioid.elements, gsioid.length) == 0) {
131                                        bodge="N3+k7/4wGxHyuP8Yxi4RhA==";
132                                }
133
134                                if (bodge) {                           
135                                        if (oidpos != 0) {
136                                                buffer_put_char(&buf, ',');
137                                        }
138                               
139                                        buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1);
140                                        buffer_append(&buf, bodge, strlen(bodge));
141
142                                        gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
143                                        gss_enc2oid[oidpos].encoded = bodge;
144                       
145                                        oidpos++;
146                                }
147                        }
148                       
149                        /* Add the required DER encoding octets and MD5 hash */
150                        deroid[0] = SSH_GSS_OIDTYPE;
151                        deroid[1] = gss_supported->elements[i].length;
152                       
153                        EVP_DigestInit(&md, evp_md);
154                        EVP_DigestUpdate(&md, deroid, 2);
155                        EVP_DigestUpdate(&md,
156                            gss_supported->elements[i].elements,
157                            gss_supported->elements[i].length);
158                        EVP_DigestFinal(&md, digest, NULL);
159
160                        /* Base64 encode it */
161                        encoded = xmalloc(EVP_MD_size(evp_md)*2);
162                        enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
163                            encoded, EVP_MD_size(evp_md)*2);
164
165                        if (oidpos != 0)
166                            buffer_put_char(&buf, ',');
167
168                        buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1);
169                        buffer_append(&buf, encoded, enclen);
170
171                        gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
172                        gss_enc2oid[oidpos].encoded = encoded;
173                        oidpos++;
174                }
175        }
176        gss_enc2oid[oidpos].oid = NULL;
177        gss_enc2oid[oidpos].encoded = NULL;
178
179        buffer_put_char(&buf, '\0');
180
181        mechs = xmalloc(buffer_len(&buf));
182        buffer_get(&buf, mechs, buffer_len(&buf));
183        buffer_free(&buf);
184
185        if (strlen(mechs) == 0) {
186                xfree(mechs);
187                mechs = NULL;
188        }
189       
190        return (mechs);
191}
192
193gss_OID
194ssh_gssapi_id_kex(Gssctxt *ctx, char *name) {
195        int i = 0;
196
197        if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) != 0)
198                return NULL;
199
200        name+=sizeof(KEX_GSS_SHA1)-1; /* Skip ID string */
201
202        while (gss_enc2oid[i].encoded != NULL &&
203            strcmp(name,gss_enc2oid[i].encoded) != 0) {
204                i++;
205        }
206
207        if (gss_enc2oid[i].oid != NULL && ctx != NULL)
208                ssh_gssapi_set_oid(ctx,gss_enc2oid[i].oid);
209
210        debug("using GSSAPI mechanism %s:%s", KEX_GSS_SHA1, gss_enc2oid[i].encoded);
211
212        return gss_enc2oid[i].oid;
213}
214
215/* Check that the OID in a data stream matches that in the context */
216int
217ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
218{
219        return (ctx != NULL && ctx->oid != GSS_C_NO_OID &&
220            ctx->oid->length == len &&
221            memcmp(ctx->oid->elements, data, len) == 0);
222}
223
224/* Set the contexts OID from a data stream */
225void
226ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len)
227{
228        if (ctx->oid != GSS_C_NO_OID) {
229                xfree(ctx->oid->elements);
230                xfree(ctx->oid);
231        }
232        ctx->oid = xmalloc(sizeof(gss_OID_desc));
233        ctx->oid->length = len;
234        ctx->oid->elements = xmalloc(len);
235        memcpy(ctx->oid->elements, data, len);
236}
237
238/* Set the contexts OID */
239void
240ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid)
241{
242        ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length);
243}
244
245/* All this effort to report an error ... */
246void
247ssh_gssapi_error(Gssctxt *ctxt)
248{
249        debug("%s", ssh_gssapi_last_error(ctxt, NULL, NULL));
250}
251
252char *
253ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status,
254    OM_uint32 *minor_status)
255{
256        OM_uint32 lmin;
257        gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
258        OM_uint32 ctx;
259        Buffer b;
260        char *ret;
261
262        buffer_init(&b);
263
264        if (major_status != NULL)
265                *major_status = ctxt->major;
266        if (minor_status != NULL)
267                *minor_status = ctxt->minor;
268
269        ctx = 0;
270        /* The GSSAPI error */
271        do {
272                gss_display_status(&lmin, ctxt->major,
273                    GSS_C_GSS_CODE, GSS_C_NULL_OID, &ctx, &msg);
274
275                buffer_append(&b, msg.value, msg.length);
276                buffer_put_char(&b, '\n');
277
278                gss_release_buffer(&lmin, &msg);
279        } while (ctx != 0);
280
281        /* The mechanism specific error */
282        do {
283                gss_display_status(&lmin, ctxt->minor,
284                    GSS_C_MECH_CODE, GSS_C_NULL_OID, &ctx, &msg);
285
286                buffer_append(&b, msg.value, msg.length);
287                buffer_put_char(&b, '\n');
288
289                gss_release_buffer(&lmin, &msg);
290        } while (ctx != 0);
291
292        buffer_put_char(&b, '\0');
293        ret = xmalloc(buffer_len(&b));
294        buffer_get(&b, ret, buffer_len(&b));
295        buffer_free(&b);
296        return (ret);
297}
298
299/*
300 * Initialise our GSSAPI context. We use this opaque structure to contain all
301 * of the data which both the client and server need to persist across
302 * {accept,init}_sec_context calls, so that when we do it from the userauth
303 * stuff life is a little easier
304 */
305void
306ssh_gssapi_build_ctx(Gssctxt **ctx)
307{
308        *ctx = xmalloc(sizeof (Gssctxt));
309        (*ctx)->major = 0;
310        (*ctx)->minor = 0;
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 */
320void
321ssh_gssapi_delete_ctx(Gssctxt **ctx)
322{
323        OM_uint32 ms;
324
325        if ((*ctx) == NULL)
326                return;
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
343        xfree(*ctx);
344        *ctx = NULL;
345}
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 */
353OM_uint32
354ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
355    gss_buffer_desc* send_tok, OM_uint32 *flags)
356{
357        int deleg_flag = 0;
358
359        if (deleg_creds) {
360                deleg_flag = GSS_C_DELEG_FLAG;
361                debug("Delegating credentials");
362        }
363
364        ctx->major = gss_init_sec_context(&ctx->minor,
365            GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid,
366            GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
367            0, NULL, recv_tok, NULL, send_tok, flags, NULL);
368
369        if (GSS_ERROR(ctx->major))
370                ssh_gssapi_error(ctx);
371
372        return (ctx->major);
373}
374
375/* Create a service name for the given host */
376OM_uint32
377ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
378{
379        gss_buffer_desc gssbuf;
380
381        gssbuf.length = sizeof("host@") + strlen(host);
382        gssbuf.value = xmalloc(gssbuf.length);
383        snprintf(gssbuf.value, gssbuf.length, "host@%s", host);
384
385        if ((ctx->major = gss_import_name(&ctx->minor,
386            &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name)))
387                ssh_gssapi_error(ctx);
388
389        xfree(gssbuf.value);
390        return (ctx->major);
391}
392
393/* Acquire credentials for a server running on the current host.
394 * Requires that the context structure contains a valid OID
395 */
396
397/* Returns a GSSAPI error code */
398OM_uint32
399ssh_gssapi_acquire_cred(Gssctxt *ctx)
400{
401        OM_uint32 status;
402        char lname[MAXHOSTNAMELEN];
403        gss_OID_set oidset;
404
405        gss_create_empty_oid_set(&status, &oidset);
406        gss_add_oid_set_member(&status, ctx->oid, &oidset);
407
408        if (gethostname(lname, MAXHOSTNAMELEN))
409                return (-1);
410
411        if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname)))
412                return (ctx->major);
413
414        if ((ctx->major = gss_acquire_cred(&ctx->minor,
415            ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL)))
416                ssh_gssapi_error(ctx);
417
418        gss_release_oid_set(&status, &oidset);
419        return (ctx->major);
420}
421
422OM_uint32
423ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
424{
425        if (ctx == NULL)
426                return -1;
427
428        if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
429            GSS_C_QOP_DEFAULT, buffer, hash)))
430                ssh_gssapi_error(ctx);
431
432        return (ctx->major);
433}
434
435/* Priviledged when used by server */
436OM_uint32
437ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
438{
439        if (ctx == NULL)
440                return -1;
441
442        ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
443            gssbuf, gssmic, NULL);
444
445        return (ctx->major);
446}
447
448void
449ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
450    const char *context)
451{
452        buffer_init(b);
453        buffer_put_string(b, session_id2, session_id2_len);
454        buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST);
455        buffer_put_cstring(b, user);
456        buffer_put_cstring(b, service);
457        buffer_put_cstring(b, context);
458}
459
460OM_uint32
461ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) {
462        if (*ctx)
463                ssh_gssapi_delete_ctx(ctx);
464        ssh_gssapi_build_ctx(ctx);
465        ssh_gssapi_set_oid(*ctx, oid);
466        return (ssh_gssapi_acquire_cred(*ctx));
467}
468
469int
470ssh_gssapi_check_mechanism(gss_OID oid, void *host)
471{
472        Gssctxt * ctx = NULL;
473        gss_buffer_desc token;
474        OM_uint32 major, minor;
475       
476        ssh_gssapi_build_ctx(&ctx);
477        ssh_gssapi_set_oid(ctx, oid);
478        ssh_gssapi_import_name(ctx, host);
479        major = ssh_gssapi_init_ctx(ctx, 0, GSS_C_NO_BUFFER, &token, NULL);
480        gss_release_buffer(&minor, &token);
481        ssh_gssapi_delete_ctx(&ctx);
482
483        return (!GSS_ERROR(major));
484}
485
486#endif /* GSSAPI */
Note: See TracBrowser for help on using the repository browser.