2 * Copyright (c) 1997 - 2004 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
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
34 #include "spnego/spnego_locl.h"
36 RCSID("$Id: accept_sec_context.c,v 1.6 2006/10/07 22:26:57 lha Exp $");
39 _gss_spnego_encode_response(OM_uint32 *minor_status,
40 const NegTokenResp *resp,
46 size_t buf_size, buf_len;
49 buf = malloc(buf_size);
51 *minor_status = ENOMEM;
56 ret = encode_NegTokenResp(buf + buf_size - 1,
62 ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
73 if (ret == ASN1_OVERFLOW) {
77 tmp = realloc (buf, buf_size);
79 *minor_status = ENOMEM;
90 } while (ret == ASN1_OVERFLOW);
92 data->value = buf + buf_size - buf_len;
93 data->length = buf_len;
96 return GSS_S_COMPLETE;
100 send_reject (OM_uint32 *minor_status,
101 gss_buffer_t output_token)
104 gss_buffer_desc data;
108 ALLOC(resp.negResult, 1);
109 if (resp.negResult == NULL) {
110 *minor_status = ENOMEM;
111 return GSS_S_FAILURE;
113 *(resp.negResult) = reject;
114 resp.supportedMech = NULL;
115 resp.responseToken = NULL;
116 resp.mechListMIC = NULL;
118 ret = _gss_spnego_encode_response (minor_status, &resp, &data, &buf);
119 free_NegTokenResp(&resp);
120 if (ret != GSS_S_COMPLETE)
123 output_token->value = malloc(data.length);
124 if (output_token->value == NULL) {
125 *minor_status = ENOMEM;
128 output_token->length = data.length;
129 memcpy(output_token->value, data.value, output_token->length);
132 if (ret != GSS_S_COMPLETE)
134 return GSS_S_BAD_MECH;
138 _gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
139 int includeMSCompatOID,
140 const gssspnego_cred cred_handle,
141 MechTypeList *mechtypelist,
142 gss_OID *preferred_mech)
145 gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
148 if (cred_handle != NULL) {
149 ret = gss_inquire_cred(minor_status,
150 cred_handle->negotiated_cred_id,
156 ret = gss_indicate_mechs(minor_status, &supported_mechs);
159 if (ret != GSS_S_COMPLETE) {
163 if (supported_mechs->count == 0) {
164 *minor_status = ENOENT;
165 gss_release_oid_set(minor_status, &supported_mechs);
166 return GSS_S_FAILURE;
169 count = supported_mechs->count;
170 if (includeMSCompatOID)
173 mechtypelist->len = 0;
174 mechtypelist->val = calloc(count, sizeof(MechType));
175 if (mechtypelist->val == NULL) {
176 *minor_status = ENOMEM;
177 gss_release_oid_set(minor_status, &supported_mechs);
178 return GSS_S_FAILURE;
181 for (i = 0; i < supported_mechs->count; i++) {
182 ret = _gss_spnego_add_mech_type(&supported_mechs->elements[i],
186 *minor_status = ENOMEM;
192 if (ret == GSS_S_COMPLETE && preferred_mech != NULL) {
193 ret = gss_duplicate_oid(minor_status,
194 &supported_mechs->elements[0],
198 if (ret != GSS_S_COMPLETE) {
199 free_MechTypeList(mechtypelist);
200 mechtypelist->len = 0;
201 mechtypelist->val = NULL;
203 gss_release_oid_set(minor_status, &supported_mechs);
209 send_supported_mechs (OM_uint32 *minor_status,
210 gss_buffer_t output_token)
213 char hostname[MAXHOSTNAMELEN], *p;
214 gss_buffer_desc name_buf;
216 gss_name_t target_princ;
217 gss_name_t canon_princ;
218 OM_uint32 ret, minor;
220 size_t buf_size, buf_len;
221 gss_buffer_desc data;
223 memset(&ni, 0, sizeof(ni));
228 ni.mechListMIC = NULL;
230 ret = _gss_spnego_indicate_mechtypelist(minor_status, 1,
232 &ni.mechTypes, NULL);
233 if (ret != GSS_S_COMPLETE) {
237 memset(&target_princ, 0, sizeof(target_princ));
238 if (gethostname(hostname, sizeof(hostname) - 1) != 0) {
239 *minor_status = errno;
240 free_NegTokenInit(&ni);
241 return GSS_S_FAILURE;
244 /* Send the constructed SAM name for this host */
245 for (p = hostname; *p != '\0' && *p != '.'; p++) {
246 *p = toupper((unsigned char)*p);
251 name_buf.length = strlen(hostname);
252 name_buf.value = hostname;
254 ret = gss_import_name(minor_status, &name_buf,
257 if (ret != GSS_S_COMPLETE) {
262 name_buf.value = NULL;
264 /* Canonicalize the name using the preferred mechanism */
265 ret = gss_canonicalize_name(minor_status,
269 if (ret != GSS_S_COMPLETE) {
270 gss_release_name(&minor, &target_princ);
274 ret = gss_display_name(minor_status, canon_princ,
275 &name_buf, &name_type);
276 if (ret != GSS_S_COMPLETE) {
277 gss_release_name(&minor, &canon_princ);
278 gss_release_name(&minor, &target_princ);
282 gss_release_name(&minor, &canon_princ);
283 gss_release_name(&minor, &target_princ);
285 ALLOC(ni.negHints, 1);
286 if (ni.negHints == NULL) {
287 *minor_status = ENOMEM;
288 gss_release_buffer(&minor, &name_buf);
289 free_NegTokenInit(&ni);
290 return GSS_S_FAILURE;
293 ALLOC(ni.negHints->hintName, 1);
294 if (ni.negHints->hintName == NULL) {
295 *minor_status = ENOMEM;
296 gss_release_buffer(&minor, &name_buf);
297 free_NegTokenInit(&ni);
298 return GSS_S_FAILURE;
301 *(ni.negHints->hintName) = name_buf.value;
302 name_buf.value = NULL;
303 ni.negHints->hintAddress = NULL;
306 buf = malloc(buf_size);
308 free_NegTokenInit(&ni);
309 *minor_status = ENOMEM;
310 return GSS_S_FAILURE;
314 ret = encode_NegTokenInit(buf + buf_size - 1,
320 ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
331 if (ret == ASN1_OVERFLOW) {
335 tmp = realloc (buf, buf_size);
337 *minor_status = ENOMEM;
339 free_NegTokenInit(&ni);
340 return GSS_S_FAILURE;
346 free_NegTokenInit(&ni);
347 return GSS_S_FAILURE;
350 } while (ret == ASN1_OVERFLOW);
352 data.value = buf + buf_size - buf_len;
353 data.length = buf_len;
355 ret = gss_encapsulate_token(&data,
356 GSS_SPNEGO_MECHANISM,
359 free_NegTokenInit (&ni);
361 if (ret != GSS_S_COMPLETE)
366 return GSS_S_CONTINUE_NEEDED;
370 send_accept (OM_uint32 *minor_status,
371 gssspnego_ctx context_handle,
372 gss_buffer_t mech_token,
373 int initial_response,
374 gss_buffer_t mech_buf,
375 gss_buffer_t output_token)
378 gss_buffer_desc data;
381 gss_buffer_desc mech_mic_buf;
383 memset(&resp, 0, sizeof(resp));
385 ALLOC(resp.negResult, 1);
386 if (resp.negResult == NULL) {
387 *minor_status = ENOMEM;
388 return GSS_S_FAILURE;
391 if (context_handle->open) {
392 if (mech_token != GSS_C_NO_BUFFER
393 && mech_token->length != 0
394 && mech_buf != GSS_C_NO_BUFFER)
395 *(resp.negResult) = accept_incomplete;
397 *(resp.negResult) = accept_completed;
399 if (initial_response && context_handle->require_mic)
400 *(resp.negResult) = request_mic;
402 *(resp.negResult) = accept_incomplete;
405 if (initial_response) {
406 ALLOC(resp.supportedMech, 1);
407 if (resp.supportedMech == NULL) {
408 free_NegTokenResp(&resp);
409 *minor_status = ENOMEM;
410 return GSS_S_FAILURE;
413 ret = der_get_oid(context_handle->preferred_mech_type->elements,
414 context_handle->preferred_mech_type->length,
418 free_NegTokenResp(&resp);
419 *minor_status = ENOMEM;
420 return GSS_S_FAILURE;
423 resp.supportedMech = NULL;
426 if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
427 ALLOC(resp.responseToken, 1);
428 if (resp.responseToken == NULL) {
429 free_NegTokenResp(&resp);
430 *minor_status = ENOMEM;
431 return GSS_S_FAILURE;
433 resp.responseToken->length = mech_token->length;
434 resp.responseToken->data = mech_token->value;
435 mech_token->length = 0;
436 mech_token->value = NULL;
438 resp.responseToken = NULL;
441 if (mech_buf != GSS_C_NO_BUFFER) {
442 ALLOC(resp.mechListMIC, 1);
443 if (resp.mechListMIC == NULL) {
444 free_NegTokenResp(&resp);
445 *minor_status = ENOMEM;
446 return GSS_S_FAILURE;
449 ret = gss_get_mic(minor_status,
450 context_handle->negotiated_ctx_id,
454 if (ret != GSS_S_COMPLETE) {
455 free_NegTokenResp(&resp);
459 resp.mechListMIC->length = mech_mic_buf.length;
460 resp.mechListMIC->data = mech_mic_buf.value;
462 resp.mechListMIC = NULL;
464 ret = _gss_spnego_encode_response (minor_status, &resp, &data, &buf);
465 if (ret != GSS_S_COMPLETE) {
466 free_NegTokenResp(&resp);
471 * The response should not be encapsulated, because
472 * it is a SubsequentContextToken (note though RFC 1964
473 * specifies encapsulation for all _Kerberos_ tokens).
475 output_token->value = malloc(data.length);
476 if (output_token->value == NULL) {
477 *minor_status = ENOMEM;
480 output_token->length = data.length;
481 memcpy(output_token->value, data.value, output_token->length);
484 if (ret != GSS_S_COMPLETE) {
485 free_NegTokenResp(&resp);
489 ret = (*(resp.negResult) == accept_completed) ? GSS_S_COMPLETE :
490 GSS_S_CONTINUE_NEEDED;
491 free_NegTokenResp(&resp);
498 (OM_uint32 *minor_status,
499 gssspnego_ctx context_handle,
500 gss_buffer_t mech_buf,
501 heim_octet_string *mechListMIC
505 gss_buffer_desc mic_buf;
507 if (context_handle->verified_mic) {
508 /* This doesn't make sense, we've already verified it? */
510 return GSS_S_DUPLICATE_TOKEN;
513 if (mechListMIC == NULL) {
515 return GSS_S_DEFECTIVE_TOKEN;
518 mic_buf.length = mechListMIC->length;
519 mic_buf.value = mechListMIC->data;
521 ret = gss_verify_mic(minor_status,
522 context_handle->negotiated_ctx_id,
527 if (ret != GSS_S_COMPLETE)
528 ret = GSS_S_DEFECTIVE_TOKEN;
534 _gss_spnego_accept_sec_context
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,
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, ret2, minor;
551 size_t ni_len, na_len;
553 gss_buffer_desc data;
556 unsigned int negResult = accept_incomplete;
557 gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
558 gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
559 gss_buffer_desc mech_buf;
560 gss_OID preferred_mech_type = GSS_C_NO_OID;
562 gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
566 output_token->length = 0;
567 output_token->value = NULL;
569 if (src_name != NULL)
570 *src_name = GSS_C_NO_NAME;
572 if (mech_type != NULL)
573 *mech_type = GSS_C_NO_OID;
575 if (ret_flags != NULL)
578 if (time_rec != NULL)
581 if (delegated_cred_handle != NULL)
582 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
584 mech_buf.value = NULL;
586 if (*context_handle == GSS_C_NO_CONTEXT) {
587 ret = _gss_spnego_alloc_sec_context(minor_status,
589 if (ret != GSS_S_COMPLETE)
592 if (input_token_buffer->length == 0) {
593 return send_supported_mechs (minor_status,
598 ctx = (gssspnego_ctx)*context_handle;
601 * The GSS-API encapsulation is only present on the initial
602 * context token (negTokenInit).
604 ret = gss_decapsulate_token (input_token_buffer,
605 GSS_SPNEGO_MECHANISM,
607 initialToken = (ret == GSS_S_COMPLETE);
610 data.value = input_token_buffer->value;
611 data.length = input_token_buffer->length;
614 ret = der_match_tag_and_length(data.value, data.length,
615 ASN1_C_CONTEXT, CONS,
616 initialToken ? 0 : 1,
620 return GSS_S_FAILURE;
623 if (len > data.length - taglen) {
624 *minor_status = ASN1_OVERRUN;
625 return GSS_S_FAILURE;
629 ret = decode_NegTokenInit((const unsigned char *)data.value + taglen,
632 ret = decode_NegTokenResp((const unsigned char *)data.value + taglen,
637 return GSS_S_DEFECTIVE_TOKEN;
640 if (!initialToken && na.negResult != NULL) {
641 negResult = *(na.negResult);
644 if (negResult == reject || negResult == request_mic) {
645 /* request_mic should only be sent by acceptor */
646 free_NegTokenResp(&na);
647 return GSS_S_DEFECTIVE_TOKEN;
651 for (i = 0; i < ni.mechTypes.len; ++i) {
652 /* Call glue layer to find first mech we support */
653 ret = _gss_spnego_select_mech(minor_status, &ni.mechTypes.val[i],
654 &preferred_mech_type);
658 if (preferred_mech_type == GSS_C_NO_OID) {
659 free_NegTokenInit(&ni);
660 return GSS_S_BAD_MECH;
664 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
667 ctx->preferred_mech_type = preferred_mech_type;
668 ctx->initiator_mech_types.len = ni.mechTypes.len;
669 ctx->initiator_mech_types.val = ni.mechTypes.val;
670 ni.mechTypes.len = 0;
671 ni.mechTypes.val = NULL;
675 gss_buffer_desc ibuf, obuf;
676 int require_mic, verify_mic, get_mic;
677 int require_response;
678 heim_octet_string *mic;
681 if (ni.mechToken != NULL) {
682 ibuf.length = ni.mechToken->length;
683 ibuf.value = ni.mechToken->data;
684 mech_input_token = &ibuf;
687 if (na.responseToken != NULL) {
688 ibuf.length = na.responseToken->length;
689 ibuf.value = na.responseToken->data;
690 mech_input_token = &ibuf;
694 if (mech_input_token != GSS_C_NO_BUFFER) {
695 gss_cred_id_t mech_cred;
696 gss_cred_id_t mech_delegated_cred;
697 gss_cred_id_t *mech_delegated_cred_p;
699 if (acceptor_cred != NULL)
700 mech_cred = acceptor_cred->negotiated_cred_id;
702 mech_cred = GSS_C_NO_CREDENTIAL;
704 if (delegated_cred_handle != NULL) {
705 mech_delegated_cred = GSS_C_NO_CREDENTIAL;
706 mech_delegated_cred_p = &mech_delegated_cred;
708 mech_delegated_cred_p = NULL;
711 if (ctx->mech_src_name != GSS_C_NO_NAME)
712 gss_release_name(&minor, &ctx->mech_src_name);
714 if (ctx->delegated_cred_id != GSS_C_NO_CREDENTIAL)
715 _gss_spnego_release_cred(&minor, &ctx->delegated_cred_id);
717 ret = gss_accept_sec_context(&minor,
718 &ctx->negotiated_ctx_id,
723 &ctx->negotiated_mech_type,
727 mech_delegated_cred_p);
728 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
729 if (mech_delegated_cred_p != NULL &&
730 mech_delegated_cred != GSS_C_NO_CREDENTIAL) {
731 ret2 = _gss_spnego_alloc_cred(minor_status,
733 &ctx->delegated_cred_id);
734 if (ret2 != GSS_S_COMPLETE)
737 mech_output_token = &obuf;
739 if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
741 free_NegTokenInit(&ni);
743 free_NegTokenResp(&na);
744 send_reject (minor_status, output_token);
745 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
748 if (ret == GSS_S_COMPLETE)
751 ret = GSS_S_COMPLETE;
753 ret2 = _gss_spnego_require_mechlist_mic(minor_status,
759 ctx->require_mic = require_mic;
761 mic = initialToken ? ni.mechListMIC : na.mechListMIC;
765 if (ctx->open && require_mic) {
766 if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
769 } else if (mech_output_token != GSS_C_NO_BUFFER &&
770 mech_output_token->length == 0) { /* Odd */
771 get_mic = verify_mic = 1;
772 } else { /* Even/One */
777 if (verify_mic || get_mic) {
781 ASN1_MALLOC_ENCODE(MechTypeList,
782 mech_buf.value, mech_buf.length,
783 &ctx->initiator_mech_types, &buf_len, eret);
785 ret2 = GSS_S_FAILURE;
786 *minor_status = eret;
789 if (mech_buf.length != buf_len)
794 ret2 = verify_mechlist_mic(minor_status, ctx, &mech_buf, mic);
797 send_reject (minor_status, output_token);
801 ctx->verified_mic = 1;
804 verify_mic = get_mic = 0;
806 if (ctx->mech_flags & GSS_C_DCE_STYLE)
807 require_response = (negResult != accept_completed);
809 require_response = 0;
812 * Check whether we need to send a result: there should be only
813 * one accept_completed response sent in the entire negotiation
815 if ((mech_output_token != GSS_C_NO_BUFFER &&
816 mech_output_token->length != 0)
819 ret2 = send_accept (minor_status,
823 get_mic ? &mech_buf : NULL,
830 if (ret2 != GSS_S_COMPLETE)
832 if (mech_output_token != NULL)
833 gss_release_buffer(&minor, mech_output_token);
834 if (mech_buf.value != NULL)
835 free(mech_buf.value);
837 free_NegTokenInit(&ni);
839 free_NegTokenResp(&na);
842 if (ret == GSS_S_COMPLETE) {
843 if (src_name != NULL) {
844 ret2 = gss_duplicate_name(minor_status,
847 if (ret2 != GSS_S_COMPLETE)
850 if (delegated_cred_handle != NULL) {
851 *delegated_cred_handle = ctx->delegated_cred_id;
852 ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL;
856 if (mech_type != NULL)
857 *mech_type = ctx->negotiated_mech_type;
858 if (ret_flags != NULL)
859 *ret_flags = ctx->mech_flags;
860 if (time_rec != NULL)
861 *time_rec = ctx->mech_time_rec;
863 if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
864 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
868 _gss_spnego_internal_delete_sec_context(&minor, context_handle,