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