1 /*
2 * Copyright (c) 2001-2003 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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 #include "includes.h"
29
30 #ifdef GSSAPI
31
32 #include "ssh.h"
33 #include "ssh2.h"
34 #include "xmalloc.h"
35 #include "buffer.h"
36 #include "bufaux.h"
37 #include "packet.h"
38 #include "compat.h"
39 #include <openssl/opensslconf.h>
40 #include <openssl/evp.h>
41 #include "cipher.h"
42 #include "kex.h"
43 #include "log.h"
44 #include "compat.h"
45 #include "xlist.h"
46
47 #include <netdb.h>
48
49 #include "ssh-gss.h"
50
51 #ifdef HAVE_GSS_OID_TO_MECH
52 #include <gssapi/gssapi_ext.h>
53 #endif /* HAVE_GSS_OID_TO_MECH */
54
55 typedef struct {
56 char *encoded;
57 gss_OID oid;
58 } ssh_gss_kex_mapping;
59
60 static ssh_gss_kex_mapping **gss_enc2oid = NULL;
61
62 static void ssh_gssapi_encode_oid_for_kex(const gss_OID oid, char **enc_name);
63 static char *ssh_gssapi_make_kexalgs_list(gss_OID_set mechs,
64 const char *old_kexalgs);
65
66 /*
67 * Populate gss_enc2oid table and return list of kexnames.
68 *
69 * If called with both mechs == GSS_C_NULL_OID_SET and kexname_list == NULL
70 * then cached gss_enc2oid table is cleaned up.
71 */
72 void
73 ssh_gssapi_mech_oids_to_kexnames(const gss_OID_set mechs, char **kexname_list)
74 {
75 ssh_gss_kex_mapping **new_gss_enc2oid, **p;
76 Buffer buf;
77 char *enc_name;
78 int i;
79
80 if (kexname_list != NULL)
81 *kexname_list = NULL; /* default to failed */
82
83 if (mechs != GSS_C_NULL_OID_SET || kexname_list == NULL) {
84 /* Cleanup gss_enc2oid table */
85 for (p = gss_enc2oid; p != NULL && *p != NULL; p++) {
86 if ((*p)->encoded)
87 xfree((*p)->encoded);
88 ssh_gssapi_release_oid(&(*p)->oid);
89 xfree(*p);
90 }
91 if (gss_enc2oid)
92 xfree(gss_enc2oid);
93 }
94
95 if (mechs == GSS_C_NULL_OID_SET && kexname_list == NULL)
96 return; /* nothing left to do */
97
98 if (mechs) {
99 gss_OID mech;
100 /* Populate gss_enc2oid table */
101 new_gss_enc2oid = xmalloc(sizeof (ssh_gss_kex_mapping *) *
102 (mechs->count + 1));
103 memset(new_gss_enc2oid, 0,
104 sizeof (ssh_gss_kex_mapping *) * (mechs->count + 1));
105
106 for (i = 0; i < mechs->count; i++) {
107 mech = &mechs->elements[i];
108 ssh_gssapi_encode_oid_for_kex((const gss_OID)mech,
109 &enc_name);
110
111 if (!enc_name)
112 continue;
113
114 new_gss_enc2oid[i] =
115 xmalloc(sizeof (ssh_gss_kex_mapping));
116 (new_gss_enc2oid[i])->encoded = enc_name;
117 (new_gss_enc2oid[i])->oid =
118 ssh_gssapi_dup_oid(&mechs->elements[i]);
119 }
120
121 /* Do this last to avoid run-ins with fatal_cleanups */
122 gss_enc2oid = new_gss_enc2oid;
123 }
124
125 if (!kexname_list)
126 return; /* nothing left to do */
127
128 /* Make kex name list */
129 buffer_init(&buf);
130 for (p = gss_enc2oid; p && *p; p++) {
131 buffer_put_char(&buf, ',');
132 buffer_append(&buf, (*p)->encoded, strlen((*p)->encoded));
133 }
134
135 if (buffer_len(&buf) == 0) {
136 buffer_free(&buf);
137 return;
138 }
139
140 buffer_consume(&buf, 1); /* consume leading ',' */
141 buffer_put_char(&buf, '\0');
142
143 *kexname_list = xstrdup(buffer_ptr(&buf));
144 buffer_free(&buf);
145 }
146
147 void
148 ssh_gssapi_mech_oid_to_kexname(const gss_OID mech, char **kexname)
149 {
150 ssh_gss_kex_mapping **p;
151
152 if (mech == GSS_C_NULL_OID || !kexname)
153 return;
154
155 *kexname = NULL; /* default to not found */
156 if (gss_enc2oid) {
157 for (p = gss_enc2oid; p && *p; p++) {
158 if (mech->length == (*p)->oid->length &&
159 memcmp(mech->elements, (*p)->oid->elements,
160 mech->length) == 0)
161 *kexname = xstrdup((*p)->encoded);
162 }
163 }
164
165 if (*kexname)
166 return; /* found */
167
168 ssh_gssapi_encode_oid_for_kex(mech, kexname);
169 }
170
171 void
172 ssh_gssapi_oid_of_kexname(const char *kexname, gss_OID *mech)
173 {
174 ssh_gss_kex_mapping **p;
175
176 if (!mech || !kexname || !*kexname)
177 return;
178
179 *mech = GSS_C_NULL_OID; /* default to not found */
180
181 if (!gss_enc2oid)
182 return;
183
184 for (p = gss_enc2oid; p && *p; p++) {
185 if (strcmp(kexname, (*p)->encoded) == 0) {
186 *mech = (*p)->oid;
187 return;
188 }
189 }
190 }
191
192 static
193 void
194 ssh_gssapi_encode_oid_for_kex(const gss_OID oid, char **enc_name)
195 {
196 Buffer buf;
197 OM_uint32 oidlen;
198 uint_t enclen;
199 const EVP_MD *evp_md = EVP_md5();
200 EVP_MD_CTX md;
201 uchar_t digest[EVP_MAX_MD_SIZE];
202 char *encoded;
203
204 if (oid == GSS_C_NULL_OID || !enc_name)
205 return;
206
207 *enc_name = NULL;
208
209 oidlen = oid->length;
210
211 /* No GSS mechs have OIDs as long as 128 -- simplify DER encoding */
212 if (oidlen > 128)
213 return; /* fail gracefully */
214
215 /*
216 * NOTE: If we need to support SSH_BUG_GSSAPI_BER this is where
217 * we'd do it.
218 *
219 * That means using "Se3H81ismmOC3OE+FwYCiQ==" for the Kerberos
220 * V mech and "N3+k7/4wGxHyuP8Yxi4RhA==" for the GSI mech. Ick.
221 */
222
223 buffer_init(&buf);
224
225 /* UNIVERSAL class tag for OBJECT IDENTIFIER */
226 buffer_put_char(&buf, 0x06);
227 buffer_put_char(&buf, oidlen); /* one octet DER length -- see above */
228
229 /* OID elements */
230 buffer_append(&buf, oid->elements, oidlen);
231
232 /* Make digest */
233 EVP_DigestInit(&md, evp_md);
234 EVP_DigestUpdate(&md, buffer_ptr(&buf), buffer_len(&buf));
235 EVP_DigestFinal(&md, digest, NULL);
236 buffer_free(&buf);
237
238 /* Base 64 encoding */
239 encoded = xmalloc(EVP_MD_size(evp_md)*2);
240 enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
241 encoded, EVP_MD_size(evp_md) * 2);
242 buffer_init(&buf);
243 buffer_append(&buf, KEX_GSS_SHA1, sizeof (KEX_GSS_SHA1) - 1);
244 buffer_append(&buf, encoded, enclen);
245 buffer_put_char(&buf, '\0');
246
247 debug2("GSS-API Mechanism encoded as %s", encoded);
248 xfree(encoded);
249
250 *enc_name = xstrdup(buffer_ptr(&buf));
251 buffer_free(&buf);
252 }
253
254 static char *
255 ssh_gssapi_make_kexalgs_list(gss_OID_set mechs, const char *old_kexalgs)
256 {
257 char *gss_kexalgs, *new_kexalgs;
258 int len;
259
260 if (mechs == GSS_C_NULL_OID_SET)
261 return (xstrdup(old_kexalgs)); /* never null */
262
263 ssh_gssapi_mech_oids_to_kexnames(mechs, &gss_kexalgs);
264
265 if (gss_kexalgs == NULL || *gss_kexalgs == '\0')
266 return (xstrdup(old_kexalgs)); /* never null */
267
268 if (old_kexalgs == NULL || *old_kexalgs == '\0')
269 return (gss_kexalgs);
270
271 len = strlen(old_kexalgs) + strlen(gss_kexalgs) + 2;
272 new_kexalgs = xmalloc(len);
273 (void) snprintf(new_kexalgs, len, "%s,%s", gss_kexalgs, old_kexalgs);
274 xfree(gss_kexalgs);
275
276 return (new_kexalgs);
277 }
278
279 void
280 ssh_gssapi_modify_kex(Kex *kex, gss_OID_set mechs, char **proposal)
281 {
282 char *kexalgs, *orig_kexalgs, *p;
283 char **hostalg, *orig_hostalgs, *new_hostalgs;
284 char **hostalgs;
285 gss_OID_set dup_mechs;
286 OM_uint32 maj, min;
287 int i;
288
289 if (kex == NULL || proposal == NULL ||
290 proposal[PROPOSAL_KEX_ALGS] == NULL) {
291 fatal("INTERNAL ERROR (%s)", __func__);
292 }
293
294 orig_hostalgs = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
295
296 if (kex->mechs == GSS_C_NULL_OID_SET && mechs == GSS_C_NULL_OID_SET)
297 return; /* didn't offer GSS last time, not offering now */
298
299 if (kex->mechs == GSS_C_NULL_OID_SET || mechs == GSS_C_NULL_OID_SET)
300 goto mod_offer; /* didn't offer last time or not offering now */
301
302 /* Check if mechs is congruent to kex->mechs (last offered) */
303 if (kex->mechs->count == mechs->count) {
304 int present, matches = 0;
305
306 for (i = 0; i < mechs->count; i++) {
307 maj = gss_test_oid_set_member(&min,
308 &kex->mechs->elements[i], mechs, &present);
309
310 if (GSS_ERROR(maj)) {
311 mechs = GSS_C_NULL_OID_SET;
312 break;
313 }
314
315 matches += (present) ? 1 : 0;
316 }
317
318 if (matches == kex->mechs->count)
319 return; /* no change in offer from last time */
320 }
321
322 mod_offer:
323 /*
324 * Remove previously offered mechs from PROPOSAL_KEX_ALGS proposal
325 *
326 * ASSUMPTION: GSS-API kex algs always go in front, so removing
327 * them is a matter of skipping them.
328 */
329 p = kexalgs = orig_kexalgs = proposal[PROPOSAL_KEX_ALGS];
330 while (p != NULL && *p != '\0' &&
331 strncmp(p, KEX_GSS_SHA1, strlen(KEX_GSS_SHA1)) == 0) {
332
333 if ((p = strchr(p, ',')) == NULL)
334 break;
335 p++;
336 kexalgs = p;
337
338 }
339 kexalgs = proposal[PROPOSAL_KEX_ALGS] = xstrdup(kexalgs);
340 xfree(orig_kexalgs);
341
342 (void) gss_release_oid_set(&min, &kex->mechs); /* ok if !kex->mechs */
343
344 /* Not offering GSS kex algorithms now -> all done */
345 if (mechs == GSS_C_NULL_OID_SET)
346 return;
347
348 /* Remember mechs we're offering */
349 maj = gss_create_empty_oid_set(&min, &dup_mechs);
350 if (GSS_ERROR(maj))
351 return;
352 for (i = 0; i < mechs->count; i++) {
353 maj = gss_add_oid_set_member(&min, &mechs->elements[i],
354 &dup_mechs);
355
356 if (GSS_ERROR(maj)) {
357 (void) gss_release_oid_set(&min, &dup_mechs);
358 return;
359 }
360 }
361
362 /* Add mechs to kex algorithms ... */
363 proposal[PROPOSAL_KEX_ALGS] = ssh_gssapi_make_kexalgs_list(mechs,
364 kexalgs);
365 xfree(kexalgs);
366 kex->mechs = dup_mechs; /* remember what we offer now */
367
368 /*
369 * ... and add null host key alg, if it wasn't there before, but
370 * not if we're the server and we have other host key algs to
371 * offer.
372 *
373 * NOTE: Never remove "null" host key alg once added.
374 */
375 if (orig_hostalgs == NULL || *orig_hostalgs == '\0') {
376 proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = xstrdup("null");
377 } else if (!kex->server) {
378 hostalgs = xsplit(orig_hostalgs, ',');
379 for (hostalg = hostalgs; *hostalg != NULL; hostalg++) {
380 if (strcmp(*hostalg, "null") == 0) {
381 xfree_split_list(hostalgs);
382 return;
383 }
384 }
385 xfree_split_list(hostalgs);
386
387 if (kex->mechs != GSS_C_NULL_OID_SET) {
388 int len;
389
390 len = strlen(orig_hostalgs) + sizeof (",null");
391 new_hostalgs = xmalloc(len);
392 (void) snprintf(new_hostalgs, len, "%s,null",
393 orig_hostalgs);
394 proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = new_hostalgs;
395 }
396
397 xfree(orig_hostalgs);
398 }
399 }
400
401 /*
402 * Yes, we harcode OIDs for some things, for now it's all we can do.
403 *
404 * We have to reference particular mechanisms due to lack of generality
405 * in the GSS-API in several areas: authorization, mapping principal
406 * names to usernames, "storing" delegated credentials, and discovering
407 * whether a mechanism is a pseudo-mechanism that negotiates mechanisms.
408 *
409 * Even if they were in some header file or if __gss_mech_to_oid()
410 * and/or __gss_oid_to_mech() were standard we'd still have to hardcode
411 * the mechanism names, and since the mechanisms have no standard names
412 * other than their OIDs it's actually worse [less portable] to hardcode
413 * names than OIDs, so we hardcode OIDs.
414 *
415 * SPNEGO is a difficult problem though -- it MUST NOT be used in SSHv2,
416 * but that's true of all possible pseudo-mechanisms that can perform
417 * mechanism negotiation, and SPNEGO could have new OIDs in the future.
418 * Ideally we could query each mechanism for its feature set and then
419 * ignore any mechanisms that negotiate mechanisms, but, alas, there's
420 * no interface to do that.
421 *
422 * In the future, if the necessary generic GSS interfaces for the issues
423 * listed above are made available (even if they differ by platform, as
424 * we can expect authorization interfaces will), then we can stop
425 * referencing specific mechanism OIDs here.
426 */
427 int
428 ssh_gssapi_is_spnego(gss_OID oid)
429 {
430 return (oid->length == 6 &&
431 memcmp("\053\006\001\005\005\002", oid->elements, 6) == 0);
432 }
433
434 int
435 ssh_gssapi_is_krb5(gss_OID oid)
436 {
437 return (oid->length == 9 &&
438 memcmp("\x2A\x86\x48\x86\xF7\x12\x01\x02\x02",
439 oid->elements, 9) == 0);
440 }
441
442 int
443 ssh_gssapi_is_dh(gss_OID oid)
444 {
445 return (oid->length == 9 &&
446 memcmp("\053\006\004\001\052\002\032\002\005",
447 oid->elements, 9) == 0);
448 }
449
450 int
451 ssh_gssapi_is_gsi(gss_OID oid)
452 {
453 return (oid->length == 9 &&
454 memcmp("\x2B\x06\x01\x04\x01\x9B\x50\x01\x01",
455 oid->elements, 9) == 0);
456 }
457
458 const char *
459 ssh_gssapi_oid_to_name(gss_OID oid)
460 {
461 #ifdef HAVE_GSS_OID_TO_MECH
462 return (__gss_oid_to_mech(oid));
463 #else
464 if (ssh_gssapi_is_krb5(oid))
465 return ("Kerberos");
466 if (ssh_gssapi_is_gsi(oid))
467 return ("GSI");
468 return ("(unknown)");
469 #endif /* HAVE_GSS_OID_TO_MECH */
470 }
471
472 char *
473 ssh_gssapi_oid_to_str(gss_OID oid)
474 {
475 #ifdef HAVE_GSS_OID_TO_STR
476 gss_buffer_desc str_buf;
477 char *str;
478 OM_uint32 maj, min;
479
480 maj = gss_oid_to_str(&min, oid, &str_buf);
481
482 if (GSS_ERROR(maj))
483 return (xstrdup("<gss_oid_to_str() failed>"));
484
485 str = xmalloc(str_buf.length + 1);
486 memset(str, 0, str_buf.length + 1);
487 strlcpy(str, str_buf.value, str_buf.length + 1);
488 (void) gss_release_buffer(&min, &str_buf);
489
490 return (str);
491 #else
492 return (xstrdup("<gss_oid_to_str() unsupported>"));
493 #endif /* HAVE_GSS_OID_TO_STR */
494 }
495
496 /* Check that the OID in a data stream matches that in the context */
497 int
498 ssh_gssapi_check_mech_oid(Gssctxt *ctx, void *data, size_t len)
499 {
500
501 return (ctx != NULL && ctx->desired_mech != GSS_C_NULL_OID &&
502 ctx->desired_mech->length == len &&
503 memcmp(ctx->desired_mech->elements, data, len) == 0);
504 }
505
506 /* Set the contexts OID from a data stream */
507 void
508 ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len)
509 {
510 if (ctx->actual_mech != GSS_C_NULL_OID) {
511 xfree(ctx->actual_mech->elements);
512 xfree(ctx->actual_mech);
513 }
514 ctx->actual_mech = xmalloc(sizeof (gss_OID_desc));
515 ctx->actual_mech->length = len;
516 ctx->actual_mech->elements = xmalloc(len);
517 memcpy(ctx->actual_mech->elements, data, len);
518 }
519
520 /* Set the contexts OID */
521 void
522 ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid)
523 {
524 ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length);
525 }
526
527 /* All this effort to report an error ... */
528
529 void
530 ssh_gssapi_error(Gssctxt *ctxt, const char *where)
531 {
532 char *errmsg = ssh_gssapi_last_error(ctxt, NULL, NULL);
533
534 if (where != NULL)
535 debug("GSS-API error while %s: %s", where, errmsg);
536 else
537 debug("GSS-API error: %s", errmsg);
538
539 /* ssh_gssapi_last_error() can't return NULL */
540 xfree(errmsg);
541 }
542
543 char *
544 ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status,
545 OM_uint32 *minor_status)
546 {
547 OM_uint32 lmin, more;
548 OM_uint32 maj, min;
549 gss_OID mech = GSS_C_NULL_OID;
550 gss_buffer_desc msg;
551 Buffer b;
552 char *ret;
553
554 buffer_init(&b);
555
556 if (ctxt) {
557 /* Get status codes from the Gssctxt */
558 maj = ctxt->major;
559 min = ctxt->minor;
560 /* Output them if desired */
561 if (major_status)
562 *major_status = maj;
563 if (minor_status)
564 *minor_status = min;
565 /* Get mechanism for minor status display */
566 mech = (ctxt->actual_mech != GSS_C_NULL_OID) ?
567 ctxt->actual_mech : ctxt->desired_mech;
568 } else if (major_status && minor_status) {
569 maj = *major_status;
570 min = *major_status;
571 } else {
572 maj = GSS_S_COMPLETE;
573 min = 0;
574 }
575
576 more = 0;
577 /* The GSSAPI error */
578 do {
579 gss_display_status(&lmin, maj, GSS_C_GSS_CODE,
580 GSS_C_NULL_OID, &more, &msg);
581
582 buffer_append(&b, msg.value, msg.length);
583 buffer_put_char(&b, '\n');
584 gss_release_buffer(&lmin, &msg);
585 } while (more != 0);
586
587 /* The mechanism specific error */
588 do {
589 /*
590 * If mech == GSS_C_NULL_OID we may get the default
591 * mechanism, whatever that is, and that may not be
592 * useful.
593 */
594 gss_display_status(&lmin, min, GSS_C_MECH_CODE, mech, &more,
595 &msg);
596
597 buffer_append(&b, msg.value, msg.length);
598 buffer_put_char(&b, '\n');
599
600 gss_release_buffer(&lmin, &msg);
601 } while (more != 0);
602
603 buffer_put_char(&b, '\0');
604 ret = xstrdup(buffer_ptr(&b));
605 buffer_free(&b);
606
607 return (ret);
608 }
609
610 /*
611 * Initialise our GSSAPI context. We use this opaque structure to contain all
612 * of the data which both the client and server need to persist across
613 * {accept,init}_sec_context calls, so that when we do it from the userauth
614 * stuff life is a little easier
615 */
616 void
617 ssh_gssapi_build_ctx(Gssctxt **ctx, int client, gss_OID mech)
618 {
619 Gssctxt *newctx;
620
621
622 newctx = (Gssctxt*)xmalloc(sizeof (Gssctxt));
623 memset(newctx, 0, sizeof (Gssctxt));
624
625
626 newctx->local = client;
627 newctx->desired_mech = ssh_gssapi_dup_oid(mech);
628
629 /* This happens to be redundant given the memset() above */
630 newctx->major = GSS_S_COMPLETE;
631 newctx->context = GSS_C_NO_CONTEXT;
632 newctx->actual_mech = GSS_C_NULL_OID;
633 newctx->desired_name = GSS_C_NO_NAME;
634 newctx->src_name = GSS_C_NO_NAME;
635 newctx->dst_name = GSS_C_NO_NAME;
636 newctx->creds = GSS_C_NO_CREDENTIAL;
637 newctx->deleg_creds = GSS_C_NO_CREDENTIAL;
638
639 newctx->default_creds = (*ctx != NULL) ? (*ctx)->default_creds : 0;
640
641 ssh_gssapi_delete_ctx(ctx);
642
643 *ctx = newctx;
644 }
645
646 gss_OID
647 ssh_gssapi_dup_oid(gss_OID oid)
648 {
649 gss_OID new_oid;
650
651 new_oid = xmalloc(sizeof (gss_OID_desc));
652
653 new_oid->elements = xmalloc(oid->length);
654 new_oid->length = oid->length;
655 memcpy(new_oid->elements, oid->elements, oid->length);
656
657 return (new_oid);
658 }
659
660 gss_OID
661 ssh_gssapi_make_oid(size_t length, void *elements)
662 {
663 gss_OID_desc oid;
664
665 oid.length = length;
666 oid.elements = elements;
667
668 return (ssh_gssapi_dup_oid(&oid));
669 }
670
671 void
672 ssh_gssapi_release_oid(gss_OID *oid)
673 {
674 OM_uint32 min;
675
676 if (oid && *oid == GSS_C_NULL_OID)
677 return;
678 (void) gss_release_oid(&min, oid);
679
680 if (*oid == GSS_C_NULL_OID)
681 return; /* libgss did own this gss_OID and released it */
682
683 xfree((*oid)->elements);
684 xfree(*oid);
685 *oid = GSS_C_NULL_OID;
686 }
687
688 struct gss_name {
689 gss_OID name_type;
690 gss_buffer_t external_name;
691 gss_OID mech_type;
692 void *mech_name;
693 };
694
695 /* Delete our context, providing it has been built correctly */
696 void
697 ssh_gssapi_delete_ctx(Gssctxt **ctx)
698 {
699 OM_uint32 ms;
700
701 if ((*ctx) == NULL)
702 return;
703
704 if ((*ctx)->context != GSS_C_NO_CONTEXT)
705 gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER);
706 #if 0
707 /* XXX */
708 if ((*ctx)->desired_mech != GSS_C_NULL_OID)
709 ssh_gssapi_release_oid(&(*ctx)->desired_mech);
710 #endif
711 if ((*ctx)->actual_mech != GSS_C_NULL_OID)
712 (void) ssh_gssapi_release_oid(&(*ctx)->actual_mech);
713 if ((*ctx)->desired_name != GSS_C_NO_NAME)
714 gss_release_name(&ms, &(*ctx)->desired_name);
715 #if 0
716 if ((*ctx)->src_name != GSS_C_NO_NAME)
717 gss_release_name(&ms, &(*ctx)->src_name);
718 #endif
719 if ((*ctx)->dst_name != GSS_C_NO_NAME)
720 gss_release_name(&ms, &(*ctx)->dst_name);
721 if ((*ctx)->creds != GSS_C_NO_CREDENTIAL)
722 gss_release_cred(&ms, &(*ctx)->creds);
723 if ((*ctx)->deleg_creds != GSS_C_NO_CREDENTIAL)
724 gss_release_cred(&ms, &(*ctx)->deleg_creds);
725
726 xfree(*ctx);
727 *ctx = NULL;
728 }
729
730 /* Create a GSS hostbased service principal name for a given server hostname */
731 int
732 ssh_gssapi_import_name(Gssctxt *ctx, const char *server_host)
733 {
734 gss_buffer_desc name_buf;
735 int ret;
736
737 /* Build target principal */
738 name_buf.length = strlen(SSH_GSS_HOSTBASED_SERVICE) +
739 strlen(server_host) + 1; /* +1 for '@' */
740 name_buf.value = xmalloc(name_buf.length + 1); /* +1 for NUL */
741 ret = snprintf(name_buf.value, name_buf.length + 1, "%s@%s",
742 SSH_GSS_HOSTBASED_SERVICE, server_host);
743
744 debug3("%s: snprintf() returned %d, expected %d", __func__, ret,
745 name_buf.length);
746
747 ctx->major = gss_import_name(&ctx->minor, &name_buf,
748 GSS_C_NT_HOSTBASED_SERVICE, &ctx->desired_name);
749
750 if (GSS_ERROR(ctx->major)) {
751 ssh_gssapi_error(ctx, "calling GSS_Import_name()");
752 return (0);
753 }
754
755 xfree(name_buf.value);
756
757 return (1);
758 }
759
760 OM_uint32
761 ssh_gssapi_get_mic(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash)
762 {
763
764 ctx->major = gss_get_mic(&ctx->minor, ctx->context,
765 GSS_C_QOP_DEFAULT, buffer, hash);
766 if (GSS_ERROR(ctx->major))
767 ssh_gssapi_error(ctx, "while getting MIC");
768 return (ctx->major);
769 }
770
771 OM_uint32
772 ssh_gssapi_verify_mic(Gssctxt *ctx, gss_buffer_desc *buffer,
773 gss_buffer_desc *hash)
774 {
775 gss_qop_t qop;
776
777 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, buffer,
778 hash, &qop);
779 if (GSS_ERROR(ctx->major))
780 ssh_gssapi_error(ctx, "while verifying MIC");
781 return (ctx->major);
782 }
783 #endif /* GSSAPI */