Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: init_sec_context.c,v 1.5 2023/06/19 21:41:43 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      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 "gsskrb5_locl.h"
     37 
     38 /*
     39  * copy the addresses from `input_chan_bindings' (if any) to
     40  * the auth context `ac'
     41  */
     42 
     43 static OM_uint32
     44 set_addresses (krb5_context context,
     45 	       krb5_auth_context ac,
     46 	       const gss_channel_bindings_t input_chan_bindings)
     47 {
     48     /* Port numbers are expected to be in application_data.value,
     49      * initator's port first */
     50 
     51     krb5_address initiator_addr, acceptor_addr;
     52     krb5_error_code kret;
     53 
     54     if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS
     55 	|| input_chan_bindings->application_data.length !=
     56 	2 * sizeof(ac->local_port))
     57 	return 0;
     58 
     59     memset(&initiator_addr, 0, sizeof(initiator_addr));
     60     memset(&acceptor_addr, 0, sizeof(acceptor_addr));
     61 
     62     ac->local_port =
     63 	*(int16_t *) input_chan_bindings->application_data.value;
     64 
     65     ac->remote_port =
     66 	*((int16_t *) input_chan_bindings->application_data.value + 1);
     67 
     68     kret = _gsskrb5i_address_to_krb5addr(context,
     69 					 input_chan_bindings->acceptor_addrtype,
     70 					 &input_chan_bindings->acceptor_address,
     71 					 ac->remote_port,
     72 					 &acceptor_addr);
     73     if (kret)
     74 	return kret;
     75 
     76     kret = _gsskrb5i_address_to_krb5addr(context,
     77 					 input_chan_bindings->initiator_addrtype,
     78 					 &input_chan_bindings->initiator_address,
     79 					 ac->local_port,
     80 					 &initiator_addr);
     81     if (kret) {
     82 	krb5_free_address (context, &acceptor_addr);
     83 	return kret;
     84     }
     85 
     86     kret = krb5_auth_con_setaddrs(context,
     87 				  ac,
     88 				  &initiator_addr,  /* local address */
     89 				  &acceptor_addr);  /* remote address */
     90 
     91     krb5_free_address (context, &initiator_addr);
     92     krb5_free_address (context, &acceptor_addr);
     93 
     94 #if 0
     95     free(input_chan_bindings->application_data.value);
     96     input_chan_bindings->application_data.value = NULL;
     97     input_chan_bindings->application_data.length = 0;
     98 #endif
     99 
    100     return kret;
    101 }
    102 
    103 OM_uint32
    104 _gsskrb5_create_ctx(
    105         OM_uint32 * minor_status,
    106 	gss_ctx_id_t * context_handle,
    107 	krb5_context context,
    108  	const gss_channel_bindings_t input_chan_bindings,
    109  	enum gss_ctx_id_t_state state)
    110 {
    111     krb5_error_code kret;
    112     gsskrb5_ctx ctx;
    113 
    114     *context_handle = NULL;
    115 
    116     ctx = malloc(sizeof(*ctx));
    117     if (ctx == NULL) {
    118 	*minor_status = ENOMEM;
    119 	return GSS_S_FAILURE;
    120     }
    121     ctx->auth_context		= NULL;
    122     ctx->deleg_auth_context	= NULL;
    123     ctx->source			= NULL;
    124     ctx->target			= NULL;
    125     ctx->kcred			= NULL;
    126     ctx->ccache			= NULL;
    127     ctx->state			= state;
    128     ctx->flags			= 0;
    129     ctx->more_flags		= 0;
    130     ctx->service_keyblock	= NULL;
    131     ctx->ticket			= NULL;
    132     krb5_data_zero(&ctx->fwd_data);
    133     ctx->endtime		= 0;
    134     ctx->order			= NULL;
    135     ctx->crypto			= NULL;
    136     HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
    137 
    138     kret = krb5_auth_con_init (context, &ctx->auth_context);
    139     if (kret) {
    140 	*minor_status = kret;
    141 	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
    142 	free(ctx);
    143 	return GSS_S_FAILURE;
    144     }
    145 
    146     kret = krb5_auth_con_init (context, &ctx->deleg_auth_context);
    147     if (kret) {
    148 	*minor_status = kret;
    149 	krb5_auth_con_free(context, ctx->auth_context);
    150 	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
    151 	free(ctx);
    152 	return GSS_S_FAILURE;
    153     }
    154 
    155     kret = set_addresses(context, ctx->auth_context, input_chan_bindings);
    156     if (kret) {
    157 	*minor_status = kret;
    158 
    159 	krb5_auth_con_free(context, ctx->auth_context);
    160 	krb5_auth_con_free(context, ctx->deleg_auth_context);
    161 
    162 	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
    163 	free(ctx);
    164 	return GSS_S_BAD_BINDINGS;
    165     }
    166 
    167     kret = set_addresses(context, ctx->deleg_auth_context, input_chan_bindings);
    168     if (kret) {
    169 	*minor_status = kret;
    170 
    171 	krb5_auth_con_free(context, ctx->auth_context);
    172 	krb5_auth_con_free(context, ctx->deleg_auth_context);
    173 
    174 	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
    175 	free(ctx);
    176 	return GSS_S_BAD_BINDINGS;
    177     }
    178 
    179     /*
    180      * We need a sequence number
    181      */
    182 
    183     krb5_auth_con_addflags(context,
    184 			   ctx->auth_context,
    185 			   KRB5_AUTH_CONTEXT_DO_SEQUENCE |
    186 			   KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED,
    187 			   NULL);
    188 
    189     /*
    190      * We need a sequence number
    191      */
    192 
    193     krb5_auth_con_addflags(context,
    194 			   ctx->deleg_auth_context,
    195 			   KRB5_AUTH_CONTEXT_DO_SEQUENCE |
    196 			   KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED,
    197 			   NULL);
    198 
    199     *context_handle = (gss_ctx_id_t)ctx;
    200 
    201     return GSS_S_COMPLETE;
    202 }
    203 
    204 
    205 static OM_uint32
    206 gsskrb5_get_creds(
    207         OM_uint32 * minor_status,
    208 	krb5_context context,
    209 	krb5_ccache ccache,
    210 	gsskrb5_ctx ctx,
    211 	gss_const_name_t target_name,
    212 	OM_uint32 time_req,
    213 	OM_uint32 * time_rec)
    214 {
    215     OM_uint32 ret;
    216     krb5_error_code kret;
    217     krb5_creds this_cred;
    218     OM_uint32 lifetime_rec;
    219 
    220     if (ctx->target) {
    221 	krb5_free_principal(context, ctx->target);
    222 	ctx->target = NULL;
    223     }
    224     if (ctx->kcred) {
    225 	krb5_free_creds(context, ctx->kcred);
    226 	ctx->kcred = NULL;
    227     }
    228 
    229     ret = _gsskrb5_canon_name(minor_status, context, target_name,
    230                               &ctx->target);
    231     if (ret)
    232 	return ret;
    233 
    234     memset(&this_cred, 0, sizeof(this_cred));
    235     this_cred.client = ctx->source;
    236     this_cred.server = ctx->target;
    237 
    238     if (time_req && time_req != GSS_C_INDEFINITE) {
    239 	krb5_timestamp ts;
    240 
    241 	krb5_timeofday (context, &ts);
    242 	this_cred.times.endtime = ts + time_req;
    243     } else {
    244 	this_cred.times.endtime   = 0;
    245     }
    246 
    247     this_cred.session.keytype = KEYTYPE_NULL;
    248 
    249     kret = krb5_get_credentials(context,
    250 				0,
    251 				ccache,
    252 				&this_cred,
    253 				&ctx->kcred);
    254     if (kret) {
    255 	*minor_status = kret;
    256 	return GSS_S_FAILURE;
    257     }
    258 
    259     ctx->endtime = ctx->kcred->times.endtime;
    260 
    261     ret = _gsskrb5_lifetime_left(minor_status, context,
    262 				 ctx->endtime, &lifetime_rec);
    263     if (ret) return ret;
    264 
    265     if (lifetime_rec == 0) {
    266 	*minor_status = 0;
    267 	return GSS_S_CONTEXT_EXPIRED;
    268     }
    269 
    270     if (time_rec) *time_rec = lifetime_rec;
    271 
    272     return GSS_S_COMPLETE;
    273 }
    274 
    275 static OM_uint32
    276 gsskrb5_initiator_ready(
    277 	OM_uint32 * minor_status,
    278 	gsskrb5_ctx ctx,
    279 	krb5_context context)
    280 {
    281     OM_uint32 ret;
    282     int32_t seq_number;
    283     int is_cfx = 0;
    284     OM_uint32 flags = ctx->flags;
    285 
    286     krb5_free_creds(context, ctx->kcred);
    287     ctx->kcred = NULL;
    288 
    289     if (ctx->more_flags & CLOSE_CCACHE)
    290 	krb5_cc_close(context, ctx->ccache);
    291     ctx->ccache = NULL;
    292 
    293     krb5_auth_con_getremoteseqnumber (context, ctx->auth_context, &seq_number);
    294 
    295     _gsskrb5i_is_cfx(context, ctx, 0);
    296     is_cfx = (ctx->more_flags & IS_CFX);
    297 
    298     ret = _gssapi_msg_order_create(minor_status,
    299 				   &ctx->order,
    300 				   _gssapi_msg_order_f(flags),
    301 				   seq_number, 0, is_cfx);
    302     if (ret) return ret;
    303 
    304     ctx->state	= INITIATOR_READY;
    305     ctx->more_flags	|= OPEN;
    306 
    307     return GSS_S_COMPLETE;
    308 }
    309 
    310 /*
    311  * handle delegated creds in init-sec-context
    312  */
    313 
    314 static void
    315 do_delegation (krb5_context context,
    316 	       krb5_auth_context ac,
    317 	       krb5_ccache ccache,
    318 	       krb5_creds *cred,
    319 	       krb5_const_principal name,
    320 	       krb5_data *fwd_data,
    321 	       uint32_t flagmask,
    322 	       uint32_t *flags)
    323 {
    324     krb5_creds creds;
    325     KDCOptions fwd_flags;
    326     krb5_error_code kret;
    327 
    328     memset (&creds, 0, sizeof(creds));
    329     krb5_data_zero (fwd_data);
    330 
    331     kret = krb5_cc_get_principal(context, ccache, &creds.client);
    332     if (kret)
    333 	goto out;
    334 
    335     kret = krb5_make_principal(context,
    336 			       &creds.server,
    337 			       creds.client->realm,
    338 			       KRB5_TGS_NAME,
    339 			       creds.client->realm,
    340 			       NULL);
    341     if (kret)
    342 	goto out;
    343 
    344     creds.times.endtime = 0;
    345 
    346     memset(&fwd_flags, 0, sizeof(fwd_flags));
    347     fwd_flags.forwarded = 1;
    348     fwd_flags.forwardable = 1;
    349 
    350     if (name->name.name_string.len < 2)
    351 	goto out;
    352 
    353     kret = krb5_get_forwarded_creds(context,
    354 				    ac,
    355 				    ccache,
    356 				    KDCOptions2int(fwd_flags),
    357 				    name->name.name_string.val[1],
    358 				    &creds,
    359 				    fwd_data);
    360 
    361  out:
    362     if (kret)
    363 	*flags &= ~flagmask;
    364     else
    365 	*flags |= flagmask;
    366 
    367     if (creds.client)
    368 	krb5_free_principal(context, creds.client);
    369     if (creds.server)
    370 	krb5_free_principal(context, creds.server);
    371 }
    372 
    373 /*
    374  * first stage of init-sec-context
    375  */
    376 
    377 static OM_uint32
    378 init_auth
    379 (OM_uint32 * minor_status,
    380  gsskrb5_cred cred,
    381  gsskrb5_ctx ctx,
    382  krb5_context context,
    383  gss_const_name_t name,
    384  const gss_OID mech_type,
    385  OM_uint32 req_flags,
    386  OM_uint32 time_req,
    387  const gss_buffer_t input_token,
    388  gss_OID * actual_mech_type,
    389  gss_buffer_t output_token,
    390  OM_uint32 * ret_flags,
    391  OM_uint32 * time_rec
    392     )
    393 {
    394     OM_uint32 ret = GSS_S_FAILURE;
    395     krb5_error_code kret;
    396     krb5_data fwd_data;
    397     OM_uint32 lifetime_rec;
    398 
    399     krb5_data_zero(&fwd_data);
    400 
    401     *minor_status = 0;
    402 
    403     if (actual_mech_type)
    404 	*actual_mech_type = GSS_KRB5_MECHANISM;
    405 
    406     if (cred == NULL) {
    407 	kret = krb5_cc_default (context, &ctx->ccache);
    408 	if (kret) {
    409 	    *minor_status = kret;
    410 	    ret = GSS_S_FAILURE;
    411 	    goto failure;
    412 	}
    413 	ctx->more_flags |= CLOSE_CCACHE;
    414     } else
    415 	ctx->ccache = cred->ccache;
    416 
    417     kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source);
    418     if (kret) {
    419 	*minor_status = kret;
    420 	ret = GSS_S_FAILURE;
    421 	goto failure;
    422     }
    423 
    424     /*
    425      * This is hideous glue for (NFS) clients that wants to limit the
    426      * available enctypes to what it can support (encryption in
    427      * kernel).
    428      */
    429     if (cred && cred->enctypes)
    430 	krb5_set_default_in_tkt_etypes(context, cred->enctypes);
    431 
    432     ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
    433 			    ctx, name, time_req, time_rec);
    434     if (ret)
    435 	goto failure;
    436 
    437     ctx->endtime = ctx->kcred->times.endtime;
    438 
    439     ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
    440     if (ret)
    441 	goto failure;
    442 
    443     ret = _gsskrb5_lifetime_left(minor_status,
    444 				 context,
    445 				 ctx->endtime,
    446 				 &lifetime_rec);
    447     if (ret)
    448 	goto failure;
    449 
    450     if (lifetime_rec == 0) {
    451 	*minor_status = 0;
    452 	ret = GSS_S_CONTEXT_EXPIRED;
    453 	goto failure;
    454     }
    455 
    456     krb5_auth_con_setkey(context,
    457 			 ctx->auth_context,
    458 			 &ctx->kcred->session);
    459 
    460     kret = krb5_auth_con_generatelocalsubkey(context,
    461 					     ctx->auth_context,
    462 					     &ctx->kcred->session);
    463     if(kret) {
    464 	*minor_status = kret;
    465 	ret = GSS_S_FAILURE;
    466 	goto failure;
    467     }
    468 
    469     return GSS_S_COMPLETE;
    470 
    471 failure:
    472     if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
    473 	krb5_cc_close(context, ctx->ccache);
    474     ctx->ccache = NULL;
    475 
    476     return ret;
    477 
    478 }
    479 
    480 static OM_uint32
    481 init_auth_restart
    482 (OM_uint32 * minor_status,
    483  gsskrb5_cred cred,
    484  gsskrb5_ctx ctx,
    485  krb5_context context,
    486  OM_uint32 req_flags,
    487  const gss_channel_bindings_t input_chan_bindings,
    488  const gss_buffer_t input_token,
    489  gss_OID * actual_mech_type,
    490  gss_buffer_t output_token,
    491  OM_uint32 * ret_flags,
    492  OM_uint32 * time_rec
    493     )
    494 {
    495     OM_uint32 ret = GSS_S_FAILURE;
    496     krb5_error_code kret;
    497     krb5_flags ap_options;
    498     krb5_data outbuf;
    499     uint32_t flags;
    500     krb5_data authenticator;
    501     Checksum cksum;
    502     krb5_enctype enctype;
    503     krb5_data fwd_data, timedata;
    504     int32_t offset = 0, oldoffset = 0;
    505     uint32_t flagmask;
    506 
    507     krb5_data_zero(&outbuf);
    508     krb5_data_zero(&fwd_data);
    509 
    510     *minor_status = 0;
    511 
    512     /*
    513      * If the credential doesn't have ok-as-delegate, check if there
    514      * is a realm setting and use that.
    515      */
    516     if (!ctx->kcred->flags.b.ok_as_delegate) {
    517 	krb5_data data;
    518 
    519 	ret = krb5_cc_get_config(context, ctx->ccache, NULL,
    520 				 "realm-config", &data);
    521 	if (ret == 0) {
    522 	    /* XXX 1 is use ok-as-delegate */
    523 	    if (data.length < 1 || ((((unsigned char *)data.data)[0]) & 1) == 0)
    524 		req_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
    525 	    krb5_data_free(&data);
    526 	}
    527     }
    528 
    529     flagmask = 0;
    530 
    531     /* if we used GSS_C_DELEG_POLICY_FLAG, trust KDC */
    532     if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
    533 	&& ctx->kcred->flags.b.ok_as_delegate)
    534 	flagmask |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
    535     /* if there still is a GSS_C_DELEG_FLAG, use that */
    536     if (req_flags & GSS_C_DELEG_FLAG)
    537 	flagmask |= GSS_C_DELEG_FLAG;
    538 
    539 
    540     flags = 0;
    541     ap_options = 0;
    542     if (flagmask & GSS_C_DELEG_FLAG) {
    543 	do_delegation (context,
    544 		       ctx->deleg_auth_context,
    545 		       ctx->ccache, ctx->kcred, ctx->target,
    546 		       &fwd_data, flagmask, &flags);
    547     }
    548 
    549     if (req_flags & GSS_C_MUTUAL_FLAG) {
    550 	flags |= GSS_C_MUTUAL_FLAG;
    551 	ap_options |= AP_OPTS_MUTUAL_REQUIRED;
    552     }
    553 
    554     if (req_flags & GSS_C_REPLAY_FLAG)
    555 	flags |= GSS_C_REPLAY_FLAG;
    556     if (req_flags & GSS_C_SEQUENCE_FLAG)
    557 	flags |= GSS_C_SEQUENCE_FLAG;
    558 #if 0
    559     if (req_flags & GSS_C_ANON_FLAG)
    560 	;                               /* XXX */
    561 #endif
    562     if (req_flags & GSS_C_DCE_STYLE) {
    563 	/* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
    564 	flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG;
    565 	ap_options |= AP_OPTS_MUTUAL_REQUIRED;
    566     }
    567     if (req_flags & GSS_C_IDENTIFY_FLAG)
    568 	flags |= GSS_C_IDENTIFY_FLAG;
    569     if (req_flags & GSS_C_EXTENDED_ERROR_FLAG)
    570 	flags |= GSS_C_EXTENDED_ERROR_FLAG;
    571 
    572     if (req_flags & GSS_C_CONF_FLAG) {
    573 	flags |= GSS_C_CONF_FLAG;
    574     }
    575     if (req_flags & GSS_C_INTEG_FLAG) {
    576 	flags |= GSS_C_INTEG_FLAG;
    577     }
    578     if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) {
    579 	flags |= GSS_C_CONF_FLAG;
    580 	flags |= GSS_C_INTEG_FLAG;
    581     }
    582     flags |= GSS_C_TRANS_FLAG;
    583 
    584     if (ret_flags)
    585 	*ret_flags = flags;
    586     ctx->flags = flags;
    587     ctx->more_flags |= LOCAL;
    588 
    589     ret = _gsskrb5_create_8003_checksum (minor_status,
    590 					 input_chan_bindings,
    591 					 flags,
    592 					 &fwd_data,
    593 					 &cksum);
    594     krb5_data_free (&fwd_data);
    595     if (ret)
    596 	goto failure;
    597 
    598     enctype = ctx->auth_context->keyblock->keytype;
    599 
    600     ret = krb5_cc_get_config(context, ctx->ccache, ctx->target,
    601 			     "time-offset", &timedata);
    602     if (ret == 0) {
    603 	if (timedata.length == 4) {
    604 	    const u_char *p = timedata.data;
    605             if (p[0] < 128) {
    606                 offset = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
    607             } else {
    608                 /*
    609                  * (p[0] << 24), if p[0] > 127 -> offset is negative, but *p is
    610                  * positive, so this is overflow -- overflow we want, but UBSAN
    611                  * flags it.
    612                  *
    613                  * NOTE: We assume the platform is a twos-complement platform.
    614                  */
    615                 offset = INT32_MIN;
    616                 offset |= ((p[0] & 0x7f) <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
    617             }
    618 	}
    619 	krb5_data_free(&timedata);
    620     }
    621 
    622     if (offset) {
    623 	krb5_get_kdc_sec_offset (context, &oldoffset, NULL);
    624 	krb5_set_kdc_sec_offset (context, offset, -1);
    625     }
    626 
    627     kret = _krb5_build_authenticator(context,
    628 				     ctx->auth_context,
    629 				     enctype,
    630 				     ctx->kcred,
    631 				     &cksum,
    632 				     &authenticator,
    633 				     KRB5_KU_AP_REQ_AUTH);
    634 
    635     if (kret) {
    636 	if (offset)
    637 	    krb5_set_kdc_sec_offset (context, oldoffset, -1);
    638 	*minor_status = kret;
    639 	ret = GSS_S_FAILURE;
    640 	goto failure;
    641     }
    642 
    643     kret = krb5_build_ap_req (context,
    644 			      enctype,
    645 			      ctx->kcred,
    646 			      ap_options,
    647 			      authenticator,
    648 			      &outbuf);
    649     if (offset)
    650 	krb5_set_kdc_sec_offset (context, oldoffset, -1);
    651     if (kret) {
    652 	*minor_status = kret;
    653 	ret = GSS_S_FAILURE;
    654 	goto failure;
    655     }
    656 
    657     if (flags & GSS_C_DCE_STYLE) {
    658 	output_token->value = outbuf.data;
    659 	output_token->length = outbuf.length;
    660     } else {
    661         ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
    662 				    (u_char *)(intptr_t)"\x01\x00",
    663 				    GSS_KRB5_MECHANISM);
    664 	krb5_data_free (&outbuf);
    665 	if (ret)
    666 	    goto failure;
    667     }
    668 
    669     free_Checksum(&cksum);
    670 
    671     if (flags & GSS_C_MUTUAL_FLAG) {
    672 	ctx->state = INITIATOR_WAIT_FOR_MUTAL;
    673 	return GSS_S_CONTINUE_NEEDED;
    674     }
    675 
    676     return gsskrb5_initiator_ready(minor_status, ctx, context);
    677 failure:
    678     if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
    679 	krb5_cc_close(context, ctx->ccache);
    680     ctx->ccache = NULL;
    681 
    682     return ret;
    683 }
    684 
    685 static krb5_error_code
    686 handle_error_packet(krb5_context context,
    687 		    gsskrb5_ctx ctx,
    688 		    krb5_data indata)
    689 {
    690     krb5_error_code kret;
    691     KRB_ERROR error;
    692 
    693     kret = krb5_rd_error(context, &indata, &error);
    694     if (kret == 0) {
    695 	kret = krb5_error_from_rd_error(context, &error, NULL);
    696 
    697 	/* save the time skrew for this host */
    698 	if (kret == KRB5KRB_AP_ERR_SKEW) {
    699 	    krb5_data timedata;
    700 	    unsigned char p[4];
    701 	    int32_t t = error.stime - time(NULL);
    702 
    703 	    p[0] = (t >> 24) & 0xFF;
    704 	    p[1] = (t >> 16) & 0xFF;
    705 	    p[2] = (t >> 8)  & 0xFF;
    706 	    p[3] = (t >> 0)  & 0xFF;
    707 
    708 	    timedata.data = p;
    709 	    timedata.length = sizeof(p);
    710 
    711 	    krb5_cc_set_config(context, ctx->ccache, ctx->target,
    712 			       "time-offset", &timedata);
    713 
    714 	    if ((ctx->more_flags & RETRIED) == 0)
    715 		 ctx->state = INITIATOR_RESTART;
    716 	    ctx->more_flags |= RETRIED;
    717 	}
    718 	free_KRB_ERROR (&error);
    719     }
    720     return kret;
    721 }
    722 
    723 
    724 static OM_uint32
    725 repl_mutual
    726 (OM_uint32 * minor_status,
    727  gsskrb5_ctx ctx,
    728  krb5_context context,
    729  const gss_OID mech_type,
    730  OM_uint32 req_flags,
    731  OM_uint32 time_req,
    732  const gss_channel_bindings_t input_chan_bindings,
    733  const gss_buffer_t input_token,
    734  gss_OID * actual_mech_type,
    735  gss_buffer_t output_token,
    736  OM_uint32 * ret_flags,
    737  OM_uint32 * time_rec
    738     )
    739 {
    740     OM_uint32 ret;
    741     krb5_error_code kret;
    742     krb5_data indata;
    743     krb5_ap_rep_enc_part *repl;
    744 
    745     output_token->length = 0;
    746     output_token->value = NULL;
    747 
    748     if (actual_mech_type)
    749 	*actual_mech_type = GSS_KRB5_MECHANISM;
    750 
    751     if (IS_DCE_STYLE(ctx)) {
    752 	/* There is no OID wrapping. */
    753 	indata.length	= input_token->length;
    754 	indata.data	= input_token->value;
    755 	kret = krb5_rd_rep(context,
    756 			   ctx->auth_context,
    757 			   &indata,
    758 			   &repl);
    759 	if (kret) {
    760 	    ret = _gsskrb5_decapsulate(minor_status,
    761 				       input_token,
    762 				       &indata,
    763 				       "\x03\x00",
    764 				       GSS_KRB5_MECHANISM);
    765 	    if (ret == GSS_S_COMPLETE) {
    766 		*minor_status = handle_error_packet(context, ctx, indata);
    767 	    } else {
    768 		*minor_status = kret;
    769 	    }
    770 	    return GSS_S_FAILURE;
    771 	}
    772     } else {
    773 	ret = _gsskrb5_decapsulate (minor_status,
    774 				    input_token,
    775 				    &indata,
    776 				    "\x02\x00",
    777 				    GSS_KRB5_MECHANISM);
    778 	if (ret == GSS_S_DEFECTIVE_TOKEN) {
    779 	    /* check if there is an error token sent instead */
    780 	    ret = _gsskrb5_decapsulate (minor_status,
    781 					input_token,
    782 					&indata,
    783 					"\x03\x00",
    784 					GSS_KRB5_MECHANISM);
    785 	    if (ret == GSS_S_COMPLETE) {
    786 		*minor_status = handle_error_packet(context, ctx, indata);
    787 		return GSS_S_FAILURE;
    788 	    }
    789 	}
    790 	kret = krb5_rd_rep (context,
    791 			    ctx->auth_context,
    792 			    &indata,
    793 			    &repl);
    794 	if (kret) {
    795 	    *minor_status = kret;
    796 	    return GSS_S_FAILURE;
    797 	}
    798     }
    799 
    800     krb5_free_ap_rep_enc_part (context,
    801 			       repl);
    802 
    803     *minor_status = 0;
    804     if (time_rec)
    805         _gsskrb5_lifetime_left(minor_status,
    806                                context,
    807                                ctx->endtime,
    808                                time_rec);
    809     if (ret_flags)
    810 	*ret_flags = ctx->flags;
    811 
    812     if (req_flags & GSS_C_DCE_STYLE) {
    813 	int32_t local_seq, remote_seq;
    814 	krb5_data outbuf;
    815 
    816 	/*
    817 	 * So DCE_STYLE is strange. The client echos the seq number
    818 	 * that the server used in the server's mk_rep in its own
    819 	 * mk_rep(). After when done, it resets to it's own seq number
    820 	 * for the gss_wrap calls.
    821 	 */
    822 
    823 	krb5_auth_con_getremoteseqnumber(context, ctx->auth_context, &remote_seq);
    824 	krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &local_seq);
    825 	krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, remote_seq);
    826 
    827 	kret = krb5_mk_rep(context, ctx->auth_context, &outbuf);
    828 	if (kret) {
    829 	    *minor_status = kret;
    830 	    return GSS_S_FAILURE;
    831 	}
    832 
    833 	/* reset local seq number */
    834 	krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, local_seq);
    835 
    836 	output_token->length = outbuf.length;
    837 	output_token->value  = outbuf.data;
    838     }
    839 
    840     return gsskrb5_initiator_ready(minor_status, ctx, context);
    841 }
    842 
    843 /*
    844  * gss_init_sec_context
    845  */
    846 
    847 OM_uint32 GSSAPI_CALLCONV _gsskrb5_init_sec_context
    848 (OM_uint32 * minor_status,
    849  gss_const_cred_id_t cred_handle,
    850  gss_ctx_id_t * context_handle,
    851  gss_const_name_t target_name,
    852  const gss_OID mech_type,
    853  OM_uint32 req_flags,
    854  OM_uint32 time_req,
    855  const gss_channel_bindings_t input_chan_bindings,
    856  const gss_buffer_t input_token,
    857  gss_OID * actual_mech_type,
    858  gss_buffer_t output_token,
    859  OM_uint32 * ret_flags,
    860  OM_uint32 * time_rec
    861     )
    862 {
    863     krb5_context context;
    864     gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
    865     gsskrb5_ctx ctx;
    866     OM_uint32 ret;
    867 
    868     GSSAPI_KRB5_INIT (&context);
    869 
    870     output_token->length = 0;
    871     output_token->value  = NULL;
    872 
    873     if (context_handle == NULL) {
    874 	*minor_status = 0;
    875 	return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
    876     }
    877 
    878     if (ret_flags)
    879 	*ret_flags = 0;
    880     if (time_rec)
    881 	*time_rec = 0;
    882 
    883     if (target_name == GSS_C_NO_NAME) {
    884 	if (actual_mech_type)
    885 	    *actual_mech_type = GSS_C_NO_OID;
    886 	*minor_status = 0;
    887 	return GSS_S_BAD_NAME;
    888     }
    889 
    890     if (mech_type != GSS_C_NO_OID &&
    891 	!gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
    892 	return GSS_S_BAD_MECH;
    893 
    894     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
    895 	OM_uint32 ret1;
    896 
    897 	if (*context_handle != GSS_C_NO_CONTEXT) {
    898 	    *minor_status = 0;
    899 	    return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
    900 	}
    901 
    902 	ret1 = _gsskrb5_create_ctx(minor_status,
    903 				  context_handle,
    904 				  context,
    905 				  input_chan_bindings,
    906 				  INITIATOR_START);
    907 	if (ret1)
    908 	    return ret1;
    909     }
    910 
    911     if (*context_handle == GSS_C_NO_CONTEXT) {
    912 	*minor_status = 0;
    913 	return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
    914     }
    915 
    916     ctx = (gsskrb5_ctx) *context_handle;
    917 
    918     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
    919 
    920  again:
    921     switch (ctx->state) {
    922     case INITIATOR_START:
    923 	ret = init_auth(minor_status,
    924 			cred,
    925 			ctx,
    926 			context,
    927 			target_name,
    928 			mech_type,
    929 			req_flags,
    930 			time_req,
    931 			input_token,
    932 			actual_mech_type,
    933 			output_token,
    934 			ret_flags,
    935 			time_rec);
    936 	if (ret != GSS_S_COMPLETE)
    937 	    break;
    938 	/* FALLTHROUGH */
    939     case INITIATOR_RESTART:
    940 	ret = init_auth_restart(minor_status,
    941 				cred,
    942 				ctx,
    943 				context,
    944 				req_flags,
    945 				input_chan_bindings,
    946 				input_token,
    947 				actual_mech_type,
    948 				output_token,
    949 				ret_flags,
    950 				time_rec);
    951 	break;
    952     case INITIATOR_WAIT_FOR_MUTAL:
    953 	ret = repl_mutual(minor_status,
    954 			  ctx,
    955 			  context,
    956 			  mech_type,
    957 			  req_flags,
    958 			  time_req,
    959 			  input_chan_bindings,
    960 			  input_token,
    961 			  actual_mech_type,
    962 			  output_token,
    963 			  ret_flags,
    964 			  time_rec);
    965 	if (ctx->state == INITIATOR_RESTART)
    966 	    goto again;
    967 	break;
    968     case INITIATOR_READY:
    969 	/*
    970 	 * If we get there, the caller have called
    971 	 * gss_init_sec_context() one time too many.
    972 	 */
    973 	_gsskrb5_set_status(EINVAL, "init_sec_context "
    974 			    "called one time too many");
    975 	*minor_status = EINVAL;
    976 	ret = GSS_S_BAD_STATUS;
    977 	break;
    978     default:
    979 	_gsskrb5_set_status(EINVAL, "init_sec_context "
    980 			    "invalid state %d for client",
    981 			    (int)ctx->state);
    982 	*minor_status = EINVAL;
    983 	ret = GSS_S_BAD_STATUS;
    984 	break;
    985     }
    986     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
    987 
    988     /* destroy context in case of error */
    989     if (GSS_ERROR(ret)) {
    990 	OM_uint32 min2;
    991 	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
    992     }
    993 
    994     return ret;
    995 
    996 }
    997