/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- send_reject
- acceptor_approved
- send_supported_mechs
- send_accept
- select_mech
- acceptor_complete
1 /*
2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * Portions Copyright (c) 2004 PADL Software Pty Ltd.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include "spnego/spnego_locl.h"
35
36 RCSID("$Id$");
37
38 static OM_uint32
39 send_reject (OM_uint32 *minor_status,
/* [<][>][^][v][top][bottom][index][help] */
40 gss_buffer_t output_token)
41 {
42 NegotiationToken nt;
43 size_t size;
44
45 nt.element = choice_NegotiationToken_negTokenResp;
46
47 ALLOC(nt.u.negTokenResp.negResult, 1);
48 if (nt.u.negTokenResp.negResult == NULL) {
49 *minor_status = ENOMEM;
50 return GSS_S_FAILURE;
51 }
52 *(nt.u.negTokenResp.negResult) = reject;
53 nt.u.negTokenResp.supportedMech = NULL;
54 nt.u.negTokenResp.responseToken = NULL;
55 nt.u.negTokenResp.mechListMIC = NULL;
56
57 ASN1_MALLOC_ENCODE(NegotiationToken,
58 output_token->value, output_token->length, &nt,
59 &size, *minor_status);
60 free_NegotiationToken(&nt);
61 if (*minor_status != 0)
62 return GSS_S_FAILURE;
63
64 return GSS_S_BAD_MECH;
65 }
66
67 static OM_uint32
68 acceptor_approved(gss_name_t target_name, gss_OID mech)
/* [<][>][^][v][top][bottom][index][help] */
69 {
70 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
71 gss_OID_set oidset;
72 OM_uint32 junk, ret;
73
74 if (target_name == GSS_C_NO_NAME)
75 return GSS_S_COMPLETE;
76
77 gss_create_empty_oid_set(&junk, &oidset);
78 gss_add_oid_set_member(&junk, mech, &oidset);
79
80 ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset,
81 GSS_C_ACCEPT, &cred, NULL, NULL);
82 gss_release_oid_set(&junk, &oidset);
83 if (ret != GSS_S_COMPLETE)
84 return ret;
85 gss_release_cred(&junk, &cred);
86
87 return GSS_S_COMPLETE;
88 }
89
90 static OM_uint32
91 send_supported_mechs (OM_uint32 *minor_status,
/* [<][>][^][v][top][bottom][index][help] */
92 gss_buffer_t output_token)
93 {
94 NegotiationTokenWin nt;
95 char hostname[MAXHOSTNAMELEN + 1], *p;
96 gss_buffer_desc name_buf;
97 gss_OID name_type;
98 gss_name_t target_princ;
99 gss_name_t canon_princ;
100 OM_uint32 minor;
101 size_t buf_len;
102 gss_buffer_desc data;
103 OM_uint32 ret;
104
105 memset(&nt, 0, sizeof(nt));
106
107 nt.element = choice_NegotiationTokenWin_negTokenInit;
108 nt.u.negTokenInit.reqFlags = NULL;
109 nt.u.negTokenInit.mechToken = NULL;
110 nt.u.negTokenInit.negHints = NULL;
111
112 ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME,
113 acceptor_approved, 1, NULL,
114 &nt.u.negTokenInit.mechTypes, NULL);
115 if (ret != GSS_S_COMPLETE) {
116 return ret;
117 }
118
119 memset(&target_princ, 0, sizeof(target_princ));
120 if (gethostname(hostname, sizeof(hostname) - 2) != 0) {
121 *minor_status = errno;
122 free_NegotiationTokenWin(&nt);
123 return GSS_S_FAILURE;
124 }
125 hostname[sizeof(hostname) - 1] = '\0';
126
127 /* Send the constructed SAM name for this host */
128 for (p = hostname; *p != '\0' && *p != '.'; p++) {
129 *p = toupper((unsigned char)*p);
130 }
131 *p++ = '$';
132 *p = '\0';
133
134 name_buf.length = strlen(hostname);
135 name_buf.value = hostname;
136
137 ret = gss_import_name(minor_status, &name_buf,
138 GSS_C_NO_OID,
139 &target_princ);
140 if (ret != GSS_S_COMPLETE) {
141 free_NegotiationTokenWin(&nt);
142 return ret;
143 }
144
145 name_buf.length = 0;
146 name_buf.value = NULL;
147
148 /* Canonicalize the name using the preferred mechanism */
149 ret = gss_canonicalize_name(minor_status,
150 target_princ,
151 GSS_C_NO_OID,
152 &canon_princ);
153 if (ret != GSS_S_COMPLETE) {
154 free_NegotiationTokenWin(&nt);
155 gss_release_name(&minor, &target_princ);
156 return ret;
157 }
158
159 ret = gss_display_name(minor_status, canon_princ,
160 &name_buf, &name_type);
161 if (ret != GSS_S_COMPLETE) {
162 free_NegotiationTokenWin(&nt);
163 gss_release_name(&minor, &canon_princ);
164 gss_release_name(&minor, &target_princ);
165 return ret;
166 }
167
168 gss_release_name(&minor, &canon_princ);
169 gss_release_name(&minor, &target_princ);
170
171 ALLOC(nt.u.negTokenInit.negHints, 1);
172 if (nt.u.negTokenInit.negHints == NULL) {
173 *minor_status = ENOMEM;
174 gss_release_buffer(&minor, &name_buf);
175 free_NegotiationTokenWin(&nt);
176 return GSS_S_FAILURE;
177 }
178
179 ALLOC(nt.u.negTokenInit.negHints->hintName, 1);
180 if (nt.u.negTokenInit.negHints->hintName == NULL) {
181 *minor_status = ENOMEM;
182 gss_release_buffer(&minor, &name_buf);
183 free_NegotiationTokenWin(&nt);
184 return GSS_S_FAILURE;
185 }
186
187 *(nt.u.negTokenInit.negHints->hintName) = name_buf.value;
188 name_buf.value = NULL;
189 nt.u.negTokenInit.negHints->hintAddress = NULL;
190
191 ASN1_MALLOC_ENCODE(NegotiationTokenWin,
192 data.value, data.length, &nt, &buf_len, ret);
193 free_NegotiationTokenWin(&nt);
194 if (ret) {
195 return ret;
196 }
197 if (data.length != buf_len)
198 abort();
199
200 ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token);
201
202 free (data.value);
203
204 if (ret != GSS_S_COMPLETE)
205 return ret;
206
207 *minor_status = 0;
208
209 return GSS_S_CONTINUE_NEEDED;
210 }
211
212 static OM_uint32
213 send_accept (OM_uint32 *minor_status,
/* [<][>][^][v][top][bottom][index][help] */
214 gssspnego_ctx context_handle,
215 gss_buffer_t mech_token,
216 int initial_response,
217 gss_buffer_t mech_buf,
218 gss_buffer_t output_token)
219 {
220 NegotiationToken nt;
221 OM_uint32 ret;
222 gss_buffer_desc mech_mic_buf;
223 size_t size;
224
225 memset(&nt, 0, sizeof(nt));
226
227 nt.element = choice_NegotiationToken_negTokenResp;
228
229 ALLOC(nt.u.negTokenResp.negResult, 1);
230 if (nt.u.negTokenResp.negResult == NULL) {
231 *minor_status = ENOMEM;
232 return GSS_S_FAILURE;
233 }
234
235 if (context_handle->open) {
236 if (mech_token != GSS_C_NO_BUFFER
237 && mech_token->length != 0
238 && mech_buf != GSS_C_NO_BUFFER)
239 *(nt.u.negTokenResp.negResult) = accept_incomplete;
240 else
241 *(nt.u.negTokenResp.negResult) = accept_completed;
242 } else {
243 if (initial_response && context_handle->require_mic)
244 *(nt.u.negTokenResp.negResult) = request_mic;
245 else
246 *(nt.u.negTokenResp.negResult) = accept_incomplete;
247 }
248
249 if (initial_response) {
250 ALLOC(nt.u.negTokenResp.supportedMech, 1);
251 if (nt.u.negTokenResp.supportedMech == NULL) {
252 free_NegotiationToken(&nt);
253 *minor_status = ENOMEM;
254 return GSS_S_FAILURE;
255 }
256
257 ret = der_get_oid(context_handle->preferred_mech_type->elements,
258 context_handle->preferred_mech_type->length,
259 nt.u.negTokenResp.supportedMech,
260 NULL);
261 if (ret) {
262 free_NegotiationToken(&nt);
263 *minor_status = ENOMEM;
264 return GSS_S_FAILURE;
265 }
266 } else {
267 nt.u.negTokenResp.supportedMech = NULL;
268 }
269
270 if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
271 ALLOC(nt.u.negTokenResp.responseToken, 1);
272 if (nt.u.negTokenResp.responseToken == NULL) {
273 free_NegotiationToken(&nt);
274 *minor_status = ENOMEM;
275 return GSS_S_FAILURE;
276 }
277 nt.u.negTokenResp.responseToken->length = mech_token->length;
278 nt.u.negTokenResp.responseToken->data = mech_token->value;
279 mech_token->length = 0;
280 mech_token->value = NULL;
281 } else {
282 nt.u.negTokenResp.responseToken = NULL;
283 }
284
285 if (mech_buf != GSS_C_NO_BUFFER) {
286 ret = gss_get_mic(minor_status,
287 context_handle->negotiated_ctx_id,
288 0,
289 mech_buf,
290 &mech_mic_buf);
291 if (ret == GSS_S_COMPLETE) {
292 ALLOC(nt.u.negTokenResp.mechListMIC, 1);
293 if (nt.u.negTokenResp.mechListMIC == NULL) {
294 gss_release_buffer(minor_status, &mech_mic_buf);
295 free_NegotiationToken(&nt);
296 *minor_status = ENOMEM;
297 return GSS_S_FAILURE;
298 }
299 nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length;
300 nt.u.negTokenResp.mechListMIC->data = mech_mic_buf.value;
301 } else if (ret == GSS_S_UNAVAILABLE) {
302 nt.u.negTokenResp.mechListMIC = NULL;
303 } else {
304 free_NegotiationToken(&nt);
305 return ret;
306 }
307
308 } else
309 nt.u.negTokenResp.mechListMIC = NULL;
310
311 ASN1_MALLOC_ENCODE(NegotiationToken,
312 output_token->value, output_token->length,
313 &nt, &size, ret);
314 if (ret) {
315 free_NegotiationToken(&nt);
316 *minor_status = ret;
317 return GSS_S_FAILURE;
318 }
319
320 /*
321 * The response should not be encapsulated, because
322 * it is a SubsequentContextToken (note though RFC 1964
323 * specifies encapsulation for all _Kerberos_ tokens).
324 */
325
326 if (*(nt.u.negTokenResp.negResult) == accept_completed)
327 ret = GSS_S_COMPLETE;
328 else
329 ret = GSS_S_CONTINUE_NEEDED;
330 free_NegotiationToken(&nt);
331 return ret;
332 }
333
334
335 static OM_uint32
336 verify_mechlist_mic
337 (OM_uint32 *minor_status,
338 gssspnego_ctx context_handle,
339 gss_buffer_t mech_buf,
340 heim_octet_string *mechListMIC
341 )
342 {
343 OM_uint32 ret;
344 gss_buffer_desc mic_buf;
345
346 if (context_handle->verified_mic) {
347 /* This doesn't make sense, we've already verified it? */
348 *minor_status = 0;
349 return GSS_S_DUPLICATE_TOKEN;
350 }
351
352 if (mechListMIC == NULL) {
353 *minor_status = 0;
354 return GSS_S_DEFECTIVE_TOKEN;
355 }
356
357 mic_buf.length = mechListMIC->length;
358 mic_buf.value = mechListMIC->data;
359
360 ret = gss_verify_mic(minor_status,
361 context_handle->negotiated_ctx_id,
362 mech_buf,
363 &mic_buf,
364 NULL);
365
366 if (ret != GSS_S_COMPLETE)
367 ret = GSS_S_DEFECTIVE_TOKEN;
368
369 return ret;
370 }
371
372 static OM_uint32
373 select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p,
/* [<][>][^][v][top][bottom][index][help] */
374 gss_OID *mech_p)
375 {
376 char mechbuf[64];
377 size_t mech_len;
378 gss_OID_desc oid;
379 gss_OID oidp;
380 gss_OID_set mechs;
381 int i;
382 OM_uint32 ret, junk;
383
384 ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
385 sizeof(mechbuf),
386 mechType,
387 &mech_len);
388 if (ret) {
389 return GSS_S_DEFECTIVE_TOKEN;
390 }
391
392 oid.length = mech_len;
393 oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
394
395 if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) {
396 return GSS_S_BAD_MECH;
397 }
398
399 *minor_status = 0;
400
401 /* Translate broken MS Kebreros OID */
402 if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
403 oidp = &_gss_spnego_krb5_mechanism_oid_desc;
404 else
405 oidp = &oid;
406
407
408 ret = gss_indicate_mechs(&junk, &mechs);
409 if (ret)
410 return (ret);
411
412 for (i = 0; i < mechs->count; i++)
413 if (gss_oid_equal(&mechs->elements[i], oidp))
414 break;
415
416 if (i == mechs->count) {
417 gss_release_oid_set(&junk, &mechs);
418 return GSS_S_BAD_MECH;
419 }
420 gss_release_oid_set(&junk, &mechs);
421
422 ret = gss_duplicate_oid(minor_status,
423 &oid, /* possibly this should be oidp */
424 mech_p);
425
426 if (verify_p) {
427 gss_name_t name = GSS_C_NO_NAME;
428 gss_buffer_desc namebuf;
429 char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
430
431 host = getenv("GSSAPI_SPNEGO_NAME");
432 if (host == NULL || issuid()) {
433 if (gethostname(hostname, sizeof(hostname)) != 0) {
434 *minor_status = errno;
435 return GSS_S_FAILURE;
436 }
437 asprintf(&str, "host@%s", hostname);
438 host = str;
439 }
440
441 namebuf.length = strlen(host);
442 namebuf.value = host;
443
444 ret = gss_import_name(minor_status, &namebuf,
445 GSS_C_NT_HOSTBASED_SERVICE, &name);
446 if (str)
447 free(str);
448 if (ret != GSS_S_COMPLETE)
449 return ret;
450
451 ret = acceptor_approved(name, *mech_p);
452 gss_release_name(&junk, &name);
453 }
454
455 return ret;
456 }
457
458
459 static OM_uint32
460 acceptor_complete(OM_uint32 * minor_status,
/* [<][>][^][v][top][bottom][index][help] */
461 gssspnego_ctx ctx,
462 int *get_mic,
463 gss_buffer_t mech_buf,
464 gss_buffer_t mech_input_token,
465 gss_buffer_t mech_output_token,
466 heim_octet_string *mic,
467 gss_buffer_t output_token)
468 {
469 OM_uint32 ret;
470 int require_mic, verify_mic;
471 gss_buffer_desc buf;
472
473 buf.length = 0;
474 buf.value = NULL;
475
476 ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
477 if (ret)
478 return ret;
479
480 ctx->require_mic = require_mic;
481
482 if (mic != NULL)
483 require_mic = 1;
484
485 if (ctx->open && require_mic) {
486 if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
487 verify_mic = 1;
488 *get_mic = 0;
489 } else if (mech_output_token != GSS_C_NO_BUFFER &&
490 mech_output_token->length == 0) { /* Odd */
491 *get_mic = verify_mic = 1;
492 } else { /* Even/One */
493 verify_mic = 0;
494 *get_mic = 1;
495 }
496
497 if (verify_mic || get_mic) {
498 int eret;
499 size_t buf_len;
500
501 ASN1_MALLOC_ENCODE(MechTypeList,
502 mech_buf->value, mech_buf->length,
503 &ctx->initiator_mech_types, &buf_len, eret);
504 if (eret) {
505 *minor_status = eret;
506 return GSS_S_FAILURE;
507 }
508 if (buf.length != buf_len)
509 abort();
510 }
511
512 if (verify_mic) {
513 ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic);
514 if (ret) {
515 if (get_mic)
516 send_reject (minor_status, output_token);
517 if (buf.value)
518 free(buf.value);
519 return ret;
520 }
521 ctx->verified_mic = 1;
522 }
523 if (buf.value)
524 free(buf.value);
525
526 } else
527 *get_mic = 0;
528
529 return GSS_S_COMPLETE;
530 }
531
532
533 static OM_uint32
534 acceptor_start
535 (OM_uint32 * minor_status,
536 gss_ctx_id_t * context_handle,
537 const gss_cred_id_t acceptor_cred_handle,
538 const gss_buffer_t input_token_buffer,
539 const gss_channel_bindings_t input_chan_bindings,
540 gss_name_t * src_name,
541 gss_OID * mech_type,
542 gss_buffer_t output_token,
543 OM_uint32 * ret_flags,
544 OM_uint32 * time_rec,
545 gss_cred_id_t *delegated_cred_handle
546 )
547 {
548 OM_uint32 ret, junk;
549 NegotiationToken nt;
550 size_t nt_len;
551 NegTokenInit *ni;
552 int i;
553 gss_buffer_desc data;
554 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
555 gss_buffer_desc mech_output_token;
556 gss_buffer_desc mech_buf;
557 gss_OID preferred_mech_type = GSS_C_NO_OID;
558 gssspnego_ctx ctx;
559 gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
560 int get_mic = 0;
561 int first_ok = 0;
562
563 mech_output_token.value = NULL;
564 mech_output_token.length = 0;
565 mech_buf.value = NULL;
566
567 if (input_token_buffer->length == 0)
568 return send_supported_mechs (minor_status, output_token);
569
570 ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
571 if (ret != GSS_S_COMPLETE)
572 return ret;
573
574 ctx = (gssspnego_ctx)*context_handle;
575
576 /*
577 * The GSS-API encapsulation is only present on the initial
578 * context token (negTokenInit).
579 */
580 ret = gss_decapsulate_token (input_token_buffer,
581 GSS_SPNEGO_MECHANISM,
582 &data);
583 if (ret)
584 return ret;
585
586 ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len);
587 gss_release_buffer(minor_status, &data);
588 if (ret) {
589 *minor_status = ret;
590 return GSS_S_DEFECTIVE_TOKEN;
591 }
592 if (nt.element != choice_NegotiationToken_negTokenInit) {
593 *minor_status = 0;
594 return GSS_S_DEFECTIVE_TOKEN;
595 }
596 ni = &nt.u.negTokenInit;
597
598 if (ni->mechTypes.len < 1) {
599 free_NegotiationToken(&nt);
600 *minor_status = 0;
601 return GSS_S_DEFECTIVE_TOKEN;
602 }
603
604 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
605
606 ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types);
607 if (ret) {
608 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
609 free_NegotiationToken(&nt);
610 *minor_status = ret;
611 return GSS_S_FAILURE;
612 }
613
614 /*
615 * First we try the opportunistic token if we have support for it,
616 * don't try to verify we have credential for the token,
617 * gss_accept_sec_context() will (hopefully) tell us that.
618 * If that failes,
619 */
620
621 ret = select_mech(minor_status,
622 &ni->mechTypes.val[0],
623 0,
624 &preferred_mech_type);
625
626 if (ret == 0 && ni->mechToken != NULL) {
627 gss_cred_id_t mech_delegated_cred = GSS_C_NO_CREDENTIAL;
628 gss_cred_id_t mech_cred;
629 gss_buffer_desc ibuf;
630
631 ibuf.length = ni->mechToken->length;
632 ibuf.value = ni->mechToken->data;
633 mech_input_token = &ibuf;
634
635 if (acceptor_cred != NULL)
636 mech_cred = acceptor_cred->negotiated_cred_id;
637 else
638 mech_cred = GSS_C_NO_CREDENTIAL;
639
640 if (ctx->mech_src_name != GSS_C_NO_NAME)
641 gss_release_name(&junk, &ctx->mech_src_name);
642
643 ret = gss_accept_sec_context(minor_status,
644 &ctx->negotiated_ctx_id,
645 mech_cred,
646 mech_input_token,
647 input_chan_bindings,
648 &ctx->mech_src_name,
649 &ctx->negotiated_mech_type,
650 &mech_output_token,
651 &ctx->mech_flags,
652 &ctx->mech_time_rec,
653 &mech_delegated_cred);
654
655 if (mech_delegated_cred && delegated_cred_handle) {
656 _gss_spnego_alloc_cred(&junk,
657 mech_delegated_cred,
658 delegated_cred_handle);
659 } else if (mech_delegated_cred != GSS_C_NO_CREDENTIAL)
660 gss_release_cred(&junk, &mech_delegated_cred);
661
662 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
663 ctx->preferred_mech_type = preferred_mech_type;
664 ctx->negotiated_mech_type = preferred_mech_type;
665 if (ret == GSS_S_COMPLETE)
666 ctx->open = 1;
667
668 ret = acceptor_complete(minor_status,
669 ctx,
670 &get_mic,
671 &mech_buf,
672 mech_input_token,
673 &mech_output_token,
674 ni->mechListMIC,
675 output_token);
676 if (ret != GSS_S_COMPLETE)
677 goto out;
678
679 first_ok = 1;
680 } else {
681 gss_mg_collect_error(preferred_mech_type, ret, *minor_status);
682 }
683 }
684
685 /*
686 * If opportunistic token failed, lets try the other mechs.
687 */
688
689 if (!first_ok && ni->mechToken != NULL) {
690
691 preferred_mech_type = GSS_C_NO_OID;
692
693 /* Call glue layer to find first mech we support */
694 for (i = 1; i < ni->mechTypes.len; ++i) {
695 ret = select_mech(minor_status,
696 &ni->mechTypes.val[i],
697 1,
698 &preferred_mech_type);
699 if (ret == 0)
700 break;
701 }
702 if (preferred_mech_type == GSS_C_NO_OID) {
703 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
704 free_NegotiationToken(&nt);
705 return ret;
706 }
707
708 ctx->preferred_mech_type = preferred_mech_type;
709 ctx->negotiated_mech_type = preferred_mech_type;
710 }
711
712 /*
713 * The initial token always have a response
714 */
715
716 ret = send_accept (minor_status,
717 ctx,
718 &mech_output_token,
719 1,
720 get_mic ? &mech_buf : NULL,
721 output_token);
722 if (ret)
723 goto out;
724
725 out:
726 if (mech_output_token.value != NULL)
727 gss_release_buffer(&junk, &mech_output_token);
728 if (mech_buf.value != NULL) {
729 free(mech_buf.value);
730 mech_buf.value = NULL;
731 }
732 free_NegotiationToken(&nt);
733
734
735 if (ret == GSS_S_COMPLETE) {
736 if (src_name != NULL && ctx->mech_src_name != NULL) {
737 spnego_name name;
738
739 name = calloc(1, sizeof(*name));
740 if (name) {
741 name->mech = ctx->mech_src_name;
742 ctx->mech_src_name = NULL;
743 *src_name = (gss_name_t)name;
744 }
745 }
746 }
747
748 if (mech_type != NULL)
749 *mech_type = ctx->negotiated_mech_type;
750 if (ret_flags != NULL)
751 *ret_flags = ctx->mech_flags;
752 if (time_rec != NULL)
753 *time_rec = ctx->mech_time_rec;
754
755 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
756 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
757 return ret;
758 }
759
760 _gss_spnego_internal_delete_sec_context(&junk, context_handle,
761 GSS_C_NO_BUFFER);
762
763 return ret;
764 }
765
766
767 static OM_uint32
768 acceptor_continue
769 (OM_uint32 * minor_status,
770 gss_ctx_id_t * context_handle,
771 const gss_cred_id_t acceptor_cred_handle,
772 const gss_buffer_t input_token_buffer,
773 const gss_channel_bindings_t input_chan_bindings,
774 gss_name_t * src_name,
775 gss_OID * mech_type,
776 gss_buffer_t output_token,
777 OM_uint32 * ret_flags,
778 OM_uint32 * time_rec,
779 gss_cred_id_t *delegated_cred_handle
780 )
781 {
782 OM_uint32 ret, ret2, minor, junk;
783 NegotiationToken nt;
784 size_t nt_len;
785 NegTokenResp *na;
786 unsigned int negResult = accept_incomplete;
787 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
788 gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
789 gss_buffer_desc mech_buf;
790 gssspnego_ctx ctx;
791 gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
792
793 mech_buf.value = NULL;
794
795 ctx = (gssspnego_ctx)*context_handle;
796
797 /*
798 * The GSS-API encapsulation is only present on the initial
799 * context token (negTokenInit).
800 */
801
802 ret = decode_NegotiationToken(input_token_buffer->value,
803 input_token_buffer->length,
804 &nt, &nt_len);
805 if (ret) {
806 *minor_status = ret;
807 return GSS_S_DEFECTIVE_TOKEN;
808 }
809 if (nt.element != choice_NegotiationToken_negTokenResp) {
810 *minor_status = 0;
811 return GSS_S_DEFECTIVE_TOKEN;
812 }
813 na = &nt.u.negTokenResp;
814
815 if (na->negResult != NULL) {
816 negResult = *(na->negResult);
817 }
818
819 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
820
821 {
822 gss_buffer_desc ibuf, obuf;
823 int require_mic, get_mic = 0;
824 int require_response;
825 heim_octet_string *mic;
826
827 if (na->responseToken != NULL) {
828 ibuf.length = na->responseToken->length;
829 ibuf.value = na->responseToken->data;
830 mech_input_token = &ibuf;
831 } else {
832 ibuf.value = NULL;
833 ibuf.length = 0;
834 }
835
836 if (mech_input_token != GSS_C_NO_BUFFER) {
837 gss_cred_id_t mech_cred;
838 gss_cred_id_t mech_delegated_cred = GSS_C_NO_CREDENTIAL;
839
840 if (acceptor_cred != NULL)
841 mech_cred = acceptor_cred->negotiated_cred_id;
842 else
843 mech_cred = GSS_C_NO_CREDENTIAL;
844
845 if (ctx->mech_src_name != GSS_C_NO_NAME)
846 gss_release_name(&minor, &ctx->mech_src_name);
847
848 ret = gss_accept_sec_context(&minor,
849 &ctx->negotiated_ctx_id,
850 mech_cred,
851 mech_input_token,
852 input_chan_bindings,
853 &ctx->mech_src_name,
854 &ctx->negotiated_mech_type,
855 &obuf,
856 &ctx->mech_flags,
857 &ctx->mech_time_rec,
858 &mech_delegated_cred);
859
860 if (mech_delegated_cred && delegated_cred_handle) {
861 _gss_spnego_alloc_cred(&junk,
862 mech_delegated_cred,
863 delegated_cred_handle);
864 } else if (mech_delegated_cred != GSS_C_NO_CREDENTIAL)
865 gss_release_cred(&junk, &mech_delegated_cred);
866
867 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
868 mech_output_token = &obuf;
869 }
870 if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
871 free_NegotiationToken(&nt);
872 gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor);
873 send_reject (minor_status, output_token);
874 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
875 return ret;
876 }
877 if (ret == GSS_S_COMPLETE)
878 ctx->open = 1;
879 } else
880 ret = GSS_S_COMPLETE;
881
882 ret2 = _gss_spnego_require_mechlist_mic(minor_status,
883 ctx,
884 &require_mic);
885 if (ret2)
886 goto out;
887
888 ctx->require_mic = require_mic;
889
890 mic = na->mechListMIC;
891 if (mic != NULL)
892 require_mic = 1;
893
894 if (ret == GSS_S_COMPLETE)
895 ret = acceptor_complete(minor_status,
896 ctx,
897 &get_mic,
898 &mech_buf,
899 mech_input_token,
900 mech_output_token,
901 na->mechListMIC,
902 output_token);
903
904 if (ctx->mech_flags & GSS_C_DCE_STYLE)
905 require_response = (negResult != accept_completed);
906 else
907 require_response = 0;
908
909 /*
910 * Check whether we need to send a result: there should be only
911 * one accept_completed response sent in the entire negotiation
912 */
913 if ((mech_output_token != GSS_C_NO_BUFFER &&
914 mech_output_token->length != 0)
915 || (ctx->open && negResult == accept_incomplete)
916 || require_response
917 || get_mic) {
918 ret2 = send_accept (minor_status,
919 ctx,
920 mech_output_token,
921 0,
922 get_mic ? &mech_buf : NULL,
923 output_token);
924 if (ret2)
925 goto out;
926 }
927
928 out:
929 if (ret2 != GSS_S_COMPLETE)
930 ret = ret2;
931 if (mech_output_token != NULL)
932 gss_release_buffer(&minor, mech_output_token);
933 if (mech_buf.value != NULL)
934 free(mech_buf.value);
935 free_NegotiationToken(&nt);
936 }
937
938 if (ret == GSS_S_COMPLETE) {
939 if (src_name != NULL && ctx->mech_src_name != NULL) {
940 spnego_name name;
941
942 name = calloc(1, sizeof(*name));
943 if (name) {
944 name->mech = ctx->mech_src_name;
945 ctx->mech_src_name = NULL;
946 *src_name = (gss_name_t)name;
947 }
948 }
949 }
950
951 if (mech_type != NULL)
952 *mech_type = ctx->negotiated_mech_type;
953 if (ret_flags != NULL)
954 *ret_flags = ctx->mech_flags;
955 if (time_rec != NULL)
956 *time_rec = ctx->mech_time_rec;
957
958 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
959 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
960 return ret;
961 }
962
963 _gss_spnego_internal_delete_sec_context(&minor, context_handle,
964 GSS_C_NO_BUFFER);
965
966 return ret;
967 }
968
969 OM_uint32
970 _gss_spnego_accept_sec_context
971 (OM_uint32 * minor_status,
972 gss_ctx_id_t * context_handle,
973 const gss_cred_id_t acceptor_cred_handle,
974 const gss_buffer_t input_token_buffer,
975 const gss_channel_bindings_t input_chan_bindings,
976 gss_name_t * src_name,
977 gss_OID * mech_type,
978 gss_buffer_t output_token,
979 OM_uint32 * ret_flags,
980 OM_uint32 * time_rec,
981 gss_cred_id_t *delegated_cred_handle
982 )
983 {
984 _gss_accept_sec_context_t *func;
985
986 *minor_status = 0;
987
988 output_token->length = 0;
989 output_token->value = NULL;
990
991 if (src_name != NULL)
992 *src_name = GSS_C_NO_NAME;
993 if (mech_type != NULL)
994 *mech_type = GSS_C_NO_OID;
995 if (ret_flags != NULL)
996 *ret_flags = 0;
997 if (time_rec != NULL)
998 *time_rec = 0;
999 if (delegated_cred_handle != NULL)
1000 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1001
1002
1003 if (*context_handle == GSS_C_NO_CONTEXT)
1004 func = acceptor_start;
1005 else
1006 func = acceptor_continue;
1007
1008
1009 return (*func)(minor_status, context_handle, acceptor_cred_handle,
1010 input_token_buffer, input_chan_bindings,
1011 src_name, mech_type, output_token, ret_flags,
1012 time_rec, delegated_cred_handle);
1013 }