Changes from doug barton to make spnego indepedant of the heimdal version of the...
[heimdal.git] / lib / gssapi / spnego / accept_sec_context.c
blob0ece042febdcae46469fa05bfe77d29c0ee6eec5
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.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "spnego/spnego_locl.h"
36 RCSID("$Id$");
38 static OM_uint32
39 send_reject (OM_uint32 *minor_status,
40 gss_buffer_t output_token)
42 NegotiationToken nt;
43 size_t size;
45 nt.element = choice_NegotiationToken_negTokenResp;
47 ALLOC(nt.u.negTokenResp.negResult, 1);
48 if (nt.u.negTokenResp.negResult == NULL) {
49 *minor_status = ENOMEM;
50 return GSS_S_FAILURE;
52 *(nt.u.negTokenResp.negResult) = reject;
53 nt.u.negTokenResp.supportedMech = NULL;
54 nt.u.negTokenResp.responseToken = NULL;
55 nt.u.negTokenResp.mechListMIC = NULL;
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;
64 return GSS_S_BAD_MECH;
67 static OM_uint32
68 acceptor_approved(gss_name_t target_name, gss_OID mech)
70 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
71 gss_OID_set oidset;
72 OM_uint32 junk, ret;
74 if (target_name == GSS_C_NO_NAME)
75 return GSS_S_COMPLETE;
77 gss_create_empty_oid_set(&junk, &oidset);
78 gss_add_oid_set_member(&junk, mech, &oidset);
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);
87 return GSS_S_COMPLETE;
90 static OM_uint32
91 send_supported_mechs (OM_uint32 *minor_status,
92 gss_buffer_t output_token)
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;
105 memset(&nt, 0, sizeof(nt));
107 nt.element = choice_NegotiationTokenWin_negTokenInit;
108 nt.u.negTokenInit.reqFlags = NULL;
109 nt.u.negTokenInit.mechToken = NULL;
110 nt.u.negTokenInit.negHints = NULL;
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;
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;
125 hostname[sizeof(hostname) - 1] = '\0';
127 /* Send the constructed SAM name for this host */
128 for (p = hostname; *p != '\0' && *p != '.'; p++) {
129 *p = toupper((unsigned char)*p);
131 *p++ = '$';
132 *p = '\0';
134 name_buf.length = strlen(hostname);
135 name_buf.value = hostname;
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;
145 name_buf.length = 0;
146 name_buf.value = NULL;
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;
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;
168 gss_release_name(&minor, &canon_princ);
169 gss_release_name(&minor, &target_princ);
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;
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;
187 *(nt.u.negTokenInit.negHints->hintName) = name_buf.value;
188 name_buf.value = NULL;
189 nt.u.negTokenInit.negHints->hintAddress = NULL;
191 ASN1_MALLOC_ENCODE(NegotiationTokenWin,
192 data.value, data.length, &nt, &buf_len, ret);
193 free_NegotiationTokenWin(&nt);
194 if (ret) {
195 return ret;
197 if (data.length != buf_len)
198 abort();
200 ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token);
202 free (data.value);
204 if (ret != GSS_S_COMPLETE)
205 return ret;
207 *minor_status = 0;
209 return GSS_S_CONTINUE_NEEDED;
212 static OM_uint32
213 send_accept (OM_uint32 *minor_status,
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)
220 NegotiationToken nt;
221 OM_uint32 ret;
222 gss_buffer_desc mech_mic_buf;
223 size_t size;
225 memset(&nt, 0, sizeof(nt));
227 nt.element = choice_NegotiationToken_negTokenResp;
229 ALLOC(nt.u.negTokenResp.negResult, 1);
230 if (nt.u.negTokenResp.negResult == NULL) {
231 *minor_status = ENOMEM;
232 return GSS_S_FAILURE;
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;
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;
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;
266 } else {
267 nt.u.negTokenResp.supportedMech = NULL;
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;
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;
285 if (mech_buf != GSS_C_NO_BUFFER) {
286 ret = gss_get_mic(minor_status,
287 context_handle->negotiated_ctx_id,
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;
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;
308 } else
309 nt.u.negTokenResp.mechListMIC = NULL;
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;
321 * The response should not be encapsulated, because
322 * it is a SubsequentContextToken (note though RFC 1964
323 * specifies encapsulation for all _Kerberos_ tokens).
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;
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
343 OM_uint32 ret;
344 gss_buffer_desc mic_buf;
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;
352 if (mechListMIC == NULL) {
353 *minor_status = 0;
354 return GSS_S_DEFECTIVE_TOKEN;
357 mic_buf.length = mechListMIC->length;
358 mic_buf.value = mechListMIC->data;
360 ret = gss_verify_mic(minor_status,
361 context_handle->negotiated_ctx_id,
362 mech_buf,
363 &mic_buf,
364 NULL);
366 if (ret != GSS_S_COMPLETE)
367 ret = GSS_S_DEFECTIVE_TOKEN;
369 return ret;
372 static OM_uint32
373 select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p,
374 gss_OID *mech_p)
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;
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;
392 oid.length = mech_len;
393 oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
395 if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) {
396 return GSS_S_BAD_MECH;
399 *minor_status = 0;
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;
408 ret = gss_indicate_mechs(&junk, &mechs);
409 if (ret)
410 return (ret);
412 for (i = 0; i < mechs->count; i++)
413 if (gss_oid_equal(&mechs->elements[i], oidp))
414 break;
416 if (i == mechs->count) {
417 gss_release_oid_set(&junk, &mechs);
418 return GSS_S_BAD_MECH;
420 gss_release_oid_set(&junk, &mechs);
422 ret = gss_duplicate_oid(minor_status,
423 &oid, /* possibly this should be oidp */
424 mech_p);
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];
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;
437 asprintf(&str, "host@%s", hostname);
438 host = str;
441 namebuf.length = strlen(host);
442 namebuf.value = host;
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;
451 ret = acceptor_approved(name, *mech_p);
452 gss_release_name(&junk, &name);
455 return ret;
459 static OM_uint32
460 acceptor_complete(OM_uint32 * minor_status,
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)
469 OM_uint32 ret;
470 int require_mic, verify_mic;
471 gss_buffer_desc buf;
473 buf.length = 0;
474 buf.value = NULL;
476 ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
477 if (ret)
478 return ret;
480 ctx->require_mic = require_mic;
482 if (mic != NULL)
483 require_mic = 1;
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;
497 if (verify_mic || get_mic) {
498 int eret;
499 size_t buf_len;
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;
508 if (buf.length != buf_len)
509 abort();
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;
521 ctx->verified_mic = 1;
523 if (buf.value)
524 free(buf.value);
526 } else
527 *get_mic = verify_mic = 0;
529 return GSS_S_COMPLETE;
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
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;
563 mech_output_token.value = NULL;
564 mech_output_token.length = 0;
565 mech_buf.value = NULL;
567 if (input_token_buffer->length == 0)
568 return send_supported_mechs (minor_status, output_token);
570 ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
571 if (ret != GSS_S_COMPLETE)
572 return ret;
574 ctx = (gssspnego_ctx)*context_handle;
577 * The GSS-API encapsulation is only present on the initial
578 * context token (negTokenInit).
580 ret = gss_decapsulate_token (input_token_buffer,
581 GSS_SPNEGO_MECHANISM,
582 &data);
583 if (ret)
584 return ret;
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;
592 if (nt.element != choice_NegotiationToken_negTokenInit) {
593 *minor_status = 0;
594 return GSS_S_DEFECTIVE_TOKEN;
596 ni = &nt.u.negTokenInit;
598 if (ni->mechTypes.len < 1) {
599 free_NegotiationToken(&nt);
600 *minor_status = 0;
601 return GSS_S_DEFECTIVE_TOKEN;
604 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
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;
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,
621 ret = select_mech(minor_status,
622 &ni->mechTypes.val[0],
624 &preferred_mech_type);
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;
631 ibuf.length = ni->mechToken->length;
632 ibuf.value = ni->mechToken->data;
633 mech_input_token = &ibuf;
635 if (acceptor_cred != NULL)
636 mech_cred = acceptor_cred->negotiated_cred_id;
637 else
638 mech_cred = GSS_C_NO_CREDENTIAL;
640 if (ctx->mech_src_name != GSS_C_NO_NAME)
641 gss_release_name(&junk, &ctx->mech_src_name);
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);
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);
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;
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;
679 first_ok = 1;
680 } else {
681 gss_mg_collect_error(preferred_mech_type, ret, *minor_status);
686 * If opportunistic token failed, lets try the other mechs.
689 if (!first_ok && ni->mechToken != NULL) {
691 preferred_mech_type = GSS_C_NO_OID;
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],
698 &preferred_mech_type);
699 if (ret == 0)
700 break;
702 if (preferred_mech_type == GSS_C_NO_OID) {
703 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
704 free_NegotiationToken(&nt);
705 return ret;
708 ctx->preferred_mech_type = preferred_mech_type;
709 ctx->negotiated_mech_type = preferred_mech_type;
713 * The initial token always have a response
716 ret = send_accept (minor_status,
717 ctx,
718 &mech_output_token,
720 get_mic ? &mech_buf : NULL,
721 output_token);
722 if (ret)
723 goto out;
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;
732 free_NegotiationToken(&nt);
735 if (ret == GSS_S_COMPLETE) {
736 if (src_name != NULL && ctx->mech_src_name != NULL) {
737 spnego_name name;
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;
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;
755 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
756 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
757 return ret;
760 _gss_spnego_internal_delete_sec_context(&junk, context_handle,
761 GSS_C_NO_BUFFER);
763 return ret;
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
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;
793 mech_buf.value = NULL;
795 ctx = (gssspnego_ctx)*context_handle;
798 * The GSS-API encapsulation is only present on the initial
799 * context token (negTokenInit).
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;
809 if (nt.element != choice_NegotiationToken_negTokenResp) {
810 *minor_status = 0;
811 return GSS_S_DEFECTIVE_TOKEN;
813 na = &nt.u.negTokenResp;
815 if (na->negResult != NULL) {
816 negResult = *(na->negResult);
819 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
822 gss_buffer_desc ibuf, obuf;
823 int require_mic, get_mic = 0;
824 int require_response;
825 heim_octet_string *mic;
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;
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;
840 if (acceptor_cred != NULL)
841 mech_cred = acceptor_cred->negotiated_cred_id;
842 else
843 mech_cred = GSS_C_NO_CREDENTIAL;
845 if (ctx->mech_src_name != GSS_C_NO_NAME)
846 gss_release_name(&minor, &ctx->mech_src_name);
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);
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);
867 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
868 mech_output_token = &obuf;
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;
877 if (ret == GSS_S_COMPLETE)
878 ctx->open = 1;
879 } else
880 ret = GSS_S_COMPLETE;
882 ret2 = _gss_spnego_require_mechlist_mic(minor_status,
883 ctx,
884 &require_mic);
885 if (ret2)
886 goto out;
888 ctx->require_mic = require_mic;
890 mic = na->mechListMIC;
891 if (mic != NULL)
892 require_mic = 1;
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);
904 if (ctx->mech_flags & GSS_C_DCE_STYLE)
905 require_response = (negResult != accept_completed);
906 else
907 require_response = 0;
910 * Check whether we need to send a result: there should be only
911 * one accept_completed response sent in the entire negotiation
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,
922 get_mic ? &mech_buf : NULL,
923 output_token);
924 if (ret2)
925 goto out;
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);
938 if (ret == GSS_S_COMPLETE) {
939 if (src_name != NULL && ctx->mech_src_name != NULL) {
940 spnego_name name;
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;
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;
958 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
959 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
960 return ret;
963 _gss_spnego_internal_delete_sec_context(&minor, context_handle,
964 GSS_C_NO_BUFFER);
966 return ret;
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
984 _gss_accept_sec_context_t *func;
986 *minor_status = 0;
988 output_token->length = 0;
989 output_token->value = NULL;
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;
1003 if (*context_handle == GSS_C_NO_CONTEXT)
1004 func = acceptor_start;
1005 else
1006 func = acceptor_continue;
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);