1 /*
   2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
   6  * All rights reserved.
   7  *
   8  * Export of this software from the United States of America may
   9  *   require a specific license from the United States Government.
  10  *   It is the responsibility of any person or organization contemplating
  11  *   export to obtain such a license before exporting.
  12  * 
  13  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
  14  * distribute this software and its documentation for any purpose and
  15  * without fee is hereby granted, provided that the above copyright
  16  * notice appear in all copies and that both that copyright notice and
  17  * this permission notice appear in supporting documentation, and that
  18  * the name of M.I.T. not be used in advertising or publicity pertaining
  19  * to distribution of the software without specific, written prior
  20  * permission.  Furthermore if you modify this software you must label
  21  * your software as modified software and not distribute it in such a
  22  * fashion that it might be confused with the original M.I.T. software.
  23  * M.I.T. makes no representations about the suitability of
  24  * this software for any purpose.  It is provided "as is" without express
  25  * or implied warranty.
  26  *
  27  */
  28 
  29 /*
  30  * A module that implements the spnego security mechanism.
  31  * It is used to negotiate the security mechanism between
  32  * peers using the GSS-API.
  33  *
  34  */
  35 
  36 /*
  37  * Copyright (c) 2006-2008, Novell, Inc.
  38  * All rights reserved.
  39  *
  40  * Redistribution and use in source and binary forms, with or without
  41  * modification, are permitted provided that the following conditions are met:
  42  *
  43  *   * Redistributions of source code must retain the above copyright notice,
  44  *       this list of conditions and the following disclaimer.
  45  *   * Redistributions in binary form must reproduce the above copyright
  46  *       notice, this list of conditions and the following disclaimer in the
  47  *       documentation and/or other materials provided with the distribution.
  48  *   * The copyright holder's name is not used to endorse or promote products
  49  *       derived from this software without specific prior written permission.
  50  *
  51  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  52  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  54  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  55  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  56  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  57  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  58  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  59  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  60  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  61  * POSSIBILITY OF SUCH DAMAGE.
  62  */
  63 /* #pragma ident        "@(#)spnego_mech.c      1.7     04/09/28 SMI" */
  64 
  65 #include        <sys/param.h>
  66 #include        <unistd.h>
  67 #include        <assert.h>
  68 #include        <stdio.h>
  69 #include        <stdlib.h>
  70 #include        <string.h>
  71 #include        <k5-int.h>
  72 #include        <krb5.h>
  73 #include        <mglueP.h>
  74 #include        "gssapiP_spnego.h"
  75 #include        "gssapiP_generic.h"
  76 #include        <gssapi_err_generic.h>
  77 #include        <locale.h>
  78 
  79 /*
  80  * SUNW17PACresync
  81  * MIT has diff names for these GSS utilities.  Solaris needs to change
  82  * them globally to get in sync w/MIT.
  83  * Revisit for full 1.7 resync.
  84  */
  85 #define gssint_get_modOptions __gss_get_modOptions
  86 #define gssint_der_length_size der_length_size
  87 #define gssint_get_der_length get_der_length
  88 #define gssint_put_der_length put_der_length
  89 #define gssint_get_mechanism __gss_get_mechanism
  90 #define gssint_copy_oid_set gss_copy_oid_set
  91 #define gssint_get_mech_type __gss_get_mech_type
  92 
  93 
  94 #undef g_token_size
  95 #undef g_verify_token_header
  96 #undef g_make_token_header
  97 
  98 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
  99 typedef const gss_OID_desc *gss_OID_const;
 100 
 101 /* der routines defined in libgss */
 102 extern unsigned int gssint_der_length_size(OM_uint32);
 103 extern int gssint_get_der_length(unsigned char **, OM_uint32, OM_uint32*);
 104 extern int gssint_put_der_length(OM_uint32, unsigned char **, OM_uint32);
 105 
 106 
 107 /* private routines for spnego_mechanism */
 108 static spnego_token_t make_spnego_token(char *);
 109 static gss_buffer_desc make_err_msg(char *);
 110 static int g_token_size(gss_OID_const, unsigned int);
 111 static int g_make_token_header(gss_OID_const, unsigned int,
 112                                unsigned char **, unsigned int);
 113 static int g_verify_token_header(gss_OID_const, unsigned int *,
 114                                  unsigned char **,
 115                                  int, unsigned int);
 116 static int g_verify_neg_token_init(unsigned char **, unsigned int);
 117 static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
 118 static gss_buffer_t get_input_token(unsigned char **, unsigned int);
 119 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
 120 static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
 121 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
 122         gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
 123 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
 124 static void check_spnego_options(spnego_gss_ctx_id_t);
 125 static spnego_gss_ctx_id_t create_spnego_ctx(void);
 126 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
 127 static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
 128 static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
 129 static int put_negResult(unsigned char **, OM_uint32, unsigned int);
 130 
 131 static OM_uint32
 132 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
 133             gss_buffer_t *, OM_uint32 *, send_token_flag *);
 134 static OM_uint32
 135 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
 136            gss_buffer_t *, OM_uint32 *, send_token_flag *);
 137 
 138 static OM_uint32
 139 init_ctx_new(OM_uint32 *, gss_cred_id_t, gss_ctx_id_t *,
 140              gss_OID_set *, send_token_flag *);
 141 static OM_uint32
 142 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
 143               gss_buffer_t *, gss_buffer_t *,
 144               OM_uint32 *, send_token_flag *);
 145 static OM_uint32
 146 init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
 147               gss_buffer_t *, gss_buffer_t *,
 148               OM_uint32 *, send_token_flag *);
 149 static OM_uint32
 150 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
 151                   gss_OID, gss_buffer_t *, gss_buffer_t *,
 152                   OM_uint32 *, send_token_flag *);
 153 static OM_uint32
 154 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
 155                    gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
 156                    gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
 157                    OM_uint32 *, send_token_flag *);
 158 
 159 static OM_uint32
 160 acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
 161             gss_cred_id_t, gss_buffer_t *,
 162             gss_buffer_t *, OM_uint32 *, send_token_flag *);
 163 static OM_uint32
 164 acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
 165              gss_buffer_t *, gss_buffer_t *,
 166              OM_uint32 *, send_token_flag *);
 167 static OM_uint32
 168 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
 169                 OM_uint32 *, send_token_flag *);
 170 static OM_uint32
 171 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
 172                  gss_buffer_t, gss_OID *, gss_buffer_t,
 173                  OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
 174                  OM_uint32 *, send_token_flag *);
 175 
 176 static gss_OID
 177 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
 178                 OM_uint32 *);
 179 static int
 180 g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
 181 
 182 static int
 183 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
 184                         int,
 185                         gss_buffer_t,
 186                         OM_uint32, gss_buffer_t, send_token_flag,
 187                         gss_buffer_t);
 188 static int
 189 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
 190                         gss_buffer_t, send_token_flag,
 191                         gss_buffer_t);
 192 
 193 static OM_uint32
 194 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
 195                  gss_OID_set *, OM_uint32 *, gss_buffer_t *,
 196                  gss_buffer_t *);
 197 static OM_uint32
 198 get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
 199                  OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
 200 
 201 static int
 202 is_kerb_mech(gss_OID oid);
 203 
 204 /* SPNEGO oid structure */
 205 static const gss_OID_desc spnego_oids[] = {
 206         {SPNEGO_OID_LENGTH, SPNEGO_OID},
 207 };
 208 
 209 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
 210 static const gss_OID_set_desc spnego_oidsets[] = {
 211         {1, (gss_OID) spnego_oids+0},
 212 };
 213 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
 214 
 215 static int make_NegHints(OM_uint32 *, gss_cred_id_t, gss_buffer_t *);
 216 static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
 217 static OM_uint32
 218 acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, gss_cred_id_t,
 219               gss_buffer_t *, OM_uint32 *, send_token_flag *);
 220 
 221 #ifdef _GSS_STATIC_LINK
 222 int gss_spnegoint_lib_init(void);
 223 void gss_spnegoint_lib_fini(void);
 224 #else
 225 gss_mechanism gss_mech_initialize(void);
 226 #endif /* _GSS_STATIC_LINK */
 227 
 228 /*
 229  * The Mech OID for SPNEGO:
 230  * { iso(1) org(3) dod(6) internet(1) security(5)
 231  *  mechanism(5) spnego(2) }
 232  */
 233 static struct gss_config spnego_mechanism =
 234 {
 235         {SPNEGO_OID_LENGTH, SPNEGO_OID},
 236         NULL,
 237         glue_spnego_gss_acquire_cred,
 238         glue_spnego_gss_release_cred,
 239         glue_spnego_gss_init_sec_context,
 240 #ifndef LEAN_CLIENT
 241         glue_spnego_gss_accept_sec_context,
 242 #else
 243         NULL,                           
 244 #endif  /* LEAN_CLIENT */
 245 /* EXPORT DELETE START */ /* CRYPT DELETE START */
 246         NULL,  /* unseal */
 247 /* EXPORT DELETE END */ /* CRYPT DELETE END */
 248         NULL,                           /* gss_process_context_token */
 249         glue_spnego_gss_delete_sec_context,     /* gss_delete_sec_context */
 250         glue_spnego_gss_context_time,
 251         glue_spnego_gss_display_status,
 252         NULL,                           /* gss_indicate_mechs */
 253         glue_spnego_gss_compare_name,
 254         glue_spnego_gss_display_name,
 255         glue_spnego_gss_import_name, /* glue */
 256         glue_spnego_gss_release_name,
 257         NULL,                           /* gss_inquire_cred */
 258         NULL,                           /* gss_add_cred */
 259 /* EXPORT DELETE START */ /* CRYPT DELETE START */
 260         NULL, /* seal */
 261 /* EXPORT DELETE END */ /* CRYPT DELETE END */
 262 #ifndef LEAN_CLIENT
 263         glue_spnego_gss_export_sec_context,     /* gss_export_sec_context */
 264         glue_spnego_gss_import_sec_context,     /* gss_import_sec_context */
 265 #else
 266         NULL,                           /* gss_export_sec_context */
 267         NULL,                           /* gss_import_sec_context */
 268 #endif /* LEAN_CLIENT */
 269         NULL,                           /* gss_inquire_cred_by_mech */
 270         glue_spnego_gss_inquire_names_for_mech,
 271         glue_spnego_gss_inquire_context,
 272         NULL,                           /* gss_internal_release_oid */
 273         glue_spnego_gss_wrap_size_limit,
 274         NULL, /* pname */
 275         NULL, /* userok */
 276         NULL, /* gss_export_name */
 277 /* EXPORT DELETE START */
 278 /* CRYPT DELETE START */
 279 #if 0
 280 /* CRYPT DELETE END */
 281         NULL, /* seal */
 282         NULL, /* unseal */
 283 /* CRYPT DELETE START */
 284 #endif
 285 /* CRYPT DELETE END */
 286 /* EXPORT DELETE END */
 287         NULL, /* sign */
 288         NULL, /* verify */
 289         NULL, /* gss_store_cred */
 290         spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
 291 };
 292 
 293 #ifdef _GSS_STATIC_LINK
 294 #include "mglueP.h"
 295 
 296 static
 297 int gss_spnegomechglue_init(void)
 298 {
 299         struct gss_mech_config mech_spnego;
 300 
 301         memset(&mech_spnego, 0, sizeof(mech_spnego));
 302         mech_spnego.mech = &spnego_mechanism;
 303         mech_spnego.mechNameStr = "spnego";
 304         mech_spnego.mech_type = GSS_C_NO_OID;
 305 
 306         return gssint_register_mechinfo(&mech_spnego);
 307 }
 308 #else
 309 /* Entry point for libgss */
 310 gss_mechanism KRB5_CALLCONV
 311 gss_mech_initialize(void)
 312 {
 313         int err;
 314 
 315         err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
 316                 spnego_gss_delete_error_info);
 317         if (err) {
 318             syslog(LOG_NOTICE,
 319                 "SPNEGO gss_mech_initialize: error message TSD key register fail");
 320             return (NULL);
 321         }
 322 
 323         return (&spnego_mechanism);
 324 }
 325 
 326 #if 0 /* SUNW17PACresync */
 327 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
 328 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
 329 int gss_krb5int_lib_init(void)
 330 #endif
 331 
 332 #endif /* _GSS_STATIC_LINK */
 333 
 334 static
 335 int gss_spnegoint_lib_init(void)
 336 {
 337 #ifdef _GSS_STATIC_LINK
 338         return gss_spnegomechglue_init();
 339 #else
 340         int err;
 341 
 342         err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
 343                 spnego_gss_delete_error_info);
 344         if (err) {
 345             syslog(LOG_NOTICE,
 346                 "SPNEGO gss_mech_initialize: error message TSD key register fail: err=%d",
 347                 err);
 348             return err;
 349         }
 350 
 351         return 0;
 352 #endif
 353 }
 354 
 355 static void gss_spnegoint_lib_fini(void)
 356 {
 357 }
 358 
 359 /*ARGSUSED*/
 360 OM_uint32
 361 glue_spnego_gss_acquire_cred(
 362         void *context,
 363         OM_uint32 *minor_status,
 364         gss_name_t desired_name,
 365         OM_uint32 time_req,
 366         gss_OID_set desired_mechs,
 367         gss_cred_usage_t cred_usage,
 368         gss_cred_id_t *output_cred_handle,
 369         gss_OID_set *actual_mechs,
 370         OM_uint32 *time_rec)
 371 {
 372         return(spnego_gss_acquire_cred(minor_status,
 373                                         desired_name,
 374                                         time_req,
 375                                         desired_mechs,
 376                                         cred_usage,
 377                                         output_cred_handle,
 378                                         actual_mechs,
 379                                         time_rec));
 380 }
 381 
 382 /*ARGSUSED*/
 383 OM_uint32
 384 spnego_gss_acquire_cred(OM_uint32 *minor_status,
 385                         gss_name_t desired_name,
 386                         OM_uint32 time_req,
 387                         gss_OID_set desired_mechs,
 388                         gss_cred_usage_t cred_usage,
 389                         gss_cred_id_t *output_cred_handle,
 390                         gss_OID_set *actual_mechs,
 391                         OM_uint32 *time_rec)
 392 {
 393         OM_uint32 status;
 394         gss_OID_set amechs;
 395         dsyslog("Entering spnego_gss_acquire_cred\n");
 396 
 397         if (actual_mechs)
 398                 *actual_mechs = NULL;
 399 
 400         if (time_rec)
 401                 *time_rec = 0;
 402 
 403         /*
 404          * If the user did not specify a list of mechs,
 405          * use get_available_mechs to collect a list of
 406          * mechs for which creds are available.
 407          */
 408         if (desired_mechs == GSS_C_NULL_OID_SET) {
 409                 status = get_available_mechs(minor_status,
 410                                 desired_name, cred_usage,
 411                                 output_cred_handle, &amechs);
 412         } else {
 413                 /*
 414                  * The caller gave a specific list of mechanisms,
 415                  * so just get whatever creds are available.
 416                  * gss_acquire_creds will return the subset of mechs for
 417                  * which the given 'output_cred_handle' is valid.
 418                  */
 419                 status = gss_acquire_cred(minor_status,
 420                                 desired_name, time_req,
 421                                 desired_mechs, cred_usage,
 422                                 output_cred_handle, &amechs,
 423                                 time_rec);
 424         }
 425 
 426         if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
 427                 (void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
 428         }
 429         (void) gss_release_oid_set(minor_status, &amechs);
 430 
 431         dsyslog("Leaving spnego_gss_acquire_cred\n");
 432         return (status);
 433 }
 434 
 435 /*ARGSUSED*/
 436 OM_uint32
 437 glue_spnego_gss_release_cred(void *context,
 438                             OM_uint32 *minor_status,
 439                             gss_cred_id_t *cred_handle)
 440 {
 441         return( spnego_gss_release_cred(minor_status, cred_handle));
 442 }
 443 
 444 /*ARGSUSED*/
 445 OM_uint32
 446 spnego_gss_release_cred(OM_uint32 *minor_status,
 447                         gss_cred_id_t *cred_handle)
 448 {
 449         OM_uint32 status;
 450 
 451         dsyslog("Entering spnego_gss_release_cred\n");
 452 
 453         if (minor_status == NULL || cred_handle == NULL)
 454                 return (GSS_S_CALL_INACCESSIBLE_WRITE);
 455 
 456         *minor_status = 0;
 457 
 458         if (*cred_handle == GSS_C_NO_CREDENTIAL)
 459                 return (GSS_S_COMPLETE);
 460 
 461         status = gss_release_cred(minor_status, cred_handle);
 462 
 463         dsyslog("Leaving spnego_gss_release_cred\n");
 464         return (status);
 465 }
 466 
 467 static void
 468 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
 469 {
 470         spnego_ctx->optionStr = gssint_get_modOptions(
 471                 (const gss_OID)&spnego_oids[0]);
 472 }
 473 
 474 static spnego_gss_ctx_id_t
 475 create_spnego_ctx(void)
 476 {
 477         spnego_gss_ctx_id_t spnego_ctx = NULL;
 478         spnego_ctx = (spnego_gss_ctx_id_t)
 479                 malloc(sizeof (spnego_gss_ctx_id_rec));
 480 
 481         if (spnego_ctx == NULL) {
 482                 return (NULL);
 483         }
 484 
 485         spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
 486         spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
 487         spnego_ctx->internal_mech = NULL;
 488         spnego_ctx->optionStr = NULL;
 489         spnego_ctx->DER_mechTypes.length = 0;
 490         spnego_ctx->DER_mechTypes.value = NULL;
 491         spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
 492         spnego_ctx->mic_reqd = 0;
 493         spnego_ctx->mic_sent = 0;
 494         spnego_ctx->mic_rcvd = 0;
 495         spnego_ctx->mech_complete = 0;
 496         spnego_ctx->nego_done = 0;
 497         spnego_ctx->internal_name = GSS_C_NO_NAME;
 498         spnego_ctx->actual_mech = GSS_C_NO_OID;
 499         spnego_ctx->err.msg = NULL;
 500         spnego_ctx->err.scratch_buf[0] = 0;
 501         check_spnego_options(spnego_ctx);
 502 
 503         return (spnego_ctx);
 504 }
 505 
 506 /*
 507  * Both initiator and acceptor call here to verify and/or create
 508  * mechListMIC, and to consistency-check the MIC state.
 509  */
 510 static OM_uint32
 511 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
 512            int send_mechtok, spnego_gss_ctx_id_t sc,
 513            gss_buffer_t *mic_out,
 514            OM_uint32 *negState, send_token_flag *tokflag)
 515 {
 516         OM_uint32 ret;
 517 
 518         ret = GSS_S_FAILURE;
 519         *mic_out = GSS_C_NO_BUFFER;
 520         if (mic_in != GSS_C_NO_BUFFER) {
 521                 if (sc->mic_rcvd) {
 522                         /* Reject MIC if we've already received a MIC. */
 523                         *negState = REJECT;
 524                         *tokflag = ERROR_TOKEN_SEND;
 525                         return GSS_S_DEFECTIVE_TOKEN;
 526                 }
 527         } else if (sc->mic_reqd && !send_mechtok) {
 528                 /*
 529                  * If the peer sends the final mechanism token, it
 530                  * must send the MIC with that token if the
 531                  * negotiation requires MICs.
 532                  */
 533                 *negState = REJECT;
 534                 *tokflag = ERROR_TOKEN_SEND;
 535                 return GSS_S_DEFECTIVE_TOKEN;
 536         }
 537         ret = process_mic(minor_status, mic_in, sc, mic_out,
 538                           negState, tokflag);
 539         if (ret != GSS_S_COMPLETE) {
 540                 return ret;
 541         }
 542         if (sc->mic_reqd) {
 543                 assert(sc->mic_sent || sc->mic_rcvd);
 544         }
 545         if (sc->mic_sent && sc->mic_rcvd) {
 546                 ret = GSS_S_COMPLETE;
 547                 *negState = ACCEPT_COMPLETE;
 548                 if (*mic_out == GSS_C_NO_BUFFER) {
 549                         /*
 550                          * We sent a MIC on the previous pass; we
 551                          * shouldn't be sending a mechanism token.
 552                          */
 553                         assert(!send_mechtok);
 554                         *tokflag = NO_TOKEN_SEND;
 555                 } else {
 556                         *tokflag = CONT_TOKEN_SEND;
 557                 }
 558         } else if (sc->mic_reqd) {
 559                 *negState = ACCEPT_INCOMPLETE;
 560                 ret = GSS_S_CONTINUE_NEEDED;
 561         } else if (*negState == ACCEPT_COMPLETE) {
 562                 ret = GSS_S_COMPLETE;
 563         } else {
 564                 ret = GSS_S_CONTINUE_NEEDED;
 565         }
 566         return ret;
 567 }
 568 
 569 /*
 570  * Perform the actual verification and/or generation of mechListMIC.
 571  */
 572 static OM_uint32
 573 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
 574             spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
 575             OM_uint32 *negState, send_token_flag *tokflag)
 576 {
 577         OM_uint32 ret, tmpmin;
 578         gss_qop_t qop_state;
 579         gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
 580 
 581         ret = GSS_S_FAILURE;
 582         if (mic_in != GSS_C_NO_BUFFER) {
 583                 ret = gss_verify_mic(minor_status, sc->ctx_handle,
 584                                      &sc->DER_mechTypes,
 585                                      mic_in, &qop_state);
 586                 if (ret != GSS_S_COMPLETE) {
 587                         *negState = REJECT;
 588                         *tokflag = ERROR_TOKEN_SEND;
 589                         return ret;
 590                 }
 591                 /* If we got a MIC, we must send a MIC. */
 592                 sc->mic_reqd = 1;
 593                 sc->mic_rcvd = 1;
 594         }
 595         if (sc->mic_reqd && !sc->mic_sent) {
 596                 ret = gss_get_mic(minor_status, sc->ctx_handle,
 597                                   GSS_C_QOP_DEFAULT,
 598                                   &sc->DER_mechTypes,
 599                                   &tmpmic);
 600                 if (ret != GSS_S_COMPLETE) {
 601                         gss_release_buffer(&tmpmin, &tmpmic);
 602                         *tokflag = NO_TOKEN_SEND;
 603                         return ret;
 604                 }
 605                 *mic_out = malloc(sizeof(gss_buffer_desc));
 606                 if (*mic_out == GSS_C_NO_BUFFER) {
 607                         gss_release_buffer(&tmpmin, &tmpmic);
 608                         *tokflag = NO_TOKEN_SEND;
 609                         return GSS_S_FAILURE;
 610                 }
 611                 **mic_out = tmpmic;
 612                 sc->mic_sent = 1;
 613         }
 614         return GSS_S_COMPLETE;
 615 }
 616 
 617 /*
 618  * Initial call to spnego_gss_init_sec_context().
 619  */
 620 static OM_uint32
 621 init_ctx_new(OM_uint32 *minor_status,
 622              gss_cred_id_t cred,
 623              gss_ctx_id_t *ctx,
 624              gss_OID_set *mechSet,
 625              send_token_flag *tokflag)
 626 {
 627         OM_uint32 ret, tmpmin;
 628         gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
 629         spnego_gss_ctx_id_t sc = NULL;
 630 
 631         /* determine negotiation mech set */
 632         if (cred == GSS_C_NO_CREDENTIAL) {
 633                 ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
 634                                           GSS_C_INITIATE, &creds, mechSet);
 635                 gss_release_cred(&tmpmin, &creds);
 636         } else {
 637                 /*
 638                  * Use the list of mechs included in the cred that we
 639                  * were given.
 640                  */
 641                 ret = gss_inquire_cred(minor_status, cred,
 642                                        NULL, NULL, NULL, mechSet);
 643         }
 644         if (ret != GSS_S_COMPLETE)
 645                 return ret;
 646 
 647         sc = create_spnego_ctx();
 648         if (sc == NULL)
 649                 return GSS_S_FAILURE;
 650 
 651         /*
 652          * need to pull the first mech from mechSet to do first
 653          * gss_init_sec_context()
 654          */
 655         ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
 656                                    &sc->internal_mech);
 657         if (ret != GSS_S_COMPLETE) {
 658             map_errcode(minor_status);
 659             goto cleanup;
 660         }
 661 
 662         if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
 663                 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
 664                 ret = GSS_S_FAILURE;
 665                 goto cleanup;
 666         }
 667         /*
 668          * The actual context is not yet determined, set the output
 669          * context handle to refer to the spnego context itself.
 670          */
 671         sc->ctx_handle = GSS_C_NO_CONTEXT;
 672         *ctx = (gss_ctx_id_t)sc;
 673         *tokflag = INIT_TOKEN_SEND;
 674         ret = GSS_S_CONTINUE_NEEDED;
 675 
 676 cleanup:
 677         gss_release_oid_set(&tmpmin, mechSet);
 678         return ret;
 679 }
 680 
 681 /*
 682  * Called by second and later calls to spnego_gss_init_sec_context()
 683  * to decode reply and update state.
 684  */
 685 static OM_uint32
 686 init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
 687               gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
 688               OM_uint32 *negState, send_token_flag *tokflag)
 689 {
 690         OM_uint32 ret, tmpmin, acc_negState;
 691         unsigned char *ptr;
 692         spnego_gss_ctx_id_t sc;
 693         gss_OID supportedMech = GSS_C_NO_OID;
 694 
 695         sc = (spnego_gss_ctx_id_t)*ctx;
 696         *negState = REJECT;
 697         *tokflag = ERROR_TOKEN_SEND;
 698 
 699         ptr = buf->value;
 700         ret = get_negTokenResp(minor_status, ptr, buf->length,
 701                                &acc_negState, &supportedMech,
 702                                responseToken, mechListMIC);
 703         if (ret != GSS_S_COMPLETE)
 704                 goto cleanup;
 705         if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
 706             supportedMech == GSS_C_NO_OID &&
 707             *responseToken == GSS_C_NO_BUFFER &&
 708             *mechListMIC == GSS_C_NO_BUFFER) {
 709                 /* Reject "empty" token. */
 710                 ret = GSS_S_DEFECTIVE_TOKEN;
 711         }
 712         if (acc_negState == REJECT) {
 713                 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
 714                 /* Solaris SPNEGO */
 715                 spnego_set_error_message(sc, *minor_status,
 716                                         dgettext(TEXT_DOMAIN,
 717                                                 "SPNEGO failed to negotiate a mechanism: server rejected request"));
 718                 map_errcode(minor_status);
 719                 *tokflag = NO_TOKEN_SEND;
 720                 ret = GSS_S_FAILURE;
 721                 goto cleanup;
 722         }
 723         /*
 724          * nego_done is false for the first call to init_ctx_cont()
 725          */
 726         if (!sc->nego_done) {
 727                 ret = init_ctx_nego(minor_status, sc,
 728                                     acc_negState,
 729                                     supportedMech, responseToken,
 730                                     mechListMIC,
 731                                     negState, tokflag);
 732         } else if (!sc->mech_complete &&
 733                    *responseToken == GSS_C_NO_BUFFER) {
 734                 /*
 735                  * mech not finished and mech token missing
 736                  */
 737                 ret = GSS_S_DEFECTIVE_TOKEN;
 738         } else if (sc->mic_reqd &&
 739                    (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
 740                 *negState = ACCEPT_INCOMPLETE;
 741                 *tokflag = CONT_TOKEN_SEND;
 742                 ret = GSS_S_CONTINUE_NEEDED;
 743         } else {
 744                 *negState = ACCEPT_COMPLETE;
 745                 *tokflag = NO_TOKEN_SEND;
 746                 ret = GSS_S_COMPLETE;
 747         }
 748 cleanup:
 749         if (supportedMech != GSS_C_NO_OID)
 750                 generic_gss_release_oid(&tmpmin, &supportedMech);
 751         return ret;
 752 }
 753 
 754 /*
 755  * Consistency checking and mechanism negotiation handling for second
 756  * call of spnego_gss_init_sec_context().  Call init_ctx_reselect() to
 757  * update internal state if acceptor has counter-proposed.
 758  */
 759 static OM_uint32
 760 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
 761               OM_uint32 acc_negState, gss_OID supportedMech,
 762               gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
 763               OM_uint32 *negState, send_token_flag *tokflag)
 764 {
 765         OM_uint32 ret;
 766 
 767         *negState = REJECT;
 768         *tokflag = ERROR_TOKEN_SEND;
 769         ret = GSS_S_DEFECTIVE_TOKEN;
 770         /*
 771          * Both supportedMech and negState must be present in first
 772          * acceptor token.
 773          */
 774         if (supportedMech == GSS_C_NO_OID) {
 775                 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
 776                 map_errcode(minor_status);
 777                 return GSS_S_DEFECTIVE_TOKEN;
 778         }
 779         if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
 780                 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
 781                 /* Solaris SPNEGO */
 782                 spnego_set_error_message(sc, *minor_status,
 783                                         dgettext(TEXT_DOMAIN,
 784                                                 "SPNEGO failed to negotiate a mechanism: defective token"));
 785                 map_errcode(minor_status);
 786                 return GSS_S_DEFECTIVE_TOKEN;
 787         }
 788 
 789         /*
 790          * If the mechanism we sent is not the mechanism returned from
 791          * the server, we need to handle the server's counter
 792          * proposal.  There is a bug in SAMBA servers that always send
 793          * the old Kerberos mech OID, even though we sent the new one.
 794          * So we will treat all the Kerberos mech OIDS as the same.
 795          */
 796         if (!(is_kerb_mech(supportedMech) &&
 797               is_kerb_mech(sc->internal_mech)) &&
 798             !g_OID_equal(supportedMech, sc->internal_mech)) {
 799                 ret = init_ctx_reselect(minor_status, sc,
 800                                         acc_negState, supportedMech,
 801                                         responseToken, mechListMIC,
 802                                         negState, tokflag);
 803 
 804         } else if (*responseToken == GSS_C_NO_BUFFER) {
 805                 if (sc->mech_complete) {
 806                         /*
 807                          * Mech completed on first call to its
 808                          * init_sec_context().  Acceptor sends no mech
 809                          * token.
 810                          */
 811                         *negState = ACCEPT_COMPLETE;
 812                         *tokflag = NO_TOKEN_SEND;
 813                         ret = GSS_S_COMPLETE;
 814                 } else {
 815                         /*
 816                          * Reject missing mech token when optimistic
 817                          * mech selected.
 818                          */
 819                         *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
 820                         map_errcode(minor_status);
 821                         ret = GSS_S_DEFECTIVE_TOKEN;
 822                 }
 823         } else if (sc->mech_complete) {
 824                 /* Reject spurious mech token. */
 825                 ret = GSS_S_DEFECTIVE_TOKEN;
 826         } else {
 827                 *negState = ACCEPT_INCOMPLETE;
 828                 *tokflag = CONT_TOKEN_SEND;
 829                 ret = GSS_S_CONTINUE_NEEDED;
 830         }
 831         sc->nego_done = 1;
 832         return ret;
 833 }
 834 
 835 /*
 836  * Handle acceptor's counter-proposal of an alternative mechanism.
 837  */
 838 static OM_uint32
 839 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
 840                   OM_uint32 acc_negState, gss_OID supportedMech,
 841                   gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
 842                   OM_uint32 *negState, send_token_flag *tokflag)
 843 {
 844         OM_uint32 ret, tmpmin;
 845 
 846         generic_gss_release_oid(&tmpmin, &sc->internal_mech);
 847         gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
 848                                GSS_C_NO_BUFFER);
 849 
 850         ret = generic_gss_copy_oid(minor_status, supportedMech,
 851                                    &sc->internal_mech);
 852         if (ret != GSS_S_COMPLETE) {
 853                 map_errcode(minor_status);
 854                 sc->internal_mech = GSS_C_NO_OID;
 855                 *tokflag = NO_TOKEN_SEND;
 856                 return ret;
 857         }
 858         if (*responseToken != GSS_C_NO_BUFFER) {
 859                 /* Reject spurious mech token. */
 860                 return GSS_S_DEFECTIVE_TOKEN;
 861         }
 862         /*
 863          * Windows 2003 and earlier don't correctly send a
 864          * negState of request-mic when counter-proposing a
 865          * mechanism.  They probably don't handle mechListMICs
 866          * properly either.
 867          */
 868         if (acc_negState != REQUEST_MIC)
 869                 return GSS_S_DEFECTIVE_TOKEN;
 870 
 871         sc->mech_complete = 0;
 872         sc->mic_reqd = 1;
 873         *negState = REQUEST_MIC;
 874         *tokflag = CONT_TOKEN_SEND;
 875         return GSS_S_CONTINUE_NEEDED;
 876 }
 877 
 878 /*
 879  * Wrap call to mechanism gss_init_sec_context() and update state
 880  * accordingly.
 881  */
 882 static OM_uint32
 883 init_ctx_call_init(OM_uint32 *minor_status,
 884                    spnego_gss_ctx_id_t sc,
 885                    gss_cred_id_t claimant_cred_handle,
 886                    gss_name_t target_name,
 887                    OM_uint32 req_flags,
 888                    OM_uint32 time_req,
 889                    gss_buffer_t mechtok_in,
 890                    gss_OID *actual_mech,
 891                    gss_buffer_t mechtok_out,
 892                    OM_uint32 *ret_flags,
 893                    OM_uint32 *time_rec,
 894                    OM_uint32 *negState,
 895                    send_token_flag *send_token)
 896 {
 897         OM_uint32 ret;
 898 
 899         ret = gss_init_sec_context(minor_status,
 900                                    claimant_cred_handle,
 901                                    &sc->ctx_handle,
 902                                    target_name,
 903                                    sc->internal_mech,
 904                                    (req_flags | GSS_C_INTEG_FLAG),
 905                                    time_req,
 906                                    GSS_C_NO_CHANNEL_BINDINGS,
 907                                    mechtok_in,
 908                                    &sc->actual_mech,
 909                                    mechtok_out,
 910                                    &sc->ctx_flags,
 911                                    time_rec);
 912         if (ret == GSS_S_COMPLETE) {
 913                 sc->mech_complete = 1;
 914                 if (ret_flags != NULL)
 915                         *ret_flags = sc->ctx_flags;
 916                 /*
 917                  * If this isn't the first time we've been called,
 918                  * we're done unless a MIC needs to be
 919                  * generated/handled.
 920                  */
 921                 if (*send_token == CONT_TOKEN_SEND &&
 922                     mechtok_out->length == 0 &&
 923                     (!sc->mic_reqd ||
 924                      !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
 925 
 926                         *negState = ACCEPT_COMPLETE;
 927                         ret = GSS_S_COMPLETE;
 928                         if (mechtok_out->length == 0) {
 929                                 *send_token = NO_TOKEN_SEND;
 930                         }
 931                 } else {
 932                         *negState = ACCEPT_INCOMPLETE;
 933                         ret = GSS_S_CONTINUE_NEEDED;
 934                 }
 935         } else if (ret != GSS_S_CONTINUE_NEEDED) {
 936                 if (*send_token == INIT_TOKEN_SEND) {
 937                         /* Don't output token on error if first call. */
 938                         *send_token = NO_TOKEN_SEND;
 939                 } else {
 940                         *send_token = ERROR_TOKEN_SEND;
 941                 }
 942                 *negState = REJECT;
 943         }
 944         return ret;
 945 }
 946 
 947 /*ARGSUSED*/
 948 OM_uint32
 949 glue_spnego_gss_init_sec_context(
 950         void *context,
 951         OM_uint32 *minor_status,
 952         gss_cred_id_t claimant_cred_handle,
 953         gss_ctx_id_t *context_handle,
 954         gss_name_t target_name,
 955         gss_OID mech_type,
 956         OM_uint32 req_flags,
 957         OM_uint32 time_req,
 958         gss_channel_bindings_t input_chan_bindings,
 959         gss_buffer_t input_token,
 960         gss_OID *actual_mech,
 961         gss_buffer_t output_token,
 962         OM_uint32 *ret_flags,
 963         OM_uint32 *time_rec)
 964 {
 965         return(spnego_gss_init_sec_context(
 966                     minor_status,
 967                     claimant_cred_handle,
 968                     context_handle,
 969                     target_name,
 970                     mech_type,
 971                     req_flags,
 972                     time_req,
 973                     input_chan_bindings,
 974                     input_token,
 975                     actual_mech,
 976                     output_token,
 977                     ret_flags,
 978                     time_rec));
 979 }
 980 
 981 /*ARGSUSED*/
 982 OM_uint32
 983 spnego_gss_init_sec_context(
 984                         OM_uint32 *minor_status,
 985                         gss_cred_id_t claimant_cred_handle,
 986                         gss_ctx_id_t *context_handle,
 987                         gss_name_t target_name,
 988                         gss_OID mech_type,
 989                         OM_uint32 req_flags,
 990                         OM_uint32 time_req,
 991                         gss_channel_bindings_t input_chan_bindings,
 992                         gss_buffer_t input_token,
 993                         gss_OID *actual_mech,
 994                         gss_buffer_t output_token,
 995                         OM_uint32 *ret_flags,
 996                         OM_uint32 *time_rec)
 997 {
 998         /*
 999          * send_token is used to indicate in later steps
1000          * what type of token, if any should be sent or processed.
1001          * NO_TOKEN_SEND = no token should be sent
1002          * INIT_TOKEN_SEND = initial token will be sent
1003          * CONT_TOKEN_SEND = continuing tokens to be sent
1004          * CHECK_MIC = no token to be sent, but have a MIC to check.
1005          */
1006         send_token_flag send_token = NO_TOKEN_SEND;
1007         OM_uint32 tmpmin, ret, negState;
1008         gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
1009         gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1010         gss_OID_set mechSet = GSS_C_NO_OID_SET;
1011         spnego_gss_ctx_id_t spnego_ctx = NULL;
1012 
1013         dsyslog("Entering init_sec_context\n");
1014 
1015         mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
1016         negState = REJECT;
1017 
1018         if (minor_status != NULL)
1019                 *minor_status = 0;
1020         if (output_token != GSS_C_NO_BUFFER) {
1021                 output_token->length = 0;
1022                 output_token->value = NULL;
1023         }
1024         if (minor_status == NULL ||
1025             output_token == GSS_C_NO_BUFFER ||
1026             context_handle == NULL)
1027                 return GSS_S_CALL_INACCESSIBLE_WRITE;
1028 
1029         if (actual_mech != NULL)
1030                 *actual_mech = GSS_C_NO_OID;
1031 
1032         if (*context_handle == GSS_C_NO_CONTEXT) {
1033                 ret = init_ctx_new(minor_status, claimant_cred_handle,
1034                                    context_handle, &mechSet, &send_token);
1035                 if (ret != GSS_S_CONTINUE_NEEDED) {
1036                         goto cleanup;
1037                 }
1038         } else {
1039                 ret = init_ctx_cont(minor_status, context_handle,
1040                                     input_token, &mechtok_in,
1041                                     &mechListMIC_in, &negState, &send_token);
1042                 if (HARD_ERROR(ret)) {
1043                         goto cleanup;
1044                 }
1045         }
1046         spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
1047 
1048         /* Solaris SPNEGO */
1049         if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
1050                 spnego_gss_save_error_info(*minor_status, spnego_ctx);
1051 
1052         if (!spnego_ctx->mech_complete) {
1053                 ret = init_ctx_call_init(
1054                         minor_status, spnego_ctx,
1055                         claimant_cred_handle,
1056                         target_name, req_flags,
1057                         time_req, mechtok_in,
1058                         actual_mech, &mechtok_out,
1059                         ret_flags, time_rec,
1060                         &negState, &send_token);
1061         }
1062         /* create mic/check mic */
1063         if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
1064             (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
1065 
1066                 ret = handle_mic(minor_status,
1067                                  mechListMIC_in,
1068                                  (mechtok_out.length != 0),
1069                                  spnego_ctx, &mechListMIC_out,
1070                                  &negState, &send_token);
1071         }
1072 cleanup:
1073         if (send_token == INIT_TOKEN_SEND) {
1074                 if (make_spnego_tokenInit_msg(spnego_ctx,
1075                                               0,
1076                                               mechListMIC_out,
1077                                               req_flags,
1078                                               &mechtok_out, send_token,
1079                                               output_token) < 0) {
1080                         ret = GSS_S_FAILURE;
1081                 }
1082         } else if (send_token != NO_TOKEN_SEND) {
1083                 if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
1084                                               &mechtok_out, mechListMIC_out,
1085                                               send_token,
1086                                               output_token) < 0) {
1087                         ret = GSS_S_FAILURE;
1088                 }
1089         }
1090         gss_release_buffer(&tmpmin, &mechtok_out);
1091         if (ret == GSS_S_COMPLETE) {
1092                 /*
1093                  * Now, switch the output context to refer to the
1094                  * negotiated mechanism's context.
1095                  */
1096                 *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
1097                 if (actual_mech != NULL)
1098                         *actual_mech = spnego_ctx->actual_mech;
1099                 if (ret_flags != NULL)
1100                         *ret_flags = spnego_ctx->ctx_flags;
1101                 release_spnego_ctx(&spnego_ctx);
1102         } else if (ret != GSS_S_CONTINUE_NEEDED) {
1103                 if (spnego_ctx != NULL) {
1104                         gss_delete_sec_context(&tmpmin,
1105                                                &spnego_ctx->ctx_handle,
1106                                                GSS_C_NO_BUFFER);
1107                         release_spnego_ctx(&spnego_ctx);
1108                 }
1109                 *context_handle = GSS_C_NO_CONTEXT;
1110         }
1111         if (mechtok_in != GSS_C_NO_BUFFER) {
1112                 gss_release_buffer(&tmpmin, mechtok_in);
1113                 free(mechtok_in);
1114         }
1115         if (mechListMIC_in != GSS_C_NO_BUFFER) {
1116                 gss_release_buffer(&tmpmin, mechListMIC_in);
1117                 free(mechListMIC_in);
1118         }
1119         if (mechListMIC_out != GSS_C_NO_BUFFER) {
1120                 gss_release_buffer(&tmpmin, mechListMIC_out);
1121                 free(mechListMIC_out);
1122         }
1123         if (mechSet != GSS_C_NO_OID_SET) {
1124                 gss_release_oid_set(&tmpmin, &mechSet);
1125         }
1126         return ret;
1127 } /* init_sec_context */
1128 
1129 /* We don't want to import KRB5 headers here */
1130 static const gss_OID_desc gss_mech_krb5_oid =
1131         { 9, "\052\206\110\206\367\022\001\002\002" };
1132 static const gss_OID_desc gss_mech_krb5_wrong_oid =
1133         { 9, "\052\206\110\202\367\022\001\002\002" };
1134 
1135 /*
1136  * verify that the input token length is not 0. If it is, just return.
1137  * If the token length is greater than 0, der encode as a sequence
1138  * and place in buf_out, advancing buf_out.
1139  */
1140 
1141 static int
1142 put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
1143               unsigned int buflen)
1144 {
1145         int ret;
1146 
1147         /* if token length is 0, we do not want to send */
1148         if (input_token->length == 0)
1149                 return (0);
1150 
1151         if (input_token->length > buflen)
1152                 return (-1);
1153 
1154         *(*buf_out)++ = SEQUENCE;
1155         if ((ret = gssint_put_der_length(input_token->length, buf_out,
1156                             input_token->length)))
1157                 return (ret);
1158         TWRITE_STR(*buf_out, input_token->value, input_token->length);
1159         return (0);
1160 }
1161 
1162 /*
1163  * NegHints ::= SEQUENCE {
1164  *    hintName       [0]  GeneralString      OPTIONAL,
1165  *    hintAddress    [1]  OCTET STRING       OPTIONAL
1166  * }
1167  */
1168 
1169 #define HOST_PREFIX     "host@"
1170 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1171 
1172 static int
1173 make_NegHints(OM_uint32 *minor_status,
1174               gss_cred_id_t cred, gss_buffer_t *outbuf)
1175 {
1176         gss_buffer_desc hintNameBuf;
1177         gss_name_t hintName = GSS_C_NO_NAME;
1178         gss_name_t hintKerberosName;
1179         gss_OID hintNameType;
1180         OM_uint32 major_status;
1181         OM_uint32 minor;
1182         unsigned int tlen = 0;
1183         unsigned int hintNameSize = 0;
1184         unsigned int negHintsSize = 0;
1185         unsigned char *ptr;
1186         unsigned char *t;
1187 
1188         *outbuf = GSS_C_NO_BUFFER;
1189 
1190         if (cred != GSS_C_NO_CREDENTIAL) {
1191                 major_status = gss_inquire_cred(minor_status,
1192                                                 cred,
1193                                                 &hintName,
1194                                                 NULL,
1195                                                 NULL,
1196                                                 NULL);
1197                 if (major_status != GSS_S_COMPLETE)
1198                         return (major_status);
1199         }
1200 
1201         if (hintName == GSS_C_NO_NAME) {
1202                 krb5_error_code code;
1203                 krb5int_access kaccess;
1204                 char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
1205 
1206                 code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
1207                 if (code != 0) {
1208                         *minor_status = code;
1209                         return (GSS_S_FAILURE);
1210                 }
1211 
1212                 /* this breaks mutual authentication but Samba relies on it */
1213                 code = (*kaccess.clean_hostname)(NULL, NULL,
1214                                                  &hostname[HOST_PREFIX_LEN],
1215                                                  MAXHOSTNAMELEN);
1216                 if (code != 0) {
1217                         *minor_status = code;
1218                         return (GSS_S_FAILURE);
1219                 }
1220 
1221                 hintNameBuf.value = hostname;
1222                 hintNameBuf.length = strlen(hostname);
1223 
1224                 major_status = gss_import_name(minor_status,
1225                                                &hintNameBuf,
1226                                                GSS_C_NT_HOSTBASED_SERVICE,
1227                                                &hintName);
1228                 if (major_status != GSS_S_COMPLETE) {
1229                         return (major_status);
1230                 }
1231         }
1232 
1233         hintNameBuf.value = NULL;
1234         hintNameBuf.length = 0;
1235 
1236         major_status = gss_canonicalize_name(minor_status,
1237                                              hintName,
1238                                              (gss_OID)&gss_mech_krb5_oid,
1239                                              &hintKerberosName);
1240         if (major_status != GSS_S_COMPLETE) {
1241                 gss_release_name(&minor, &hintName);
1242                 return (major_status);
1243         }
1244         gss_release_name(&minor, &hintName);
1245 
1246         major_status = gss_display_name(minor_status,
1247                                         hintKerberosName,
1248                                         &hintNameBuf,
1249                                         &hintNameType);
1250         if (major_status != GSS_S_COMPLETE) {
1251                 gss_release_name(&minor, &hintKerberosName);
1252                 return (major_status);
1253         }
1254         gss_release_name(&minor, &hintKerberosName);
1255 
1256         /*
1257          * Now encode the name hint into a NegHints ASN.1 type
1258          */
1259         major_status = GSS_S_FAILURE;
1260 
1261         /* Length of DER encoded GeneralString */
1262         tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
1263                 hintNameBuf.length;
1264         hintNameSize = tlen;
1265 
1266         /* Length of DER encoded hintName */
1267         tlen += 1 + gssint_der_length_size(hintNameSize);
1268         negHintsSize = tlen;
1269 
1270         t = (unsigned char *)malloc(tlen);
1271         if (t == NULL) {
1272                 *minor_status = ENOMEM;
1273                 goto errout;
1274         }
1275 
1276         ptr = t;
1277 
1278         *ptr++ = CONTEXT | 0x00; /* hintName identifier */
1279         if (gssint_put_der_length(hintNameSize,
1280                                   &ptr, tlen - (int)(ptr-t)))
1281                 goto errout;
1282 
1283         *ptr++ = GENERAL_STRING;
1284         if (gssint_put_der_length(hintNameBuf.length,
1285                                   &ptr, tlen - (int)(ptr-t)))
1286                 goto errout;
1287 
1288         memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
1289         ptr += hintNameBuf.length;
1290 
1291         *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1292         if (*outbuf == NULL) {
1293                 *minor_status = ENOMEM;
1294                 goto errout;
1295         }
1296         (*outbuf)->value = (void *)t;
1297         (*outbuf)->length = ptr - t;
1298 
1299         t = NULL; /* don't free */
1300 
1301         *minor_status = 0;
1302         major_status = GSS_S_COMPLETE;
1303 
1304 errout:
1305         if (t != NULL) {
1306                 free(t);
1307         }
1308 
1309         gss_release_buffer(&minor, &hintNameBuf);
1310         return (major_status);
1311 }
1312 
1313 static OM_uint32
1314 acc_ctx_hints(OM_uint32 *minor_status,
1315               gss_ctx_id_t *ctx,
1316               gss_cred_id_t cred,
1317               gss_buffer_t *mechListMIC,
1318               OM_uint32 *negState,
1319               send_token_flag *return_token)
1320 {
1321         OM_uint32 tmpmin, ret;
1322         gss_OID_set supported_mechSet;
1323         spnego_gss_ctx_id_t sc = NULL;
1324 
1325         *mechListMIC = GSS_C_NO_BUFFER;
1326         supported_mechSet = GSS_C_NO_OID_SET;
1327         *return_token = ERROR_TOKEN_SEND;
1328         *negState = REJECT;
1329         *minor_status = 0;
1330 
1331         *ctx = GSS_C_NO_CONTEXT;
1332         ret = GSS_S_DEFECTIVE_TOKEN;
1333 
1334         if (cred != GSS_C_NO_CREDENTIAL) {
1335                 ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
1336                                        NULL, &supported_mechSet);
1337                 if (ret != GSS_S_COMPLETE) {
1338                         *return_token = NO_TOKEN_SEND;
1339                         goto cleanup;
1340                 }
1341         } else {
1342                 ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
1343                                           GSS_C_ACCEPT, NULL,
1344                                           &supported_mechSet);
1345                 if (ret != GSS_S_COMPLETE) {
1346                         *return_token = NO_TOKEN_SEND;
1347                         goto cleanup;
1348                 }
1349         }
1350 
1351         ret = make_NegHints(minor_status, cred, mechListMIC);
1352         if (ret != GSS_S_COMPLETE) {
1353                 *return_token = NO_TOKEN_SEND;
1354                 goto cleanup;
1355         }
1356 
1357         /*
1358          * Select the best match between the list of mechs
1359          * that the initiator requested and the list that
1360          * the acceptor will support.
1361          */
1362         sc = create_spnego_ctx();
1363         if (sc == NULL) {
1364                 ret = GSS_S_FAILURE;
1365                 *return_token = NO_TOKEN_SEND;
1366                 goto cleanup;
1367         }
1368         if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
1369                 ret = GSS_S_FAILURE;
1370                 *return_token = NO_TOKEN_SEND;
1371                 goto cleanup;
1372         }
1373         sc->internal_mech = GSS_C_NO_OID;
1374 
1375         *negState = ACCEPT_INCOMPLETE;
1376         *return_token = INIT_TOKEN_SEND;
1377         sc->firstpass = 1;
1378         *ctx = (gss_ctx_id_t)sc;
1379         ret = GSS_S_COMPLETE;
1380 
1381 cleanup:
1382         gss_release_oid_set(&tmpmin, &supported_mechSet);
1383         return ret;
1384 }
1385 
1386 /*
1387  * Solaris SPNEGO
1388  * mechoidset2str()
1389  * Input an OID set of mechs and output a string like so:
1390  *   '{ x y z } (mechname0), { a b c } (mechname1) ...'.
1391  * On error return NULL.
1392  * Caller needs to free returned string.
1393  */
1394 static const char *mech_no_map = "Can't map OID to mechname via /etc/gss/mech";
1395 static const char *oid_no_map = "Can't map OID to string";
1396 static char *
1397 mechoidset2str(gss_OID_set mechset)
1398 {
1399         int i, l;
1400         char buf[256] = {0};
1401         char *s = NULL;
1402 
1403         if (!mechset)
1404                 return NULL;
1405 
1406         for (i = 0; i < mechset->count; i++) {
1407                 OM_uint32 maj, min;
1408                 gss_buffer_desc oidstr;
1409                 gss_buffer_t oidstrp = &oidstr;
1410                 gss_OID mech_oid = &mechset->elements[i];
1411                 /* No need to free mech_name. */
1412                 const char *mech_name = __gss_oid_to_mech(mech_oid);
1413 
1414                 if (i > 0)
1415                         if (strlcat(buf, ", ", sizeof (buf)) >= sizeof (buf)) {
1416                                 if (oidstrp->value)
1417                                         gss_release_buffer(&min, oidstrp);
1418                                 break;
1419                         }
1420 
1421                 /* Add '{ x y x ... }'. */
1422                 maj = gss_oid_to_str(&min, mech_oid, oidstrp);
1423                 if (strlcat(buf, maj ? oid_no_map : oidstrp->value,
1424                             sizeof (buf)) >= sizeof (buf)) {
1425                         if (oidstrp->value)
1426                                 gss_release_buffer(&min, oidstrp);
1427                         break;
1428                 }
1429                 if (oidstrp->value)
1430                         gss_release_buffer(&min, oidstrp);
1431 
1432                 /* Add '(mech name)'. */
1433                 if (strlcat(buf, " (", sizeof (buf)) >= sizeof (buf))
1434                         break;
1435                 if (strlcat(buf, mech_name ? mech_name : mech_no_map,
1436                             sizeof (buf)) >= sizeof (buf))
1437                         break;
1438                 if (strlcat(buf, ") ", sizeof (buf)) >= sizeof (buf))
1439                         break;
1440         }
1441 
1442         /* Even if we have buf overflow, let's output what we got so far. */
1443         if (mechset->count) {
1444                 l = strlen(buf);
1445                 if (l > 0) {
1446                         s = malloc(l + 1);
1447                         if (!s)
1448                                 return NULL;
1449                         (void) strlcpy(s, buf, l);
1450                 }
1451         }
1452 
1453         return s ? s : NULL;
1454 }
1455 
1456 /*
1457  * Set negState to REJECT if the token is defective, else
1458  * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
1459  * preferred mechanism is supported.
1460  */
1461 static OM_uint32
1462 acc_ctx_new(OM_uint32 *minor_status,
1463             gss_buffer_t buf,
1464             gss_ctx_id_t *ctx,
1465             gss_cred_id_t cred,
1466             gss_buffer_t *mechToken,
1467             gss_buffer_t *mechListMIC,
1468             OM_uint32 *negState,
1469             send_token_flag *return_token)
1470 {
1471         OM_uint32 tmpmin, ret, req_flags;
1472         gss_OID_set supported_mechSet, mechTypes;
1473         gss_buffer_desc der_mechTypes;
1474         gss_OID mech_wanted;
1475         spnego_gss_ctx_id_t sc = NULL;
1476 
1477         ret = GSS_S_DEFECTIVE_TOKEN;
1478         der_mechTypes.length = 0;
1479         der_mechTypes.value = NULL;
1480         *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1481         supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
1482         *return_token = ERROR_TOKEN_SEND;
1483         *negState = REJECT;
1484         *minor_status = 0;
1485 
1486         ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1487                                &mechTypes, &req_flags,
1488                                mechToken, mechListMIC);
1489         if (ret != GSS_S_COMPLETE) {
1490                 goto cleanup;
1491         }
1492         if (cred != GSS_C_NO_CREDENTIAL) {
1493                 ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
1494                                        NULL, &supported_mechSet);
1495                 if (ret != GSS_S_COMPLETE) {
1496                         *return_token = NO_TOKEN_SEND;
1497                         goto cleanup;
1498                 }
1499         } else {
1500                 ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
1501                                           GSS_C_ACCEPT, NULL,
1502                                           &supported_mechSet);
1503                 if (ret != GSS_S_COMPLETE) {
1504                         *return_token = NO_TOKEN_SEND;
1505                         goto cleanup;
1506                 }
1507         }
1508         /*
1509          * Select the best match between the list of mechs
1510          * that the initiator requested and the list that
1511          * the acceptor will support.
1512          */
1513         mech_wanted = negotiate_mech_type(minor_status,
1514                                         supported_mechSet,
1515                                         mechTypes,
1516                                         negState);
1517         if (*negState == REJECT) {
1518                 /* Solaris SPNEGO: Spruce-up error msg */
1519                 char *mechTypesStr = mechoidset2str(mechTypes);
1520                 spnego_gss_ctx_id_t tmpsc = create_spnego_ctx();
1521                 if (tmpsc && *minor_status == ERR_SPNEGO_NEGOTIATION_FAILED) {
1522                         spnego_set_error_message(tmpsc, *minor_status,
1523                                                 dgettext(TEXT_DOMAIN,
1524                                                         "SPNEGO failed to negotiate a mechanism: client requested mech set '%s'"),
1525                                 mechTypesStr ? mechTypesStr : "<null>");
1526                 }
1527                 if (mechTypesStr)
1528                         free(mechTypesStr);
1529 
1530                 /*
1531                  * We save error here cuz the tmp ctx goes away (very) soon.
1532                  * So callers of acc_ctx_new() should NOT call it again.
1533                  */
1534                 spnego_gss_save_error_info(*minor_status, tmpsc);
1535                 if (tmpsc)
1536                         release_spnego_ctx(&tmpsc);
1537                 ret = GSS_S_BAD_MECH;
1538                 goto cleanup;
1539         }
1540 
1541         sc = (spnego_gss_ctx_id_t)*ctx;
1542         if (sc != NULL) {
1543                 gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1544                 assert(mech_wanted != GSS_C_NO_OID);
1545         } else
1546                 sc = create_spnego_ctx();
1547         if (sc == NULL) {
1548                 ret = GSS_S_FAILURE;
1549                 *return_token = NO_TOKEN_SEND;
1550                 generic_gss_release_oid(&tmpmin, &mech_wanted);
1551                 goto cleanup;
1552         }
1553         sc->internal_mech = mech_wanted;
1554         sc->DER_mechTypes = der_mechTypes;
1555         der_mechTypes.length = 0;
1556         der_mechTypes.value = NULL;
1557 
1558         if (*negState == REQUEST_MIC)
1559                 sc->mic_reqd = 1;
1560 
1561         *return_token = INIT_TOKEN_SEND;
1562         sc->firstpass = 1;
1563         *ctx = (gss_ctx_id_t)sc;
1564         ret = GSS_S_COMPLETE;
1565 cleanup:
1566         gss_release_oid_set(&tmpmin, &mechTypes);
1567         gss_release_oid_set(&tmpmin, &supported_mechSet);
1568         if (der_mechTypes.length != 0)
1569                 gss_release_buffer(&tmpmin, &der_mechTypes);
1570         return ret;
1571 }
1572 
1573 static OM_uint32
1574 acc_ctx_cont(OM_uint32 *minstat,
1575              gss_buffer_t buf,
1576              gss_ctx_id_t *ctx,
1577              gss_buffer_t *responseToken,
1578              gss_buffer_t *mechListMIC,
1579              OM_uint32 *negState,
1580              send_token_flag *return_token)
1581 {
1582         OM_uint32 ret, tmpmin;
1583         gss_OID supportedMech;
1584         spnego_gss_ctx_id_t sc;
1585         unsigned int len;
1586         unsigned char *ptr, *bufstart;
1587 
1588         sc = (spnego_gss_ctx_id_t)*ctx;
1589         ret = GSS_S_DEFECTIVE_TOKEN;
1590         *negState = REJECT;
1591         *minstat = 0;
1592         supportedMech = GSS_C_NO_OID;
1593         *return_token = ERROR_TOKEN_SEND;
1594         *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1595 
1596         ptr = bufstart = buf->value;
1597 #define REMAIN (buf->length - (ptr - bufstart))
1598         if (REMAIN > INT_MAX)
1599                 return GSS_S_DEFECTIVE_TOKEN;
1600 
1601         /*
1602          * Attempt to work with old Sun SPNEGO.
1603          */
1604         if (*ptr == HEADER_ID) {
1605                 ret = g_verify_token_header(gss_mech_spnego,
1606                                             &len, &ptr, 0, REMAIN);
1607                 if (ret) {
1608                         *minstat = ret;
1609                         return GSS_S_DEFECTIVE_TOKEN;
1610                 }
1611         }
1612         if (*ptr != (CONTEXT | 0x01)) {
1613                 return GSS_S_DEFECTIVE_TOKEN;
1614         }
1615         ret = get_negTokenResp(minstat, ptr, REMAIN,
1616                                negState, &supportedMech,
1617                                responseToken, mechListMIC);
1618         if (ret != GSS_S_COMPLETE)
1619                 goto cleanup;
1620 
1621         if (*responseToken == GSS_C_NO_BUFFER &&
1622             *mechListMIC == GSS_C_NO_BUFFER) {
1623 
1624                 ret = GSS_S_DEFECTIVE_TOKEN;
1625                 goto cleanup;
1626         }
1627         if (supportedMech != GSS_C_NO_OID) {
1628                 ret = GSS_S_DEFECTIVE_TOKEN;
1629                 goto cleanup;
1630         }
1631         sc->firstpass = 0;
1632         *negState = ACCEPT_INCOMPLETE;
1633         *return_token = CONT_TOKEN_SEND;
1634 cleanup:
1635         if (supportedMech != GSS_C_NO_OID) {
1636                 generic_gss_release_oid(&tmpmin, &supportedMech);
1637         }
1638         return ret;
1639 #undef REMAIN
1640 }
1641 
1642 /*
1643  * Verify that mech OID is either exactly the same as the negotiated
1644  * mech OID, or is a mech OID supported by the negotiated mech.  MS
1645  * implementations can list a most preferred mech using an incorrect
1646  * krb5 OID while emitting a krb5 initiator mech token having the
1647  * correct krb5 mech OID.
1648  */
1649 static OM_uint32
1650 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1651                 spnego_gss_ctx_id_t sc, gss_OID mechoid,
1652                 OM_uint32 *negState, send_token_flag *tokflag)
1653 {
1654         OM_uint32 ret, tmpmin;
1655         gss_mechanism mech = NULL;
1656         gss_OID_set mech_set = GSS_C_NO_OID_SET;
1657         int present = 0;
1658 
1659         if (g_OID_equal(sc->internal_mech, mechoid))
1660                 return GSS_S_COMPLETE;
1661 
1662         /*
1663          * SUNW17PACresync
1664          * If both mechs are kerb, we are done.
1665          */
1666         if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) {
1667                 return GSS_S_COMPLETE;
1668         }
1669 
1670         mech = gssint_get_mechanism(mechoid);
1671         if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1672                 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1673                 {
1674                         /*
1675                          * Solaris SPNEGO
1676                          * Spruce-up error msg.
1677                          */
1678                         OM_uint32 maj, maj_sc, min;
1679                         gss_buffer_desc oidstr, oidstr_sc;
1680                         /* No need to free mnamestr. */
1681                         const char *mnamestr = __gss_oid_to_mech(
1682                                 sc->internal_mech);
1683                         maj_sc = gss_oid_to_str(&min,
1684                                                 sc->internal_mech,
1685                                                 &oidstr_sc);
1686                         maj = gss_oid_to_str(&min, mechoid, &oidstr);
1687                         spnego_set_error_message(sc, *minor_status,
1688                                                 dgettext(TEXT_DOMAIN,
1689                                                         "SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
1690                                         maj ? oid_no_map: oidstr.value,
1691                                         maj_sc ? oid_no_map: oidstr_sc.value,
1692                                         mnamestr ? mnamestr : mech_no_map);
1693                         if (!maj)
1694                                 (void) gss_release_buffer(&min, &oidstr);
1695                         if (!maj_sc)
1696                                 (void) gss_release_buffer(&min, &oidstr_sc);
1697                 }
1698                 map_errcode(minor_status);
1699                 *negState = REJECT;
1700                 *tokflag = ERROR_TOKEN_SEND;
1701                 return GSS_S_BAD_MECH;
1702         }
1703         ret = mech->gss_indicate_mechs(mech->context, minor_status, &mech_set);
1704         if (ret != GSS_S_COMPLETE) {
1705                 *tokflag = NO_TOKEN_SEND;
1706                 map_error(minor_status, mech);
1707                 goto cleanup;
1708         }
1709         ret = gss_test_oid_set_member(minor_status, sc->internal_mech,
1710                                       mech_set, &present);
1711         if (ret != GSS_S_COMPLETE)
1712                 goto cleanup;
1713         if (!present) {
1714                 {
1715                         /*
1716                          * Solaris SPNEGO
1717                          * Spruce-up error msg.
1718                          */
1719                         OM_uint32 maj, min;
1720                         gss_buffer_desc oidstr;
1721                         char *mech_set_str = mechoidset2str(mech_set);
1722                         /* No need to free mnamestr. */
1723                         const char *mnamestr =
1724                                 __gss_oid_to_mech(sc->internal_mech);
1725                         maj = gss_oid_to_str(&min, sc->internal_mech, &oidstr);
1726                         *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1727                         spnego_set_error_message(sc, *minor_status,
1728                                                 dgettext(TEXT_DOMAIN,
1729                                                         "SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
1730                                 maj ? oid_no_map: oidstr.value,
1731                                 mnamestr ? mnamestr : mech_no_map,
1732                                 mech_set_str ? mech_set_str : "<null>");
1733                         if (!maj)
1734                                 (void) gss_release_buffer(&min, &oidstr);
1735                         if (mech_set_str)
1736                                 free(mech_set_str);
1737                 }
1738                 map_errcode(minor_status);
1739                 *negState = REJECT;
1740                 *tokflag = ERROR_TOKEN_SEND;
1741                 ret = GSS_S_BAD_MECH;
1742         }
1743 cleanup:
1744         gss_release_oid_set(&tmpmin, &mech_set);
1745         return ret;
1746 }
1747 #ifndef LEAN_CLIENT
1748 /*
1749  * Wrap call to gss_accept_sec_context() and update state
1750  * accordingly.
1751  */
1752 static OM_uint32
1753 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1754                  gss_cred_id_t cred, gss_buffer_t mechtok_in,
1755                  gss_OID *mech_type, gss_buffer_t mechtok_out,
1756                  OM_uint32 *ret_flags, OM_uint32 *time_rec,
1757                  gss_cred_id_t *delegated_cred_handle,
1758                  OM_uint32 *negState, send_token_flag *tokflag)
1759 {
1760         OM_uint32 ret;
1761         gss_OID_desc mechoid;
1762 
1763         if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1764                 /*
1765                  * mechoid is an alias; don't free it.
1766                  */
1767                 ret = gssint_get_mech_type(&mechoid, mechtok_in);
1768                 if (ret != GSS_S_COMPLETE) {
1769                         *tokflag = NO_TOKEN_SEND;
1770                         return ret;
1771                 }
1772                 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1773                                     negState, tokflag);
1774                 if (ret != GSS_S_COMPLETE)
1775                         return ret;
1776         }
1777 
1778         ret = gss_accept_sec_context(minor_status,
1779                                      &sc->ctx_handle,
1780                                      cred,
1781                                      mechtok_in,
1782                                      GSS_C_NO_CHANNEL_BINDINGS,
1783                                      &sc->internal_name,
1784                                      mech_type,
1785                                      mechtok_out,
1786                                      &sc->ctx_flags,
1787                                      time_rec,
1788                                      delegated_cred_handle);
1789 
1790         if (ret == GSS_S_COMPLETE) {
1791 #ifdef MS_BUG_TEST
1792                 /*
1793                  * Force MIC to be not required even if we previously
1794                  * requested a MIC.
1795                  */
1796                 char *envstr = getenv("MS_FORCE_NO_MIC");
1797 
1798                 if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1799                     !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1800                     sc->mic_reqd) {
1801 
1802                         sc->mic_reqd = 0;
1803                 }
1804 #endif
1805                 sc->mech_complete = 1;
1806                 if (ret_flags != NULL)
1807                         *ret_flags = sc->ctx_flags;
1808 
1809                 if (!sc->mic_reqd) {
1810                         *negState = ACCEPT_COMPLETE;
1811                         ret = GSS_S_COMPLETE;
1812                 } else {
1813                         ret = GSS_S_CONTINUE_NEEDED;
1814                 }
1815         } else if (ret != GSS_S_CONTINUE_NEEDED) {
1816                 *negState = REJECT;
1817                 *tokflag = ERROR_TOKEN_SEND;
1818         }
1819         return ret;
1820 }
1821 
1822 /*ARGSUSED*/
1823 OM_uint32
1824 glue_spnego_gss_accept_sec_context(
1825                             void *context,
1826                             OM_uint32 *minor_status,
1827                             gss_ctx_id_t *context_handle,
1828                             gss_cred_id_t verifier_cred_handle,
1829                             gss_buffer_t input_token,
1830                             gss_channel_bindings_t input_chan_bindings,
1831                             gss_name_t *src_name,
1832                             gss_OID *mech_type,
1833                             gss_buffer_t output_token,
1834                             OM_uint32 *ret_flags,
1835                             OM_uint32 *time_rec,
1836                             gss_cred_id_t *delegated_cred_handle)
1837 {
1838         return(spnego_gss_accept_sec_context(
1839                     minor_status,
1840                     context_handle,
1841                     verifier_cred_handle,
1842                     input_token,
1843                     input_chan_bindings,
1844                     src_name,
1845                     mech_type,
1846                     output_token,
1847                     ret_flags,
1848                     time_rec,
1849                     delegated_cred_handle));
1850 }
1851 
1852 /*ARGSUSED*/
1853 OM_uint32
1854 spnego_gss_accept_sec_context(
1855                             OM_uint32 *minor_status,
1856                             gss_ctx_id_t *context_handle,
1857                             gss_cred_id_t verifier_cred_handle,
1858                             gss_buffer_t input_token,
1859                             gss_channel_bindings_t input_chan_bindings,
1860                             gss_name_t *src_name,
1861                             gss_OID *mech_type,
1862                             gss_buffer_t output_token,
1863                             OM_uint32 *ret_flags,
1864                             OM_uint32 *time_rec,
1865                             gss_cred_id_t *delegated_cred_handle)
1866 {
1867         OM_uint32 ret, tmpmin, negState;
1868         send_token_flag return_token;
1869         gss_buffer_t mechtok_in, mic_in, mic_out;
1870         gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1871         spnego_gss_ctx_id_t sc = NULL;
1872         OM_uint32 mechstat = GSS_S_FAILURE;
1873         int sendTokenInit = 0, tmpret;
1874 
1875         mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1876 
1877         if (minor_status != NULL)
1878                 *minor_status = 0;
1879         if (output_token != GSS_C_NO_BUFFER) {
1880                 output_token->length = 0;
1881                 output_token->value = NULL;
1882         }
1883 
1884 
1885         if (minor_status == NULL ||
1886             output_token == GSS_C_NO_BUFFER ||
1887             context_handle == NULL) {
1888                 return GSS_S_CALL_INACCESSIBLE_WRITE;
1889         }
1890 
1891         if (input_token == GSS_C_NO_BUFFER) {
1892                 return GSS_S_CALL_INACCESSIBLE_READ;
1893         }
1894 
1895         sc = (spnego_gss_ctx_id_t)*context_handle;
1896         if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1897                 if (src_name != NULL)
1898                         *src_name = GSS_C_NO_NAME;
1899                 if (mech_type != NULL)
1900                         *mech_type = GSS_C_NO_OID;
1901                 if (time_rec != NULL)
1902                         *time_rec = 0;
1903                 if (ret_flags != NULL)
1904                         *ret_flags = 0;
1905                 if (delegated_cred_handle != NULL)
1906                         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1907                 if (input_token->length == 0) {
1908                         ret = acc_ctx_hints(minor_status,
1909                                             context_handle,
1910                                             verifier_cred_handle,
1911                                             &mic_out,
1912                                             &negState,
1913                                             &return_token);
1914                         if (ret != GSS_S_COMPLETE)
1915                                 goto cleanup;
1916                         sendTokenInit = 1;
1917                         ret = GSS_S_CONTINUE_NEEDED;
1918                 } else {
1919                         /* Can set negState to REQUEST_MIC */
1920                         ret = acc_ctx_new(minor_status, input_token,
1921                                           context_handle, verifier_cred_handle,
1922                                           &mechtok_in, &mic_in,
1923                                           &negState, &return_token);
1924                         if (ret != GSS_S_COMPLETE)
1925                                 goto cleanup;
1926                         ret = GSS_S_CONTINUE_NEEDED;
1927                 }
1928         } else {
1929                 /* Can set negState to ACCEPT_INCOMPLETE */
1930                 ret = acc_ctx_cont(minor_status, input_token,
1931                                    context_handle, &mechtok_in,
1932                                    &mic_in, &negState, &return_token);
1933                 if (ret != GSS_S_COMPLETE)
1934                         goto cleanup;
1935                 ret = GSS_S_CONTINUE_NEEDED;
1936         }
1937 
1938         sc = (spnego_gss_ctx_id_t)*context_handle;
1939         /*
1940          * Handle mechtok_in and mic_in only if they are
1941          * present in input_token.  If neither is present, whether
1942          * this is an error depends on whether this is the first
1943          * round-trip.  RET is set to a default value according to
1944          * whether it is the first round-trip.
1945          */
1946         mechstat = GSS_S_FAILURE;
1947         if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1948                 ret = acc_ctx_call_acc(minor_status, sc,
1949                                        verifier_cred_handle, mechtok_in,
1950                                        mech_type, &mechtok_out,
1951                                        ret_flags, time_rec,
1952                                        delegated_cred_handle,
1953                                        &negState, &return_token);
1954         } else if (negState == REQUEST_MIC) {
1955                 mechstat = GSS_S_CONTINUE_NEEDED;
1956         }
1957 
1958         /* Solaris SPNEGO */
1959         if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
1960                 spnego_gss_save_error_info(*minor_status, sc);
1961 
1962         if (!HARD_ERROR(ret) && sc->mech_complete &&
1963             (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1964 
1965                 ret = handle_mic(minor_status, mic_in,
1966                                  (mechtok_out.length != 0),
1967                                  sc, &mic_out,
1968                                  &negState, &return_token);
1969         }
1970 
1971 cleanup:
1972         if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1973                 assert(sc != NULL);
1974                 tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1975                                                    GSS_C_NO_BUFFER,
1976                                                    return_token, output_token);
1977                 if (tmpret < 0)
1978                         ret = GSS_S_FAILURE;
1979         } else if (return_token != NO_TOKEN_SEND &&
1980                    return_token != CHECK_MIC) {
1981                 tmpret = make_spnego_tokenTarg_msg(negState,
1982                                                    sc ? sc->internal_mech :
1983                                                    GSS_C_NO_OID,
1984                                                    &mechtok_out, mic_out,
1985                                                    return_token,
1986                                                    output_token);
1987                 if (tmpret < 0)
1988                         ret = GSS_S_FAILURE;
1989         }
1990         if (ret == GSS_S_COMPLETE) {
1991                 *context_handle = (gss_ctx_id_t)sc->ctx_handle;
1992                 if (sc->internal_name != GSS_C_NO_NAME &&
1993                     src_name != NULL) {
1994                         *src_name = sc->internal_name;
1995                 }
1996                 release_spnego_ctx(&sc);
1997         }
1998         gss_release_buffer(&tmpmin, &mechtok_out);
1999         if (mechtok_in != GSS_C_NO_BUFFER) {
2000                 gss_release_buffer(&tmpmin, mechtok_in);
2001                 free(mechtok_in);
2002         }
2003         if (mic_in != GSS_C_NO_BUFFER) {
2004                 gss_release_buffer(&tmpmin, mic_in);
2005                 free(mic_in);
2006         }
2007         if (mic_out != GSS_C_NO_BUFFER) {
2008                 gss_release_buffer(&tmpmin, mic_out);
2009                 free(mic_out);
2010         }
2011         return ret;
2012 }
2013 #endif /*  LEAN_CLIENT */
2014 
2015 /*ARGSUSED*/
2016 OM_uint32
2017 glue_spnego_gss_display_status(
2018         void *context,
2019                 OM_uint32 *minor_status,
2020                 OM_uint32 status_value,
2021                 int status_type,
2022                 gss_OID mech_type,
2023                 OM_uint32 *message_context,
2024                 gss_buffer_t status_string)
2025 {
2026         return (spnego_gss_display_status(minor_status,
2027                                         status_value,
2028                                         status_type,
2029                                         mech_type,
2030                                         message_context,
2031                                         status_string));
2032 }
2033 
2034 /*ARGSUSED*/
2035 OM_uint32
2036 spnego_gss_display_status(
2037                 OM_uint32 *minor_status,
2038                 OM_uint32 status_value,
2039                 int status_type,
2040                 gss_OID mech_type,
2041                 OM_uint32 *message_context,
2042                 gss_buffer_t status_string)
2043 {
2044         dsyslog("Entering display_status\n");
2045 
2046         *message_context = 0;
2047         switch (status_value) {
2048             case ERR_SPNEGO_NO_MECHS_AVAILABLE:
2049                 /* CSTYLED */
2050                 *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
2051                 break;
2052             case ERR_SPNEGO_NO_CREDS_ACQUIRED:
2053                 /* CSTYLED */
2054                 *status_string = make_err_msg("SPNEGO failed to acquire creds");
2055                 break;
2056             case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
2057                 /* CSTYLED */
2058                 *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
2059                 break;
2060             case ERR_SPNEGO_NEGOTIATION_FAILED:
2061                 /* CSTYLED */
2062                 return(spnego_gss_display_status2(minor_status,
2063                                                     status_value,
2064                                                     status_type,
2065                                                     mech_type,
2066                                                     message_context,
2067                                                     status_string));
2068             case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
2069                 /* CSTYLED */
2070                 *status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
2071                 break;
2072             default:
2073                 /*
2074                  * Solaris SPNEGO
2075                  * If mech_spnego calls mech_krb5 (via libgss) and an
2076                  * error occurs there, give it a shot.
2077                  */
2078                 /* CSTYLED */
2079                 return(krb5_gss_display_status2(minor_status,
2080                                                 status_value,
2081                                                 status_type,
2082                                                 (gss_OID)&gss_mech_krb5_oid,
2083                                                 message_context,
2084                                                 status_string));
2085 
2086         }
2087 
2088         dsyslog("Leaving display_status\n");
2089         return (GSS_S_COMPLETE);
2090 }
2091 
2092 /*ARGSUSED*/
2093 OM_uint32
2094 glue_spnego_gss_import_name(
2095         void *context,
2096                     OM_uint32 *minor_status,
2097                     gss_buffer_t input_name_buffer,
2098                     gss_OID input_name_type,
2099                     gss_name_t *output_name)
2100 {
2101         return(spnego_gss_import_name(minor_status,
2102                                     input_name_buffer,
2103                                     input_name_type,
2104                                     output_name));
2105 }
2106 
2107 /*ARGSUSED*/
2108 OM_uint32
2109 spnego_gss_import_name(
2110                     OM_uint32 *minor_status,
2111                     gss_buffer_t input_name_buffer,
2112                     gss_OID input_name_type,
2113                     gss_name_t *output_name)
2114 {
2115         OM_uint32 status;
2116 
2117         dsyslog("Entering import_name\n");
2118 
2119         status = gss_import_name(minor_status, input_name_buffer,
2120                         input_name_type, output_name);
2121 
2122         dsyslog("Leaving import_name\n");
2123         return (status);
2124 }
2125 
2126 /*ARGSUSED*/
2127 OM_uint32
2128 glue_spnego_gss_release_name(
2129         void *context,
2130                         OM_uint32 *minor_status,
2131                         gss_name_t *input_name)
2132 {
2133         return(spnego_gss_release_name(minor_status, input_name));
2134 }
2135 
2136 /*ARGSUSED*/
2137 OM_uint32
2138 spnego_gss_release_name(
2139                         OM_uint32 *minor_status,
2140                         gss_name_t *input_name)
2141 {
2142         OM_uint32 status;
2143 
2144         dsyslog("Entering release_name\n");
2145 
2146         status = gss_release_name(minor_status, input_name);
2147 
2148         dsyslog("Leaving release_name\n");
2149         return (status);
2150 }
2151 
2152 /*ARGSUSED*/
2153 OM_uint32
2154 glue_spnego_gss_compare_name(
2155         void *context,
2156         OM_uint32 *minor_status,
2157         const gss_name_t name1,
2158         const gss_name_t name2,
2159         int *name_equal)
2160 {
2161         return(spnego_gss_compare_name(minor_status,
2162                                 name1,
2163                                 name2,
2164                                 name_equal));
2165 }
2166 /*ARGSUSED*/
2167 OM_uint32
2168 spnego_gss_compare_name(
2169                         OM_uint32 *minor_status,
2170                         const gss_name_t name1,
2171                         const gss_name_t name2,
2172                         int *name_equal)
2173 {
2174         OM_uint32 status = GSS_S_COMPLETE;
2175         dsyslog("Entering compare_name\n");
2176 
2177         status = gss_compare_name(minor_status, name1, name2, name_equal);
2178 
2179         dsyslog("Leaving compare_name\n");
2180         return (status);
2181 }
2182 
2183 /*ARGSUSED*/
2184 OM_uint32
2185 glue_spnego_gss_display_name(
2186         void *context,
2187                         OM_uint32 *minor_status,
2188                         gss_name_t input_name,
2189                         gss_buffer_t output_name_buffer,
2190                         gss_OID *output_name_type)
2191 {
2192         return(spnego_gss_display_name(
2193                     minor_status,
2194                     input_name,
2195                     output_name_buffer,
2196                     output_name_type));
2197 }
2198 
2199 /*ARGSUSED*/
2200 OM_uint32
2201 spnego_gss_display_name(
2202                         OM_uint32 *minor_status,
2203                         gss_name_t input_name,
2204                         gss_buffer_t output_name_buffer,
2205                         gss_OID *output_name_type)
2206 {
2207         OM_uint32 status = GSS_S_COMPLETE;
2208         dsyslog("Entering display_name\n");
2209 
2210         status = gss_display_name(minor_status, input_name,
2211                         output_name_buffer, output_name_type);
2212 
2213         dsyslog("Leaving display_name\n");
2214         return (status);
2215 }
2216 
2217 
2218 /*ARGSUSED*/
2219 OM_uint32
2220 glue_spnego_gss_inquire_names_for_mech(
2221         void            *context,
2222         OM_uint32       *minor_status,
2223         gss_OID         mechanism,
2224         gss_OID_set     *name_types)
2225 {
2226         return(spnego_gss_inquire_names_for_mech(minor_status,
2227                                                 mechanism,
2228                                                 name_types));
2229 }
2230 /*ARGSUSED*/
2231 OM_uint32
2232 spnego_gss_inquire_names_for_mech(
2233                                 OM_uint32       *minor_status,
2234                                 gss_OID         mechanism,
2235                                 gss_OID_set     *name_types)
2236 {
2237         OM_uint32   major, minor;
2238 
2239         dsyslog("Entering inquire_names_for_mech\n");
2240         /*
2241          * We only know how to handle our own mechanism.
2242          */
2243         if ((mechanism != GSS_C_NULL_OID) &&
2244             !g_OID_equal(gss_mech_spnego, mechanism)) {
2245                 *minor_status = 0;
2246                 return (GSS_S_FAILURE);
2247         }
2248 
2249         major = gss_create_empty_oid_set(minor_status, name_types);
2250         if (major == GSS_S_COMPLETE) {
2251                 /* Now add our members. */
2252                 if (((major = gss_add_oid_set_member(minor_status,
2253                                 (gss_OID) GSS_C_NT_USER_NAME,
2254                                 name_types)) == GSS_S_COMPLETE) &&
2255                     ((major = gss_add_oid_set_member(minor_status,
2256                                 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2257                                 name_types)) == GSS_S_COMPLETE) &&
2258                     ((major = gss_add_oid_set_member(minor_status,
2259                                 (gss_OID) GSS_C_NT_STRING_UID_NAME,
2260                                 name_types)) == GSS_S_COMPLETE)) {
2261                         major = gss_add_oid_set_member(minor_status,
2262                                 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2263                                 name_types);
2264                 }
2265 
2266                 if (major != GSS_S_COMPLETE)
2267                         (void) gss_release_oid_set(&minor, name_types);
2268         }
2269 
2270         dsyslog("Leaving inquire_names_for_mech\n");
2271         return (major);
2272 }
2273 
2274 OM_uint32
2275 spnego_gss_unwrap(
2276                 OM_uint32 *minor_status,
2277                 gss_ctx_id_t context_handle,
2278                 gss_buffer_t input_message_buffer,
2279                 gss_buffer_t output_message_buffer,
2280                 int *conf_state,
2281                 gss_qop_t *qop_state)
2282 {
2283         OM_uint32 ret;
2284         ret = gss_unwrap(minor_status,
2285                         context_handle,
2286                         input_message_buffer,
2287                         output_message_buffer,
2288                         conf_state,
2289                         qop_state);
2290 
2291         return (ret);
2292 }
2293 
2294 OM_uint32
2295 spnego_gss_wrap(
2296                 OM_uint32 *minor_status,
2297                 gss_ctx_id_t context_handle,
2298                 int conf_req_flag,
2299                 gss_qop_t qop_req,
2300                 gss_buffer_t input_message_buffer,
2301                 int *conf_state,
2302                 gss_buffer_t output_message_buffer)
2303 {
2304         OM_uint32 ret;
2305         ret = gss_wrap(minor_status,
2306                     context_handle,
2307                     conf_req_flag,
2308                     qop_req,
2309                     input_message_buffer,
2310                     conf_state,
2311                     output_message_buffer);
2312 
2313         return (ret);
2314 }
2315 
2316 OM_uint32
2317 spnego_gss_process_context_token(
2318                                 OM_uint32       *minor_status,
2319                                 const gss_ctx_id_t context_handle,
2320                                 const gss_buffer_t token_buffer)
2321 {
2322         OM_uint32 ret;
2323         ret = gss_process_context_token(minor_status,
2324                                         context_handle,
2325                                         token_buffer);
2326 
2327         return (ret);
2328 }
2329 
2330 OM_uint32
2331 glue_spnego_gss_delete_sec_context(
2332         void *context,
2333                             OM_uint32 *minor_status,
2334                             gss_ctx_id_t *context_handle,
2335                             gss_buffer_t output_token)
2336 {
2337         return(spnego_gss_delete_sec_context(minor_status,
2338                                             context_handle, output_token));
2339 }
2340 
2341 OM_uint32
2342 spnego_gss_delete_sec_context(
2343                             OM_uint32 *minor_status,
2344                             gss_ctx_id_t *context_handle,
2345                             gss_buffer_t output_token)
2346 {
2347         OM_uint32 ret = GSS_S_COMPLETE;
2348         spnego_gss_ctx_id_t *ctx =
2349                     (spnego_gss_ctx_id_t *)context_handle;
2350 
2351         if (context_handle == NULL)
2352                 return (GSS_S_FAILURE);
2353 
2354         /*
2355          * If this is still an SPNEGO mech, release it locally.
2356          */
2357         if (*ctx != NULL &&
2358             (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2359                 (void) release_spnego_ctx(ctx);
2360                 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */
2361                 if (output_token) {
2362                         output_token->length = 0; 
2363                         output_token->value = NULL;
2364                 }
2365         } else {
2366                 ret = gss_delete_sec_context(minor_status,
2367                                     context_handle,
2368                                     output_token);
2369         }
2370 
2371         return (ret);
2372 }
2373 
2374 OM_uint32
2375 glue_spnego_gss_context_time(
2376         void *context,
2377         OM_uint32       *minor_status,
2378         const gss_ctx_id_t context_handle,
2379         OM_uint32       *time_rec)
2380 {
2381         return(spnego_gss_context_time(minor_status,
2382                                     context_handle,
2383                                     time_rec));
2384 }
2385 
2386 OM_uint32
2387 spnego_gss_context_time(
2388                         OM_uint32       *minor_status,
2389                         const gss_ctx_id_t context_handle,
2390                         OM_uint32       *time_rec)
2391 {
2392         OM_uint32 ret;
2393         ret = gss_context_time(minor_status,
2394                             context_handle,
2395                             time_rec);
2396         return (ret);
2397 }
2398 
2399 #ifndef LEAN_CLIENT
2400 OM_uint32
2401 glue_spnego_gss_export_sec_context(
2402         void *context,
2403         OM_uint32         *minor_status,
2404         gss_ctx_id_t *context_handle,
2405         gss_buffer_t interprocess_token)
2406 {
2407         return(spnego_gss_export_sec_context(minor_status,
2408                                     context_handle,
2409                                     interprocess_token));
2410 }
2411 OM_uint32
2412 spnego_gss_export_sec_context(
2413                             OM_uint32     *minor_status,
2414                             gss_ctx_id_t *context_handle,
2415                             gss_buffer_t interprocess_token)
2416 {
2417         OM_uint32 ret;
2418         ret = gss_export_sec_context(minor_status,
2419                                     context_handle,
2420                                     interprocess_token);
2421         return (ret);
2422 }
2423 
2424 OM_uint32
2425 glue_spnego_gss_import_sec_context(
2426         void *context,
2427         OM_uint32               *minor_status,
2428         const gss_buffer_t      interprocess_token,
2429         gss_ctx_id_t            *context_handle)
2430 {
2431         return(spnego_gss_import_sec_context(minor_status,
2432                                     interprocess_token,
2433                                     context_handle));
2434 }
2435 OM_uint32
2436 spnego_gss_import_sec_context(
2437         OM_uint32               *minor_status,
2438         const gss_buffer_t      interprocess_token,
2439         gss_ctx_id_t            *context_handle)
2440 {
2441         OM_uint32 ret;
2442         ret = gss_import_sec_context(minor_status,
2443                                     interprocess_token,
2444                                     context_handle);
2445         return (ret);
2446 }
2447 #endif /* LEAN_CLIENT */
2448 
2449 OM_uint32
2450 glue_spnego_gss_inquire_context(
2451         void *context,
2452                         OM_uint32       *minor_status,
2453                         const gss_ctx_id_t context_handle,
2454                         gss_name_t      *src_name,
2455                         gss_name_t      *targ_name,
2456                         OM_uint32       *lifetime_rec,
2457                         gss_OID         *mech_type,
2458                         OM_uint32       *ctx_flags,
2459                         int             *locally_initiated,
2460                         int             *opened)
2461 {
2462         return(spnego_gss_inquire_context(
2463                     minor_status,
2464                     context_handle,
2465                     src_name,
2466                     targ_name,
2467                     lifetime_rec,
2468                     mech_type,
2469                     ctx_flags,
2470                     locally_initiated,
2471                     opened));
2472 }
2473 
2474 OM_uint32
2475 spnego_gss_inquire_context(
2476                         OM_uint32       *minor_status,
2477                         const gss_ctx_id_t context_handle,
2478                         gss_name_t      *src_name,
2479                         gss_name_t      *targ_name,
2480                         OM_uint32       *lifetime_rec,
2481                         gss_OID         *mech_type,
2482                         OM_uint32       *ctx_flags,
2483                         int             *locally_initiated,
2484                         int             *opened)
2485 {
2486         OM_uint32 ret = GSS_S_COMPLETE;
2487 
2488         ret = gss_inquire_context(minor_status,
2489                                 context_handle,
2490                                 src_name,
2491                                 targ_name,
2492                                 lifetime_rec,
2493                                 mech_type,
2494                                 ctx_flags,
2495                                 locally_initiated,
2496                                 opened);
2497 
2498         return (ret);
2499 }
2500 
2501 OM_uint32
2502 glue_spnego_gss_wrap_size_limit(
2503         void *context,
2504         OM_uint32       *minor_status,
2505         const gss_ctx_id_t context_handle,
2506         int             conf_req_flag,
2507         gss_qop_t       qop_req,
2508         OM_uint32       req_output_size,
2509         OM_uint32       *max_input_size)
2510 {
2511         return(spnego_gss_wrap_size_limit(minor_status,
2512                                 context_handle,
2513                                 conf_req_flag,
2514                                 qop_req,
2515                                 req_output_size,
2516                                 max_input_size));
2517 }
2518 
2519 OM_uint32
2520 spnego_gss_wrap_size_limit(
2521         OM_uint32       *minor_status,
2522         const gss_ctx_id_t context_handle,
2523         int             conf_req_flag,
2524         gss_qop_t       qop_req,
2525         OM_uint32       req_output_size,
2526         OM_uint32       *max_input_size)
2527 {
2528         OM_uint32 ret;
2529         ret = gss_wrap_size_limit(minor_status,
2530                                 context_handle,
2531                                 conf_req_flag,
2532                                 qop_req,
2533                                 req_output_size,
2534                                 max_input_size);
2535         return (ret);
2536 }
2537 
2538 #if 0 /* SUNW17PACresync */
2539 OM_uint32
2540 spnego_gss_get_mic(
2541                 OM_uint32 *minor_status,
2542                 const gss_ctx_id_t context_handle,
2543                 gss_qop_t  qop_req,
2544                 const gss_buffer_t message_buffer,
2545                 gss_buffer_t message_token)
2546 {
2547         OM_uint32 ret;
2548         ret = gss_get_mic(minor_status,
2549                     context_handle,
2550                     qop_req,
2551                     message_buffer,
2552                     message_token);
2553         return (ret);
2554 }
2555 #endif
2556 
2557 OM_uint32
2558 spnego_gss_verify_mic(
2559                 OM_uint32 *minor_status,
2560                 const gss_ctx_id_t context_handle,
2561                 const gss_buffer_t msg_buffer,
2562                 const gss_buffer_t token_buffer,
2563                 gss_qop_t *qop_state)
2564 {
2565         OM_uint32 ret;
2566         ret = gss_verify_mic(minor_status,
2567                             context_handle,
2568                             msg_buffer,
2569                             token_buffer,
2570                             qop_state);
2571         return (ret);
2572 }
2573 
2574 OM_uint32
2575 spnego_gss_inquire_sec_context_by_oid(
2576                 OM_uint32 *minor_status,
2577                 const gss_ctx_id_t context_handle,
2578                 const gss_OID desired_object,
2579                 gss_buffer_set_t *data_set)
2580 {
2581         OM_uint32 ret;
2582         ret = gss_inquire_sec_context_by_oid(minor_status,
2583                             context_handle,
2584                             desired_object,
2585                             data_set);
2586         return (ret);
2587 }
2588 
2589 /*
2590  * SUNW17PACresync
2591  * These GSS funcs not needed yet, so disable them.
2592  * Revisit for full 1.7 resync.
2593  */
2594 #if 0
2595 OM_uint32
2596 spnego_gss_set_sec_context_option(
2597                 OM_uint32 *minor_status,
2598                 gss_ctx_id_t *context_handle,
2599                 const gss_OID desired_object,
2600                 const gss_buffer_t value)
2601 {
2602         OM_uint32 ret;
2603         ret = gss_set_sec_context_option(minor_status,
2604                             context_handle,
2605                             desired_object,
2606                             value);
2607         return (ret);
2608 }
2609 
2610 OM_uint32
2611 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2612                      gss_ctx_id_t context_handle,
2613                      int conf_req_flag,
2614                      gss_qop_t qop_req,
2615                      gss_buffer_t input_assoc_buffer,
2616                      gss_buffer_t input_payload_buffer,
2617                      int *conf_state,
2618                      gss_buffer_t output_message_buffer)
2619 {
2620         OM_uint32 ret;
2621         ret = gss_wrap_aead(minor_status,
2622                             context_handle,
2623                             conf_req_flag,
2624                             qop_req,
2625                             input_assoc_buffer,
2626                             input_payload_buffer,
2627                             conf_state,
2628                             output_message_buffer);
2629 
2630         return (ret);
2631 }
2632 
2633 OM_uint32
2634 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2635                        gss_ctx_id_t context_handle,
2636                        gss_buffer_t input_message_buffer,
2637                        gss_buffer_t input_assoc_buffer,
2638                        gss_buffer_t output_payload_buffer,
2639                        int *conf_state,
2640                        gss_qop_t *qop_state)
2641 {
2642         OM_uint32 ret;
2643         ret = gss_unwrap_aead(minor_status,
2644                               context_handle,
2645                               input_message_buffer,
2646                               input_assoc_buffer,
2647                               output_payload_buffer,
2648                               conf_state,
2649                               qop_state);
2650         return (ret);
2651 }
2652 
2653 OM_uint32
2654 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2655                     gss_ctx_id_t context_handle,
2656                     int conf_req_flag,
2657                     gss_qop_t qop_req,
2658                     int *conf_state,
2659                     gss_iov_buffer_desc *iov,
2660                     int iov_count)
2661 {
2662         OM_uint32 ret;
2663         ret = gss_wrap_iov(minor_status,
2664                            context_handle,
2665                            conf_req_flag,
2666                            qop_req,
2667                            conf_state,
2668                            iov,
2669                            iov_count);
2670         return (ret);
2671 }
2672 
2673 OM_uint32
2674 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2675                       gss_ctx_id_t context_handle,
2676                       int *conf_state,
2677                       gss_qop_t *qop_state,
2678                       gss_iov_buffer_desc *iov,
2679                       int iov_count)
2680 {
2681         OM_uint32 ret;
2682         ret = gss_unwrap_iov(minor_status,
2683                              context_handle,
2684                              conf_state,
2685                              qop_state,
2686                              iov,
2687                              iov_count);
2688         return (ret);
2689 }
2690 
2691 OM_uint32
2692 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2693                            gss_ctx_id_t context_handle,
2694                            int conf_req_flag,
2695                            gss_qop_t qop_req,
2696                            int *conf_state,
2697                            gss_iov_buffer_desc *iov,
2698                            int iov_count)
2699 {
2700         OM_uint32 ret;
2701         ret = gss_wrap_iov_length(minor_status,
2702                                   context_handle,
2703                                   conf_req_flag,
2704                                   qop_req,
2705                                   conf_state,
2706                                   iov,
2707                                   iov_count);
2708         return (ret);
2709 }
2710 
2711 
2712 OM_uint32
2713 spnego_gss_complete_auth_token(
2714                 OM_uint32 *minor_status,
2715                 const gss_ctx_id_t context_handle,
2716                 gss_buffer_t input_message_buffer)
2717 {
2718         OM_uint32 ret;
2719         ret = gss_complete_auth_token(minor_status,
2720                                       context_handle,
2721                                       input_message_buffer);
2722         return (ret);
2723 }
2724 #endif /* 0 */
2725 
2726 /*
2727  * We will release everything but the ctx_handle so that it
2728  * can be passed back to init/accept context. This routine should
2729  * not be called until after the ctx_handle memory is assigned to
2730  * the supplied context handle from init/accept context.
2731  */
2732 static void
2733 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2734 {
2735         spnego_gss_ctx_id_t context;
2736         OM_uint32 minor_stat;
2737         context = *ctx;
2738 
2739         if (context != NULL) {
2740                 (void) gss_release_buffer(&minor_stat,
2741                                         &context->DER_mechTypes);
2742 
2743                 (void) generic_gss_release_oid(&minor_stat,
2744                                 &context->internal_mech);
2745 
2746                 if (context->optionStr != NULL) {
2747                         free(context->optionStr);
2748                         context->optionStr = NULL;
2749                 }
2750                 free(context);
2751                 *ctx = NULL;
2752         }
2753 }
2754 
2755 /*
2756  * Can't use gss_indicate_mechs by itself to get available mechs for
2757  * SPNEGO because it will also return the SPNEGO mech and we do not
2758  * want to consider SPNEGO as an available security mech for
2759  * negotiation. For this reason, get_available_mechs will return
2760  * all available mechs except SPNEGO.
2761  *
2762  * If a ptr to a creds list is given, this function will attempt
2763  * to acquire creds for the creds given and trim the list of
2764  * returned mechanisms to only those for which creds are valid.
2765  *
2766  */
2767 static OM_uint32
2768 get_available_mechs(OM_uint32 *minor_status,
2769         gss_name_t name, gss_cred_usage_t usage,
2770         gss_cred_id_t *creds, gss_OID_set *rmechs)
2771 {
2772         unsigned int    i;
2773         int             found = 0;
2774         OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2775         gss_OID_set mechs, goodmechs;
2776 
2777         major_status = gss_indicate_mechs(minor_status, &mechs);
2778 
2779         if (major_status != GSS_S_COMPLETE) {
2780                 return (major_status);
2781         }
2782 
2783         major_status = gss_create_empty_oid_set(minor_status, rmechs);
2784 
2785         if (major_status != GSS_S_COMPLETE) {
2786                 (void) gss_release_oid_set(minor_status, &mechs);
2787                 return (major_status);
2788         }
2789 
2790         for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2791                 if ((mechs->elements[i].length
2792                     != spnego_mechanism.mech_type.length) ||
2793                     memcmp(mechs->elements[i].elements,
2794                         spnego_mechanism.mech_type.elements,
2795                         spnego_mechanism.mech_type.length)) {
2796                         /*
2797                          * Solaris SPNEGO Kerberos: gss_indicate_mechs is stupid as
2798                          * it never inferences any of the related OIDs of the
2799                          * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
2800                          * We add KRB5_WRONG here so that old MS clients can
2801                          * negotiate this mechanism, which allows extensions
2802                          * in Kerberos (clock skew adjustment, refresh ccache).
2803                          */
2804                         if (is_kerb_mech(&mechs->elements[i])) {
2805                             extern gss_OID_desc * const gss_mech_krb5_wrong;
2806 
2807                                 major_status =
2808                                   gss_add_oid_set_member(minor_status,
2809                                   gss_mech_krb5_wrong, rmechs);
2810                         }
2811 
2812                         major_status = gss_add_oid_set_member(minor_status,
2813                                                               &mechs->elements[i],
2814                                                               rmechs);
2815                         if (major_status == GSS_S_COMPLETE)
2816                                 found++;
2817                 }
2818         }
2819 
2820         /*
2821          * If the caller wanted a list of creds returned,
2822          * trim the list of mechanisms down to only those
2823          * for which the creds are valid.
2824          */
2825         if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2826                 major_status = gss_acquire_cred(minor_status,
2827                                                 name, GSS_C_INDEFINITE, 
2828                                                 *rmechs, usage, creds,
2829                                                 &goodmechs, NULL);
2830 
2831                 /*
2832                  * Drop the old list in favor of the new
2833                  * "trimmed" list.
2834                  */
2835                 (void) gss_release_oid_set(&tmpmin, rmechs);
2836                 if (major_status == GSS_S_COMPLETE) {
2837                         (void) gssint_copy_oid_set(&tmpmin,
2838                                         goodmechs, rmechs);
2839                         (void) gss_release_oid_set(&tmpmin, &goodmechs);
2840                 }
2841         }
2842 
2843         (void) gss_release_oid_set(&tmpmin, &mechs);
2844         if (found == 0 || major_status != GSS_S_COMPLETE) {
2845                 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2846                 map_errcode(minor_status);
2847                 if (major_status == GSS_S_COMPLETE)
2848                         major_status = GSS_S_FAILURE;
2849         }
2850 
2851         return (major_status);
2852 }
2853 
2854 /* following are token creation and reading routines */
2855 
2856 /*
2857  * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2858  * advance the buffer, otherwise, decode the mech_oid from the buffer and
2859  * place in gss_OID.
2860  */
2861 static gss_OID
2862 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2863 {
2864         OM_uint32       status;
2865         gss_OID_desc    toid;
2866         gss_OID         mech_out = NULL;
2867         unsigned char           *start, *end;
2868 
2869         if (length < 1 || **buff_in != MECH_OID)
2870                 return (NULL);
2871 
2872         start = *buff_in;
2873         end = start + length;
2874 
2875         (*buff_in)++;
2876         toid.length = *(*buff_in)++;
2877 
2878         if ((*buff_in + toid.length) > end)
2879                 return (NULL);
2880 
2881         toid.elements = *buff_in;
2882         *buff_in += toid.length;
2883 
2884         status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
2885 
2886         if (status != GSS_S_COMPLETE) {
2887                 map_errcode(minor_status);
2888                 mech_out = NULL;
2889         }
2890 
2891         return (mech_out);
2892 }
2893 
2894 /*
2895  * der encode the given mechanism oid into buf_out, advancing the
2896  * buffer pointer.
2897  */
2898 
2899 static int
2900 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
2901 {
2902         if (buflen < mech->length + 2)
2903                 return (-1);
2904         *(*buf_out)++ = MECH_OID;
2905         *(*buf_out)++ = (unsigned char) mech->length;
2906         memcpy((void *)(*buf_out), mech->elements, mech->length);
2907         *buf_out += mech->length;
2908         return (0);
2909 }
2910 
2911 /*
2912  * verify that buff_in points to an octet string, if it does not,
2913  * return NULL and don't advance the pointer. If it is an octet string
2914  * decode buff_in into a gss_buffer_t and return it, advancing the
2915  * buffer pointer.
2916  */
2917 static gss_buffer_t
2918 get_input_token(unsigned char **buff_in, unsigned int buff_length)
2919 {
2920         gss_buffer_t input_token;
2921         unsigned int bytes;
2922 
2923         if (**buff_in != OCTET_STRING)
2924                 return (NULL);
2925 
2926         (*buff_in)++;
2927         input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
2928 
2929         if (input_token == NULL)
2930                 return (NULL);
2931 
2932         input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes);
2933         if ((int)input_token->length == -1) {
2934                 free(input_token);
2935                 return (NULL);
2936         }
2937         input_token->value = malloc(input_token->length);
2938 
2939         if (input_token->value == NULL) {
2940                 free(input_token);
2941                 return (NULL);
2942         }
2943 
2944         (void) memcpy(input_token->value, *buff_in, input_token->length);
2945         *buff_in += input_token->length;
2946         return (input_token);
2947 }
2948 
2949 /*
2950  * verify that the input token length is not 0. If it is, just return.
2951  * If the token length is greater than 0, der encode as an octet string
2952  * and place in buf_out, advancing buf_out.
2953  */
2954 
2955 static int
2956 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
2957                 unsigned int buflen)
2958 {
2959         int ret;
2960 
2961         /* if token length is 0, we do not want to send */
2962         if (input_token->length == 0)
2963                 return (0);
2964 
2965         if (input_token->length > buflen)
2966                 return (-1);
2967 
2968         *(*buf_out)++ = OCTET_STRING;
2969         if ((ret = gssint_put_der_length(input_token->length, buf_out,
2970                             input_token->length)))
2971                 return (ret);
2972         TWRITE_STR(*buf_out, input_token->value, input_token->length);
2973         return (0);
2974 }
2975 
2976 /*
2977  * verify that buff_in points to a sequence of der encoding. The mech
2978  * set is the only sequence of encoded object in the token, so if it is
2979  * a sequence of encoding, decode the mechset into a gss_OID_set and
2980  * return it, advancing the buffer pointer.
2981  */
2982 static gss_OID_set
2983 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
2984              unsigned int buff_length)
2985 {
2986         gss_OID_set returned_mechSet;
2987         OM_uint32 major_status;
2988         int length; /* SUNW17PACresync */
2989         OM_uint32 bytes;
2990         OM_uint32 set_length;
2991         unsigned char           *start;
2992         int i;
2993 
2994         if (**buff_in != SEQUENCE_OF)
2995                 return (NULL);
2996 
2997         start = *buff_in;
2998         (*buff_in)++;
2999 
3000         length = gssint_get_der_length(buff_in, buff_length, &bytes);
3001         if (length < 0) /* SUNW17PACresync - MIT17 lacks this check */
3002                 return (NULL);
3003 
3004         major_status = gss_create_empty_oid_set(minor_status,
3005                                                 &returned_mechSet);
3006         if (major_status != GSS_S_COMPLETE)
3007                 return (NULL);
3008 
3009         for (set_length = 0, i = 0; set_length < length; i++) {
3010                 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
3011                         buff_length - (*buff_in - start));
3012                 if (temp != NULL) {
3013                     major_status = gss_add_oid_set_member(minor_status,
3014                                         temp, &returned_mechSet);
3015                     if (major_status == GSS_S_COMPLETE) {
3016                         set_length += returned_mechSet->elements[i].length +2;
3017                         if (generic_gss_release_oid(minor_status, &temp))
3018                             map_errcode(minor_status);
3019                     }
3020                 }
3021         }
3022 
3023         return (returned_mechSet);
3024 }
3025 
3026 /*
3027  * Encode mechSet into buf.
3028  */
3029 static int
3030 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
3031 {
3032         unsigned char *ptr;
3033         unsigned int i;
3034         unsigned int tlen, ilen;
3035 
3036         tlen = ilen = 0;
3037         for (i = 0; i < mechSet->count; i++) {
3038                 /*
3039                  * 0x06 [DER LEN] [OID]
3040                  */
3041                 ilen += 1 +
3042                         gssint_der_length_size(mechSet->elements[i].length) +
3043                         mechSet->elements[i].length;
3044         }
3045         /*
3046          * 0x30 [DER LEN]
3047          */
3048         tlen = 1 + gssint_der_length_size(ilen) + ilen;
3049         ptr = malloc(tlen);
3050         if (ptr == NULL)
3051                 return -1;
3052 
3053         buf->value = ptr;
3054         buf->length = tlen;
3055 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
3056 
3057         *ptr++ = SEQUENCE_OF;
3058         if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
3059                 return -1;
3060         for (i = 0; i < mechSet->count; i++) {
3061                 if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
3062                         return -1;
3063                 }
3064         }
3065         return 0;
3066 #undef REMAIN
3067 }
3068 
3069 /*
3070  * Verify that buff_in is pointing to a BIT_STRING with the correct
3071  * length and padding for the req_flags. If it is, decode req_flags
3072  * and return them, otherwise, return NULL.
3073  */
3074 static OM_uint32
3075 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
3076               OM_uint32 *req_flags)
3077 {
3078         unsigned int len;
3079 
3080         if (**buff_in != (CONTEXT | 0x01))
3081                 return (0);
3082 
3083         if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
3084                                 bodysize, &len) < 0)
3085                 return GSS_S_DEFECTIVE_TOKEN;
3086 
3087         if (*(*buff_in)++ != BIT_STRING)
3088                 return GSS_S_DEFECTIVE_TOKEN;
3089 
3090         if (*(*buff_in)++ != BIT_STRING_LENGTH)
3091                 return GSS_S_DEFECTIVE_TOKEN;
3092 
3093         if (*(*buff_in)++ != BIT_STRING_PADDING)
3094                 return GSS_S_DEFECTIVE_TOKEN;
3095 
3096         *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
3097         return (0);
3098 }
3099 
3100 static OM_uint32
3101 get_negTokenInit(OM_uint32 *minor_status,
3102                  gss_buffer_t buf,
3103                  gss_buffer_t der_mechSet,
3104                  gss_OID_set *mechSet,
3105                  OM_uint32 *req_flags,
3106                  gss_buffer_t *mechtok,
3107                  gss_buffer_t *mechListMIC)
3108 {
3109         OM_uint32 err;
3110         unsigned char *ptr, *bufstart;
3111         unsigned int len;
3112         gss_buffer_desc tmpbuf;
3113 
3114         *minor_status = 0;
3115         der_mechSet->length = 0;
3116         der_mechSet->value = NULL;
3117         *mechSet = GSS_C_NO_OID_SET;
3118         *req_flags = 0;
3119         *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3120 
3121         ptr = bufstart = buf->value;
3122         if ((buf->length - (ptr - bufstart)) > INT_MAX)
3123                 return GSS_S_FAILURE;
3124 #define REMAIN (buf->length - (ptr - bufstart))
3125 
3126         err = g_verify_token_header(gss_mech_spnego,
3127                                     &len, &ptr, 0, REMAIN);
3128         if (err) {
3129                 *minor_status = err;
3130                 map_errcode(minor_status);
3131                 return GSS_S_FAILURE;
3132         }
3133         *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
3134         if (*minor_status) {
3135                 map_errcode(minor_status);
3136                 return GSS_S_FAILURE;
3137         }
3138 
3139         /* alias into input_token */
3140         tmpbuf.value = ptr;
3141         tmpbuf.length = REMAIN;
3142         *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
3143         if (*mechSet == NULL)
3144                 return GSS_S_FAILURE;
3145 
3146         tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
3147         der_mechSet->value = malloc(tmpbuf.length);
3148         if (der_mechSet->value == NULL)
3149                 return GSS_S_FAILURE;
3150         memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
3151         der_mechSet->length = tmpbuf.length;
3152 
3153         err = get_req_flags(&ptr, REMAIN, req_flags);
3154         if (err != GSS_S_COMPLETE) {
3155                 return err;
3156         }
3157         if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
3158                                  REMAIN, &len) >= 0) {
3159                 *mechtok = get_input_token(&ptr, len);
3160                 if (*mechtok == GSS_C_NO_BUFFER) {
3161                         return GSS_S_FAILURE;
3162                 }
3163         }
3164         if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
3165                                  REMAIN, &len) >= 0) {
3166                 *mechListMIC = get_input_token(&ptr, len);
3167                 if (*mechListMIC == GSS_C_NO_BUFFER) {
3168                         return GSS_S_FAILURE;
3169                 }
3170         }
3171         return GSS_S_COMPLETE;
3172 #undef REMAIN
3173 }
3174 
3175 static OM_uint32
3176 get_negTokenResp(OM_uint32 *minor_status,
3177                  unsigned char *buf, unsigned int buflen,
3178                  OM_uint32 *negState,
3179                  gss_OID *supportedMech,
3180                  gss_buffer_t *responseToken,
3181                  gss_buffer_t *mechListMIC)
3182 {
3183         unsigned char *ptr, *bufstart;
3184         unsigned int len;
3185         int tmplen;
3186         unsigned int tag, bytes;
3187 
3188         *negState = ACCEPT_DEFECTIVE_TOKEN;
3189         *supportedMech = GSS_C_NO_OID;
3190         *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3191         ptr = bufstart = buf;
3192 #define REMAIN (buflen - (ptr - bufstart))
3193 
3194         if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
3195                 return GSS_S_DEFECTIVE_TOKEN;
3196         if (*ptr++ == SEQUENCE) {
3197                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3198                 if (tmplen < 0)
3199                         return GSS_S_DEFECTIVE_TOKEN;
3200         }
3201         if (REMAIN < 1)
3202                 tag = 0;
3203         else
3204                 tag = *ptr++;
3205 
3206         if (tag == CONTEXT) {
3207                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3208                 if (tmplen < 0)
3209                         return GSS_S_DEFECTIVE_TOKEN;
3210 
3211                 if (g_get_tag_and_length(&ptr, ENUMERATED,
3212                                          REMAIN, &len) < 0)
3213                         return GSS_S_DEFECTIVE_TOKEN;
3214 
3215                 if (len != ENUMERATION_LENGTH)
3216                         return GSS_S_DEFECTIVE_TOKEN;
3217 
3218                 if (REMAIN < 1)
3219                         return GSS_S_DEFECTIVE_TOKEN;
3220                 *negState = *ptr++;
3221 
3222                 if (REMAIN < 1)
3223                         tag = 0;
3224                 else
3225                         tag = *ptr++;
3226         }
3227         if (tag == (CONTEXT | 0x01)) {
3228                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3229                 if (tmplen < 0)
3230                         return GSS_S_DEFECTIVE_TOKEN;
3231 
3232                 *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3233                 if (*supportedMech == GSS_C_NO_OID)
3234                         return GSS_S_DEFECTIVE_TOKEN;
3235 
3236                 if (REMAIN < 1)
3237                         tag = 0;
3238                 else
3239                         tag = *ptr++;
3240         }
3241         if (tag == (CONTEXT | 0x02)) {
3242                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3243                 if (tmplen < 0)
3244                         return GSS_S_DEFECTIVE_TOKEN;
3245 
3246                 *responseToken = get_input_token(&ptr, REMAIN);
3247                 if (*responseToken == GSS_C_NO_BUFFER)
3248                         return GSS_S_DEFECTIVE_TOKEN;
3249 
3250                 if (REMAIN < 1)
3251                         tag = 0;
3252                 else
3253                         tag = *ptr++;
3254         }
3255         if (tag == (CONTEXT | 0x03)) {
3256                 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3257                 if (tmplen < 0)
3258                         return GSS_S_DEFECTIVE_TOKEN;
3259 
3260                 *mechListMIC = get_input_token(&ptr, REMAIN);
3261                 if (*mechListMIC == GSS_C_NO_BUFFER)
3262                         return GSS_S_DEFECTIVE_TOKEN;
3263         }
3264         return GSS_S_COMPLETE;
3265 #undef REMAIN
3266 }
3267 
3268 /*
3269  * der encode the passed negResults as an ENUMERATED type and
3270  * place it in buf_out, advancing the buffer.
3271  */
3272 
3273 static int
3274 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3275               unsigned int buflen)
3276 {
3277         if (buflen < 3)
3278                 return (-1);
3279         *(*buf_out)++ = ENUMERATED;
3280         *(*buf_out)++ = ENUMERATION_LENGTH;
3281         *(*buf_out)++ = (unsigned char) negResult;
3282         return (0);
3283 }
3284 
3285 /*
3286  * This routine compares the recieved mechset to the mechset that
3287  * this server can support. It looks sequentially through the mechset
3288  * and the first one that matches what the server can support is
3289  * chosen as the negotiated mechanism. If one is found, negResult
3290  * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3291  * it's not the first mech, otherwise we return NULL and negResult
3292  * is set to REJECT.
3293  *
3294  * NOTE: There is currently no way to specify a preference order of
3295  * mechanisms supported by the acceptor.
3296  */
3297 static gss_OID
3298 negotiate_mech_type(OM_uint32 *minor_status,
3299                     gss_OID_set supported_mechSet,
3300                     gss_OID_set mechset,
3301                     OM_uint32 *negResult)
3302 {
3303         gss_OID returned_mech;
3304         OM_uint32 status;
3305         int present;
3306         unsigned int i;
3307 
3308         for (i = 0; i < mechset->count; i++) {
3309                 gss_OID mech_oid = &mechset->elements[i];
3310 
3311                 /*
3312                  * Solaris SPNEGO Kerberos: MIT compares against MS' wrong OID, but
3313                  * we actually want to select it if the client supports, as this
3314                  * will enable features on MS clients that allow credential
3315                  * refresh on rekeying and caching system times from servers.
3316                  */ 
3317 #if 0
3318                 /* Accept wrong mechanism OID from MS clients */
3319                 if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
3320                     memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
3321                         mech_oid = (gss_OID)&gss_mech_krb5_oid;
3322 #endif
3323 
3324                 gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
3325                 if (!present)
3326                         continue;
3327 
3328                 if (i == 0)
3329                         *negResult = ACCEPT_INCOMPLETE;
3330                 else
3331                         *negResult = REQUEST_MIC;
3332 
3333                 status = generic_gss_copy_oid(minor_status,
3334                                               &mechset->elements[i],
3335                                               &returned_mech);
3336                 if (status != GSS_S_COMPLETE) {
3337                         *negResult = REJECT;
3338                         map_errcode(minor_status);
3339                         return (NULL);
3340                 }
3341                 return (returned_mech);
3342         }
3343         /* Solaris SPNEGO */
3344         *minor_status= ERR_SPNEGO_NEGOTIATION_FAILED;
3345 
3346         *negResult = REJECT;
3347         return (NULL);
3348 }
3349 
3350 /*
3351  * the next two routines make a token buffer suitable for
3352  * spnego_gss_display_status. These currently take the string
3353  * in name and place it in the token. Eventually, if
3354  * spnego_gss_display_status returns valid error messages,
3355  * these routines will be changes to return the error string.
3356  */
3357 static spnego_token_t
3358 make_spnego_token(char *name)
3359 {
3360         return (spnego_token_t)strdup(name);
3361 }
3362 
3363 static gss_buffer_desc
3364 make_err_msg(char *name)
3365 {
3366         gss_buffer_desc buffer;
3367 
3368         if (name == NULL) {
3369                 buffer.length = 0;
3370                 buffer.value = NULL;
3371         } else {
3372                 buffer.length = strlen(name)+1;
3373                 buffer.value = make_spnego_token(name);
3374         }
3375 
3376         return (buffer);
3377 }
3378 
3379 /*
3380  * Create the client side spnego token passed back to gss_init_sec_context
3381  * and eventually up to the application program and over to the server.
3382  *
3383  * Use DER rules, definite length method per RFC 2478
3384  */
3385 static int
3386 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3387                           int negHintsCompat,
3388                           gss_buffer_t mechListMIC, OM_uint32 req_flags,
3389                           gss_buffer_t data, send_token_flag sendtoken,
3390                           gss_buffer_t outbuf)
3391 {
3392         int ret = 0;
3393         unsigned int tlen, dataLen = 0;
3394         unsigned int negTokenInitSize = 0;
3395         unsigned int negTokenInitSeqSize = 0;
3396         unsigned int negTokenInitContSize = 0;
3397         unsigned int rspTokenSize = 0;
3398         unsigned int mechListTokenSize = 0;
3399         unsigned int micTokenSize = 0;
3400         unsigned char *t;
3401         unsigned char *ptr;
3402 
3403         if (outbuf == GSS_C_NO_BUFFER)
3404                 return (-1);
3405 
3406         outbuf->length = 0;
3407         outbuf->value = NULL;
3408 
3409         /* calculate the data length */
3410 
3411         /*
3412          * 0xa0 [DER LEN] [mechTypes]
3413          */
3414         mechListTokenSize = 1 +
3415                 gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3416                 spnego_ctx->DER_mechTypes.length;
3417         dataLen += mechListTokenSize;
3418 
3419         /*
3420          * If a token from gss_init_sec_context exists,
3421          * add the length of the token + the ASN.1 overhead
3422          */
3423         if (data != NULL) {
3424                 /*
3425                  * Encoded in final output as:
3426                  * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3427                  * -----s--------|--------s2----------
3428                  */
3429                 rspTokenSize = 1 +
3430                         gssint_der_length_size(data->length) +
3431                         data->length;
3432                 dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3433                         rspTokenSize;
3434         }
3435 
3436         if (mechListMIC) {
3437                 /*
3438                  * Encoded in final output as:
3439                  * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3440                  *      --s--     -----tlen------------
3441                  */
3442                 micTokenSize = 1 +
3443                         gssint_der_length_size(mechListMIC->length) +
3444                         mechListMIC->length;
3445                 dataLen += 1 +
3446                         gssint_der_length_size(micTokenSize) +
3447                         micTokenSize;
3448         }
3449 
3450         /*
3451          * Add size of DER encoding
3452          * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3453          *   0x30 [DER_LEN] [data]
3454          *
3455          */
3456         negTokenInitContSize = dataLen;
3457         negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3458         dataLen = negTokenInitSeqSize;
3459 
3460         /*
3461          * negTokenInitSize indicates the bytes needed to
3462          * hold the ASN.1 encoding of the entire NegTokenInit
3463          * SEQUENCE.
3464          * 0xa0 [DER_LEN] + data
3465          *
3466          */
3467         negTokenInitSize = 1 +
3468                 gssint_der_length_size(negTokenInitSeqSize) +
3469                 negTokenInitSeqSize;
3470 
3471         tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3472 
3473         t = (unsigned char *) malloc(tlen);
3474 
3475         if (t == NULL) {
3476                 return (-1);
3477         }
3478 
3479         ptr = t;
3480 
3481         /* create the message */
3482         if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3483                             &ptr, tlen)))
3484                 goto errout;
3485 
3486         *ptr++ = CONTEXT; /* NegotiationToken identifier */
3487         if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3488                 goto errout;
3489 
3490         *ptr++ = SEQUENCE;
3491         if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3492                                          tlen - (int)(ptr-t))))
3493                 goto errout;
3494 
3495         *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3496         if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3497                                          &ptr, tlen - (int)(ptr-t))))
3498                 goto errout;
3499 
3500         /* We already encoded the MechSetList */
3501         (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3502                       spnego_ctx->DER_mechTypes.length);
3503 
3504         ptr += spnego_ctx->DER_mechTypes.length;
3505 
3506         if (data != NULL) {
3507                 *ptr++ = CONTEXT | 0x02;
3508                 if ((ret = gssint_put_der_length(rspTokenSize,
3509                                 &ptr, tlen - (int)(ptr - t))))
3510                         goto errout;
3511 
3512                 if ((ret = put_input_token(&ptr, data,
3513                         tlen - (int)(ptr - t))))
3514                         goto errout;
3515         }
3516 
3517         if (mechListMIC != GSS_C_NO_BUFFER) {
3518                 *ptr++ = CONTEXT | 0x03;
3519                 if ((ret = gssint_put_der_length(micTokenSize,
3520                                 &ptr, tlen - (int)(ptr - t))))
3521                         goto errout;
3522 
3523                 if (negHintsCompat) {
3524                         ret = put_neg_hints(&ptr, mechListMIC,
3525                                             tlen - (int)(ptr - t));
3526                         if (ret)
3527                                 goto errout;
3528                 } else if ((ret = put_input_token(&ptr, mechListMIC,
3529                                 tlen - (int)(ptr - t))))
3530                         goto errout;
3531         }
3532 
3533 errout:
3534         if (ret != 0) {
3535                 if (t)
3536                         free(t);
3537                 t = NULL;
3538                 tlen = 0;
3539         }
3540         outbuf->length = tlen;
3541         outbuf->value = (void *) t;
3542 
3543         return (ret);
3544 }
3545 
3546 /*
3547  * create the server side spnego token passed back to
3548  * gss_accept_sec_context and eventually up to the application program
3549  * and over to the client.
3550  */
3551 static int
3552 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3553                           gss_buffer_t data, gss_buffer_t mechListMIC,
3554                           send_token_flag sendtoken,
3555                           gss_buffer_t outbuf)
3556 {
3557         unsigned int tlen = 0;
3558         unsigned int ret = 0;
3559         unsigned int NegTokenTargSize = 0;
3560         unsigned int NegTokenSize = 0;
3561         unsigned int rspTokenSize = 0;
3562         unsigned int micTokenSize = 0;
3563         unsigned int dataLen = 0;
3564         unsigned char *t;
3565         unsigned char *ptr;
3566 
3567         if (outbuf == GSS_C_NO_BUFFER)
3568                 return (GSS_S_DEFECTIVE_TOKEN);
3569 
3570         outbuf->length = 0;
3571         outbuf->value = NULL;
3572 
3573         /*
3574          * ASN.1 encoding of the negResult
3575          * ENUMERATED type is 3 bytes
3576          *  ENUMERATED TAG, Length, Value,
3577          * Plus 2 bytes for the CONTEXT id and length.
3578          */
3579         dataLen = 5;
3580 
3581         /*
3582          * calculate data length
3583          *
3584          * If this is the initial token, include length of
3585          * mech_type and the negotiation result fields.
3586          */
3587         if (sendtoken == INIT_TOKEN_SEND) {
3588                 int mechlistTokenSize;
3589                 /*
3590                  * 1 byte for the CONTEXT ID(0xa0),
3591                  * 1 byte for the OID ID(0x06)
3592                  * 1 byte for OID Length field
3593                  * Plus the rest... (OID Length, OID value)
3594                  */
3595                 mechlistTokenSize = 3 + mech_wanted->length +
3596                         gssint_der_length_size(mech_wanted->length);
3597 
3598                 dataLen += mechlistTokenSize;
3599         }
3600         if (data != NULL && data->length > 0) {
3601                 /* Length of the inner token */
3602                 rspTokenSize = 1 + gssint_der_length_size(data->length) +
3603                         data->length;
3604 
3605                 dataLen += rspTokenSize;
3606 
3607                 /* Length of the outer token */
3608                 dataLen += 1 + gssint_der_length_size(rspTokenSize);
3609         }
3610         if (mechListMIC != NULL) {
3611 
3612                 /* Length of the inner token */
3613                 micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3614                         mechListMIC->length;
3615 
3616                 dataLen += micTokenSize;
3617 
3618                 /* Length of the outer token */
3619                 dataLen += 1 + gssint_der_length_size(micTokenSize);
3620         }
3621         /*
3622          * Add size of DER encoded:
3623          * NegTokenTarg [ SEQUENCE ] of
3624          *    NegResult[0] ENUMERATED {
3625          *      accept_completed(0),
3626          *      accept_incomplete(1),
3627          *      reject(2) }
3628          *    supportedMech [1] MechType OPTIONAL,
3629          *    responseToken [2] OCTET STRING OPTIONAL,
3630          *    mechListMIC   [3] OCTET STRING OPTIONAL
3631          *
3632          * size = data->length + MechListMic + SupportedMech len +
3633          *      Result Length + ASN.1 overhead
3634          */
3635         NegTokenTargSize = dataLen;
3636         dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3637 
3638         /*
3639          * NegotiationToken [ CHOICE ]{
3640          *    negTokenInit  [0]  NegTokenInit,
3641          *    negTokenTarg  [1]  NegTokenTarg }
3642          */
3643         NegTokenSize = dataLen;
3644         dataLen += 1 + gssint_der_length_size(NegTokenSize);
3645 
3646         tlen = dataLen;
3647         t = (unsigned char *) malloc(tlen);
3648 
3649         if (t == NULL) {
3650                 ret = GSS_S_DEFECTIVE_TOKEN;
3651                 goto errout;
3652         }
3653 
3654         ptr = t;
3655 
3656         /*
3657          * Indicate that we are sending CHOICE 1
3658          * (NegTokenTarg)
3659          */
3660         *ptr++ = CONTEXT | 0x01;
3661         if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3662                 ret = GSS_S_DEFECTIVE_TOKEN;
3663                 goto errout;
3664         }
3665         *ptr++ = SEQUENCE;
3666         if (gssint_put_der_length(NegTokenTargSize, &ptr,
3667                                   tlen - (int)(ptr-t)) < 0) {
3668                 ret = GSS_S_DEFECTIVE_TOKEN;
3669                 goto errout;
3670         }
3671 
3672         /*
3673          * First field of the NegTokenTarg SEQUENCE
3674          * is the ENUMERATED NegResult.
3675          */
3676         *ptr++ = CONTEXT;
3677         if (gssint_put_der_length(3, &ptr,
3678                                   tlen - (int)(ptr-t)) < 0) {
3679                 ret = GSS_S_DEFECTIVE_TOKEN;
3680                 goto errout;
3681         }
3682         if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3683                 ret = GSS_S_DEFECTIVE_TOKEN;
3684                 goto errout;
3685         }
3686         if (sendtoken == INIT_TOKEN_SEND) {
3687                 /*
3688                  * Next, is the Supported MechType
3689                  */
3690                 *ptr++ = CONTEXT | 0x01;
3691                 if (gssint_put_der_length(mech_wanted->length + 2,
3692                                           &ptr,
3693                                           tlen - (int)(ptr - t)) < 0) {
3694                         ret = GSS_S_DEFECTIVE_TOKEN;
3695                         goto errout;
3696                 }
3697                 if (put_mech_oid(&ptr, mech_wanted,
3698                                  tlen - (int)(ptr - t)) < 0) {
3699                         ret = GSS_S_DEFECTIVE_TOKEN;
3700                         goto errout;
3701                 }
3702         }
3703         if (data != NULL && data->length > 0) {
3704                 *ptr++ = CONTEXT | 0x02;
3705                 if (gssint_put_der_length(rspTokenSize, &ptr,
3706                                           tlen - (int)(ptr - t)) < 0) {
3707                         ret = GSS_S_DEFECTIVE_TOKEN;
3708                         goto errout;
3709                 }
3710                 if (put_input_token(&ptr, data,
3711                                     tlen - (int)(ptr - t)) < 0) {
3712                         ret = GSS_S_DEFECTIVE_TOKEN;
3713                         goto errout;
3714                 }
3715         }
3716         if (mechListMIC != NULL) {
3717                 *ptr++ = CONTEXT | 0x03;
3718                 if (gssint_put_der_length(micTokenSize, &ptr,
3719                                           tlen - (int)(ptr - t)) < 0) {
3720                         ret = GSS_S_DEFECTIVE_TOKEN;
3721                         goto errout;
3722                 }
3723                 if (put_input_token(&ptr, mechListMIC,
3724                                     tlen - (int)(ptr - t)) < 0) {
3725                         ret = GSS_S_DEFECTIVE_TOKEN;
3726                         goto errout;
3727                 }
3728         }
3729         ret = GSS_S_COMPLETE;
3730 errout:
3731         if (ret != GSS_S_COMPLETE) {
3732                 if (t)
3733                         free(t);
3734         } else {
3735                 outbuf->length = ptr - t;
3736                 outbuf->value = (void *) t;
3737         }
3738 
3739         return (ret);
3740 }
3741 
3742 /* determine size of token */
3743 static int
3744 g_token_size(gss_OID_const mech, unsigned int body_size)
3745 {
3746         int hdrsize;
3747 
3748         /*
3749          * Initialize the header size to the
3750          * MECH_OID byte + the bytes needed to indicate the
3751          * length of the OID + the OID itself.
3752          *
3753          * 0x06 [MECHLENFIELD] MECHDATA
3754          */
3755         hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3756 
3757         /*
3758          * Now add the bytes needed for the initial header
3759          * token bytes:
3760          * 0x60 + [DER_LEN] + HDRSIZE
3761          */
3762         hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3763 
3764         return (hdrsize + body_size);
3765 }
3766 
3767 /*
3768  * generate token header.
3769  *
3770  * Use DER Definite Length method per RFC2478
3771  * Use of indefinite length encoding will not be compatible
3772  * with Microsoft or others that actually follow the spec.
3773  */
3774 static int
3775 g_make_token_header(gss_OID_const mech,
3776                     unsigned int body_size,
3777                     unsigned char **buf,
3778                     unsigned int totallen)
3779 {
3780         int ret = 0;
3781         unsigned int hdrsize;
3782         unsigned char *p = *buf;
3783 
3784         hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3785 
3786         *(*buf)++ = HEADER_ID;
3787         if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3788                 return (ret);
3789 
3790         *(*buf)++ = MECH_OID;
3791         if ((ret = gssint_put_der_length(mech->length, buf,
3792                             totallen - (int)(p - *buf))))
3793                 return (ret);
3794         TWRITE_STR(*buf, mech->elements, mech->length);
3795         return (0);
3796 }
3797 
3798 /*
3799  * NOTE: This checks that the length returned by
3800  * gssint_get_der_length() is not greater than the number of octets
3801  * remaining, even though gssint_get_der_length() already checks, in
3802  * theory.
3803  */
3804 static int
3805 g_get_tag_and_length(unsigned char **buf, int tag,
3806                      unsigned int buflen, unsigned int *outlen)
3807 {
3808         unsigned char *ptr = *buf;
3809         int ret = -1; /* pessimists, assume failure ! */
3810         unsigned int encoded_len;
3811         unsigned int tmplen = 0;
3812 
3813         *outlen = 0;
3814         if (buflen > 1 && *ptr == tag) {
3815                 ptr++;
3816                 tmplen = gssint_get_der_length(&ptr, buflen - 1,
3817                                                 &encoded_len);
3818                 if (tmplen < 0) {
3819                         ret = -1;
3820                 } else if (tmplen > buflen - (ptr - *buf)) {
3821                         ret = -1;
3822                 } else
3823                         ret = 0;
3824         }
3825         *outlen = tmplen;
3826         *buf = ptr;
3827         return (ret);
3828 }
3829 
3830 static int
3831 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
3832 {
3833         unsigned char *buf = *buf_in;
3834         unsigned char *endptr = buf + cur_size;
3835         unsigned int seqsize;
3836         int ret = 0;
3837         unsigned int bytes;
3838 
3839         /*
3840          * Verify this is a NegotiationToken type token
3841          * - check for a0(context specific identifier)
3842          * - get length and verify that enoughd ata exists
3843          */
3844         if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
3845                 return (G_BAD_TOK_HEADER);
3846 
3847         cur_size = seqsize; /* should indicate bytes remaining */
3848 
3849         /*
3850          * Verify the next piece, it should identify this as
3851          * a strucure of type NegTokenInit.
3852          */
3853         if (*buf++ == SEQUENCE) {
3854                 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3855                         return (G_BAD_TOK_HEADER);
3856                 /*
3857                  * Make sure we have the entire buffer as described
3858                  */
3859                 if (buf + seqsize > endptr)
3860                         return (G_BAD_TOK_HEADER);
3861         } else {
3862                 return (G_BAD_TOK_HEADER);
3863         }
3864 
3865         cur_size = seqsize; /* should indicate bytes remaining */
3866 
3867         /*
3868          * Verify that the first blob is a sequence of mechTypes
3869          */
3870         if (*buf++ == CONTEXT) {
3871                 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3872                         return (G_BAD_TOK_HEADER);
3873                 /*
3874                  * Make sure we have the entire buffer as described
3875                  */
3876                 if (buf + bytes > endptr)
3877                         return (G_BAD_TOK_HEADER);
3878         } else {
3879                 return (G_BAD_TOK_HEADER);
3880         }
3881 
3882         /*
3883          * At this point, *buf should be at the beginning of the
3884          * DER encoded list of mech types that are to be negotiated.
3885          */
3886         *buf_in = buf;
3887 
3888         return (ret);
3889 
3890 }
3891 
3892 /* verify token header. */
3893 static int
3894 g_verify_token_header(gss_OID_const mech,
3895                     unsigned int *body_size,
3896                     unsigned char **buf_in,
3897                     int tok_type,
3898                     unsigned int toksize)
3899 {
3900         unsigned char *buf = *buf_in;
3901         int seqsize;
3902         gss_OID_desc toid;
3903         int ret = 0;
3904         unsigned int bytes;
3905 
3906         if (toksize-- < 1)
3907                 return (G_BAD_TOK_HEADER);
3908 
3909         if (*buf++ != HEADER_ID)
3910                 return (G_BAD_TOK_HEADER);
3911 
3912         if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
3913                 return (G_BAD_TOK_HEADER);
3914 
3915         if ((seqsize + bytes) != toksize)
3916                 return (G_BAD_TOK_HEADER);
3917 
3918         if (toksize-- < 1)
3919                 return (G_BAD_TOK_HEADER);
3920 
3921 
3922         if (*buf++ != MECH_OID)
3923                 return (G_BAD_TOK_HEADER);
3924 
3925         if (toksize-- < 1)
3926                 return (G_BAD_TOK_HEADER);
3927 
3928         toid.length = *buf++;
3929 
3930         if (toksize < toid.length)
3931                 return (G_BAD_TOK_HEADER);
3932         else
3933                 toksize -= toid.length;
3934 
3935         toid.elements = buf;
3936         buf += toid.length;
3937 
3938         if (!g_OID_equal(&toid, mech))
3939                 ret = G_WRONG_MECH;
3940 
3941         /*
3942          * G_WRONG_MECH is not returned immediately because it's more important
3943          * to return G_BAD_TOK_HEADER if the token header is in fact bad
3944          */
3945         if (toksize < 2)
3946                 return (G_BAD_TOK_HEADER);
3947         else
3948                 toksize -= 2;
3949 
3950         if (!ret) {
3951                 *buf_in = buf;
3952                 *body_size = toksize;
3953         }
3954 
3955         return (ret);
3956 }
3957 
3958 /*
3959  * Return non-zero if the oid is one of the kerberos mech oids,
3960  * otherwise return zero.
3961  *
3962  * N.B. There are 3 oids that represent the kerberos mech:
3963  * RFC-specified GSS_MECH_KRB5_OID,
3964  * Old pre-RFC   GSS_MECH_KRB5_OLD_OID,
3965  * Incorrect MS  GSS_MECH_KRB5_WRONG_OID
3966  */
3967 
3968 static int
3969 is_kerb_mech(gss_OID oid)
3970 {
3971         int answer = 0;
3972         OM_uint32 minor;
3973         extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3974         
3975         (void) gss_test_oid_set_member(&minor,
3976                 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
3977         
3978         return (answer);
3979 }