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