xref: /titanic_50/usr/src/cmd/ssh/libssh/common/ssh-gss.c (revision b9aa66a73c9016cf5c71fe80efe90ce9f2ca5c73)
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