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