1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * A module that implements the spnego security mechanism.
26 * It is used to negotiate the security mechanism between
27 * peers using the GSS-API.
28 *
29 */
30
31 #pragma ident "%Z%%M% %I% %E% SMI"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include "gssapiP_spnego.h"
37 #include <mechglueP.h>
38 #include <gssapi_err_generic.h>
39 #include <rpc/types.h>
40 #include <libintl.h>
41
42 /* der routines defined in libgss */
43 extern unsigned int der_length_size(OM_uint32);
44 extern int get_der_length(uchar_t **, OM_uint32, OM_uint32*);
45 extern int put_der_length(OM_uint32, uchar_t **, OM_uint32);
46
47
48 /* private routines for spnego_mechanism */
49 static spnego_token_t make_spnego_token(char *);
50 static gss_buffer_desc make_err_msg(char *);
51 static int g_token_size(gss_OID, OM_uint32);
52 static int g_make_token_header(gss_OID, int, uchar_t **, int);
53 static int g_verify_token_header(gss_OID, int *, uchar_t **, int, int);
54 static int g_verify_neg_token_init(uchar_t **, int);
55 static OM_uint32 get_negResult(unsigned char **, int);
56 static gss_OID get_mech_oid(OM_uint32 *, uchar_t **, size_t);
57 static gss_buffer_t get_input_token(unsigned char **, int);
58 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, int);
59 static OM_uint32 get_req_flags(uchar_t **, int *, OM_uint32 *);
60 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
61 gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
62 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
63 static void check_spnego_options(spnego_gss_ctx_id_t);
64 static spnego_gss_ctx_id_t create_spnego_ctx(void);
65 static int put_mech_set(uchar_t **, gss_OID_set, int);
66 static int put_input_token(uchar_t **, gss_buffer_t, int);
67 static int put_mech_oid(uchar_t **, gss_OID_desc *, int);
68 static int put_negResult(uchar_t **, OM_uint32, int);
69
70 static gss_OID
71 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
72 OM_uint32 *, bool_t *);
73 static int
74 g_get_tag_and_length(unsigned char **, uchar_t, int, int *);
75
76 static int
77 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, gss_OID_set,
78 gss_buffer_t, send_token_flag,
79 gss_buffer_t);
80 static int
81 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
82 gss_buffer_t, send_token_flag, int,
83 gss_buffer_t);
84
85 /*
86 * The Mech OID for SPNEGO:
87 * { iso(1) org(3) dod(6) internet(1) security(5)
88 * mechanism(5) spnego(2) }
89 */
90 static struct gss_config spnego_mechanism =
91 {{SPNEGO_OID_LENGTH, SPNEGO_OID},
92 NULL,
93 spnego_gss_acquire_cred,
94 spnego_gss_release_cred,
95 spnego_gss_init_sec_context,
96 spnego_gss_accept_sec_context,
97 /* EXPORT DELETE START */ /* CRYPT DELETE START */
98 spnego_gss_unseal, /* gss_unseal */
99 /* EXPORT DELETE END */ /* CRYPT DELETE END */
100 NULL, /* gss_process_context_token */
101 spnego_gss_delete_sec_context, /* gss_delete_sec_context */
102 spnego_gss_context_time, /* gss_context_time */
103 spnego_gss_display_status,
104 NULL, /* gss_indicate_mechs */
105 NULL, /* gss_compare_name */
106 spnego_gss_display_name,
107 spnego_gss_import_name,
108 spnego_gss_release_name,
109 spnego_gss_inquire_cred, /* gss_inquire_cred */
110 NULL, /* gss_add_cred */
111 /* EXPORT DELETE START */ /* CRYPT DELETE START */
112 spnego_gss_seal, /* gss_seal */
113 /* EXPORT DELETE END */ /* CRYPT DELETE END */
114 spnego_gss_export_sec_context, /* gss_export_sec_context */
115 spnego_gss_import_sec_context, /* gss_import_sec_context */
116 NULL, /* gss_inquire_cred_by_mech */
117 spnego_gss_inquire_names_for_mech,
118 spnego_gss_inquire_context, /* gss_inquire_context */
119 NULL, /* gss_internal_release_oid */
120 spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */
121 NULL, /* gss_pname_to_uid */
122 NULL, /* __gss_userok */
123 NULL, /* gss_export_name */
124 /* EXPORT DELETE START */
125 /* CRYPT DELETE START */
126 #if 0
127 /* CRYPT DELETE END */
128 spnego_gss_seal,
129 spnego_gss_unseal,
130 /* CRYPT DELETE START */
131 #endif
132 /* CRYPT DELETE END */
133 /* EXPORT DELETE END */
134 spnego_gss_sign, /* gss_sign */
135 spnego_gss_verify, /* gss_verify */
136 NULL, /* gss_store_cred */
137 };
138
139 gss_mechanism
140 gss_mech_initialize(const gss_OID oid)
141 {
142 dsyslog("Entering gss_mech_initialize\n");
143
144 if (oid == NULL ||
145 !g_OID_equal(oid, &spnego_mechanism.mech_type)) {
146 dsyslog("invalid spnego mechanism oid.\n");
147 return (NULL);
148 }
149
150 dsyslog("Leaving gss_mech_initialize\n");
151 return (&spnego_mechanism);
152 }
153
154 /*ARGSUSED*/
155 OM_uint32
156 spnego_gss_acquire_cred(void *ctx,
157 OM_uint32 *minor_status,
158 gss_name_t desired_name,
159 OM_uint32 time_req,
160 gss_OID_set desired_mechs,
161 gss_cred_usage_t cred_usage,
162 gss_cred_id_t *output_cred_handle,
163 gss_OID_set *actual_mechs,
164 OM_uint32 *time_rec)
165 {
166 OM_uint32 status;
167 gss_OID_set amechs;
168 dsyslog("Entering spnego_gss_acquire_cred\n");
169
170 if (actual_mechs)
171 *actual_mechs = NULL;
172
173 if (time_rec)
174 *time_rec = 0;
175
176 /*
177 * If the user did not specify a list of mechs,
178 * use get_available_mechs to collect a list of
179 * mechs for which creds are available.
180 */
181 if (desired_mechs == GSS_C_NULL_OID_SET) {
182 status = get_available_mechs(minor_status,
183 desired_name, cred_usage,
184 output_cred_handle, &amechs);
185 } else {
186 /*
187 * The caller gave a specific list of mechanisms,
188 * so just get whatever creds are available.
189 * gss_acquire_creds will return the subset of mechs for
190 * which the given 'output_cred_handle' is valid.
191 */
192 status = gss_acquire_cred(minor_status,
193 desired_name, time_req, desired_mechs, cred_usage,
194 output_cred_handle, &amechs, time_rec);
195 }
196
197 if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
198 (void) gss_copy_oid_set(minor_status, amechs, actual_mechs);
199 }
200 (void) gss_release_oid_set(minor_status, &amechs);
201
202 dsyslog("Leaving spnego_gss_acquire_cred\n");
203 return (status);
204 }
205
206 /*ARGSUSED*/
207 OM_uint32
208 spnego_gss_release_cred(void *ctx,
209 OM_uint32 *minor_status,
210 gss_cred_id_t *cred_handle)
211 {
212 OM_uint32 status;
213
214 dsyslog("Entering spnego_gss_release_cred\n");
215
216 if (minor_status == NULL || cred_handle == NULL)
217 return (GSS_S_CALL_INACCESSIBLE_WRITE);
218
219 *minor_status = 0;
220
221 if (*cred_handle == GSS_C_NO_CREDENTIAL)
222 return (GSS_S_COMPLETE);
223
224 status = gss_release_cred(minor_status, cred_handle);
225
226 dsyslog("Leaving spnego_gss_release_cred\n");
227 return (status);
228 }
229
230 static void
231 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
232 {
233 spnego_ctx->optionStr = __gss_get_modOptions(
234 (const gss_OID)&spnego_oids[0]);
235 if (spnego_ctx->optionStr != NULL &&
236 strstr(spnego_ctx->optionStr, "msinterop")) {
237 spnego_ctx->MS_Interop = 1;
238 } else {
239 spnego_ctx->MS_Interop = 0;
240 }
241 }
242
243 static spnego_gss_ctx_id_t
244 create_spnego_ctx(void)
245 {
246 spnego_gss_ctx_id_t spnego_ctx = NULL;
247 spnego_ctx = (spnego_gss_ctx_id_t)
248 malloc(sizeof (spnego_gss_ctx_id_rec));
249
250 if (spnego_ctx == NULL) {
251 return (NULL);
252 }
253
254 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
255 spnego_ctx->internal_mech = NULL;
256 spnego_ctx->optionStr = NULL;
257 spnego_ctx->optimistic = 0;
258 spnego_ctx->MS_Interop = 0;
259 spnego_ctx->DER_mechTypes.length = NULL;
260 spnego_ctx->DER_mechTypes.value = GSS_C_NO_BUFFER;
261
262 check_spnego_options(spnego_ctx);
263
264 return (spnego_ctx);
265 }
266
267 /*ARGSUSED*/
268 OM_uint32
269 spnego_gss_init_sec_context(void *ct,
270 OM_uint32 *minor_status,
271 gss_cred_id_t claimant_cred_handle,
272 gss_ctx_id_t *context_handle,
273 gss_name_t target_name,
274 gss_OID mech_type,
275 OM_uint32 req_flags,
276 OM_uint32 time_req,
277 gss_channel_bindings_t input_chan_bindings,
278 gss_buffer_t input_token,
279 gss_OID *actual_mech,
280 gss_buffer_t output_token,
281 OM_uint32 *ret_flags,
282 OM_uint32 *time_rec)
283 {
284 OM_uint32 ret = 0;
285 OM_uint32 status = 0;
286 OM_uint32 mstat;
287 OM_uint32 local_ret_flags = 0;
288
289 /*
290 * send_token is used to indicate in later steps
291 * what type of token, if any should be sent or processed.
292 * NO_TOKEN_SEND = no token should be sent
293 * INIT_TOKEN_SEND = initial token will be sent
294 * CONT_TOKEN_SEND = continuing tokens to be sent
295 * CHECK_MIC = no token to be sent, but have a MIC to check.
296 */
297 send_token_flag send_token = NO_TOKEN_SEND;
298
299 gss_OID_set mechSet;
300 spnego_gss_ctx_id_t spnego_ctx = NULL;
301 gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
302 gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
303 gss_buffer_t mechListMIC = NULL;
304 gss_cred_id_t *credlistptr = NULL, credlist;
305 gss_qop_t *qop_state = NULL;
306 unsigned char *ptr;
307 int len;
308
309 dsyslog("Entering init_sec_context\n");
310
311 if (context_handle == NULL)
312 return (GSS_S_NO_CONTEXT);
313
314 *minor_status = 0;
315 output_token->length = 0;
316 output_token->value = NULL;
317
318 if (actual_mech)
319 *actual_mech = NULL;
320
321 if (*context_handle == GSS_C_NO_CONTEXT) {
322
323 /* determine negotiation mech set */
324 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
325 credlistptr = &credlist;
326
327 mstat = get_available_mechs(minor_status,
328 GSS_C_NO_NAME, GSS_C_INITIATE,
329 credlistptr, &mechSet);
330 } else {
331 /*
332 * Use the list of mechs included in the
333 * cred that we were given.
334 */
335 mstat = gss_inquire_cred(minor_status,
336 claimant_cred_handle, NULL, NULL,
337 NULL, &mechSet);
338 }
339 if (mstat != GSS_S_COMPLETE)
340 return (mstat);
341
342 if ((spnego_ctx = create_spnego_ctx()) == NULL) {
343 ret = GSS_S_FAILURE;
344 goto cleanup;
345 }
346
347 /*
348 * need to pull the first mech from mechSet to do first
349 * init ctx
350 */
351 status = generic_gss_copy_oid(minor_status,
352 mechSet->elements, &spnego_ctx->internal_mech);
353
354 if (status != GSS_S_COMPLETE) {
355 ret = GSS_S_FAILURE;
356 goto cleanup;
357 }
358
359 if (input_token != NULL && input_token->value != NULL) {
360 ret = GSS_S_DEFECTIVE_TOKEN;
361 goto cleanup;
362 }
363
364 /*
365 * The actual context is not yet determined,
366 * set the output context_handle to refer to
367 * the spnego context itself.
368 */
369 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
370 *context_handle = (gss_ctx_id_t)spnego_ctx;
371 send_token = INIT_TOKEN_SEND;
372 ret = GSS_S_CONTINUE_NEEDED;
373 } else {
374 mechSet = NULL;
375 spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
376
377 if (input_token == NULL || input_token->value == NULL) {
378 ret = GSS_S_DEFECTIVE_TOKEN;
379 goto cleanup;
380 }
381 ptr = (unsigned char *) input_token->value;
382
383 switch (get_negResult(&ptr, input_token->length)) {
384 case ACCEPT_DEFECTIVE_TOKEN:
385 *minor_status = 1;
386 ret = GSS_S_DEFECTIVE_TOKEN;
387 break;
388 case ACCEPT_INCOMPLETE: {
389 /* pull out mech from token */
390 gss_OID internal_mech =
391 get_mech_oid(minor_status, &ptr,
392 input_token->length -
393 (ptr - (uchar_t *)input_token->value));
394
395 /*
396 * check if first mech in neg set, if it isn't,
397 * release and copy chosen mech to context,
398 * delete internal context from prior mech
399 */
400 if (internal_mech != NULL &&
401 ((internal_mech->length !=
402 spnego_ctx->internal_mech->length) ||
403 /* CSTYLED */
404 memcmp(spnego_ctx->internal_mech->elements,
405 internal_mech->elements,
406 spnego_ctx->internal_mech->length))) {
407
408 (void) gss_delete_sec_context(&mstat,
409 &spnego_ctx->ctx_handle, NULL);
410
411 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
412 (void) generic_gss_release_oid(
413 &mstat, &spnego_ctx->internal_mech);
414
415 status = generic_gss_copy_oid(
416 minor_status, internal_mech,
417 &spnego_ctx->internal_mech);
418
419 if (status != GSS_S_COMPLETE)
420 ret = GSS_S_DEFECTIVE_TOKEN;
421 else
422 ret = GSS_S_COMPLETE;
423
424 (void) generic_gss_release_oid(&mstat,
425 &internal_mech);
426 } else if (internal_mech == NULL) {
427 ret = GSS_S_DEFECTIVE_TOKEN;
428 send_token = NO_TOKEN_SEND;
429 } else {
430 ret = GSS_S_COMPLETE;
431 }
432 if (ret == GSS_S_COMPLETE) {
433 /*
434 * Check for a token, it may contain
435 * an error message.
436 */
437 if (*ptr == (CONTEXT | 0x02)) {
438 if (g_get_tag_and_length(&ptr,
439 (CONTEXT | 0x02),
440 input_token->length - (ptr -
441 (uchar_t *)input_token->value),
442 &len) < 0) {
443 ret = GSS_S_DEFECTIVE_TOKEN;
444 } else {
445 i_input_token = get_input_token(
446 &ptr, len);
447 if (i_input_token != NULL) {
448 /*CSTYLED*/
449 ret = GSS_S_CONTINUE_NEEDED;
450 send_token =
451 CONT_TOKEN_SEND;
452 } else {
453 /*CSTYLED*/
454 ret = GSS_S_DEFECTIVE_TOKEN;
455 send_token =
456 NO_TOKEN_SEND;
457 }
458 }
459 }
460 }
461 break;
462 }
463 case ACCEPT_COMPLETE:
464 /* pull out mech from token */
465 if (spnego_ctx->internal_mech != NULL)
466 (void) generic_gss_release_oid(&mstat,
467 &spnego_ctx->internal_mech);
468
469 spnego_ctx->internal_mech =
470 get_mech_oid(minor_status, &ptr,
471 input_token->length -
472 (ptr - (uchar_t *)input_token->value));
473
474 if (spnego_ctx->internal_mech == NULL) {
475 /* CSTYLED */
476 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
477 ret = GSS_S_FAILURE;
478 }
479
480 if (ret != GSS_S_FAILURE && *ptr == (CONTEXT | 0x02)) {
481 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
482 input_token->length - (ptr -
483 (uchar_t *)input_token->value), &len) < 0) {
484 ret = GSS_S_DEFECTIVE_TOKEN;
485 } else {
486 i_input_token = get_input_token(&ptr,
487 len);
488 if (i_input_token != NULL) {
489 ret = GSS_S_COMPLETE;
490 send_token = CHECK_MIC;
491 } else {
492 ret = GSS_S_DEFECTIVE_TOKEN;
493 send_token = NO_TOKEN_SEND;
494 }
495 }
496 } else if (ret == GSS_S_CONTINUE_NEEDED ||
497 ret == GSS_S_COMPLETE) {
498 send_token = CHECK_MIC;
499 }
500
501 /*
502 * If we sent "optimistic" initial token,
503 * but the acceptor did not send a response token,
504 * this is an error.
505 */
506 if (ret == GSS_S_COMPLETE &&
507 i_input_token == GSS_C_NO_BUFFER &&
508 spnego_ctx->last_status == GSS_S_CONTINUE_NEEDED &&
509 spnego_ctx->optimistic) {
510 /* CSTYLED */
511 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
512 ret = GSS_S_DEFECTIVE_TOKEN;
513 send_token = NO_TOKEN_SEND;
514 }
515
516 if (send_token != NO_TOKEN_SEND) {
517 if (i_input_token == NULL)
518 ret = GSS_S_COMPLETE;
519 else
520 ret = GSS_S_CONTINUE_NEEDED;
521 send_token = CHECK_MIC;
522 }
523 break;
524
525 case REJECT:
526 ret = GSS_S_BAD_MECH;
527 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
528 send_token = NO_TOKEN_SEND;
529 break;
530
531 default:
532 ret = GSS_S_FAILURE;
533 send_token = NO_TOKEN_SEND;
534 break;
535 }
536 }
537
538
539 if (send_token == NO_TOKEN_SEND) {
540 output_token->length = 0;
541 output_token->value = NULL;
542 goto cleanup;
543 }
544
545 i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
546
547 if (i_output_token == NULL) {
548 ret = status = GSS_S_FAILURE;
549 goto cleanup;
550 }
551
552 i_output_token->length = 0;
553 i_output_token->value = NULL;
554
555 if (ret == GSS_S_CONTINUE_NEEDED) {
556 gss_OID inner_mech_type = GSS_C_NO_OID;
557
558 status = gss_init_sec_context(minor_status,
559 claimant_cred_handle,
560 &spnego_ctx->ctx_handle,
561 target_name,
562 spnego_ctx->internal_mech,
563 req_flags,
564 time_req,
565 NULL,
566 i_input_token,
567 &inner_mech_type,
568 i_output_token,
569 &local_ret_flags,
570 time_rec);
571
572 if (ret_flags)
573 *ret_flags = local_ret_flags;
574
575 spnego_ctx->last_status = status;
576
577 if (i_input_token != GSS_C_NO_BUFFER) {
578 (void) gss_release_buffer(&mstat, i_input_token);
579 free(i_input_token);
580 }
581
582 if ((status != GSS_S_COMPLETE) &&
583 (status != GSS_S_CONTINUE_NEEDED)) {
584 ret = status;
585 }
586
587 /* create mic/check mic */
588 if ((i_output_token->length == 0) &&
589 (status == GSS_S_COMPLETE) &&
590 (local_ret_flags & GSS_C_INTEG_FLAG)) {
591 if (*ptr == (CONTEXT | 0x03)) {
592 if (g_get_tag_and_length(&ptr,
593 (CONTEXT | 0x03), input_token->length -
594 (ptr - (uchar_t *)input_token->value),
595 &len) < 0) {
596 ret = GSS_S_DEFECTIVE_TOKEN;
597 } else {
598 ret = GSS_S_COMPLETE;
599 mechListMIC = get_input_token(&ptr,
600 len);
601 if (mechListMIC == NULL)
602 ret = GSS_S_DEFECTIVE_TOKEN;
603 else if (!spnego_ctx->MS_Interop &&
604 spnego_ctx->DER_mechTypes.length >
605 0) {
606 status = gss_verify_mic(
607 minor_status,
608 spnego_ctx->ctx_handle,
609 &spnego_ctx->DER_mechTypes,
610 mechListMIC, qop_state);
611 }
612 }
613 } else if (!spnego_ctx->MS_Interop) {
614 /*
615 * If no MIC was sent and we are in
616 * "standard" mode (i.e. NOT MS_Interop),
617 * the MIC must be present.
618 */
619 ret = GSS_S_DEFECTIVE_TOKEN;
620 } else {
621 /* In "MS_Interop" mode, MIC is ignored. */
622 ret = GSS_S_COMPLETE;
623 }
624 }
625 }
626
627 if ((status == GSS_S_COMPLETE) &&
628 (ret == GSS_S_COMPLETE)) {
629 if (actual_mech) {
630 (void) generic_gss_release_oid(&mstat, actual_mech);
631 ret = generic_gss_copy_oid(&mstat,
632 spnego_ctx->internal_mech, actual_mech);
633 if (ret != GSS_S_COMPLETE)
634 goto cleanup;
635 }
636
637 } else if (ret == GSS_S_CONTINUE_NEEDED) {
638 if (make_spnego_tokenInit_msg(spnego_ctx,
639 mechSet, i_output_token, send_token,
640 output_token) < 0) {
641 ret = GSS_S_DEFECTIVE_TOKEN;
642 }
643 }
644
645 cleanup:
646 if (status != GSS_S_COMPLETE)
647 ret = status;
648 if (ret != GSS_S_COMPLETE &&
649 ret != GSS_S_CONTINUE_NEEDED) {
650 if (spnego_ctx != NULL &&
651 spnego_ctx->ctx_handle != NULL)
652 gss_delete_sec_context(&mstat, &spnego_ctx->ctx_handle,
653 GSS_C_NO_BUFFER);
654
655 if (spnego_ctx != NULL)
656 release_spnego_ctx(&spnego_ctx);
657
658 *context_handle = GSS_C_NO_CONTEXT;
659
660 if (output_token)
661 (void) gss_release_buffer(&mstat, output_token);
662 }
663
664 if (i_output_token != GSS_C_NO_BUFFER) {
665 (void) gss_release_buffer(&mstat, i_output_token);
666 free(i_output_token);
667 }
668
669 if (mechListMIC != GSS_C_NO_BUFFER) {
670 (void) gss_release_buffer(&mstat, mechListMIC);
671 free(mechListMIC);
672 }
673
674 if (mechSet != NULL)
675 (void) gss_release_oid_set(&mstat, &mechSet);
676
677 if (credlistptr != NULL)
678 (void) gss_release_cred(&mstat, credlistptr);
679
680 return (ret);
681 } /* init_sec_context */
682
683 /*ARGSUSED*/
684 OM_uint32
685 spnego_gss_accept_sec_context(void *ct,
686 OM_uint32 *minor_status,
687 gss_ctx_id_t *context_handle,
688 gss_cred_id_t verifier_cred_handle,
689 gss_buffer_t input_token,
690 gss_channel_bindings_t input_chan_bindings,
691 gss_name_t *src_name,
692 gss_OID *mech_type,
693 gss_buffer_t output_token,
694 OM_uint32 *ret_flags,
695 OM_uint32 *time_rec,
696 gss_cred_id_t *delegated_cred_handle)
697 {
698 spnego_gss_ctx_id_t spnego_ctx = NULL;
699 gss_OID mech_wanted = NULL;
700 gss_OID_set mechSet = GSS_C_NO_OID_SET;
701 gss_OID_set supported_mechSet = GSS_C_NO_OID_SET;
702 gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
703 gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
704 gss_buffer_t mechListMIC = GSS_C_NO_BUFFER;
705 gss_cred_id_t acquired_cred = NULL;
706 gss_name_t internal_name = GSS_C_NO_NAME;
707 OM_uint32 status = GSS_S_COMPLETE;
708 OM_uint32 ret = GSS_S_COMPLETE;
709 unsigned char *ptr;
710 unsigned char *bufstart;
711 int bodysize;
712 int err, len;
713 OM_uint32 negResult;
714 OM_uint32 minor_stat;
715 OM_uint32 mstat;
716 OM_uint32 req_flags;
717 OM_uint32 mechsetlen;
718 gss_qop_t qop_state;
719 send_token_flag return_token = NO_TOKEN_SEND;
720 bool_t firstMech;
721 bool_t Need_Cred = FALSE;
722 OM_uint32 local_ret_flags = 0;
723 uchar_t *buf, *tmp;
724
725 dsyslog("Entering accept_sec_context\n");
726
727 if (context_handle == NULL)
728 return (GSS_S_NO_CONTEXT);
729
730 if (src_name)
731 *src_name = (gss_name_t)NULL;
732
733 output_token->length = 0;
734 output_token->value = NULL;
735 *minor_status = 0;
736
737 if (mech_type)
738 *mech_type = GSS_C_NULL_OID;
739
740 /* return a bogus cred handle */
741 if (delegated_cred_handle)
742 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
743
744 if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
745 Need_Cred = TRUE;
746 }
747
748 /* Check for defective input token. */
749 ptr = bufstart = (unsigned char *) input_token->value;
750 if (err = g_verify_token_header((gss_OID)gss_mech_spnego, &bodysize,
751 &ptr, 0, input_token->length)) {
752 *minor_status = err;
753 ret = GSS_S_DEFECTIVE_TOKEN;
754 negResult = REJECT;
755 return_token = ERROR_TOKEN_SEND;
756 goto senderror;
757 }
758
759 /*
760 * set up of context, determine mech to be used, save mechset
761 * for use later in integrety check.
762 */
763 if (*context_handle == GSS_C_NO_CONTEXT) {
764 if ((spnego_ctx = create_spnego_ctx()) == NULL)
765 return (GSS_S_FAILURE);
766
767 /*
768 * Until the accept operation is complete, the
769 * context_handle returned should refer to
770 * the spnego context.
771 */
772 *context_handle = (gss_ctx_id_t)spnego_ctx;
773 minor_stat = get_available_mechs(minor_status,
774 GSS_C_NO_NAME, GSS_C_ACCEPT,
775 NULL, &supported_mechSet);
776
777 if (minor_stat != GSS_S_COMPLETE) {
778 release_spnego_ctx(&spnego_ctx);
779 *context_handle = GSS_C_NO_CONTEXT;
780 return (minor_stat);
781 }
782
783 if (Need_Cred) {
784 minor_stat = gss_acquire_cred(minor_status,
785 GSS_C_NO_NAME, NULL, supported_mechSet,
786 GSS_C_ACCEPT, &acquired_cred, NULL,
787 NULL);
788
789 if (minor_stat != GSS_S_COMPLETE) {
790 (void) gss_release_oid_set(minor_status,
791 &supported_mechSet);
792 release_spnego_ctx(&spnego_ctx);
793 *context_handle = GSS_C_NO_CONTEXT;
794 return (minor_stat);
795 } else {
796 verifier_cred_handle = acquired_cred;
797 }
798 }
799
800 if (err = g_verify_neg_token_init(&ptr, input_token->length)) {
801 *minor_status = err;
802 ret = GSS_S_DEFECTIVE_TOKEN;
803 negResult = REJECT;
804 return_token = ERROR_TOKEN_SEND;
805 goto senderror;
806 }
807
808 /*
809 * Allocate space to hold the mechTypes
810 * because we need it later.
811 */
812 mechsetlen = input_token->length - (ptr - bufstart);
813 buf = (uchar_t *)malloc(mechsetlen);
814 if (buf == NULL) {
815 ret = GSS_S_FAILURE;
816 goto cleanup;
817 }
818 (void) memcpy(buf, ptr, mechsetlen);
819 ptr = bufstart = buf;
820
821 /*
822 * Get pointers to the DER encoded MechSet so we
823 * can properly check and calculate a MIC later.
824 */
825 spnego_ctx->DER_mechTypes.value = ptr;
826 mechSet = get_mech_set(minor_status, &ptr, mechsetlen);
827 if (mechSet == NULL) {
828 ret = GSS_S_DEFECTIVE_TOKEN;
829 negResult = REJECT;
830 return_token = ERROR_TOKEN_SEND;
831 goto senderror;
832 }
833 spnego_ctx->DER_mechTypes.length = ptr - bufstart;
834 mechsetlen -= (ptr - bufstart);
835
836 /*
837 * Select the best match between the list of mechs
838 * that the initiator requested and the list that
839 * the acceptor will support.
840 */
841 mech_wanted = negotiate_mech_type(minor_status,
842 supported_mechSet, mechSet, &negResult,
843 &firstMech);
844
845 (void) gss_release_oid_set(&minor_stat, &supported_mechSet);
846 (void) gss_release_oid_set(&minor_stat, &mechSet);
847 supported_mechSet = NULL;
848 mechSet = NULL;
849
850 if (get_req_flags(&ptr, (int *)&mechsetlen, &req_flags) ==
851 ACCEPT_DEFECTIVE_TOKEN) {
852 negResult = REJECT;
853 }
854
855 tmp = ptr;
856 if (negResult == ACCEPT_COMPLETE) {
857 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
858 mechsetlen, &len) < 0) {
859 negResult = REJECT;
860 } else {
861 i_input_token = get_input_token(&ptr, len);
862 if (i_input_token == NULL) {
863 negResult = REJECT;
864 }
865 }
866 return_token = INIT_TOKEN_SEND;
867 }
868 if (negResult == REJECT) {
869 ret = GSS_S_DEFECTIVE_TOKEN;
870 return_token = ERROR_TOKEN_SEND;
871 } else {
872 ret = GSS_S_CONTINUE_NEEDED;
873 return_token = INIT_TOKEN_SEND;
874 }
875
876 mechsetlen -= ptr - tmp;
877 /*
878 * Check to see if there is a MechListMIC field
879 */
880 if (negResult == ACCEPT_COMPLETE && mechsetlen > 0) {
881 tmp = ptr;
882 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
883 mechsetlen, &len) >= 0) {
884 mechListMIC = get_input_token(&ptr, len);
885 if (mechListMIC == GSS_C_NO_BUFFER) {
886 negResult = REJECT;
887 return_token = ERROR_TOKEN_SEND;
888 ret = GSS_S_DEFECTIVE_TOKEN;
889 }
890 mechsetlen -= (ptr - tmp);
891 }
892 }
893 } else {
894 /*
895 * get internal input token and context for continued
896 * calls of spnego_gss_init_sec_context.
897 */
898 i_input_token = get_input_token(&ptr,
899 input_token->length - (ptr -
900 (uchar_t *)input_token->value));
901 if (i_input_token == NULL) {
902 negResult = REJECT;
903 return_token = ERROR_TOKEN_SEND;
904 ret = GSS_S_DEFECTIVE_TOKEN;
905 } else {
906 spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
907 return_token = CONT_TOKEN_SEND;
908 }
909 }
910
911 /*
912 * If we still don't have a cred, we have an error.
913 */
914 if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
915 ret = GSS_S_FAILURE;
916 goto cleanup;
917 }
918
919 /* If we have an error already, bail out */
920 if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED)
921 goto senderror;
922
923 if (i_input_token != GSS_C_NO_BUFFER) {
924 i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
925
926 if (i_output_token == NULL) {
927 ret = GSS_S_FAILURE;
928 goto cleanup;
929 }
930
931 i_output_token->length = 0;
932 i_output_token->value = NULL;
933
934 status = gss_accept_sec_context(&minor_stat,
935 &spnego_ctx->ctx_handle, verifier_cred_handle,
936 i_input_token, GSS_C_NO_CHANNEL_BINDINGS,
937 &internal_name, mech_type, i_output_token,
938 &local_ret_flags, time_rec, delegated_cred_handle);
939
940 if ((status != GSS_S_COMPLETE) &&
941 (status != GSS_S_CONTINUE_NEEDED)) {
942 *minor_status = minor_stat;
943 (void) gss_release_buffer(&mstat, i_input_token);
944
945 if (i_input_token != GSS_C_NO_BUFFER) {
946 free(i_input_token);
947 i_input_token = GSS_C_NO_BUFFER;
948 }
949
950 ret = status;
951
952 /*
953 * Reject the request with an error token.
954 */
955 negResult = REJECT;
956 return_token = ERROR_TOKEN_SEND;
957
958 goto senderror;
959 }
960
961 if (ret_flags)
962 *ret_flags = local_ret_flags;
963
964 if (i_input_token != GSS_C_NO_BUFFER) {
965 (void) gss_release_buffer(&mstat, i_input_token);
966 free(i_input_token);
967 i_input_token = GSS_C_NO_BUFFER;
968 }
969
970 /* If we got a MIC, verify it if possible */
971 if ((status == GSS_S_COMPLETE) &&
972 (local_ret_flags & GSS_C_INTEG_FLAG) &&
973 mechListMIC != GSS_C_NO_BUFFER &&
974 !spnego_ctx->MS_Interop) {
975
976 ret = gss_verify_mic(minor_status,
977 spnego_ctx->ctx_handle,
978 &spnego_ctx->DER_mechTypes,
979 mechListMIC, &qop_state);
980
981 (void) gss_release_buffer(&mstat, mechListMIC);
982 free(mechListMIC);
983 mechListMIC = GSS_C_NO_BUFFER;
984
985 if (ret != GSS_S_COMPLETE) {
986 negResult = REJECT;
987 return_token = ERROR_TOKEN_SEND;
988 goto senderror;
989 }
990 }
991
992 /*
993 * If the MIC was verified OK, create a new MIC
994 * for the response message.
995 */
996 if (status == GSS_S_COMPLETE &&
997 (local_ret_flags & GSS_C_INTEG_FLAG) &&
998 !spnego_ctx->MS_Interop) {
999 mechListMIC = (gss_buffer_t)
1000 malloc(sizeof (gss_buffer_desc));
1001
1002 if (mechListMIC == NULL ||
1003 spnego_ctx->DER_mechTypes.length == 0) {
1004 ret = GSS_S_FAILURE;
1005 goto cleanup;
1006 }
1007
1008 ret = gss_get_mic(minor_status,
1009 spnego_ctx->ctx_handle,
1010 GSS_C_QOP_DEFAULT,
1011 &spnego_ctx->DER_mechTypes,
1012 mechListMIC);
1013
1014 if (ret != GSS_S_COMPLETE) {
1015 negResult = REJECT;
1016 return_token = ERROR_TOKEN_SEND;
1017 goto senderror;
1018 }
1019 }
1020 ret = status;
1021
1022 if (status == GSS_S_COMPLETE) {
1023 if (internal_name != NULL && src_name != NULL)
1024 *src_name = internal_name;
1025 }
1026
1027
1028 if (status == GSS_S_CONTINUE_NEEDED) {
1029 if (return_token == INIT_TOKEN_SEND)
1030 negResult = ACCEPT_INCOMPLETE;
1031 }
1032 }
1033
1034 senderror:
1035 if ((return_token == INIT_TOKEN_SEND) ||
1036 (return_token == CONT_TOKEN_SEND) ||
1037 (return_token == ERROR_TOKEN_SEND)) {
1038 int MS_Interop = 0;
1039
1040 if (spnego_ctx)
1041 MS_Interop = spnego_ctx->MS_Interop;
1042
1043 /*
1044 * create response for the initiator.
1045 */
1046 err = make_spnego_tokenTarg_msg(negResult,
1047 mech_wanted, i_output_token,
1048 mechListMIC, return_token,
1049 MS_Interop, output_token);
1050
1051 (void) gss_release_buffer(&mstat, mechListMIC);
1052 free(mechListMIC);
1053
1054 /*
1055 * If we could not make the response token,
1056 * we will have to fail without sending a response.
1057 */
1058 if (err) {
1059 (void) gss_release_buffer(&mstat, output_token);
1060 }
1061 } else {
1062 (void) gss_release_buffer(&mstat, output_token);
1063 }
1064
1065 cleanup:
1066 if (ret != GSS_S_COMPLETE &&
1067 ret != GSS_S_CONTINUE_NEEDED) {
1068 if (spnego_ctx != NULL) {
1069 (void) gss_delete_sec_context(&mstat,
1070 &spnego_ctx->ctx_handle, NULL);
1071
1072 spnego_ctx->ctx_handle = NULL;
1073
1074 release_spnego_ctx(&spnego_ctx);
1075 }
1076 *context_handle = GSS_C_NO_CONTEXT;
1077 }
1078 if (mech_wanted != NULL) {
1079 generic_gss_release_oid(&mstat, &mech_wanted);
1080 }
1081
1082 (void) gss_release_cred(minor_status, &acquired_cred);
1083 (void) gss_release_oid_set(minor_status, &supported_mechSet);
1084
1085 (void) gss_release_buffer(&mstat, i_output_token);
1086 free(i_output_token);
1087
1088 return (ret);
1089 }
1090
1091 /*ARGSUSED*/
1092 OM_uint32
1093 spnego_gss_display_status(void *ctx,
1094 OM_uint32 *minor_status,
1095 OM_uint32 status_value,
1096 int status_type,
1097 gss_OID mech_type,
1098 OM_uint32 *message_context,
1099 gss_buffer_t status_string)
1100 {
1101 OM_uint32 ret = GSS_S_COMPLETE;
1102 dsyslog("Entering display_status\n");
1103
1104 *message_context = 0;
1105 switch (status_value) {
1106 case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1107 *status_string = make_err_msg(gettext(
1108 "SPNEGO cannot find mechanisms to negotiate"));
1109 break;
1110 case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1111 *status_string = make_err_msg(gettext(
1112 "SPNEGO failed to acquire creds"));
1113 break;
1114 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1115 *status_string = make_err_msg(gettext(
1116 "SPNEGO acceptor did not select a mechanism"));
1117 break;
1118 case ERR_SPNEGO_NEGOTIATION_FAILED:
1119 *status_string = make_err_msg(gettext(
1120 "SPNEGO failed to negotiate a mechanism"));
1121 break;
1122 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1123 *status_string = make_err_msg(gettext(
1124 "SPNEGO acceptor did not return a valid token"));
1125 break;
1126 case ERR_SPNEGO_BAD_INPUT_PARAMETER:
1127 *status_string = make_err_msg(gettext(
1128 "SPNEGO function received an incorrect input "
1129 "parameter"));
1130 break;
1131 default:
1132 status_string->length = 0;
1133 status_string->value = "";
1134 ret = GSS_S_BAD_STATUS;
1135 break;
1136 }
1137
1138 dsyslog("Leaving display_status\n");
1139 return (ret);
1140 }
1141
1142 /*ARGSUSED*/
1143 OM_uint32
1144 spnego_gss_import_name(void *ctx,
1145 OM_uint32 *minor_status,
1146 gss_buffer_t input_name_buffer,
1147 gss_OID input_name_type,
1148 gss_name_t *output_name)
1149 {
1150 OM_uint32 status;
1151
1152 dsyslog("Entering import_name\n");
1153
1154 status = gss_import_name(minor_status, input_name_buffer,
1155 input_name_type, output_name);
1156
1157 dsyslog("Leaving import_name\n");
1158 return (status);
1159 }
1160
1161 /*ARGSUSED*/
1162 OM_uint32
1163 spnego_gss_release_name(void *ctx,
1164 OM_uint32 *minor_status,
1165 gss_name_t *input_name)
1166 {
1167 OM_uint32 status;
1168
1169 dsyslog("Entering release_name\n");
1170
1171 status = gss_release_name(minor_status, input_name);
1172
1173 dsyslog("Leaving release_name\n");
1174 return (status);
1175 }
1176
1177 /*ARGSUSED*/
1178 OM_uint32
1179 spnego_gss_display_name(void *ctx,
1180 OM_uint32 *minor_status,
1181 gss_name_t input_name,
1182 gss_buffer_t output_name_buffer,
1183 gss_OID *output_name_type)
1184 {
1185 OM_uint32 status = GSS_S_COMPLETE;
1186 dsyslog("Entering display_name\n");
1187
1188 status = gss_display_name(minor_status, input_name,
1189 output_name_buffer, output_name_type);
1190
1191 dsyslog("Leaving display_name\n");
1192 return (status);
1193 }
1194
1195 /*ARGSUSED*/
1196 OM_uint32
1197 spnego_gss_inquire_cred(void *ctx,
1198 OM_uint32 *minor_status,
1199 const gss_cred_id_t cred_handle,
1200 gss_name_t *name,
1201 OM_uint32 *lifetime,
1202 gss_cred_usage_t *cred_usage,
1203 gss_OID_set *mechanisms)
1204 {
1205 OM_uint32 stat = GSS_S_COMPLETE;
1206 gss_cred_id_t *credlistptr = NULL, credlist = NULL;
1207 OM_uint32 init_lt, accept_lt;
1208 int i;
1209
1210 if (cred_handle == GSS_C_NO_CREDENTIAL) {
1211 OM_uint32 tstat;
1212 credlistptr = &credlist;
1213
1214 /*
1215 * Get a list of all non-SPNEGO
1216 * mechanisms that are available and
1217 * acquire a default cred.
1218 */
1219 stat = get_available_mechs(minor_status,
1220 NULL, GSS_C_BOTH, credlistptr, mechanisms);
1221
1222 /*
1223 * inquire about the default cred from the
1224 * first non-SPNEGO mechanism that was found.
1225 */
1226 if (stat == GSS_S_COMPLETE && mechanisms &&
1227 (*mechanisms)->count > 0) {
1228 i = 0;
1229 do {
1230 stat = gss_inquire_cred_by_mech(
1231 minor_status, credlist,
1232 &((*mechanisms)->elements[i]),
1233 name, &init_lt, &accept_lt,
1234 cred_usage);
1235
1236 /*
1237 * Set the lifetime to the correct value.
1238 */
1239 if (stat == GSS_S_COMPLETE) {
1240 if (*cred_usage == GSS_C_INITIATE)
1241 *lifetime = init_lt;
1242 else
1243 *lifetime = accept_lt;
1244 }
1245 if (credlist != GSS_C_NO_CREDENTIAL)
1246 (void) gss_release_cred(&tstat,
1247 &credlist);
1248 } while (stat != GSS_S_COMPLETE &&
1249 (i < (*mechanisms)->count));
1250 }
1251 } else {
1252 /*
1253 * This should not happen, it cannot be processed.
1254 */
1255 stat = GSS_S_FAILURE;
1256 if (minor_status != NULL)
1257 *minor_status = ERR_SPNEGO_BAD_INPUT_PARAMETER;
1258 }
1259 return (stat);
1260 }
1261
1262 /*ARGSUSED*/
1263 OM_uint32
1264 spnego_gss_inquire_names_for_mech(void *ctx,
1265 OM_uint32 *minor_status,
1266 gss_OID mechanism,
1267 gss_OID_set *name_types)
1268 {
1269 OM_uint32 major, minor;
1270
1271 dsyslog("Entering inquire_names_for_mech\n");
1272 /*
1273 * We only know how to handle our own mechanism.
1274 */
1275 if ((mechanism != GSS_C_NULL_OID) &&
1276 !g_OID_equal(gss_mech_spnego, mechanism)) {
1277 *minor_status = 0;
1278 return (GSS_S_FAILURE);
1279 }
1280
1281 major = gss_create_empty_oid_set(minor_status, name_types);
1282 if (major == GSS_S_COMPLETE) {
1283 /* Now add our members. */
1284 if (((major = gss_add_oid_set_member(minor_status,
1285 (gss_OID) GSS_C_NT_USER_NAME,
1286 name_types)) == GSS_S_COMPLETE) &&
1287 ((major = gss_add_oid_set_member(minor_status,
1288 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
1289 name_types)) == GSS_S_COMPLETE) &&
1290 ((major = gss_add_oid_set_member(minor_status,
1291 (gss_OID) GSS_C_NT_STRING_UID_NAME,
1292 name_types)) == GSS_S_COMPLETE)) {
1293 major = gss_add_oid_set_member(minor_status,
1294 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
1295 name_types);
1296 }
1297
1298 if (major != GSS_S_COMPLETE)
1299 (void) gss_release_oid_set(&minor, name_types);
1300 }
1301
1302 dsyslog("Leaving inquire_names_for_mech\n");
1303 return (major);
1304 }
1305
1306 OM_uint32
1307 spnego_gss_unseal(void *context,
1308 OM_uint32 *minor_status,
1309 gss_ctx_id_t context_handle,
1310 gss_buffer_t input_message_buffer,
1311 gss_buffer_t output_message_buffer,
1312 int *conf_state,
1313 int *qop_state)
1314 {
1315 OM_uint32 ret;
1316 spnego_gss_ctx_id_t ctx = (spnego_gss_ctx_id_t)context_handle;
1317
1318 if (context_handle == NULL)
1319 return (GSS_S_NO_CONTEXT);
1320
1321 ret = gss_unseal(minor_status,
1322 ctx->ctx_handle, input_message_buffer,
1323 output_message_buffer, conf_state, qop_state);
1324
1325 return (ret);
1326 }
1327
1328 OM_uint32
1329 spnego_gss_seal(void *context,
1330 OM_uint32 *minor_status,
1331 gss_ctx_id_t context_handle,
1332 int conf_req_flag,
1333 int qop_req,
1334 gss_buffer_t input_message_buffer,
1335 int *conf_state,
1336 gss_buffer_t output_message_buffer)
1337 {
1338 OM_uint32 ret;
1339 spnego_gss_ctx_id_t ctx =
1340 (spnego_gss_ctx_id_t)context_handle;
1341
1342 if (context_handle == NULL)
1343 return (GSS_S_NO_CONTEXT);
1344
1345 ret = gss_seal(minor_status,
1346 ctx->ctx_handle, conf_req_flag,
1347 qop_req, input_message_buffer,
1348 conf_state, output_message_buffer);
1349
1350 return (ret);
1351 }
1352
1353 OM_uint32
1354 spnego_gss_process_context_token(void *context,
1355 OM_uint32 *minor_status,
1356 const gss_ctx_id_t context_handle,
1357 const gss_buffer_t token_buffer)
1358 {
1359 OM_uint32 ret;
1360 spnego_gss_ctx_id_t ctx =
1361 (spnego_gss_ctx_id_t)context_handle;
1362
1363 if (context_handle == NULL)
1364 return (GSS_S_NO_CONTEXT);
1365
1366 ret = gss_process_context_token(minor_status,
1367 ctx->ctx_handle, token_buffer);
1368
1369 return (ret);
1370 }
1371
1372 OM_uint32
1373 spnego_gss_delete_sec_context(void *context,
1374 OM_uint32 *minor_status,
1375 gss_ctx_id_t *context_handle,
1376 gss_buffer_t output_token)
1377 {
1378 OM_uint32 ret = GSS_S_COMPLETE;
1379 spnego_gss_ctx_id_t *ctx =
1380 (spnego_gss_ctx_id_t *)context_handle;
1381
1382 if (context_handle == NULL || *ctx == NULL)
1383 return (GSS_S_NO_CONTEXT);
1384
1385 /*
1386 * If this is still a SPNEGO mech, release it locally.
1387 */
1388 if ((*ctx)->ctx_handle == GSS_C_NO_CONTEXT) {
1389 (void) release_spnego_ctx(ctx);
1390 } else {
1391 ret = gss_delete_sec_context(minor_status,
1392 &(*ctx)->ctx_handle, output_token);
1393 }
1394
1395 return (ret);
1396 }
1397
1398 OM_uint32
1399 spnego_gss_context_time(void *context,
1400 OM_uint32 *minor_status,
1401 const gss_ctx_id_t context_handle,
1402 OM_uint32 *time_rec)
1403 {
1404 OM_uint32 ret;
1405 spnego_gss_ctx_id_t ctx =
1406 (spnego_gss_ctx_id_t)context_handle;
1407
1408 if (context_handle == NULL)
1409 return (GSS_S_NO_CONTEXT);
1410
1411 ret = gss_context_time(minor_status,
1412 ctx->ctx_handle, time_rec);
1413
1414 return (ret);
1415 }
1416
1417 OM_uint32
1418 spnego_gss_export_sec_context(void *context,
1419 OM_uint32 *minor_status,
1420 gss_ctx_id_t *context_handle,
1421 gss_buffer_t interprocess_token)
1422 {
1423 OM_uint32 ret;
1424 spnego_gss_ctx_id_t *ctx =
1425 (spnego_gss_ctx_id_t *)context_handle;
1426
1427 if (context_handle == NULL || *ctx == NULL)
1428 return (GSS_S_NO_CONTEXT);
1429
1430 ret = gss_export_sec_context(minor_status,
1431 &(*ctx)->ctx_handle, interprocess_token);
1432 return (ret);
1433 }
1434
1435 OM_uint32
1436 spnego_gss_import_sec_context(void *context,
1437 OM_uint32 *minor_status,
1438 const gss_buffer_t interprocess_token,
1439 gss_ctx_id_t *context_handle)
1440 {
1441 OM_uint32 ret;
1442 spnego_gss_ctx_id_t ctx;
1443
1444 if (context_handle == NULL)
1445 return (GSS_S_NO_CONTEXT);
1446
1447 if ((ctx = create_spnego_ctx()) == NULL) {
1448 *minor_status = ENOMEM;
1449 return (GSS_S_FAILURE);
1450 }
1451
1452 ret = gss_import_sec_context(minor_status,
1453 interprocess_token, &(ctx->ctx_handle));
1454 if (GSS_ERROR(ret)) {
1455 (void) release_spnego_ctx(&ctx);
1456 return (ret);
1457 }
1458
1459 *context_handle = (gss_ctx_id_t)ctx;
1460
1461 return (ret);
1462 }
1463
1464 OM_uint32
1465 spnego_gss_inquire_context(void *context,
1466 OM_uint32 *minor_status,
1467 const gss_ctx_id_t context_handle,
1468 gss_name_t *src_name,
1469 gss_name_t *targ_name,
1470 OM_uint32 *lifetime_rec,
1471 gss_OID *mech_type,
1472 OM_uint32 *ctx_flags,
1473 int *locally_initiated,
1474 int *open)
1475 {
1476 OM_uint32 ret = GSS_S_COMPLETE;
1477 spnego_gss_ctx_id_t ctx =
1478 (spnego_gss_ctx_id_t)context_handle;
1479
1480 if (context_handle == NULL)
1481 return (GSS_S_NO_CONTEXT);
1482
1483 ret = gss_inquire_context(minor_status,
1484 ctx->ctx_handle, src_name,
1485 targ_name, lifetime_rec,
1486 mech_type, ctx_flags,
1487 locally_initiated, open);
1488
1489 return (ret);
1490 }
1491
1492 OM_uint32
1493 spnego_gss_wrap_size_limit(void *context,
1494 OM_uint32 *minor_status,
1495 const gss_ctx_id_t context_handle,
1496 int conf_req_flag,
1497 gss_qop_t qop_req,
1498 OM_uint32 req_output_size,
1499 OM_uint32 *max_input_size)
1500 {
1501 OM_uint32 ret;
1502 spnego_gss_ctx_id_t ctx =
1503 (spnego_gss_ctx_id_t)context_handle;
1504
1505 if (context_handle == NULL)
1506 return (GSS_S_NO_CONTEXT);
1507
1508 ret = gss_wrap_size_limit(minor_status,
1509 ctx->ctx_handle, conf_req_flag,
1510 qop_req, req_output_size,
1511 max_input_size);
1512 return (ret);
1513 }
1514
1515 OM_uint32
1516 spnego_gss_sign(void *context,
1517 OM_uint32 *minor_status,
1518 const gss_ctx_id_t context_handle,
1519 int qop_req,
1520 const gss_buffer_t message_buffer,
1521 gss_buffer_t message_token)
1522 {
1523 OM_uint32 ret;
1524 spnego_gss_ctx_id_t ctx =
1525 (spnego_gss_ctx_id_t)context_handle;
1526
1527 if (context_handle == NULL)
1528 return (GSS_S_NO_CONTEXT);
1529
1530 ret = gss_sign(minor_status,
1531 ctx->ctx_handle,
1532 qop_req,
1533 message_buffer,
1534 message_token);
1535
1536 return (ret);
1537 }
1538
1539 OM_uint32
1540 spnego_gss_verify(void *context,
1541 OM_uint32 *minor_status,
1542 const gss_ctx_id_t context_handle,
1543 const gss_buffer_t msg_buffer,
1544 const gss_buffer_t token_buffer,
1545 int *qop_state)
1546 {
1547 OM_uint32 ret;
1548 spnego_gss_ctx_id_t ctx =
1549 (spnego_gss_ctx_id_t)context_handle;
1550
1551 if (context_handle == NULL)
1552 return (GSS_S_NO_CONTEXT);
1553
1554 ret = gss_verify_mic(minor_status,
1555 ctx->ctx_handle,
1556 msg_buffer,
1557 token_buffer,
1558 (uint32_t *)qop_state);
1559 return (ret);
1560 }
1561
1562 /*
1563 * We will release everything but the ctx_handle so that it
1564 * can be passed back to init/accept context. This routine should
1565 * not be called until after the ctx_handle memory is assigned to
1566 * the supplied context handle from init/accept context.
1567 */
1568 static void
1569 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
1570 {
1571 spnego_gss_ctx_id_t context;
1572 OM_uint32 minor_stat;
1573
1574 if (ctx != NULL)
1575 context = *ctx;
1576 else
1577 return;
1578
1579 if (context != NULL) {
1580 (void) gss_release_buffer(&minor_stat,
1581 &context->DER_mechTypes);
1582
1583 (void) generic_gss_release_oid(&minor_stat,
1584 &context->internal_mech);
1585
1586 if (context->optionStr != NULL) {
1587 free(context->optionStr);
1588 context->optionStr = NULL;
1589 }
1590 if (context->ctx_handle != GSS_C_NO_CONTEXT)
1591 gss_delete_sec_context(&minor_stat,
1592 &context->ctx_handle, GSS_C_NO_BUFFER);
1593
1594 free(context);
1595 *ctx = NULL;
1596 }
1597 }
1598
1599 /*
1600 * Can't use gss_indicate_mechs by itself to get available mechs for
1601 * SPNEGO because it will also return the SPNEGO mech and we do not
1602 * want to consider SPNEGO as an available security mech for
1603 * negotiation. For this reason, get_available_mechs will return
1604 * all available mechs except SPNEGO.
1605 *
1606 * If a ptr to a creds list is given, this function will attempt
1607 * to acquire creds for the creds given and trim the list of
1608 * returned mechanisms to only those for which creds are valid.
1609 *
1610 */
1611 static OM_uint32
1612 get_available_mechs(OM_uint32 *minor_status,
1613 gss_name_t name, gss_cred_usage_t usage,
1614 gss_cred_id_t *creds, gss_OID_set *rmechs)
1615 {
1616 int i;
1617 int found = 0;
1618 OM_uint32 stat = GSS_S_COMPLETE;
1619 gss_OID_set mechs, goodmechs;
1620
1621 stat = gss_indicate_mechs(minor_status, &mechs);
1622
1623 if (stat != GSS_S_COMPLETE) {
1624 return (stat);
1625 }
1626
1627 stat = gss_create_empty_oid_set(minor_status, rmechs);
1628
1629 if (stat != GSS_S_COMPLETE) {
1630 (void) gss_release_oid_set(minor_status, &mechs);
1631 return (stat);
1632 }
1633
1634 for (i = 0; i < mechs->count && stat == GSS_S_COMPLETE; i++) {
1635 if ((mechs->elements[i].length
1636 != spnego_mechanism.mech_type.length) ||
1637 memcmp(mechs->elements[i].elements,
1638 spnego_mechanism.mech_type.elements,
1639 spnego_mechanism.mech_type.length)) {
1640
1641 stat = gss_add_oid_set_member(minor_status,
1642 &mechs->elements[i], rmechs);
1643 if (stat == GSS_S_COMPLETE)
1644 found++;
1645 }
1646 }
1647
1648 /*
1649 * If the caller wanted a list of creds returned,
1650 * trim the list of mechanisms down to only those
1651 * for which the creds are valid.
1652 */
1653 if (found > 0 && stat == GSS_S_COMPLETE && creds != NULL) {
1654 stat = gss_acquire_cred(minor_status,
1655 name, NULL, *rmechs, usage, creds,
1656 &goodmechs, NULL);
1657
1658 /*
1659 * Drop the old list in favor of the new
1660 * "trimmed" list.
1661 */
1662 (void) gss_release_oid_set(minor_status, rmechs);
1663 if (stat == GSS_S_COMPLETE) {
1664 (void) gss_copy_oid_set(minor_status,
1665 goodmechs, rmechs);
1666 (void) gss_release_oid_set(minor_status, &goodmechs);
1667 }
1668 }
1669
1670 (void) gss_release_oid_set(minor_status, &mechs);
1671 if (found == 0 || stat != GSS_S_COMPLETE) {
1672 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
1673 if (stat == GSS_S_COMPLETE)
1674 stat = GSS_S_FAILURE;
1675 }
1676
1677 return (stat);
1678 }
1679
1680 /* following are token creation and reading routines */
1681
1682 /*
1683 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
1684 * advance the buffer, otherwise, decode the mech_oid from the buffer and
1685 * place in gss_OID.
1686 */
1687 static gss_OID
1688 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
1689 {
1690 OM_uint32 status;
1691 gss_OID_desc toid;
1692 gss_OID mech_out = NULL;
1693 uchar_t *start, *end;
1694
1695 if (length < 1 || **buff_in != MECH_OID)
1696 return (NULL);
1697
1698 start = *buff_in;
1699 end = start + length;
1700
1701 (*buff_in)++;
1702 toid.length = *(*buff_in)++;
1703
1704 if ((*buff_in + toid.length) > end)
1705 return (NULL);
1706
1707 toid.elements = *buff_in;
1708 *buff_in += toid.length;
1709
1710 status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
1711
1712 if (status != GSS_S_COMPLETE)
1713 mech_out = NULL;
1714
1715 return (mech_out);
1716 }
1717
1718 /*
1719 * der encode the given mechanism oid into buf_out, advancing the
1720 * buffer pointer.
1721 */
1722
1723 static int
1724 put_mech_oid(unsigned char **buf_out, gss_OID_desc *mech, int buflen)
1725 {
1726 if (buflen < mech->length + 2)
1727 return (-1);
1728 *(*buf_out)++ = MECH_OID;
1729 *(*buf_out)++ = (unsigned char) mech->length;
1730 memcpy((void *)(*buf_out), mech->elements, mech->length);
1731 *buf_out += mech->length;
1732 return (0);
1733 }
1734
1735 /*
1736 * verify that buff_in points to an octet string, if it does not,
1737 * return NULL and don't advance the pointer. If it is an octet string
1738 * decode buff_in into a gss_buffer_t and return it, advancing the
1739 * buffer pointer.
1740 */
1741 static gss_buffer_t
1742 get_input_token(unsigned char **buff_in, int buff_length)
1743 {
1744 gss_buffer_t input_token;
1745 unsigned int bytes;
1746
1747 if (**buff_in != OCTET_STRING)
1748 return (NULL);
1749
1750 (*buff_in)++;
1751 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
1752
1753 if (input_token == NULL)
1754 return (NULL);
1755
1756 input_token->length = get_der_length(buff_in, buff_length, &bytes);
1757 if ((int)input_token->length == -1) {
1758 free(input_token);
1759 return (NULL);
1760 }
1761 input_token->value = malloc(input_token->length);
1762
1763 if (input_token->value == NULL) {
1764 free(input_token);
1765 return (NULL);
1766 }
1767
1768 (void) memcpy(input_token->value, *buff_in, input_token->length);
1769 *buff_in += input_token->length;
1770 return (input_token);
1771 }
1772
1773 /*
1774 * verify that the input token length is not 0. If it is, just return.
1775 * If the token length is greater than 0, der encode as an octet string
1776 * and place in buf_out, advancing buf_out.
1777 */
1778
1779 static int
1780 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
1781 int buflen)
1782 {
1783 int ret;
1784
1785 /* if token length is 0, we do not want to send */
1786 if (input_token->length == 0)
1787 return (0);
1788
1789 if (input_token->length > buflen)
1790 return (-1);
1791
1792 *(*buf_out)++ = OCTET_STRING;
1793 if ((ret = put_der_length(input_token->length, buf_out,
1794 input_token->length)))
1795 return (ret);
1796 TWRITE_STR(*buf_out, input_token->value, ((int)input_token->length));
1797 return (0);
1798 }
1799
1800 /*
1801 * verify that buff_in points to a sequence of der encoding. The mech
1802 * set is the only sequence of encoded object in the token, so if it is
1803 * a sequence of encoding, decode the mechset into a gss_OID_set and
1804 * return it, advancing the buffer pointer.
1805 */
1806 static gss_OID_set
1807 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, int buff_length)
1808 {
1809 gss_OID_set returned_mechSet;
1810 OM_uint32 major_status;
1811 OM_uint32 length;
1812 OM_uint32 bytes;
1813 OM_uint32 set_length;
1814 uchar_t *start;
1815 int i;
1816
1817 if (**buff_in != SEQUENCE_OF)
1818 return (NULL);
1819
1820 start = *buff_in;
1821 (*buff_in)++;
1822
1823 length = get_der_length(buff_in, buff_length, &bytes);
1824
1825 major_status = gss_create_empty_oid_set(minor_status,
1826 &returned_mechSet);
1827 if (major_status != GSS_S_COMPLETE)
1828 return (NULL);
1829
1830 for (set_length = 0, i = 0; set_length < length; i++) {
1831 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
1832 buff_length - (*buff_in - start));
1833 if (temp != NULL) {
1834 major_status = gss_add_oid_set_member(minor_status,
1835 temp, &returned_mechSet);
1836 if (major_status == GSS_S_COMPLETE) {
1837 set_length +=
1838 returned_mechSet->elements[i].length +2;
1839 generic_gss_release_oid(minor_status, &temp);
1840 }
1841 }
1842 }
1843
1844 return (returned_mechSet);
1845 }
1846
1847 /*
1848 * der encode the passed mechSet and place it into buf_out,
1849 * advancing the buffer pointer.
1850 */
1851 static int
1852 put_mech_set(uchar_t **buf_out, gss_OID_set mechSet, int buflen)
1853 {
1854 int i, ret;
1855 OM_uint32 length = 0;
1856 uchar_t *start;
1857
1858 if (buf_out == NULL || *buf_out == NULL)
1859 return (-1);
1860
1861 start = *buf_out;
1862
1863 *(*buf_out)++ = SEQUENCE_OF;
1864
1865 for (i = 0; i < mechSet->count; i++) {
1866 /*
1867 * Mech OID ASN.1 size = 2 + length.
1868 * 1 = 0x06, 1 for length of OID
1869 * typically, less than 128, so only 1 byte needed.
1870 */
1871 length += 1 + der_length_size(mechSet->elements[i].length) +
1872 mechSet->elements[i].length;
1873 }
1874 if (length > (buflen-1))
1875 return (-1);
1876
1877 if (put_der_length(length, buf_out, buflen-1) < 0)
1878 return (-1);
1879
1880 for (i = 0; i < mechSet->count; i++) {
1881 if ((ret = put_mech_oid(buf_out, &mechSet->elements[i],
1882 buflen - (int)(*buf_out - start))))
1883 return (ret);
1884 }
1885 return (0);
1886 }
1887
1888 /*
1889 * Verify that buff_in is pointing to a BIT_STRING with the correct
1890 * length and padding for the req_flags. If it is, decode req_flags
1891 * and return them, otherwise, return NULL.
1892 */
1893 static OM_uint32
1894 get_req_flags(unsigned char **buff_in, int *bodysize, OM_uint32 *req_flags)
1895 {
1896 int len;
1897 uchar_t *start = *buff_in;
1898
1899 /* It is OK if no ReqFlags data is sent. */
1900 if (**buff_in != (CONTEXT | 0x01))
1901 return (0);
1902
1903 /* If they are sent, make sure the fields are correct. */
1904 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1905 *bodysize, &len) < 0)
1906 return (ACCEPT_DEFECTIVE_TOKEN);
1907
1908 /* We don't care what the flags are. */
1909 (*buff_in) += len;
1910
1911 /* Don't return any flags, this field is useless */
1912 *req_flags = 0;
1913
1914 *bodysize -= *buff_in - start;
1915 return (0);
1916 }
1917
1918 /*
1919 * get the negotiation results, decoding the ENUMERATED type result
1920 * from the buffer, advancing the buffer pointer.
1921 */
1922 static OM_uint32
1923 get_negResult(unsigned char **buff_in, int bodysize)
1924 {
1925 unsigned char *iptr = *buff_in;
1926 int len;
1927 unsigned int bytes;
1928 OM_uint32 result;
1929 /*
1930 * Verify that the data is ASN.1 encoded correctly
1931 */
1932 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1933 bodysize, &len) < 0)
1934 return (ACCEPT_DEFECTIVE_TOKEN);
1935
1936 if (*(*buff_in)++ == SEQUENCE) {
1937 if ((len = get_der_length(buff_in,
1938 bodysize - (*buff_in - iptr), &bytes)) < 0)
1939 return (ACCEPT_DEFECTIVE_TOKEN);
1940 } else {
1941 return (ACCEPT_INCOMPLETE);
1942 }
1943
1944 /*
1945 * if we find an octet string, we need to return
1946 * incomplete so that we process the token correctly.
1947 * Anything else unexpected, we reject.
1948 */
1949 if (*(*buff_in)++ == CONTEXT) {
1950 if ((len = get_der_length(buff_in, bodysize -
1951 (*buff_in - iptr), &bytes)) < 0)
1952 return (ACCEPT_DEFECTIVE_TOKEN);
1953 } else {
1954 return (ACCEPT_INCOMPLETE);
1955 }
1956
1957 if (*(*buff_in) == OCTET_STRING)
1958 return (ACCEPT_INCOMPLETE);
1959
1960 if (*(*buff_in)++ != ENUMERATED)
1961 return (ACCEPT_DEFECTIVE_TOKEN);
1962
1963 if (*(*buff_in)++ != ENUMERATION_LENGTH)
1964 return (ACCEPT_DEFECTIVE_TOKEN);
1965
1966 /*
1967 * Save the result byte to return later.
1968 * This is the result
1969 */
1970 result = (OM_uint32)*(*buff_in)++;
1971
1972 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1973 bodysize - (*buff_in - iptr), &len) < 0)
1974 result = ACCEPT_DEFECTIVE_TOKEN;
1975
1976 return (result);
1977 }
1978
1979 /*
1980 * der encode the passed negResults as an ENUMERATED type and
1981 * place it in buf_out, advancing the buffer.
1982 */
1983
1984 static int
1985 put_negResult(uchar_t **buf_out, OM_uint32 negResult, int buflen)
1986 {
1987 if (buflen < 3)
1988 return (-1);
1989 *(*buf_out)++ = ENUMERATED;
1990 *(*buf_out)++ = ENUMERATION_LENGTH;
1991 *(*buf_out)++ = (unsigned char) negResult;
1992 return (0);
1993 }
1994
1995 /*
1996 * This routine compares the recieved mechset to the mechset that
1997 * this server can support. It looks sequentially through the mechset
1998 * and the first one that matches what the server can support is
1999 * chosen as the negotiated mechanism. If one is found, negResult
2000 * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult
2001 * is set to REJECT. Also, for purposes of determining latter behavior,
2002 * the flag, firstMech is used to indicate if the chosen mechanism is the
2003 * first of the mechset or not.
2004 */
2005 static gss_OID
2006 negotiate_mech_type(OM_uint32 *minor_status,
2007 gss_OID_set supported_mechSet,
2008 gss_OID_set mechset,
2009 OM_uint32 *negResult,
2010 bool_t *firstMech)
2011 {
2012 gss_OID returned_mech;
2013 OM_uint32 status;
2014 int present;
2015 int i;
2016
2017 for (i = 0; i < mechset->count; i++) {
2018 gss_test_oid_set_member(minor_status, &mechset->elements[i],
2019 supported_mechSet, &present);
2020 if (present == TRUE) {
2021 *negResult = ACCEPT_COMPLETE;
2022
2023 if (i == 0)
2024 *firstMech = TRUE;
2025 else
2026 *firstMech = FALSE;
2027
2028 status = generic_gss_copy_oid(minor_status,
2029 &mechset->elements[i], &returned_mech);
2030
2031 if (status != GSS_S_COMPLETE) {
2032 *negResult = REJECT;
2033 return (NULL);
2034 }
2035
2036 return (returned_mech);
2037 }
2038 }
2039
2040 *negResult = REJECT;
2041 return (NULL);
2042 }
2043
2044 /*
2045 * the next two routines make a token buffer suitable for
2046 * spnego_gss_display_status. These currently take the string
2047 * in name and place it in the token. Eventually, if
2048 * spnego_gss_display_status returns valid error messages,
2049 * these routines will be changes to return the error string.
2050 */
2051 static spnego_token_t
2052 make_spnego_token(char *name)
2053 {
2054 spnego_token_t token;
2055
2056 token = (spnego_token_t)malloc(strlen(name)+1);
2057
2058 if (token == NULL)
2059 return (NULL);
2060 strcpy(token, name);
2061 return (token);
2062 }
2063
2064 static gss_buffer_desc
2065 make_err_msg(char *name)
2066 {
2067 gss_buffer_desc buffer;
2068
2069 if (name == NULL) {
2070 buffer.length = 0;
2071 buffer.value = NULL;
2072 } else {
2073 buffer.length = strlen(name)+1;
2074 buffer.value = make_spnego_token(name);
2075 }
2076
2077 return (buffer);
2078 }
2079
2080 /*
2081 * Create the client side spnego token passed back to gss_init_sec_context
2082 * and eventually up to the application program and over to the server.
2083 *
2084 * Use DER rules, definite length method per RFC 2478
2085 */
2086 static int
2087 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
2088 gss_OID_set mechSet,
2089 gss_buffer_t data, send_token_flag sendtoken,
2090 gss_buffer_t outbuf)
2091 {
2092 OM_uint32 status, minor_stat;
2093 int tlen, dataLen = 0, ret = 0;
2094 int MechSetLen = 0;
2095 int negTokenInitSize = 0;
2096 int i;
2097 unsigned char *t;
2098 unsigned char *ptr;
2099 unsigned char *MechListPtr = NULL;
2100 gss_buffer_desc MICbuff;
2101
2102 if (outbuf == GSS_C_NO_BUFFER)
2103 return (-1);
2104
2105 outbuf->length = 0;
2106 outbuf->value = NULL;
2107
2108 /* calculate the data length */
2109
2110 /* no token generated if sendtoken is not init or cont */
2111 if ((sendtoken < INIT_TOKEN_SEND) ||
2112 (sendtoken > CONT_TOKEN_SEND)) {
2113 return (-1);
2114 }
2115
2116 /*
2117 * if this is the init token, we will send the mechset
2118 * so include it's length.
2119 */
2120 if (sendtoken == INIT_TOKEN_SEND) {
2121 /*
2122 * Count bytes for the mechSet data
2123 * Encoded in final output as:
2124 * 0xa0 [DER LEN] 0x30 [DER LEN] [DATA]
2125 */
2126 for (i = 0; i < mechSet->count; i++)
2127 MechSetLen += 1 +
2128 der_length_size(mechSet->elements[i].length) +
2129 mechSet->elements[i].length;
2130
2131 MechSetLen += 1 + der_length_size(MechSetLen);
2132 dataLen += 1 + der_length_size(MechSetLen) + MechSetLen;
2133
2134 MechListPtr = (uchar_t *)malloc(dataLen);
2135 ptr = (uchar_t *)MechListPtr;
2136
2137 if (MechListPtr != NULL) {
2138 if ((ret = put_mech_set(&ptr, mechSet, dataLen))) {
2139 free(MechListPtr);
2140 goto errout;
2141 }
2142 } else {
2143 ret = -1;
2144 goto errout;
2145 }
2146
2147 /*
2148 * The MIC is done over the DER encoded mechSet.
2149 */
2150 spnego_ctx->DER_mechTypes.value = MechListPtr;
2151 spnego_ctx->DER_mechTypes.length = ptr - MechListPtr;
2152
2153 /*
2154 * Only send the MIC if we are *NOT* interoperating
2155 * with Microsoft.
2156 */
2157 if (!spnego_ctx->MS_Interop) {
2158 /*
2159 * MechListMIC = DER(MIC(DER(MechSet)))
2160 * Calculate it here, stick it in the buffer later.
2161 */
2162 MICbuff.length = 0;
2163 MICbuff.value = NULL;
2164 status = gss_get_mic(&minor_stat,
2165 spnego_ctx->ctx_handle, GSS_C_QOP_DEFAULT,
2166 &spnego_ctx->DER_mechTypes, &MICbuff);
2167 /*
2168 * If the MIC operation succeeded, use it,
2169 * but don't fail if it did not succeed.
2170 * MIC is optional and is not supported by all
2171 * mechanisms all the time.
2172 */
2173 if (status == GSS_S_COMPLETE) {
2174 /*
2175 * Encoded in final output as:
2176 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
2177 * --s-- -------tlen------------
2178 */
2179 tlen = 1 + der_length_size(MICbuff.length) +
2180 MICbuff.length;
2181
2182 dataLen += 1 + der_length_size(tlen) + tlen;
2183 }
2184 }
2185 }
2186
2187 /*
2188 * If a token from gss_init_sec_context exists,
2189 * add the length of the token + the ASN.1 overhead
2190 */
2191 if (data != NULL) {
2192 /*
2193 * Encoded in final output as:
2194 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
2195 * -----s--------|--------s2----------
2196 */
2197 tlen = 1 + der_length_size(data->length) + data->length;
2198
2199 dataLen += 1 + der_length_size(tlen) + tlen;
2200 }
2201
2202 /*
2203 * Add size of DER encoding
2204 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
2205 * 0x30 [DER_LEN] [data]
2206 *
2207 */
2208 dataLen += 1 + der_length_size(dataLen);
2209
2210 /*
2211 * negTokenInitSize indicates the bytes needed to
2212 * hold the ASN.1 encoding of the entire NegTokenInit
2213 * SEQUENCE.
2214 * 0xa0 [DER_LEN] + data
2215 *
2216 */
2217 negTokenInitSize = dataLen;
2218
2219 tlen = g_token_size((gss_OID)gss_mech_spnego,
2220 negTokenInitSize + 1 +
2221 der_length_size(negTokenInitSize));
2222
2223 t = (unsigned char *) malloc(tlen);
2224
2225 if (t == NULL) {
2226 return (-1);
2227 }
2228
2229 ptr = t;
2230
2231 /* create the message */
2232 if ((ret = g_make_token_header((gss_OID)gss_mech_spnego,
2233 1 + negTokenInitSize + der_length_size(negTokenInitSize),
2234 &ptr, tlen)))
2235 goto errout;
2236
2237 if (sendtoken == INIT_TOKEN_SEND) {
2238 *ptr++ = CONTEXT; /* NegotiationToken identifier */
2239 if ((ret = put_der_length(negTokenInitSize, &ptr, tlen)))
2240 goto errout;
2241
2242 *ptr++ = SEQUENCE;
2243 if ((ret = put_der_length(negTokenInitSize - 4, &ptr,
2244 tlen - (int)(ptr-t))))
2245 goto errout;
2246
2247 *ptr++ = CONTEXT; /* MechTypeList identifier */
2248 if ((ret = put_der_length(spnego_ctx->DER_mechTypes.length,
2249 &ptr, tlen - (int)(ptr-t))))
2250 goto errout;
2251
2252 /* We already encoded the MechSetList */
2253 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
2254 spnego_ctx->DER_mechTypes.length);
2255
2256 ptr += spnego_ctx->DER_mechTypes.length;
2257
2258 }
2259
2260 if (data != NULL) {
2261 *ptr++ = CONTEXT | 0x02;
2262 if ((ret = put_der_length(data->length + 4,
2263 &ptr, tlen - (int)(ptr - t))))
2264 goto errout;
2265
2266 if ((ret = put_input_token(&ptr, data,
2267 tlen - (int)(ptr - t))))
2268 goto errout;
2269
2270 /*
2271 * We are in "optimistic" mode if we send a token
2272 * with out initial message.
2273 */
2274 spnego_ctx->optimistic = (sendtoken == INIT_TOKEN_SEND);
2275 }
2276
2277 if (!spnego_ctx->MS_Interop && MICbuff.length > 0) {
2278 /* We already calculated the MechListMIC above */
2279 int len = 1 + der_length_size(MICbuff.length) + MICbuff.length;
2280 *ptr++ = CONTEXT | 0x03;
2281 if ((ret = put_der_length(len, &ptr, tlen - (int)(ptr - t))))
2282 goto errout;
2283
2284 if ((ret = put_input_token(&ptr, &MICbuff,
2285 tlen - (int)(ptr - t))))
2286 goto errout;
2287
2288 (void) gss_release_buffer(&minor_stat, &MICbuff);
2289 }
2290
2291 errout:
2292 if (ret != 0) {
2293 if (t)
2294 free(t);
2295 t = NULL;
2296 tlen = 0;
2297 }
2298 outbuf->length = tlen;
2299 outbuf->value = (void *) t;
2300
2301 return (ret);
2302 }
2303
2304 /*
2305 * create the server side spnego token passed back to
2306 * gss_accept_sec_context and eventually up to the application program
2307 * and over to the client.
2308 */
2309 static int
2310 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
2311 gss_buffer_t data, gss_buffer_t mechListMIC,
2312 send_token_flag sendtoken, int MS_Flag,
2313 gss_buffer_t outbuf)
2314 {
2315 int tlen;
2316 int ret;
2317 int NegTokenTargSize;
2318 int negresultTokenSize;
2319 int NegTokenSize;
2320 int rspTokenSize;
2321 int micTokenSize;
2322 int dataLen = 0;
2323 unsigned char *t;
2324 unsigned char *ptr;
2325
2326 if (outbuf == GSS_C_NO_BUFFER)
2327 return (GSS_S_DEFECTIVE_TOKEN);
2328
2329 outbuf->length = 0;
2330 outbuf->value = NULL;
2331
2332 /*
2333 * ASN.1 encoding of the negResult
2334 * ENUMERATED type is 3 bytes
2335 * ENUMERATED TAG, Length, Value,
2336 * Plus 2 bytes for the CONTEXT id and length.
2337 */
2338 negresultTokenSize = 5;
2339
2340 /*
2341 * calculate data length
2342 *
2343 * If this is the initial token, include length of
2344 * mech_type and the negotiation result fields.
2345 */
2346 if (sendtoken == INIT_TOKEN_SEND) {
2347
2348 if (mech_wanted != NULL) {
2349 int mechlistTokenSize;
2350 /*
2351 * 1 byte for the CONTEXT ID(0xa0),
2352 * 1 byte for the OID ID(0x06)
2353 * 1 byte for OID Length field
2354 * Plus the rest... (OID Length, OID value)
2355 */
2356 mechlistTokenSize = 3 + mech_wanted->length +
2357 der_length_size(mech_wanted->length);
2358
2359 dataLen = negresultTokenSize + mechlistTokenSize;
2360 }
2361 } else {
2362 /*
2363 * If this is a response from a server, count
2364 * the space needed for the negResult field.
2365 * LENGTH(2) + ENUM(2) + result
2366 */
2367 dataLen = negresultTokenSize;
2368 }
2369 if (data != NULL && data->length > 0) {
2370 /* Length of the inner token */
2371 rspTokenSize = 1 + der_length_size(data->length) +
2372 data->length;
2373
2374 dataLen += rspTokenSize;
2375
2376 /* Length of the outer token */
2377 dataLen += 1 + der_length_size(rspTokenSize);
2378 }
2379 if (mechListMIC != NULL) {
2380
2381 /* Length of the inner token */
2382 micTokenSize = 1 + der_length_size(mechListMIC->length) +
2383 mechListMIC->length;
2384
2385 dataLen += micTokenSize;
2386
2387 /* Length of the outer token */
2388 dataLen += 1 + der_length_size(micTokenSize);
2389 } else if (data != NULL && data->length > 0 && MS_Flag) {
2390 dataLen += rspTokenSize;
2391 dataLen += 1 + der_length_size(rspTokenSize);
2392 }
2393
2394 /*
2395 * Add size of DER encoded:
2396 * NegTokenTarg [ SEQUENCE ] of
2397 * NegResult[0] ENUMERATED {
2398 * accept_completed(0),
2399 * accept_incomplete(1),
2400 * reject(2) }
2401 * supportedMech [1] MechType OPTIONAL,
2402 * responseToken [2] OCTET STRING OPTIONAL,
2403 * mechListMIC [3] OCTET STRING OPTIONAL
2404 *
2405 * size = data->length + MechListMic + SupportedMech len +
2406 * Result Length + ASN.1 overhead
2407 */
2408 NegTokenTargSize = dataLen;
2409 dataLen += 1 + der_length_size(NegTokenTargSize);
2410
2411 /*
2412 * NegotiationToken [ CHOICE ]{
2413 * negTokenInit [0] NegTokenInit,
2414 * negTokenTarg [1] NegTokenTarg }
2415 */
2416 NegTokenSize = dataLen;
2417 dataLen += 1 + der_length_size(NegTokenSize);
2418
2419 tlen = dataLen;
2420 t = (unsigned char *) malloc(tlen);
2421
2422 if (t == NULL) {
2423 ret = GSS_S_DEFECTIVE_TOKEN;
2424 goto errout;
2425 }
2426
2427 ptr = t;
2428
2429 if (sendtoken == INIT_TOKEN_SEND ||
2430 sendtoken == ERROR_TOKEN_SEND) {
2431 /*
2432 * Indicate that we are sending CHOICE 1
2433 * (NegTokenTarg)
2434 */
2435 *ptr++ = CONTEXT | 0x01;
2436 if ((ret = put_der_length(NegTokenSize, &ptr, dataLen))) {
2437 ret = GSS_S_DEFECTIVE_TOKEN;
2438 goto errout;
2439 }
2440
2441 *ptr++ = SEQUENCE;
2442 if ((ret = put_der_length(NegTokenTargSize, &ptr,
2443 tlen - (int)(ptr-t)))) {
2444 ret = GSS_S_DEFECTIVE_TOKEN;
2445 goto errout;
2446 }
2447
2448 /*
2449 * First field of the NegTokenTarg SEQUENCE
2450 * is the ENUMERATED NegResult.
2451 */
2452 *ptr++ = CONTEXT;
2453 if ((ret = put_der_length(3, &ptr,
2454 tlen - (int)(ptr-t)))) {
2455 ret = GSS_S_DEFECTIVE_TOKEN;
2456 goto errout;
2457 }
2458 if ((ret = put_negResult(&ptr, status,
2459 tlen - (int)(ptr - t)))) {
2460 ret = GSS_S_DEFECTIVE_TOKEN;
2461 goto errout;
2462 }
2463
2464 if (sendtoken != ERROR_TOKEN_SEND && mech_wanted != NULL) {
2465 /*
2466 * Next, is the Supported MechType
2467 */
2468 *ptr++ = CONTEXT | 0x01;
2469 if ((ret = put_der_length(mech_wanted->length + 2,
2470 &ptr, tlen - (int)(ptr - t)))) {
2471 ret = GSS_S_DEFECTIVE_TOKEN;
2472 goto errout;
2473 }
2474 if ((ret = put_mech_oid(&ptr, mech_wanted,
2475 tlen - (int)(ptr - t)))) {
2476 ret = GSS_S_DEFECTIVE_TOKEN;
2477 goto errout;
2478 }
2479 }
2480 }
2481
2482 if (data != NULL && data->length > 0) {
2483 *ptr++ = CONTEXT | 0x02;
2484 if ((ret = put_der_length(rspTokenSize, &ptr,
2485 tlen - (int)(ptr - t)))) {
2486 ret = GSS_S_DEFECTIVE_TOKEN;
2487 goto errout;
2488 }
2489 if ((ret = put_input_token(&ptr, data,
2490 tlen - (int)(ptr - t)))) {
2491 ret = GSS_S_DEFECTIVE_TOKEN;
2492 goto errout;
2493 }
2494 }
2495 if (mechListMIC != NULL) {
2496 *ptr++ = CONTEXT | 0x03;
2497 if ((ret = put_der_length(micTokenSize, &ptr,
2498 tlen - (int)(ptr - t)))) {
2499 ret = GSS_S_DEFECTIVE_TOKEN;
2500 goto errout;
2501 }
2502 if ((ret = put_input_token(&ptr, mechListMIC,
2503 tlen - (int)(ptr - t)))) {
2504 ret = GSS_S_DEFECTIVE_TOKEN;
2505 goto errout;
2506 }
2507 } else if (data != NULL && data->length > 0 && MS_Flag) {
2508 *ptr++ = CONTEXT | 0x03;
2509 if ((ret = put_der_length(rspTokenSize, &ptr,
2510 tlen - (int)(ptr - t)))) {
2511 ret = GSS_S_DEFECTIVE_TOKEN;
2512 goto errout;
2513 }
2514 if ((ret = put_input_token(&ptr, data,
2515 tlen - (int)(ptr - t)))) {
2516 ret = GSS_S_DEFECTIVE_TOKEN;
2517 }
2518 }
2519 errout:
2520 if (ret != 0) {
2521 if (t)
2522 free(t);
2523 } else {
2524 outbuf->length = ptr - t;
2525 outbuf->value = (void *) t;
2526 }
2527
2528 return (ret);
2529 }
2530
2531 /* determine size of token */
2532 static int
2533 g_token_size(gss_OID mech, unsigned int body_size)
2534 {
2535 int hdrsize;
2536
2537 /*
2538 * Initialize the header size to the
2539 * MECH_OID byte + the bytes needed to indicate the
2540 * length of the OID + the OID itself.
2541 *
2542 * 0x06 [MECHLENFIELD] MECHDATA
2543 */
2544 hdrsize = 1 + der_length_size(mech->length) + mech->length;
2545
2546 /*
2547 * Now add the bytes needed for the initial header
2548 * token bytes:
2549 * 0x60 + [DER_LEN] + HDRSIZE
2550 */
2551 hdrsize += 1 + der_length_size(body_size + hdrsize);
2552
2553 return (hdrsize + body_size);
2554 }
2555
2556 /*
2557 * generate token header.
2558 *
2559 * Use DER Definite Length method per RFC2478
2560 * Use of indefinite length encoding will not be compatible
2561 * with Microsoft or others that actually follow the spec.
2562 */
2563 static int
2564 g_make_token_header(gss_OID mech,
2565 int body_size,
2566 unsigned char **buf,
2567 int totallen)
2568 {
2569 int hdrsize, ret = 0;
2570 unsigned char *p = *buf;
2571
2572 hdrsize = 1 + der_length_size(mech->length) + mech->length;
2573
2574 *(*buf)++ = HEADER_ID;
2575 if ((ret = put_der_length(hdrsize + body_size, buf, totallen)))
2576 return (ret);
2577
2578 *(*buf)++ = MECH_OID;
2579 if ((ret = put_der_length(mech->length, buf,
2580 totallen - (int)(p - *buf))))
2581 return (ret);
2582 TWRITE_STR(*buf, mech->elements, ((int)mech->length));
2583 return (0);
2584 }
2585
2586 static int
2587 g_get_tag_and_length(unsigned char **buf, uchar_t tag, int buflen, int *outlen)
2588 {
2589 unsigned char *ptr = *buf;
2590 int ret = -1; /* pessimists, assume failure ! */
2591 OM_uint32 encoded_len;
2592
2593 if (buflen > 0 && *ptr == tag) {
2594 ptr++;
2595 *outlen = get_der_length(&ptr, buflen, &encoded_len);
2596 if (*outlen < 0)
2597 ret = *outlen;
2598 if ((ptr + *outlen) > (*buf + buflen))
2599 ret = -1;
2600 else
2601 ret = 0;
2602 }
2603
2604 *buf = ptr;
2605 return (ret);
2606 }
2607
2608 static int
2609 g_verify_neg_token_init(unsigned char **buf_in, int cur_size)
2610 {
2611 unsigned char *buf = *buf_in;
2612 unsigned char *endptr = buf + cur_size;
2613 int seqsize;
2614 int ret = 0;
2615 unsigned int bytes;
2616
2617 /*
2618 * Verify this is a NegotiationToken type token
2619 * - check for a0(context specific identifier)
2620 * - get length and verify that enoughd ata exists
2621 */
2622 if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
2623 return (G_BAD_TOK_HEADER);
2624
2625 cur_size = seqsize; /* should indicate bytes remaining */
2626
2627 /*
2628 * Verify the next piece, it should identify this as
2629 * a strucure of type NegTokenInit.
2630 */
2631 if (*buf++ == SEQUENCE) {
2632 if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
2633 return (G_BAD_TOK_HEADER);
2634 /*
2635 * Make sure we have the entire buffer as described
2636 */
2637 if (buf + seqsize > endptr)
2638 return (G_BAD_TOK_HEADER);
2639 } else {
2640 return (G_BAD_TOK_HEADER);
2641 }
2642
2643 cur_size = seqsize; /* should indicate bytes remaining */
2644
2645 /*
2646 * Verify that the first blob is a sequence of mechTypes
2647 */
2648 if (*buf++ == CONTEXT) {
2649 if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
2650 return (G_BAD_TOK_HEADER);
2651 /*
2652 * Make sure we have the entire buffer as described
2653 */
2654 if (buf + bytes > endptr)
2655 return (G_BAD_TOK_HEADER);
2656 } else {
2657 return (G_BAD_TOK_HEADER);
2658 }
2659
2660 /*
2661 * At this point, *buf should be at the beginning of the
2662 * DER encoded list of mech types that are to be negotiated.
2663 */
2664 *buf_in = buf;
2665
2666 return (ret);
2667
2668 }
2669
2670 /* verify token header. */
2671 static int
2672 g_verify_token_header(gss_OID mech,
2673 int *body_size,
2674 unsigned char **buf_in,
2675 int tok_type,
2676 int toksize)
2677 {
2678 unsigned char *buf = *buf_in;
2679 int seqsize;
2680 gss_OID_desc toid;
2681 int ret = 0;
2682 unsigned int bytes;
2683
2684 if ((toksize -= 1) < 0)
2685 return (G_BAD_TOK_HEADER);
2686
2687 if (*buf++ != HEADER_ID)
2688 return (G_BAD_TOK_HEADER);
2689
2690 if ((seqsize = get_der_length(&buf, toksize, &bytes)) < 0)
2691 return (G_BAD_TOK_HEADER);
2692
2693 if ((seqsize + bytes) != toksize)
2694 return (G_BAD_TOK_HEADER);
2695
2696 if ((toksize -= 1) < 0)
2697 return (G_BAD_TOK_HEADER);
2698
2699
2700 if (*buf++ != MECH_OID)
2701 return (G_BAD_TOK_HEADER);
2702
2703 if ((toksize -= 1) < 0)
2704 return (G_BAD_TOK_HEADER);
2705
2706 toid.length = *buf++;
2707
2708 if ((toksize -= toid.length) < 0)
2709 return (G_BAD_TOK_HEADER);
2710
2711 toid.elements = buf;
2712 buf += toid.length;
2713
2714 if (!g_OID_equal(&toid, mech))
2715 ret = G_WRONG_MECH;
2716
2717 /*
2718 * G_WRONG_MECH is not returned immediately because it's more important
2719 * to return G_BAD_TOK_HEADER if the token header is in fact bad
2720 */
2721 if ((toksize -= 2) < 0)
2722 return (G_BAD_TOK_HEADER);
2723
2724 if (!ret) {
2725 *buf_in = buf;
2726 *body_size = toksize;
2727 }
2728
2729 return (ret);
2730 }