1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. *
3*0Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
4*0Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
5*0Sstevel@tonic-gate  * are met:
6*0Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
7*0Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
8*0Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
9*0Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
10*0Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
11*0Sstevel@tonic-gate  *
12*0Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
13*0Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
14*0Sstevel@tonic-gate  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
15*0Sstevel@tonic-gate  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
16*0Sstevel@tonic-gate  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
17*0Sstevel@tonic-gate  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
18*0Sstevel@tonic-gate  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
19*0Sstevel@tonic-gate  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20*0Sstevel@tonic-gate  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
21*0Sstevel@tonic-gate  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22*0Sstevel@tonic-gate  */
23*0Sstevel@tonic-gate /*
24*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
25*0Sstevel@tonic-gate  * Use is subject to license terms.
26*0Sstevel@tonic-gate  */
27*0Sstevel@tonic-gate 
28*0Sstevel@tonic-gate #include "includes.h"
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate #ifdef GSSAPI
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate #include "ssh.h"
35*0Sstevel@tonic-gate #include "ssh2.h"
36*0Sstevel@tonic-gate #include "xmalloc.h"
37*0Sstevel@tonic-gate #include "buffer.h"
38*0Sstevel@tonic-gate #include "bufaux.h"
39*0Sstevel@tonic-gate #include "packet.h"
40*0Sstevel@tonic-gate #include "compat.h"
41*0Sstevel@tonic-gate #include <openssl/evp.h>
42*0Sstevel@tonic-gate #include "cipher.h"
43*0Sstevel@tonic-gate #include "kex.h"
44*0Sstevel@tonic-gate #include "log.h"
45*0Sstevel@tonic-gate #include "compat.h"
46*0Sstevel@tonic-gate #include "xlist.h"
47*0Sstevel@tonic-gate #include "monitor_wrap.h"
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate #include <netdb.h>
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate #include "ssh-gss.h"
52*0Sstevel@tonic-gate 
53*0Sstevel@tonic-gate #ifdef HAVE_GSS_OID_TO_MECH
54*0Sstevel@tonic-gate #include <gssapi/gssapi_ext.h>
55*0Sstevel@tonic-gate #endif /* HAVE_GSS_OID_TO_MECH */
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate typedef struct {
58*0Sstevel@tonic-gate 	char *encoded;
59*0Sstevel@tonic-gate 	gss_OID oid;
60*0Sstevel@tonic-gate } ssh_gss_kex_mapping;
61*0Sstevel@tonic-gate 
62*0Sstevel@tonic-gate static ssh_gss_kex_mapping **gss_enc2oid = NULL;
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate static void ssh_gssapi_encode_oid_for_kex(const gss_OID oid, char **enc_name);
65*0Sstevel@tonic-gate static char *ssh_gssapi_make_kexalgs_list(gss_OID_set mechs,
66*0Sstevel@tonic-gate 					  const char *old_kexalgs);
67*0Sstevel@tonic-gate 
68*0Sstevel@tonic-gate /*
69*0Sstevel@tonic-gate  * Populate gss_enc2oid table and return list of kexnames.
70*0Sstevel@tonic-gate  *
71*0Sstevel@tonic-gate  * If called with both mechs == GSS_C_NULL_OID_SET and kexname_list == NULL
72*0Sstevel@tonic-gate  * then cached gss_enc2oid table is cleaned up.
73*0Sstevel@tonic-gate  */
74*0Sstevel@tonic-gate void
75*0Sstevel@tonic-gate ssh_gssapi_mech_oids_to_kexnames(const gss_OID_set mechs, char **kexname_list)
76*0Sstevel@tonic-gate {
77*0Sstevel@tonic-gate 	ssh_gss_kex_mapping **new_gss_enc2oid, **p;
78*0Sstevel@tonic-gate 	Buffer buf;
79*0Sstevel@tonic-gate 	char *enc_name;
80*0Sstevel@tonic-gate 	int i;
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate 	if (kexname_list != NULL)
83*0Sstevel@tonic-gate 		*kexname_list = NULL; /* default to failed */
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate 	if (mechs != GSS_C_NULL_OID_SET || kexname_list == NULL) {
86*0Sstevel@tonic-gate 		/* Cleanup gss_enc2oid table */
87*0Sstevel@tonic-gate 		for (p = gss_enc2oid ; p != NULL && *p != NULL ; p++) {
88*0Sstevel@tonic-gate 			if ((*p)->encoded)
89*0Sstevel@tonic-gate 				xfree((*p)->encoded);
90*0Sstevel@tonic-gate 			ssh_gssapi_release_oid(&(*p)->oid);
91*0Sstevel@tonic-gate 			xfree(*p);
92*0Sstevel@tonic-gate 		}
93*0Sstevel@tonic-gate 		if (gss_enc2oid)
94*0Sstevel@tonic-gate 			xfree(gss_enc2oid);
95*0Sstevel@tonic-gate 	}
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate 	if (mechs == GSS_C_NULL_OID_SET && kexname_list == NULL)
98*0Sstevel@tonic-gate 		return; /* nothing left to do */
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate 	if (mechs) {
101*0Sstevel@tonic-gate 		gss_OID mech;
102*0Sstevel@tonic-gate 		/* Populate gss_enc2oid table */
103*0Sstevel@tonic-gate 		new_gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping *) *
104*0Sstevel@tonic-gate 				(mechs->count + 1));
105*0Sstevel@tonic-gate 		memset(new_gss_enc2oid, 0,
106*0Sstevel@tonic-gate 			sizeof(ssh_gss_kex_mapping *) * (mechs->count + 1));
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate 		for (i = 0 ; i < mechs->count ; i++) {
109*0Sstevel@tonic-gate 			mech = &mechs->elements[i];
110*0Sstevel@tonic-gate 			ssh_gssapi_encode_oid_for_kex((const gss_OID)mech,
111*0Sstevel@tonic-gate 				&enc_name);
112*0Sstevel@tonic-gate 
113*0Sstevel@tonic-gate 			if (!enc_name)
114*0Sstevel@tonic-gate 				continue;
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate 			new_gss_enc2oid[i] =
117*0Sstevel@tonic-gate 				xmalloc(sizeof(ssh_gss_kex_mapping));
118*0Sstevel@tonic-gate 			(new_gss_enc2oid[i])->encoded = enc_name;
119*0Sstevel@tonic-gate 			(new_gss_enc2oid[i])->oid =
120*0Sstevel@tonic-gate 				ssh_gssapi_dup_oid(&mechs->elements[i]);
121*0Sstevel@tonic-gate 		}
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate 		/* Do this last to avoid run-ins with fatal_cleanups */
124*0Sstevel@tonic-gate 		gss_enc2oid = new_gss_enc2oid;
125*0Sstevel@tonic-gate 	}
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate 	if (!kexname_list)
128*0Sstevel@tonic-gate 		return; /* nothing left to do */
129*0Sstevel@tonic-gate 
130*0Sstevel@tonic-gate 	/* Make kex name list */
131*0Sstevel@tonic-gate 	buffer_init(&buf);
132*0Sstevel@tonic-gate 	for (p = gss_enc2oid ; p && *p ; p++) {
133*0Sstevel@tonic-gate 		buffer_put_char(&buf,',');
134*0Sstevel@tonic-gate 		buffer_append(&buf, (*p)->encoded, strlen((*p)->encoded));
135*0Sstevel@tonic-gate 	}
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	if (buffer_len(&buf) == 0) {
138*0Sstevel@tonic-gate 		buffer_free(&buf);
139*0Sstevel@tonic-gate 		return;
140*0Sstevel@tonic-gate 	}
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate 	buffer_consume(&buf, 1); /* consume leading ',' */
143*0Sstevel@tonic-gate 	buffer_put_char(&buf,'\0');
144*0Sstevel@tonic-gate 
145*0Sstevel@tonic-gate 	*kexname_list = xstrdup(buffer_ptr(&buf));
146*0Sstevel@tonic-gate 	buffer_free(&buf);
147*0Sstevel@tonic-gate }
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate void
150*0Sstevel@tonic-gate ssh_gssapi_mech_oid_to_kexname(const gss_OID mech, char **kexname)
151*0Sstevel@tonic-gate {
152*0Sstevel@tonic-gate 	ssh_gss_kex_mapping **p;
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	if (mech == GSS_C_NULL_OID || !kexname)
155*0Sstevel@tonic-gate 		return;
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate 	*kexname = NULL; /* default to not found */
158*0Sstevel@tonic-gate 	if (gss_enc2oid) {
159*0Sstevel@tonic-gate 		for (p = gss_enc2oid ; p && *p ; p++) {
160*0Sstevel@tonic-gate 			if (mech->length == (*p)->oid->length &&
161*0Sstevel@tonic-gate 			    memcmp(mech->elements, (*p)->oid->elements,
162*0Sstevel@tonic-gate 					mech->length) == 0)
163*0Sstevel@tonic-gate 				*kexname = xstrdup((*p)->encoded);
164*0Sstevel@tonic-gate 		}
165*0Sstevel@tonic-gate 	}
166*0Sstevel@tonic-gate 
167*0Sstevel@tonic-gate 	if (*kexname)
168*0Sstevel@tonic-gate 		return; /* found */
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 	ssh_gssapi_encode_oid_for_kex(mech, kexname);
171*0Sstevel@tonic-gate }
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate void
174*0Sstevel@tonic-gate ssh_gssapi_oid_of_kexname(const char *kexname, gss_OID *mech)
175*0Sstevel@tonic-gate {
176*0Sstevel@tonic-gate 	ssh_gss_kex_mapping **p;
177*0Sstevel@tonic-gate 
178*0Sstevel@tonic-gate 	if (!mech || !kexname || !*kexname)
179*0Sstevel@tonic-gate 		return;
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate 	*mech = GSS_C_NULL_OID; /* default to not found */
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate 	if (!gss_enc2oid)
184*0Sstevel@tonic-gate 		return;
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	for (p = gss_enc2oid ; p && *p ; p++) {
187*0Sstevel@tonic-gate 		if (strcmp(kexname, (*p)->encoded) == 0) {
188*0Sstevel@tonic-gate 			*mech = (*p)->oid;
189*0Sstevel@tonic-gate 			return;
190*0Sstevel@tonic-gate 		}
191*0Sstevel@tonic-gate 	}
192*0Sstevel@tonic-gate }
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate static
195*0Sstevel@tonic-gate void
196*0Sstevel@tonic-gate ssh_gssapi_encode_oid_for_kex(const gss_OID oid, char **enc_name)
197*0Sstevel@tonic-gate {
198*0Sstevel@tonic-gate 	Buffer		buf;
199*0Sstevel@tonic-gate 	OM_uint32	oidlen;
200*0Sstevel@tonic-gate 	u_int		enclen;
201*0Sstevel@tonic-gate 	const EVP_MD    *evp_md = EVP_md5();
202*0Sstevel@tonic-gate 	EVP_MD_CTX	md;
203*0Sstevel@tonic-gate 	u_char          digest[EVP_MAX_MD_SIZE];
204*0Sstevel@tonic-gate 	char		*encoded;
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate 	if (oid == GSS_C_NULL_OID || !enc_name)
207*0Sstevel@tonic-gate 		return;
208*0Sstevel@tonic-gate 
209*0Sstevel@tonic-gate 	*enc_name = NULL;
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate 	oidlen = oid->length;
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate 	/* No GSS mechs have OIDs as long as 128 -- simplify DER encoding */
214*0Sstevel@tonic-gate 	if (oidlen > 128)
215*0Sstevel@tonic-gate 		return; /* fail gracefully */
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	/*
218*0Sstevel@tonic-gate 	 * NOTE:  If we need to support SSH_BUG_GSSAPI_BER this is where
219*0Sstevel@tonic-gate 	 * we'd do it.
220*0Sstevel@tonic-gate 	 *
221*0Sstevel@tonic-gate 	 * That means using "Se3H81ismmOC3OE+FwYCiQ==" for the Kerberos
222*0Sstevel@tonic-gate 	 * V mech and "N3+k7/4wGxHyuP8Yxi4RhA==" for the GSI mech.  Ick.
223*0Sstevel@tonic-gate 	 */
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate 	buffer_init(&buf);
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate 	/* UNIVERSAL class tag for OBJECT IDENTIFIER */
228*0Sstevel@tonic-gate 	buffer_put_char(&buf, 0x06);
229*0Sstevel@tonic-gate 	buffer_put_char(&buf, oidlen); /* one octet DER length -- see above */
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	/* OID elements */
232*0Sstevel@tonic-gate 	buffer_append(&buf, oid->elements, oidlen);
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 	/* Make digest */
235*0Sstevel@tonic-gate 	EVP_DigestInit(&md, evp_md);
236*0Sstevel@tonic-gate 	EVP_DigestUpdate(&md, buffer_ptr(&buf), buffer_len(&buf));
237*0Sstevel@tonic-gate 	EVP_DigestFinal(&md, digest, NULL);
238*0Sstevel@tonic-gate 	buffer_free(&buf);
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	/* Base 64 encoding */
241*0Sstevel@tonic-gate 	encoded=xmalloc(EVP_MD_size(evp_md)*2);
242*0Sstevel@tonic-gate 	enclen=__b64_ntop(digest, EVP_MD_size(evp_md),
243*0Sstevel@tonic-gate 			    encoded,EVP_MD_size(evp_md)*2);
244*0Sstevel@tonic-gate 	buffer_init(&buf);
245*0Sstevel@tonic-gate 	buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1);
246*0Sstevel@tonic-gate 	buffer_append(&buf, encoded, enclen);
247*0Sstevel@tonic-gate 	buffer_put_char(&buf, '\0');
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	debug2("GSS-API Mechanism encoded as %s",encoded);
250*0Sstevel@tonic-gate 
251*0Sstevel@tonic-gate 	*enc_name = xstrdup(buffer_ptr(&buf));
252*0Sstevel@tonic-gate 	buffer_free(&buf);
253*0Sstevel@tonic-gate }
254*0Sstevel@tonic-gate 
255*0Sstevel@tonic-gate static
256*0Sstevel@tonic-gate char *
257*0Sstevel@tonic-gate ssh_gssapi_make_kexalgs_list(gss_OID_set mechs, const char *old_kexalgs)
258*0Sstevel@tonic-gate {
259*0Sstevel@tonic-gate 	char *gss_kexalgs, *new_kexalgs;
260*0Sstevel@tonic-gate 	int len;
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate 	if (mechs == GSS_C_NULL_OID_SET)
263*0Sstevel@tonic-gate 		return (xstrdup(old_kexalgs)); /* never null */
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate 	ssh_gssapi_mech_oids_to_kexnames(mechs, &gss_kexalgs);
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 	if (gss_kexalgs == NULL || *gss_kexalgs == '\0')
268*0Sstevel@tonic-gate 		return (xstrdup(old_kexalgs)); /* never null */
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	if (old_kexalgs == NULL || *old_kexalgs == '\0')
271*0Sstevel@tonic-gate 		return (gss_kexalgs);
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate 	len = strlen(old_kexalgs) + strlen(gss_kexalgs) + 2;
274*0Sstevel@tonic-gate 	new_kexalgs = xmalloc(len);
275*0Sstevel@tonic-gate 	(void) snprintf(new_kexalgs, len, "%s,%s", gss_kexalgs, old_kexalgs);
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 	return (new_kexalgs);
278*0Sstevel@tonic-gate }
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate void
281*0Sstevel@tonic-gate ssh_gssapi_modify_kex(Kex *kex, gss_OID_set mechs, char **proposal)
282*0Sstevel@tonic-gate {
283*0Sstevel@tonic-gate 	char *kexalgs, *orig_kexalgs, *p;
284*0Sstevel@tonic-gate 	char **hostalg, *orig_hostalgs, *new_hostalgs;
285*0Sstevel@tonic-gate 	char **hostalgs;
286*0Sstevel@tonic-gate 	gss_OID_set dup_mechs;
287*0Sstevel@tonic-gate 	OM_uint32 maj, min;
288*0Sstevel@tonic-gate 	int i;
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate 	if (kex == NULL || proposal == NULL ||
291*0Sstevel@tonic-gate 	    (orig_kexalgs = proposal[PROPOSAL_KEX_ALGS]) == NULL) {
292*0Sstevel@tonic-gate 		fatal("INTERNAL ERROR (%s)", __func__);
293*0Sstevel@tonic-gate 	}
294*0Sstevel@tonic-gate 
295*0Sstevel@tonic-gate 	orig_hostalgs = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate 	if (kex->mechs == GSS_C_NULL_OID_SET && mechs == GSS_C_NULL_OID_SET)
298*0Sstevel@tonic-gate 		return; /* didn't offer GSS last time, not offering now */
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate 	if (kex->mechs == GSS_C_NULL_OID_SET || mechs == GSS_C_NULL_OID_SET)
301*0Sstevel@tonic-gate 		goto mod_offer; /* didn't offer last time or not offering now */
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	/* Check if mechs is congruent to kex->mechs (last offered) */
304*0Sstevel@tonic-gate 	if (kex->mechs->count == mechs->count) {
305*0Sstevel@tonic-gate 		int present, matches = 0;
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 		for ( i = 0 ; i < mechs->count ; i++ ) {
308*0Sstevel@tonic-gate 			maj = gss_test_oid_set_member(&min,
309*0Sstevel@tonic-gate 				&kex->mechs->elements[i], mechs,
310*0Sstevel@tonic-gate 				&present);
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 			if (GSS_ERROR(maj)) {
313*0Sstevel@tonic-gate 				mechs = GSS_C_NULL_OID_SET;
314*0Sstevel@tonic-gate 				break;
315*0Sstevel@tonic-gate 			}
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate 			matches += (present) ? 1 : 0;
318*0Sstevel@tonic-gate 		}
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate 		if (matches == kex->mechs->count)
321*0Sstevel@tonic-gate 			return; /* no change in offer from last time */
322*0Sstevel@tonic-gate 	}
323*0Sstevel@tonic-gate 
324*0Sstevel@tonic-gate mod_offer:
325*0Sstevel@tonic-gate 	/*
326*0Sstevel@tonic-gate 	 * Remove previously offered mechs from PROPOSAL_KEX_ALGS proposal
327*0Sstevel@tonic-gate 	 *
328*0Sstevel@tonic-gate 	 * ASSUMPTION: GSS-API kex algs always go in front, so removing
329*0Sstevel@tonic-gate 	 * them is a matter of skipping them.
330*0Sstevel@tonic-gate 	 */
331*0Sstevel@tonic-gate 	p = kexalgs = orig_kexalgs = proposal[PROPOSAL_KEX_ALGS];
332*0Sstevel@tonic-gate 	while (p != NULL && *p != '\0' &&
333*0Sstevel@tonic-gate 		strncmp(p, KEX_GSS_SHA1, strlen(KEX_GSS_SHA1)) == 0) {
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate 		if ((p = strchr(p, ',')) == NULL)
336*0Sstevel@tonic-gate 			break;
337*0Sstevel@tonic-gate 		p++;
338*0Sstevel@tonic-gate 		kexalgs = p;
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 	}
341*0Sstevel@tonic-gate 	kexalgs = proposal[PROPOSAL_KEX_ALGS] = xstrdup(kexalgs);
342*0Sstevel@tonic-gate 	xfree(orig_kexalgs);
343*0Sstevel@tonic-gate 
344*0Sstevel@tonic-gate 	(void) gss_release_oid_set(&min, &kex->mechs); /* ok if !kex->mechs */
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 	/* Not offering GSS kexalgs now -> all done */
347*0Sstevel@tonic-gate 	if (mechs == GSS_C_NULL_OID_SET)
348*0Sstevel@tonic-gate 		return;
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	/* Remember mechs we're offering */
351*0Sstevel@tonic-gate 	maj = gss_create_empty_oid_set(&min, &dup_mechs);
352*0Sstevel@tonic-gate 	if (GSS_ERROR(maj))
353*0Sstevel@tonic-gate 		return;
354*0Sstevel@tonic-gate 	for ( i = 0 ; i < mechs->count ; i++ ) {
355*0Sstevel@tonic-gate 		maj = gss_add_oid_set_member(&min, &mechs->elements[i],
356*0Sstevel@tonic-gate 			&dup_mechs);
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 		if (GSS_ERROR(maj)) {
359*0Sstevel@tonic-gate 			(void) gss_release_oid_set(&min, &dup_mechs);
360*0Sstevel@tonic-gate 			return;
361*0Sstevel@tonic-gate 		}
362*0Sstevel@tonic-gate 	}
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 	/* Add mechs to kexalgs ... */
365*0Sstevel@tonic-gate 	proposal[PROPOSAL_KEX_ALGS] = ssh_gssapi_make_kexalgs_list(mechs, kexalgs);
366*0Sstevel@tonic-gate 	kex->mechs = dup_mechs; /* remember what we offer now */
367*0Sstevel@tonic-gate 
368*0Sstevel@tonic-gate 	/*
369*0Sstevel@tonic-gate 	 * ... and add null host key alg, if it wasn't there before, but
370*0Sstevel@tonic-gate 	 * not if we're the server and we have other host key algs to
371*0Sstevel@tonic-gate 	 * offer.
372*0Sstevel@tonic-gate 	 *
373*0Sstevel@tonic-gate 	 * NOTE: Never remove "null" host key alg once added.
374*0Sstevel@tonic-gate 	 */
375*0Sstevel@tonic-gate 	if (orig_hostalgs == NULL || *orig_hostalgs == '\0') {
376*0Sstevel@tonic-gate 		proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = xstrdup("null");
377*0Sstevel@tonic-gate 	} else if (!kex->server) {
378*0Sstevel@tonic-gate 		hostalgs = xsplit(orig_hostalgs, ',');
379*0Sstevel@tonic-gate 		for ( hostalg = hostalgs ; *hostalg != NULL ; hostalg++ ) {
380*0Sstevel@tonic-gate 			if (strcmp(*hostalg, "null") == 0) {
381*0Sstevel@tonic-gate 				xfree_split_list(hostalgs);
382*0Sstevel@tonic-gate 				return;
383*0Sstevel@tonic-gate 			}
384*0Sstevel@tonic-gate 		}
385*0Sstevel@tonic-gate 		xfree_split_list(hostalgs);
386*0Sstevel@tonic-gate 
387*0Sstevel@tonic-gate 		if (kex->mechs != GSS_C_NULL_OID_SET) {
388*0Sstevel@tonic-gate 			int len;
389*0Sstevel@tonic-gate 
390*0Sstevel@tonic-gate 			len = strlen(orig_hostalgs) + sizeof(",null");
391*0Sstevel@tonic-gate 			new_hostalgs = xmalloc(len);
392*0Sstevel@tonic-gate 			(void) snprintf(new_hostalgs, len, "%s,null",
393*0Sstevel@tonic-gate 					orig_hostalgs);
394*0Sstevel@tonic-gate 			proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = new_hostalgs;
395*0Sstevel@tonic-gate 		}
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 		xfree(orig_hostalgs);
398*0Sstevel@tonic-gate 	}
399*0Sstevel@tonic-gate }
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate /*
402*0Sstevel@tonic-gate  * Yes, we harcode OIDs for some things, for now it's all we can do.
403*0Sstevel@tonic-gate  *
404*0Sstevel@tonic-gate  * We have to reference particular mechanisms due to lack of generality
405*0Sstevel@tonic-gate  * in the GSS-API in several areas: authorization, mapping principal
406*0Sstevel@tonic-gate  * names to usernames, "storing" delegated credentials, and discovering
407*0Sstevel@tonic-gate  * whether a mechanism is a pseudo-mechanism that negotiates mechanisms.
408*0Sstevel@tonic-gate  *
409*0Sstevel@tonic-gate  * Even if they were in some header file or if __gss_mech_to_oid()
410*0Sstevel@tonic-gate  * and/or __gss_oid_to_mech() were standard we'd still have to hardcode
411*0Sstevel@tonic-gate  * the mechanism names, and since the mechanisms have no standard names
412*0Sstevel@tonic-gate  * other than their OIDs it's actually worse [less portable] to hardcode
413*0Sstevel@tonic-gate  * names than OIDs, so we hardcode OIDs.
414*0Sstevel@tonic-gate  *
415*0Sstevel@tonic-gate  * SPNEGO is a difficult problem though -- it MUST NOT be used in SSHv2,
416*0Sstevel@tonic-gate  * but that's true of all possible pseudo-mechanisms that can perform
417*0Sstevel@tonic-gate  * mechanism negotiation, and SPNEGO could have new OIDs in the future.
418*0Sstevel@tonic-gate  * Ideally we could query each mechanism for its feature set and then
419*0Sstevel@tonic-gate  * ignore any mechanisms that negotiate mechanisms, but, alas, there's
420*0Sstevel@tonic-gate  * no interface to do that.
421*0Sstevel@tonic-gate  *
422*0Sstevel@tonic-gate  * In the future, if the necessary generic GSS interfaces for the issues
423*0Sstevel@tonic-gate  * listed above are made available (even if they differ by platform, as
424*0Sstevel@tonic-gate  * we can expect authorization interfaces will), then we can stop
425*0Sstevel@tonic-gate  * referencing specific mechanism OIDs here.
426*0Sstevel@tonic-gate  */
427*0Sstevel@tonic-gate int
428*0Sstevel@tonic-gate ssh_gssapi_is_spnego(gss_OID oid)
429*0Sstevel@tonic-gate {
430*0Sstevel@tonic-gate 	return (oid->length == 6 &&
431*0Sstevel@tonic-gate 		memcmp("\053\006\001\005\005\002",
432*0Sstevel@tonic-gate 			    oid->elements, 6) == 0);
433*0Sstevel@tonic-gate }
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate int
436*0Sstevel@tonic-gate ssh_gssapi_is_krb5(gss_OID oid)
437*0Sstevel@tonic-gate {
438*0Sstevel@tonic-gate 	return (oid->length == 9 &&
439*0Sstevel@tonic-gate 		memcmp("\x2A\x86\x48\x86\xF7\x12\x01\x02\x02",
440*0Sstevel@tonic-gate 			    oid->elements, 9) == 0);
441*0Sstevel@tonic-gate }
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate int
444*0Sstevel@tonic-gate ssh_gssapi_is_dh(gss_OID oid)
445*0Sstevel@tonic-gate {
446*0Sstevel@tonic-gate 	return (oid->length == 9 &&
447*0Sstevel@tonic-gate 		memcmp("\053\006\004\001\052\002\032\002\005",
448*0Sstevel@tonic-gate 			    oid->elements, 9) == 0);
449*0Sstevel@tonic-gate }
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate int
452*0Sstevel@tonic-gate ssh_gssapi_is_gsi(gss_OID oid)
453*0Sstevel@tonic-gate {
454*0Sstevel@tonic-gate 	return (oid->length == 9 &&
455*0Sstevel@tonic-gate 		memcmp("\x2B\x06\x01\x04\x01\x9B\x50\x01\x01",
456*0Sstevel@tonic-gate 			    oid->elements, 9) == 0);
457*0Sstevel@tonic-gate }
458*0Sstevel@tonic-gate 
459*0Sstevel@tonic-gate const
460*0Sstevel@tonic-gate char *
461*0Sstevel@tonic-gate ssh_gssapi_oid_to_name(gss_OID oid)
462*0Sstevel@tonic-gate {
463*0Sstevel@tonic-gate #ifdef HAVE_GSS_OID_TO_MECH
464*0Sstevel@tonic-gate 	return __gss_oid_to_mech(oid);
465*0Sstevel@tonic-gate #else
466*0Sstevel@tonic-gate 	if (ssh_gssapi_is_krb5(oid))
467*0Sstevel@tonic-gate 		return "Kerberos";
468*0Sstevel@tonic-gate 	if (ssh_gssapi_is_gsi(oid))
469*0Sstevel@tonic-gate 		return "GSI";
470*0Sstevel@tonic-gate 	return "(unknown)";
471*0Sstevel@tonic-gate #endif /* HAVE_GSS_OID_TO_MECH */
472*0Sstevel@tonic-gate }
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate char *
475*0Sstevel@tonic-gate ssh_gssapi_oid_to_str(gss_OID oid)
476*0Sstevel@tonic-gate {
477*0Sstevel@tonic-gate #ifdef HAVE_GSS_OID_TO_STR
478*0Sstevel@tonic-gate 	gss_buffer_desc	str_buf;
479*0Sstevel@tonic-gate 	char		*str;
480*0Sstevel@tonic-gate 	OM_uint32	maj, min;
481*0Sstevel@tonic-gate 
482*0Sstevel@tonic-gate 	maj = gss_oid_to_str(&min, oid, &str_buf);
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 	if (GSS_ERROR(maj))
485*0Sstevel@tonic-gate 		return xstrdup("<gss_oid_to_str() failed>");
486*0Sstevel@tonic-gate 
487*0Sstevel@tonic-gate 	str = xmalloc(str_buf.length + 1);
488*0Sstevel@tonic-gate 	memset(str, 0, str_buf.length + 1);
489*0Sstevel@tonic-gate 	strlcpy(str, str_buf.value, str_buf.length + 1);
490*0Sstevel@tonic-gate 	(void) gss_release_buffer(&min, &str_buf);
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate 	return str;
493*0Sstevel@tonic-gate #else
494*0Sstevel@tonic-gate 	return xstrdup("<gss_oid_to_str() unsupported>");
495*0Sstevel@tonic-gate #endif /* HAVE_GSS_OID_TO_STR */
496*0Sstevel@tonic-gate }
497*0Sstevel@tonic-gate 
498*0Sstevel@tonic-gate /* Check that the OID in a data stream matches that in the context */
499*0Sstevel@tonic-gate int ssh_gssapi_check_mech_oid(Gssctxt *ctx, void *data, size_t len) {
500*0Sstevel@tonic-gate 
501*0Sstevel@tonic-gate   return (ctx!=NULL && ctx->desired_mech != GSS_C_NULL_OID &&
502*0Sstevel@tonic-gate   	  ctx->desired_mech->length == len &&
503*0Sstevel@tonic-gate   	  memcmp(ctx->desired_mech->elements,data,len)==0);
504*0Sstevel@tonic-gate }
505*0Sstevel@tonic-gate 
506*0Sstevel@tonic-gate /* Set the contexts OID from a data stream */
507*0Sstevel@tonic-gate void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) {
508*0Sstevel@tonic-gate   if (ctx->actual_mech != GSS_C_NULL_OID) {
509*0Sstevel@tonic-gate 	xfree(ctx->actual_mech->elements);
510*0Sstevel@tonic-gate    	xfree(ctx->actual_mech);
511*0Sstevel@tonic-gate   }
512*0Sstevel@tonic-gate   ctx->actual_mech=xmalloc(sizeof(gss_OID_desc));
513*0Sstevel@tonic-gate   ctx->actual_mech->length=len;
514*0Sstevel@tonic-gate   ctx->actual_mech->elements=xmalloc(len);
515*0Sstevel@tonic-gate   memcpy(ctx->actual_mech->elements,data,len);
516*0Sstevel@tonic-gate }
517*0Sstevel@tonic-gate 
518*0Sstevel@tonic-gate /* Set the contexts OID */
519*0Sstevel@tonic-gate void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) {
520*0Sstevel@tonic-gate   ssh_gssapi_set_oid_data(ctx,oid->elements,oid->length);
521*0Sstevel@tonic-gate }
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate /* All this effort to report an error ... */
524*0Sstevel@tonic-gate 
525*0Sstevel@tonic-gate void
526*0Sstevel@tonic-gate ssh_gssapi_error(Gssctxt *ctxt, const char *where) {
527*0Sstevel@tonic-gate 	if (where)
528*0Sstevel@tonic-gate 		debug("GSS-API error while %s: %s", where,
529*0Sstevel@tonic-gate 			ssh_gssapi_last_error(ctxt,NULL,NULL));
530*0Sstevel@tonic-gate 	else
531*0Sstevel@tonic-gate 		debug("GSS-API error: %s",
532*0Sstevel@tonic-gate 			ssh_gssapi_last_error(ctxt,NULL,NULL));
533*0Sstevel@tonic-gate }
534*0Sstevel@tonic-gate 
535*0Sstevel@tonic-gate char *
536*0Sstevel@tonic-gate ssh_gssapi_last_error(Gssctxt *ctxt,
537*0Sstevel@tonic-gate 		      OM_uint32 *major_status, OM_uint32 *minor_status) {
538*0Sstevel@tonic-gate 	OM_uint32 lmin, more;
539*0Sstevel@tonic-gate 	OM_uint32 maj, min;
540*0Sstevel@tonic-gate 	gss_OID mech = GSS_C_NULL_OID;
541*0Sstevel@tonic-gate         gss_buffer_desc msg;
542*0Sstevel@tonic-gate         Buffer b;
543*0Sstevel@tonic-gate         char *ret;
544*0Sstevel@tonic-gate 
545*0Sstevel@tonic-gate         buffer_init(&b);
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 	if (ctxt) {
548*0Sstevel@tonic-gate 		/* Get status codes from the Gssctxt */
549*0Sstevel@tonic-gate 		maj = ctxt->major;
550*0Sstevel@tonic-gate 		min = ctxt->minor;
551*0Sstevel@tonic-gate 		/* Output them if desired */
552*0Sstevel@tonic-gate 		if (major_status)
553*0Sstevel@tonic-gate 			*major_status = maj;
554*0Sstevel@tonic-gate 		if (minor_status)
555*0Sstevel@tonic-gate 			*minor_status = min;
556*0Sstevel@tonic-gate 		/* Get mechanism for minor status display */
557*0Sstevel@tonic-gate 		mech = (ctxt->actual_mech != GSS_C_NULL_OID) ?
558*0Sstevel@tonic-gate 			    ctxt->actual_mech : ctxt->desired_mech;
559*0Sstevel@tonic-gate 	} else if (major_status && minor_status) {
560*0Sstevel@tonic-gate 		maj = *major_status;
561*0Sstevel@tonic-gate 		min = *major_status;
562*0Sstevel@tonic-gate 	} else {
563*0Sstevel@tonic-gate 		maj = GSS_S_COMPLETE;
564*0Sstevel@tonic-gate 		min = 0;
565*0Sstevel@tonic-gate 	}
566*0Sstevel@tonic-gate 
567*0Sstevel@tonic-gate         more = 0;
568*0Sstevel@tonic-gate 	/* The GSSAPI error */
569*0Sstevel@tonic-gate         do {
570*0Sstevel@tonic-gate         	gss_display_status(&lmin, maj,
571*0Sstevel@tonic-gate         			   GSS_C_GSS_CODE, GSS_C_NULL_OID,
572*0Sstevel@tonic-gate         		           &more, &msg);
573*0Sstevel@tonic-gate 
574*0Sstevel@tonic-gate         	buffer_append(&b,msg.value,msg.length);
575*0Sstevel@tonic-gate         	buffer_put_char(&b,'\n');
576*0Sstevel@tonic-gate        	    	gss_release_buffer(&lmin, &msg);
577*0Sstevel@tonic-gate         } while (more!=0);
578*0Sstevel@tonic-gate 
579*0Sstevel@tonic-gate         /* The mechanism specific error */
580*0Sstevel@tonic-gate         do {
581*0Sstevel@tonic-gate 		/*
582*0Sstevel@tonic-gate 		 * If mech == GSS_C_NULL_OID we may get the default
583*0Sstevel@tonic-gate 		 * mechanism, whatever that is, and that may not be
584*0Sstevel@tonic-gate 		 * useful.
585*0Sstevel@tonic-gate 		 */
586*0Sstevel@tonic-gate         	gss_display_status(&lmin, min,
587*0Sstevel@tonic-gate         			   GSS_C_MECH_CODE, mech,
588*0Sstevel@tonic-gate         			   &more, &msg);
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate         	buffer_append(&b,msg.value,msg.length);
591*0Sstevel@tonic-gate         	buffer_put_char(&b,'\n');
592*0Sstevel@tonic-gate 
593*0Sstevel@tonic-gate         	gss_release_buffer(&lmin, &msg);
594*0Sstevel@tonic-gate         } while (more!=0);
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate         buffer_put_char(&b,'\0');
597*0Sstevel@tonic-gate         ret=xstrdup(buffer_ptr(&b));
598*0Sstevel@tonic-gate         buffer_free(&b);
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate         return (ret);
601*0Sstevel@tonic-gate }
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate /* Initialise our GSSAPI context. We use this opaque structure to contain all
604*0Sstevel@tonic-gate  * of the data which both the client and server need to persist across
605*0Sstevel@tonic-gate  * {accept,init}_sec_context calls, so that when we do it from the userauth
606*0Sstevel@tonic-gate  * stuff life is a little easier
607*0Sstevel@tonic-gate  */
608*0Sstevel@tonic-gate void
609*0Sstevel@tonic-gate ssh_gssapi_build_ctx(Gssctxt **ctx, int client, gss_OID mech)
610*0Sstevel@tonic-gate {
611*0Sstevel@tonic-gate 	Gssctxt *newctx;
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 	ssh_gssapi_delete_ctx(ctx);
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 	newctx = (Gssctxt*)xmalloc(sizeof (Gssctxt));
616*0Sstevel@tonic-gate 	memset(newctx, 0, sizeof(Gssctxt));
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 	newctx->local = client;
619*0Sstevel@tonic-gate 	newctx->desired_mech = ssh_gssapi_dup_oid(mech);
620*0Sstevel@tonic-gate 
621*0Sstevel@tonic-gate 	/* This happens to be redundant given the memset() above */
622*0Sstevel@tonic-gate 	newctx->major = GSS_S_COMPLETE;
623*0Sstevel@tonic-gate 	newctx->context = GSS_C_NO_CONTEXT;
624*0Sstevel@tonic-gate 	newctx->actual_mech =  GSS_C_NULL_OID;
625*0Sstevel@tonic-gate 	newctx->desired_name = GSS_C_NO_NAME;
626*0Sstevel@tonic-gate 	newctx->src_name = GSS_C_NO_NAME;
627*0Sstevel@tonic-gate 	newctx->dst_name = GSS_C_NO_NAME;
628*0Sstevel@tonic-gate 	newctx->creds = GSS_C_NO_CREDENTIAL;
629*0Sstevel@tonic-gate 	newctx->deleg_creds = GSS_C_NO_CREDENTIAL;
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 	*ctx = newctx;
632*0Sstevel@tonic-gate }
633*0Sstevel@tonic-gate 
634*0Sstevel@tonic-gate gss_OID
635*0Sstevel@tonic-gate ssh_gssapi_dup_oid(gss_OID oid)
636*0Sstevel@tonic-gate {
637*0Sstevel@tonic-gate 	gss_OID new_oid;
638*0Sstevel@tonic-gate 
639*0Sstevel@tonic-gate 	new_oid = xmalloc(sizeof(gss_OID_desc));
640*0Sstevel@tonic-gate 
641*0Sstevel@tonic-gate 	new_oid->elements = xmalloc(oid->length);
642*0Sstevel@tonic-gate 	new_oid->length = oid->length;
643*0Sstevel@tonic-gate 	memcpy(new_oid->elements, oid->elements, oid->length);
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate 	return (new_oid);
646*0Sstevel@tonic-gate }
647*0Sstevel@tonic-gate 
648*0Sstevel@tonic-gate gss_OID
649*0Sstevel@tonic-gate ssh_gssapi_make_oid(size_t length, void *elements)
650*0Sstevel@tonic-gate {
651*0Sstevel@tonic-gate 	gss_OID_desc oid;
652*0Sstevel@tonic-gate 
653*0Sstevel@tonic-gate 	oid.length = length;
654*0Sstevel@tonic-gate 	oid.elements = elements;
655*0Sstevel@tonic-gate 
656*0Sstevel@tonic-gate 	return (ssh_gssapi_dup_oid(&oid));
657*0Sstevel@tonic-gate }
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate void
660*0Sstevel@tonic-gate ssh_gssapi_release_oid(gss_OID *oid)
661*0Sstevel@tonic-gate {
662*0Sstevel@tonic-gate 	OM_uint32 min;
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 	if (oid && *oid == GSS_C_NULL_OID)
665*0Sstevel@tonic-gate 		return;
666*0Sstevel@tonic-gate 	(void) gss_release_oid(&min, oid);
667*0Sstevel@tonic-gate 
668*0Sstevel@tonic-gate 	if (*oid == GSS_C_NULL_OID)
669*0Sstevel@tonic-gate 		return; /* libgss did own this gss_OID and released it */
670*0Sstevel@tonic-gate 
671*0Sstevel@tonic-gate 	xfree((*oid)->elements);
672*0Sstevel@tonic-gate 	xfree(*oid);
673*0Sstevel@tonic-gate 	*oid = GSS_C_NULL_OID;
674*0Sstevel@tonic-gate }
675*0Sstevel@tonic-gate 
676*0Sstevel@tonic-gate struct gss_name {
677*0Sstevel@tonic-gate 	gss_OID                 name_type;
678*0Sstevel@tonic-gate 	gss_buffer_t            external_name;
679*0Sstevel@tonic-gate 	gss_OID                 mech_type;
680*0Sstevel@tonic-gate 	void			*mech_name;
681*0Sstevel@tonic-gate };
682*0Sstevel@tonic-gate 
683*0Sstevel@tonic-gate /* Delete our context, providing it has been built correctly */
684*0Sstevel@tonic-gate void
685*0Sstevel@tonic-gate ssh_gssapi_delete_ctx(Gssctxt **ctx)
686*0Sstevel@tonic-gate {
687*0Sstevel@tonic-gate 	OM_uint32 ms;
688*0Sstevel@tonic-gate 
689*0Sstevel@tonic-gate 	if ((*ctx) == NULL)
690*0Sstevel@tonic-gate 		return;
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 	if ((*ctx)->context != GSS_C_NO_CONTEXT)
693*0Sstevel@tonic-gate 		gss_delete_sec_context(&ms,&(*ctx)->context,GSS_C_NO_BUFFER);
694*0Sstevel@tonic-gate 	/* XXX if ((*ctx)->desired_mech != GSS_C_NULL_OID)
695*0Sstevel@tonic-gate 		ssh_gssapi_release_oid(&(*ctx)->desired_mech);*/
696*0Sstevel@tonic-gate 	if ((*ctx)->actual_mech != GSS_C_NULL_OID)
697*0Sstevel@tonic-gate 		(void) ssh_gssapi_release_oid(&(*ctx)->actual_mech);
698*0Sstevel@tonic-gate 	if ((*ctx)->desired_name != GSS_C_NO_NAME)
699*0Sstevel@tonic-gate 		gss_release_name(&ms,&(*ctx)->desired_name);
700*0Sstevel@tonic-gate 	/* if ((*ctx)->src_name != GSS_C_NO_NAME)
701*0Sstevel@tonic-gate 		gss_release_name(&ms,&(*ctx)->src_name); */
702*0Sstevel@tonic-gate 	if ((*ctx)->dst_name != GSS_C_NO_NAME)
703*0Sstevel@tonic-gate 		gss_release_name(&ms,&(*ctx)->dst_name);
704*0Sstevel@tonic-gate 	if ((*ctx)->creds != GSS_C_NO_CREDENTIAL)
705*0Sstevel@tonic-gate 		gss_release_cred(&ms,&(*ctx)->creds);
706*0Sstevel@tonic-gate 	if ((*ctx)->deleg_creds != GSS_C_NO_CREDENTIAL)
707*0Sstevel@tonic-gate 		gss_release_cred(&ms,&(*ctx)->deleg_creds);
708*0Sstevel@tonic-gate 
709*0Sstevel@tonic-gate 	xfree(*ctx);
710*0Sstevel@tonic-gate 	*ctx=NULL;
711*0Sstevel@tonic-gate }
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate /* Create a GSS hostbased service principal name for a given server hostname */
714*0Sstevel@tonic-gate int
715*0Sstevel@tonic-gate ssh_gssapi_import_name(Gssctxt *ctx, const char *server_host)
716*0Sstevel@tonic-gate {
717*0Sstevel@tonic-gate 	gss_buffer_desc name_buf;
718*0Sstevel@tonic-gate 	int		ret;
719*0Sstevel@tonic-gate 
720*0Sstevel@tonic-gate 	/* Build target principal */
721*0Sstevel@tonic-gate 
722*0Sstevel@tonic-gate 	/* Non-portable but very neat code relying on SUSv3:
723*0Sstevel@tonic-gate 	name_buf.length = snprintf(NULL, 0, "%s@%S",
724*0Sstevel@tonic-gate 		SSH_GSS_HOSTBASED_SERVICE, server_host);
725*0Sstevel@tonic-gate 	 */
726*0Sstevel@tonic-gate 
727*0Sstevel@tonic-gate 	name_buf.length = strlen(SSH_GSS_HOSTBASED_SERVICE) +
728*0Sstevel@tonic-gate 		    strlen(server_host) + 1; /* +1 for '@' */
729*0Sstevel@tonic-gate 	name_buf.value = xmalloc(name_buf.length + 1); /* +1 for NUL */
730*0Sstevel@tonic-gate 	ret = snprintf(name_buf.value, name_buf.length + 1, "%s@%s",
731*0Sstevel@tonic-gate 			SSH_GSS_HOSTBASED_SERVICE, server_host);
732*0Sstevel@tonic-gate 
733*0Sstevel@tonic-gate 	debug3("%s: snprintf() returned %d, expected %d", __func__, ret, name_buf.length + 1);
734*0Sstevel@tonic-gate 
735*0Sstevel@tonic-gate 	ctx->major = gss_import_name(&ctx->minor, &name_buf,
736*0Sstevel@tonic-gate 		GSS_C_NT_HOSTBASED_SERVICE, &ctx->desired_name);
737*0Sstevel@tonic-gate 
738*0Sstevel@tonic-gate 	if (GSS_ERROR(ctx->major)) {
739*0Sstevel@tonic-gate 		ssh_gssapi_error(ctx, "calling GSS_Import_name()");
740*0Sstevel@tonic-gate 		return 0;
741*0Sstevel@tonic-gate 	}
742*0Sstevel@tonic-gate 
743*0Sstevel@tonic-gate 	xfree(name_buf.value);
744*0Sstevel@tonic-gate 
745*0Sstevel@tonic-gate 	return 1;
746*0Sstevel@tonic-gate }
747*0Sstevel@tonic-gate 
748*0Sstevel@tonic-gate OM_uint32
749*0Sstevel@tonic-gate ssh_gssapi_get_mic(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) {
750*0Sstevel@tonic-gate 
751*0Sstevel@tonic-gate 	ctx->major=gss_get_mic(&ctx->minor,ctx->context,
752*0Sstevel@tonic-gate 				GSS_C_QOP_DEFAULT, buffer, hash);
753*0Sstevel@tonic-gate 	if (GSS_ERROR(ctx->major))
754*0Sstevel@tonic-gate 		ssh_gssapi_error(ctx, "while getting MIC");
755*0Sstevel@tonic-gate 	return(ctx->major);
756*0Sstevel@tonic-gate }
757*0Sstevel@tonic-gate 
758*0Sstevel@tonic-gate OM_uint32
759*0Sstevel@tonic-gate ssh_gssapi_verify_mic(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) {
760*0Sstevel@tonic-gate 	gss_qop_t qop;
761*0Sstevel@tonic-gate 
762*0Sstevel@tonic-gate 	ctx->major=gss_verify_mic(&ctx->minor,ctx->context, buffer, hash, &qop);
763*0Sstevel@tonic-gate 	if (GSS_ERROR(ctx->major))
764*0Sstevel@tonic-gate 		ssh_gssapi_error(ctx, "while verifying MIC");
765*0Sstevel@tonic-gate 	return(ctx->major);
766*0Sstevel@tonic-gate }
767*0Sstevel@tonic-gate #endif /* GSSAPI */
768