Home | History | Annotate | Download | only in common
      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 2008 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 	uint_t enclen;
    200 	const EVP_MD *evp_md = EVP_md5();
    201 	EVP_MD_CTX md;
    202 	uchar_t 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 char *
    255 ssh_gssapi_make_kexalgs_list(gss_OID_set mechs, const char *old_kexalgs)
    256 {
    257 	char *gss_kexalgs, *new_kexalgs;
    258 	int len;
    259 
    260 	if (mechs == GSS_C_NULL_OID_SET)
    261 		return (xstrdup(old_kexalgs)); /* never null */
    262 
    263 	ssh_gssapi_mech_oids_to_kexnames(mechs, &gss_kexalgs);
    264 
    265 	if (gss_kexalgs == NULL || *gss_kexalgs == '\0')
    266 		return (xstrdup(old_kexalgs)); /* never null */
    267 
    268 	if (old_kexalgs == NULL || *old_kexalgs == '\0')
    269 		return (gss_kexalgs);
    270 
    271 	len = strlen(old_kexalgs) + strlen(gss_kexalgs) + 2;
    272 	new_kexalgs = xmalloc(len);
    273 	(void) snprintf(new_kexalgs, len, "%s,%s", gss_kexalgs, old_kexalgs);
    274 
    275 	return (new_kexalgs);
    276 }
    277 
    278 void
    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 	    (orig_kexalgs = 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 kexalgs 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 kexalgs ... */
    362 	proposal[PROPOSAL_KEX_ALGS] = ssh_gssapi_make_kexalgs_list(mechs,
    363 	    kexalgs);
    364 	kex->mechs = dup_mechs; /* remember what we offer now */
    365 
    366 	/*
    367 	 * ... and add null host key alg, if it wasn't there before, but
    368 	 * not if we're the server and we have other host key algs to
    369 	 * offer.
    370 	 *
    371 	 * NOTE: Never remove "null" host key alg once added.
    372 	 */
    373 	if (orig_hostalgs == NULL || *orig_hostalgs == '\0') {
    374 		proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = xstrdup("null");
    375 	} else if (!kex->server) {
    376 		hostalgs = xsplit(orig_hostalgs, ',');
    377 		for (hostalg = hostalgs; *hostalg != NULL; hostalg++) {
    378 			if (strcmp(*hostalg, "null") == 0) {
    379 				xfree_split_list(hostalgs);
    380 				return;
    381 			}
    382 		}
    383 		xfree_split_list(hostalgs);
    384 
    385 		if (kex->mechs != GSS_C_NULL_OID_SET) {
    386 			int len;
    387 
    388 			len = strlen(orig_hostalgs) + sizeof (",null");
    389 			new_hostalgs = xmalloc(len);
    390 			(void) snprintf(new_hostalgs, len, "%s,null",
    391 			    orig_hostalgs);
    392 			proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = new_hostalgs;
    393 		}
    394 
    395 		xfree(orig_hostalgs);
    396 	}
    397 }
    398 
    399 /*
    400  * Yes, we harcode OIDs for some things, for now it's all we can do.
    401  *
    402  * We have to reference particular mechanisms due to lack of generality
    403  * in the GSS-API in several areas: authorization, mapping principal
    404  * names to usernames, "storing" delegated credentials, and discovering
    405  * whether a mechanism is a pseudo-mechanism that negotiates mechanisms.
    406  *
    407  * Even if they were in some header file or if __gss_mech_to_oid()
    408  * and/or __gss_oid_to_mech() were standard we'd still have to hardcode
    409  * the mechanism names, and since the mechanisms have no standard names
    410  * other than their OIDs it's actually worse [less portable] to hardcode
    411  * names than OIDs, so we hardcode OIDs.
    412  *
    413  * SPNEGO is a difficult problem though -- it MUST NOT be used in SSHv2,
    414  * but that's true of all possible pseudo-mechanisms that can perform
    415  * mechanism negotiation, and SPNEGO could have new OIDs in the future.
    416  * Ideally we could query each mechanism for its feature set and then
    417  * ignore any mechanisms that negotiate mechanisms, but, alas, there's
    418  * no interface to do that.
    419  *
    420  * In the future, if the necessary generic GSS interfaces for the issues
    421  * listed above are made available (even if they differ by platform, as
    422  * we can expect authorization interfaces will), then we can stop
    423  * referencing specific mechanism OIDs here.
    424  */
    425 int
    426 ssh_gssapi_is_spnego(gss_OID oid)
    427 {
    428 	return (oid->length == 6 &&
    429 	    memcmp("\053\006\001\005\005\002", oid->elements, 6) == 0);
    430 }
    431 
    432 int
    433 ssh_gssapi_is_krb5(gss_OID oid)
    434 {
    435 	return (oid->length == 9 &&
    436 	    memcmp("\x2A\x86\x48\x86\xF7\x12\x01\x02\x02",
    437 	    oid->elements, 9) == 0);
    438 }
    439 
    440 int
    441 ssh_gssapi_is_dh(gss_OID oid)
    442 {
    443 	return (oid->length == 9 &&
    444 	    memcmp("\053\006\004\001\052\002\032\002\005",
    445 	    oid->elements, 9) == 0);
    446 }
    447 
    448 int
    449 ssh_gssapi_is_gsi(gss_OID oid)
    450 {
    451 	return (oid->length == 9 &&
    452 	    memcmp("\x2B\x06\x01\x04\x01\x9B\x50\x01\x01",
    453 	    oid->elements, 9) == 0);
    454 }
    455 
    456 const char *
    457 ssh_gssapi_oid_to_name(gss_OID oid)
    458 {
    459 #ifdef HAVE_GSS_OID_TO_MECH
    460 	return (__gss_oid_to_mech(oid));
    461 #else
    462 	if (ssh_gssapi_is_krb5(oid))
    463 		return ("Kerberos");
    464 	if (ssh_gssapi_is_gsi(oid))
    465 		return ("GSI");
    466 	return ("(unknown)");
    467 #endif /* HAVE_GSS_OID_TO_MECH */
    468 }
    469 
    470 char *
    471 ssh_gssapi_oid_to_str(gss_OID oid)
    472 {
    473 #ifdef HAVE_GSS_OID_TO_STR
    474 	gss_buffer_desc	str_buf;
    475 	char		*str;
    476 	OM_uint32	maj, min;
    477 
    478 	maj = gss_oid_to_str(&min, oid, &str_buf);
    479 
    480 	if (GSS_ERROR(maj))
    481 		return (xstrdup("<gss_oid_to_str() failed>"));
    482 
    483 	str = xmalloc(str_buf.length + 1);
    484 	memset(str, 0, str_buf.length + 1);
    485 	strlcpy(str, str_buf.value, str_buf.length + 1);
    486 	(void) gss_release_buffer(&min, &str_buf);
    487 
    488 	return (str);
    489 #else
    490 	return (xstrdup("<gss_oid_to_str() unsupported>"));
    491 #endif /* HAVE_GSS_OID_TO_STR */
    492 }
    493 
    494 /* Check that the OID in a data stream matches that in the context */
    495 int
    496 ssh_gssapi_check_mech_oid(Gssctxt *ctx, void *data, size_t len)
    497 {
    498 
    499 	return (ctx != NULL && ctx->desired_mech != GSS_C_NULL_OID &&
    500 	    ctx->desired_mech->length == len &&
    501 	    memcmp(ctx->desired_mech->elements, data, len) == 0);
    502 }
    503 
    504 /* Set the contexts OID from a data stream */
    505 void
    506 ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len)
    507 {
    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
    520 ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid)
    521 {
    522 	ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length);
    523 }
    524 
    525 /* All this effort to report an error ... */
    526 
    527 void
    528 ssh_gssapi_error(Gssctxt *ctxt, const char *where)
    529 {
    530 	char *errmsg = ssh_gssapi_last_error(ctxt, NULL, NULL);
    531 
    532 	if (where != NULL)
    533 		debug("GSS-API error while %s: %s", where, errmsg);
    534 	else
    535 		debug("GSS-API error: %s", errmsg);
    536 
    537 	/* ssh_gssapi_last_error() can't return NULL */
    538 	xfree(errmsg);
    539 }
    540 
    541 char *
    542 ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status,
    543     OM_uint32 *minor_status)
    544 {
    545 	OM_uint32 lmin, more;
    546 	OM_uint32 maj, min;
    547 	gss_OID mech = GSS_C_NULL_OID;
    548 	gss_buffer_desc msg;
    549 	Buffer b;
    550 	char *ret;
    551 
    552 	buffer_init(&b);
    553 
    554 	if (ctxt) {
    555 		/* Get status codes from the Gssctxt */
    556 		maj = ctxt->major;
    557 		min = ctxt->minor;
    558 		/* Output them if desired */
    559 		if (major_status)
    560 			*major_status = maj;
    561 		if (minor_status)
    562 			*minor_status = min;
    563 		/* Get mechanism for minor status display */
    564 		mech = (ctxt->actual_mech != GSS_C_NULL_OID) ?
    565 		    ctxt->actual_mech : ctxt->desired_mech;
    566 	} else if (major_status && minor_status) {
    567 		maj = *major_status;
    568 		min = *major_status;
    569 	} else {
    570 		maj = GSS_S_COMPLETE;
    571 		min = 0;
    572 	}
    573 
    574 	more = 0;
    575 	/* The GSSAPI error */
    576 	do {
    577 		gss_display_status(&lmin, maj, GSS_C_GSS_CODE,
    578 		    GSS_C_NULL_OID, &more, &msg);
    579 
    580 		buffer_append(&b, msg.value, msg.length);
    581 		buffer_put_char(&b, '\n');
    582 		gss_release_buffer(&lmin, &msg);
    583 	} while (more != 0);
    584 
    585 	/* The mechanism specific error */
    586 	do {
    587 		/*
    588 		 * If mech == GSS_C_NULL_OID we may get the default
    589 		 * mechanism, whatever that is, and that may not be
    590 		 * useful.
    591 		 */
    592 		gss_display_status(&lmin, min, GSS_C_MECH_CODE, mech, &more,
    593 		    &msg);
    594 
    595 		buffer_append(&b, msg.value, msg.length);
    596 		buffer_put_char(&b, '\n');
    597 
    598 		gss_release_buffer(&lmin, &msg);
    599 	} while (more != 0);
    600 
    601 	buffer_put_char(&b, '\0');
    602 	ret = xstrdup(buffer_ptr(&b));
    603 	buffer_free(&b);
    604 
    605 	return (ret);
    606 }
    607 
    608 /*
    609  * Initialise our GSSAPI context. We use this opaque structure to contain all
    610  * of the data which both the client and server need to persist across
    611  * {accept,init}_sec_context calls, so that when we do it from the userauth
    612  * stuff life is a little easier
    613  */
    614 void
    615 ssh_gssapi_build_ctx(Gssctxt **ctx, int client, gss_OID mech)
    616 {
    617 	Gssctxt *newctx;
    618 
    619 
    620 	newctx = (Gssctxt*)xmalloc(sizeof (Gssctxt));
    621 	memset(newctx, 0, sizeof (Gssctxt));
    622 
    623 
    624 	newctx->local = client;
    625 	newctx->desired_mech = ssh_gssapi_dup_oid(mech);
    626 
    627 	/* This happens to be redundant given the memset() above */
    628 	newctx->major = GSS_S_COMPLETE;
    629 	newctx->context = GSS_C_NO_CONTEXT;
    630 	newctx->actual_mech =  GSS_C_NULL_OID;
    631 	newctx->desired_name = GSS_C_NO_NAME;
    632 	newctx->src_name = GSS_C_NO_NAME;
    633 	newctx->dst_name = GSS_C_NO_NAME;
    634 	newctx->creds = GSS_C_NO_CREDENTIAL;
    635 	newctx->deleg_creds = GSS_C_NO_CREDENTIAL;
    636 
    637 	newctx->default_creds = (*ctx != NULL) ? (*ctx)->default_creds : 0;
    638 
    639 	ssh_gssapi_delete_ctx(ctx);
    640 
    641 	*ctx = newctx;
    642 }
    643 
    644 gss_OID
    645 ssh_gssapi_dup_oid(gss_OID oid)
    646 {
    647 	gss_OID new_oid;
    648 
    649 	new_oid = xmalloc(sizeof (gss_OID_desc));
    650 
    651 	new_oid->elements = xmalloc(oid->length);
    652 	new_oid->length = oid->length;
    653 	memcpy(new_oid->elements, oid->elements, oid->length);
    654 
    655 	return (new_oid);
    656 }
    657 
    658 gss_OID
    659 ssh_gssapi_make_oid(size_t length, void *elements)
    660 {
    661 	gss_OID_desc oid;
    662 
    663 	oid.length = length;
    664 	oid.elements = elements;
    665 
    666 	return (ssh_gssapi_dup_oid(&oid));
    667 }
    668 
    669 void
    670 ssh_gssapi_release_oid(gss_OID *oid)
    671 {
    672 	OM_uint32 min;
    673 
    674 	if (oid && *oid == GSS_C_NULL_OID)
    675 		return;
    676 	(void) gss_release_oid(&min, oid);
    677 
    678 	if (*oid == GSS_C_NULL_OID)
    679 		return; /* libgss did own this gss_OID and released it */
    680 
    681 	xfree((*oid)->elements);
    682 	xfree(*oid);
    683 	*oid = GSS_C_NULL_OID;
    684 }
    685 
    686 struct gss_name {
    687 	gss_OID		name_type;
    688 	gss_buffer_t	external_name;
    689 	gss_OID		mech_type;
    690 	void		*mech_name;
    691 };
    692 
    693 /* Delete our context, providing it has been built correctly */
    694 void
    695 ssh_gssapi_delete_ctx(Gssctxt **ctx)
    696 {
    697 	OM_uint32 ms;
    698 
    699 	if ((*ctx) == NULL)
    700 		return;
    701 
    702 	if ((*ctx)->context != GSS_C_NO_CONTEXT)
    703 		gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER);
    704 #if 0
    705 	/* XXX */
    706 	if ((*ctx)->desired_mech != GSS_C_NULL_OID)
    707 		ssh_gssapi_release_oid(&(*ctx)->desired_mech);
    708 #endif
    709 	if ((*ctx)->actual_mech != GSS_C_NULL_OID)
    710 		(void) ssh_gssapi_release_oid(&(*ctx)->actual_mech);
    711 	if ((*ctx)->desired_name != GSS_C_NO_NAME)
    712 		gss_release_name(&ms, &(*ctx)->desired_name);
    713 #if 0
    714 	if ((*ctx)->src_name != GSS_C_NO_NAME)
    715 		gss_release_name(&ms, &(*ctx)->src_name);
    716 #endif
    717 	if ((*ctx)->dst_name != GSS_C_NO_NAME)
    718 		gss_release_name(&ms, &(*ctx)->dst_name);
    719 	if ((*ctx)->creds != GSS_C_NO_CREDENTIAL)
    720 		gss_release_cred(&ms, &(*ctx)->creds);
    721 	if ((*ctx)->deleg_creds != GSS_C_NO_CREDENTIAL)
    722 		gss_release_cred(&ms, &(*ctx)->deleg_creds);
    723 
    724 	xfree(*ctx);
    725 	*ctx = NULL;
    726 }
    727 
    728 /* Create a GSS hostbased service principal name for a given server hostname */
    729 int
    730 ssh_gssapi_import_name(Gssctxt *ctx, const char *server_host)
    731 {
    732 	gss_buffer_desc name_buf;
    733 	int		ret;
    734 
    735 	/* Build target principal */
    736 	name_buf.length = strlen(SSH_GSS_HOSTBASED_SERVICE) +
    737 	    strlen(server_host) + 1; /* +1 for '@' */
    738 	name_buf.value = xmalloc(name_buf.length + 1); /* +1 for NUL */
    739 	ret = snprintf(name_buf.value, name_buf.length + 1, "%s@%s",
    740 	    SSH_GSS_HOSTBASED_SERVICE, server_host);
    741 
    742 	debug3("%s: snprintf() returned %d, expected %d", __func__, ret,
    743 	    name_buf.length + 1);
    744 
    745 	ctx->major = gss_import_name(&ctx->minor, &name_buf,
    746 	    GSS_C_NT_HOSTBASED_SERVICE, &ctx->desired_name);
    747 
    748 	if (GSS_ERROR(ctx->major)) {
    749 		ssh_gssapi_error(ctx, "calling GSS_Import_name()");
    750 		return (0);
    751 	}
    752 
    753 	xfree(name_buf.value);
    754 
    755 	return (1);
    756 }
    757 
    758 OM_uint32
    759 ssh_gssapi_get_mic(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash)
    760 {
    761 
    762 	ctx->major = gss_get_mic(&ctx->minor, ctx->context,
    763 	    GSS_C_QOP_DEFAULT, buffer, hash);
    764 	if (GSS_ERROR(ctx->major))
    765 		ssh_gssapi_error(ctx, "while getting MIC");
    766 	return (ctx->major);
    767 }
    768 
    769 OM_uint32
    770 ssh_gssapi_verify_mic(Gssctxt *ctx, gss_buffer_desc *buffer,
    771     gss_buffer_desc *hash)
    772 {
    773 	gss_qop_t qop;
    774 
    775 	ctx->major = gss_verify_mic(&ctx->minor, ctx->context, buffer,
    776 	    hash, &qop);
    777 	if (GSS_ERROR(ctx->major))
    778 		ssh_gssapi_error(ctx, "while verifying MIC");
    779 	return (ctx->major);
    780 }
    781 #endif /* GSSAPI */
    782