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