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