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