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