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