xref: /freebsd/crypto/krb5/src/lib/gssapi/spnego/spnego_mech.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /*
2  * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
3  * All rights reserved.
4  *
5  * Export of this software from the United States of America may
6  *   require a specific license from the United States Government.
7  *   It is the responsibility of any person or organization contemplating
8  *   export to obtain such a license before exporting.
9  *
10  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11  * distribute this software and its documentation for any purpose and
12  * without fee is hereby granted, provided that the above copyright
13  * notice appear in all copies and that both that copyright notice and
14  * this permission notice appear in supporting documentation, and that
15  * the name of M.I.T. not be used in advertising or publicity pertaining
16  * to distribution of the software without specific, written prior
17  * permission.  Furthermore if you modify this software you must label
18  * your software as modified software and not distribute it in such a
19  * fashion that it might be confused with the original M.I.T. software.
20  * M.I.T. makes no representations about the suitability of
21  * this software for any purpose.  It is provided "as is" without express
22  * or implied warranty.
23  */
24 /*
25  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  *
28  * A module that implements the spnego security mechanism.
29  * It is used to negotiate the security mechanism between
30  * peers using the GSS-API.  SPNEGO is specified in RFC 4178.
31  *
32  */
33 /*
34  * Copyright (c) 2006-2008, Novell, Inc.
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions are met:
39  *
40  *   * Redistributions of source code must retain the above copyright notice,
41  *       this list of conditions and the following disclaimer.
42  *   * Redistributions in binary form must reproduce the above copyright
43  *       notice, this list of conditions and the following disclaimer in the
44  *       documentation and/or other materials provided with the distribution.
45  *   * The copyright holder's name is not used to endorse or promote products
46  *       derived from this software without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
49  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
52  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58  * POSSIBILITY OF SUCH DAMAGE.
59  */
60 /* #pragma ident	"@(#)spnego_mech.c	1.7	04/09/28 SMI" */
61 
62 #include	<k5-int.h>
63 #include	<k5-der.h>
64 #include	<krb5.h>
65 #include	<mglueP.h>
66 #include	"gssapiP_spnego.h"
67 #include	<gssapi_err_generic.h>
68 
69 
70 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
71 typedef const gss_OID_desc *gss_OID_const;
72 
73 /* private routines for spnego_mechanism */
74 static spnego_token_t make_spnego_token(const char *);
75 static gss_buffer_desc make_err_msg(const char *);
76 static int verify_token_header(struct k5input *, gss_OID_const);
77 static gss_OID get_mech_oid(OM_uint32 *minor_status, struct k5input *);
78 static gss_buffer_t get_octet_string(struct k5input *);
79 static gss_OID_set get_mech_set(OM_uint32 *, struct k5input *);
80 static OM_uint32 get_req_flags(struct k5input *, OM_uint32 *);
81 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
82 				     gss_const_key_value_set_t,
83 				     gss_cred_id_t *, gss_OID_set *,
84 				     OM_uint32 *);
85 static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_ctx_id_t,
86 				      spnego_gss_cred_id_t, gss_cred_usage_t);
87 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
88 static spnego_gss_ctx_id_t create_spnego_ctx(int);
89 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
90 
91 static OM_uint32
92 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
93 	    gss_buffer_t *, OM_uint32 *, send_token_flag *);
94 static OM_uint32
95 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
96 	   gss_buffer_t *, OM_uint32 *, send_token_flag *);
97 
98 static OM_uint32
99 init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, send_token_flag *,
100 	     spnego_gss_ctx_id_t *);
101 static OM_uint32
102 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
103 	      gss_buffer_t *, gss_buffer_t *, send_token_flag *);
104 static OM_uint32
105 init_ctx_cont(OM_uint32 *, spnego_gss_ctx_id_t, gss_buffer_t,
106 	      gss_buffer_t *, gss_buffer_t *,
107 	      OM_uint32 *, send_token_flag *);
108 static OM_uint32
109 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
110 		  gss_OID, gss_buffer_t *, gss_buffer_t *, send_token_flag *);
111 static OM_uint32
112 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
113 		   OM_uint32, gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
114 		   gss_channel_bindings_t,
115 		   gss_buffer_t, OM_uint32 *, send_token_flag *);
116 
117 static OM_uint32
118 acc_ctx_new(OM_uint32 *, gss_buffer_t, spnego_gss_cred_id_t, gss_buffer_t *,
119 	    gss_buffer_t *, OM_uint32 *, send_token_flag *,
120 	    spnego_gss_ctx_id_t *);
121 static OM_uint32
122 acc_ctx_cont(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t, gss_buffer_t *,
123 	     gss_buffer_t *, OM_uint32 *, send_token_flag *);
124 static OM_uint32
125 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
126 		OM_uint32 *, send_token_flag *);
127 static OM_uint32
128 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
129 		 gss_buffer_t, gss_channel_bindings_t, gss_buffer_t,
130 		 OM_uint32 *, OM_uint32 *, send_token_flag *);
131 
132 static gss_OID
133 negotiate_mech(spnego_gss_ctx_id_t, gss_OID_set, OM_uint32 *);
134 
135 static int
136 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
137 			int,
138 			gss_buffer_t,
139 			OM_uint32, gss_buffer_t, send_token_flag,
140 			gss_buffer_t);
141 static OM_uint32
142 make_spnego_tokenTarg_msg(uint8_t, gss_OID, gss_buffer_t,
143 			gss_buffer_t, send_token_flag,
144 			gss_buffer_t);
145 
146 static OM_uint32
147 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
148 		 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
149 		 gss_buffer_t *);
150 static OM_uint32
151 get_negTokenResp(OM_uint32 *, struct k5input *, OM_uint32 *, gss_OID *,
152 		 gss_buffer_t *, gss_buffer_t *);
153 
154 static int
155 is_kerb_mech(gss_OID oid);
156 
157 /* SPNEGO oid structure */
158 static const gss_OID_desc spnego_oids[] = {
159 	{SPNEGO_OID_LENGTH, SPNEGO_OID},
160 };
161 
162 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
163 static const gss_OID_set_desc spnego_oidsets[] = {
164 	{1, (gss_OID) spnego_oids+0},
165 };
166 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
167 
168 static gss_OID_desc negoex_mech = { NEGOEX_OID_LENGTH, NEGOEX_OID };
169 
170 static int make_NegHints(OM_uint32 *, gss_buffer_t *);
171 static OM_uint32
172 acc_ctx_hints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *, OM_uint32 *,
173 	      send_token_flag *, spnego_gss_ctx_id_t *);
174 
175 /*
176  * The Mech OID for SPNEGO:
177  * { iso(1) org(3) dod(6) internet(1) security(5)
178  *  mechanism(5) spnego(2) }
179  */
180 static struct gss_config spnego_mechanism =
181 {
182 	{SPNEGO_OID_LENGTH, SPNEGO_OID},
183 	NULL,
184 	spnego_gss_acquire_cred,
185 	spnego_gss_release_cred,
186 	spnego_gss_init_sec_context,
187 #ifndef LEAN_CLIENT
188 	spnego_gss_accept_sec_context,
189 #else
190 	NULL,
191 #endif  /* LEAN_CLIENT */
192 	NULL,				/* gss_process_context_token */
193 	spnego_gss_delete_sec_context,	/* gss_delete_sec_context */
194 	spnego_gss_context_time,	/* gss_context_time */
195 	spnego_gss_get_mic,		/* gss_get_mic */
196 	spnego_gss_verify_mic,		/* gss_verify_mic */
197 	spnego_gss_wrap,		/* gss_wrap */
198 	spnego_gss_unwrap,		/* gss_unwrap */
199 	spnego_gss_display_status,
200 	NULL,				/* gss_indicate_mechs */
201 	spnego_gss_compare_name,
202 	spnego_gss_display_name,
203 	spnego_gss_import_name,
204 	spnego_gss_release_name,
205 	spnego_gss_inquire_cred,	/* gss_inquire_cred */
206 	NULL,				/* gss_add_cred */
207 #ifndef LEAN_CLIENT
208 	spnego_gss_export_sec_context,		/* gss_export_sec_context */
209 	spnego_gss_import_sec_context,		/* gss_import_sec_context */
210 #else
211 	NULL,				/* gss_export_sec_context */
212 	NULL,				/* gss_import_sec_context */
213 #endif /* LEAN_CLIENT */
214 	NULL, 				/* gss_inquire_cred_by_mech */
215 	spnego_gss_inquire_names_for_mech,
216 	spnego_gss_inquire_context,	/* gss_inquire_context */
217 	NULL,				/* gss_internal_release_oid */
218 	spnego_gss_wrap_size_limit,	/* gss_wrap_size_limit */
219 	spnego_gss_localname,
220 	NULL,				/* gss_userok */
221 	NULL,				/* gss_export_name */
222 	spnego_gss_duplicate_name,	/* gss_duplicate_name */
223 	NULL,				/* gss_store_cred */
224  	spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
225  	spnego_gss_inquire_cred_by_oid,	/* gss_inquire_cred_by_oid */
226  	spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */
227 	spnego_gss_set_cred_option,	/* gssspi_set_cred_option */
228  	NULL,				/* gssspi_mech_invoke */
229 	spnego_gss_wrap_aead,
230 	spnego_gss_unwrap_aead,
231 	spnego_gss_wrap_iov,
232 	spnego_gss_unwrap_iov,
233 	spnego_gss_wrap_iov_length,
234 	spnego_gss_complete_auth_token,
235 	spnego_gss_acquire_cred_impersonate_name,
236 	NULL,				/* gss_add_cred_impersonate_name */
237 	spnego_gss_display_name_ext,
238 	spnego_gss_inquire_name,
239 	spnego_gss_get_name_attribute,
240 	spnego_gss_set_name_attribute,
241 	spnego_gss_delete_name_attribute,
242 	spnego_gss_export_name_composite,
243 	spnego_gss_map_name_to_any,
244 	spnego_gss_release_any_name_mapping,
245 	spnego_gss_pseudo_random,
246 	spnego_gss_set_neg_mechs,
247 	spnego_gss_inquire_saslname_for_mech,
248 	spnego_gss_inquire_mech_for_saslname,
249 	spnego_gss_inquire_attrs_for_mech,
250 	spnego_gss_acquire_cred_from,
251 	NULL,				/* gss_store_cred_into */
252 	spnego_gss_acquire_cred_with_password,
253 	spnego_gss_export_cred,
254 	spnego_gss_import_cred,
255 	NULL,				/* gssspi_import_sec_context_by_mech */
256 	NULL,				/* gssspi_import_name_by_mech */
257 	NULL,				/* gssspi_import_cred_by_mech */
258 	spnego_gss_get_mic_iov,
259 	spnego_gss_verify_mic_iov,
260 	spnego_gss_get_mic_iov_length
261 };
262 
263 #ifdef _GSS_STATIC_LINK
264 #include "mglueP.h"
265 
gss_spnegomechglue_init(void)266 static int gss_spnegomechglue_init(void)
267 {
268 	struct gss_mech_config mech_spnego;
269 
270 	memset(&mech_spnego, 0, sizeof(mech_spnego));
271 	mech_spnego.mech = &spnego_mechanism;
272 	mech_spnego.mechNameStr = "spnego";
273 	mech_spnego.mech_type = GSS_C_NO_OID;
274 
275 	return gssint_register_mechinfo(&mech_spnego);
276 }
277 #else
278 gss_mechanism KRB5_CALLCONV
gss_mech_initialize(void)279 gss_mech_initialize(void)
280 {
281 	return (&spnego_mechanism);
282 }
283 
284 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
285 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
286 int gss_krb5int_lib_init(void);
287 #endif /* _GSS_STATIC_LINK */
288 
gss_spnegoint_lib_init(void)289 int gss_spnegoint_lib_init(void)
290 {
291 	int err;
292 
293 	err = k5_key_register(K5_KEY_GSS_SPNEGO_STATUS, NULL);
294 	if (err)
295 		return err;
296 
297 #ifdef _GSS_STATIC_LINK
298 	return gss_spnegomechglue_init();
299 #else
300 	return 0;
301 #endif
302 }
303 
gss_spnegoint_lib_fini(void)304 void gss_spnegoint_lib_fini(void)
305 {
306 	k5_key_delete(K5_KEY_GSS_SPNEGO_STATUS);
307 }
308 
309 static OM_uint32
create_spnego_cred(OM_uint32 * minor_status,gss_cred_id_t mcred,spnego_gss_cred_id_t * cred_out)310 create_spnego_cred(OM_uint32 *minor_status, gss_cred_id_t mcred,
311 		   spnego_gss_cred_id_t *cred_out)
312 {
313 	spnego_gss_cred_id_t spcred;
314 
315 	*cred_out = NULL;
316 	spcred = calloc(1, sizeof(*spcred));
317 	if (spcred == NULL) {
318 		*minor_status = ENOMEM;
319 		return GSS_S_FAILURE;
320 	}
321 	spcred->mcred = mcred;
322 	*cred_out = spcred;
323 	return GSS_S_COMPLETE;
324 }
325 
326 /*ARGSUSED*/
327 OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred(OM_uint32 * minor_status,gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)328 spnego_gss_acquire_cred(OM_uint32 *minor_status,
329 			gss_name_t desired_name,
330 			OM_uint32 time_req,
331 			gss_OID_set desired_mechs,
332 			gss_cred_usage_t cred_usage,
333 			gss_cred_id_t *output_cred_handle,
334 			gss_OID_set *actual_mechs,
335 			OM_uint32 *time_rec)
336 {
337     return spnego_gss_acquire_cred_from(minor_status, desired_name, time_req,
338 					desired_mechs, cred_usage, NULL,
339 					output_cred_handle, actual_mechs,
340 					time_rec);
341 }
342 
343 /*ARGSUSED*/
344 OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred_from(OM_uint32 * minor_status,const gss_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_const_key_value_set_t cred_store,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)345 spnego_gss_acquire_cred_from(OM_uint32 *minor_status,
346 			     const gss_name_t desired_name,
347 			     OM_uint32 time_req,
348 			     const gss_OID_set desired_mechs,
349 			     gss_cred_usage_t cred_usage,
350 			     gss_const_key_value_set_t cred_store,
351 			     gss_cred_id_t *output_cred_handle,
352 			     gss_OID_set *actual_mechs,
353 			     OM_uint32 *time_rec)
354 {
355 	OM_uint32 status, tmpmin;
356 	gss_OID_set amechs;
357 	gss_cred_id_t mcred = NULL;
358 	spnego_gss_cred_id_t spcred = NULL;
359 	dsyslog("Entering spnego_gss_acquire_cred\n");
360 
361 	if (actual_mechs)
362 		*actual_mechs = NULL;
363 
364 	if (time_rec)
365 		*time_rec = 0;
366 
367 	/* We will obtain a mechglue credential and wrap it in a
368 	 * spnego_gss_cred_id_rec structure.  Allocate the wrapper. */
369 	status = create_spnego_cred(minor_status, mcred, &spcred);
370 	if (status != GSS_S_COMPLETE)
371 		return (status);
372 
373 	/*
374 	 * Always use get_available_mechs to collect a list of
375 	 * mechs for which creds are available.
376 	 */
377 	status = get_available_mechs(minor_status, desired_name,
378 				     cred_usage, cred_store, &mcred,
379 				     &amechs, time_rec);
380 
381 	if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
382 		(void) gssint_copy_oid_set(&tmpmin, amechs, actual_mechs);
383 	}
384 	(void) gss_release_oid_set(&tmpmin, &amechs);
385 
386 	if (status == GSS_S_COMPLETE) {
387 		spcred->mcred = mcred;
388 		*output_cred_handle = (gss_cred_id_t)spcred;
389 	} else {
390 		free(spcred);
391 		*output_cred_handle = GSS_C_NO_CREDENTIAL;
392 	}
393 
394 	dsyslog("Leaving spnego_gss_acquire_cred\n");
395 	return (status);
396 }
397 
398 /*ARGSUSED*/
399 OM_uint32 KRB5_CALLCONV
spnego_gss_release_cred(OM_uint32 * minor_status,gss_cred_id_t * cred_handle)400 spnego_gss_release_cred(OM_uint32 *minor_status,
401 			gss_cred_id_t *cred_handle)
402 {
403 	spnego_gss_cred_id_t spcred = NULL;
404 
405 	dsyslog("Entering spnego_gss_release_cred\n");
406 
407 	if (minor_status == NULL || cred_handle == NULL)
408 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
409 
410 	*minor_status = 0;
411 
412 	if (*cred_handle == GSS_C_NO_CREDENTIAL)
413 		return (GSS_S_COMPLETE);
414 
415 	spcred = (spnego_gss_cred_id_t)*cred_handle;
416 	*cred_handle = GSS_C_NO_CREDENTIAL;
417 	gss_release_oid_set(minor_status, &spcred->neg_mechs);
418 	gss_release_cred(minor_status, &spcred->mcred);
419 	free(spcred);
420 
421 	dsyslog("Leaving spnego_gss_release_cred\n");
422 	return (GSS_S_COMPLETE);
423 }
424 
425 static spnego_gss_ctx_id_t
create_spnego_ctx(int initiate)426 create_spnego_ctx(int initiate)
427 {
428 	spnego_gss_ctx_id_t spnego_ctx = NULL;
429 
430 	spnego_ctx = malloc(sizeof(*spnego_ctx));
431 	if (spnego_ctx == NULL) {
432 		return (NULL);
433 	}
434 
435 	spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
436 	spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
437 	spnego_ctx->mech_set = NULL;
438 	spnego_ctx->internal_mech = NULL;
439 	spnego_ctx->DER_mechTypes.length = 0;
440 	spnego_ctx->DER_mechTypes.value = NULL;
441 	spnego_ctx->mic_reqd = 0;
442 	spnego_ctx->mic_sent = 0;
443 	spnego_ctx->mic_rcvd = 0;
444 	spnego_ctx->mech_complete = 0;
445 	spnego_ctx->nego_done = 0;
446 	spnego_ctx->opened = 0;
447 	spnego_ctx->initiate = initiate;
448 	spnego_ctx->internal_name = GSS_C_NO_NAME;
449 	spnego_ctx->actual_mech = GSS_C_NO_OID;
450 	spnego_ctx->deleg_cred = GSS_C_NO_CREDENTIAL;
451 	spnego_ctx->negoex_step = 0;
452 	memset(&spnego_ctx->negoex_transcript, 0, sizeof(struct k5buf));
453 	spnego_ctx->negoex_seqnum = 0;
454 	K5_TAILQ_INIT(&spnego_ctx->negoex_mechs);
455 	spnego_ctx->kctx = NULL;
456 	memset(spnego_ctx->negoex_conv_id, 0, GUID_LENGTH);
457 
458 	return (spnego_ctx);
459 }
460 
461 /* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165)
462  * gssntlmssp(655) controls(1) spnego_req_mechlistMIC(2) */
463 static const gss_OID_desc spnego_req_mechlistMIC_oid =
464 	{ 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x02" };
465 
466 /*
467  * Return nonzero if the mechanism has reason to believe that a mechlistMIC
468  * exchange will be required.  Microsoft servers erroneously require SPNEGO
469  * mechlistMIC if they see an internal MIC within an NTLMSSP Authenticate
470  * message, even if NTLMSSP was the preferred mechanism.
471  */
472 static int
mech_requires_mechlistMIC(spnego_gss_ctx_id_t sc)473 mech_requires_mechlistMIC(spnego_gss_ctx_id_t sc)
474 {
475 	OM_uint32 major, minor;
476 	gss_ctx_id_t ctx = sc->ctx_handle;
477 	gss_OID oid = (gss_OID)&spnego_req_mechlistMIC_oid;
478 	gss_buffer_set_t bufs;
479 	int result;
480 
481 	major = gss_inquire_sec_context_by_oid(&minor, ctx, oid, &bufs);
482 	if (major != GSS_S_COMPLETE)
483 		return 0;
484 
485 	/* Report true if the mech returns a single buffer containing a single
486 	 * byte with value 1. */
487 	result = (bufs != NULL && bufs->count == 1 &&
488 		  bufs->elements[0].length == 1 &&
489 		  memcmp(bufs->elements[0].value, "\1", 1) == 0);
490 	(void) gss_release_buffer_set(&minor, &bufs);
491 	return result;
492 }
493 
494 /* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) Microsoft(311)
495  * security(2) mechanisms(2) NTLM(10) */
496 static const gss_OID_desc gss_mech_ntlmssp_oid =
497 	{ 10, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" };
498 
499 /* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165)
500  * gssntlmssp(655) controls(1) ntlmssp_reset_crypto(3) */
501 static const gss_OID_desc ntlmssp_reset_crypto_oid =
502 	{ 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x03" };
503 
504 /*
505  * MS-SPNG section 3.3.5.1 warns that the NTLM mechanism requires special
506  * handling of the crypto state to interop with Windows.  If the mechanism for
507  * sc is SPNEGO, invoke a mechanism-specific operation on the context to reset
508  * the RC4 state after producing or verifying a MIC.  Ignore a result of
509  * GSS_S_UNAVAILABLE for compatibility with older versions of the mechanism
510  * that do not support this functionality.
511  */
512 static OM_uint32
ntlmssp_reset_crypto_state(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 verify)513 ntlmssp_reset_crypto_state(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
514 			   OM_uint32 verify)
515 {
516 	OM_uint32 major, minor;
517 	gss_buffer_desc value;
518 
519 	if (!g_OID_equal(sc->internal_mech, &gss_mech_ntlmssp_oid))
520 		return GSS_S_COMPLETE;
521 
522 	value.length = sizeof(verify);
523 	value.value = &verify;
524 	major = gss_set_sec_context_option(&minor, &sc->ctx_handle,
525 					   (gss_OID)&ntlmssp_reset_crypto_oid,
526 					   &value);
527 	if (major == GSS_S_UNAVAILABLE)
528 		return GSS_S_COMPLETE;
529 	*minor_status = minor;
530 	return major;
531 }
532 
533 /*
534  * Both initiator and acceptor call here to verify and/or create mechListMIC,
535  * and to consistency-check the MIC state.  handle_mic is invoked only if the
536  * negotiated mech has completed and supports MICs.
537  */
538 static OM_uint32
handle_mic(OM_uint32 * minor_status,gss_buffer_t mic_in,int send_mechtok,spnego_gss_ctx_id_t sc,gss_buffer_t * mic_out,OM_uint32 * negState,send_token_flag * tokflag)539 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
540 	   int send_mechtok, spnego_gss_ctx_id_t sc,
541 	   gss_buffer_t *mic_out,
542 	   OM_uint32 *negState, send_token_flag *tokflag)
543 {
544 	OM_uint32 ret;
545 
546 	ret = GSS_S_FAILURE;
547 	*mic_out = GSS_C_NO_BUFFER;
548 	if (mic_in != GSS_C_NO_BUFFER) {
549 		if (sc->mic_rcvd) {
550 			/* Reject MIC if we've already received a MIC. */
551 			*negState = REJECT;
552 			*tokflag = ERROR_TOKEN_SEND;
553 			return GSS_S_DEFECTIVE_TOKEN;
554 		}
555 	} else if (sc->mic_reqd && !send_mechtok) {
556 		/*
557 		 * If the peer sends the final mechanism token, it
558 		 * must send the MIC with that token if the
559 		 * negotiation requires MICs.
560 		 */
561 		*negState = REJECT;
562 		*tokflag = ERROR_TOKEN_SEND;
563 		return GSS_S_DEFECTIVE_TOKEN;
564 	}
565 	ret = process_mic(minor_status, mic_in, sc, mic_out,
566 			  negState, tokflag);
567 	if (ret != GSS_S_COMPLETE) {
568 		return ret;
569 	}
570 	if (sc->mic_reqd) {
571 		assert(sc->mic_sent || sc->mic_rcvd);
572 	}
573 	if (sc->mic_sent && sc->mic_rcvd) {
574 		ret = GSS_S_COMPLETE;
575 		*negState = ACCEPT_COMPLETE;
576 		if (*mic_out == GSS_C_NO_BUFFER) {
577 			/*
578 			 * We sent a MIC on the previous pass; we
579 			 * shouldn't be sending a mechanism token.
580 			 */
581 			assert(!send_mechtok);
582 			*tokflag = NO_TOKEN_SEND;
583 		} else {
584 			*tokflag = CONT_TOKEN_SEND;
585 		}
586 	} else if (sc->mic_reqd) {
587 		*negState = ACCEPT_INCOMPLETE;
588 		ret = GSS_S_CONTINUE_NEEDED;
589 	} else if (*negState == ACCEPT_COMPLETE) {
590 		ret = GSS_S_COMPLETE;
591 	} else {
592 		ret = GSS_S_CONTINUE_NEEDED;
593 	}
594 	return ret;
595 }
596 
597 /*
598  * Perform the actual verification and/or generation of mechListMIC.
599  */
600 static OM_uint32
process_mic(OM_uint32 * minor_status,gss_buffer_t mic_in,spnego_gss_ctx_id_t sc,gss_buffer_t * mic_out,OM_uint32 * negState,send_token_flag * tokflag)601 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
602 	    spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
603 	    OM_uint32 *negState, send_token_flag *tokflag)
604 {
605 	OM_uint32 ret, tmpmin;
606 	gss_qop_t qop_state;
607 	gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
608 
609 	ret = GSS_S_FAILURE;
610 	if (mic_in != GSS_C_NO_BUFFER) {
611 		ret = gss_verify_mic(minor_status, sc->ctx_handle,
612 				     &sc->DER_mechTypes,
613 				     mic_in, &qop_state);
614 		if (ret == GSS_S_COMPLETE)
615 			ret = ntlmssp_reset_crypto_state(minor_status, sc, 1);
616 		if (ret != GSS_S_COMPLETE) {
617 			*negState = REJECT;
618 			*tokflag = ERROR_TOKEN_SEND;
619 			return ret;
620 		}
621 		/* If we got a MIC, we must send a MIC. */
622 		sc->mic_reqd = 1;
623 		sc->mic_rcvd = 1;
624 	}
625 	if (sc->mic_reqd && !sc->mic_sent) {
626 		ret = gss_get_mic(minor_status, sc->ctx_handle,
627 				  GSS_C_QOP_DEFAULT,
628 				  &sc->DER_mechTypes,
629 				  &tmpmic);
630 		if (ret == GSS_S_COMPLETE)
631 			ret = ntlmssp_reset_crypto_state(minor_status, sc, 0);
632 		if (ret != GSS_S_COMPLETE) {
633 			gss_release_buffer(&tmpmin, &tmpmic);
634 			*tokflag = NO_TOKEN_SEND;
635 			return ret;
636 		}
637 		*mic_out = malloc(sizeof(gss_buffer_desc));
638 		if (*mic_out == GSS_C_NO_BUFFER) {
639 			gss_release_buffer(&tmpmin, &tmpmic);
640 			*tokflag = NO_TOKEN_SEND;
641 			return GSS_S_FAILURE;
642 		}
643 		**mic_out = tmpmic;
644 		sc->mic_sent = 1;
645 	}
646 	return GSS_S_COMPLETE;
647 }
648 
649 /* Create a new SPNEGO context handle for the initial call to
650  * spnego_gss_init_sec_context().  */
651 static OM_uint32
init_ctx_new(OM_uint32 * minor_status,spnego_gss_cred_id_t spcred,send_token_flag * tokflag,spnego_gss_ctx_id_t * sc_out)652 init_ctx_new(OM_uint32 *minor_status,
653 	     spnego_gss_cred_id_t spcred,
654 	     send_token_flag *tokflag,
655 	     spnego_gss_ctx_id_t *sc_out)
656 {
657 	OM_uint32 ret;
658 	spnego_gss_ctx_id_t sc = NULL;
659 
660 	*sc_out = NULL;
661 
662 	sc = create_spnego_ctx(1);
663 	if (sc == NULL)
664 		return GSS_S_FAILURE;
665 
666 	/* determine negotiation mech set */
667 	ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_INITIATE);
668 	if (ret != GSS_S_COMPLETE)
669 		goto cleanup;
670 
671 	/* Set an initial internal mech to make the first context token. */
672 	sc->internal_mech = &sc->mech_set->elements[0];
673 
674 	if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
675 		ret = GSS_S_FAILURE;
676 		goto cleanup;
677 	}
678 
679 	sc->ctx_handle = GSS_C_NO_CONTEXT;
680 	*sc_out = sc;
681 	sc = NULL;
682 	*tokflag = INIT_TOKEN_SEND;
683 	ret = GSS_S_COMPLETE;
684 
685 cleanup:
686 	release_spnego_ctx(&sc);
687 	return ret;
688 }
689 
690 /*
691  * Called by second and later calls to spnego_gss_init_sec_context()
692  * to decode reply and update state.
693  */
694 static OM_uint32
init_ctx_cont(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,gss_buffer_t buf,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * acc_negState,send_token_flag * tokflag)695 init_ctx_cont(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
696 	      gss_buffer_t buf, gss_buffer_t *responseToken,
697 	      gss_buffer_t *mechListMIC, OM_uint32 *acc_negState,
698 	      send_token_flag *tokflag)
699 {
700 	OM_uint32 ret, tmpmin;
701 	gss_OID supportedMech = GSS_C_NO_OID;
702 	struct k5input in;
703 
704 	*acc_negState = UNSPECIFIED;
705 	*tokflag = ERROR_TOKEN_SEND;
706 
707 	k5_input_init(&in, buf->value, buf->length);
708 	ret = get_negTokenResp(minor_status, &in, acc_negState, &supportedMech,
709 			       responseToken, mechListMIC);
710 	if (ret != GSS_S_COMPLETE)
711 		goto cleanup;
712 
713 	/* Bail out now on a reject with no error token.  If we have an error
714 	 * token, keep going and get a better error status from the mech. */
715 	if (*acc_negState == REJECT && *responseToken == GSS_C_NO_BUFFER) {
716 		if (!sc->nego_done) {
717 			/* RFC 4178 says to return GSS_S_BAD_MECH on a
718 			 * mechanism negotiation failure. */
719 			*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
720 			map_errcode(minor_status);
721 			ret = GSS_S_BAD_MECH;
722 		} else {
723 			ret = GSS_S_FAILURE;
724 		}
725 		*tokflag = NO_TOKEN_SEND;
726 		goto cleanup;
727 	}
728 	/*
729 	 * nego_done is false for the first call to init_ctx_cont()
730 	 */
731 	if (!sc->nego_done) {
732 		ret = init_ctx_nego(minor_status, sc, *acc_negState,
733 				    supportedMech, responseToken, mechListMIC,
734 				    tokflag);
735 	} else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
736 		   (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
737 		/* Missing or spurious token from acceptor. */
738 		ret = GSS_S_DEFECTIVE_TOKEN;
739 	} else if (!sc->mech_complete ||
740 		   (sc->mic_reqd &&
741 		    (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
742 		/* Not obviously done; we may decide we're done later in
743 		 * init_ctx_call_init or handle_mic. */
744 		*tokflag = CONT_TOKEN_SEND;
745 		ret = GSS_S_COMPLETE;
746 	} else {
747 		/* mech finished on last pass and no MIC required, so done. */
748 		*tokflag = NO_TOKEN_SEND;
749 		ret = GSS_S_COMPLETE;
750 	}
751 cleanup:
752 	if (supportedMech != GSS_C_NO_OID)
753 		generic_gss_release_oid(&tmpmin, &supportedMech);
754 	return ret;
755 }
756 
757 /*
758  * Consistency checking and mechanism negotiation handling for second
759  * call of spnego_gss_init_sec_context().  Call init_ctx_reselect() to
760  * update internal state if acceptor has counter-proposed.
761  */
762 static OM_uint32
init_ctx_nego(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 acc_negState,gss_OID supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,send_token_flag * tokflag)763 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
764 	      OM_uint32 acc_negState, gss_OID supportedMech,
765 	      gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
766 	      send_token_flag *tokflag)
767 {
768 	OM_uint32 ret;
769 
770 	*tokflag = ERROR_TOKEN_SEND;
771 	ret = GSS_S_DEFECTIVE_TOKEN;
772 
773 	/*
774 	 * According to RFC 4178, both supportedMech and negState must be
775 	 * present in the first acceptor token.  However, some Java
776 	 * implementations include only a responseToken in the first
777 	 * NegTokenResp.  In this case we can use sc->internal_mech as the
778 	 * negotiated mechanism.  (We do not currently look at acc_negState
779 	 * when continuing with the optimistic mechanism.)
780 	 */
781 	if (supportedMech == GSS_C_NO_OID)
782 		supportedMech = sc->internal_mech;
783 
784 	/*
785 	 * If the mechanism we sent is not the mechanism returned from
786 	 * the server, we need to handle the server's counter
787 	 * proposal.  There is a bug in SAMBA servers that always send
788 	 * the old Kerberos mech OID, even though we sent the new one.
789 	 * So we will treat all the Kerberos mech OIDS as the same.
790          */
791 	if (!(is_kerb_mech(supportedMech) &&
792 	      is_kerb_mech(sc->internal_mech)) &&
793 	    !g_OID_equal(supportedMech, sc->internal_mech)) {
794 		ret = init_ctx_reselect(minor_status, sc,
795 					acc_negState, supportedMech,
796 					responseToken, mechListMIC, tokflag);
797 
798 	} else if (*responseToken == GSS_C_NO_BUFFER) {
799 		if (sc->mech_complete) {
800 			/*
801 			 * Mech completed on first call to its
802 			 * init_sec_context().  Acceptor sends no mech
803 			 * token.
804 			 */
805 			*tokflag = NO_TOKEN_SEND;
806 			ret = GSS_S_COMPLETE;
807 		} else {
808 			/*
809 			 * Reject missing mech token when optimistic
810 			 * mech selected.
811 			 */
812 			*minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
813 			map_errcode(minor_status);
814 			ret = GSS_S_DEFECTIVE_TOKEN;
815 		}
816 	} else if ((*responseToken)->length == 0 && sc->mech_complete) {
817 		/* Handle old IIS servers returning empty token instead of
818 		 * null tokens in the non-mutual auth case. */
819 		*tokflag = NO_TOKEN_SEND;
820 		ret = GSS_S_COMPLETE;
821 	} else if (sc->mech_complete) {
822 		/* Reject spurious mech token. */
823 		ret = GSS_S_DEFECTIVE_TOKEN;
824 	} else {
825 		*tokflag = CONT_TOKEN_SEND;
826 		ret = GSS_S_COMPLETE;
827 	}
828 	sc->nego_done = 1;
829 	return ret;
830 }
831 
832 /*
833  * Handle acceptor's counter-proposal of an alternative mechanism.
834  */
835 static OM_uint32
init_ctx_reselect(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 acc_negState,gss_OID supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,send_token_flag * tokflag)836 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
837 		  OM_uint32 acc_negState, gss_OID supportedMech,
838 		  gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
839 		  send_token_flag *tokflag)
840 {
841 	OM_uint32 tmpmin;
842 	size_t i;
843 
844 	gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
845 			       GSS_C_NO_BUFFER);
846 
847 	/* Find supportedMech in sc->mech_set. */
848 	for (i = 0; i < sc->mech_set->count; i++) {
849 		if (g_OID_equal(supportedMech, &sc->mech_set->elements[i]))
850 			break;
851 	}
852 	if (i == sc->mech_set->count)
853 		return GSS_S_DEFECTIVE_TOKEN;
854 	sc->internal_mech = &sc->mech_set->elements[i];
855 
856 	/*
857 	 * A server conforming to RFC4178 MUST set REQUEST_MIC here, but
858 	 * Windows Server 2003 and earlier implement (roughly) RFC2478 instead,
859 	 * and send ACCEPT_INCOMPLETE.  Tolerate that only if we are falling
860 	 * back to NTLMSSP.
861 	 */
862 	if (acc_negState == ACCEPT_INCOMPLETE) {
863 		if (!g_OID_equal(supportedMech, &gss_mech_ntlmssp_oid))
864 			return GSS_S_DEFECTIVE_TOKEN;
865 	} else if (acc_negState != REQUEST_MIC) {
866 		return GSS_S_DEFECTIVE_TOKEN;
867 	}
868 
869 	sc->mech_complete = 0;
870 	sc->mic_reqd = (acc_negState == REQUEST_MIC);
871 	*tokflag = CONT_TOKEN_SEND;
872 	return GSS_S_COMPLETE;
873 }
874 
875 /*
876  * Wrap call to mechanism gss_init_sec_context() and update state
877  * accordingly.
878  */
879 static OM_uint32
init_ctx_call_init(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,spnego_gss_cred_id_t spcred,OM_uint32 acc_negState,gss_name_t target_name,OM_uint32 req_flags,OM_uint32 time_req,gss_buffer_t mechtok_in,gss_channel_bindings_t bindings,gss_buffer_t mechtok_out,OM_uint32 * time_rec,send_token_flag * send_token)880 init_ctx_call_init(OM_uint32 *minor_status,
881 		   spnego_gss_ctx_id_t sc,
882 		   spnego_gss_cred_id_t spcred,
883 		   OM_uint32 acc_negState,
884 		   gss_name_t target_name,
885 		   OM_uint32 req_flags,
886 		   OM_uint32 time_req,
887 		   gss_buffer_t mechtok_in,
888 		   gss_channel_bindings_t bindings,
889 		   gss_buffer_t mechtok_out,
890 		   OM_uint32 *time_rec,
891 		   send_token_flag *send_token)
892 {
893 	OM_uint32 ret, tmpret, tmpmin, mech_req_flags;
894 	gss_cred_id_t mcred;
895 
896 	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
897 
898 	mech_req_flags = req_flags;
899 	if (spcred == NULL || !spcred->no_ask_integ)
900 		mech_req_flags |= GSS_C_INTEG_FLAG;
901 
902 	if (gss_oid_equal(sc->internal_mech, &negoex_mech)) {
903 		ret = negoex_init(minor_status, sc, mcred, target_name,
904 				  mech_req_flags, time_req, mechtok_in,
905 				  bindings, mechtok_out, time_rec);
906 	} else {
907 		ret = gss_init_sec_context(minor_status, mcred,
908 					   &sc->ctx_handle, target_name,
909 					   sc->internal_mech, mech_req_flags,
910 					   time_req, bindings, mechtok_in,
911 					   &sc->actual_mech, mechtok_out,
912 					   &sc->ctx_flags, time_rec);
913 	}
914 
915 	/* Bail out if the acceptor gave us an error token but the mech didn't
916 	 * see it as an error. */
917 	if (acc_negState == REJECT && !GSS_ERROR(ret)) {
918 		ret = GSS_S_DEFECTIVE_TOKEN;
919 		goto fail;
920 	}
921 
922 	if (ret == GSS_S_COMPLETE) {
923 		sc->mech_complete = 1;
924 		/*
925 		 * Microsoft SPNEGO implementations expect an even number of
926 		 * token exchanges.  So if we're sending a final token, ask for
927 		 * a zero-length token back from the server.  Also ask for a
928 		 * token back if this is the first token or if a MIC exchange
929 		 * is required.
930 		 */
931 		if (*send_token == CONT_TOKEN_SEND &&
932 		    mechtok_out->length == 0 &&
933 		    (!sc->mic_reqd || !(sc->ctx_flags & GSS_C_INTEG_FLAG)))
934 			*send_token = NO_TOKEN_SEND;
935 
936 		return GSS_S_COMPLETE;
937 	}
938 
939 	if (ret == GSS_S_CONTINUE_NEEDED)
940 		return GSS_S_COMPLETE;
941 
942 	if (*send_token != INIT_TOKEN_SEND) {
943 		*send_token = ERROR_TOKEN_SEND;
944 		return ret;
945 	}
946 
947 	/*
948 	 * Since this is the first token, we can fall back to later mechanisms
949 	 * in the list.  Since the mechanism list is expected to be short, we
950 	 * can do this with recursion.  If all mechanisms produce errors, the
951 	 * caller should get the error from the first mech in the list.
952 	 */
953 	gssalloc_free(sc->mech_set->elements->elements);
954 	memmove(sc->mech_set->elements, sc->mech_set->elements + 1,
955 		--sc->mech_set->count * sizeof(*sc->mech_set->elements));
956 	if (sc->mech_set->count == 0)
957 		goto fail;
958 	gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
959 	if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0)
960 		goto fail;
961 	gss_delete_sec_context(&tmpmin, &sc->ctx_handle, GSS_C_NO_BUFFER);
962 	tmpret = init_ctx_call_init(&tmpmin, sc, spcred, acc_negState,
963 				    target_name, req_flags, time_req,
964 				    mechtok_in, bindings, mechtok_out,
965 				    time_rec, send_token);
966 	if (HARD_ERROR(tmpret))
967 		goto fail;
968 	*minor_status = tmpmin;
969 	return tmpret;
970 
971 fail:
972 	/* Don't output token on error from first call. */
973 	*send_token = NO_TOKEN_SEND;
974 	return ret;
975 }
976 
977 /*ARGSUSED*/
978 OM_uint32 KRB5_CALLCONV
spnego_gss_init_sec_context(OM_uint32 * minor_status,gss_cred_id_t claimant_cred_handle,gss_ctx_id_t * context_handle,gss_name_t target_name,gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,gss_channel_bindings_t bindings,gss_buffer_t input_token,gss_OID * actual_mech,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)979 spnego_gss_init_sec_context(
980 			OM_uint32 *minor_status,
981 			gss_cred_id_t claimant_cred_handle,
982 			gss_ctx_id_t *context_handle,
983 			gss_name_t target_name,
984 			gss_OID mech_type,
985 			OM_uint32 req_flags,
986 			OM_uint32 time_req,
987 			gss_channel_bindings_t bindings,
988 			gss_buffer_t input_token,
989 			gss_OID *actual_mech,
990 			gss_buffer_t output_token,
991 			OM_uint32 *ret_flags,
992 			OM_uint32 *time_rec)
993 {
994 	send_token_flag send_token = NO_TOKEN_SEND;
995 	OM_uint32 tmpmin, ret, negState = UNSPECIFIED, acc_negState;
996 	gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
997 	gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
998 	spnego_gss_cred_id_t spcred = NULL;
999 	spnego_gss_ctx_id_t spnego_ctx = NULL;
1000 
1001 	dsyslog("Entering init_sec_context\n");
1002 
1003 	mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
1004 
1005 	/*
1006 	 * This function works in three steps:
1007 	 *
1008 	 *   1. Perform mechanism negotiation.
1009 	 *   2. Invoke the negotiated or optimistic mech's gss_init_sec_context
1010 	 *      function and examine the results.
1011 	 *   3. Process or generate MICs if necessary.
1012 	 *
1013 	 * The three steps share responsibility for determining when the
1014 	 * exchange is complete.  If the selected mech completed in a previous
1015 	 * call and no MIC exchange is expected, then step 1 will decide.  If
1016 	 * the selected mech completes in this call and no MIC exchange is
1017 	 * expected, then step 2 will decide.  If a MIC exchange is expected,
1018 	 * then step 3 will decide.  If an error occurs in any step, the
1019 	 * exchange will be aborted, possibly with an error token.
1020 	 *
1021 	 * negState determines the state of the negotiation, and is
1022 	 * communicated to the acceptor if a continuing token is sent.
1023 	 * send_token is used to indicate what type of token, if any, should be
1024 	 * generated.
1025 	 */
1026 
1027 	/* Validate arguments. */
1028 	if (minor_status != NULL)
1029 		*minor_status = 0;
1030 	if (output_token != GSS_C_NO_BUFFER) {
1031 		output_token->length = 0;
1032 		output_token->value = NULL;
1033 	}
1034 	if (minor_status == NULL ||
1035 	    output_token == GSS_C_NO_BUFFER ||
1036 	    context_handle == NULL)
1037 		return GSS_S_CALL_INACCESSIBLE_WRITE;
1038 
1039 	if (actual_mech != NULL)
1040 		*actual_mech = GSS_C_NO_OID;
1041 	if (time_rec != NULL)
1042 		*time_rec = 0;
1043 
1044 	/* Step 1: perform mechanism negotiation. */
1045 	spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
1046 	spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
1047 	if (spnego_ctx == NULL) {
1048 		ret = init_ctx_new(minor_status, spcred, &send_token,
1049 				   &spnego_ctx);
1050 		if (ret != GSS_S_COMPLETE)
1051 			goto cleanup;
1052 		*context_handle = (gss_ctx_id_t)spnego_ctx;
1053 		acc_negState = UNSPECIFIED;
1054 	} else {
1055 		ret = init_ctx_cont(minor_status, spnego_ctx, input_token,
1056 				    &mechtok_in, &mechListMIC_in,
1057 				    &acc_negState, &send_token);
1058 		if (ret != GSS_S_COMPLETE)
1059 			goto cleanup;
1060 	}
1061 
1062 	/* Step 2: invoke the selected or optimistic mechanism's
1063 	 * gss_init_sec_context function, if it didn't complete previously. */
1064 	if (!spnego_ctx->mech_complete) {
1065 		ret = init_ctx_call_init(minor_status, spnego_ctx, spcred,
1066 					 acc_negState, target_name, req_flags,
1067 					 time_req, mechtok_in, bindings,
1068 					 &mechtok_out, time_rec, &send_token);
1069 		if (ret != GSS_S_COMPLETE)
1070 			goto cleanup;
1071 
1072 		/* Give the mechanism a chance to force a mechlistMIC. */
1073 		if (mech_requires_mechlistMIC(spnego_ctx))
1074 			spnego_ctx->mic_reqd = 1;
1075 	}
1076 
1077 	/* Step 3: process or generate the MIC, if the negotiated mech is
1078 	 * complete and supports MICs.  Also decide the outgoing negState. */
1079 	negState = ACCEPT_INCOMPLETE;
1080 	if (spnego_ctx->mech_complete &&
1081 	    (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
1082 
1083 		ret = handle_mic(minor_status,
1084 				 mechListMIC_in,
1085 				 (mechtok_out.length != 0),
1086 				 spnego_ctx, &mechListMIC_out,
1087 				 &negState, &send_token);
1088 		if (HARD_ERROR(ret))
1089 			goto cleanup;
1090 	}
1091 
1092 	if (ret_flags != NULL)
1093 		*ret_flags = spnego_ctx->ctx_flags & ~GSS_C_PROT_READY_FLAG;
1094 
1095 	ret = (send_token == NO_TOKEN_SEND || negState == ACCEPT_COMPLETE) ?
1096 		GSS_S_COMPLETE : GSS_S_CONTINUE_NEEDED;
1097 
1098 cleanup:
1099 	if (send_token == INIT_TOKEN_SEND) {
1100 		if (make_spnego_tokenInit_msg(spnego_ctx,
1101 					      0,
1102 					      mechListMIC_out,
1103 					      req_flags,
1104 					      &mechtok_out, send_token,
1105 					      output_token) < 0) {
1106 			ret = GSS_S_FAILURE;
1107 		}
1108 	} else if (send_token != NO_TOKEN_SEND) {
1109 		if (send_token == ERROR_TOKEN_SEND)
1110 			negState = REJECT;
1111 		if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
1112 					      &mechtok_out, mechListMIC_out,
1113 					      send_token,
1114 					      output_token) < 0) {
1115 			ret = GSS_S_FAILURE;
1116 		}
1117 	}
1118 	gss_release_buffer(&tmpmin, &mechtok_out);
1119 	if (ret == GSS_S_COMPLETE) {
1120 		spnego_ctx->opened = 1;
1121 		if (actual_mech != NULL)
1122 			*actual_mech = spnego_ctx->actual_mech;
1123 		/* Get an updated lifetime if we didn't call into the mech. */
1124 		if (time_rec != NULL && *time_rec == 0) {
1125 			(void) gss_context_time(&tmpmin,
1126 						spnego_ctx->ctx_handle,
1127 						time_rec);
1128 		}
1129 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
1130 		if (spnego_ctx != NULL) {
1131 			gss_delete_sec_context(&tmpmin,
1132 					       &spnego_ctx->ctx_handle,
1133 					       GSS_C_NO_BUFFER);
1134 			release_spnego_ctx(&spnego_ctx);
1135 		}
1136 		*context_handle = GSS_C_NO_CONTEXT;
1137 	}
1138 	if (mechtok_in != GSS_C_NO_BUFFER) {
1139 		gss_release_buffer(&tmpmin, mechtok_in);
1140 		free(mechtok_in);
1141 	}
1142 	if (mechListMIC_in != GSS_C_NO_BUFFER) {
1143 		gss_release_buffer(&tmpmin, mechListMIC_in);
1144 		free(mechListMIC_in);
1145 	}
1146 	if (mechListMIC_out != GSS_C_NO_BUFFER) {
1147 		gss_release_buffer(&tmpmin, mechListMIC_out);
1148 		free(mechListMIC_out);
1149 	}
1150 	return ret;
1151 } /* init_sec_context */
1152 
1153 /* We don't want to import KRB5 headers here */
1154 static const gss_OID_desc gss_mech_krb5_oid =
1155 	{ 9, "\052\206\110\206\367\022\001\002\002" };
1156 static const gss_OID_desc gss_mech_krb5_wrong_oid =
1157 	{ 9, "\052\206\110\202\367\022\001\002\002" };
1158 
1159 /*
1160  * NegHints ::= SEQUENCE {
1161  *    hintName       [0]  GeneralString      OPTIONAL,
1162  *    hintAddress    [1]  OCTET STRING       OPTIONAL
1163  * }
1164  */
1165 
1166 #define HOST_PREFIX	"host@"
1167 #define HOST_PREFIX_LEN	(sizeof(HOST_PREFIX) - 1)
1168 
1169 /* Encode the dummy hintname string (as specified in [MS-SPNG]) into a
1170  * DER-encoded [0] tagged GeneralString, and place the result in *outbuf. */
1171 static int
make_NegHints(OM_uint32 * minor_status,gss_buffer_t * outbuf)1172 make_NegHints(OM_uint32 *minor_status, gss_buffer_t *outbuf)
1173 {
1174 	OM_uint32 major_status;
1175 	size_t hint_len, tlen;
1176 	uint8_t *t;
1177 	const char *hintname = "not_defined_in_RFC4178@please_ignore";
1178 	const size_t hintname_len = strlen(hintname);
1179 	struct k5buf buf;
1180 
1181 	*outbuf = GSS_C_NO_BUFFER;
1182 	major_status = GSS_S_FAILURE;
1183 
1184 	hint_len = k5_der_value_len(hintname_len);
1185 	tlen = k5_der_value_len(hint_len);
1186 
1187 	t = gssalloc_malloc(tlen);
1188 	if (t == NULL) {
1189 		*minor_status = ENOMEM;
1190 		goto errout;
1191 	}
1192 	k5_buf_init_fixed(&buf, t, tlen);
1193 
1194 	k5_der_add_taglen(&buf, CONTEXT | 0x00, hint_len);
1195 	k5_der_add_value(&buf, GENERAL_STRING, hintname, hintname_len);
1196 	assert(buf.len == tlen);
1197 
1198 	*outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1199 	if (*outbuf == NULL) {
1200 		*minor_status = ENOMEM;
1201 		goto errout;
1202 	}
1203 	(*outbuf)->value = (void *)t;
1204 	(*outbuf)->length = tlen;
1205 
1206 	t = NULL; /* don't free */
1207 
1208 	*minor_status = 0;
1209 	major_status = GSS_S_COMPLETE;
1210 
1211 errout:
1212 	if (t != NULL) {
1213 		free(t);
1214 	}
1215 
1216 	return (major_status);
1217 }
1218 
1219 /*
1220  * Create a new SPNEGO context handle for the initial call to
1221  * spnego_gss_accept_sec_context() when the request is empty.  For empty
1222  * requests, we implement the Microsoft NegHints extension to SPNEGO for
1223  * compatibility with some versions of Samba.  See:
1224  * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-spng/8e71cf53-e867-4b79-b5b5-38c92be3d472
1225  */
1226 static OM_uint32
acc_ctx_hints(OM_uint32 * minor_status,spnego_gss_cred_id_t spcred,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token,spnego_gss_ctx_id_t * sc_out)1227 acc_ctx_hints(OM_uint32 *minor_status,
1228 	      spnego_gss_cred_id_t spcred,
1229 	      gss_buffer_t *mechListMIC,
1230 	      OM_uint32 *negState,
1231 	      send_token_flag *return_token,
1232 	      spnego_gss_ctx_id_t *sc_out)
1233 {
1234 	OM_uint32 ret;
1235 	spnego_gss_ctx_id_t sc = NULL;
1236 
1237 	*mechListMIC = GSS_C_NO_BUFFER;
1238 	*return_token = NO_TOKEN_SEND;
1239 	*negState = REJECT;
1240 	*minor_status = 0;
1241 	*sc_out = NULL;
1242 
1243 	ret = make_NegHints(minor_status, mechListMIC);
1244 	if (ret != GSS_S_COMPLETE)
1245 		goto cleanup;
1246 
1247 	sc = create_spnego_ctx(0);
1248 	if (sc == NULL) {
1249 		ret = GSS_S_FAILURE;
1250 		goto cleanup;
1251 	}
1252 
1253 	ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_ACCEPT);
1254 	if (ret != GSS_S_COMPLETE)
1255 		goto cleanup;
1256 
1257 	if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
1258 		ret = GSS_S_FAILURE;
1259 		goto cleanup;
1260 	}
1261 	sc->internal_mech = GSS_C_NO_OID;
1262 
1263 	*negState = ACCEPT_INCOMPLETE;
1264 	*return_token = INIT_TOKEN_SEND;
1265 	sc->firstpass = 1;
1266 	*sc_out = sc;
1267 	sc = NULL;
1268 	ret = GSS_S_COMPLETE;
1269 
1270 cleanup:
1271 	release_spnego_ctx(&sc);
1272 
1273 	return ret;
1274 }
1275 
1276 /*
1277  * Create a new SPNEGO context handle for the initial call to
1278  * spnego_gss_accept_sec_context().  Set negState to REJECT if the token is
1279  * defective, else ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether
1280  * the initiator's preferred mechanism is supported.
1281  */
1282 static OM_uint32
acc_ctx_new(OM_uint32 * minor_status,gss_buffer_t buf,spnego_gss_cred_id_t spcred,gss_buffer_t * mechToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token,spnego_gss_ctx_id_t * sc_out)1283 acc_ctx_new(OM_uint32 *minor_status,
1284 	    gss_buffer_t buf,
1285 	    spnego_gss_cred_id_t spcred,
1286 	    gss_buffer_t *mechToken,
1287 	    gss_buffer_t *mechListMIC,
1288 	    OM_uint32 *negState,
1289 	    send_token_flag *return_token,
1290 	    spnego_gss_ctx_id_t *sc_out)
1291 {
1292 	OM_uint32 tmpmin, ret, req_flags;
1293 	gss_OID_set mechTypes;
1294 	gss_buffer_desc der_mechTypes;
1295 	gss_OID mech_wanted;
1296 	spnego_gss_ctx_id_t sc = NULL;
1297 
1298 	ret = GSS_S_DEFECTIVE_TOKEN;
1299 	der_mechTypes.length = 0;
1300 	der_mechTypes.value = NULL;
1301 	*mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1302 	mechTypes = GSS_C_NO_OID_SET;
1303 	*return_token = ERROR_TOKEN_SEND;
1304 	*negState = REJECT;
1305 	*minor_status = 0;
1306 
1307 	ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1308 			       &mechTypes, &req_flags,
1309 			       mechToken, mechListMIC);
1310 	if (ret != GSS_S_COMPLETE) {
1311 		goto cleanup;
1312 	}
1313 
1314 	sc = create_spnego_ctx(0);
1315 	if (sc == NULL) {
1316 		ret = GSS_S_FAILURE;
1317 		*return_token = NO_TOKEN_SEND;
1318 		goto cleanup;
1319 	}
1320 
1321 	ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_ACCEPT);
1322 	if (ret != GSS_S_COMPLETE) {
1323 		*return_token = NO_TOKEN_SEND;
1324 		goto cleanup;
1325 	}
1326 	/*
1327 	 * Select the best match between the list of mechs
1328 	 * that the initiator requested and the list that
1329 	 * the acceptor will support.
1330 	 */
1331 	mech_wanted = negotiate_mech(sc, mechTypes, negState);
1332 	if (*negState == REJECT) {
1333 		ret = GSS_S_BAD_MECH;
1334 		goto cleanup;
1335 	}
1336 
1337 	sc->internal_mech = mech_wanted;
1338 	sc->DER_mechTypes = der_mechTypes;
1339 	der_mechTypes.length = 0;
1340 	der_mechTypes.value = NULL;
1341 
1342 	if (*negState == REQUEST_MIC)
1343 		sc->mic_reqd = 1;
1344 
1345 	*return_token = INIT_TOKEN_SEND;
1346 	sc->firstpass = 1;
1347 	*sc_out = sc;
1348 	sc = NULL;
1349 	ret = GSS_S_COMPLETE;
1350 
1351 cleanup:
1352 	release_spnego_ctx(&sc);
1353 	gss_release_oid_set(&tmpmin, &mechTypes);
1354 	if (der_mechTypes.length != 0)
1355 		gss_release_buffer(&tmpmin, &der_mechTypes);
1356 
1357 	return ret;
1358 }
1359 
1360 static OM_uint32
acc_ctx_cont(OM_uint32 * minstat,gss_buffer_t buf,spnego_gss_ctx_id_t sc,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token)1361 acc_ctx_cont(OM_uint32 *minstat,
1362 	     gss_buffer_t buf,
1363 	     spnego_gss_ctx_id_t sc,
1364 	     gss_buffer_t *responseToken,
1365 	     gss_buffer_t *mechListMIC,
1366 	     OM_uint32 *negState,
1367 	     send_token_flag *return_token)
1368 {
1369 	OM_uint32 ret, tmpmin;
1370 	gss_OID supportedMech;
1371 	struct k5input in;
1372 
1373 	ret = GSS_S_DEFECTIVE_TOKEN;
1374 	*negState = REJECT;
1375 	*minstat = 0;
1376 	supportedMech = GSS_C_NO_OID;
1377 	*return_token = ERROR_TOKEN_SEND;
1378 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1379 
1380 	k5_input_init(&in, buf->value, buf->length);
1381 
1382 	/* Attempt to work with old Sun SPNEGO. */
1383 	if (in.len > 0 && *in.ptr == HEADER_ID) {
1384 		ret = verify_token_header(&in, gss_mech_spnego);
1385 		if (ret) {
1386 			*minstat = ret;
1387 			return GSS_S_DEFECTIVE_TOKEN;
1388 		}
1389 	}
1390 
1391 	ret = get_negTokenResp(minstat, &in, negState, &supportedMech,
1392 			       responseToken, mechListMIC);
1393 	if (ret != GSS_S_COMPLETE)
1394 		goto cleanup;
1395 
1396 	if (*responseToken == GSS_C_NO_BUFFER &&
1397 	    *mechListMIC == GSS_C_NO_BUFFER) {
1398 
1399 		ret = GSS_S_DEFECTIVE_TOKEN;
1400 		goto cleanup;
1401 	}
1402 	if (supportedMech != GSS_C_NO_OID) {
1403 		ret = GSS_S_DEFECTIVE_TOKEN;
1404 		goto cleanup;
1405 	}
1406 	sc->firstpass = 0;
1407 	*negState = ACCEPT_INCOMPLETE;
1408 	*return_token = CONT_TOKEN_SEND;
1409 cleanup:
1410 	if (supportedMech != GSS_C_NO_OID) {
1411 		generic_gss_release_oid(&tmpmin, &supportedMech);
1412 	}
1413 	return ret;
1414 }
1415 
1416 /*
1417  * Verify that mech OID is either exactly the same as the negotiated
1418  * mech OID, or is a mech OID supported by the negotiated mech.  MS
1419  * implementations can list a most preferred mech using an incorrect
1420  * krb5 OID while emitting a krb5 initiator mech token having the
1421  * correct krb5 mech OID.
1422  */
1423 static OM_uint32
acc_ctx_vfy_oid(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,gss_OID mechoid,OM_uint32 * negState,send_token_flag * tokflag)1424 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1425 		spnego_gss_ctx_id_t sc, gss_OID mechoid,
1426 		OM_uint32 *negState, send_token_flag *tokflag)
1427 {
1428 	OM_uint32 ret, tmpmin;
1429 	gss_mechanism mech = NULL;
1430 	gss_OID_set mech_set = GSS_C_NO_OID_SET;
1431 	int present = 0;
1432 
1433 	if (g_OID_equal(sc->internal_mech, mechoid))
1434 		return GSS_S_COMPLETE;
1435 
1436 	mech = gssint_get_mechanism(sc->internal_mech);
1437 	if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1438 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1439 		map_errcode(minor_status);
1440 		*negState = REJECT;
1441 		*tokflag = ERROR_TOKEN_SEND;
1442 		return GSS_S_BAD_MECH;
1443 	}
1444 	ret = mech->gss_indicate_mechs(minor_status, &mech_set);
1445 	if (ret != GSS_S_COMPLETE) {
1446 		*tokflag = NO_TOKEN_SEND;
1447 		map_error(minor_status, mech);
1448 		goto cleanup;
1449 	}
1450 	ret = gss_test_oid_set_member(minor_status, mechoid,
1451 				      mech_set, &present);
1452 	if (ret != GSS_S_COMPLETE)
1453 		goto cleanup;
1454 	if (!present) {
1455 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1456 		map_errcode(minor_status);
1457 		*negState = REJECT;
1458 		*tokflag = ERROR_TOKEN_SEND;
1459 		ret = GSS_S_BAD_MECH;
1460 	}
1461 cleanup:
1462 	gss_release_oid_set(&tmpmin, &mech_set);
1463 	return ret;
1464 }
1465 #ifndef LEAN_CLIENT
1466 /*
1467  * Wrap call to gss_accept_sec_context() and update state
1468  * accordingly.
1469  */
1470 static OM_uint32
acc_ctx_call_acc(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,spnego_gss_cred_id_t spcred,gss_buffer_t mechtok_in,gss_channel_bindings_t bindings,gss_buffer_t mechtok_out,OM_uint32 * time_rec,OM_uint32 * negState,send_token_flag * tokflag)1471 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1472 		 spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
1473 		 gss_channel_bindings_t bindings, gss_buffer_t mechtok_out,
1474 		 OM_uint32 *time_rec, OM_uint32 *negState,
1475 		 send_token_flag *tokflag)
1476 {
1477 	OM_uint32 ret, tmpmin;
1478 	gss_OID_desc mechoid;
1479 	gss_cred_id_t mcred;
1480 	int negoex = gss_oid_equal(sc->internal_mech, &negoex_mech);
1481 
1482 	if (sc->ctx_handle == GSS_C_NO_CONTEXT && !negoex) {
1483 		/*
1484 		 * mechoid is an alias; don't free it.
1485 		 */
1486 		ret = gssint_get_mech_type(&mechoid, mechtok_in);
1487 		if (ret != GSS_S_COMPLETE) {
1488 			*tokflag = NO_TOKEN_SEND;
1489 			return ret;
1490 		}
1491 		ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1492 				      negState, tokflag);
1493 		if (ret != GSS_S_COMPLETE)
1494 			return ret;
1495 	}
1496 
1497 	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
1498 	if (negoex) {
1499 		ret = negoex_accept(minor_status, sc, mcred, mechtok_in,
1500 				    bindings, mechtok_out, time_rec);
1501 	} else {
1502 		(void) gss_release_name(&tmpmin, &sc->internal_name);
1503 		(void) gss_release_cred(&tmpmin, &sc->deleg_cred);
1504 		ret = gss_accept_sec_context(minor_status, &sc->ctx_handle,
1505 					     mcred, mechtok_in, bindings,
1506 					     &sc->internal_name,
1507 					     &sc->actual_mech, mechtok_out,
1508 					     &sc->ctx_flags, time_rec,
1509 					     &sc->deleg_cred);
1510 	}
1511 	if (ret == GSS_S_COMPLETE) {
1512 #ifdef MS_BUG_TEST
1513 		/*
1514 		 * Force MIC to be not required even if we previously
1515 		 * requested a MIC.
1516 		 */
1517 		char *envstr = getenv("MS_FORCE_NO_MIC");
1518 
1519 		if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1520 		    !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1521 		    sc->mic_reqd) {
1522 
1523 			sc->mic_reqd = 0;
1524 		}
1525 #endif
1526 		sc->mech_complete = 1;
1527 
1528 		if (!sc->mic_reqd ||
1529 		    !(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1530 			/* No MIC exchange required, so we're done. */
1531 			*negState = ACCEPT_COMPLETE;
1532 			ret = GSS_S_COMPLETE;
1533 		} else {
1534 			/* handle_mic will decide if we're done. */
1535 			ret = GSS_S_CONTINUE_NEEDED;
1536 		}
1537 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
1538 		*negState = REJECT;
1539 		*tokflag = ERROR_TOKEN_SEND;
1540 	}
1541 	return ret;
1542 }
1543 
1544 /*ARGSUSED*/
1545 OM_uint32 KRB5_CALLCONV
spnego_gss_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_cred_id_t verifier_cred_handle,gss_buffer_t input_token,gss_channel_bindings_t bindings,gss_name_t * src_name,gss_OID * mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * delegated_cred_handle)1546 spnego_gss_accept_sec_context(
1547 			    OM_uint32 *minor_status,
1548 			    gss_ctx_id_t *context_handle,
1549 			    gss_cred_id_t verifier_cred_handle,
1550 			    gss_buffer_t input_token,
1551 			    gss_channel_bindings_t bindings,
1552 			    gss_name_t *src_name,
1553 			    gss_OID *mech_type,
1554 			    gss_buffer_t output_token,
1555 			    OM_uint32 *ret_flags,
1556 			    OM_uint32 *time_rec,
1557 			    gss_cred_id_t *delegated_cred_handle)
1558 {
1559 	OM_uint32 ret, tmpmin, negState;
1560 	send_token_flag return_token;
1561 	gss_buffer_t mechtok_in, mic_in, mic_out;
1562 	gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1563 	spnego_gss_ctx_id_t sc = NULL;
1564 	spnego_gss_cred_id_t spcred = NULL;
1565 	int sendTokenInit = 0, tmpret;
1566 
1567 	mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1568 
1569 	/*
1570 	 * This function works in three steps:
1571 	 *
1572 	 *   1. Perform mechanism negotiation.
1573 	 *   2. Invoke the negotiated mech's gss_accept_sec_context function
1574 	 *      and examine the results.
1575 	 *   3. Process or generate MICs if necessary.
1576 	 *
1577 	 * Step one determines whether the negotiation requires a MIC exchange,
1578 	 * while steps two and three share responsibility for determining when
1579 	 * the exchange is complete.  If the selected mech completes in this
1580 	 * call and no MIC exchange is expected, then step 2 will decide.  If a
1581 	 * MIC exchange is expected, then step 3 will decide.  If an error
1582 	 * occurs in any step, the exchange will be aborted, possibly with an
1583 	 * error token.
1584 	 *
1585 	 * negState determines the state of the negotiation, and is
1586 	 * communicated to the acceptor if a continuing token is sent.
1587 	 * return_token is used to indicate what type of token, if any, should
1588 	 * be generated.
1589 	 */
1590 
1591 	/* Validate arguments. */
1592 	if (minor_status != NULL)
1593 		*minor_status = 0;
1594 	if (output_token != GSS_C_NO_BUFFER) {
1595 		output_token->length = 0;
1596 		output_token->value = NULL;
1597 	}
1598 	if (src_name != NULL)
1599 		*src_name = GSS_C_NO_NAME;
1600 	if (mech_type != NULL)
1601 		*mech_type = GSS_C_NO_OID;
1602 	if (time_rec != NULL)
1603 		*time_rec = 0;
1604 	if (ret_flags != NULL)
1605 		*ret_flags = 0;
1606 	if (delegated_cred_handle != NULL)
1607 		*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1608 
1609 	if (minor_status == NULL ||
1610 	    output_token == GSS_C_NO_BUFFER ||
1611 	    context_handle == NULL)
1612 		return GSS_S_CALL_INACCESSIBLE_WRITE;
1613 
1614 	if (input_token == GSS_C_NO_BUFFER)
1615 		return GSS_S_CALL_INACCESSIBLE_READ;
1616 
1617 	/* Step 1: Perform mechanism negotiation. */
1618 	sc = (spnego_gss_ctx_id_t)*context_handle;
1619 	spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
1620 	if (sc == NULL && input_token->length == 0) {
1621 		/* Process a request for NegHints. */
1622 		ret = acc_ctx_hints(minor_status, spcred, &mic_out, &negState,
1623 				    &return_token, &sc);
1624 		if (ret != GSS_S_COMPLETE)
1625 			goto cleanup;
1626 		*context_handle = (gss_ctx_id_t)sc;
1627 		sendTokenInit = 1;
1628 		ret = GSS_S_CONTINUE_NEEDED;
1629 	} else if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1630 		if (sc != NULL) {
1631 			/* Discard the context from the NegHints request. */
1632 			release_spnego_ctx(&sc);
1633 			*context_handle = GSS_C_NO_CONTEXT;
1634 		}
1635 		/* Process an initial token; can set negState to
1636 		 * REQUEST_MIC. */
1637 		ret = acc_ctx_new(minor_status, input_token, spcred,
1638 				  &mechtok_in, &mic_in, &negState,
1639 				  &return_token, &sc);
1640 		if (ret != GSS_S_COMPLETE)
1641 			goto cleanup;
1642 		*context_handle = (gss_ctx_id_t)sc;
1643 		ret = GSS_S_CONTINUE_NEEDED;
1644 	} else {
1645 		/* Process a response token.  Can set negState to
1646 		 * ACCEPT_INCOMPLETE. */
1647 		ret = acc_ctx_cont(minor_status, input_token, sc, &mechtok_in,
1648 				   &mic_in, &negState, &return_token);
1649 		if (ret != GSS_S_COMPLETE)
1650 			goto cleanup;
1651 		ret = GSS_S_CONTINUE_NEEDED;
1652 	}
1653 
1654 	/* Step 2: invoke the negotiated mechanism's gss_accept_sec_context
1655 	 * function. */
1656 	/*
1657 	 * Handle mechtok_in and mic_in only if they are
1658 	 * present in input_token.  If neither is present, whether
1659 	 * this is an error depends on whether this is the first
1660 	 * round-trip.  RET is set to a default value according to
1661 	 * whether it is the first round-trip.
1662 	 */
1663 	if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1664 		ret = acc_ctx_call_acc(minor_status, sc, spcred, mechtok_in,
1665 				       bindings, &mechtok_out, time_rec,
1666 				       &negState, &return_token);
1667 	}
1668 
1669 	/* Step 3: process or generate the MIC, if the negotiated mech is
1670 	 * complete and supports MICs. */
1671 	if (!HARD_ERROR(ret) && sc->mech_complete &&
1672 	    (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1673 
1674 		ret = handle_mic(minor_status, mic_in,
1675 				 (mechtok_out.length != 0),
1676 				 sc, &mic_out,
1677 				 &negState, &return_token);
1678 	}
1679 
1680 	if (!HARD_ERROR(ret) && ret_flags != NULL)
1681 		*ret_flags = sc->ctx_flags & ~GSS_C_PROT_READY_FLAG;
1682 
1683 cleanup:
1684 	if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1685 		assert(sc != NULL);
1686 		tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1687 						   GSS_C_NO_BUFFER,
1688 						   return_token, output_token);
1689 		if (tmpret < 0)
1690 			ret = GSS_S_FAILURE;
1691 	} else if (return_token != NO_TOKEN_SEND &&
1692 		   return_token != CHECK_MIC) {
1693 		tmpret = make_spnego_tokenTarg_msg(negState,
1694 						   sc ? sc->internal_mech :
1695 						   GSS_C_NO_OID,
1696 						   &mechtok_out, mic_out,
1697 						   return_token,
1698 						   output_token);
1699 		if (tmpret < 0)
1700 			ret = GSS_S_FAILURE;
1701 	}
1702 	if (ret == GSS_S_COMPLETE) {
1703 		sc->opened = 1;
1704 		if (sc->internal_name != GSS_C_NO_NAME &&
1705 		    src_name != NULL) {
1706 			*src_name = sc->internal_name;
1707 			sc->internal_name = GSS_C_NO_NAME;
1708 		}
1709 		if (mech_type != NULL)
1710 			*mech_type = sc->actual_mech;
1711 		/* Get an updated lifetime if we didn't call into the mech. */
1712 		if (time_rec != NULL && *time_rec == 0) {
1713 			(void) gss_context_time(&tmpmin, sc->ctx_handle,
1714 						time_rec);
1715 		}
1716 		if (delegated_cred_handle != NULL) {
1717 			*delegated_cred_handle = sc->deleg_cred;
1718 			sc->deleg_cred = GSS_C_NO_CREDENTIAL;
1719 		}
1720 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
1721 		if (sc != NULL) {
1722 			gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
1723 					       GSS_C_NO_BUFFER);
1724 			release_spnego_ctx(&sc);
1725 		}
1726 		*context_handle = GSS_C_NO_CONTEXT;
1727 	}
1728 	gss_release_buffer(&tmpmin, &mechtok_out);
1729 	if (mechtok_in != GSS_C_NO_BUFFER) {
1730 		gss_release_buffer(&tmpmin, mechtok_in);
1731 		free(mechtok_in);
1732 	}
1733 	if (mic_in != GSS_C_NO_BUFFER) {
1734 		gss_release_buffer(&tmpmin, mic_in);
1735 		free(mic_in);
1736 	}
1737 	if (mic_out != GSS_C_NO_BUFFER) {
1738 		gss_release_buffer(&tmpmin, mic_out);
1739 		free(mic_out);
1740 	}
1741 	return ret;
1742 }
1743 #endif /*  LEAN_CLIENT */
1744 
1745 static struct {
1746 	OM_uint32 status;
1747 	const char *msg;
1748 } msg_table[] = {
1749 	{ ERR_SPNEGO_NO_MECHS_AVAILABLE,
1750 	  N_("SPNEGO cannot find mechanisms to negotiate") },
1751 	{ ERR_SPNEGO_NO_CREDS_ACQUIRED,
1752 	  N_("SPNEGO failed to acquire creds") },
1753 	{ ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR,
1754 	  N_("SPNEGO acceptor did not select a mechanism") },
1755 	{ ERR_SPNEGO_NEGOTIATION_FAILED,
1756 	  N_("SPNEGO failed to negotiate a mechanism") },
1757 	{ ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR,
1758 	  N_("SPNEGO acceptor did not return a valid token") },
1759 	{ ERR_NEGOEX_INVALID_MESSAGE_SIGNATURE,
1760 	  N_("Invalid NegoEx signature") },
1761 	{ ERR_NEGOEX_INVALID_MESSAGE_TYPE,
1762 	  N_("Invalid NegoEx message type") },
1763 	{ ERR_NEGOEX_INVALID_MESSAGE_SIZE,
1764 	  N_("Invalid NegoEx message size") },
1765 	{ ERR_NEGOEX_INVALID_CONVERSATION_ID,
1766 	  N_("Invalid NegoEx conversation ID") },
1767 	{ ERR_NEGOEX_AUTH_SCHEME_NOT_FOUND,
1768 	  N_("NegoEx authentication scheme not found") },
1769 	{ ERR_NEGOEX_MISSING_NEGO_MESSAGE,
1770 	  N_("Missing NegoEx negotiate message") },
1771 	{ ERR_NEGOEX_MISSING_AP_REQUEST_MESSAGE,
1772 	  N_("Missing NegoEx authentication protocol request message") },
1773 	{ ERR_NEGOEX_NO_AVAILABLE_MECHS,
1774 	  N_("No mutually supported NegoEx authentication schemes") },
1775 	{ ERR_NEGOEX_NO_VERIFY_KEY,
1776 	  N_("No NegoEx verify key") },
1777 	{ ERR_NEGOEX_UNKNOWN_CHECKSUM_SCHEME,
1778 	  N_("Unknown NegoEx checksum scheme") },
1779 	{ ERR_NEGOEX_INVALID_CHECKSUM,
1780 	  N_("Invalid NegoEx checksum") },
1781 	{ ERR_NEGOEX_UNSUPPORTED_CRITICAL_EXTENSION,
1782 	  N_("Unsupported critical NegoEx extension") },
1783 	{ ERR_NEGOEX_UNSUPPORTED_VERSION,
1784 	  N_("Unsupported NegoEx version") },
1785 	{ ERR_NEGOEX_MESSAGE_OUT_OF_SEQUENCE,
1786 	  N_("NegoEx message out of sequence") },
1787 };
1788 
1789 /*ARGSUSED*/
1790 OM_uint32 KRB5_CALLCONV
spnego_gss_display_status(OM_uint32 * minor_status,OM_uint32 status_value,int status_type,gss_OID mech_type,OM_uint32 * message_context,gss_buffer_t status_string)1791 spnego_gss_display_status(
1792 		OM_uint32 *minor_status,
1793 		OM_uint32 status_value,
1794 		int status_type,
1795 		gss_OID mech_type,
1796 		OM_uint32 *message_context,
1797 		gss_buffer_t status_string)
1798 {
1799 	OM_uint32 maj = GSS_S_COMPLETE;
1800 	const char *msg;
1801 	size_t i;
1802 	int ret;
1803 
1804 	*message_context = 0;
1805 	for (i = 0; i < sizeof(msg_table) / sizeof(*msg_table); i++) {
1806 		if (status_value == msg_table[i].status) {
1807 			msg = dgettext(KRB5_TEXTDOMAIN, msg_table[i].msg);
1808 			*status_string = make_err_msg(msg);
1809 			return GSS_S_COMPLETE;
1810 		}
1811 	}
1812 
1813 	/* Not one of our minor codes; might be from a mech.  Call back
1814 	 * to gss_display_status, but first check for recursion. */
1815 	if (k5_getspecific(K5_KEY_GSS_SPNEGO_STATUS) != NULL) {
1816 		/* Perhaps we returned a com_err code like ENOMEM. */
1817 		const char *err = error_message(status_value);
1818 		*status_string = make_err_msg(err);
1819 		return GSS_S_COMPLETE;
1820 	}
1821 	/* Set a non-null pointer value; doesn't matter which one. */
1822 	ret = k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, &ret);
1823 	if (ret != 0) {
1824 		*minor_status = ret;
1825 		return GSS_S_FAILURE;
1826 	}
1827 
1828 	maj = gss_display_status(minor_status, status_value,
1829 				 status_type, mech_type,
1830 				 message_context, status_string);
1831 	/* This is unlikely to fail; not much we can do if it does. */
1832 	(void)k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, NULL);
1833 
1834 	return maj;
1835 }
1836 
1837 
1838 /*ARGSUSED*/
1839 OM_uint32 KRB5_CALLCONV
spnego_gss_import_name(OM_uint32 * minor_status,gss_buffer_t input_name_buffer,gss_OID input_name_type,gss_name_t * output_name)1840 spnego_gss_import_name(
1841 		    OM_uint32 *minor_status,
1842 		    gss_buffer_t input_name_buffer,
1843 		    gss_OID input_name_type,
1844 		    gss_name_t *output_name)
1845 {
1846 	OM_uint32 status;
1847 
1848 	dsyslog("Entering import_name\n");
1849 
1850 	status = gss_import_name(minor_status, input_name_buffer,
1851 			input_name_type, output_name);
1852 
1853 	dsyslog("Leaving import_name\n");
1854 	return (status);
1855 }
1856 
1857 /*ARGSUSED*/
1858 OM_uint32 KRB5_CALLCONV
spnego_gss_release_name(OM_uint32 * minor_status,gss_name_t * input_name)1859 spnego_gss_release_name(
1860 			OM_uint32 *minor_status,
1861 			gss_name_t *input_name)
1862 {
1863 	OM_uint32 status;
1864 
1865 	dsyslog("Entering release_name\n");
1866 
1867 	status = gss_release_name(minor_status, input_name);
1868 
1869 	dsyslog("Leaving release_name\n");
1870 	return (status);
1871 }
1872 
1873 /*ARGSUSED*/
1874 OM_uint32 KRB5_CALLCONV
spnego_gss_duplicate_name(OM_uint32 * minor_status,const gss_name_t input_name,gss_name_t * output_name)1875 spnego_gss_duplicate_name(
1876 			OM_uint32 *minor_status,
1877 			const gss_name_t input_name,
1878 			gss_name_t *output_name)
1879 {
1880 	OM_uint32 status;
1881 
1882 	dsyslog("Entering duplicate_name\n");
1883 
1884 	status = gss_duplicate_name(minor_status, input_name, output_name);
1885 
1886 	dsyslog("Leaving duplicate_name\n");
1887 	return (status);
1888 }
1889 
1890 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_cred(OM_uint32 * minor_status,gss_cred_id_t cred_handle,gss_name_t * name,OM_uint32 * lifetime,int * cred_usage,gss_OID_set * mechanisms)1891 spnego_gss_inquire_cred(
1892 			OM_uint32 *minor_status,
1893 			gss_cred_id_t cred_handle,
1894 			gss_name_t *name,
1895 			OM_uint32 *lifetime,
1896 			int *cred_usage,
1897 			gss_OID_set *mechanisms)
1898 {
1899 	OM_uint32 status;
1900 	spnego_gss_cred_id_t spcred = NULL;
1901 	gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
1902 	OM_uint32 tmp_minor_status;
1903 	OM_uint32 initiator_lifetime, acceptor_lifetime;
1904 
1905 	dsyslog("Entering inquire_cred\n");
1906 
1907 	/*
1908 	 * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
1909 	 * supplied we call gss_inquire_cred_by_mech() on the
1910 	 * first non-SPNEGO mechanism.
1911 	 */
1912 	spcred = (spnego_gss_cred_id_t)cred_handle;
1913 	if (spcred == NULL) {
1914 		status = get_available_mechs(minor_status,
1915 			GSS_C_NO_NAME,
1916 			GSS_C_BOTH,
1917 			GSS_C_NO_CRED_STORE,
1918 			&creds,
1919 			mechanisms, NULL);
1920 		if (status != GSS_S_COMPLETE) {
1921 			dsyslog("Leaving inquire_cred\n");
1922 			return (status);
1923 		}
1924 
1925 		if ((*mechanisms)->count == 0) {
1926 			gss_release_cred(&tmp_minor_status, &creds);
1927 			gss_release_oid_set(&tmp_minor_status, mechanisms);
1928 			dsyslog("Leaving inquire_cred\n");
1929 			return (GSS_S_DEFECTIVE_CREDENTIAL);
1930 		}
1931 
1932 		assert((*mechanisms)->elements != NULL);
1933 
1934 		status = gss_inquire_cred_by_mech(minor_status,
1935 			creds,
1936 			&(*mechanisms)->elements[0],
1937 			name,
1938 			&initiator_lifetime,
1939 			&acceptor_lifetime,
1940 			cred_usage);
1941 		if (status != GSS_S_COMPLETE) {
1942 			gss_release_cred(&tmp_minor_status, &creds);
1943 			dsyslog("Leaving inquire_cred\n");
1944 			return (status);
1945 		}
1946 
1947 		if (lifetime != NULL)
1948 			*lifetime = (*cred_usage == GSS_C_ACCEPT) ?
1949 				acceptor_lifetime : initiator_lifetime;
1950 
1951 		gss_release_cred(&tmp_minor_status, &creds);
1952 	} else {
1953 		status = gss_inquire_cred(minor_status, spcred->mcred,
1954 					  name, lifetime,
1955 					  cred_usage, mechanisms);
1956 	}
1957 
1958 	dsyslog("Leaving inquire_cred\n");
1959 
1960 	return (status);
1961 }
1962 
1963 /*ARGSUSED*/
1964 OM_uint32 KRB5_CALLCONV
spnego_gss_compare_name(OM_uint32 * minor_status,const gss_name_t name1,const gss_name_t name2,int * name_equal)1965 spnego_gss_compare_name(
1966 			OM_uint32 *minor_status,
1967 			const gss_name_t name1,
1968 			const gss_name_t name2,
1969 			int *name_equal)
1970 {
1971 	OM_uint32 status = GSS_S_COMPLETE;
1972 	dsyslog("Entering compare_name\n");
1973 
1974 	status = gss_compare_name(minor_status, name1, name2, name_equal);
1975 
1976 	dsyslog("Leaving compare_name\n");
1977 	return (status);
1978 }
1979 
1980 /*ARGSUSED*/
1981 /*ARGSUSED*/
1982 OM_uint32 KRB5_CALLCONV
spnego_gss_display_name(OM_uint32 * minor_status,gss_name_t input_name,gss_buffer_t output_name_buffer,gss_OID * output_name_type)1983 spnego_gss_display_name(
1984 			OM_uint32 *minor_status,
1985 			gss_name_t input_name,
1986 			gss_buffer_t output_name_buffer,
1987 			gss_OID *output_name_type)
1988 {
1989 	OM_uint32 status = GSS_S_COMPLETE;
1990 	dsyslog("Entering display_name\n");
1991 
1992 	status = gss_display_name(minor_status, input_name,
1993 			output_name_buffer, output_name_type);
1994 
1995 	dsyslog("Leaving display_name\n");
1996 	return (status);
1997 }
1998 
1999 
2000 /*ARGSUSED*/
2001 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_names_for_mech(OM_uint32 * minor_status,gss_OID mechanism,gss_OID_set * name_types)2002 spnego_gss_inquire_names_for_mech(
2003 				OM_uint32	*minor_status,
2004 				gss_OID		mechanism,
2005 				gss_OID_set	*name_types)
2006 {
2007 	OM_uint32   major, minor;
2008 
2009 	dsyslog("Entering inquire_names_for_mech\n");
2010 	/*
2011 	 * We only know how to handle our own mechanism.
2012 	 */
2013 	if ((mechanism != GSS_C_NULL_OID) &&
2014 	    !g_OID_equal(gss_mech_spnego, mechanism)) {
2015 		*minor_status = 0;
2016 		return (GSS_S_FAILURE);
2017 	}
2018 
2019 	major = gss_create_empty_oid_set(minor_status, name_types);
2020 	if (major == GSS_S_COMPLETE) {
2021 		/* Now add our members. */
2022 		if (((major = gss_add_oid_set_member(minor_status,
2023 				(gss_OID) GSS_C_NT_USER_NAME,
2024 				name_types)) == GSS_S_COMPLETE) &&
2025 		    ((major = gss_add_oid_set_member(minor_status,
2026 				(gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2027 				name_types)) == GSS_S_COMPLETE) &&
2028 		    ((major = gss_add_oid_set_member(minor_status,
2029 				(gss_OID) GSS_C_NT_STRING_UID_NAME,
2030 				name_types)) == GSS_S_COMPLETE)) {
2031 			major = gss_add_oid_set_member(minor_status,
2032 				(gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2033 				name_types);
2034 		}
2035 
2036 		if (major != GSS_S_COMPLETE)
2037 			(void) gss_release_oid_set(&minor, name_types);
2038 	}
2039 
2040 	dsyslog("Leaving inquire_names_for_mech\n");
2041 	return (major);
2042 }
2043 
2044 OM_uint32 KRB5_CALLCONV
spnego_gss_unwrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer,gss_buffer_t output_message_buffer,int * conf_state,gss_qop_t * qop_state)2045 spnego_gss_unwrap(
2046 		OM_uint32 *minor_status,
2047 		gss_ctx_id_t context_handle,
2048 		gss_buffer_t input_message_buffer,
2049 		gss_buffer_t output_message_buffer,
2050 		int *conf_state,
2051 		gss_qop_t *qop_state)
2052 {
2053 	OM_uint32 ret;
2054 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2055 
2056 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2057 		return (GSS_S_NO_CONTEXT);
2058 
2059 	ret = gss_unwrap(minor_status,
2060 			sc->ctx_handle,
2061 			input_message_buffer,
2062 			output_message_buffer,
2063 			conf_state,
2064 			qop_state);
2065 
2066 	return (ret);
2067 }
2068 
2069 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_message_buffer,int * conf_state,gss_buffer_t output_message_buffer)2070 spnego_gss_wrap(
2071 		OM_uint32 *minor_status,
2072 		gss_ctx_id_t context_handle,
2073 		int conf_req_flag,
2074 		gss_qop_t qop_req,
2075 		gss_buffer_t input_message_buffer,
2076 		int *conf_state,
2077 		gss_buffer_t output_message_buffer)
2078 {
2079 	OM_uint32 ret;
2080 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2081 
2082 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2083 		return (GSS_S_NO_CONTEXT);
2084 
2085 	ret = gss_wrap(minor_status,
2086 		    sc->ctx_handle,
2087 		    conf_req_flag,
2088 		    qop_req,
2089 		    input_message_buffer,
2090 		    conf_state,
2091 		    output_message_buffer);
2092 
2093 	return (ret);
2094 }
2095 
2096 OM_uint32 KRB5_CALLCONV
spnego_gss_process_context_token(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_buffer_t token_buffer)2097 spnego_gss_process_context_token(
2098 				OM_uint32	*minor_status,
2099 				const gss_ctx_id_t context_handle,
2100 				const gss_buffer_t token_buffer)
2101 {
2102 	OM_uint32 ret;
2103 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2104 
2105 	/* SPNEGO doesn't have its own context tokens. */
2106 	if (!sc->opened)
2107 		return (GSS_S_DEFECTIVE_TOKEN);
2108 
2109 	ret = gss_process_context_token(minor_status,
2110 					sc->ctx_handle,
2111 					token_buffer);
2112 
2113 	return (ret);
2114 }
2115 
2116 OM_uint32 KRB5_CALLCONV
spnego_gss_delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)2117 spnego_gss_delete_sec_context(
2118 			    OM_uint32 *minor_status,
2119 			    gss_ctx_id_t *context_handle,
2120 			    gss_buffer_t output_token)
2121 {
2122 	OM_uint32 ret = GSS_S_COMPLETE;
2123 	spnego_gss_ctx_id_t *ctx =
2124 		    (spnego_gss_ctx_id_t *)context_handle;
2125 
2126 	*minor_status = 0;
2127 
2128 	if (context_handle == NULL)
2129 		return (GSS_S_FAILURE);
2130 
2131 	if (*ctx == NULL)
2132 		return (GSS_S_COMPLETE);
2133 
2134 	(void) gss_delete_sec_context(minor_status, &(*ctx)->ctx_handle,
2135 				      output_token);
2136 	(void) release_spnego_ctx(ctx);
2137 
2138 	return (ret);
2139 }
2140 
2141 OM_uint32 KRB5_CALLCONV
spnego_gss_context_time(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,OM_uint32 * time_rec)2142 spnego_gss_context_time(
2143 			OM_uint32	*minor_status,
2144 			const gss_ctx_id_t context_handle,
2145 			OM_uint32	*time_rec)
2146 {
2147 	OM_uint32 ret;
2148 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2149 
2150 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2151 		return (GSS_S_NO_CONTEXT);
2152 
2153 	ret = gss_context_time(minor_status,
2154 			    sc->ctx_handle,
2155 			    time_rec);
2156 	return (ret);
2157 }
2158 #ifndef LEAN_CLIENT
2159 OM_uint32 KRB5_CALLCONV
spnego_gss_export_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t interprocess_token)2160 spnego_gss_export_sec_context(
2161 			    OM_uint32	  *minor_status,
2162 			    gss_ctx_id_t *context_handle,
2163 			    gss_buffer_t interprocess_token)
2164 {
2165 	OM_uint32 ret;
2166 	spnego_gss_ctx_id_t sc = *(spnego_gss_ctx_id_t *)context_handle;
2167 
2168 	/* We don't currently support exporting partially established
2169 	 * contexts. */
2170 	if (!sc->opened)
2171 		return GSS_S_UNAVAILABLE;
2172 
2173 	ret = gss_export_sec_context(minor_status,
2174 				    &sc->ctx_handle,
2175 				    interprocess_token);
2176 	if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
2177 		release_spnego_ctx(&sc);
2178 		*context_handle = GSS_C_NO_CONTEXT;
2179 	}
2180 	return (ret);
2181 }
2182 
2183 OM_uint32 KRB5_CALLCONV
spnego_gss_import_sec_context(OM_uint32 * minor_status,const gss_buffer_t interprocess_token,gss_ctx_id_t * context_handle)2184 spnego_gss_import_sec_context(
2185 	OM_uint32		*minor_status,
2186 	const gss_buffer_t	interprocess_token,
2187 	gss_ctx_id_t		*context_handle)
2188 {
2189 	OM_uint32 ret, tmpmin;
2190 	gss_ctx_id_t mctx;
2191 	spnego_gss_ctx_id_t sc;
2192 	int initiate, opened;
2193 
2194 	ret = gss_import_sec_context(minor_status, interprocess_token, &mctx);
2195 	if (ret != GSS_S_COMPLETE)
2196 		return ret;
2197 
2198 	ret = gss_inquire_context(&tmpmin, mctx, NULL, NULL, NULL, NULL, NULL,
2199 				  &initiate, &opened);
2200 	if (ret != GSS_S_COMPLETE || !opened) {
2201 		/* We don't currently support importing partially established
2202 		 * contexts. */
2203 		(void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER);
2204 		return GSS_S_FAILURE;
2205 	}
2206 
2207 	sc = create_spnego_ctx(initiate);
2208 	if (sc == NULL) {
2209 		(void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER);
2210 		return GSS_S_FAILURE;
2211 	}
2212 	sc->ctx_handle = mctx;
2213 	sc->opened = 1;
2214 	*context_handle = (gss_ctx_id_t)sc;
2215 	return GSS_S_COMPLETE;
2216 }
2217 #endif /* LEAN_CLIENT */
2218 
2219 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_context(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_name_t * src_name,gss_name_t * targ_name,OM_uint32 * lifetime_rec,gss_OID * mech_type,OM_uint32 * ctx_flags,int * locally_initiated,int * opened)2220 spnego_gss_inquire_context(
2221 			OM_uint32	*minor_status,
2222 			const gss_ctx_id_t context_handle,
2223 			gss_name_t	*src_name,
2224 			gss_name_t	*targ_name,
2225 			OM_uint32	*lifetime_rec,
2226 			gss_OID		*mech_type,
2227 			OM_uint32	*ctx_flags,
2228 			int		*locally_initiated,
2229 			int		*opened)
2230 {
2231 	OM_uint32 ret = GSS_S_COMPLETE;
2232 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2233 
2234 	if (src_name != NULL)
2235 		*src_name = GSS_C_NO_NAME;
2236 	if (targ_name != NULL)
2237 		*targ_name = GSS_C_NO_NAME;
2238 	if (lifetime_rec != NULL)
2239 		*lifetime_rec = 0;
2240 	if (mech_type != NULL)
2241 		*mech_type = (gss_OID)gss_mech_spnego;
2242 	if (ctx_flags != NULL)
2243 		*ctx_flags = 0;
2244 	if (locally_initiated != NULL)
2245 		*locally_initiated = sc->initiate;
2246 	if (opened != NULL)
2247 		*opened = sc->opened;
2248 
2249 	if (sc->ctx_handle != GSS_C_NO_CONTEXT) {
2250 		ret = gss_inquire_context(minor_status, sc->ctx_handle,
2251 					  src_name, targ_name, lifetime_rec,
2252 					  mech_type, ctx_flags, NULL, NULL);
2253 	}
2254 
2255 	if (!sc->opened) {
2256 		/*
2257 		 * We are still doing SPNEGO negotiation, so report SPNEGO as
2258 		 * the OID.  After negotiation is complete we will report the
2259 		 * underlying mechanism OID.
2260 		 */
2261 		if (mech_type != NULL)
2262 			*mech_type = (gss_OID)gss_mech_spnego;
2263 
2264 		/*
2265 		 * Remove flags we don't support with partially-established
2266 		 * contexts.  (Change this to keep GSS_C_TRANS_FLAG if we add
2267 		 * support for exporting partial SPNEGO contexts.)
2268 		 */
2269 		if (ctx_flags != NULL) {
2270 			*ctx_flags &= ~GSS_C_PROT_READY_FLAG;
2271 			*ctx_flags &= ~GSS_C_TRANS_FLAG;
2272 		}
2273 	}
2274 
2275 	return (ret);
2276 }
2277 
2278 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_size_limit(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,OM_uint32 req_output_size,OM_uint32 * max_input_size)2279 spnego_gss_wrap_size_limit(
2280 	OM_uint32	*minor_status,
2281 	const gss_ctx_id_t context_handle,
2282 	int		conf_req_flag,
2283 	gss_qop_t	qop_req,
2284 	OM_uint32	req_output_size,
2285 	OM_uint32	*max_input_size)
2286 {
2287 	OM_uint32 ret;
2288 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2289 
2290 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2291 		return (GSS_S_NO_CONTEXT);
2292 
2293 	ret = gss_wrap_size_limit(minor_status,
2294 				sc->ctx_handle,
2295 				conf_req_flag,
2296 				qop_req,
2297 				req_output_size,
2298 				max_input_size);
2299 	return (ret);
2300 }
2301 
2302 OM_uint32 KRB5_CALLCONV
spnego_gss_localname(OM_uint32 * minor_status,const gss_name_t pname,const gss_const_OID mech_type,gss_buffer_t localname)2303 spnego_gss_localname(OM_uint32 *minor_status, const gss_name_t pname,
2304 		     const gss_const_OID mech_type, gss_buffer_t localname)
2305 {
2306 	return gss_localname(minor_status, pname, GSS_C_NO_OID, localname);
2307 }
2308 
2309 OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_qop_t qop_req,const gss_buffer_t message_buffer,gss_buffer_t message_token)2310 spnego_gss_get_mic(
2311 		OM_uint32 *minor_status,
2312 		const gss_ctx_id_t context_handle,
2313 		gss_qop_t  qop_req,
2314 		const gss_buffer_t message_buffer,
2315 		gss_buffer_t message_token)
2316 {
2317 	OM_uint32 ret;
2318 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2319 
2320 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2321 		return (GSS_S_NO_CONTEXT);
2322 
2323 	ret = gss_get_mic(minor_status,
2324 		    sc->ctx_handle,
2325 		    qop_req,
2326 		    message_buffer,
2327 		    message_token);
2328 	return (ret);
2329 }
2330 
2331 OM_uint32 KRB5_CALLCONV
spnego_gss_verify_mic(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_buffer_t msg_buffer,const gss_buffer_t token_buffer,gss_qop_t * qop_state)2332 spnego_gss_verify_mic(
2333 		OM_uint32 *minor_status,
2334 		const gss_ctx_id_t context_handle,
2335 		const gss_buffer_t msg_buffer,
2336 		const gss_buffer_t token_buffer,
2337 		gss_qop_t *qop_state)
2338 {
2339 	OM_uint32 ret;
2340 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2341 
2342 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2343 		return (GSS_S_NO_CONTEXT);
2344 
2345 	ret = gss_verify_mic(minor_status,
2346 			    sc->ctx_handle,
2347 			    msg_buffer,
2348 			    token_buffer,
2349 			    qop_state);
2350 	return (ret);
2351 }
2352 
2353 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_sec_context_by_oid(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)2354 spnego_gss_inquire_sec_context_by_oid(
2355 		OM_uint32 *minor_status,
2356 		const gss_ctx_id_t context_handle,
2357 		const gss_OID desired_object,
2358 		gss_buffer_set_t *data_set)
2359 {
2360 	OM_uint32 ret;
2361 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2362 
2363 	/* There are no SPNEGO-specific OIDs for this function. */
2364 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2365 		return (GSS_S_UNAVAILABLE);
2366 
2367 	ret = gss_inquire_sec_context_by_oid(minor_status,
2368 			    sc->ctx_handle,
2369 			    desired_object,
2370 			    data_set);
2371 	return (ret);
2372 }
2373 
2374 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_cred_by_oid(OM_uint32 * minor_status,const gss_cred_id_t cred_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)2375 spnego_gss_inquire_cred_by_oid(
2376 		OM_uint32 *minor_status,
2377 		const gss_cred_id_t cred_handle,
2378 		const gss_OID desired_object,
2379 		gss_buffer_set_t *data_set)
2380 {
2381 	OM_uint32 ret;
2382 	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2383 	gss_cred_id_t mcred;
2384 	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2385 	ret = gss_inquire_cred_by_oid(minor_status,
2386 				mcred,
2387 				desired_object,
2388 				data_set);
2389 	return (ret);
2390 }
2391 
2392 /* This is the same OID as KRB5_NO_CI_FLAGS_X_OID. */
2393 #define NO_CI_FLAGS_X_OID_LENGTH 6
2394 #define NO_CI_FLAGS_X_OID "\x2a\x85\x70\x2b\x0d\x1d"
2395 static const gss_OID_desc no_ci_flags_oid[] = {
2396 	{NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID},
2397 };
2398 
2399 OM_uint32 KRB5_CALLCONV
spnego_gss_set_cred_option(OM_uint32 * minor_status,gss_cred_id_t * cred_handle,const gss_OID desired_object,const gss_buffer_t value)2400 spnego_gss_set_cred_option(
2401 		OM_uint32 *minor_status,
2402 		gss_cred_id_t *cred_handle,
2403 		const gss_OID desired_object,
2404 		const gss_buffer_t value)
2405 {
2406 	OM_uint32 ret;
2407 	OM_uint32 tmp_minor_status;
2408 	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)*cred_handle;
2409 	gss_cred_id_t mcred;
2410 
2411 	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2412 	ret = gss_set_cred_option(minor_status,
2413 				  &mcred,
2414 				  desired_object,
2415 				  value);
2416 	if (ret == GSS_S_COMPLETE && spcred == NULL) {
2417 		/*
2418 		 * If the mechanism allocated a new credential handle, then
2419 		 * we need to wrap it up in an SPNEGO credential handle.
2420 		 */
2421 
2422 		ret = create_spnego_cred(minor_status, mcred, &spcred);
2423 		if (ret != GSS_S_COMPLETE) {
2424 			gss_release_cred(&tmp_minor_status, &mcred);
2425 			return (ret);
2426 		}
2427 		*cred_handle = (gss_cred_id_t)spcred;
2428 	}
2429 
2430 	if (ret != GSS_S_COMPLETE)
2431 		return (ret);
2432 
2433 	/* Recognize KRB5_NO_CI_FLAGS_X_OID and avoid asking for integrity. */
2434 	if (g_OID_equal(desired_object, no_ci_flags_oid))
2435 		spcred->no_ask_integ = 1;
2436 
2437 	return (GSS_S_COMPLETE);
2438 }
2439 
2440 OM_uint32 KRB5_CALLCONV
spnego_gss_set_sec_context_option(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,const gss_OID desired_object,const gss_buffer_t value)2441 spnego_gss_set_sec_context_option(
2442 		OM_uint32 *minor_status,
2443 		gss_ctx_id_t *context_handle,
2444 		const gss_OID desired_object,
2445 		const gss_buffer_t value)
2446 {
2447 	OM_uint32 ret;
2448 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)*context_handle;
2449 
2450 	/* There are no SPNEGO-specific OIDs for this function, and we cannot
2451 	 * construct an empty SPNEGO context with it. */
2452 	if (sc == NULL || sc->ctx_handle == GSS_C_NO_CONTEXT)
2453 		return (GSS_S_UNAVAILABLE);
2454 
2455 	ret = gss_set_sec_context_option(minor_status,
2456 			    &sc->ctx_handle,
2457 			    desired_object,
2458 			    value);
2459 	return (ret);
2460 }
2461 
2462 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_aead(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_assoc_buffer,gss_buffer_t input_payload_buffer,int * conf_state,gss_buffer_t output_message_buffer)2463 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2464 		     gss_ctx_id_t context_handle,
2465 		     int conf_req_flag,
2466 		     gss_qop_t qop_req,
2467 		     gss_buffer_t input_assoc_buffer,
2468 		     gss_buffer_t input_payload_buffer,
2469 		     int *conf_state,
2470 		     gss_buffer_t output_message_buffer)
2471 {
2472 	OM_uint32 ret;
2473 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2474 
2475 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2476 		return (GSS_S_NO_CONTEXT);
2477 
2478 	ret = gss_wrap_aead(minor_status,
2479 			    sc->ctx_handle,
2480 			    conf_req_flag,
2481 			    qop_req,
2482 			    input_assoc_buffer,
2483 			    input_payload_buffer,
2484 			    conf_state,
2485 			    output_message_buffer);
2486 
2487 	return (ret);
2488 }
2489 
2490 OM_uint32 KRB5_CALLCONV
spnego_gss_unwrap_aead(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer,gss_buffer_t input_assoc_buffer,gss_buffer_t output_payload_buffer,int * conf_state,gss_qop_t * qop_state)2491 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2492 		       gss_ctx_id_t context_handle,
2493 		       gss_buffer_t input_message_buffer,
2494 		       gss_buffer_t input_assoc_buffer,
2495 		       gss_buffer_t output_payload_buffer,
2496 		       int *conf_state,
2497 		       gss_qop_t *qop_state)
2498 {
2499 	OM_uint32 ret;
2500 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2501 
2502 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2503 		return (GSS_S_NO_CONTEXT);
2504 
2505 	ret = gss_unwrap_aead(minor_status,
2506 			      sc->ctx_handle,
2507 			      input_message_buffer,
2508 			      input_assoc_buffer,
2509 			      output_payload_buffer,
2510 			      conf_state,
2511 			      qop_state);
2512 	return (ret);
2513 }
2514 
2515 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)2516 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2517 		    gss_ctx_id_t context_handle,
2518 		    int conf_req_flag,
2519 		    gss_qop_t qop_req,
2520 		    int *conf_state,
2521 		    gss_iov_buffer_desc *iov,
2522 		    int iov_count)
2523 {
2524 	OM_uint32 ret;
2525 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2526 
2527 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2528 		return (GSS_S_NO_CONTEXT);
2529 
2530 	ret = gss_wrap_iov(minor_status,
2531 			   sc->ctx_handle,
2532 			   conf_req_flag,
2533 			   qop_req,
2534 			   conf_state,
2535 			   iov,
2536 			   iov_count);
2537 	return (ret);
2538 }
2539 
2540 OM_uint32 KRB5_CALLCONV
spnego_gss_unwrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int * conf_state,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)2541 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2542 		      gss_ctx_id_t context_handle,
2543 		      int *conf_state,
2544 		      gss_qop_t *qop_state,
2545 		      gss_iov_buffer_desc *iov,
2546 		      int iov_count)
2547 {
2548 	OM_uint32 ret;
2549 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2550 
2551 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2552 		return (GSS_S_NO_CONTEXT);
2553 
2554 	ret = gss_unwrap_iov(minor_status,
2555 			     sc->ctx_handle,
2556 			     conf_state,
2557 			     qop_state,
2558 			     iov,
2559 			     iov_count);
2560 	return (ret);
2561 }
2562 
2563 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)2564 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2565 			   gss_ctx_id_t context_handle,
2566 			   int conf_req_flag,
2567 			   gss_qop_t qop_req,
2568 			   int *conf_state,
2569 			   gss_iov_buffer_desc *iov,
2570 			   int iov_count)
2571 {
2572 	OM_uint32 ret;
2573 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2574 
2575 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2576 		return (GSS_S_NO_CONTEXT);
2577 
2578 	ret = gss_wrap_iov_length(minor_status,
2579 				  sc->ctx_handle,
2580 				  conf_req_flag,
2581 				  qop_req,
2582 				  conf_state,
2583 				  iov,
2584 				  iov_count);
2585 	return (ret);
2586 }
2587 
2588 
2589 OM_uint32 KRB5_CALLCONV
spnego_gss_complete_auth_token(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer)2590 spnego_gss_complete_auth_token(
2591 		OM_uint32 *minor_status,
2592 		const gss_ctx_id_t context_handle,
2593 		gss_buffer_t input_message_buffer)
2594 {
2595 	OM_uint32 ret;
2596 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2597 
2598 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2599 		return (GSS_S_UNAVAILABLE);
2600 
2601 	ret = gss_complete_auth_token(minor_status,
2602 				      sc->ctx_handle,
2603 				      input_message_buffer);
2604 	return (ret);
2605 }
2606 
2607 OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred_impersonate_name(OM_uint32 * minor_status,const gss_cred_id_t impersonator_cred_handle,const gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)2608 spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
2609 					 const gss_cred_id_t impersonator_cred_handle,
2610 					 const gss_name_t desired_name,
2611 					 OM_uint32 time_req,
2612 					 gss_OID_set desired_mechs,
2613 					 gss_cred_usage_t cred_usage,
2614 					 gss_cred_id_t *output_cred_handle,
2615 					 gss_OID_set *actual_mechs,
2616 					 OM_uint32 *time_rec)
2617 {
2618 	OM_uint32 status, tmpmin;
2619 	gss_OID_set amechs = GSS_C_NULL_OID_SET;
2620 	spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
2621 	gss_cred_id_t imp_mcred, out_mcred = GSS_C_NO_CREDENTIAL;
2622 
2623 	dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
2624 
2625 	if (actual_mechs)
2626 		*actual_mechs = NULL;
2627 
2628 	if (time_rec)
2629 		*time_rec = 0;
2630 
2631 	imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
2632 	imp_mcred = imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL;
2633 	status = gss_inquire_cred(minor_status, imp_mcred, NULL, NULL,
2634 				  NULL, &amechs);
2635 	if (status != GSS_S_COMPLETE)
2636 		return status;
2637 
2638 	status = gss_acquire_cred_impersonate_name(minor_status, imp_mcred,
2639 						   desired_name, time_req,
2640 						   amechs, cred_usage,
2641 						   &out_mcred, actual_mechs,
2642 						   time_rec);
2643 	if (status != GSS_S_COMPLETE)
2644 		goto cleanup;
2645 
2646 	status = create_spnego_cred(minor_status, out_mcred, &out_spcred);
2647 	if (status != GSS_S_COMPLETE)
2648 		goto cleanup;
2649 
2650 	out_mcred = GSS_C_NO_CREDENTIAL;
2651 	*output_cred_handle = (gss_cred_id_t)out_spcred;
2652 
2653 cleanup:
2654 	(void) gss_release_oid_set(&tmpmin, &amechs);
2655 	(void) gss_release_cred(&tmpmin, &out_mcred);
2656 
2657 	dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
2658 	return (status);
2659 }
2660 
2661 OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred_with_password(OM_uint32 * minor_status,const gss_name_t desired_name,const gss_buffer_t password,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)2662 spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
2663 				      const gss_name_t desired_name,
2664 				      const gss_buffer_t password,
2665 				      OM_uint32 time_req,
2666 				      const gss_OID_set desired_mechs,
2667 				      gss_cred_usage_t cred_usage,
2668 				      gss_cred_id_t *output_cred_handle,
2669 				      gss_OID_set *actual_mechs,
2670 				      OM_uint32 *time_rec)
2671 {
2672 	OM_uint32 status, tmpmin;
2673 	gss_OID_set amechs = GSS_C_NULL_OID_SET;
2674 	gss_cred_id_t mcred = NULL;
2675 	spnego_gss_cred_id_t spcred = NULL;
2676 
2677 	dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
2678 
2679 	if (actual_mechs)
2680 		*actual_mechs = NULL;
2681 
2682 	if (time_rec)
2683 		*time_rec = 0;
2684 
2685 	status = get_available_mechs(minor_status, desired_name,
2686 				     cred_usage, GSS_C_NO_CRED_STORE,
2687 				     NULL, &amechs, NULL);
2688 	if (status != GSS_S_COMPLETE)
2689 	    goto cleanup;
2690 
2691 	status = gss_acquire_cred_with_password(minor_status, desired_name,
2692 						password, time_req, amechs,
2693 						cred_usage, &mcred,
2694 						actual_mechs, time_rec);
2695 	if (status != GSS_S_COMPLETE)
2696 	    goto cleanup;
2697 
2698 	status = create_spnego_cred(minor_status, mcred, &spcred);
2699 	if (status != GSS_S_COMPLETE)
2700 		goto cleanup;
2701 
2702 	mcred = GSS_C_NO_CREDENTIAL;
2703 	*output_cred_handle = (gss_cred_id_t)spcred;
2704 
2705 cleanup:
2706 
2707 	(void) gss_release_oid_set(&tmpmin, &amechs);
2708 	(void) gss_release_cred(&tmpmin, &mcred);
2709 
2710 	dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
2711 	return (status);
2712 }
2713 
2714 OM_uint32 KRB5_CALLCONV
spnego_gss_display_name_ext(OM_uint32 * minor_status,gss_name_t name,gss_OID display_as_name_type,gss_buffer_t display_name)2715 spnego_gss_display_name_ext(OM_uint32 *minor_status,
2716 			    gss_name_t name,
2717 			    gss_OID display_as_name_type,
2718 			    gss_buffer_t display_name)
2719 {
2720 	OM_uint32 ret;
2721 	ret = gss_display_name_ext(minor_status,
2722 				   name,
2723 				   display_as_name_type,
2724 				   display_name);
2725 	return (ret);
2726 }
2727 
2728 
2729 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_name(OM_uint32 * minor_status,gss_name_t name,int * name_is_MN,gss_OID * MN_mech,gss_buffer_set_t * attrs)2730 spnego_gss_inquire_name(OM_uint32 *minor_status,
2731 			gss_name_t name,
2732 			int *name_is_MN,
2733 			gss_OID *MN_mech,
2734 			gss_buffer_set_t *attrs)
2735 {
2736 	OM_uint32 ret;
2737 	ret = gss_inquire_name(minor_status,
2738 			       name,
2739 			       name_is_MN,
2740 			       MN_mech,
2741 			       attrs);
2742 	return (ret);
2743 }
2744 
2745 OM_uint32 KRB5_CALLCONV
spnego_gss_get_name_attribute(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t attr,int * authenticated,int * complete,gss_buffer_t value,gss_buffer_t display_value,int * more)2746 spnego_gss_get_name_attribute(OM_uint32 *minor_status,
2747 			      gss_name_t name,
2748 			      gss_buffer_t attr,
2749 			      int *authenticated,
2750 			      int *complete,
2751 			      gss_buffer_t value,
2752 			      gss_buffer_t display_value,
2753 			      int *more)
2754 {
2755 	OM_uint32 ret;
2756 	ret = gss_get_name_attribute(minor_status,
2757 				     name,
2758 				     attr,
2759 				     authenticated,
2760 				     complete,
2761 				     value,
2762 				     display_value,
2763 				     more);
2764 	return (ret);
2765 }
2766 
2767 OM_uint32 KRB5_CALLCONV
spnego_gss_set_name_attribute(OM_uint32 * minor_status,gss_name_t name,int complete,gss_buffer_t attr,gss_buffer_t value)2768 spnego_gss_set_name_attribute(OM_uint32 *minor_status,
2769 			      gss_name_t name,
2770 			      int complete,
2771 			      gss_buffer_t attr,
2772 			      gss_buffer_t value)
2773 {
2774 	OM_uint32 ret;
2775 	ret = gss_set_name_attribute(minor_status,
2776 				     name,
2777 				     complete,
2778 				     attr,
2779 				     value);
2780 	return (ret);
2781 }
2782 
2783 OM_uint32 KRB5_CALLCONV
spnego_gss_delete_name_attribute(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t attr)2784 spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
2785 				 gss_name_t name,
2786 				 gss_buffer_t attr)
2787 {
2788 	OM_uint32 ret;
2789 	ret = gss_delete_name_attribute(minor_status,
2790 					name,
2791 					attr);
2792 	return (ret);
2793 }
2794 
2795 OM_uint32 KRB5_CALLCONV
spnego_gss_export_name_composite(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t exp_composite_name)2796 spnego_gss_export_name_composite(OM_uint32 *minor_status,
2797 				 gss_name_t name,
2798 				 gss_buffer_t exp_composite_name)
2799 {
2800 	OM_uint32 ret;
2801 	ret = gss_export_name_composite(minor_status,
2802 					name,
2803 					exp_composite_name);
2804 	return (ret);
2805 }
2806 
2807 OM_uint32 KRB5_CALLCONV
spnego_gss_map_name_to_any(OM_uint32 * minor_status,gss_name_t name,int authenticated,gss_buffer_t type_id,gss_any_t * output)2808 spnego_gss_map_name_to_any(OM_uint32 *minor_status,
2809 			   gss_name_t name,
2810 			   int authenticated,
2811 			   gss_buffer_t type_id,
2812 			   gss_any_t *output)
2813 {
2814 	OM_uint32 ret;
2815 	ret = gss_map_name_to_any(minor_status,
2816 				  name,
2817 				  authenticated,
2818 				  type_id,
2819 				  output);
2820 	return (ret);
2821 }
2822 
2823 OM_uint32 KRB5_CALLCONV
spnego_gss_release_any_name_mapping(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t type_id,gss_any_t * input)2824 spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,
2825 				    gss_name_t name,
2826 				    gss_buffer_t type_id,
2827 				    gss_any_t *input)
2828 {
2829 	OM_uint32 ret;
2830 	ret = gss_release_any_name_mapping(minor_status,
2831 					   name,
2832 					   type_id,
2833 					   input);
2834 	return (ret);
2835 }
2836 
2837 OM_uint32 KRB5_CALLCONV
spnego_gss_pseudo_random(OM_uint32 * minor_status,gss_ctx_id_t context,int prf_key,const gss_buffer_t prf_in,ssize_t desired_output_len,gss_buffer_t prf_out)2838 spnego_gss_pseudo_random(OM_uint32 *minor_status,
2839 			 gss_ctx_id_t context,
2840 			 int prf_key,
2841 			 const gss_buffer_t prf_in,
2842 			 ssize_t desired_output_len,
2843 			 gss_buffer_t prf_out)
2844 {
2845 	OM_uint32 ret;
2846 	spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context;
2847 
2848 	if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2849 		return (GSS_S_NO_CONTEXT);
2850 
2851 	ret = gss_pseudo_random(minor_status,
2852 				sc->ctx_handle,
2853 				prf_key,
2854 				prf_in,
2855 				desired_output_len,
2856 				prf_out);
2857         return (ret);
2858 }
2859 
2860 OM_uint32 KRB5_CALLCONV
spnego_gss_set_neg_mechs(OM_uint32 * minor_status,gss_cred_id_t cred_handle,const gss_OID_set mech_list)2861 spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
2862 			 gss_cred_id_t cred_handle,
2863 			 const gss_OID_set mech_list)
2864 {
2865 	OM_uint32 ret;
2866 	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2867 
2868 	/* Store mech_list in spcred for use in negotiation logic. */
2869 	gss_release_oid_set(minor_status, &spcred->neg_mechs);
2870 	ret = generic_gss_copy_oid_set(minor_status, mech_list,
2871 				       &spcred->neg_mechs);
2872 	if (ret == GSS_S_COMPLETE) {
2873 		(void) gss_set_neg_mechs(minor_status,
2874 					 spcred->mcred,
2875 					 spcred->neg_mechs);
2876 	}
2877 
2878 	return (ret);
2879 }
2880 
2881 #define SPNEGO_SASL_NAME	"SPNEGO"
2882 #define SPNEGO_SASL_NAME_LEN	(sizeof(SPNEGO_SASL_NAME) - 1)
2883 
2884 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_mech_for_saslname(OM_uint32 * minor_status,const gss_buffer_t sasl_mech_name,gss_OID * mech_type)2885 spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
2886                                      const gss_buffer_t sasl_mech_name,
2887                                      gss_OID *mech_type)
2888 {
2889 	if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&
2890 	    memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,
2891 		   SPNEGO_SASL_NAME_LEN) == 0) {
2892 		if (mech_type != NULL)
2893 			*mech_type = (gss_OID)gss_mech_spnego;
2894 		return (GSS_S_COMPLETE);
2895 	}
2896 
2897 	return (GSS_S_BAD_MECH);
2898 }
2899 
2900 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_saslname_for_mech(OM_uint32 * minor_status,const gss_OID desired_mech,gss_buffer_t sasl_mech_name,gss_buffer_t mech_name,gss_buffer_t mech_description)2901 spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
2902                                      const gss_OID desired_mech,
2903                                      gss_buffer_t sasl_mech_name,
2904                                      gss_buffer_t mech_name,
2905                                      gss_buffer_t mech_description)
2906 {
2907 	*minor_status = 0;
2908 
2909 	if (!g_OID_equal(desired_mech, gss_mech_spnego))
2910 		return (GSS_S_BAD_MECH);
2911 
2912 	if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||
2913 	    !g_make_string_buffer("spnego", mech_name) ||
2914 	    !g_make_string_buffer("Simple and Protected GSS-API "
2915 				  "Negotiation Mechanism", mech_description))
2916 		goto fail;
2917 
2918 	return (GSS_S_COMPLETE);
2919 
2920 fail:
2921 	*minor_status = ENOMEM;
2922 	return (GSS_S_FAILURE);
2923 }
2924 
2925 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_attrs_for_mech(OM_uint32 * minor_status,gss_const_OID mech,gss_OID_set * mech_attrs,gss_OID_set * known_mech_attrs)2926 spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
2927 				  gss_const_OID mech,
2928 				  gss_OID_set *mech_attrs,
2929 				  gss_OID_set *known_mech_attrs)
2930 {
2931 	OM_uint32 major, tmpMinor;
2932 
2933 	/* known_mech_attrs is handled by mechglue */
2934 	*minor_status = 0;
2935 
2936 	if (mech_attrs == NULL)
2937 	    return (GSS_S_COMPLETE);
2938 
2939 	major = gss_create_empty_oid_set(minor_status, mech_attrs);
2940 	if (GSS_ERROR(major))
2941 		goto cleanup;
2942 
2943 #define MA_SUPPORTED(ma)    do {					\
2944 		major = gss_add_oid_set_member(minor_status,		\
2945 					       (gss_OID)ma, mech_attrs); \
2946 		if (GSS_ERROR(major))					\
2947 			goto cleanup;					\
2948 	} while (0)
2949 
2950 	MA_SUPPORTED(GSS_C_MA_MECH_NEGO);
2951 	MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
2952 
2953 cleanup:
2954 	if (GSS_ERROR(major))
2955 		gss_release_oid_set(&tmpMinor, mech_attrs);
2956 
2957 	return (major);
2958 }
2959 
2960 OM_uint32 KRB5_CALLCONV
spnego_gss_export_cred(OM_uint32 * minor_status,gss_cred_id_t cred_handle,gss_buffer_t token)2961 spnego_gss_export_cred(OM_uint32 *minor_status,
2962 		       gss_cred_id_t cred_handle,
2963 		       gss_buffer_t token)
2964 {
2965 	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2966 
2967 	return (gss_export_cred(minor_status, spcred->mcred, token));
2968 }
2969 
2970 OM_uint32 KRB5_CALLCONV
spnego_gss_import_cred(OM_uint32 * minor_status,gss_buffer_t token,gss_cred_id_t * cred_handle)2971 spnego_gss_import_cred(OM_uint32 *minor_status,
2972 		       gss_buffer_t token,
2973 		       gss_cred_id_t *cred_handle)
2974 {
2975 	OM_uint32 ret;
2976 	spnego_gss_cred_id_t spcred;
2977 	gss_cred_id_t mcred;
2978 
2979 	ret = gss_import_cred(minor_status, token, &mcred);
2980 	if (GSS_ERROR(ret))
2981 		return (ret);
2982 
2983 	ret = create_spnego_cred(minor_status, mcred, &spcred);
2984 	if (GSS_ERROR(ret))
2985 	    return (ret);
2986 
2987 	*cred_handle = (gss_cred_id_t)spcred;
2988 	return (ret);
2989 }
2990 
2991 OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)2992 spnego_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
2993 		       gss_qop_t qop_req, gss_iov_buffer_desc *iov,
2994 		       int iov_count)
2995 {
2996     spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2997 
2998     if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2999 	    return (GSS_S_NO_CONTEXT);
3000 
3001     return gss_get_mic_iov(minor_status, sc->ctx_handle, qop_req, iov,
3002 			   iov_count);
3003 }
3004 
3005 OM_uint32 KRB5_CALLCONV
spnego_gss_verify_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)3006 spnego_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
3007 			  gss_qop_t *qop_state, gss_iov_buffer_desc *iov,
3008 			  int iov_count)
3009 {
3010     spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
3011 
3012     if (sc->ctx_handle == GSS_C_NO_CONTEXT)
3013 	    return (GSS_S_NO_CONTEXT);
3014 
3015     return gss_verify_mic_iov(minor_status, sc->ctx_handle, qop_state, iov,
3016 			      iov_count);
3017 }
3018 
3019 OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)3020 spnego_gss_get_mic_iov_length(OM_uint32 *minor_status,
3021 			      gss_ctx_id_t context_handle, gss_qop_t qop_req,
3022 			      gss_iov_buffer_desc *iov, int iov_count)
3023 {
3024     spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
3025 
3026     if (sc->ctx_handle == GSS_C_NO_CONTEXT)
3027 	    return (GSS_S_NO_CONTEXT);
3028 
3029     return gss_get_mic_iov_length(minor_status, sc->ctx_handle, qop_req, iov,
3030 				  iov_count);
3031 }
3032 
3033 /*
3034  * We will release everything but the ctx_handle so that it
3035  * can be passed back to init/accept context. This routine should
3036  * not be called until after the ctx_handle memory is assigned to
3037  * the supplied context handle from init/accept context.
3038  */
3039 static void
release_spnego_ctx(spnego_gss_ctx_id_t * ctx)3040 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
3041 {
3042 	spnego_gss_ctx_id_t context;
3043 	OM_uint32 minor_stat;
3044 	context = *ctx;
3045 
3046 	if (context != NULL) {
3047 		(void) gss_release_buffer(&minor_stat,
3048 					&context->DER_mechTypes);
3049 
3050 		(void) gss_release_oid_set(&minor_stat, &context->mech_set);
3051 
3052 		(void) gss_release_name(&minor_stat, &context->internal_name);
3053 		(void) gss_release_cred(&minor_stat, &context->deleg_cred);
3054 
3055 		negoex_release_context(context);
3056 
3057 		free(context);
3058 		*ctx = NULL;
3059 	}
3060 }
3061 
3062 /*
3063  * Can't use gss_indicate_mechs by itself to get available mechs for
3064  * SPNEGO because it will also return the SPNEGO mech and we do not
3065  * want to consider SPNEGO as an available security mech for
3066  * negotiation. For this reason, get_available_mechs will return
3067  * all available, non-deprecated mechs except SPNEGO and NegoEx-
3068  * only mechanisms.
3069  *
3070  * Note that gss_acquire_cred_from(GSS_C_NO_OID_SET) will filter
3071  * out hidden (GSS_C_MA_NOT_INDICATED) mechanisms such as NegoEx, so
3072  * calling gss_indicate_mechs_by_attrs() also works around that.
3073  *
3074  * If a ptr to a creds list is given, this function will attempt
3075  * to acquire creds for the creds given and trim the list of
3076  * returned mechanisms to only those for which creds are valid.
3077  *
3078  */
3079 static OM_uint32
get_available_mechs(OM_uint32 * minor_status,gss_name_t name,gss_cred_usage_t usage,gss_const_key_value_set_t cred_store,gss_cred_id_t * creds,gss_OID_set * rmechs,OM_uint32 * time_rec)3080 get_available_mechs(OM_uint32 *minor_status,
3081 	gss_name_t name, gss_cred_usage_t usage,
3082 	gss_const_key_value_set_t cred_store,
3083 	gss_cred_id_t *creds, gss_OID_set *rmechs, OM_uint32 *time_rec)
3084 {
3085 	OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
3086 	gss_OID_set mechs, goodmechs;
3087 	gss_OID_set_desc except_attrs;
3088 	gss_OID_desc attr_oids[3];
3089 
3090 	*rmechs = GSS_C_NO_OID_SET;
3091 
3092 	attr_oids[0] = *GSS_C_MA_DEPRECATED;
3093 	attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH;
3094 	attr_oids[2] = *GSS_C_MA_MECH_NEGO;     /* Exclude ourselves */
3095 	except_attrs.count = sizeof(attr_oids) / sizeof(attr_oids[0]);
3096 	except_attrs.elements = attr_oids;
3097 	major_status = gss_indicate_mechs_by_attrs(minor_status,
3098 						   GSS_C_NO_OID_SET,
3099 						   &except_attrs,
3100 						   GSS_C_NO_OID_SET, &mechs);
3101 
3102 	/*
3103 	 * If the caller wanted a list of creds returned,
3104 	 * trim the list of mechanisms down to only those
3105 	 * for which the creds are valid.
3106 	 */
3107 	if (mechs->count > 0 && major_status == GSS_S_COMPLETE &&
3108 	    creds != NULL) {
3109 		major_status = gss_acquire_cred_from(minor_status, name,
3110 						     GSS_C_INDEFINITE,
3111 						     mechs, usage,
3112 						     cred_store, creds,
3113 						     &goodmechs, time_rec);
3114 
3115 		/*
3116 		 * Drop the old list in favor of the new
3117 		 * "trimmed" list.
3118 		 */
3119 		if (major_status == GSS_S_COMPLETE) {
3120 			(void) gss_release_oid_set(&tmpmin, &mechs);
3121 			mechs = goodmechs;
3122 		}
3123 	}
3124 
3125 	if (mechs->count > 0 && major_status == GSS_S_COMPLETE) {
3126 		*rmechs = mechs;
3127 	} else {
3128 		(void) gss_release_oid_set(&tmpmin, &mechs);
3129 		*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
3130 		map_errcode(minor_status);
3131 		if (major_status == GSS_S_COMPLETE)
3132 			major_status = GSS_S_FAILURE;
3133 	}
3134 
3135 	return (major_status);
3136 }
3137 
3138 /* Return true if mech asserts the GSS_C_MA_NEGOEX_AND_SPNEGO attribute. */
3139 static int
negoex_and_spnego(gss_OID mech)3140 negoex_and_spnego(gss_OID mech)
3141 {
3142 	OM_uint32 ret, minor;
3143 	gss_OID_set attrs;
3144 	int present;
3145 
3146 	ret = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
3147 	if (ret != GSS_S_COMPLETE || attrs == GSS_C_NO_OID_SET)
3148 		return 0;
3149 
3150 	(void) generic_gss_test_oid_set_member(&minor,
3151 					       GSS_C_MA_NEGOEX_AND_SPNEGO,
3152 					       attrs, &present);
3153 	(void) gss_release_oid_set(&minor, &attrs);
3154 	return present;
3155 }
3156 
3157 /*
3158  * Fill sc->mech_set with the SPNEGO-negotiable mechanism OIDs, and
3159  * sc->negoex_mechs with an entry for each NegoEx-negotiable mechanism.  Take
3160  * into account the mech set provided with gss_set_neg_mechs() if it exists.
3161  */
3162 static OM_uint32
get_negotiable_mechs(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,spnego_gss_cred_id_t spcred,gss_cred_usage_t usage)3163 get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
3164 		     spnego_gss_cred_id_t spcred, gss_cred_usage_t usage)
3165 {
3166 	OM_uint32 ret, tmpmin;
3167 	gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
3168 	gss_OID_set cred_mechs = GSS_C_NULL_OID_SET, mechs;
3169 	unsigned int i;
3170 	int present, added_negoex = 0;
3171 	auth_scheme scheme;
3172 
3173 	if (spcred != NULL) {
3174 		/* Get the list of mechs in the mechglue cred. */
3175 		ret = gss_inquire_cred(minor_status, spcred->mcred, NULL,
3176 				       NULL, NULL, &cred_mechs);
3177 		if (ret != GSS_S_COMPLETE)
3178 			return (ret);
3179 	} else {
3180 		/* Start with the list of available mechs. */
3181 		ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
3182 					  GSS_C_NO_CRED_STORE, &creds,
3183 					  &cred_mechs, NULL);
3184 		if (ret != GSS_S_COMPLETE)
3185 			return (ret);
3186 		gss_release_cred(&tmpmin, &creds);
3187 	}
3188 
3189 	/* If gss_set_neg_mechs() was called, use that to determine the
3190 	 * iteration order.  Otherwise iterate over the credential mechs. */
3191 	mechs = (spcred != NULL && spcred->neg_mechs != GSS_C_NULL_OID_SET) ?
3192 	    spcred->neg_mechs : cred_mechs;
3193 
3194 	ret = gss_create_empty_oid_set(minor_status, &sc->mech_set);
3195 	if (ret != GSS_S_COMPLETE)
3196 		goto cleanup;
3197 
3198 	for (i = 0; i < mechs->count; i++) {
3199 		if (mechs != cred_mechs) {
3200 			/* Intersect neg_mechs with cred_mechs. */
3201 			gss_test_oid_set_member(&tmpmin, &mechs->elements[i],
3202 						cred_mechs, &present);
3203 			if (!present)
3204 				continue;
3205 		}
3206 
3207 		/* Query the auth scheme to see if this is a NegoEx mech. */
3208 		ret = gssspi_query_mechanism_info(&tmpmin, &mechs->elements[i],
3209 						  scheme);
3210 		if (ret == GSS_S_COMPLETE) {
3211 			/* Add an entry for this mech to the NegoEx list. */
3212 			ret = negoex_add_auth_mech(minor_status, sc,
3213 						   &mechs->elements[i],
3214 						   scheme);
3215 			if (ret != GSS_S_COMPLETE)
3216 				goto cleanup;
3217 
3218 			/* Add the NegoEx OID to the SPNEGO list at the
3219 			 * position of the first NegoEx mechanism. */
3220 			if (!added_negoex) {
3221 				ret = gss_add_oid_set_member(minor_status,
3222 							     &negoex_mech,
3223 							     &sc->mech_set);
3224 				if (ret != GSS_S_COMPLETE)
3225 					goto cleanup;
3226 				added_negoex = 1;
3227 			}
3228 
3229 			/* Skip this mech in the SPNEGO list unless it asks for
3230 			 * direct SPNEGO negotiation. */
3231 			if (!negoex_and_spnego(&mechs->elements[i]))
3232 				continue;
3233 		}
3234 
3235 		/* Add this mech to the SPNEGO list. */
3236 		ret = gss_add_oid_set_member(minor_status, &mechs->elements[i],
3237 					     &sc->mech_set);
3238 		if (ret != GSS_S_COMPLETE)
3239 			goto cleanup;
3240 	}
3241 
3242 	*minor_status = 0;
3243 
3244 cleanup:
3245 	if (ret != GSS_S_COMPLETE || sc->mech_set->count == 0) {
3246 		*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
3247 		map_errcode(minor_status);
3248 		ret = GSS_S_FAILURE;
3249 	}
3250 
3251 	gss_release_oid_set(&tmpmin, &cred_mechs);
3252 	return (ret);
3253 }
3254 
3255 /* following are token creation and reading routines */
3256 
3257 /*
3258  * If in contains a tagged OID encoding, return a copy of the contents as a
3259  * gss_OID and advance in past the encoding.  Otherwise return NULL and do not
3260  * advance in.
3261  */
3262 static gss_OID
get_mech_oid(OM_uint32 * minor_status,struct k5input * in)3263 get_mech_oid(OM_uint32 *minor_status, struct k5input *in)
3264 {
3265 	struct k5input oidrep;
3266 	OM_uint32 status;
3267 	gss_OID_desc oid;
3268 	gss_OID mech_out = NULL;
3269 
3270 	if (!k5_der_get_value(in, MECH_OID, &oidrep))
3271 		return (NULL);
3272 
3273 	oid.length = oidrep.len;
3274 	oid.elements = (uint8_t *)oidrep.ptr;
3275 	status = generic_gss_copy_oid(minor_status, &oid, &mech_out);
3276 	if (status != GSS_S_COMPLETE) {
3277 		map_errcode(minor_status);
3278 		mech_out = NULL;
3279 	}
3280 
3281 	return (mech_out);
3282 }
3283 
3284 /*
3285  * If in contains a tagged octet string encoding, return a copy of the contents
3286  * as a gss_buffer_t and advance in past the encoding.  Otherwise return NULL
3287  * and do not advance in.
3288  */
3289 static gss_buffer_t
get_octet_string(struct k5input * in)3290 get_octet_string(struct k5input *in)
3291 {
3292 	gss_buffer_t input_token;
3293 	struct k5input ostr;
3294 
3295 	if (!k5_der_get_value(in, OCTET_STRING, &ostr))
3296 		return (NULL);
3297 
3298 	input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
3299 	if (input_token == NULL)
3300 		return (NULL);
3301 
3302 	input_token->length = ostr.len;
3303 	if (input_token->length > 0) {
3304 		input_token->value = gssalloc_malloc(input_token->length);
3305 		if (input_token->value == NULL) {
3306 			free(input_token);
3307 			return (NULL);
3308 		}
3309 
3310 		memcpy(input_token->value, ostr.ptr, input_token->length);
3311 	} else {
3312 		input_token->value = NULL;
3313 	}
3314 	return (input_token);
3315 }
3316 
3317 /*
3318  * verify that buff_in points to a sequence of der encoding. The mech
3319  * set is the only sequence of encoded object in the token, so if it is
3320  * a sequence of encoding, decode the mechset into a gss_OID_set and
3321  * return it, advancing the buffer pointer.
3322  */
3323 static gss_OID_set
get_mech_set(OM_uint32 * minor_status,struct k5input * in)3324 get_mech_set(OM_uint32 *minor_status, struct k5input *in)
3325 {
3326 	gss_OID_set returned_mechSet;
3327 	OM_uint32 major_status, tmpmin;
3328 	struct k5input seq;
3329 
3330 	if (!k5_der_get_value(in, SEQUENCE_OF, &seq))
3331 		return (NULL);
3332 
3333 	major_status = gss_create_empty_oid_set(minor_status,
3334 						&returned_mechSet);
3335 	if (major_status != GSS_S_COMPLETE)
3336 		return (NULL);
3337 
3338 	while (!seq.status && seq.len > 0) {
3339 		gss_OID_desc *oid = get_mech_oid(minor_status, &seq);
3340 
3341 		if (oid == NULL) {
3342 			gss_release_oid_set(&tmpmin, &returned_mechSet);
3343 			return (NULL);
3344 		}
3345 
3346 		major_status = gss_add_oid_set_member(minor_status,
3347 						      oid, &returned_mechSet);
3348 		generic_gss_release_oid(minor_status, &oid);
3349 		if (major_status != GSS_S_COMPLETE) {
3350 			gss_release_oid_set(&tmpmin, &returned_mechSet);
3351 			return (NULL);
3352 		}
3353 	}
3354 
3355 	return (returned_mechSet);
3356 }
3357 
3358 /*
3359  * Encode mechSet into buf.
3360  */
3361 static int
put_mech_set(gss_OID_set mechSet,gss_buffer_t buffer_out)3362 put_mech_set(gss_OID_set mechSet, gss_buffer_t buffer_out)
3363 {
3364 	uint8_t *ptr;
3365 	size_t ilen, tlen, i;
3366 	struct k5buf buf;
3367 
3368 	ilen = 0;
3369 	for (i = 0; i < mechSet->count; i++)
3370 	    ilen += k5_der_value_len(mechSet->elements[i].length);
3371 	tlen = k5_der_value_len(ilen);
3372 
3373 	ptr = gssalloc_malloc(tlen);
3374 	if (ptr == NULL)
3375 		return -1;
3376 	k5_buf_init_fixed(&buf, ptr, tlen);
3377 
3378 	k5_der_add_taglen(&buf, SEQUENCE_OF, ilen);
3379 	for (i = 0; i < mechSet->count; i++) {
3380 		k5_der_add_value(&buf, MECH_OID,
3381 				 mechSet->elements[i].elements,
3382 				 mechSet->elements[i].length);
3383 	}
3384 	assert(buf.len == tlen);
3385 
3386 	buffer_out->value = ptr;
3387 	buffer_out->length = tlen;
3388 	return 0;
3389 }
3390 
3391 /* Decode SPNEGO request flags from the DER encoding of a bit string and set
3392  * them in *ret_flags. */
3393 static OM_uint32
get_req_flags(struct k5input * in,OM_uint32 * req_flags)3394 get_req_flags(struct k5input *in, OM_uint32 *req_flags)
3395 {
3396 	if (in->status || in->len != 4 ||
3397 	    k5_input_get_byte(in) != BIT_STRING ||
3398 	    k5_input_get_byte(in) != BIT_STRING_LENGTH ||
3399 	    k5_input_get_byte(in) != BIT_STRING_PADDING)
3400 		return GSS_S_DEFECTIVE_TOKEN;
3401 
3402 	*req_flags = k5_input_get_byte(in) >> 1;
3403 	return GSS_S_COMPLETE;
3404 }
3405 
3406 static OM_uint32
get_negTokenInit(OM_uint32 * minor_status,gss_buffer_t buf,gss_buffer_t der_mechSet,gss_OID_set * mechSet,OM_uint32 * req_flags,gss_buffer_t * mechtok,gss_buffer_t * mechListMIC)3407 get_negTokenInit(OM_uint32 *minor_status,
3408 		 gss_buffer_t buf,
3409 		 gss_buffer_t der_mechSet,
3410 		 gss_OID_set *mechSet,
3411 		 OM_uint32 *req_flags,
3412 		 gss_buffer_t *mechtok,
3413 		 gss_buffer_t *mechListMIC)
3414 {
3415 	OM_uint32 err;
3416 	struct k5input in, seq, field;
3417 
3418 	*minor_status = 0;
3419 	der_mechSet->length = 0;
3420 	der_mechSet->value = NULL;
3421 	*mechSet = GSS_C_NO_OID_SET;
3422 	*req_flags = 0;
3423 	*mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3424 
3425 	k5_input_init(&in, buf->value, buf->length);
3426 
3427 	/* Advance past the framing header. */
3428 	err = verify_token_header(&in, gss_mech_spnego);
3429 	if (err)
3430 		return GSS_S_DEFECTIVE_TOKEN;
3431 
3432 	/* Advance past the [0] tag for the NegotiationToken choice. */
3433 	if (!k5_der_get_value(&in, CONTEXT, &seq))
3434 		return GSS_S_DEFECTIVE_TOKEN;
3435 
3436 	/* Advance past the SEQUENCE tag. */
3437 	if (!k5_der_get_value(&seq, SEQUENCE, &seq))
3438 		return GSS_S_DEFECTIVE_TOKEN;
3439 
3440 	/* Get the contents of the mechTypes field.  Reject an empty field here
3441 	 * since we musn't allocate a zero-length buffer in the next step. */
3442 	if (!k5_der_get_value(&seq, CONTEXT, &field) || field.len == 0)
3443 		return GSS_S_DEFECTIVE_TOKEN;
3444 
3445 	/* Store a copy of the contents for MIC computation. */
3446 	der_mechSet->value = gssalloc_malloc(field.len);
3447 	if (der_mechSet->value == NULL)
3448 		return GSS_S_FAILURE;
3449 	memcpy(der_mechSet->value, field.ptr, field.len);
3450 	der_mechSet->length = field.len;
3451 
3452 	/* Decode the contents into an OID set. */
3453 	*mechSet = get_mech_set(minor_status, &field);
3454 	if (*mechSet == NULL)
3455 		return GSS_S_FAILURE;
3456 
3457 	if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {
3458 		err = get_req_flags(&field, req_flags);
3459 		if (err != GSS_S_COMPLETE)
3460 			return err;
3461 	}
3462 
3463 	if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {
3464 		*mechtok = get_octet_string(&field);
3465 		if (*mechtok == GSS_C_NO_BUFFER)
3466 			return GSS_S_FAILURE;
3467 	}
3468 
3469 	if (k5_der_get_value(&seq, CONTEXT | 0x03, &field)) {
3470 		*mechListMIC = get_octet_string(&field);
3471 		if (*mechListMIC == GSS_C_NO_BUFFER)
3472 			return GSS_S_FAILURE;
3473 	}
3474 
3475 	return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;
3476 }
3477 
3478 /* Decode a NegotiationToken of type negTokenResp. */
3479 static OM_uint32
get_negTokenResp(OM_uint32 * minor_status,struct k5input * in,OM_uint32 * negState,gss_OID * supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC)3480 get_negTokenResp(OM_uint32 *minor_status, struct k5input *in,
3481 		 OM_uint32 *negState, gss_OID *supportedMech,
3482 		 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC)
3483 {
3484 	struct k5input seq, field, en;
3485 
3486 	*negState = UNSPECIFIED;
3487 	*supportedMech = GSS_C_NO_OID;
3488 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3489 
3490 	/* Advance past the [1] tag for the NegotiationToken choice. */
3491 	if (!k5_der_get_value(in, CONTEXT | 0x01, &seq))
3492 		return GSS_S_DEFECTIVE_TOKEN;
3493 
3494 	/* Advance seq past the SEQUENCE tag (historically this code allows the
3495 	 * tag to be missing). */
3496 	(void)k5_der_get_value(&seq, SEQUENCE, &seq);
3497 
3498 	if (k5_der_get_value(&seq, CONTEXT, &field)) {
3499 		if (!k5_der_get_value(&field, ENUMERATED, &en))
3500 			return GSS_S_DEFECTIVE_TOKEN;
3501 		if (en.len != ENUMERATION_LENGTH)
3502 			return GSS_S_DEFECTIVE_TOKEN;
3503 		*negState = *en.ptr;
3504 	}
3505 
3506 	if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {
3507 		*supportedMech = get_mech_oid(minor_status, &field);
3508 		if (*supportedMech == GSS_C_NO_OID)
3509 			return GSS_S_DEFECTIVE_TOKEN;
3510 	}
3511 
3512 	if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {
3513 		*responseToken = get_octet_string(&field);
3514 		if (*responseToken == GSS_C_NO_BUFFER)
3515 			return GSS_S_DEFECTIVE_TOKEN;
3516 	}
3517 
3518 	if (k5_der_get_value(&seq, CONTEXT | 0x04, &field)) {
3519 		*mechListMIC = get_octet_string(&field);
3520 
3521                 /* Handle Windows 2000 duplicate response token */
3522                 if (*responseToken &&
3523                     ((*responseToken)->length == (*mechListMIC)->length) &&
3524                     !memcmp((*responseToken)->value, (*mechListMIC)->value,
3525                             (*responseToken)->length)) {
3526 			OM_uint32 tmpmin;
3527 
3528 			gss_release_buffer(&tmpmin, *mechListMIC);
3529 			free(*mechListMIC);
3530 			*mechListMIC = NULL;
3531 		}
3532 	}
3533 
3534 	return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;
3535 }
3536 
3537 /*
3538  * This routine compares the received mechset to the mechset that
3539  * this server can support. It looks sequentially through the mechset
3540  * and the first one that matches what the server can support is
3541  * chosen as the negotiated mechanism. If one is found, negResult
3542  * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3543  * it's not the first mech, otherwise we return NULL and negResult
3544  * is set to REJECT. The returned pointer is an alias into
3545  * received->elements and should not be freed.
3546  *
3547  * NOTE: There is currently no way to specify a preference order of
3548  * mechanisms supported by the acceptor.
3549  */
3550 static gss_OID
negotiate_mech(spnego_gss_ctx_id_t ctx,gss_OID_set received,OM_uint32 * negResult)3551 negotiate_mech(spnego_gss_ctx_id_t ctx, gss_OID_set received,
3552 	       OM_uint32 *negResult)
3553 {
3554 	size_t i, j;
3555 	int wrong_krb5_oid;
3556 
3557 	for (i = 0; i < received->count; i++) {
3558 		gss_OID mech_oid = &received->elements[i];
3559 
3560 		/* Accept wrong mechanism OID from MS clients */
3561 		wrong_krb5_oid = 0;
3562 		if (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid)) {
3563 			mech_oid = (gss_OID)&gss_mech_krb5_oid;
3564 			wrong_krb5_oid = 1;
3565 		}
3566 
3567 		for (j = 0; j < ctx->mech_set->count; j++) {
3568 			if (g_OID_equal(mech_oid,
3569 					&ctx->mech_set->elements[j])) {
3570 				*negResult = (i == 0) ? ACCEPT_INCOMPLETE :
3571 					REQUEST_MIC;
3572 				return wrong_krb5_oid ?
3573 					(gss_OID)&gss_mech_krb5_wrong_oid :
3574 					&ctx->mech_set->elements[j];
3575 			}
3576 		}
3577 	}
3578 	*negResult = REJECT;
3579 	return (NULL);
3580 }
3581 
3582 /*
3583  * the next two routines make a token buffer suitable for
3584  * spnego_gss_display_status. These currently take the string
3585  * in name and place it in the token. Eventually, if
3586  * spnego_gss_display_status returns valid error messages,
3587  * these routines will be changes to return the error string.
3588  */
3589 static spnego_token_t
make_spnego_token(const char * name)3590 make_spnego_token(const char *name)
3591 {
3592 	return (spnego_token_t)gssalloc_strdup(name);
3593 }
3594 
3595 static gss_buffer_desc
make_err_msg(const char * name)3596 make_err_msg(const char *name)
3597 {
3598 	gss_buffer_desc buffer;
3599 
3600 	if (name == NULL) {
3601 		buffer.length = 0;
3602 		buffer.value = NULL;
3603 	} else {
3604 		buffer.length = strlen(name)+1;
3605 		buffer.value = make_spnego_token(name);
3606 	}
3607 
3608 	return (buffer);
3609 }
3610 
3611 /*
3612  * Create the client side spnego token passed back to gss_init_sec_context
3613  * and eventually up to the application program and over to the server.
3614  *
3615  * Use DER rules, definite length method per RFC 2478
3616  */
3617 static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,int negHintsCompat,gss_buffer_t mic,OM_uint32 req_flags,gss_buffer_t token,send_token_flag sendtoken,gss_buffer_t outbuf)3618 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, int negHintsCompat,
3619 			  gss_buffer_t mic, OM_uint32 req_flags,
3620 			  gss_buffer_t token, send_token_flag sendtoken,
3621 			  gss_buffer_t outbuf)
3622 {
3623 	size_t f0len, f2len, f3len, fields_len, seq_len, choice_len;
3624 	size_t mech_len, framed_len;
3625 	uint8_t *t;
3626 	struct k5buf buf;
3627 
3628 	if (outbuf == GSS_C_NO_BUFFER)
3629 		return (-1);
3630 
3631 	outbuf->length = 0;
3632 	outbuf->value = NULL;
3633 
3634 	/* Calculate the length of each field and the total fields length. */
3635 	fields_len = 0;
3636 	/* mechTypes [0] MechTypeList, previously assembled in spnego_ctx */
3637 	f0len = spnego_ctx->DER_mechTypes.length;
3638 	fields_len += k5_der_value_len(f0len);
3639 	if (token != NULL) {
3640 		/* mechToken [2] OCTET STRING OPTIONAL */
3641 		f2len = k5_der_value_len(token->length);
3642 		fields_len += k5_der_value_len(f2len);
3643 	}
3644 	if (mic != GSS_C_NO_BUFFER) {
3645 		/* mechListMIC [3] OCTET STRING OPTIONAL */
3646 		f3len = k5_der_value_len(mic->length);
3647 		fields_len += k5_der_value_len(f3len);
3648 	}
3649 
3650 	/* Calculate the length of the sequence and choice. */
3651 	seq_len = k5_der_value_len(fields_len);
3652 	choice_len = k5_der_value_len(seq_len);
3653 
3654 	/* Calculate the framed token length. */
3655 	mech_len = k5_der_value_len(gss_mech_spnego->length);
3656 	framed_len = k5_der_value_len(mech_len + choice_len);
3657 
3658 	/* Allocate space and prepare a buffer. */
3659 	t = gssalloc_malloc(framed_len);
3660 	if (t == NULL)
3661 		return (-1);
3662 	k5_buf_init_fixed(&buf, t, framed_len);
3663 
3664 	/* Add generic token framing. */
3665 	k5_der_add_taglen(&buf, HEADER_ID, mech_len + choice_len);
3666 	k5_der_add_value(&buf, MECH_OID, gss_mech_spnego->elements,
3667 			 gss_mech_spnego->length);
3668 
3669 	/* Add NegotiationToken choice tag and NegTokenInit sequence tag. */
3670 	k5_der_add_taglen(&buf, CONTEXT | 0x00, seq_len);
3671 	k5_der_add_taglen(&buf, SEQUENCE, fields_len);
3672 
3673 	/* Add the already-encoded mechanism list as mechTypes. */
3674 	k5_der_add_value(&buf, CONTEXT | 0x00, spnego_ctx->DER_mechTypes.value,
3675 			 spnego_ctx->DER_mechTypes.length);
3676 
3677 	if (token != NULL) {
3678 		k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);
3679 		k5_der_add_value(&buf, OCTET_STRING, token->value,
3680 				 token->length);
3681 	}
3682 
3683 	if (mic != GSS_C_NO_BUFFER) {
3684 		uint8_t id = negHintsCompat ? SEQUENCE : OCTET_STRING;
3685 		k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);
3686 		k5_der_add_value(&buf, id, mic->value, mic->length);
3687 	}
3688 
3689 	assert(buf.len == framed_len);
3690 	outbuf->length = framed_len;
3691 	outbuf->value = t;
3692 
3693 	return (0);
3694 }
3695 
3696 /*
3697  * create the server side spnego token passed back to
3698  * gss_accept_sec_context and eventually up to the application program
3699  * and over to the client.
3700  */
3701 static OM_uint32
make_spnego_tokenTarg_msg(uint8_t status,gss_OID mech_wanted,gss_buffer_t token,gss_buffer_t mic,send_token_flag sendtoken,gss_buffer_t outbuf)3702 make_spnego_tokenTarg_msg(uint8_t status, gss_OID mech_wanted,
3703 			  gss_buffer_t token, gss_buffer_t mic,
3704 			  send_token_flag sendtoken,
3705 			  gss_buffer_t outbuf)
3706 {
3707 	size_t f0len, f1len, f2len, f3len, fields_len, seq_len, choice_len;
3708 	uint8_t *t;
3709 	struct k5buf buf;
3710 
3711 	if (outbuf == GSS_C_NO_BUFFER)
3712 		return (GSS_S_DEFECTIVE_TOKEN);
3713 	if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
3714 		return (GSS_S_DEFECTIVE_TOKEN);
3715 
3716 	outbuf->length = 0;
3717 	outbuf->value = NULL;
3718 
3719 	/* Calculate the length of each field and the total fields length. */
3720 	fields_len = 0;
3721 	/* negState [0] ENUMERATED { ... } OPTIONAL */
3722 	f0len = k5_der_value_len(1);
3723 	fields_len += k5_der_value_len(f0len);
3724 	if (sendtoken == INIT_TOKEN_SEND) {
3725 		/* supportedMech [1] MechType OPTIONAL */
3726 		f1len = k5_der_value_len(mech_wanted->length);
3727 		fields_len += k5_der_value_len(f1len);
3728 	}
3729 	if (token != NULL && token->length > 0) {
3730 		/* mechToken [2] OCTET STRING OPTIONAL */
3731 		f2len = k5_der_value_len(token->length);
3732 		fields_len += k5_der_value_len(f2len);
3733 	}
3734 	if (mic != NULL) {
3735 		/* mechListMIC [3] OCTET STRING OPTIONAL */
3736 		f3len = k5_der_value_len(mic->length);
3737 		fields_len += k5_der_value_len(f3len);
3738 	}
3739 
3740 	/* Calculate the length of the sequence and choice. */
3741 	seq_len = k5_der_value_len(fields_len);
3742 	choice_len = k5_der_value_len(seq_len);
3743 
3744 	/* Allocate space and prepare a buffer. */
3745 	t = gssalloc_malloc(choice_len);
3746 	if (t == NULL)
3747 		return (GSS_S_DEFECTIVE_TOKEN);
3748 	k5_buf_init_fixed(&buf, t, choice_len);
3749 
3750 	/* Add the choice tag and begin the sequence. */
3751 	k5_der_add_taglen(&buf, CONTEXT | 0x01, seq_len);
3752 	k5_der_add_taglen(&buf, SEQUENCE, fields_len);
3753 
3754 	/* Add the negState field. */
3755 	k5_der_add_taglen(&buf, CONTEXT | 0x00, f0len);
3756 	k5_der_add_value(&buf, ENUMERATED, &status, 1);
3757 
3758 	if (sendtoken == INIT_TOKEN_SEND) {
3759 		/* Add the supportedMech field. */
3760 		k5_der_add_taglen(&buf, CONTEXT | 0x01, f1len);
3761 		k5_der_add_value(&buf, MECH_OID, mech_wanted->elements,
3762 				 mech_wanted->length);
3763 	}
3764 
3765 	if (token != NULL && token->length > 0) {
3766 		/* Add the mechToken field. */
3767 		k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);
3768 		k5_der_add_value(&buf, OCTET_STRING, token->value,
3769 				 token->length);
3770 	}
3771 
3772 	if (mic != NULL) {
3773 		/* Add the mechListMIC field. */
3774 		k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);
3775 		k5_der_add_value(&buf, OCTET_STRING, mic->value, mic->length);
3776 	}
3777 
3778 	assert(buf.len == choice_len);
3779 	outbuf->length = choice_len;
3780 	outbuf->value = t;
3781 
3782 	return (0);
3783 }
3784 
3785 /* Advance in past the [APPLICATION 0] tag and thisMech field of an
3786  * InitialContextToken encoding, checking that thisMech matches mech. */
3787 static int
verify_token_header(struct k5input * in,gss_OID_const mech)3788 verify_token_header(struct k5input *in, gss_OID_const mech)
3789 {
3790 	gss_OID_desc oid;
3791 	struct k5input field;
3792 
3793 	if (!k5_der_get_value(in, HEADER_ID, in))
3794 		return (G_BAD_TOK_HEADER);
3795 	if (!k5_der_get_value(in, MECH_OID, &field))
3796 		return (G_BAD_TOK_HEADER);
3797 
3798 	oid.length = field.len;
3799 	oid.elements = (uint8_t *)field.ptr;
3800 	return g_OID_equal(&oid, mech) ? 0 : G_WRONG_MECH;
3801 }
3802 
3803 /*
3804  * Return non-zero if the oid is one of the kerberos mech oids,
3805  * otherwise return zero.
3806  *
3807  * N.B. There are 3 oids that represent the kerberos mech:
3808  * RFC-specified GSS_MECH_KRB5_OID,
3809  * Old pre-RFC   GSS_MECH_KRB5_OLD_OID,
3810  * Incorrect MS  GSS_MECH_KRB5_WRONG_OID
3811  */
3812 
3813 static int
is_kerb_mech(gss_OID oid)3814 is_kerb_mech(gss_OID oid)
3815 {
3816 	int answer = 0;
3817 	OM_uint32 minor;
3818 	extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3819 
3820 	(void) gss_test_oid_set_member(&minor,
3821 		oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
3822 
3823 	return (answer);
3824 }
3825