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 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
     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
    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
    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
    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 *
    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
    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
    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
    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
    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
    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 *
    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 *
    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
    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
    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
    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
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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