text: /*
   * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
   * (Royal Institute of Technology, Stockholm, Sweden).
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   *
   * 3. Neither the name of the Institute nor the names of its contributors
   *    may be used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   */
  
  #include "gsskrb5_locl.h"
  
  HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
  krb5_keytab _gsskrb5_keytab;
  
  OM_uint32
  _gsskrb5_register_acceptor_identity (const char *identity)
  {
      krb5_context context;
      krb5_error_code ret;
  
      ret = _gsskrb5_init(&context);
      if(ret)
  	return GSS_S_FAILURE;
  
      HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
  
      if(_gsskrb5_keytab != NULL) {
  	krb5_kt_close(context, _gsskrb5_keytab);
  	_gsskrb5_keytab = NULL;
      }
      if (identity == NULL) {
  	ret = krb5_kt_default(context, &_gsskrb5_keytab);
      } else {
  	char *p;
  
  	asprintf(&p, "FILE:%s", identity);
  	if(p == NULL) {
  	    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
  	    return GSS_S_FAILURE;
  	}
  	ret = krb5_kt_resolve(context, p, &_gsskrb5_keytab);
  	free(p);
      }
      HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
      if(ret)
  	return GSS_S_FAILURE;
      return GSS_S_COMPLETE;
  }
  
  void
  _gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor)
  {
      krb5_error_code ret;
      krb5_keyblock *key;
  
      if (acceptor) {
  	if (ctx->auth_context->local_subkey)
  	    key = ctx->auth_context->local_subkey;
  	else
  	    key = ctx->auth_context->remote_subkey;
      } else {
  	if (ctx->auth_context->remote_subkey)
  	    key = ctx->auth_context->remote_subkey;
  	else
  	    key = ctx->auth_context->local_subkey;
      }
      if (key == NULL)
  	key = ctx->auth_context->keyblock;
  
      if (key == NULL)
  	return;
  	
      switch (key->keytype) {
      case ETYPE_DES_CBC_CRC:
      case ETYPE_DES_CBC_MD4:
      case ETYPE_DES_CBC_MD5:
      case ETYPE_DES3_CBC_MD5:
      case ETYPE_DES3_CBC_SHA1:
      case ETYPE_ARCFOUR_HMAC_MD5:
      case ETYPE_ARCFOUR_HMAC_MD5_56:
  	break;
      default :
          ctx->more_flags |= IS_CFX;
  
  	if ((acceptor && ctx->auth_context->local_subkey) ||
  	    (!acceptor && ctx->auth_context->remote_subkey))
  	    ctx->more_flags |= ACCEPTOR_SUBKEY;
  	break;
      }
      if (ctx->crypto)
          krb5_crypto_destroy(context, ctx->crypto);
      ret = krb5_crypto_init(context, key, 0, &ctx->crypto);
  }
  
  
  static OM_uint32
  gsskrb5_accept_delegated_token
  (OM_uint32 * minor_status,
   gsskrb5_ctx ctx,
   krb5_context context,
   gss_cred_id_t * delegated_cred_handle
      )
  {
      krb5_ccache ccache = NULL;
      krb5_error_code kret;
      int32_t ac_flags, ret = GSS_S_COMPLETE;
  
      *minor_status = 0;
  
      /* XXX Create a new delegated_cred_handle? */
      if (delegated_cred_handle == NULL) {
  	kret = krb5_cc_default (context, &ccache);
      } else {
  	*delegated_cred_handle = NULL;
  	kret = krb5_cc_new_unique (context, krb5_cc_type_memory,
  				   NULL, &ccache);
      }
      if (kret) {
  	ctx->flags &= ~GSS_C_DELEG_FLAG;
  	goto out;
      }
  
      kret = krb5_cc_initialize(context, ccache, ctx->source);
      if (kret) {
  	ctx->flags &= ~GSS_C_DELEG_FLAG;
  	goto out;
      }
  
      krb5_auth_con_removeflags(context,
  			      ctx->auth_context,
  			      KRB5_AUTH_CONTEXT_DO_TIME,
  			      &ac_flags);
      kret = krb5_rd_cred2(context,
  			 ctx->auth_context,
  			 ccache,
  			 &ctx->fwd_data);
      krb5_auth_con_setflags(context,
  			   ctx->auth_context,
  			   ac_flags);
      if (kret) {
  	ctx->flags &= ~GSS_C_DELEG_FLAG;
  	ret = GSS_S_FAILURE;
  	*minor_status = kret;
  	goto out;
      }
  
      if (delegated_cred_handle) {
  	gsskrb5_cred handle;
  	
  	ret = _gsskrb5_krb5_import_cred(minor_status,
  					ccache,
  					NULL,
  					NULL,
  					delegated_cred_handle);
  	if (ret != GSS_S_COMPLETE)
  	    goto out;
  
  	handle = (gsskrb5_cred) *delegated_cred_handle;
  
  	handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
  	krb5_cc_close(context, ccache);
  	ccache = NULL;
      }
  
  out:
      if (ccache) {
  	/* Don't destroy the default cred cache */
  	if (delegated_cred_handle == NULL)
  	    krb5_cc_close(context, ccache);
  	else
  	    krb5_cc_destroy(context, ccache);
      }
      return ret;
  }
  
  static OM_uint32
  gsskrb5_acceptor_ready(OM_uint32 * minor_status,
  		       gsskrb5_ctx ctx,
  		       krb5_context context,
  		       gss_cred_id_t *delegated_cred_handle)
  {
      OM_uint32 ret;
      int32_t seq_number;
      int is_cfx = 0;
  
      krb5_auth_getremoteseqnumber (context,
  				  ctx->auth_context,
  				  &seq_number);
  
      _gsskrb5i_is_cfx(context, ctx, 1);
      is_cfx = (ctx->more_flags & IS_CFX);
  
      ret = _gssapi_msg_order_create(minor_status,
  				   &ctx->order,
  				   _gssapi_msg_order_f(ctx->flags),
  				   seq_number, 0, is_cfx);
      if (ret)
  	return ret;
  
      /*
       * If requested, set local sequence num to remote sequence if this
       * isn't a mutual authentication context
       */
      if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
  	krb5_auth_con_setlocalseqnumber(context,
  					ctx->auth_context,
  					seq_number);
      }
  
      /*
       * We should handle the delegation ticket, in case it's there
       */
      if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
  	ret = gsskrb5_accept_delegated_token(minor_status,
  					     ctx,
  					     context,
  					     delegated_cred_handle);
  	if (ret)
  	    return ret;
      } else {
  	/* Well, looks like it wasn't there after all */
  	ctx->flags &= ~GSS_C_DELEG_FLAG;
      }
  
      ctx->state = ACCEPTOR_READY;
      ctx->more_flags |= OPEN;
  
      return GSS_S_COMPLETE;
  }
  
  static OM_uint32
  send_error_token(OM_uint32 *minor_status,
  		 krb5_context context,
  		 krb5_error_code kret,
  		 krb5_principal server,
  		 krb5_data *indata,
  		 gss_buffer_t output_token)
  {
      krb5_principal ap_req_server = NULL;
      krb5_error_code ret;
      krb5_data outbuf;
  
      /* build server from request if the acceptor had not selected one */
      if (server == NULL) {
  	AP_REQ ap_req;
  
  	ret = krb5_decode_ap_req(context, indata, &ap_req);
  	if (ret) {
  	    *minor_status = ret;
  	    return GSS_S_FAILURE;
  	}
  	ret = _krb5_principalname2krb5_principal(context,
  						  &ap_req_server,
  						  ap_req.ticket.sname,
  						  ap_req.ticket.realm);
  	free_AP_REQ(&ap_req);
  	if (ret) {
  	    *minor_status = ret;
  	    return GSS_S_FAILURE;
  	}
  	server = ap_req_server;
      }
  
      ret = krb5_mk_error(context, kret, NULL, NULL, NULL,
  			server, NULL, NULL, &outbuf);
      if (ap_req_server)
  	krb5_free_principal(context, ap_req_server);
      if (ret) {
  	*minor_status = ret;
  	return GSS_S_FAILURE;
      }
  
      ret = _gsskrb5_encapsulate(minor_status,
  			       &outbuf,
  			       output_token,
  			       "\x03\x00",
  			       GSS_KRB5_MECHANISM);
      krb5_data_free (&outbuf);
      if (ret)
  	return ret;
  
      *minor_status = 0;
      return GSS_S_CONTINUE_NEEDED;
  }
  
  
  static OM_uint32
  gsskrb5_acceptor_start(OM_uint32 * minor_status,
  		       gsskrb5_ctx ctx,
  		       krb5_context context,
  		       const gss_cred_id_t acceptor_cred_handle,
  		       const gss_buffer_t input_token_buffer,
  		       const gss_channel_bindings_t input_chan_bindings,
  		       gss_name_t * src_name,
  		       gss_OID * mech_type,
  		       gss_buffer_t output_token,
  		       OM_uint32 * ret_flags,
  		       OM_uint32 * time_rec,
  		       gss_cred_id_t * delegated_cred_handle)
  {
      krb5_error_code kret;
      OM_uint32 ret = GSS_S_COMPLETE;
      krb5_data indata;
      krb5_flags ap_options;
      krb5_keytab keytab = NULL;
      int is_cfx = 0;
      const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
  
      /*
       * We may, or may not, have an escapsulation.
       */
      ret = _gsskrb5_decapsulate (minor_status,
  				input_token_buffer,
  				&indata,
  				"\x01\x00",
  				GSS_KRB5_MECHANISM);
  
      if (ret) {
  	/* Assume that there is no OID wrapping. */
  	indata.length	= input_token_buffer->length;
  	indata.data	= input_token_buffer->value;
      }
  
      /*
       * We need to get our keytab
       */
      if (acceptor_cred == NULL) {
  	if (_gsskrb5_keytab != NULL)
  	    keytab = _gsskrb5_keytab;
      } else if (acceptor_cred->keytab != NULL) {
  	keytab = acceptor_cred->keytab;
      }
  
      /*
       * We need to check the ticket and create the AP-REP packet
       */
  
      {
  	krb5_rd_req_in_ctx in = NULL;
  	krb5_rd_req_out_ctx out = NULL;
  	krb5_principal server = NULL;
  
  	if (acceptor_cred)
  	    server = acceptor_cred->principal;
  
  	kret = krb5_rd_req_in_ctx_alloc(context, &in);
  	if (kret == 0)
  	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
  	if (kret) {
  	    if (in)
  		krb5_rd_req_in_ctx_free(context, in);
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
  
  	kret = krb5_rd_req_ctx(context,
  			       &ctx->auth_context,
  			       &indata,
  			       server,
  			       in, &out);
  	krb5_rd_req_in_ctx_free(context, in);
  	if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) {
  	    /*
  	     * No reply in non-MUTUAL mode, but we don't know that its
  	     * non-MUTUAL mode yet, thats inside the 8003 checksum, so
  	     * lets only send the error token on clock skew, that
  	     * limit when send error token for non-MUTUAL.
  	     */
  	    return send_error_token(minor_status, context, kret,
  				    server, &indata, output_token);
  	} else if (kret) {
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
  
  	/*
  	 * we need to remember some data on the context_handle.
  	 */
  	kret = krb5_rd_req_out_get_ap_req_options(context, out,
  						  &ap_options);
  	if (kret == 0)
  	    kret = krb5_rd_req_out_get_ticket(context, out,
  					      &ctx->ticket);
  	if (kret == 0)
  	    kret = krb5_rd_req_out_get_keyblock(context, out,
  						&ctx->service_keyblock);
  	ctx->lifetime = ctx->ticket->ticket.endtime;
  
  	krb5_rd_req_out_ctx_free(context, out);
  	if (kret) {
  	    ret = GSS_S_FAILURE;
  	    *minor_status = kret;
  	    return ret;
  	}
      }
  
  
      /*
       * We need to copy the principal names to the context and the
       * calling layer.
       */
      kret = krb5_copy_principal(context,
  			       ctx->ticket->client,
  			       &ctx->source);
      if (kret) {
  	ret = GSS_S_FAILURE;
  	*minor_status = kret;
      }
  
      kret = krb5_copy_principal(context,
  			       ctx->ticket->server,
  			       &ctx->target);
      if (kret) {
  	ret = GSS_S_FAILURE;
  	*minor_status = kret;
  	return ret;
      }
  
      /*
       * We need to setup some compat stuff, this assumes that
       * context_handle->target is already set.
       */
      ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
      if (ret)
  	return ret;
  
      if (src_name != NULL) {
  	kret = krb5_copy_principal (context,
  				    ctx->ticket->client,
  				    (gsskrb5_name*)src_name);
  	if (kret) {
  	    ret = GSS_S_FAILURE;
  	    *minor_status = kret;
  	    return ret;
  	}
      }
  
      /*
       * We need to get the flags out of the 8003 checksum.
       */
      {
  	krb5_authenticator authenticator;
  
  	kret = krb5_auth_con_getauthenticator(context,
  					      ctx->auth_context,
  					      &authenticator);
  	if(kret) {
  	    ret = GSS_S_FAILURE;
  	    *minor_status = kret;
  	    return ret;
  	}
  
          if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
              ret = _gsskrb5_verify_8003_checksum(minor_status,
  						input_chan_bindings,
  						authenticator->cksum,
  						&ctx->flags,
  						&ctx->fwd_data);
  
  	    krb5_free_authenticator(context, &authenticator);
  	    if (ret) {
  		return ret;
  	    }
          } else {
  	    krb5_crypto crypto;
  
  	    kret = krb5_crypto_init(context,
  				    ctx->auth_context->keyblock,
  				    0, &crypto);
  	    if(kret) {
  		krb5_free_authenticator(context, &authenticator);
  
  		ret = GSS_S_FAILURE;
  		*minor_status = kret;
  		return ret;
  	    }
  
  	    /*
  	     * Windows accepts Samba3's use of a kerberos, rather than
  	     * GSSAPI checksum here
  	     */
  
  	    kret = krb5_verify_checksum(context,
  					crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
  					authenticator->cksum);
  	    krb5_free_authenticator(context, &authenticator);
  	    krb5_crypto_destroy(context, crypto);
  
  	    if(kret) {
  		ret = GSS_S_BAD_SIG;
  		*minor_status = kret;
  		return ret;
  	    }
  
  	    /*
  	     * Samba style get some flags (but not DCE-STYLE), use
  	     * ap_options to guess the mutual flag.
  	     */
   	    ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
  	    if (ap_options & AP_OPTS_MUTUAL_REQUIRED)
  		ctx->flags |= GSS_C_MUTUAL_FLAG;
          }
      }
  
      if(ctx->flags & GSS_C_MUTUAL_FLAG) {
  	krb5_data outbuf;
  	int use_subkey = 0;
  	
  	_gsskrb5i_is_cfx(context, ctx, 1);
  	is_cfx = (ctx->more_flags & IS_CFX);
  	
  	if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) {
  	    use_subkey = 1;
  	} else {
  	    krb5_keyblock *rkey;
  
  	    /*
  	     * If there is a initiator subkey, copy that to acceptor
  	     * subkey to match Windows behavior
  	     */
  	    kret = krb5_auth_con_getremotesubkey(context,
  						 ctx->auth_context,
  						 &rkey);
  	    if (kret == 0) {
  		kret = krb5_auth_con_setlocalsubkey(context,
  						    ctx->auth_context,
  						    rkey);
  		if (kret == 0)
  		    use_subkey = 1;
  		krb5_free_keyblock(context, rkey);
  	    }
  	}
  	if (use_subkey) {
  	    ctx->more_flags |= ACCEPTOR_SUBKEY;
  	    krb5_auth_con_addflags(context, ctx->auth_context,
  				   KRB5_AUTH_CONTEXT_USE_SUBKEY,
  				   NULL);
  	}
  	
  	kret = krb5_mk_rep(context,
  			   ctx->auth_context,
  			   &outbuf);
  	if (kret) {
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
  	
  	if (IS_DCE_STYLE(ctx)) {
  	    output_token->length = outbuf.length;
  	    output_token->value = outbuf.data;
  	} else {
  	    ret = _gsskrb5_encapsulate(minor_status,
  				       &outbuf,
  				       output_token,
  				       "\x02\x00",
  				       GSS_KRB5_MECHANISM);
  	    krb5_data_free (&outbuf);
  	    if (ret)
  		return ret;
  	}
      }
  
      ctx->flags |= GSS_C_TRANS_FLAG;
  
      /* Remember the flags */
  
      ctx->lifetime = ctx->ticket->ticket.endtime;
      ctx->more_flags |= OPEN;
  
      if (mech_type)
  	*mech_type = GSS_KRB5_MECHANISM;
  
      if (time_rec) {
  	ret = _gsskrb5_lifetime_left(minor_status,
  				     context,
  				     ctx->lifetime,
  				     time_rec);
  	if (ret) {
  	    return ret;
  	}
      }
  
      /*
       * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
       * the client.
       */
      if (IS_DCE_STYLE(ctx)) {
  	/*
  	 * Return flags to caller, but we haven't processed
  	 * delgations yet
  	 */
  	if (ret_flags)
  	    *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
  
  	ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
  	return GSS_S_CONTINUE_NEEDED;
      }
  
      ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
  				 delegated_cred_handle);
  
      if (ret_flags)
  	*ret_flags = ctx->flags;
  
      return ret;
  }
  
  static OM_uint32
  acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
  			   gsskrb5_ctx ctx,
  			   krb5_context context,
  			   const gss_cred_id_t acceptor_cred_handle,
  			   const gss_buffer_t input_token_buffer,
  			   const gss_channel_bindings_t input_chan_bindings,
  			   gss_name_t * src_name,
  			   gss_OID * mech_type,
  			   gss_buffer_t output_token,
  			   OM_uint32 * ret_flags,
  			   OM_uint32 * time_rec,
  			   gss_cred_id_t * delegated_cred_handle)
  {
      OM_uint32 ret;
      krb5_error_code kret;
      krb5_data inbuf;
      int32_t r_seq_number, l_seq_number;
  	
      /*
       * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
       */
  
      inbuf.length = input_token_buffer->length;
      inbuf.data = input_token_buffer->value;
  
      /*
       * We need to remeber the old remote seq_number, then check if the
       * client has replied with our local seq_number, and then reset
       * the remote seq_number to the old value
       */
      {
  	kret = krb5_auth_con_getlocalseqnumber(context,
  					       ctx->auth_context,
  					       &l_seq_number);
  	if (kret) {
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
  
  	kret = krb5_auth_getremoteseqnumber(context,
  					    ctx->auth_context,
  					    &r_seq_number);
  	if (kret) {
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
  
  	kret = krb5_auth_con_setremoteseqnumber(context,
  						ctx->auth_context,
  						l_seq_number);
  	if (kret) {
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
      }
  
      /*
       * We need to verify the AP_REP, but we need to flag that this is
       * DCE_STYLE, so don't check the timestamps this time, but put the
       * flag DO_TIME back afterward.
      */
      {
  	krb5_ap_rep_enc_part *repl;
  	int32_t auth_flags;
  		
  	krb5_auth_con_removeflags(context,
  				  ctx->auth_context,
  				  KRB5_AUTH_CONTEXT_DO_TIME,
  				  &auth_flags);
  
  	kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
  	if (kret) {
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
  	krb5_free_ap_rep_enc_part(context, repl);
  	krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
      }
  
      /* We need to check the liftime */
      {
  	OM_uint32 lifetime_rec;
  
  	ret = _gsskrb5_lifetime_left(minor_status,
  				     context,
  				     ctx->lifetime,
  				     &lifetime_rec);
  	if (ret) {
  	    return ret;
  	}
  	if (lifetime_rec == 0) {
  	    return GSS_S_CONTEXT_EXPIRED;
  	}
  	
  	if (time_rec) *time_rec = lifetime_rec;
      }
  
      /* We need to give the caller the flags which are in use */
      if (ret_flags) *ret_flags = ctx->flags;
  
      if (src_name) {
  	kret = krb5_copy_principal(context,
  				   ctx->source,
  				   (gsskrb5_name*)src_name);
  	if (kret) {
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
      }
  
      /*
       * After the krb5_rd_rep() the remote and local seq_number should
       * be the same, because the client just replies the seq_number
       * from our AP-REP in its AP-REP, but then the client uses the
       * seq_number from its AP-REQ for GSS_wrap()
       */
      {
  	int32_t tmp_r_seq_number, tmp_l_seq_number;
  
  	kret = krb5_auth_getremoteseqnumber(context,
  					    ctx->auth_context,
  					    &tmp_r_seq_number);
  	if (kret) {
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
  
  	kret = krb5_auth_con_getlocalseqnumber(context,
  					       ctx->auth_context,
  					       &tmp_l_seq_number);
  	if (kret) {
  
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
  
  	/*
  	 * Here we check if the client has responsed with our local seq_number,
  	 */
  	if (tmp_r_seq_number != tmp_l_seq_number) {
  	    return GSS_S_UNSEQ_TOKEN;
  	}
      }
  
      /*
       * We need to reset the remote seq_number, because the client will use,
       * the old one for the GSS_wrap() calls
       */
      {
  	kret = krb5_auth_con_setremoteseqnumber(context,
  						ctx->auth_context,
  						r_seq_number);	
  	if (kret) {
  	    *minor_status = kret;
  	    return GSS_S_FAILURE;
  	}
      }
  
      return gsskrb5_acceptor_ready(minor_status, ctx, context,
  				  delegated_cred_handle);
  }
  
  
  OM_uint32
  _gsskrb5_accept_sec_context(OM_uint32 * minor_status,
  			    gss_ctx_id_t * context_handle,
  			    const gss_cred_id_t acceptor_cred_handle,
  			    const gss_buffer_t input_token_buffer,
  			    const gss_channel_bindings_t input_chan_bindings,
  			    gss_name_t * src_name,
  			    gss_OID * mech_type,
  			    gss_buffer_t output_token,
  			    OM_uint32 * ret_flags,
  			    OM_uint32 * time_rec,
  			    gss_cred_id_t * delegated_cred_handle)
  {
      krb5_context context;
      OM_uint32 ret;
      gsskrb5_ctx ctx;
  
      GSSAPI_KRB5_INIT(&context);
  
      output_token->length = 0;
      output_token->value = NULL;
  
      if (src_name != NULL)
  	*src_name = NULL;
      if (mech_type)
  	*mech_type = GSS_KRB5_MECHANISM;
  
      if (*context_handle == GSS_C_NO_CONTEXT) {
  	ret = _gsskrb5_create_ctx(minor_status,
  				  context_handle,
  				  context,
  				  input_chan_bindings,
  				  ACCEPTOR_START);
  	if (ret)
  	    return ret;
      }
  
      ctx = (gsskrb5_ctx)*context_handle;
  
  
      /*
       * TODO: check the channel_bindings
       * (above just sets them to krb5 layer)
       */
  
      HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
  
      switch (ctx->state) {
      case ACCEPTOR_START:
  	ret = gsskrb5_acceptor_start(minor_status,
  				     ctx,
  				     context,
  				     acceptor_cred_handle,
  				     input_token_buffer,
  				     input_chan_bindings,
  				     src_name,
  				     mech_type,
  				     output_token,
  				     ret_flags,
  				     time_rec,
  				     delegated_cred_handle);
  	break;
      case ACCEPTOR_WAIT_FOR_DCESTYLE:
  	ret = acceptor_wait_for_dcestyle(minor_status,
  					 ctx,
  					 context,
  					 acceptor_cred_handle,
  					 input_token_buffer,
  					 input_chan_bindings,
  					 src_name,
  					 mech_type,
  					 output_token,
  					 ret_flags,
  					 time_rec,
  					 delegated_cred_handle);
  	break;
      case ACCEPTOR_READY:
  	/*
  	 * If we get there, the caller have called
  	 * gss_accept_sec_context() one time too many.
  	 */
  	ret =  GSS_S_BAD_STATUS;
  	break;
      default:
  	/* TODO: is this correct here? --metze */
  	ret =  GSS_S_BAD_STATUS;
  	break;
      }
  
      HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
  
      if (GSS_ERROR(ret)) {
  	OM_uint32 min2;
  	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
      }
  
      return ret;
  }
  