]> andersk Git - gssapi-openssh.git/blob - openssh/gss-genr.c
apply updates from OpenSSH-4.3p1-hpn11-none.patch
[gssapi-openssh.git] / openssh / gss-genr.c
1 /*      $OpenBSD: gss-genr.c,v 1.6 2005/10/13 22:24:31 stevesk Exp $    */
2
3 /*
4  * Copyright (c) 2001-2005 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 "buffer.h"
33 #include "bufaux.h"
34 #include "compat.h"
35 #include <openssl/evp.h>
36 #include "kex.h"
37 #include "log.h"
38 #include "monitor_wrap.h"
39 #include "canohost.h"
40 #include "ssh2.h"
41 #include <openssl/evp.h>
42
43 #include "ssh-gss.h"
44
45 extern u_char *session_id2;
46 extern u_int session_id2_len;
47
48 typedef struct {
49         char *encoded;
50         gss_OID oid;
51 } ssh_gss_kex_mapping;
52
53 /*
54  * XXX - It would be nice to find a more elegant way of handling the
55  * XXX   passing of the key exchange context to the userauth routines
56  */
57
58 Gssctxt *gss_kex_context = NULL;
59
60 static ssh_gss_kex_mapping *gss_enc2oid = NULL;
61
62 int 
63 ssh_gssapi_oid_table_ok() {
64         return (gss_enc2oid != NULL);
65 }
66
67 /*
68  * Return a list of the gss-group1-sha1 mechanisms supported by this program
69  *
70  * We test mechanisms to ensure that we can use them, to avoid starting
71  * a key exchange with a bad mechanism
72  */
73
74
75 char *
76 ssh_gssapi_client_mechanisms(const char *host) {
77         gss_OID_set gss_supported;
78         OM_uint32 min_status;
79
80         gss_indicate_mechs(&min_status, &gss_supported);
81
82         return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
83             (void *)host));
84 }
85
86 char *
87 ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
88     void *data) {
89         Buffer buf;
90         size_t i;
91         int oidpos, enclen;
92         char *mechs, *encoded;
93         unsigned char digest[EVP_MAX_MD_SIZE];
94         char deroid[2];
95         const EVP_MD *evp_md = EVP_md5();
96         EVP_MD_CTX md;
97
98         if (gss_enc2oid != NULL) {
99                 for (i=0;gss_enc2oid[i].encoded!=NULL;i++)
100                         xfree(gss_enc2oid[i].encoded);
101                 xfree(gss_enc2oid);
102         }
103
104         gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping)*
105             (gss_supported->count+1));
106
107         buffer_init(&buf);
108
109         oidpos = 0;
110         for (i = 0;i < gss_supported->count;i++) {
111                 if (gss_supported->elements[i].length < 128 &&
112                     (*check)(&(gss_supported->elements[i]), data)) {
113
114                         deroid[0] = SSH_GSS_OIDTYPE;
115                         deroid[1] = gss_supported->elements[i].length;
116
117                         EVP_DigestInit(&md, evp_md);
118                         EVP_DigestUpdate(&md, deroid, 2);
119                         EVP_DigestUpdate(&md,
120                             gss_supported->elements[i].elements,
121                             gss_supported->elements[i].length);
122                         EVP_DigestFinal(&md, digest, NULL);
123
124                         encoded = xmalloc(EVP_MD_size(evp_md)*2);
125                         enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
126                             encoded, EVP_MD_size(evp_md)*2);
127
128                         if (oidpos != 0)
129                             buffer_put_char(&buf, ',');
130
131                         buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
132                             sizeof(KEX_GSS_GEX_SHA1_ID)-1);
133                         buffer_append(&buf, encoded, enclen);
134                         buffer_put_char(&buf,',');
135                         buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, 
136                             sizeof(KEX_GSS_GRP1_SHA1_ID)-1);
137                         buffer_append(&buf, encoded, enclen);
138
139                         gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
140                         gss_enc2oid[oidpos].encoded = encoded;
141                         oidpos++;
142                 }
143         }
144         gss_enc2oid[oidpos].oid = NULL;
145         gss_enc2oid[oidpos].encoded = NULL;
146
147         buffer_put_char(&buf, '\0');
148
149         mechs = xmalloc(buffer_len(&buf));
150         buffer_get(&buf, mechs, buffer_len(&buf));
151         buffer_free(&buf);
152
153         if (strlen(mechs) == 0) {
154                 xfree(mechs);
155                 mechs = NULL;
156         }
157         
158         return (mechs);
159 }
160
161 gss_OID
162 ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int *gex) {
163         int i = 0;
164
165         if (strncmp(name, KEX_GSS_GRP1_SHA1_ID,
166             sizeof(KEX_GSS_GRP1_SHA1_ID)-1) == 0) {
167                 name+=sizeof(KEX_GSS_GRP1_SHA1_ID)-1;
168                 *gex = 0;
169         } else if (strncmp(name, KEX_GSS_GEX_SHA1_ID,
170             sizeof(KEX_GSS_GEX_SHA1_ID)-1) == 0) {
171                 name+=sizeof(KEX_GSS_GEX_SHA1_ID)-1;
172                 *gex = 1;
173         } else {
174                 return NULL;
175         }
176
177         while (gss_enc2oid[i].encoded != NULL &&
178             strcmp(name, gss_enc2oid[i].encoded) != 0) {
179                 i++;
180         }
181
182         if (gss_enc2oid[i].oid != NULL && ctx != NULL)
183                 ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
184
185         return gss_enc2oid[i].oid;
186 }
187
188 /* Check that the OID in a data stream matches that in the context */
189 int
190 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
191 {
192         return (ctx != NULL && ctx->oid != GSS_C_NO_OID &&
193             ctx->oid->length == len &&
194             memcmp(ctx->oid->elements, data, len) == 0);
195 }
196
197 /* Set the contexts OID from a data stream */
198 void
199 ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len)
200 {
201         if (ctx->oid != GSS_C_NO_OID) {
202                 xfree(ctx->oid->elements);
203                 xfree(ctx->oid);
204         }
205         ctx->oid = xmalloc(sizeof(gss_OID_desc));
206         ctx->oid->length = len;
207         ctx->oid->elements = xmalloc(len);
208         memcpy(ctx->oid->elements, data, len);
209 }
210
211 /* Set the contexts OID */
212 void
213 ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid)
214 {
215         ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length);
216 }
217
218 /* All this effort to report an error ... */
219 void
220 ssh_gssapi_error(Gssctxt *ctxt)
221 {
222         debug("%s", ssh_gssapi_last_error(ctxt, NULL, NULL));
223 }
224
225 char *
226 ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status,
227     OM_uint32 *minor_status)
228 {
229         OM_uint32 lmin;
230         gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
231         OM_uint32 ctx;
232         Buffer b;
233         char *ret;
234
235         buffer_init(&b);
236
237         if (major_status != NULL)
238                 *major_status = ctxt->major;
239         if (minor_status != NULL)
240                 *minor_status = ctxt->minor;
241
242         ctx = 0;
243         /* The GSSAPI error */
244         do {
245                 gss_display_status(&lmin, ctxt->major,
246                     GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg);
247
248                 buffer_append(&b, msg.value, msg.length);
249                 buffer_put_char(&b, '\n');
250
251                 gss_release_buffer(&lmin, &msg);
252         } while (ctx != 0);
253
254         /* The mechanism specific error */
255         do {
256                 gss_display_status(&lmin, ctxt->minor,
257                     GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg);
258
259                 buffer_append(&b, msg.value, msg.length);
260                 buffer_put_char(&b, '\n');
261
262                 gss_release_buffer(&lmin, &msg);
263         } while (ctx != 0);
264
265         buffer_put_char(&b, '\0');
266         ret = xmalloc(buffer_len(&b));
267         buffer_get(&b, ret, buffer_len(&b));
268         buffer_free(&b);
269         return (ret);
270 }
271
272 /*
273  * Initialise our GSSAPI context. We use this opaque structure to contain all
274  * of the data which both the client and server need to persist across
275  * {accept,init}_sec_context calls, so that when we do it from the userauth
276  * stuff life is a little easier
277  */
278 void
279 ssh_gssapi_build_ctx(Gssctxt **ctx)
280 {
281         *ctx = xmalloc(sizeof (Gssctxt));
282         (*ctx)->major = 0;
283         (*ctx)->minor = 0;
284         (*ctx)->context = GSS_C_NO_CONTEXT;
285         (*ctx)->name = GSS_C_NO_NAME;
286         (*ctx)->oid = GSS_C_NO_OID;
287         (*ctx)->creds = GSS_C_NO_CREDENTIAL;
288         (*ctx)->client = GSS_C_NO_NAME;
289         (*ctx)->client_creds = GSS_C_NO_CREDENTIAL;
290 }
291
292 /* Delete our context, providing it has been built correctly */
293 void
294 ssh_gssapi_delete_ctx(Gssctxt **ctx)
295 {
296 #if !defined(MECHGLUE)
297         OM_uint32 ms;
298 #endif
299
300         if ((*ctx) == NULL)
301                 return;
302 #if !defined(MECHGLUE) /* mechglue has some memory management issues */
303         if ((*ctx)->context != GSS_C_NO_CONTEXT)
304                 gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER);
305         if ((*ctx)->name != GSS_C_NO_NAME)
306                 gss_release_name(&ms, &(*ctx)->name);
307         if ((*ctx)->oid != GSS_C_NO_OID) {
308                 xfree((*ctx)->oid->elements);
309                 xfree((*ctx)->oid);
310                 (*ctx)->oid = GSS_C_NO_OID;
311         }
312         if ((*ctx)->creds != GSS_C_NO_CREDENTIAL)
313                 gss_release_cred(&ms, &(*ctx)->creds);
314         if ((*ctx)->client != GSS_C_NO_NAME)
315                 gss_release_name(&ms, &(*ctx)->client);
316         if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL)
317                 gss_release_cred(&ms, &(*ctx)->client_creds);
318 #endif
319
320         xfree(*ctx);
321         *ctx = NULL;
322 }
323
324 /*
325  * Wrapper to init_sec_context
326  * Requires that the context contains:
327  *      oid
328  *      server name (from ssh_gssapi_import_name)
329  */
330 OM_uint32
331 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
332     gss_buffer_desc* send_tok, OM_uint32 *flags)
333 {
334         int deleg_flag = 0;
335
336         if (deleg_creds) {
337                 deleg_flag = GSS_C_DELEG_FLAG;
338                 debug("Delegating credentials");
339         }
340
341         ctx->major = gss_init_sec_context(&ctx->minor,
342             GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid,
343             GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
344             0, NULL, recv_tok, NULL, send_tok, flags, NULL);
345
346         if (GSS_ERROR(ctx->major))
347                 ssh_gssapi_error(ctx);
348
349         return (ctx->major);
350 }
351
352 /* Create a service name for the given host */
353 OM_uint32
354 ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
355 {
356         gss_buffer_desc gssbuf;
357         char *xhost;
358
359         /* Make a copy of the host name, in case it was returned by a
360          * previous call to gethostbyname(). */ 
361         xhost = xstrdup(host);
362
363         /* Make sure we have the FQDN. Some GSSAPI implementations don't do
364          * this for us themselves */
365         resolve_localhost(&xhost);
366         
367         gssbuf.length = sizeof("host@") + strlen(xhost);
368         gssbuf.value = xmalloc(gssbuf.length);
369         snprintf(gssbuf.value, gssbuf.length, "host@%s", xhost);
370
371         if ((ctx->major = gss_import_name(&ctx->minor,
372             &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name)))
373                 ssh_gssapi_error(ctx);
374
375         xfree(xhost);
376         xfree(gssbuf.value);
377         return (ctx->major);
378 }
379
380 /* Acquire credentials for a server running on the current host.
381  * Requires that the context structure contains a valid OID
382  */
383
384 /* Returns a GSSAPI error code */
385 OM_uint32
386 ssh_gssapi_acquire_cred(Gssctxt *ctx)
387 {
388         OM_uint32 status;
389         char lname[MAXHOSTNAMELEN];
390         gss_OID_set oidset;
391
392         gss_create_empty_oid_set(&status, &oidset);
393         gss_add_oid_set_member(&status, ctx->oid, &oidset);
394
395         if (gethostname(lname, MAXHOSTNAMELEN))
396                 return (-1);
397
398         if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname)))
399                 return (ctx->major);
400
401         if ((ctx->major = gss_acquire_cred(&ctx->minor,
402             ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL)))
403                 ssh_gssapi_error(ctx);
404
405         gss_release_oid_set(&status, &oidset);
406         return (ctx->major);
407 }
408
409 OM_uint32
410 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
411 {
412         if (ctx == NULL) 
413                 return -1;
414
415         if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
416             GSS_C_QOP_DEFAULT, buffer, hash)))
417                 ssh_gssapi_error(ctx);
418
419         return (ctx->major);
420 }
421
422 /* Priviledged when used by server */
423 OM_uint32
424 ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
425 {
426         if (ctx == NULL)
427                 return -1;
428
429         ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
430             gssbuf, gssmic, NULL);
431
432         return (ctx->major);
433 }
434
435 void
436 ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
437     const char *context)
438 {
439         buffer_init(b);
440         buffer_put_string(b, session_id2, session_id2_len);
441         buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST);
442         buffer_put_cstring(b, user);
443         buffer_put_cstring(b, service);
444         buffer_put_cstring(b, context);
445 }
446
447 OM_uint32
448 ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
449 {
450         if (*ctx)
451                 ssh_gssapi_delete_ctx(ctx);
452         ssh_gssapi_build_ctx(ctx);
453         ssh_gssapi_set_oid(*ctx, oid);
454         return (ssh_gssapi_acquire_cred(*ctx));
455 }
456
457 int
458 ssh_gssapi_check_mechanism(gss_OID oid, void *host) {
459         Gssctxt * ctx = NULL;
460         gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
461         OM_uint32 major, minor;
462         
463         ssh_gssapi_build_ctx(&ctx);
464         ssh_gssapi_set_oid(ctx, oid);
465         ssh_gssapi_import_name(ctx, host);
466         major = ssh_gssapi_init_ctx(ctx, 0, GSS_C_NO_BUFFER, &token, NULL);
467         gss_release_buffer(&minor, &token);
468         ssh_gssapi_delete_ctx(&ctx);
469         return (!GSS_ERROR(major));
470 }
471
472 #endif /* GSSAPI */
This page took 0.082747 seconds and 5 git commands to generate.