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