put_negResult(unsigned char **buf_out, OM_uint32 negResult,
	      unsigned int buflen)
{
	if (buflen < 3)
		return (-1);
	*(*buf_out)++ = ENUMERATED;
	*(*buf_out)++ = ENUMERATION_LENGTH;
	*(*buf_out)++ = (unsigned char) negResult;
	return (0);
}

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);


g_make_token_header(gss_OID_const mech,
		    unsigned int body_size,
		    unsigned char **buf,
		    unsigned int totallen)
{
	int ret = 0;
	unsigned int hdrsize;
	unsigned char *p = *buf;

	hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;

	*(*buf)++ = HEADER_ID;
	if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
		return (ret);

	*(*buf)++ = MECH_OID;
	if ((ret = gssint_put_der_length(mech->length, buf,
			    totallen - (int)(p - *buf))))
		return (ret);
	TWRITE_STR(*buf, mech->elements, mech->length);
	return (0);
}

Fix null deref in SPNEGO acceptor [CVE-2014-4344]

When processing a continuation token, acc_ctx_cont was dereferencing
the initial byte of the token without checking the length.  This could
result in a null dereference.

CVE-2014-4344:

In MIT krb5 1.5 and newer, an unauthenticated or partially
authenticated remote attacker can cause a NULL dereference and
application crash during a SPNEGO negotiation by sending an empty
token as the second or later context token from initiator to acceptor.
The attacker must provide at least one valid context token in the
security context negotiation before sending the empty token.  This can
be done by an unauthenticated attacker by forcing SPNEGO to
renegotiate the underlying mechanism, or by using IAKERB to wrap an
unauthenticated AS-REQ as the first token.

    CVSSv2 Vector: AV:N/AC:L/Au:N/C:N/I:N/A:C/E:POC/RL:OF/RC:C

[kaduk@mit.edu: CVE summary, CVSSv2 vector]

ticket: 7970 (new)
subject: NULL dereference in SPNEGO acceptor for continuation tokens [CVE-2014-4344]
target_version: 1.12.2
tags: pullup

typedef const gss_OID_desc *gss_OID_const;

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static int
g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);

static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
			int,
			gss_buffer_t,
			OM_uint32, gss_buffer_t, send_token_flag,
			gss_buffer_t);

static int
make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
			gss_buffer_t, send_token_flag,
			gss_buffer_t);

static OM_uint32
get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
		 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);

static int
is_kerb_mech(gss_OID oid);

static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);

static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);

int gss_krb5int_lib_init(void);


acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
		 spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
		 gss_OID *mech_type, gss_buffer_t mechtok_out,
		 OM_uint32 *ret_flags, OM_uint32 *time_rec,
		 gss_cred_id_t *delegated_cred_handle,
		 OM_uint32 *negState, send_token_flag *tokflag)
{
	OM_uint32 ret;
	gss_OID_desc mechoid;
	gss_cred_id_t mcred;

	if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
		/*
		 * mechoid is an alias; don't free it.
		 */
		ret = gssint_get_mech_type(&mechoid, mechtok_in);
		if (ret != GSS_S_COMPLETE) {
			*tokflag = NO_TOKEN_SEND;
			return ret;
		}
		ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
				      negState, tokflag);
		if (ret != GSS_S_COMPLETE)
			return ret;
	}

	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
	ret = gss_accept_sec_context(minor_status,
				     &sc->ctx_handle,
				     mcred,
				     mechtok_in,
				     GSS_C_NO_CHANNEL_BINDINGS,
				     &sc->internal_name,
				     mech_type,
				     mechtok_out,
				     &sc->ctx_flags,
				     time_rec,
				     delegated_cred_handle);
	if (ret == GSS_S_COMPLETE) {
#ifdef MS_BUG_TEST
		/*
		 * Force MIC to be not required even if we previously
		 * requested a MIC.
		 */
		char *envstr = getenv("MS_FORCE_NO_MIC");

		if (envstr != NULL && strcmp(envstr, "1") == 0 &&
		    !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
		    sc->mic_reqd) {

			sc->mic_reqd = 0;
		}
#endif
		sc->mech_complete = 1;
		if (ret_flags != NULL)
			*ret_flags = sc->ctx_flags;

		if (!sc->mic_reqd ||
		    !(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
			/* No MIC exchange required, so we're done. */
			*negState = ACCEPT_COMPLETE;
			ret = GSS_S_COMPLETE;
		} else {
			/* handle_mic will decide if we're done. */
			ret = GSS_S_CONTINUE_NEEDED;
		}
	} else if (ret != GSS_S_CONTINUE_NEEDED) {
		*negState = REJECT;
		*tokflag = ERROR_TOKEN_SEND;
	}
	return ret;
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

typedef const gss_OID_desc *gss_OID_const;

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);


spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
				 gss_name_t name,
				 gss_buffer_t attr)
{
	OM_uint32 ret;
	ret = gss_delete_name_attribute(minor_status,
					name,
					attr);
	return (ret);
}

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


spnego_gss_verify_mic(
		OM_uint32 *minor_status,
		const gss_ctx_id_t context_handle,
		const gss_buffer_t msg_buffer,
		const gss_buffer_t token_buffer,
		gss_qop_t *qop_state)
{
	OM_uint32 ret;
	ret = gss_verify_mic(minor_status,
			    context_handle,
			    msg_buffer,
			    token_buffer,
			    qop_state);
	return (ret);
}

typedef const gss_OID_desc *gss_OID_const;

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


spnego_gss_import_cred(OM_uint32 *minor_status,
		       gss_buffer_t token,
		       gss_cred_id_t *cred_handle)
{
	OM_uint32 ret;
	spnego_gss_cred_id_t spcred;
	gss_cred_id_t mcred;

	ret = gss_import_cred(minor_status, token, &mcred);
	if (GSS_ERROR(ret))
		return (ret);
	spcred = malloc(sizeof(*spcred));
	if (spcred == NULL) {
		gss_release_cred(minor_status, &mcred);
		*minor_status = ENOMEM;
		return (GSS_S_FAILURE);
	}
	spcred->mcred = mcred;
	spcred->neg_mechs = GSS_C_NULL_OID_SET;
	*cred_handle = (gss_cred_id_t)spcred;
	return (ret);
}


make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
			  gss_buffer_t data, gss_buffer_t mechListMIC,
			  send_token_flag sendtoken,
			  gss_buffer_t outbuf)
{
	unsigned int tlen = 0;
	unsigned int ret = 0;
	unsigned int NegTokenTargSize = 0;
	unsigned int NegTokenSize = 0;
	unsigned int rspTokenSize = 0;
	unsigned int micTokenSize = 0;
	unsigned int dataLen = 0;
	unsigned char *t;
	unsigned char *ptr;

	if (outbuf == GSS_C_NO_BUFFER)
		return (GSS_S_DEFECTIVE_TOKEN);
	if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
	    return (GSS_S_DEFECTIVE_TOKEN);

	outbuf->length = 0;
	outbuf->value = NULL;

	/*
	 * ASN.1 encoding of the negResult
	 * ENUMERATED type is 3 bytes
	 *  ENUMERATED TAG, Length, Value,
	 * Plus 2 bytes for the CONTEXT id and length.
	 */
	dataLen = 5;

	/*
	 * calculate data length
	 *
	 * If this is the initial token, include length of
	 * mech_type and the negotiation result fields.
	 */
	if (sendtoken == INIT_TOKEN_SEND) {
		int mechlistTokenSize;
		/*
		 * 1 byte for the CONTEXT ID(0xa0),
		 * 1 byte for the OID ID(0x06)
		 * 1 byte for OID Length field
		 * Plus the rest... (OID Length, OID value)
		 */
		mechlistTokenSize = 3 + mech_wanted->length +
			gssint_der_length_size(mech_wanted->length);

		dataLen += mechlistTokenSize;
	}
	if (data != NULL && data->length > 0) {
		/* Length of the inner token */
		rspTokenSize = 1 + gssint_der_length_size(data->length) +
			data->length;

		dataLen += rspTokenSize;

		/* Length of the outer token */
		dataLen += 1 + gssint_der_length_size(rspTokenSize);
	}
	if (mechListMIC != NULL) {

		/* Length of the inner token */
		micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
			mechListMIC->length;

		dataLen += micTokenSize;

		/* Length of the outer token */
		dataLen += 1 + gssint_der_length_size(micTokenSize);
	}
	/*
	 * Add size of DER encoded:
	 * NegTokenTarg [ SEQUENCE ] of
	 *    NegResult[0] ENUMERATED {
	 *	accept_completed(0),
	 *	accept_incomplete(1),
	 *	reject(2) }
	 *    supportedMech [1] MechType OPTIONAL,
	 *    responseToken [2] OCTET STRING OPTIONAL,
	 *    mechListMIC   [3] OCTET STRING OPTIONAL
	 *
	 * size = data->length + MechListMic + SupportedMech len +
	 *	Result Length + ASN.1 overhead
	 */
	NegTokenTargSize = dataLen;
	dataLen += 1 + gssint_der_length_size(NegTokenTargSize);

	/*
	 * NegotiationToken [ CHOICE ]{
	 *    negTokenInit  [0]	 NegTokenInit,
	 *    negTokenTarg  [1]	 NegTokenTarg }
	 */
	NegTokenSize = dataLen;
	dataLen += 1 + gssint_der_length_size(NegTokenSize);

	tlen = dataLen;
	t = (unsigned char *) gssalloc_malloc(tlen);

	if (t == NULL) {
		ret = GSS_S_DEFECTIVE_TOKEN;
		goto errout;
	}

	ptr = t;

	/*
	 * Indicate that we are sending CHOICE 1
	 * (NegTokenTarg)
	 */
	*ptr++ = CONTEXT | 0x01;
	if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
		ret = GSS_S_DEFECTIVE_TOKEN;
		goto errout;
	}
	*ptr++ = SEQUENCE;
	if (gssint_put_der_length(NegTokenTargSize, &ptr,
				  tlen - (int)(ptr-t)) < 0) {
		ret = GSS_S_DEFECTIVE_TOKEN;
		goto errout;
	}

	/*
	 * First field of the NegTokenTarg SEQUENCE
	 * is the ENUMERATED NegResult.
	 */
	*ptr++ = CONTEXT;
	if (gssint_put_der_length(3, &ptr,
				  tlen - (int)(ptr-t)) < 0) {
		ret = GSS_S_DEFECTIVE_TOKEN;
		goto errout;
	}
	if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
		ret = GSS_S_DEFECTIVE_TOKEN;
		goto errout;
	}
	if (sendtoken == INIT_TOKEN_SEND) {
		/*
		 * Next, is the Supported MechType
		 */
		*ptr++ = CONTEXT | 0x01;
		if (gssint_put_der_length(mech_wanted->length + 2,
					  &ptr,
					  tlen - (int)(ptr - t)) < 0) {
			ret = GSS_S_DEFECTIVE_TOKEN;
			goto errout;
		}
		if (put_mech_oid(&ptr, mech_wanted,
				 tlen - (int)(ptr - t)) < 0) {
			ret = GSS_S_DEFECTIVE_TOKEN;
			goto errout;
		}
	}
	if (data != NULL && data->length > 0) {
		*ptr++ = CONTEXT | 0x02;
		if (gssint_put_der_length(rspTokenSize, &ptr,
					  tlen - (int)(ptr - t)) < 0) {
			ret = GSS_S_DEFECTIVE_TOKEN;
			goto errout;
		}
		if (put_input_token(&ptr, data,
				    tlen - (int)(ptr - t)) < 0) {
			ret = GSS_S_DEFECTIVE_TOKEN;
			goto errout;
		}
	}
	if (mechListMIC != NULL) {
		*ptr++ = CONTEXT | 0x03;
		if (gssint_put_der_length(micTokenSize, &ptr,
					  tlen - (int)(ptr - t)) < 0) {
			ret = GSS_S_DEFECTIVE_TOKEN;
			goto errout;
		}
		if (put_input_token(&ptr, mechListMIC,
				    tlen - (int)(ptr - t)) < 0) {
			ret = GSS_S_DEFECTIVE_TOKEN;
			goto errout;
		}
	}
	ret = GSS_S_COMPLETE;
errout:
	if (ret != GSS_S_COMPLETE) {
		if (t)
			free(t);
	} else {
		outbuf->length = ptr - t;
		outbuf->value = (void *) t;
	}

	return (ret);
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);


get_negTokenInit(OM_uint32 *minor_status,
		 gss_buffer_t buf,
		 gss_buffer_t der_mechSet,
		 gss_OID_set *mechSet,
		 OM_uint32 *req_flags,
		 gss_buffer_t *mechtok,
		 gss_buffer_t *mechListMIC)
{
	OM_uint32 err;
	unsigned char *ptr, *bufstart;
	unsigned int len;
	gss_buffer_desc tmpbuf;

	*minor_status = 0;
	der_mechSet->length = 0;
	der_mechSet->value = NULL;
	*mechSet = GSS_C_NO_OID_SET;
	*req_flags = 0;
	*mechtok = *mechListMIC = GSS_C_NO_BUFFER;

	ptr = bufstart = buf->value;
	if ((buf->length - (ptr - bufstart)) > INT_MAX)
		return GSS_S_FAILURE;
#define REMAIN (buf->length - (ptr - bufstart))

	err = g_verify_token_header(gss_mech_spnego,
				    &len, &ptr, 0, REMAIN);
	if (err) {
		*minor_status = err;
		map_errcode(minor_status);
		return GSS_S_FAILURE;
	}
	*minor_status = g_verify_neg_token_init(&ptr, REMAIN);
	if (*minor_status) {
		map_errcode(minor_status);
		return GSS_S_FAILURE;
	}

	/* alias into input_token */
	tmpbuf.value = ptr;
	tmpbuf.length = REMAIN;
	*mechSet = get_mech_set(minor_status, &ptr, REMAIN);
	if (*mechSet == NULL)
		return GSS_S_FAILURE;

	tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
	der_mechSet->value = gssalloc_malloc(tmpbuf.length);
	if (der_mechSet->value == NULL)
		return GSS_S_FAILURE;
	memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
	der_mechSet->length = tmpbuf.length;

	err = get_req_flags(&ptr, REMAIN, req_flags);
	if (err != GSS_S_COMPLETE) {
		return err;
	}
	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
				 REMAIN, &len) >= 0) {
		*mechtok = get_input_token(&ptr, len);
		if (*mechtok == GSS_C_NO_BUFFER) {
			return GSS_S_FAILURE;
		}
	}
	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
				 REMAIN, &len) >= 0) {
		*mechListMIC = get_input_token(&ptr, len);
		if (*mechListMIC == GSS_C_NO_BUFFER) {
			return GSS_S_FAILURE;
		}
	}
	return GSS_S_COMPLETE;
#undef REMAIN
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);


is_kerb_mech(gss_OID oid)
{
	int answer = 0;
	OM_uint32 minor;
	extern const gss_OID_set_desc * const gss_mech_set_krb5_both;

	(void) gss_test_oid_set_member(&minor,
		oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);

	return (answer);
}

typedef const gss_OID_desc *gss_OID_const;

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);


put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
		unsigned int buflen)
{
	int ret;

	/* if token length is 0, we do not want to send */
	if (input_token->length == 0)
		return (0);

	if (input_token->length > buflen)
		return (-1);

	*(*buf_out)++ = OCTET_STRING;
	if ((ret = gssint_put_der_length(input_token->length, buf_out,
			    input_token->length)))
		return (ret);
	TWRITE_STR(*buf_out, input_token->value, input_token->length);
	return (0);
}

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);


negotiate_mech(gss_OID_set supported, gss_OID_set received,
	       OM_uint32 *negResult)
{
	size_t i, j;

	for (i = 0; i < received->count; i++) {
		gss_OID mech_oid = &received->elements[i];

		/* Accept wrong mechanism OID from MS clients */
		if (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid))
			mech_oid = (gss_OID)&gss_mech_krb5_oid;

		for (j = 0; j < supported->count; j++) {
			if (g_OID_equal(mech_oid, &supported->elements[j])) {
				*negResult = (i == 0) ? ACCEPT_INCOMPLETE :
					REQUEST_MIC;
				return &received->elements[i];
			}
		}
	}
	*negResult = REJECT;
	return (NULL);
}

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		 gss_buffer_t, gss_OID *, gss_buffer_t,
		 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
		 OM_uint32 *, send_token_flag *);


spnego_gss_set_cred_option(
		OM_uint32 *minor_status,
		gss_cred_id_t *cred_handle,
		const gss_OID desired_object,
		const gss_buffer_t value)
{
	OM_uint32 ret;
	OM_uint32 tmp_minor_status;
	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)*cred_handle;
	gss_cred_id_t mcred;

	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
	ret = gss_set_cred_option(minor_status,
				  &mcred,
				  desired_object,
				  value);
	if (ret == GSS_S_COMPLETE && spcred == NULL) {
		/*
		 * If the mechanism allocated a new credential handle, then
		 * we need to wrap it up in an SPNEGO credential handle.
		 */

		spcred = malloc(sizeof(spnego_gss_cred_id_rec));
		if (spcred == NULL) {
			gss_release_cred(&tmp_minor_status, &mcred);
			*minor_status = ENOMEM;
			return (GSS_S_FAILURE);
		}
		spcred->mcred = mcred;
		spcred->neg_mechs = GSS_C_NULL_OID_SET;
		*cred_handle = (gss_cred_id_t)spcred;
	}

	return (ret);
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

typedef const gss_OID_desc *gss_OID_const;

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
				      const gss_name_t desired_name,
				      const gss_buffer_t password,
				      OM_uint32 time_req,
				      const gss_OID_set desired_mechs,
				      gss_cred_usage_t cred_usage,
				      gss_cred_id_t *output_cred_handle,
				      gss_OID_set *actual_mechs,
				      OM_uint32 *time_rec)
{
	OM_uint32 status, tmpmin;
	gss_OID_set amechs = GSS_C_NULL_OID_SET;
	gss_cred_id_t mcred = NULL;
	spnego_gss_cred_id_t spcred = NULL;

	dsyslog("Entering spnego_gss_acquire_cred_with_password\n");

	if (actual_mechs)
		*actual_mechs = NULL;

	if (time_rec)
		*time_rec = 0;

	status = get_available_mechs(minor_status, desired_name,
				     cred_usage, GSS_C_NO_CRED_STORE,
				     NULL, &amechs);
	if (status != GSS_S_COMPLETE)
	    goto cleanup;

	status = gss_acquire_cred_with_password(minor_status, desired_name,
						password, time_req, amechs,
						cred_usage, &mcred,
						actual_mechs, time_rec);
	if (status != GSS_S_COMPLETE)
	    goto cleanup;

	spcred = malloc(sizeof(spnego_gss_cred_id_rec));
	if (spcred == NULL) {
		*minor_status = ENOMEM;
		status = GSS_S_FAILURE;
		goto cleanup;
	}
	spcred->neg_mechs = GSS_C_NULL_OID_SET;
	spcred->mcred = mcred;
	mcred = GSS_C_NO_CREDENTIAL;
	*output_cred_handle = (gss_cred_id_t)spcred;

cleanup:

	(void) gss_release_oid_set(&tmpmin, &amechs);
	(void) gss_release_cred(&tmpmin, &mcred);

	dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
	return (status);
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

typedef const gss_OID_desc *gss_OID_const;

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);


mech_requires_mechlistMIC(spnego_gss_ctx_id_t sc)
{
	OM_uint32 major, minor;
	gss_ctx_id_t ctx = sc->ctx_handle;
	gss_OID oid = (gss_OID)&spnego_req_mechlistMIC_oid;
	gss_buffer_set_t bufs;
	int result;

	major = gss_inquire_sec_context_by_oid(&minor, ctx, oid, &bufs);
	if (major != GSS_S_COMPLETE)
		return 0;

	/* Report true if the mech returns a single buffer containing a single
	 * byte with value 1. */
	result = (bufs != NULL && bufs->count == 1 &&
		  bufs->elements[0].length == 1 &&
		  memcmp(bufs->elements[0].value, "\1", 1) == 0);
	(void) gss_release_buffer_set(&minor, &bufs);
	return result;
}


spnego_gss_release_name(
			OM_uint32 *minor_status,
			gss_name_t *input_name)
{
	OM_uint32 status;

	dsyslog("Entering release_name\n");

	status = gss_release_name(minor_status, input_name);

	dsyslog("Leaving release_name\n");
	return (status);
}

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		 gss_buffer_t, gss_OID *, gss_buffer_t,
		 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
		 OM_uint32 *, send_token_flag *);

static gss_OID
negotiate_mech(gss_OID_set, gss_OID_set, OM_uint32 *);


spnego_gss_complete_auth_token(
		OM_uint32 *minor_status,
		const gss_ctx_id_t context_handle,
		gss_buffer_t input_message_buffer)
{
	OM_uint32 ret;
	ret = gss_complete_auth_token(minor_status,
				      context_handle,
				      input_message_buffer);
	return (ret);
}


init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
	      OM_uint32 acc_negState, gss_OID supportedMech,
	      gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
	      OM_uint32 *negState, send_token_flag *tokflag)
{
	OM_uint32 ret;

	*negState = REJECT;
	*tokflag = ERROR_TOKEN_SEND;
	ret = GSS_S_DEFECTIVE_TOKEN;
	/*
	 * Both supportedMech and negState must be present in first
	 * acceptor token.
	 */
	if (supportedMech == GSS_C_NO_OID) {
		*minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
		map_errcode(minor_status);
		return GSS_S_DEFECTIVE_TOKEN;
	}
	if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
		map_errcode(minor_status);
		return GSS_S_DEFECTIVE_TOKEN;
	}

	/*
	 * If the mechanism we sent is not the mechanism returned from
	 * the server, we need to handle the server's counter
	 * proposal.  There is a bug in SAMBA servers that always send
	 * the old Kerberos mech OID, even though we sent the new one.
	 * So we will treat all the Kerberos mech OIDS as the same.
         */
	if (!(is_kerb_mech(supportedMech) &&
	      is_kerb_mech(sc->internal_mech)) &&
	    !g_OID_equal(supportedMech, sc->internal_mech)) {
		ret = init_ctx_reselect(minor_status, sc,
					acc_negState, supportedMech,
					responseToken, mechListMIC,
					negState, tokflag);

	} else if (*responseToken == GSS_C_NO_BUFFER) {
		if (sc->mech_complete) {
			/*
			 * Mech completed on first call to its
			 * init_sec_context().  Acceptor sends no mech
			 * token.
			 */
			*negState = ACCEPT_COMPLETE;
			*tokflag = NO_TOKEN_SEND;
			ret = GSS_S_COMPLETE;
		} else {
			/*
			 * Reject missing mech token when optimistic
			 * mech selected.
			 */
			*minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
			map_errcode(minor_status);
			ret = GSS_S_DEFECTIVE_TOKEN;
		}
	} else if ((*responseToken)->length == 0 && sc->mech_complete) {
		/* Handle old IIS servers returning empty token instead of
		 * null tokens in the non-mutual auth case. */
		*negState = ACCEPT_COMPLETE;
		*tokflag = NO_TOKEN_SEND;
		ret = GSS_S_COMPLETE;
	} else if (sc->mech_complete) {
		/* Reject spurious mech token. */
		ret = GSS_S_DEFECTIVE_TOKEN;
	} else {
		*negState = ACCEPT_INCOMPLETE;
		*tokflag = CONT_TOKEN_SEND;
		ret = GSS_S_CONTINUE_NEEDED;
	}
	sc->nego_done = 1;
	return ret;
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);


check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
{
	spnego_ctx->optionStr = gssint_get_modOptions(
		(const gss_OID)&spnego_oids[0]);
}

typedef const gss_OID_desc *gss_OID_const;

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		 gss_buffer_t, gss_OID *, gss_buffer_t,
		 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
		 OM_uint32 *, send_token_flag *);

static gss_OID
negotiate_mech(gss_OID_set, gss_OID_set, OM_uint32 *);

static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
			int,
			gss_buffer_t,
			OM_uint32, gss_buffer_t, send_token_flag,
			gss_buffer_t);

static int
make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
			gss_buffer_t, send_token_flag,
			gss_buffer_t);

static OM_uint32
get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
		 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);

static int
is_kerb_mech(gss_OID oid);

static const gss_OID_desc spnego_oids[] = {
	{SPNEGO_OID_LENGTH, SPNEGO_OID},
};

const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;

static const gss_OID_set_desc spnego_oidsets[] = {
	{1, (gss_OID) spnego_oids+0},
};

const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;


init_ctx_call_init(OM_uint32 *minor_status,
		   spnego_gss_ctx_id_t sc,
		   spnego_gss_cred_id_t spcred,
		   gss_name_t target_name,
		   OM_uint32 req_flags,
		   OM_uint32 time_req,
		   gss_buffer_t mechtok_in,
		   gss_OID *actual_mech,
		   gss_buffer_t mechtok_out,
		   OM_uint32 *ret_flags,
		   OM_uint32 *time_rec,
		   OM_uint32 *negState,
		   send_token_flag *send_token)
{
	OM_uint32 ret, tmpret, tmpmin;
	gss_cred_id_t mcred;

	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
	ret = gss_init_sec_context(minor_status,
				   mcred,
				   &sc->ctx_handle,
				   target_name,
				   sc->internal_mech,
				   (req_flags | GSS_C_INTEG_FLAG),
				   time_req,
				   GSS_C_NO_CHANNEL_BINDINGS,
				   mechtok_in,
				   &sc->actual_mech,
				   mechtok_out,
				   &sc->ctx_flags,
				   time_rec);
	if (ret == GSS_S_COMPLETE) {
		sc->mech_complete = 1;
		if (ret_flags != NULL)
			*ret_flags = sc->ctx_flags;
		/*
		 * Microsoft SPNEGO implementations expect an even number of
		 * token exchanges.  So if we're sending a final token, ask for
		 * a zero-length token back from the server.  Also ask for a
		 * token back if this is the first token or if a MIC exchange
		 * is required.
		 */
		if (*send_token == CONT_TOKEN_SEND &&
		    mechtok_out->length == 0 &&
		    (!sc->mic_reqd ||
		     !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
			/* The exchange is complete. */
			*negState = ACCEPT_COMPLETE;
			ret = GSS_S_COMPLETE;
			*send_token = NO_TOKEN_SEND;
		} else {
			/* Ask for one more hop. */
			*negState = ACCEPT_INCOMPLETE;
			ret = GSS_S_CONTINUE_NEEDED;
		}
		return ret;
	}

	if (ret == GSS_S_CONTINUE_NEEDED)
		return ret;

	if (*send_token != INIT_TOKEN_SEND) {
		*send_token = ERROR_TOKEN_SEND;
		*negState = REJECT;
		return ret;
	}

	/*
	 * Since this is the first token, we can fall back to later mechanisms
	 * in the list.  Since the mechanism list is expected to be short, we
	 * can do this with recursion.  If all mechanisms produce errors, the
	 * caller should get the error from the first mech in the list.
	 */
	gssalloc_free(sc->mech_set->elements->elements);
	memmove(sc->mech_set->elements, sc->mech_set->elements + 1,
		--sc->mech_set->count * sizeof(*sc->mech_set->elements));
	if (sc->mech_set->count == 0)
		goto fail;
	gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
	if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0)
		goto fail;
	tmpret = init_ctx_call_init(&tmpmin, sc, spcred, target_name,
				    req_flags, time_req, mechtok_in,
				    actual_mech, mechtok_out, ret_flags,
				    time_rec, negState, send_token);
	if (HARD_ERROR(tmpret))
		goto fail;
	*minor_status = tmpmin;
	return tmpret;

fail:
	/* Don't output token on error from first call. */
	*send_token = NO_TOKEN_SEND;
	*negState = REJECT;
	return ret;
}


spnego_gss_import_name(
		    OM_uint32 *minor_status,
		    gss_buffer_t input_name_buffer,
		    gss_OID input_name_type,
		    gss_name_t *output_name)
{
	OM_uint32 status;

	dsyslog("Entering import_name\n");

	status = gss_import_name(minor_status, input_name_buffer,
			input_name_type, output_name);

	dsyslog("Leaving import_name\n");
	return (status);
}


spnego_gss_unwrap_iov(OM_uint32 *minor_status,
		      gss_ctx_id_t context_handle,
		      int *conf_state,
		      gss_qop_t *qop_state,
		      gss_iov_buffer_desc *iov,
		      int iov_count)
{
	OM_uint32 ret;
	ret = gss_unwrap_iov(minor_status,
			     context_handle,
			     conf_state,
			     qop_state,
			     iov,
			     iov_count);
	return (ret);
}

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);


init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
	      gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
	      OM_uint32 *negState, send_token_flag *tokflag)
{
	OM_uint32 ret, tmpmin, acc_negState;
	unsigned char *ptr;
	spnego_gss_ctx_id_t sc;
	gss_OID supportedMech = GSS_C_NO_OID;

	sc = (spnego_gss_ctx_id_t)*ctx;
	*negState = REJECT;
	*tokflag = ERROR_TOKEN_SEND;

	ptr = buf->value;
	ret = get_negTokenResp(minor_status, ptr, buf->length,
			       &acc_negState, &supportedMech,
			       responseToken, mechListMIC);
	if (ret != GSS_S_COMPLETE)
		goto cleanup;
	if (acc_negState == REJECT) {
		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
		map_errcode(minor_status);
		*tokflag = NO_TOKEN_SEND;
		ret = GSS_S_FAILURE;
		goto cleanup;
	}
	/*
	 * nego_done is false for the first call to init_ctx_cont()
	 */
	if (!sc->nego_done) {
		ret = init_ctx_nego(minor_status, sc,
				    acc_negState,
				    supportedMech, responseToken,
				    mechListMIC,
				    negState, tokflag);
	} else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
		   (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
		/* Missing or spurious token from acceptor. */
		ret = GSS_S_DEFECTIVE_TOKEN;
	} else if (!sc->mech_complete ||
		   (sc->mic_reqd &&
		    (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
		/* Not obviously done; we may decide we're done later in
		 * init_ctx_call_init or handle_mic. */
		*negState = ACCEPT_INCOMPLETE;
		*tokflag = CONT_TOKEN_SEND;
		ret = GSS_S_CONTINUE_NEEDED;
	} else {
		/* mech finished on last pass and no MIC required, so done. */
		*negState = ACCEPT_COMPLETE;
		*tokflag = NO_TOKEN_SEND;
		ret = GSS_S_COMPLETE;
	}
cleanup:
	if (supportedMech != GSS_C_NO_OID)
		generic_gss_release_oid(&tmpmin, &supportedMech);
	return ret;
}


spnego_gss_inquire_context(
			OM_uint32	*minor_status,
			const gss_ctx_id_t context_handle,
			gss_name_t	*src_name,
			gss_name_t	*targ_name,
			OM_uint32	*lifetime_rec,
			gss_OID		*mech_type,
			OM_uint32	*ctx_flags,
			int		*locally_initiated,
			int		*opened)
{
	OM_uint32 ret = GSS_S_COMPLETE;

	ret = gss_inquire_context(minor_status,
				context_handle,
				src_name,
				targ_name,
				lifetime_rec,
				mech_type,
				ctx_flags,
				locally_initiated,
				opened);

	return (ret);
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

typedef const gss_OID_desc *gss_OID_const;

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);


gss_mech_initialize(void)
{
	return (&spnego_mechanism);
}

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);


spnego_gss_display_name(
			OM_uint32 *minor_status,
			gss_name_t input_name,
			gss_buffer_t output_name_buffer,
			gss_OID *output_name_type)
{
	OM_uint32 status = GSS_S_COMPLETE;
	dsyslog("Entering display_name\n");

	status = gss_display_name(minor_status, input_name,
			output_name_buffer, output_name_type);

	dsyslog("Leaving display_name\n");
	return (status);
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


spnego_gss_acquire_cred(OM_uint32 *minor_status,
			gss_name_t desired_name,
			OM_uint32 time_req,
			gss_OID_set desired_mechs,
			gss_cred_usage_t cred_usage,
			gss_cred_id_t *output_cred_handle,
			gss_OID_set *actual_mechs,
			OM_uint32 *time_rec)
{
    return spnego_gss_acquire_cred_from(minor_status, desired_name, time_req,
					desired_mechs, cred_usage, NULL,
					output_cred_handle, actual_mechs,
					time_rec);
}


put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
	      unsigned int buflen)
{
	int ret;

	/* if token length is 0, we do not want to send */
	if (input_token->length == 0)
		return (0);

	if (input_token->length > buflen)
		return (-1);

	*(*buf_out)++ = SEQUENCE;
	if ((ret = gssint_put_der_length(input_token->length, buf_out,
			    input_token->length)))
		return (ret);
	TWRITE_STR(*buf_out, input_token->value, input_token->length);
	return (0);
}


spnego_gss_wrap_aead(OM_uint32 *minor_status,
		     gss_ctx_id_t context_handle,
		     int conf_req_flag,
		     gss_qop_t qop_req,
		     gss_buffer_t input_assoc_buffer,
		     gss_buffer_t input_payload_buffer,
		     int *conf_state,
		     gss_buffer_t output_message_buffer)
{
	OM_uint32 ret;
	ret = gss_wrap_aead(minor_status,
			    context_handle,
			    conf_req_flag,
			    qop_req,
			    input_assoc_buffer,
			    input_payload_buffer,
			    conf_state,
			    output_message_buffer);

	return (ret);
}


spnego_gss_set_name_attribute(OM_uint32 *minor_status,
			      gss_name_t name,
			      int complete,
			      gss_buffer_t attr,
			      gss_buffer_t value)
{
	OM_uint32 ret;
	ret = gss_set_name_attribute(minor_status,
				     name,
				     complete,
				     attr,
				     value);
	return (ret);
}


spnego_gss_process_context_token(
				OM_uint32	*minor_status,
				const gss_ctx_id_t context_handle,
				const gss_buffer_t token_buffer)
{
	OM_uint32 ret;
	ret = gss_process_context_token(minor_status,
					context_handle,
					token_buffer);

	return (ret);
}


acc_ctx_hints(OM_uint32 *minor_status,
	      gss_ctx_id_t *ctx,
	      spnego_gss_cred_id_t spcred,
	      gss_buffer_t *mechListMIC,
	      OM_uint32 *negState,
	      send_token_flag *return_token)
{
	OM_uint32 tmpmin, ret;
	gss_OID_set supported_mechSet;
	spnego_gss_ctx_id_t sc = NULL;

	*mechListMIC = GSS_C_NO_BUFFER;
	supported_mechSet = GSS_C_NO_OID_SET;
	*return_token = NO_TOKEN_SEND;
	*negState = REJECT;
	*minor_status = 0;

	/* A hint request must be the first token received. */
	if (*ctx != GSS_C_NO_CONTEXT)
	    return GSS_S_DEFECTIVE_TOKEN;

	ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
				   &supported_mechSet);
	if (ret != GSS_S_COMPLETE)
		goto cleanup;

	ret = make_NegHints(minor_status, spcred, mechListMIC);
	if (ret != GSS_S_COMPLETE)
		goto cleanup;

	sc = create_spnego_ctx();
	if (sc == NULL) {
		ret = GSS_S_FAILURE;
		goto cleanup;
	}
	if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
		ret = GSS_S_FAILURE;
		goto cleanup;
	}
	sc->internal_mech = GSS_C_NO_OID;

	*negState = ACCEPT_INCOMPLETE;
	*return_token = INIT_TOKEN_SEND;
	sc->firstpass = 1;
	*ctx = (gss_ctx_id_t)sc;
	sc = NULL;
	ret = GSS_S_COMPLETE;

cleanup:
	release_spnego_ctx(&sc);
	gss_release_oid_set(&tmpmin, &supported_mechSet);

	return ret;
}


handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
	   int send_mechtok, spnego_gss_ctx_id_t sc,
	   gss_buffer_t *mic_out,
	   OM_uint32 *negState, send_token_flag *tokflag)
{
	OM_uint32 ret;

	ret = GSS_S_FAILURE;
	*mic_out = GSS_C_NO_BUFFER;
	if (mic_in != GSS_C_NO_BUFFER) {
		if (sc->mic_rcvd) {
			/* Reject MIC if we've already received a MIC. */
			*negState = REJECT;
			*tokflag = ERROR_TOKEN_SEND;
			return GSS_S_DEFECTIVE_TOKEN;
		}
	} else if (sc->mic_reqd && !send_mechtok) {
		/*
		 * If the peer sends the final mechanism token, it
		 * must send the MIC with that token if the
		 * negotiation requires MICs.
		 */
		*negState = REJECT;
		*tokflag = ERROR_TOKEN_SEND;
		return GSS_S_DEFECTIVE_TOKEN;
	}
	ret = process_mic(minor_status, mic_in, sc, mic_out,
			  negState, tokflag);
	if (ret != GSS_S_COMPLETE) {
		return ret;
	}
	if (sc->mic_reqd) {
		assert(sc->mic_sent || sc->mic_rcvd);
	}
	if (sc->mic_sent && sc->mic_rcvd) {
		ret = GSS_S_COMPLETE;
		*negState = ACCEPT_COMPLETE;
		if (*mic_out == GSS_C_NO_BUFFER) {
			/*
			 * We sent a MIC on the previous pass; we
			 * shouldn't be sending a mechanism token.
			 */
			assert(!send_mechtok);
			*tokflag = NO_TOKEN_SEND;
		} else {
			*tokflag = CONT_TOKEN_SEND;
		}
	} else if (sc->mic_reqd) {
		*negState = ACCEPT_INCOMPLETE;
		ret = GSS_S_CONTINUE_NEEDED;
	} else if (*negState == ACCEPT_COMPLETE) {
		ret = GSS_S_COMPLETE;
	} else {
		ret = GSS_S_CONTINUE_NEEDED;
	}
	return ret;
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);


spnego_gss_inquire_sec_context_by_oid(
		OM_uint32 *minor_status,
		const gss_ctx_id_t context_handle,
		const gss_OID desired_object,
		gss_buffer_set_t *data_set)
{
	OM_uint32 ret;
	ret = gss_inquire_sec_context_by_oid(minor_status,
			    context_handle,
			    desired_object,
			    data_set);
	return (ret);
}

typedef const gss_OID_desc *gss_OID_const;

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		 gss_buffer_t, gss_OID *, gss_buffer_t,
		 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
		 OM_uint32 *, send_token_flag *);


init_ctx_new(OM_uint32 *minor_status,
	     spnego_gss_cred_id_t spcred,
	     gss_ctx_id_t *ctx,
	     send_token_flag *tokflag)
{
	OM_uint32 ret;
	spnego_gss_ctx_id_t sc = NULL;

	sc = create_spnego_ctx();
	if (sc == NULL)
		return GSS_S_FAILURE;

	/* determine negotiation mech set */
	ret = get_negotiable_mechs(minor_status, spcred, GSS_C_INITIATE,
				   &sc->mech_set);
	if (ret != GSS_S_COMPLETE)
		goto cleanup;

	/* Set an initial internal mech to make the first context token. */
	sc->internal_mech = &sc->mech_set->elements[0];

	if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
		ret = GSS_S_FAILURE;
		goto cleanup;
	}
	/*
	 * The actual context is not yet determined, set the output
	 * context handle to refer to the spnego context itself.
	 */
	sc->ctx_handle = GSS_C_NO_CONTEXT;
	*ctx = (gss_ctx_id_t)sc;
	sc = NULL;
	*tokflag = INIT_TOKEN_SEND;
	ret = GSS_S_CONTINUE_NEEDED;

cleanup:
	release_spnego_ctx(&sc);
	return ret;
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

int gss_krb5int_lib_init(void);


int gss_spnegoint_lib_init(void)
{
	int err;

	err = k5_key_register(K5_KEY_GSS_SPNEGO_STATUS, NULL);
	if (err)
		return err;

#ifdef _GSS_STATIC_LINK
	return gss_spnegomechglue_init();
#else
	return 0;
#endif
}

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static void release_spnego_ctx(spnego_gss_ctx_id_t *);

static void check_spnego_options(spnego_gss_ctx_id_t);

static spnego_gss_ctx_id_t create_spnego_ctx(void);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static int
g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);

static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
			int,
			gss_buffer_t,
			OM_uint32, gss_buffer_t, send_token_flag,
			gss_buffer_t);

static int
make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
			gss_buffer_t, send_token_flag,
			gss_buffer_t);

static OM_uint32
get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
		 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);

static int
is_kerb_mech(gss_OID oid);

static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);

static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);


spnego_gss_inquire_cred(
			OM_uint32 *minor_status,
			gss_cred_id_t cred_handle,
			gss_name_t *name,
			OM_uint32 *lifetime,
			int *cred_usage,
			gss_OID_set *mechanisms)
{
	OM_uint32 status;
	spnego_gss_cred_id_t spcred = NULL;
	gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
	OM_uint32 tmp_minor_status;
	OM_uint32 initiator_lifetime, acceptor_lifetime;

	dsyslog("Entering inquire_cred\n");

	/*
	 * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
	 * supplied we call gss_inquire_cred_by_mech() on the
	 * first non-SPNEGO mechanism.
	 */
	spcred = (spnego_gss_cred_id_t)cred_handle;
	if (spcred == NULL) {
		status = get_available_mechs(minor_status,
			GSS_C_NO_NAME,
			GSS_C_BOTH,
			GSS_C_NO_CRED_STORE,
			&creds,
			mechanisms);
		if (status != GSS_S_COMPLETE) {
			dsyslog("Leaving inquire_cred\n");
			return (status);
		}

		if ((*mechanisms)->count == 0) {
			gss_release_cred(&tmp_minor_status, &creds);
			gss_release_oid_set(&tmp_minor_status, mechanisms);
			dsyslog("Leaving inquire_cred\n");
			return (GSS_S_DEFECTIVE_CREDENTIAL);
		}

		assert((*mechanisms)->elements != NULL);

		status = gss_inquire_cred_by_mech(minor_status,
			creds,
			&(*mechanisms)->elements[0],
			name,
			&initiator_lifetime,
			&acceptor_lifetime,
			cred_usage);
		if (status != GSS_S_COMPLETE) {
			gss_release_cred(&tmp_minor_status, &creds);
			dsyslog("Leaving inquire_cred\n");
			return (status);
		}

		if (lifetime != NULL)
			*lifetime = (*cred_usage == GSS_C_ACCEPT) ?
				acceptor_lifetime : initiator_lifetime;

		gss_release_cred(&tmp_minor_status, &creds);
	} else {
		status = gss_inquire_cred(minor_status, spcred->mcred,
					  name, lifetime,
					  cred_usage, mechanisms);
	}

	dsyslog("Leaving inquire_cred\n");

	return (status);
}

#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)

extern unsigned int gssint_der_length_size(unsigned int);

extern int gssint_get_der_length(unsigned char **, unsigned int,
				 unsigned int*);

extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);

static int g_token_size(gss_OID_const, unsigned int);

static int g_make_token_header(gss_OID_const, unsigned int,
			       unsigned char **, unsigned int);

static int g_verify_token_header(gss_OID_const, unsigned int *,
				 unsigned char **,
				 int, unsigned int);

static int g_verify_neg_token_init(unsigned char **, unsigned int);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);


get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_cred_id_t spcred,
		     gss_cred_usage_t usage, gss_OID_set *rmechs)
{
	OM_uint32 ret, tmpmin;
	gss_cred_id_t creds = GSS_C_NO_CREDENTIAL, *credptr;
	gss_OID_set cred_mechs = GSS_C_NULL_OID_SET;
	gss_OID_set intersect_mechs = GSS_C_NULL_OID_SET;
	unsigned int i;
	int present;

	if (spcred == NULL) {
		/*
		 * The default credentials were supplied.  Return a list of all
		 * available mechs except SPNEGO.  When initiating, trim this
		 * list to mechs we can acquire credentials for.
		 */
		credptr = (usage == GSS_C_INITIATE) ? &creds : NULL;
		ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
					  GSS_C_NO_CRED_STORE, credptr,
					  rmechs);
		gss_release_cred(&tmpmin, &creds);
		return (ret);
	}

	/* Get the list of mechs in the mechglue cred. */
	ret = gss_inquire_cred(minor_status, spcred->mcred, NULL, NULL, NULL,
			       &cred_mechs);
	if (ret != GSS_S_COMPLETE)
		return (ret);

	if (spcred->neg_mechs == GSS_C_NULL_OID_SET) {
		/* gss_set_neg_mechs was never called; return cred_mechs. */
		*rmechs = cred_mechs;
		*minor_status = 0;
		return (GSS_S_COMPLETE);
	}

	/* Compute the intersection of cred_mechs and spcred->neg_mechs,
	 * preserving the order in spcred->neg_mechs. */
	ret = gss_create_empty_oid_set(minor_status, &intersect_mechs);
	if (ret != GSS_S_COMPLETE) {
		gss_release_oid_set(&tmpmin, &cred_mechs);
		return (ret);
	}

	for (i = 0; i < spcred->neg_mechs->count; i++) {
		gss_test_oid_set_member(&tmpmin,
					&spcred->neg_mechs->elements[i],
					cred_mechs, &present);
		if (!present)
			continue;
		ret = gss_add_oid_set_member(minor_status,
					     &spcred->neg_mechs->elements[i],
					     &intersect_mechs);
		if (ret != GSS_S_COMPLETE)
			break;
	}

	gss_release_oid_set(&tmpmin, &cred_mechs);
	if (intersect_mechs->count == 0 || ret != GSS_S_COMPLETE) {
		gss_release_oid_set(&tmpmin, &intersect_mechs);
		*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
		map_errcode(minor_status);
		return (GSS_S_FAILURE);
	}

	*rmechs = intersect_mechs;
	*minor_status = 0;
	return (GSS_S_COMPLETE);
}


spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
			 gss_cred_id_t cred_handle,
			 const gss_OID_set mech_list)
{
	OM_uint32 ret;
	spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;

	/* Store mech_list in spcred for use in negotiation logic. */
	gss_release_oid_set(minor_status, &spcred->neg_mechs);
	ret = generic_gss_copy_oid_set(minor_status, mech_list,
				       &spcred->neg_mechs);
	return (ret);
}

typedef const gss_OID_desc *gss_OID_const;

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);


spnego_gss_get_mic(
		OM_uint32 *minor_status,
		const gss_ctx_id_t context_handle,
		gss_qop_t  qop_req,
		const gss_buffer_t message_buffer,
		gss_buffer_t message_token)
{
	OM_uint32 ret;
	ret = gss_get_mic(minor_status,
		    context_handle,
		    qop_req,
		    message_buffer,
		    message_token);
	return (ret);
}

typedef const gss_OID_desc *gss_OID_const;

static spnego_token_t make_spnego_token(const char *);

static gss_buffer_desc make_err_msg(const char *);

static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);

static gss_buffer_t get_input_token(unsigned char **, unsigned int);

static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);

static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);

static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
				     gss_const_key_value_set_t,
				     gss_cred_id_t *, gss_OID_set *);

static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
				      gss_cred_usage_t, gss_OID_set *);

static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);

static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);

static int put_negResult(unsigned char **, OM_uint32, unsigned int);

static OM_uint32
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
	   gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
	     send_token_flag *);

static OM_uint32
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
	      gss_buffer_t *, gss_buffer_t *,
	      OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
		  gss_OID, gss_buffer_t *, gss_buffer_t *,
		  OM_uint32 *, send_token_flag *);

static OM_uint32
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
		   OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	    spnego_gss_cred_id_t, gss_buffer_t *,
	    gss_buffer_t *, OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
	     gss_buffer_t *, gss_buffer_t *,
	     OM_uint32 *, send_token_flag *);

static OM_uint32
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
		OM_uint32 *, send_token_flag *);