* Makefile.in, configure.in: Add spnego directory
authorTom Yu <tlyu@mit.edu>
Sun, 5 Mar 2006 23:19:10 +0000 (23:19 +0000)
committerTom Yu <tlyu@mit.edu>
Sun, 5 Mar 2006 23:19:10 +0000 (23:19 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/users/tlyu/branches/mechglue@17700 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/gssapi/ChangeLog
src/lib/gssapi/Makefile.in
src/lib/gssapi/configure.in
src/lib/gssapi/spnego/Makefile.in [new file with mode: 0644]
src/lib/gssapi/spnego/gssapiP_spnego.h [new file with mode: 0644]
src/lib/gssapi/spnego/spnego_mech.c [new file with mode: 0644]

index 61ec844c90e130e19be40ebe7a6c46782944a558..a93fa625ac4c45f7e614948ff20dc64771016420 100644 (file)
@@ -1,3 +1,7 @@
+2006-03-05  Tom Yu  <tlyu@mit.edu>
+
+       * Makefile.in, configure.in: Add spnego directory.
+
 2005-08-20  Ken Raeburn  <raeburn@mit.edu>
 
        * configure.in: Use K5_AC_INIT instead of AC_INIT.
index 2f7ed757a4c3309fdeed3e62585470527462bae8..5f38b3d357966acbcdfd423f9f5b79268d361423 100644 (file)
@@ -2,7 +2,7 @@ thisconfigdir=.
 myfulldir=lib/gssapi
 mydir=.
 BUILDTOP=$(REL)..$(S)..
-LOCAL_SUBDIRS= generic mechglue krb5
+LOCAL_SUBDIRS= generic mechglue krb5 spnego
 
 ##DOSLIBNAME=$(OUTPRE)gssapi.lib
 ##DOSOBJFILELIST=@$(OUTPRE)generic.lst @$(OUTPRE)krb5.lst @$(OUTPRE)gssapi.lst
index 8c97868435f146f0546da1f4d0b65f12f2e244f5..a9b0f72fd671cddee251ebf156f28d253cf1d552 100644 (file)
@@ -16,4 +16,4 @@ AC_CHECK_HEADER(xom.h,[
 AC_SUBST(include_xom)
 KRB5_BUILD_LIBOBJS
 KRB5_BUILD_LIBRARY_WITH_DEPS
-V5_AC_OUTPUT_MAKEFILE(. generic krb5 mechglue)
+V5_AC_OUTPUT_MAKEFILE(. generic krb5 mechglue spnego)
diff --git a/src/lib/gssapi/spnego/Makefile.in b/src/lib/gssapi/spnego/Makefile.in
new file mode 100644 (file)
index 0000000..3f1e1c0
--- /dev/null
@@ -0,0 +1,23 @@
+thisconfigdir=./..
+myfulldir=lib/gssapi/spnego
+mydir=spnego
+BUILDTOP=$(REL)..$(S)..$(S)..
+LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. -I../generic -I$(srcdir)/../generic -I../mechglue -I$(srcdir)/../mechglue
+
+##DOS##BUILDTOP = ..\..\..
+##DOS##PREFIXDIR=spnego
+##DOS##OBJFILE = ..\$(OUTPRE)spnego.lst
+
+##DOS##DLL_EXP_TYPE=GSS
+
+SRCS = $(srcdir)/spnego_mech.c
+
+OBJS = $(OUTPRE)spnego_mech.$(OBJEXT)
+
+STLIBOBJS = spnego_mech.o
+
+all-unix:: all-libobjs
+
+clean-unix:: clean-libobjs
+
+# @libobj_frag@
diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h
new file mode 100644 (file)
index 0000000..29d4347
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef        _GSSAPIP_SPNEGO_H_
+#define        _GSSAPIP_SPNEGO_H_
+
+#pragma ident  "@(#)gssapiP_spnego.h   1.3     03/09/18 SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <gssapi/gssapi.h>
+#include <synch.h>
+#include <syslog.h>
+
+#define        SEC_CONTEXT_TOKEN 1
+#define        SPNEGO_SIZE_OF_INT 4
+
+#define        ACCEPT_COMPLETE 0
+#define        ACCEPT_INCOMPLETE 1
+#define        REJECT 2
+#define        ACCEPT_DEFECTIVE_TOKEN 3
+
+/*
+ * constants for der encoding/decoding routines.
+ */
+
+#define        MECH_OID                0x06
+#define        OCTET_STRING            0x04
+#define        CONTEXT                 0xa0
+#define        SEQUENCE                0x30
+#define        SEQUENCE_OF             0x30
+#define        BIT_STRING              0x03
+#define        BIT_STRING_LENGTH       0x02
+#define        BIT_STRING_PADDING      0x01
+#define        ENUMERATED              0x0a
+#define        ENUMERATION_LENGTH      1
+#define        HEADER_ID               0x60
+
+/*
+ * SPNEGO specific error codes (minor status codes)
+ */
+#define        ERR_SPNEGO_NO_MECHS_AVAILABLE           0x20000001
+#define        ERR_SPNEGO_NO_CREDS_ACQUIRED            0x20000002
+#define        ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR        0x20000003
+#define        ERR_SPNEGO_NEGOTIATION_FAILED           0x20000004
+#define        ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR       0x20000005
+
+/*
+ * send_token_flag is used to indicate in later steps what type
+ * of token, if any should be sent or processed.
+ * NO_TOKEN_SEND = no token should be sent
+ * INIT_TOKEN_SEND = initial token will be sent
+ * CONT_TOKEN_SEND = continuing tokens to be sent
+ * CHECK_MIC = no token to be sent, but have a MIC to check.
+ * ERROR_TOKEN_SEND = error token from peer needs to be sent.
+ */
+
+typedef        enum {NO_TOKEN_SEND, INIT_TOKEN_SEND, CONT_TOKEN_SEND,
+               CHECK_MIC, ERROR_TOKEN_SEND} send_token_flag;
+
+/*
+ * The Mech OID:
+ * { iso(1) org(3) dod(6) internet(1) security(5)
+ *  mechanism(5) spnego(2) }
+ */
+
+#define        SPNEGO_OID_LENGTH 6
+#define        SPNEGO_OID "\053\006\001\005\005\002"
+
+typedef void *spnego_token_t;
+
+/* spnego name structure for internal representation. */
+typedef struct {
+       gss_OID type;
+       gss_buffer_t buffer;
+       gss_OID mech_type;
+       gss_name_t      mech_name;
+} spnego_name_desc, *spnego_name_t;
+
+/* Structure for context handle */
+typedef struct {
+       OM_uint32       magic_num;
+       gss_buffer_desc DER_mechTypes;
+       gss_OID internal_mech;
+       gss_ctx_id_t ctx_handle;
+       char  *optionStr;
+       int MS_Interop;
+       int optimistic;
+       OM_uint32 last_status;
+} spnego_gss_ctx_id_rec, *spnego_gss_ctx_id_t;
+
+/*
+ * The magic number must be less than a standard pagesize
+ * to avoid a possible collision with a real address.
+ */
+#define        SPNEGO_MAGIC_ID  0x00000fed
+
+/* SPNEGO oid structure */
+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;
+
+#define        TWRITE_STR(ptr, str, len) \
+       memcpy((ptr), (char *)(str), (len)); \
+       (ptr) += (len);
+
+#ifdef DEBUG
+#define        dsyslog(a) syslog(LOG_DEBUG, a)
+#else
+#define        dsyslog(a)
+#define        SPNEGO_STATIC
+#endif /* DEBUG */
+
+/*
+ * declarations of internal name mechanism functions
+ */
+
+OM_uint32 spnego_gss_acquire_cred
+(
+       void *,                 /* spnego context */
+       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 */
+);
+
+OM_uint32 spnego_gss_release_cred
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       /* CSTYLED */
+       gss_cred_id_t   *       /* cred_handle */
+);
+
+OM_uint32 spnego_gss_init_sec_context
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       gss_cred_id_t,          /* claimant_cred_handle */
+       gss_ctx_id_t *,         /* context_handle */
+       gss_name_t,             /* target_name */
+       gss_OID,                /* mech_type */
+       OM_uint32,              /* req_flags */
+       OM_uint32,              /* time_req */
+       gss_channel_bindings_t, /* input_chan_bindings */
+       gss_buffer_t,           /* input_token */
+       gss_OID *,              /* actual_mech_type */
+       gss_buffer_t,           /* output_token */
+       OM_uint32 *,            /* ret_flags */
+       OM_uint32 *             /* time_rec */
+);
+
+OM_uint32 spnego_gss_accept_sec_context
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       gss_ctx_id_t *,         /* context_handle */
+       gss_cred_id_t,          /* verifier_cred_handle */
+       gss_buffer_t,           /* input_token_buffer */
+       gss_channel_bindings_t, /* input_chan_bindings */
+       gss_name_t *,           /* src_name */
+       gss_OID *,              /* mech_type */
+       gss_buffer_t,           /* output_token */
+       OM_uint32 *,            /* ret_flags */
+       OM_uint32 *,            /* time_rec */
+       /* CSTYLED */
+       gss_cred_id_t *         /* delegated_cred_handle */
+);
+
+OM_uint32 spnego_gss_display_name
+(
+       void *,
+       OM_uint32 *,            /* minor_status */
+       gss_name_t,             /*  input_name */
+       gss_buffer_t,           /*  output_name_buffer */
+       gss_OID *               /* output_name_type */
+);
+
+OM_uint32 spnego_gss_display_status
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       OM_uint32,              /* status_value */
+       int,                    /* status_type */
+       gss_OID,                /* mech_type */
+       OM_uint32 *,            /* message_context */
+       gss_buffer_t            /* status_string */
+);
+
+OM_uint32 spnego_gss_import_name
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       gss_buffer_t,           /* input_name_buffer */
+       gss_OID,                /* input_name_type */
+       /* CSTYLED */
+       gss_name_t *            /* output_name */
+);
+
+OM_uint32 spnego_gss_release_name
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       /* CSTYLED */
+       gss_name_t *            /* input_name */
+);
+
+OM_uint32 spnego_gss_inquire_names_for_mech
+(
+       void *,                 /* spnego context */
+       OM_uint32 *,            /* minor_status */
+       gss_OID,                /* mechanism */
+       gss_OID_set *           /* name_types */
+);
+
+OM_uint32 spnego_gss_unseal
+(
+       void *context,
+       OM_uint32 *minor_status,
+       gss_ctx_id_t context_handle,
+       gss_buffer_t input_message_buffer,
+       gss_buffer_t output_message_buffer,
+       int *conf_state,
+       int *qop_state
+);
+
+OM_uint32 spnego_gss_seal
+(
+       void *context,
+       OM_uint32 *minor_status,
+       gss_ctx_id_t context_handle,
+       int conf_req_flag,
+       int qop_req,
+       gss_buffer_t input_message_buffer,
+       int *conf_state,
+       gss_buffer_t output_message_buffer
+);
+
+OM_uint32 spnego_gss_process_context_token
+(
+       void *context,
+       OM_uint32       *minor_status,
+       const gss_ctx_id_t context_handle,
+       const gss_buffer_t token_buffer
+);
+
+OM_uint32 spnego_gss_delete_sec_context
+(
+       void *context,
+       OM_uint32 *minor_status,
+       gss_ctx_id_t *context_handle,
+       gss_buffer_t output_token
+);
+
+OM_uint32 spnego_gss_context_time
+(
+       void *context,
+       OM_uint32       *minor_status,
+       const gss_ctx_id_t context_handle,
+       OM_uint32       *time_rec
+);
+
+OM_uint32 spnego_gss_export_sec_context
+(
+       void *context,
+       OM_uint32       *minor_status,
+       gss_ctx_id_t    *context_handle,
+       gss_buffer_t    interprocess_token
+);
+
+OM_uint32 spnego_gss_import_sec_context
+(
+       void                    *context,
+       OM_uint32               *minor_status,
+       const gss_buffer_t      interprocess_token,
+       gss_ctx_id_t            *context_handle
+);
+
+OM_uint32 spnego_gss_inquire_context
+(
+       void            *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             *open
+);
+
+OM_uint32 spnego_gss_wrap_size_limit
+(
+       void            *context,
+       OM_uint32       *minor_status,
+       const gss_ctx_id_t context_handle,
+       int             conf_req_flag,
+       gss_qop_t       qop_req,
+       OM_uint32       req_output_size,
+       OM_uint32       *max_input_size
+);
+
+OM_uint32 spnego_gss_sign
+(
+       void *context,
+       OM_uint32 *minor_status,
+       const gss_ctx_id_t context_handle,
+       int  qop_req,
+       const gss_buffer_t message_buffer,
+       gss_buffer_t message_token
+);
+
+OM_uint32 spnego_gss_verify
+(
+       void *context,
+       OM_uint32 *minor_status,
+       const gss_ctx_id_t context_handle,
+       const gss_buffer_t msg_buffer,
+       const gss_buffer_t token_buffer,
+       int *qop_state
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GSSAPIP_SPNEGO_H_ */
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
new file mode 100644 (file)
index 0000000..0f165ee
--- /dev/null
@@ -0,0 +1,2647 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * A module that implements the spnego security mechanism.
+ * It is used to negotiate the security mechanism between
+ * peers using the GSS-API.
+ *
+ */
+
+#pragma ident  "@(#)spnego_mech.c      1.7     04/09/28 SMI"
+
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       "gssapiP_spnego.h"
+#include       <mglueP.h>
+#include       <gssapi_err_generic.h>
+#include       <rpc/types.h>
+#include       <libintl.h>
+
+/* der routines defined in libgss */
+extern unsigned int der_length_size(OM_uint32);
+extern int get_der_length(uchar_t **, OM_uint32, OM_uint32*);
+extern int put_der_length(OM_uint32, uchar_t **, OM_uint32);
+
+
+/* private routines for spnego_mechanism */
+static spnego_token_t make_spnego_token(char *);
+static gss_buffer_desc make_err_msg(char *);
+static int g_token_size(gss_OID, OM_uint32);
+static int g_make_token_header(gss_OID, int, uchar_t **, int);
+static int g_verify_token_header(gss_OID, int *, uchar_t **, int, int);
+static int g_verify_neg_token_init(uchar_t **, int);
+static OM_uint32 get_negResult(unsigned char **, int);
+static gss_OID get_mech_oid(OM_uint32 *, uchar_t **, size_t);
+static gss_buffer_t get_input_token(unsigned char **, int);
+static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, int);
+static OM_uint32 get_req_flags(uchar_t **, int *, OM_uint32 *);
+static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
+       gss_cred_usage_t, gss_cred_id_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_req_flags(uchar_t **, OM_uint32, int);
+static int put_mech_set(uchar_t **, gss_OID_set, int);
+static int put_input_token(uchar_t **, gss_buffer_t, int);
+static int put_mech_oid(uchar_t **, gss_OID_desc *, int);
+static int put_negResult(uchar_t **, OM_uint32, int);
+
+static gss_OID
+negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
+               OM_uint32 *, bool_t *);
+static int
+g_get_tag_and_length(unsigned char **, uchar_t, int, int *);
+
+static int
+make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, gss_OID_set,
+                       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, int,
+                       gss_buffer_t);
+
+/*
+ * The Mech OID for SPNEGO:
+ * { iso(1) org(3) dod(6) internet(1) security(5)
+ *  mechanism(5) spnego(2) }
+ */
+static struct gss_config spnego_mechanism =
+{{SPNEGO_OID_LENGTH, SPNEGO_OID},
+       NULL,
+       spnego_gss_acquire_cred,
+       spnego_gss_release_cred,
+       spnego_gss_init_sec_context,
+       spnego_gss_accept_sec_context,
+/* EXPORT DELETE START */ /* CRYPT DELETE START */
+       spnego_gss_unseal,              /* gss_unseal */
+/* EXPORT DELETE END */ /* CRYPT DELETE END */
+       NULL,                           /* gss_process_context_token */
+       spnego_gss_delete_sec_context,  /* gss_delete_sec_context */
+       spnego_gss_context_time,        /* gss_context_time */
+       spnego_gss_display_status,
+       NULL,                           /* gss_indicate_mechs */
+       NULL,                           /* gss_compare_name */
+       spnego_gss_display_name,
+       spnego_gss_import_name,
+       spnego_gss_release_name,
+       NULL,                           /* gss_inquire_cred */
+       NULL,                           /* gss_add_cred */
+/* EXPORT DELETE START */ /* CRYPT DELETE START */
+       spnego_gss_seal,                /* gss_seal */
+/* EXPORT DELETE END */ /* CRYPT DELETE END */
+       spnego_gss_export_sec_context,  /* gss_export_sec_context */
+       spnego_gss_import_sec_context,  /* gss_import_sec_context */
+       NULL,                           /* gss_inquire_cred_by_mech */
+       spnego_gss_inquire_names_for_mech,
+       spnego_gss_inquire_context,     /* gss_inquire_context */
+       NULL,                           /* gss_internal_release_oid */
+       spnego_gss_wrap_size_limit,     /* gss_wrap_size_limit */
+       NULL,                           /* gss_pname_to_uid */
+       NULL,                           /* __gss_userok */
+       NULL,                           /* gss_export_name */
+/* EXPORT DELETE START */
+/* CRYPT DELETE START */
+#if 0
+/* CRYPT DELETE END */
+       spnego_gss_seal,
+       spnego_gss_unseal,
+/* CRYPT DELETE START */
+#endif
+/* CRYPT DELETE END */
+/* EXPORT DELETE END */
+       spnego_gss_sign,                /* gss_sign */
+       spnego_gss_verify,              /* gss_verify */
+       NULL,                           /* gss_store_cred */
+};
+
+gss_mechanism
+gss_mech_initialize(const gss_OID oid)
+{
+       dsyslog("Entering gss_mech_initialize\n");
+
+       if (oid == NULL ||
+               !g_OID_equal(oid, &spnego_mechanism.mech_type)) {
+               dsyslog("invalid spnego mechanism oid.\n");
+               return (NULL);
+       }
+
+       dsyslog("Leaving gss_mech_initialize\n");
+       return (&spnego_mechanism);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_acquire_cred(void *ctx,
+                       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)
+{
+       OM_uint32 status;
+       gss_OID_set amechs;
+       dsyslog("Entering spnego_gss_acquire_cred\n");
+
+       if (actual_mechs)
+               *actual_mechs = NULL;
+
+       if (time_rec)
+               *time_rec = 0;
+
+       /*
+        * If the user did not specify a list of mechs,
+        * use get_available_mechs to collect a list of
+        * mechs for which creds are available.
+        */
+       if (desired_mechs == GSS_C_NULL_OID_SET) {
+               status = get_available_mechs(minor_status,
+                               desired_name, cred_usage,
+                               output_cred_handle, &amechs);
+       } else {
+               /*
+                * The caller gave a specific list of mechanisms,
+                * so just get whatever creds are available.
+                * gss_acquire_creds will return the subset of mechs for
+                * which the given 'output_cred_handle' is valid.
+                */
+               status = gss_acquire_cred(minor_status,
+                               desired_name, time_req,
+                               desired_mechs, cred_usage,
+                               output_cred_handle, &amechs,
+                               time_rec);
+       }
+
+       if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
+               (void) gss_copy_oid_set(minor_status, amechs, actual_mechs);
+       }
+       (void) gss_release_oid_set(minor_status, &amechs);
+
+       dsyslog("Leaving spnego_gss_acquire_cred\n");
+       return (status);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_release_cred(void *ctx,
+                       OM_uint32 *minor_status,
+                       gss_cred_id_t *cred_handle)
+{
+       OM_uint32 status;
+
+       dsyslog("Entering spnego_gss_release_cred\n");
+
+       if (minor_status == NULL || cred_handle == NULL)
+               return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+       *minor_status = 0;
+
+       if (*cred_handle == GSS_C_NO_CREDENTIAL)
+               return (GSS_S_COMPLETE);
+
+       status = gss_release_cred(minor_status, cred_handle);
+
+       dsyslog("Leaving spnego_gss_release_cred\n");
+       return (status);
+}
+
+static void
+check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
+{
+       spnego_ctx->optionStr = __gss_get_modOptions(
+               (const gss_OID)&spnego_oids[0]);
+       if (spnego_ctx->optionStr != NULL &&
+               strstr(spnego_ctx->optionStr, "msinterop")) {
+                       spnego_ctx->MS_Interop = 1;
+       } else {
+               spnego_ctx->MS_Interop = 0;
+       }
+}
+
+static spnego_gss_ctx_id_t
+create_spnego_ctx(void)
+{
+       spnego_gss_ctx_id_t spnego_ctx = NULL;
+       spnego_ctx = (spnego_gss_ctx_id_t)
+               malloc(sizeof (spnego_gss_ctx_id_rec));
+
+       if (spnego_ctx == NULL) {
+               return (NULL);
+       }
+
+       spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
+       spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
+       spnego_ctx->internal_mech = NULL;
+       spnego_ctx->optionStr = NULL;
+       spnego_ctx->optimistic = 0;
+       spnego_ctx->MS_Interop = 0;
+       spnego_ctx->DER_mechTypes.length = NULL;
+       spnego_ctx->DER_mechTypes.value = GSS_C_NO_BUFFER;
+
+       check_spnego_options(spnego_ctx);
+
+       return (spnego_ctx);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_init_sec_context(void *ct,
+                       OM_uint32 *minor_status,
+                       gss_cred_id_t claimant_cred_handle,
+                       gss_ctx_id_t *context_handle,
+                       gss_name_t target_name,
+                       gss_OID mech_type,
+                       OM_uint32 req_flags,
+                       OM_uint32 time_req,
+                       gss_channel_bindings_t input_chan_bindings,
+                       gss_buffer_t input_token,
+                       gss_OID *actual_mech,
+                       gss_buffer_t output_token,
+                       OM_uint32 *ret_flags,
+                       OM_uint32 *time_rec)
+{
+       OM_uint32 ret = 0;
+       OM_uint32 status = 0;
+       OM_uint32 mstat;
+       OM_uint32 local_ret_flags = 0;
+
+       /*
+        * send_token is used to indicate in later steps
+        * what type of token, if any should be sent or processed.
+        * NO_TOKEN_SEND = no token should be sent
+        * INIT_TOKEN_SEND = initial token will be sent
+        * CONT_TOKEN_SEND = continuing tokens to be sent
+        * CHECK_MIC = no token to be sent, but have a MIC to check.
+        */
+       send_token_flag send_token = NO_TOKEN_SEND;
+
+       gss_OID_set mechSet;
+       spnego_gss_ctx_id_t spnego_ctx = NULL;
+       gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
+       gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
+       gss_buffer_t mechListMIC = NULL;
+       gss_cred_id_t *credlistptr = NULL, credlist;
+       gss_qop_t *qop_state = NULL;
+       unsigned char *ptr;
+       int len;
+
+       dsyslog("Entering init_sec_context\n");
+
+       if (context_handle == NULL)
+               return (GSS_S_FAILURE);
+
+       *minor_status = 0;
+       output_token->length = 0;
+       output_token->value = NULL;
+
+       if (actual_mech)
+               *actual_mech = NULL;
+
+       if (*context_handle == GSS_C_NO_CONTEXT) {
+
+               /* determine negotiation mech set */
+               if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
+                       credlistptr = &credlist;
+
+                       mstat = get_available_mechs(minor_status,
+                               GSS_C_NO_NAME, GSS_C_INITIATE,
+                               credlistptr, &mechSet);
+               } else {
+                       /*
+                        * Use the list of mechs included in the
+                        * cred that we were given.
+                        */
+                       mstat = gss_inquire_cred(minor_status,
+                                       claimant_cred_handle,
+                                       NULL, NULL, NULL,
+                                       &mechSet);
+               }
+               if (mstat != GSS_S_COMPLETE)
+                       return (mstat);
+
+               if ((spnego_ctx = create_spnego_ctx()) == NULL) {
+                       ret = GSS_S_FAILURE;
+                       goto cleanup;
+               }
+
+               /*
+                * need to pull the first mech from mechSet to do first
+                * init ctx
+                */
+               status = generic_gss_copy_oid(minor_status,
+                                       mechSet->elements,
+                                       &spnego_ctx->internal_mech);
+
+               if (status != GSS_S_COMPLETE) {
+                       ret = GSS_S_FAILURE;
+                       goto cleanup;
+               }
+
+               if (input_token != NULL && input_token->value != NULL) {
+                       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.
+                */
+               spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
+               *context_handle = (gss_ctx_id_t)spnego_ctx;
+               send_token = INIT_TOKEN_SEND;
+               ret = GSS_S_CONTINUE_NEEDED;
+       } else {
+               mechSet = NULL;
+               spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
+
+               if (input_token == NULL || input_token->value == NULL) {
+                       ret = GSS_S_FAILURE;
+                       goto cleanup;
+               }
+               ptr = (unsigned char *) input_token->value;
+
+               switch (get_negResult(&ptr, input_token->length)) {
+               case ACCEPT_DEFECTIVE_TOKEN:
+                       *minor_status = 1;
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       break;
+               case ACCEPT_INCOMPLETE: {
+                       /* pull out mech from token */
+                       gss_OID internal_mech =
+                               get_mech_oid(minor_status, &ptr,
+                               input_token->length -
+                               (ptr - (uchar_t *)input_token->value));
+
+                       /*
+                        * check if first mech in neg set, if it isn't,
+                        * release and copy chosen mech to context,
+                        * delete internal context from prior mech
+                        */
+                       if (internal_mech != NULL &&
+                           ((internal_mech->length !=
+                           spnego_ctx->internal_mech->length) ||
+                           /* CSTYLED */
+                           memcmp(spnego_ctx->internal_mech->elements,
+                                   internal_mech->elements,
+                                   spnego_ctx->internal_mech->length))) {
+
+                           (void) gss_delete_sec_context(&mstat,
+                                       &spnego_ctx->ctx_handle, NULL);
+
+                           spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
+                           (void) generic_gss_release_oid(
+                               &mstat, &spnego_ctx->internal_mech);
+
+                           status = generic_gss_copy_oid(
+                                       minor_status, internal_mech,
+                                       &spnego_ctx->internal_mech);
+
+                           if (status != GSS_S_COMPLETE)
+                               ret = GSS_S_DEFECTIVE_TOKEN;
+                           else
+                               ret = GSS_S_COMPLETE;
+
+                           (void) generic_gss_release_oid(&mstat,
+                               &internal_mech);
+                       } else if (internal_mech == NULL) {
+                               ret = GSS_S_DEFECTIVE_TOKEN;
+                               send_token = NO_TOKEN_SEND;
+                       } else {
+                               ret = GSS_S_COMPLETE;
+                       }
+                       if (ret == GSS_S_COMPLETE) {
+                       /*
+                        * Check for a token, it may contain
+                        * an error message.
+                        */
+                           if (*ptr ==  (CONTEXT | 0x02)) {
+                                   if (g_get_tag_and_length(&ptr,
+                                       (CONTEXT | 0x02),
+                                       input_token->length -
+                                       (ptr - (uchar_t *)input_token->value),
+                                       &len) < 0) {
+                                       ret = GSS_S_DEFECTIVE_TOKEN;
+                                   } else {
+                                       i_input_token = get_input_token(&ptr,
+                                               len);
+                                       if (i_input_token  != NULL) {
+                                               ret = GSS_S_CONTINUE_NEEDED;
+                                               send_token = CONT_TOKEN_SEND;
+                                       } else {
+                                               ret = GSS_S_DEFECTIVE_TOKEN;
+                                               send_token = NO_TOKEN_SEND;
+                                       }
+                                   }
+                           }
+                       }
+                       break;
+               }
+               case ACCEPT_COMPLETE:
+                       /* pull out mech from token */
+                       if (spnego_ctx->internal_mech != NULL)
+                               (void) generic_gss_release_oid(&mstat,
+                                   &spnego_ctx->internal_mech);
+
+                       spnego_ctx->internal_mech =
+                               get_mech_oid(minor_status, &ptr,
+                               input_token->length -
+                               (ptr - (uchar_t *)input_token->value));
+
+                       if (spnego_ctx->internal_mech == NULL) {
+                               /* CSTYLED */
+                               *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
+                               ret = GSS_S_FAILURE;
+                       }
+
+                       if (ret != GSS_S_FAILURE && *ptr == (CONTEXT | 0x02)) {
+                           if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
+                               input_token->length -
+                               (ptr - (uchar_t *)input_token->value),
+                               &len) < 0) {
+                               ret = GSS_S_DEFECTIVE_TOKEN;
+                           } else {
+                               i_input_token = get_input_token(&ptr, len);
+                               if (i_input_token  != NULL) {
+                                       ret = GSS_S_COMPLETE;
+                                       send_token = CHECK_MIC;
+                               } else {
+                                       ret = GSS_S_DEFECTIVE_TOKEN;
+                                       send_token = NO_TOKEN_SEND;
+                               }
+                           }
+                       } else if (ret == GSS_S_CONTINUE_NEEDED ||
+                               ret == GSS_S_COMPLETE) {
+                               send_token = CHECK_MIC;
+                       }
+
+                       /*
+                        * If we sent "optimistic" initial token,
+                        * but the acceptor did not send a response token,
+                        * this is an error.
+                        */
+                       if (ret == GSS_S_COMPLETE &&
+                           i_input_token == GSS_C_NO_BUFFER &&
+                           spnego_ctx->last_status == GSS_S_CONTINUE_NEEDED &&
+                           spnego_ctx->optimistic) {
+                               /* CSTYLED */
+                               *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
+                               ret = GSS_S_DEFECTIVE_TOKEN;
+                               send_token = NO_TOKEN_SEND;
+                       }
+
+                       if (send_token != NO_TOKEN_SEND) {
+                               if (i_input_token == NULL)
+                                       ret = GSS_S_COMPLETE;
+                               else
+                                       ret = GSS_S_CONTINUE_NEEDED;
+                               send_token = CHECK_MIC;
+                       }
+                       break;
+
+               case REJECT:
+                       ret = GSS_S_BAD_MECH;
+                       *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+                       send_token = NO_TOKEN_SEND;
+                       break;
+
+               default:
+                       ret = GSS_S_FAILURE;
+                       send_token = NO_TOKEN_SEND;
+                       break;
+               }
+       }
+
+       if (actual_mech) {
+               (void) generic_gss_release_oid(&mstat, actual_mech);
+               ret = generic_gss_copy_oid(&mstat,
+                       (gss_OID) gss_mech_spnego, actual_mech);
+               if (ret != GSS_S_COMPLETE)
+                       goto cleanup;
+       }
+
+       if (send_token == NO_TOKEN_SEND) {
+               output_token->length = 0;
+               output_token->value = NULL;
+               goto cleanup;
+       }
+
+       i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
+
+       if (i_output_token == NULL) {
+               ret = status = GSS_S_FAILURE;
+               goto cleanup;
+       }
+
+       i_output_token->length = 0;
+       i_output_token->value = NULL;
+
+       if (ret == GSS_S_CONTINUE_NEEDED) {
+               gss_OID inner_mech_type = GSS_C_NO_OID;
+
+               status = gss_init_sec_context(minor_status,
+                               claimant_cred_handle,
+                               &spnego_ctx->ctx_handle,
+                               target_name,
+                               spnego_ctx->internal_mech,
+                               req_flags,
+                               time_req,
+                               NULL,
+                               i_input_token,
+                               &inner_mech_type,
+                               i_output_token,
+                               &local_ret_flags,
+                               time_rec);
+
+               if (ret_flags)
+                       *ret_flags = local_ret_flags;
+
+               spnego_ctx->last_status = status;
+
+               if (i_input_token != GSS_C_NO_BUFFER) {
+                   (void) gss_release_buffer(&mstat, i_input_token);
+                   free(i_input_token);
+               }
+
+               if ((status != GSS_S_COMPLETE) &&
+                   (status != GSS_S_CONTINUE_NEEDED)) {
+                       ret = status;
+               }
+
+               /* create mic/check mic */
+               if ((i_output_token->length == 0) &&
+                   (status == GSS_S_COMPLETE) &&
+                   (local_ret_flags & GSS_C_INTEG_FLAG)) {
+                       if (*ptr == (CONTEXT | 0x03) &&
+                           g_get_tag_and_length(&ptr,
+                                       (CONTEXT | 0x03),
+                                       input_token->length -
+                                       (ptr - (uchar_t *)input_token->value),
+                                       &len) < 0) {
+                           ret = GSS_S_DEFECTIVE_TOKEN;
+                       } else {
+                           ret = GSS_S_COMPLETE;
+                           mechListMIC = get_input_token(&ptr, len);
+                           if (mechListMIC == NULL)
+                               ret = GSS_S_DEFECTIVE_TOKEN;
+                           else if (!spnego_ctx->MS_Interop &&
+                               spnego_ctx->DER_mechTypes.length > 0) {
+                               status = gss_verify_mic(minor_status,
+                                           spnego_ctx->ctx_handle,
+                                           &spnego_ctx->DER_mechTypes,
+                                           mechListMIC,
+                                           qop_state);
+                           }
+                       }
+               }
+       }
+
+       if ((status == GSS_S_COMPLETE) &&
+           (ret == GSS_S_COMPLETE)) {
+               /*
+                * Now, switch the output context to refer to the
+                * negotiated mechanism's context.
+                */
+               *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
+
+               if (actual_mech) {
+                       (void) generic_gss_release_oid(&mstat, actual_mech);
+                       ret = generic_gss_copy_oid(&mstat,
+                               spnego_ctx->internal_mech, actual_mech);
+                       if (ret != GSS_S_COMPLETE)
+                               goto cleanup;
+               }
+
+               release_spnego_ctx(&spnego_ctx);
+       } else if (ret == GSS_S_CONTINUE_NEEDED) {
+               if (make_spnego_tokenInit_msg(spnego_ctx,
+                               mechSet, req_flags,
+                               i_output_token, send_token,
+                               output_token) < 0) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+               }
+       }
+
+cleanup:
+       if (status != GSS_S_COMPLETE)
+               ret = status;
+       if (ret != GSS_S_COMPLETE &&
+           ret != GSS_S_CONTINUE_NEEDED) {
+               if (spnego_ctx != NULL &&
+                   spnego_ctx->ctx_handle != NULL)
+                       free(spnego_ctx->ctx_handle);
+
+               if (spnego_ctx != NULL)
+                       release_spnego_ctx(&spnego_ctx);
+
+               *context_handle = GSS_C_NO_CONTEXT;
+
+               if (output_token)
+                       (void) gss_release_buffer(&mstat, output_token);
+       }
+
+       if (i_output_token != GSS_C_NO_BUFFER) {
+               (void) gss_release_buffer(&mstat, i_output_token);
+               free(i_output_token);
+       }
+
+       if (mechListMIC != GSS_C_NO_BUFFER) {
+               (void) gss_release_buffer(&mstat, mechListMIC);
+               free(mechListMIC);
+       }
+
+       if (mechSet != NULL)
+               (void) gss_release_oid_set(&mstat, &mechSet);
+
+       if (credlistptr != NULL)
+               (void) gss_release_cred(&mstat, credlistptr);
+
+       return (ret);
+} /* init_sec_context */
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_accept_sec_context(void *ct,
+                           OM_uint32 *minor_status,
+                           gss_ctx_id_t *context_handle,
+                           gss_cred_id_t verifier_cred_handle,
+                           gss_buffer_t input_token,
+                           gss_channel_bindings_t input_chan_bindings,
+                           gss_name_t *src_name,
+                           gss_OID *mech_type,
+                           gss_buffer_t output_token,
+                           OM_uint32 *ret_flags,
+                           OM_uint32 *time_rec,
+                           gss_cred_id_t *delegated_cred_handle)
+{
+       spnego_gss_ctx_id_t spnego_ctx = NULL;
+       gss_OID mech_wanted = NULL;
+       gss_OID_set mechSet = GSS_C_NO_OID_SET;
+       gss_OID_set supported_mechSet = GSS_C_NO_OID_SET;
+       gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
+       gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
+       gss_buffer_t mechListMIC = GSS_C_NO_BUFFER;
+       gss_cred_id_t acquired_cred = NULL;
+       gss_name_t internal_name = GSS_C_NO_NAME;
+       OM_uint32 status = GSS_S_COMPLETE;
+       OM_uint32 ret = GSS_S_COMPLETE;
+       unsigned char *ptr;
+       unsigned char *bufstart;
+       int bodysize;
+       int err, len;
+       OM_uint32 negResult;
+       OM_uint32 minor_stat;
+       OM_uint32 mstat;
+       OM_uint32 req_flags;
+       OM_uint32 mechsetlen;
+       gss_qop_t qop_state;
+       send_token_flag return_token =  NO_TOKEN_SEND;
+       bool_t firstMech;
+       bool_t Need_Cred = FALSE;
+       OM_uint32 local_ret_flags = 0;
+       uchar_t *buf, *tmp;
+
+       dsyslog("Entering accept_sec_context\n");
+
+       if (context_handle == NULL)
+               return (GSS_S_FAILURE);
+
+       if (src_name)
+               *src_name = (gss_name_t)NULL;
+
+       output_token->length = 0;
+       output_token->value = NULL;
+       *minor_status = 0;
+
+       if (mech_type)
+               *mech_type = GSS_C_NULL_OID;
+
+       /* return a bogus cred handle */
+       if (delegated_cred_handle)
+               *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+
+       if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
+               Need_Cred = TRUE;
+       }
+
+       /* Check for defective input token. */
+       ptr = bufstart = (unsigned char *) input_token->value;
+       if (err = g_verify_token_header((gss_OID)gss_mech_spnego, &bodysize,
+                                       &ptr, 0,
+                                       input_token->length)) {
+               *minor_status = err;
+               ret = GSS_S_DEFECTIVE_TOKEN;
+               negResult = REJECT;
+               return_token = ERROR_TOKEN_SEND;
+               goto senderror;
+       }
+
+       /*
+        * set up of context, determine mech to be used, save mechset
+        * for use later in integrety check.
+        */
+       if (*context_handle == GSS_C_NO_CONTEXT) {
+               if ((spnego_ctx = create_spnego_ctx()) == NULL)
+                       return (GSS_S_FAILURE);
+
+               /*
+                * Until the accept operation is complete, the
+                * context_handle returned should refer to
+                * the spnego context.
+                */
+               *context_handle = (gss_ctx_id_t)spnego_ctx;
+               minor_stat = get_available_mechs(minor_status,
+                               GSS_C_NO_NAME, GSS_C_ACCEPT,
+                               NULL, &supported_mechSet);
+
+               if (minor_stat != GSS_S_COMPLETE) {
+                       release_spnego_ctx(&spnego_ctx);
+                       *context_handle = GSS_C_NO_CONTEXT;
+                       return (minor_stat);
+               }
+
+               if (Need_Cred) {
+                       minor_stat = gss_acquire_cred(minor_status,
+                                       GSS_C_NO_NAME, NULL,
+                                       supported_mechSet,
+                                       GSS_C_ACCEPT,
+                                       &acquired_cred, NULL,
+                                       NULL);
+
+                       if (minor_stat != GSS_S_COMPLETE) {
+                               (void) gss_release_oid_set(minor_status,
+                                               &supported_mechSet);
+                               release_spnego_ctx(&spnego_ctx);
+                               *context_handle = GSS_C_NO_CONTEXT;
+                               return (minor_stat);
+                       } else {
+                               verifier_cred_handle = acquired_cred;
+                       }
+               }
+
+               if (err = g_verify_neg_token_init(&ptr, input_token->length)) {
+                       *minor_status = err;
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       negResult = REJECT;
+                       return_token = ERROR_TOKEN_SEND;
+                       goto senderror;
+               }
+
+               /*
+                * Allocate space to hold the mechTypes
+                * because we need it later.
+                */
+               mechsetlen = input_token->length - (ptr - bufstart);
+               buf = (uchar_t *)malloc(mechsetlen);
+               if (buf == NULL) {
+                       ret = GSS_S_FAILURE;
+                       goto cleanup;
+               }
+               (void) memcpy(buf, ptr, mechsetlen);
+               ptr = bufstart = buf;
+
+               /*
+                * Get pointers to the DER encoded MechSet so we
+                * can properly check and calculate a MIC later.
+                */
+               spnego_ctx->DER_mechTypes.value = ptr;
+               mechSet = get_mech_set(minor_status, &ptr, mechsetlen);
+               if (mechSet == NULL) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       negResult = REJECT;
+                       return_token = ERROR_TOKEN_SEND;
+                       goto senderror;
+               }
+               spnego_ctx->DER_mechTypes.length = ptr - bufstart;
+               mechsetlen -= (ptr - bufstart);
+
+               /*
+                * Select the best match between the list of mechs
+                * that the initiator requested and the list that
+                * the acceptor will support.
+                */
+               mech_wanted = negotiate_mech_type(minor_status,
+                                               supported_mechSet,
+                                               mechSet,
+                                               &negResult,
+                                               &firstMech);
+
+               (void) gss_release_oid_set(&minor_stat, &supported_mechSet);
+               (void) gss_release_oid_set(&minor_stat, &mechSet);
+               supported_mechSet = NULL;
+               mechSet = NULL;
+
+               if (get_req_flags(&ptr, (int *)&mechsetlen, &req_flags) ==
+                       ACCEPT_DEFECTIVE_TOKEN) {
+                       negResult = REJECT;
+               }
+
+               tmp = ptr;
+               if (negResult == ACCEPT_COMPLETE) {
+                       if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
+                                               mechsetlen,
+                                               &len) < 0) {
+                               negResult = REJECT;
+                       } else {
+                               i_input_token = get_input_token(&ptr, len);
+                               if (i_input_token == NULL) {
+                                       negResult = REJECT;
+                               }
+                       }
+                       return_token = INIT_TOKEN_SEND;
+               }
+               if (negResult == REJECT) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       return_token = ERROR_TOKEN_SEND;
+               } else {
+                       ret = GSS_S_CONTINUE_NEEDED;
+                       return_token = INIT_TOKEN_SEND;
+               }
+
+               mechsetlen -= ptr - tmp;
+               /*
+                * Check to see if there is a MechListMIC field
+                */
+               if (negResult == ACCEPT_COMPLETE && mechsetlen > 0) {
+                       tmp = ptr;
+                       if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
+                                               mechsetlen, &len) >= 0) {
+                               mechListMIC = get_input_token(&ptr, len);
+                               if (mechListMIC == GSS_C_NO_BUFFER) {
+                                       negResult = REJECT;
+                                       return_token = ERROR_TOKEN_SEND;
+                                       ret = GSS_S_DEFECTIVE_TOKEN;
+                               }
+                               mechsetlen -= (ptr - tmp);
+                       }
+               }
+       } else {
+               /*
+                * get internal input token and context for continued
+                * calls of spnego_gss_init_sec_context.
+                */
+               i_input_token = get_input_token(&ptr,
+                                       input_token->length -
+                                       (ptr - (uchar_t *)input_token->value));
+               if (i_input_token == NULL) {
+                       negResult = REJECT;
+                       return_token = ERROR_TOKEN_SEND;
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+               } else {
+                       spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
+                       return_token = CONT_TOKEN_SEND;
+               }
+       }
+
+       /*
+        * If we still don't have a cred, we have an error.
+        */
+       if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
+               ret = GSS_S_FAILURE;
+               goto cleanup;
+       }
+
+       /* If we have an error already, bail out */
+       if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED)
+               goto senderror;
+
+       if (i_input_token != GSS_C_NO_BUFFER) {
+               i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
+
+               if (i_output_token == NULL) {
+                       ret = GSS_S_FAILURE;
+                       goto cleanup;
+               }
+
+               i_output_token->length = 0;
+               i_output_token->value = NULL;
+
+               status = gss_accept_sec_context(&minor_stat,
+                                               &spnego_ctx->ctx_handle,
+                                               verifier_cred_handle,
+                                               i_input_token,
+                                               GSS_C_NO_CHANNEL_BINDINGS,
+                                               &internal_name,
+                                               mech_type,
+                                               i_output_token,
+                                               &local_ret_flags,
+                                               time_rec,
+                                               delegated_cred_handle);
+
+               if ((status != GSS_S_COMPLETE) &&
+                   (status != GSS_S_CONTINUE_NEEDED)) {
+                       *minor_status = minor_stat;
+                       (void) gss_release_buffer(&mstat, i_input_token);
+
+                       if (i_input_token != GSS_C_NO_BUFFER) {
+                               free(i_input_token);
+                               i_input_token = GSS_C_NO_BUFFER;
+                       }
+
+                       ret = status;
+
+                       /*
+                        * Reject the request with an error token.
+                        */
+                       negResult = REJECT;
+                       return_token = ERROR_TOKEN_SEND;
+
+                       goto senderror;
+               }
+
+               if (ret_flags)
+                       *ret_flags = local_ret_flags;
+
+               if (i_input_token != GSS_C_NO_BUFFER) {
+                       (void) gss_release_buffer(&mstat, i_input_token);
+                       free(i_input_token);
+                       i_input_token = GSS_C_NO_BUFFER;
+               }
+
+               /* If we got a MIC, verify it if possible */
+               if ((status == GSS_S_COMPLETE) &&
+                   (local_ret_flags & GSS_C_INTEG_FLAG) &&
+                   mechListMIC != GSS_C_NO_BUFFER &&
+                   !spnego_ctx->MS_Interop) {
+
+                       ret = gss_verify_mic(minor_status,
+                                   spnego_ctx->ctx_handle,
+                                   &spnego_ctx->DER_mechTypes,
+                                   mechListMIC,
+                                   &qop_state);
+
+                       (void) gss_release_buffer(&mstat, mechListMIC);
+                       free(mechListMIC);
+                       mechListMIC = GSS_C_NO_BUFFER;
+
+                       if (ret != GSS_S_COMPLETE) {
+                               negResult = REJECT;
+                               return_token = ERROR_TOKEN_SEND;
+                               goto senderror;
+                       }
+               }
+
+               /*
+                * If the MIC was verified OK, create a new MIC
+                * for the response message.
+                */
+               if (status == GSS_S_COMPLETE &&
+                   (local_ret_flags & GSS_C_INTEG_FLAG) &&
+                   !spnego_ctx->MS_Interop) {
+
+                       mechListMIC = (gss_buffer_t)
+                               malloc(sizeof (gss_buffer_desc));
+
+                       if (mechListMIC == NULL ||
+                           spnego_ctx->DER_mechTypes.length == 0) {
+                               ret = GSS_S_FAILURE;
+                               goto cleanup;
+                       }
+
+                       ret = gss_get_mic(minor_status,
+                                       spnego_ctx->ctx_handle,
+                                       GSS_C_QOP_DEFAULT,
+                                       &spnego_ctx->DER_mechTypes,
+                                       mechListMIC);
+
+                       if (ret != GSS_S_COMPLETE) {
+                               negResult = REJECT;
+                               return_token = ERROR_TOKEN_SEND;
+                               goto senderror;
+                       }
+               }
+               ret = status;
+
+               /*
+                * If we got this far OK, then switch the
+                * returned context handle to reference the
+                * "real" context handle for the mech.
+                */
+               if (status == GSS_S_COMPLETE) {
+                       *context_handle =
+                               (gss_ctx_id_t)spnego_ctx->ctx_handle;
+                       if (internal_name != NULL && src_name != NULL)
+                               *src_name = internal_name;
+               }
+
+
+               if (status == GSS_S_CONTINUE_NEEDED) {
+                       if (return_token == INIT_TOKEN_SEND)
+                               negResult = ACCEPT_INCOMPLETE;
+               }
+       }
+
+senderror:
+       if ((return_token == INIT_TOKEN_SEND) ||
+           (return_token == CONT_TOKEN_SEND) ||
+           (return_token == ERROR_TOKEN_SEND)) {
+               int MS_Interop = 0;
+
+               if (spnego_ctx)
+                       MS_Interop = spnego_ctx->MS_Interop;
+
+               /*
+                * create response for the initiator.
+                */
+               err = make_spnego_tokenTarg_msg(negResult,
+                                       mech_wanted,
+                                       i_output_token,
+                                       mechListMIC,
+                                       return_token,
+                                       MS_Interop,
+                                       output_token);
+
+               (void) gss_release_buffer(&mstat, mechListMIC);
+               free(mechListMIC);
+
+               /*
+                * If we could not make the response token,
+                * we will have to fail without sending a response.
+                */
+               if (err) {
+                       (void) gss_release_buffer(&mstat, output_token);
+               }
+       } else {
+               (void) gss_release_buffer(&mstat, output_token);
+       }
+       release_spnego_ctx(&spnego_ctx);
+
+cleanup:
+       if (ret != GSS_S_COMPLETE &&
+           ret != GSS_S_CONTINUE_NEEDED) {
+               if (spnego_ctx != NULL) {
+                       (void) gss_delete_sec_context(&mstat,
+                               &spnego_ctx->ctx_handle, NULL);
+
+                       spnego_ctx->ctx_handle = NULL;
+
+                       release_spnego_ctx(&spnego_ctx);
+               }
+               *context_handle = GSS_C_NO_CONTEXT;
+       }
+       if (mech_wanted != NULL) {
+               generic_gss_release_oid(&mstat, &mech_wanted);
+       }
+
+       (void) gss_release_cred(minor_status, &acquired_cred);
+       (void) gss_release_oid_set(minor_status, &supported_mechSet);
+
+       (void) gss_release_buffer(&mstat, i_output_token);
+       free(i_output_token);
+
+       return (ret);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_display_status(void *ctx,
+               OM_uint32 *minor_status,
+               OM_uint32 status_value,
+               int status_type,
+               gss_OID mech_type,
+               OM_uint32 *message_context,
+               gss_buffer_t status_string)
+{
+       dsyslog("Entering display_status\n");
+
+       *message_context = 0;
+       switch (status_value) {
+           case ERR_SPNEGO_NO_MECHS_AVAILABLE:
+               /* CSTYLED */
+               *status_string = make_err_msg(gettext("SPNEGO cannot find mechanisms to negotiate"));
+               break;
+           case ERR_SPNEGO_NO_CREDS_ACQUIRED:
+               /* CSTYLED */
+               *status_string = make_err_msg(gettext("SPNEGO failed to acquire creds"));
+               break;
+           case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
+               /* CSTYLED */
+               *status_string = make_err_msg(gettext("SPNEGO acceptor did not select a mechanism"));
+               break;
+           case ERR_SPNEGO_NEGOTIATION_FAILED:
+               /* CSTYLED */
+               *status_string = make_err_msg(gettext("SPNEGO failed to negotiate a mechanism"));
+               break;
+           case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
+               /* CSTYLED */
+               *status_string = make_err_msg(gettext("SPNEGO acceptor did not return a valid token"));
+               break;
+           default:
+               status_string->length = 0;
+               status_string->value = "";
+               break;
+       }
+
+       dsyslog("Leaving display_status\n");
+       return (GSS_S_COMPLETE);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_import_name(void *ctx,
+                   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);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_release_name(void *ctx,
+                       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);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_display_name(void *ctx,
+                       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);
+}
+
+/*ARGSUSED*/
+OM_uint32
+spnego_gss_inquire_names_for_mech(void *ctx,
+                               OM_uint32       *minor_status,
+                               gss_OID         mechanism,
+                               gss_OID_set     *name_types)
+{
+       OM_uint32   major, minor;
+
+       dsyslog("Entering inquire_names_for_mech\n");
+       /*
+        * We only know how to handle our own mechanism.
+        */
+       if ((mechanism != GSS_C_NULL_OID) &&
+           !g_OID_equal(gss_mech_spnego, mechanism)) {
+               *minor_status = 0;
+               return (GSS_S_FAILURE);
+       }
+
+       major = gss_create_empty_oid_set(minor_status, name_types);
+       if (major == GSS_S_COMPLETE) {
+               /* Now add our members. */
+               if (((major = gss_add_oid_set_member(minor_status,
+                               (gss_OID) GSS_C_NT_USER_NAME,
+                               name_types)) == GSS_S_COMPLETE) &&
+                   ((major = gss_add_oid_set_member(minor_status,
+                               (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
+                               name_types)) == GSS_S_COMPLETE) &&
+                   ((major = gss_add_oid_set_member(minor_status,
+                               (gss_OID) GSS_C_NT_STRING_UID_NAME,
+                               name_types)) == GSS_S_COMPLETE)) {
+                       major = gss_add_oid_set_member(minor_status,
+                               (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
+                               name_types);
+               }
+
+               if (major != GSS_S_COMPLETE)
+                       (void) gss_release_oid_set(&minor, name_types);
+       }
+
+       dsyslog("Leaving inquire_names_for_mech\n");
+       return (major);
+}
+
+OM_uint32
+spnego_gss_unseal(void *context,
+               OM_uint32 *minor_status,
+               gss_ctx_id_t context_handle,
+               gss_buffer_t input_message_buffer,
+               gss_buffer_t output_message_buffer,
+               int *conf_state,
+               int *qop_state)
+{
+       OM_uint32 ret;
+       ret = gss_unseal(minor_status,
+                       context_handle,
+                       input_message_buffer,
+                       output_message_buffer,
+                       conf_state,
+                       qop_state);
+
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_seal(void *context,
+               OM_uint32 *minor_status,
+               gss_ctx_id_t context_handle,
+               int conf_req_flag,
+               int qop_req,
+               gss_buffer_t input_message_buffer,
+               int *conf_state,
+               gss_buffer_t output_message_buffer)
+{
+       OM_uint32 ret;
+       ret = gss_seal(minor_status,
+                   context_handle,
+                   conf_req_flag,
+                   qop_req,
+                   input_message_buffer,
+                   conf_state,
+                   output_message_buffer);
+
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_process_context_token(void *context,
+                               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);
+}
+
+OM_uint32
+spnego_gss_delete_sec_context(void *context,
+                           OM_uint32 *minor_status,
+                           gss_ctx_id_t *context_handle,
+                           gss_buffer_t output_token)
+{
+       OM_uint32 ret = GSS_S_COMPLETE;
+       spnego_gss_ctx_id_t *ctx =
+                   (spnego_gss_ctx_id_t *)context_handle;
+
+       if (context_handle == NULL)
+               return (GSS_S_FAILURE);
+
+       /*
+        * If this is still an SPNEGO mech, release it locally.
+        */
+       if (*ctx != NULL &&
+           (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
+               (void) release_spnego_ctx(ctx);
+       } else {
+               ret = gss_delete_sec_context(minor_status,
+                                   context_handle,
+                                   output_token);
+       }
+
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_context_time(void *context,
+                       OM_uint32       *minor_status,
+                       const gss_ctx_id_t context_handle,
+                       OM_uint32       *time_rec)
+{
+       OM_uint32 ret;
+       ret = gss_context_time(minor_status,
+                           context_handle,
+                           time_rec);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_export_sec_context(void *context,
+                           OM_uint32     *minor_status,
+                           gss_ctx_id_t *context_handle,
+                           gss_buffer_t interprocess_token)
+{
+       OM_uint32 ret;
+       ret = gss_export_sec_context(minor_status,
+                                   context_handle,
+                                   interprocess_token);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_import_sec_context(void *context,
+       OM_uint32               *minor_status,
+       const gss_buffer_t      interprocess_token,
+       gss_ctx_id_t            *context_handle)
+{
+       OM_uint32 ret;
+       ret = gss_import_sec_context(minor_status,
+                                   interprocess_token,
+                                   context_handle);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_inquire_context(void *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             *open)
+{
+       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,
+                               open);
+
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_wrap_size_limit(void *context,
+       OM_uint32       *minor_status,
+       const gss_ctx_id_t context_handle,
+       int             conf_req_flag,
+       gss_qop_t       qop_req,
+       OM_uint32       req_output_size,
+       OM_uint32       *max_input_size)
+{
+       OM_uint32 ret;
+       ret = gss_wrap_size_limit(minor_status,
+                               context_handle,
+                               conf_req_flag,
+                               qop_req,
+                               req_output_size,
+                               max_input_size);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_sign(void *context,
+               OM_uint32 *minor_status,
+               const gss_ctx_id_t context_handle,
+               int  qop_req,
+               const gss_buffer_t message_buffer,
+               gss_buffer_t message_token)
+{
+       OM_uint32 ret;
+       ret = gss_sign(minor_status,
+                   context_handle,
+                   qop_req,
+                   message_buffer,
+                   message_token);
+       return (ret);
+}
+
+OM_uint32
+spnego_gss_verify(void *context,
+               OM_uint32 *minor_status,
+               const gss_ctx_id_t context_handle,
+               const gss_buffer_t msg_buffer,
+               const gss_buffer_t token_buffer,
+               int *qop_state)
+{
+       OM_uint32 ret;
+       ret = gss_verify_mic(minor_status,
+                           context_handle,
+                           msg_buffer,
+                           token_buffer,
+                           (uint32_t *)qop_state);
+       return (ret);
+}
+
+/*
+ * We will release everything but the ctx_handle so that it
+ * can be passed back to init/accept context. This routine should
+ * not be called until after the ctx_handle memory is assigned to
+ * the supplied context handle from init/accept context.
+ */
+static void
+release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
+{
+       spnego_gss_ctx_id_t context;
+       OM_uint32 minor_stat;
+       context = *ctx;
+
+       if (context != NULL) {
+               (void) gss_release_buffer(&minor_stat,
+                                       &context->DER_mechTypes);
+
+               (void) generic_gss_release_oid(&minor_stat,
+                               &context->internal_mech);
+
+               if (context->optionStr != NULL) {
+                       free(context->optionStr);
+                       context->optionStr = NULL;
+               }
+               free(context);
+               *ctx = NULL;
+       }
+}
+
+/*
+ * Can't use gss_indicate_mechs by itself to get available mechs for
+ * SPNEGO because it will also return the SPNEGO mech and we do not
+ * want to consider SPNEGO as an available security mech for
+ * negotiation. For this reason, get_available_mechs will return
+ * all available mechs except SPNEGO.
+ *
+ * If a ptr to a creds list is given, this function will attempt
+ * to acquire creds for the creds given and trim the list of
+ * returned mechanisms to only those for which creds are valid.
+ *
+ */
+static OM_uint32
+get_available_mechs(OM_uint32 *minor_status,
+       gss_name_t name, gss_cred_usage_t usage,
+       gss_cred_id_t *creds, gss_OID_set *rmechs)
+{
+       int             i;
+       int             found = 0;
+       OM_uint32 stat = GSS_S_COMPLETE;
+       gss_OID_set mechs, goodmechs;
+
+       stat = gss_indicate_mechs(minor_status, &mechs);
+
+       if (stat != GSS_S_COMPLETE) {
+               return (stat);
+       }
+
+       stat = gss_create_empty_oid_set(minor_status, rmechs);
+
+       if (stat != GSS_S_COMPLETE) {
+               (void) gss_release_oid_set(minor_status, &mechs);
+               return (stat);
+       }
+
+       for (i = 0; i < mechs->count && stat == GSS_S_COMPLETE; i++) {
+               if ((mechs->elements[i].length
+                   != spnego_mechanism.mech_type.length) ||
+                   memcmp(mechs->elements[i].elements,
+                       spnego_mechanism.mech_type.elements,
+                       spnego_mechanism.mech_type.length)) {
+
+                       stat = gss_add_oid_set_member(minor_status,
+                                           &mechs->elements[i],
+                                           rmechs);
+                       if (stat == GSS_S_COMPLETE)
+                               found++;
+               }
+       }
+
+       /*
+        * If the caller wanted a list of creds returned,
+        * trim the list of mechanisms down to only those
+        * for which the creds are valid.
+        */
+       if (found > 0 && stat == GSS_S_COMPLETE && creds != NULL) {
+               stat = gss_acquire_cred(minor_status,
+                       name, NULL, *rmechs, usage, creds,
+                       &goodmechs, NULL);
+
+               /*
+                * Drop the old list in favor of the new
+                * "trimmed" list.
+                */
+               (void) gss_release_oid_set(minor_status, rmechs);
+               if (stat == GSS_S_COMPLETE) {
+                       (void) gss_copy_oid_set(minor_status,
+                                       goodmechs, rmechs);
+                       (void) gss_release_oid_set(minor_status, &goodmechs);
+               }
+       }
+
+       (void) gss_release_oid_set(minor_status, &mechs);
+       if (found == 0 || stat != GSS_S_COMPLETE) {
+               *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
+               if (stat == GSS_S_COMPLETE)
+                       stat = GSS_S_FAILURE;
+       }
+
+       return (stat);
+}
+
+/* following are token creation and reading routines */
+
+/*
+ * If buff_in is not pointing to a MECH_OID, then return NULL and do not
+ * advance the buffer, otherwise, decode the mech_oid from the buffer and
+ * place in gss_OID.
+ */
+static gss_OID
+get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
+{
+       OM_uint32       status;
+       gss_OID_desc    toid;
+       gss_OID         mech_out = NULL;
+       uchar_t         *start, *end;
+
+       if (length < 1 || **buff_in != MECH_OID)
+               return (NULL);
+
+       start = *buff_in;
+       end = start + length;
+
+       (*buff_in)++;
+       toid.length = *(*buff_in)++;
+
+       if ((*buff_in + toid.length) > end)
+               return (NULL);
+
+       toid.elements = *buff_in;
+       *buff_in += toid.length;
+
+       status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
+
+       if (status != GSS_S_COMPLETE)
+               mech_out = NULL;
+
+       return (mech_out);
+}
+
+/*
+ * der encode the given mechanism oid into buf_out, advancing the
+ * buffer pointer.
+ */
+
+static int
+put_mech_oid(unsigned char **buf_out, gss_OID_desc *mech, int buflen)
+{
+       if (buflen < mech->length + 2)
+               return (-1);
+       *(*buf_out)++ = MECH_OID;
+       *(*buf_out)++ = (unsigned char) mech->length;
+       memcpy((void *)(*buf_out), mech->elements, mech->length);
+       *buf_out += mech->length;
+       return (0);
+}
+
+/*
+ * verify that buff_in points to an octet string, if it does not,
+ * return NULL and don't advance the pointer. If it is an octet string
+ * decode buff_in into a gss_buffer_t and return it, advancing the
+ * buffer pointer.
+ */
+static gss_buffer_t
+get_input_token(unsigned char **buff_in, int buff_length)
+{
+       gss_buffer_t input_token;
+       unsigned int bytes;
+
+       if (**buff_in != OCTET_STRING)
+               return (NULL);
+
+       (*buff_in)++;
+       input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
+
+       if (input_token == NULL)
+               return (NULL);
+
+       input_token->length = get_der_length(buff_in, buff_length, &bytes);
+       if ((int)input_token->length == -1) {
+               free(input_token);
+               return (NULL);
+       }
+       input_token->value = malloc(input_token->length);
+
+       if (input_token->value == NULL) {
+               free(input_token);
+               return (NULL);
+       }
+
+       (void) memcpy(input_token->value, *buff_in, input_token->length);
+       *buff_in += input_token->length;
+       return (input_token);
+}
+
+/*
+ * verify that the input token length is not 0. If it is, just return.
+ * If the token length is greater than 0, der encode as an octet string
+ * and place in buf_out, advancing buf_out.
+ */
+
+static int
+put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
+               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 = put_der_length(input_token->length, buf_out,
+                           input_token->length)))
+               return (ret);
+       TWRITE_STR(*buf_out, input_token->value, ((int)input_token->length));
+       return (0);
+}
+
+/*
+ * verify that buff_in points to a sequence of der encoding. The mech
+ * set is the only sequence of encoded object in the token, so if it is
+ * a sequence of encoding, decode the mechset into a gss_OID_set and
+ * return it, advancing the buffer pointer.
+ */
+static gss_OID_set
+get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, int buff_length)
+{
+       gss_OID_set returned_mechSet;
+       OM_uint32 major_status;
+       OM_uint32 length;
+       OM_uint32 bytes;
+       OM_uint32 set_length;
+       uchar_t         *start;
+       int i;
+
+       if (**buff_in != SEQUENCE_OF)
+               return (NULL);
+
+       start = *buff_in;
+       (*buff_in)++;
+
+       length = get_der_length(buff_in, buff_length, &bytes);
+
+       major_status = gss_create_empty_oid_set(minor_status,
+                                               &returned_mechSet);
+       if (major_status != GSS_S_COMPLETE)
+               return (NULL);
+
+       for (set_length = 0, i = 0; set_length < length; i++) {
+               gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
+                       buff_length - (*buff_in - start));
+               if (temp != NULL) {
+                   major_status = gss_add_oid_set_member(minor_status,
+                                       temp, &returned_mechSet);
+                   if (major_status == GSS_S_COMPLETE) {
+                       set_length += returned_mechSet->elements[i].length +2;
+                       generic_gss_release_oid(minor_status, &temp);
+                   }
+               }
+       }
+
+       return (returned_mechSet);
+}
+
+/*
+ * der encode the passed mechSet and place it into buf_out,
+ * advancing the buffer pointer.
+ */
+static int
+put_mech_set(uchar_t **buf_out, gss_OID_set mechSet, int buflen)
+{
+       int i, ret;
+       OM_uint32 length = 0;
+       uchar_t *start;
+
+       if (buf_out == NULL || *buf_out == NULL)
+               return (-1);
+
+       start = *buf_out;
+
+       *(*buf_out)++ = SEQUENCE_OF;
+
+       for (i = 0; i < mechSet->count; i++) {
+               /*
+                * Mech OID ASN.1 size = 2 + length.
+                * 1 = 0x06, 1 for length of OID
+                * typically, less than 128, so only 1 byte needed.
+                */
+               length += 1 + der_length_size(mechSet->elements[i].length) +
+                       mechSet->elements[i].length;
+       }
+       if (length > (buflen-1))
+               return (-1);
+
+       if (put_der_length(length, buf_out, buflen-1) < 0)
+               return (-1);
+
+       for (i = 0; i < mechSet->count; i++) {
+               if ((ret = put_mech_oid(buf_out, &mechSet->elements[i],
+                       buflen - (int)(*buf_out  - start))))
+                       return (ret);
+       }
+       return (0);
+}
+
+/*
+ * Verify that buff_in is pointing to a BIT_STRING with the correct
+ * length and padding for the req_flags. If it is, decode req_flags
+ * and return them, otherwise, return NULL.
+ */
+static OM_uint32
+get_req_flags(unsigned char **buff_in, int *bodysize, OM_uint32 *req_flags)
+{
+       int len;
+       uchar_t *start = *buff_in;
+
+       if (**buff_in != (CONTEXT | 0x01))
+               return (0);
+
+       if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
+                               *bodysize, &len) < 0)
+               return (ACCEPT_DEFECTIVE_TOKEN);
+
+       if (*(*buff_in)++ != BIT_STRING)
+               return (ACCEPT_DEFECTIVE_TOKEN);
+
+       if (*(*buff_in)++ != BIT_STRING_LENGTH)
+               return (ACCEPT_DEFECTIVE_TOKEN);
+
+       if (*(*buff_in)++ != BIT_STRING_PADDING)
+               return (ACCEPT_DEFECTIVE_TOKEN);
+
+       *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
+       *bodysize -= *buff_in - start;
+       return (0);
+}
+
+/*
+ * der encode the passed req_flags into buf_out, advancing
+ * the buffer pointer.
+ */
+
+static int
+put_req_flags(unsigned char **buf_out, OM_uint32 req_flags, int buflen)
+{
+       int ret = 0;
+       if (buflen < 6)
+               return (-1);
+
+       *(*buf_out)++ = CONTEXT | 0x01;
+       if ((ret = put_der_length(4, buf_out, buflen-1)) != 0)
+               return (ret);
+
+       *(*buf_out)++ = BIT_STRING;
+       *(*buf_out)++ = BIT_STRING_LENGTH;
+       *(*buf_out)++ = BIT_STRING_PADDING;
+       *(*buf_out)++ = (unsigned char) (req_flags << 1);
+       return (ret);
+}
+
+/*
+ * get the negotiation results, decoding the ENUMERATED type result
+ * from the buffer, advancing the buffer pointer.
+ */
+static OM_uint32
+get_negResult(unsigned char **buff_in, int bodysize)
+{
+       unsigned char *iptr = *buff_in;
+       int len;
+       unsigned int bytes;
+       OM_uint32 result;
+       /*
+        * Verify that the data is ASN.1 encoded correctly
+        */
+       if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
+                               bodysize, &len) < 0)
+               return (ACCEPT_DEFECTIVE_TOKEN);
+
+       if (*(*buff_in)++ == SEQUENCE) {
+               if ((len = get_der_length(buff_in,
+                               bodysize - (*buff_in - iptr),
+                               &bytes)) < 0)
+                       return (ACCEPT_DEFECTIVE_TOKEN);
+       } else {
+               return (ACCEPT_INCOMPLETE);
+       }
+
+       /*
+        * if we find an octet string, we need to return
+        * incomplete so that we process the token correctly.
+        * Anything else unexpected, we reject.
+        */
+       if (*(*buff_in)++ == CONTEXT) {
+               if ((len = get_der_length(buff_in, bodysize -
+                               (*buff_in - iptr), &bytes)) < 0)
+                       return (ACCEPT_DEFECTIVE_TOKEN);
+       } else {
+               return (ACCEPT_INCOMPLETE);
+       }
+
+       if (*(*buff_in) == OCTET_STRING)
+               return (ACCEPT_INCOMPLETE);
+
+       if (*(*buff_in)++ != ENUMERATED)
+               return (ACCEPT_DEFECTIVE_TOKEN);
+
+       if (*(*buff_in)++ != ENUMERATION_LENGTH)
+               return (ACCEPT_DEFECTIVE_TOKEN);
+
+       /*
+        * Save the result byte to return later.
+        * This is the result
+        */
+       result = (OM_uint32)*(*buff_in)++;
+
+       if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
+                               bodysize - (*buff_in - iptr),
+                               &len) < 0)
+               result = ACCEPT_DEFECTIVE_TOKEN;
+
+       return (result);
+}
+
+/*
+ * der encode the passed negResults as an ENUMERATED type and
+ * place it in buf_out, advancing the buffer.
+ */
+
+static int
+put_negResult(uchar_t **buf_out, OM_uint32 negResult, int buflen)
+{
+       if (buflen < 3)
+               return (-1);
+       *(*buf_out)++ = ENUMERATED;
+       *(*buf_out)++ = ENUMERATION_LENGTH;
+       *(*buf_out)++ = (unsigned char) negResult;
+       return (0);
+}
+
+/*
+ * This routine compares the recieved mechset to the mechset that
+ * this server can support. It looks sequentially through the mechset
+ * and the first one that matches what the server can support is
+ * chosen as the negotiated mechanism. If one is found, negResult
+ * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult
+ * is set to REJECT. Also, for purposes of determining latter behavior,
+ * the flag, firstMech is used to indicate if the chosen mechanism is the
+ * first of the mechset or not.
+ */
+static gss_OID
+negotiate_mech_type(OM_uint32 *minor_status,
+               gss_OID_set supported_mechSet,
+               gss_OID_set mechset,
+               OM_uint32 *negResult,
+               bool_t *firstMech)
+{
+       gss_OID returned_mech;
+       OM_uint32 status;
+       int present;
+       int i;
+
+       for (i = 0; i < mechset->count; i++) {
+               gss_test_oid_set_member(minor_status, &mechset->elements[i],
+                                       supported_mechSet, &present);
+               if (present == TRUE) {
+                       *negResult = ACCEPT_COMPLETE;
+
+                       if (i == 0)
+                               *firstMech = TRUE;
+                       else
+                               *firstMech = FALSE;
+
+                       status = generic_gss_copy_oid(minor_status,
+                                                   &mechset->elements[i],
+                                                   &returned_mech);
+
+                       if (status != GSS_S_COMPLETE) {
+                               *negResult = REJECT;
+                               return (NULL);
+                       }
+
+                       return (returned_mech);
+               }
+       }
+
+       *negResult = REJECT;
+       return (NULL);
+}
+
+/*
+ * the next two routines make a token buffer suitable for
+ * spnego_gss_display_status. These currently take the string
+ * in name and place it in the token. Eventually, if
+ * spnego_gss_display_status returns valid error messages,
+ * these routines will be changes to return the error string.
+ */
+static spnego_token_t
+make_spnego_token(char *name)
+{
+       spnego_token_t token;
+
+       token = (spnego_token_t)malloc(strlen(name)+1);
+
+       if (token == NULL)
+               return (NULL);
+       strcpy(token, name);
+       return (token);
+}
+
+static gss_buffer_desc
+make_err_msg(char *name)
+{
+       gss_buffer_desc buffer;
+
+       if (name == NULL) {
+               buffer.length = 0;
+               buffer.value = NULL;
+       } else {
+               buffer.length = strlen(name)+1;
+               buffer.value = make_spnego_token(name);
+       }
+
+       return (buffer);
+}
+
+/*
+ * Create the client side spnego token passed back to gss_init_sec_context
+ * and eventually up to the application program and over to the server.
+ *
+ * Use DER rules, definite length method per RFC 2478
+ */
+static int
+make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
+               gss_OID_set mechSet, OM_uint32 req_flags,
+               gss_buffer_t data, send_token_flag sendtoken,
+               gss_buffer_t outbuf)
+{
+       OM_uint32 status, minor_stat;
+       int tlen, dataLen = 0, ret = 0;
+       int MechSetLen = 0;
+       int negTokenInitSize = 0;
+       int i;
+       unsigned char *t;
+       unsigned char *ptr;
+       unsigned char *MechListPtr = NULL;
+       gss_buffer_desc MICbuff;
+
+       if (outbuf == GSS_C_NO_BUFFER)
+               return (-1);
+
+       outbuf->length = 0;
+       outbuf->value = NULL;
+
+       /* calculate the data length */
+
+       /* no token generated if sendtoken is not init or cont */
+       if ((sendtoken < INIT_TOKEN_SEND) ||
+           (sendtoken > CONT_TOKEN_SEND)) {
+               return (-1);
+       }
+
+       /*
+        * if this is the init token, we will send the mechset and req_flags
+        * so include their length.
+        */
+       if (sendtoken == INIT_TOKEN_SEND) {
+               /*
+                * Count bytes for the mechSet data
+                * Encoded in final output as:
+                * 0xa0 [DER LEN] 0x30 [DER LEN] [DATA]
+                */
+               for (i = 0; i < mechSet->count; i++)
+                       MechSetLen += 1 +
+                               der_length_size(mechSet->elements[i].length) +
+                               mechSet->elements[i].length;
+
+               MechSetLen += 1 + der_length_size(MechSetLen);
+               dataLen += 1 + der_length_size(MechSetLen) + MechSetLen;
+
+               MechListPtr = (uchar_t *)malloc(dataLen);
+               ptr = (uchar_t *)MechListPtr;
+
+               if (MechListPtr != NULL) {
+                       if ((ret = put_mech_set(&ptr, mechSet, dataLen))) {
+                               free(MechListPtr);
+                               goto errout;
+                       }
+               } else {
+                       ret = -1;
+                       goto errout;
+               }
+
+               /*
+                * The MIC is done over the DER encoded mechSet.
+                */
+               spnego_ctx->DER_mechTypes.value = MechListPtr;
+               spnego_ctx->DER_mechTypes.length = ptr - MechListPtr;
+
+               /*
+                * Only send the MIC if we are *NOT* interoperating
+                * with Microsoft.
+                */
+               if (!spnego_ctx->MS_Interop) {
+                       /*
+                        * MechListMIC = DER(MIC(DER(MechSet)))
+                        * Calculate it here, stick it in the buffer later.
+                        */
+                       MICbuff.length = 0;
+                       MICbuff.value = NULL;
+                       status = gss_get_mic(&minor_stat,
+                               spnego_ctx->ctx_handle,
+                               GSS_C_QOP_DEFAULT,
+                               &spnego_ctx->DER_mechTypes,
+                               &MICbuff);
+                       /*
+                        * If the MIC operation succeeded, use it,
+                        * but don't fail if it did not succeed.
+                        * MIC is optional and is not supported by all
+                        * mechanisms all the time.
+                        */
+                       if (status  == GSS_S_COMPLETE) {
+                               /*
+                                * Encoded in final output as:
+                                * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
+                                *      --s--   -------tlen------------
+                                */
+                               tlen = 1 + der_length_size(MICbuff.length) +
+                                       MICbuff.length;
+
+                               dataLen += 1 + der_length_size(tlen) + tlen;
+                       }
+               }
+
+               /*
+                * 4 bytes for ret_flags:
+                *   ASN.1 token + ASN.1 Length + Padding + Flags
+                *   0xa1 LENGTH BIT_STRING BIT_STRING_LEN PAD DATA
+                */
+               if (req_flags != 0)
+                       dataLen += 6;
+       }
+
+       /*
+        * If a token from gss_init_sec_context exists,
+        * add the length of the token + the ASN.1 overhead
+        */
+       if (data != NULL) {
+               /*
+                * Encoded in final output as:
+                * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
+                * -----s--------|--------s2----------
+                */
+               tlen = 1 + der_length_size(data->length) + data->length;
+
+               dataLen += 1 + der_length_size(tlen) + tlen;
+       }
+
+       /*
+        * Add size of DER encoding
+        * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
+        *   0x30 [DER_LEN] [data]
+        *
+        */
+       dataLen += 1 + der_length_size(dataLen);
+
+       /*
+        * negTokenInitSize indicates the bytes needed to
+        * hold the ASN.1 encoding of the entire NegTokenInit
+        * SEQUENCE.
+        * 0xa0 [DER_LEN] + data
+        *
+        */
+       negTokenInitSize = dataLen;
+
+       tlen = g_token_size((gss_OID)gss_mech_spnego,
+                           negTokenInitSize + 1 +
+                           der_length_size(negTokenInitSize));
+
+       t = (unsigned char *) malloc(tlen);
+
+       if (t == NULL) {
+               return (-1);
+       }
+
+       ptr = t;
+
+       /* create the message */
+       if ((ret = g_make_token_header((gss_OID)gss_mech_spnego,
+                           1 + negTokenInitSize +
+                           der_length_size(negTokenInitSize),
+                           &ptr, tlen)))
+               goto errout;
+
+       if (sendtoken == INIT_TOKEN_SEND) {
+               *ptr++ = CONTEXT; /* NegotiationToken identifier */
+               if ((ret = put_der_length(negTokenInitSize, &ptr, tlen)))
+                       goto errout;
+
+               *ptr++ = SEQUENCE;
+               if ((ret = put_der_length(negTokenInitSize - 4, &ptr,
+                           tlen - (int)(ptr-t))))
+                       goto errout;
+
+               *ptr++ = CONTEXT; /* MechTypeList identifier */
+               if ((ret = put_der_length(spnego_ctx->DER_mechTypes.length,
+                       &ptr, tlen - (int)(ptr-t))))
+                       goto errout;
+
+               /* We already encoded the MechSetList */
+               (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
+                       spnego_ctx->DER_mechTypes.length);
+
+               ptr += spnego_ctx->DER_mechTypes.length;
+
+               if (req_flags != 0) {
+                       if ((ret = put_req_flags(&ptr, req_flags,
+                               tlen - (int)(ptr-t))))
+                               goto errout;
+               }
+       }
+
+       if (data != NULL) {
+               *ptr++ = CONTEXT | 0x02;
+               if ((ret = put_der_length(data->length + 4,
+                               &ptr, tlen - (int)(ptr - t))))
+                       goto errout;
+
+               if ((ret = put_input_token(&ptr, data,
+                       tlen - (int)(ptr - t))))
+                       goto errout;
+
+               /*
+                * We are in "optimistic" mode if we send a token
+                * with out initial message.
+                */
+               spnego_ctx->optimistic = (sendtoken == INIT_TOKEN_SEND);
+       }
+
+       if (!spnego_ctx->MS_Interop && MICbuff.length > 0) {
+               /* We already calculated the MechListMIC above */
+               *ptr++ = CONTEXT | 0x03;
+               if ((ret = put_der_length(MICbuff.length,
+                               &ptr, tlen - (int)(ptr - t))))
+                       goto errout;
+
+               if ((ret = put_input_token(&ptr, &MICbuff,
+                               tlen - (int)(ptr - t))))
+                       goto errout;
+
+               (void) gss_release_buffer(&minor_stat, &MICbuff);
+       }
+
+errout:
+       if (ret != 0) {
+               if (t)
+                       free(t);
+               t = NULL;
+               tlen = 0;
+       }
+       outbuf->length = tlen;
+       outbuf->value = (void *) t;
+
+       return (ret);
+}
+
+/*
+ * create the server side spnego token passed back to
+ * gss_accept_sec_context and eventually up to the application program
+ * and over to the client.
+ */
+static int
+make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
+                       gss_buffer_t data, gss_buffer_t mechListMIC,
+                       send_token_flag sendtoken, int MS_Flag,
+                       gss_buffer_t outbuf)
+{
+       int tlen;
+       int ret;
+       int NegTokenTargSize;
+       int negresultTokenSize;
+       int NegTokenSize;
+       int rspTokenSize;
+       int micTokenSize;
+       int dataLen = 0;
+       unsigned char *t;
+       unsigned char *ptr;
+
+       if (outbuf == GSS_C_NO_BUFFER)
+               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.
+        */
+       negresultTokenSize = 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) {
+
+               if (mech_wanted != NULL) {
+                       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 +
+                               der_length_size(mech_wanted->length);
+
+                       dataLen = negresultTokenSize + mechlistTokenSize;
+               }
+       } else {
+               /*
+                * If this is a response from a server, count
+                * the space needed for the negResult field.
+                * LENGTH(2) + ENUM(2) + result
+                */
+               dataLen = negresultTokenSize;
+       }
+       if (data != NULL && data->length > 0) {
+               /* Length of the inner token */
+               rspTokenSize = 1 + der_length_size(data->length) +
+                       data->length;
+
+               dataLen += rspTokenSize;
+
+               /* Length of the outer token */
+               dataLen += 1 + der_length_size(rspTokenSize);
+       }
+       if (mechListMIC != NULL) {
+
+               /* Length of the inner token */
+               micTokenSize = 1 + der_length_size(mechListMIC->length) +
+                       mechListMIC->length;
+
+               dataLen += micTokenSize;
+
+               /* Length of the outer token */
+               dataLen += 1 + der_length_size(micTokenSize);
+       } else if (data != NULL && data->length > 0 && MS_Flag) {
+               dataLen += rspTokenSize;
+               dataLen += 1 + der_length_size(rspTokenSize);
+       }
+
+       /*
+        * 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 + der_length_size(NegTokenTargSize);
+
+       /*
+        * NegotiationToken [ CHOICE ]{
+        *    negTokenInit  [0]  NegTokenInit,
+        *    negTokenTarg  [1]  NegTokenTarg }
+        */
+       NegTokenSize = dataLen;
+       dataLen += 1 + der_length_size(NegTokenSize);
+
+       tlen = dataLen;
+       t = (unsigned char *) malloc(tlen);
+
+       if (t == NULL) {
+               ret = GSS_S_DEFECTIVE_TOKEN;
+               goto errout;
+       }
+
+       ptr = t;
+
+       if (sendtoken == INIT_TOKEN_SEND ||
+           sendtoken == ERROR_TOKEN_SEND) {
+               /*
+                * Indicate that we are sending CHOICE 1
+                * (NegTokenTarg)
+                */
+               *ptr++ = CONTEXT | 0x01;
+               if ((ret = put_der_length(NegTokenSize, &ptr, dataLen))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+
+               *ptr++ = SEQUENCE;
+               if ((ret = put_der_length(NegTokenTargSize, &ptr,
+                           tlen - (int)(ptr-t)))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+
+               /*
+                * First field of the NegTokenTarg SEQUENCE
+                * is the ENUMERATED NegResult.
+                */
+               *ptr++ = CONTEXT;
+               if ((ret = put_der_length(3, &ptr,
+                       tlen - (int)(ptr-t)))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+               if ((ret = put_negResult(&ptr, status,
+                               tlen - (int)(ptr - t)))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+
+               if (sendtoken != ERROR_TOKEN_SEND && mech_wanted != NULL) {
+                       /*
+                        * Next, is the Supported MechType
+                        */
+                       *ptr++ = CONTEXT | 0x01;
+                       if ((ret = put_der_length(mech_wanted->length + 2,
+                               &ptr, tlen - (int)(ptr - t)))) {
+                               ret = GSS_S_DEFECTIVE_TOKEN;
+                               goto errout;
+                       }
+                       if ((ret = put_mech_oid(&ptr, mech_wanted,
+                               tlen - (int)(ptr - t)))) {
+                               ret = GSS_S_DEFECTIVE_TOKEN;
+                               goto errout;
+                       }
+               }
+       }
+
+       if (data != NULL && data->length > 0) {
+               *ptr++ = CONTEXT | 0x02;
+               if ((ret = put_der_length(rspTokenSize, &ptr,
+                           tlen - (int)(ptr - t)))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+               if ((ret = put_input_token(&ptr, data,
+                               tlen - (int)(ptr - t)))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+       }
+       if (mechListMIC != NULL) {
+               *ptr++ = CONTEXT | 0x03;
+               if ((ret = put_der_length(micTokenSize, &ptr,
+                           tlen - (int)(ptr - t)))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+               if ((ret = put_input_token(&ptr, mechListMIC,
+                               tlen - (int)(ptr - t)))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+       } else if (data != NULL && data->length > 0 && MS_Flag) {
+               *ptr++ = CONTEXT | 0x03;
+               if ((ret = put_der_length(rspTokenSize, &ptr,
+                           tlen - (int)(ptr - t)))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+                       goto errout;
+               }
+               if ((ret = put_input_token(&ptr, data,
+                               tlen - (int)(ptr - t)))) {
+                       ret = GSS_S_DEFECTIVE_TOKEN;
+               }
+       }
+errout:
+       if (ret != 0) {
+               if (t)
+                       free(t);
+       } else {
+               outbuf->length = ptr - t;
+               outbuf->value = (void *) t;
+       }
+
+       return (ret);
+}
+
+/* determine size of token */
+static int
+g_token_size(gss_OID mech, unsigned int body_size)
+{
+       int hdrsize;
+
+       /*
+        * Initialize the header size to the
+        * MECH_OID byte + the bytes needed to indicate the
+        * length of the OID + the OID itself.
+        *
+        * 0x06 [MECHLENFIELD] MECHDATA
+        */
+       hdrsize = 1 + der_length_size(mech->length) + mech->length;
+
+       /*
+        * Now add the bytes needed for the initial header
+        * token bytes:
+        * 0x60 + [DER_LEN] + HDRSIZE
+        */
+       hdrsize += 1 + der_length_size(body_size + hdrsize);
+
+       return (hdrsize + body_size);
+}
+
+/*
+ * generate token header.
+ *
+ * Use DER Definite Length method per RFC2478
+ * Use of indefinite length encoding will not be compatible
+ * with Microsoft or others that actually follow the spec.
+ */
+static int
+g_make_token_header(gss_OID mech,
+                   int body_size,
+                   unsigned char **buf,
+                   int totallen)
+{
+       int hdrsize, ret = 0;
+       unsigned char *p = *buf;
+
+       hdrsize = 1 + der_length_size(mech->length) + mech->length;
+
+       *(*buf)++ = HEADER_ID;
+       if ((ret = put_der_length(hdrsize + body_size, buf, totallen)))
+               return (ret);
+
+       *(*buf)++ = MECH_OID;
+       if ((ret = put_der_length(mech->length, buf,
+                           totallen - (int)(p - *buf))))
+               return (ret);
+       TWRITE_STR(*buf, mech->elements, ((int)mech->length));
+       return (0);
+}
+
+static int
+g_get_tag_and_length(unsigned char **buf, uchar_t tag, int buflen, int *outlen)
+{
+       unsigned char *ptr = *buf;
+       int ret = -1; /* pessimists, assume failure ! */
+       OM_uint32 encoded_len;
+
+       if (buflen > 0 && *ptr == tag) {
+               ptr++;
+               *outlen = get_der_length(&ptr, buflen, &encoded_len);
+               if (*outlen < 0)
+                       ret = *outlen;
+               if ((ptr + *outlen) > (*buf + buflen))
+                       ret = -1;
+               else
+                       ret = 0;
+       }
+
+       *buf = ptr;
+       return (ret);
+}
+
+static int
+g_verify_neg_token_init(unsigned char **buf_in, int cur_size)
+{
+       unsigned char *buf = *buf_in;
+       unsigned char *endptr = buf + cur_size;
+       int seqsize;
+       int ret = 0;
+       unsigned int bytes;
+
+       /*
+        * Verify this is a NegotiationToken type token
+        * - check for a0(context specific identifier)
+        * - get length and verify that enoughd ata exists
+        */
+       if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       cur_size = seqsize; /* should indicate bytes remaining */
+
+       /*
+        * Verify the next piece, it should identify this as
+        * a strucure of type NegTokenInit.
+        */
+       if (*buf++ == SEQUENCE) {
+               if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
+                       return (G_BAD_TOK_HEADER);
+               /*
+                * Make sure we have the entire buffer as described
+                */
+               if (buf + seqsize > endptr)
+                       return (G_BAD_TOK_HEADER);
+       } else {
+               return (G_BAD_TOK_HEADER);
+       }
+
+       cur_size = seqsize; /* should indicate bytes remaining */
+
+       /*
+        * Verify that the first blob is a sequence of mechTypes
+        */
+       if (*buf++ == CONTEXT) {
+               if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
+                       return (G_BAD_TOK_HEADER);
+               /*
+                * Make sure we have the entire buffer as described
+                */
+               if (buf + bytes > endptr)
+                       return (G_BAD_TOK_HEADER);
+       } else {
+               return (G_BAD_TOK_HEADER);
+       }
+
+       /*
+        * At this point, *buf should be at the beginning of the
+        * DER encoded list of mech types that are to be negotiated.
+        */
+       *buf_in = buf;
+
+       return (ret);
+
+}
+
+/* verify token header. */
+static int
+g_verify_token_header(gss_OID mech,
+                   int *body_size,
+                   unsigned char **buf_in,
+                   int tok_type,
+                   int toksize)
+{
+       unsigned char *buf = *buf_in;
+       int seqsize;
+       gss_OID_desc toid;
+       int ret = 0;
+       unsigned int bytes;
+
+       if ((toksize -= 1) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       if (*buf++ != HEADER_ID)
+               return (G_BAD_TOK_HEADER);
+
+       if ((seqsize = get_der_length(&buf, toksize, &bytes)) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       if ((seqsize + bytes) != toksize)
+               return (G_BAD_TOK_HEADER);
+
+       if ((toksize -= 1) < 0)
+               return (G_BAD_TOK_HEADER);
+
+
+       if (*buf++ != MECH_OID)
+               return (G_BAD_TOK_HEADER);
+
+       if ((toksize -= 1) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       toid.length = *buf++;
+
+       if ((toksize -= toid.length) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       toid.elements = buf;
+       buf += toid.length;
+
+       if (!g_OID_equal(&toid, mech))
+               ret = G_WRONG_MECH;
+
+       /*
+        * G_WRONG_MECH is not returned immediately because it's more important
+        * to return G_BAD_TOK_HEADER if the token header is in fact bad
+        */
+       if ((toksize -= 2) < 0)
+               return (G_BAD_TOK_HEADER);
+
+       if (!ret) {
+               *buf_in = buf;
+               *body_size = toksize;
+       }
+
+       return (ret);
+}