1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  *
  26  * A module that implements the spnego security mechanism.
  27  * It is used to negotiate the security mechanism between
  28  * peers using the GSS-API.
  29  *
  30  */
  31 
  32 #include        <stdio.h>
  33 #include        <stdlib.h>
  34 #include        <errno.h>
  35 #include        "gssapiP_spnego.h"
  36 #include        <mechglueP.h>
  37 #include        <gssapi_err_generic.h>
  38 #include        <rpc/types.h>
  39 #include        <libintl.h>
  40 
  41 /* der routines defined in libgss */
  42 extern unsigned int der_length_size(OM_uint32);
  43 extern int get_der_length(uchar_t **, OM_uint32, OM_uint32*);
  44 extern int put_der_length(OM_uint32, uchar_t **, OM_uint32);
  45 
  46 
  47 /* private routines for spnego_mechanism */
  48 static spnego_token_t make_spnego_token(char *);
  49 static gss_buffer_desc make_err_msg(char *);
  50 static int g_token_size(gss_OID, OM_uint32);
  51 static int g_make_token_header(gss_OID, int, uchar_t **, int);
  52 static int g_verify_token_header(gss_OID, int *, uchar_t **, int, int);
  53 static int g_verify_neg_token_init(uchar_t **, int);
  54 static OM_uint32 get_negResult(unsigned char **, int);
  55 static gss_OID get_mech_oid(OM_uint32 *, uchar_t **, size_t);
  56 static gss_buffer_t get_input_token(unsigned char **, int);
  57 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, int);
  58 static OM_uint32 get_req_flags(uchar_t **, int *, OM_uint32 *);
  59 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
  60         gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
  61 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
  62 static void check_spnego_options(spnego_gss_ctx_id_t);
  63 static spnego_gss_ctx_id_t create_spnego_ctx(void);
  64 static int put_mech_set(uchar_t **, gss_OID_set, int);
  65 static int put_input_token(uchar_t **, gss_buffer_t, int);
  66 static int put_mech_oid(uchar_t **, gss_OID_desc *, int);
  67 static int put_negResult(uchar_t **, OM_uint32, int);
  68 
  69 static gss_OID
  70 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
  71                 OM_uint32 *, bool_t *);
  72 static int
  73 g_get_tag_and_length(unsigned char **, uchar_t, int, int *);
  74 
  75 static int
  76 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, gss_OID_set,
  77                         gss_buffer_t, send_token_flag,
  78                         gss_buffer_t);
  79 static int
  80 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
  81                         gss_buffer_t, send_token_flag, int,
  82                         gss_buffer_t);
  83 
  84 /*
  85  * The Mech OID for SPNEGO:
  86  * { iso(1) org(3) dod(6) internet(1) security(5)
  87  *  mechanism(5) spnego(2) }
  88  */
  89 static struct gss_config spnego_mechanism =
  90 {{SPNEGO_OID_LENGTH, SPNEGO_OID},
  91         NULL,
  92         spnego_gss_acquire_cred,
  93         spnego_gss_release_cred,
  94         spnego_gss_init_sec_context,
  95         spnego_gss_accept_sec_context,
  96 /* EXPORT DELETE START */ /* CRYPT DELETE START */
  97         spnego_gss_unseal,              /* gss_unseal */
  98 /* EXPORT DELETE END */ /* CRYPT DELETE END */
  99         NULL,                           /* gss_process_context_token */
 100         spnego_gss_delete_sec_context,  /* gss_delete_sec_context */
 101         spnego_gss_context_time,        /* gss_context_time */
 102         spnego_gss_display_status,
 103         NULL,                           /* gss_indicate_mechs */
 104         NULL,                           /* gss_compare_name */
 105         spnego_gss_display_name,
 106         spnego_gss_import_name,
 107         spnego_gss_release_name,
 108         spnego_gss_inquire_cred,        /* gss_inquire_cred */
 109         NULL,                           /* gss_add_cred */
 110 /* EXPORT DELETE START */ /* CRYPT DELETE START */
 111         spnego_gss_seal,                /* gss_seal */
 112 /* EXPORT DELETE END */ /* CRYPT DELETE END */
 113         spnego_gss_export_sec_context,  /* gss_export_sec_context */
 114         spnego_gss_import_sec_context,  /* gss_import_sec_context */
 115         NULL,                           /* gss_inquire_cred_by_mech */
 116         spnego_gss_inquire_names_for_mech,
 117         spnego_gss_inquire_context,     /* gss_inquire_context */
 118         NULL,                           /* gss_internal_release_oid */
 119         spnego_gss_wrap_size_limit,     /* gss_wrap_size_limit */
 120         NULL,                           /* gss_pname_to_uid */
 121         NULL,                           /* __gss_userok */
 122         NULL,                           /* gss_export_name */
 123 /* EXPORT DELETE START */
 124 /* CRYPT DELETE START */
 125 #if 0
 126 /* CRYPT DELETE END */
 127         spnego_gss_seal,
 128         spnego_gss_unseal,
 129 /* CRYPT DELETE START */
 130 #endif
 131 /* CRYPT DELETE END */
 132 /* EXPORT DELETE END */
 133         spnego_gss_sign,                /* gss_sign */
 134         spnego_gss_verify,              /* gss_verify */
 135         NULL,                           /* gss_store_cred */
 136 };
 137 
 138 gss_mechanism
 139 gss_mech_initialize(const gss_OID oid)
 140 {
 141         dsyslog("Entering gss_mech_initialize\n");
 142 
 143         if (oid == NULL ||
 144             !g_OID_equal(oid, &spnego_mechanism.mech_type)) {
 145                 dsyslog("invalid spnego mechanism oid.\n");
 146                 return (NULL);
 147         }
 148 
 149         dsyslog("Leaving gss_mech_initialize\n");
 150         return (&spnego_mechanism);
 151 }
 152 
 153 /*ARGSUSED*/
 154 OM_uint32
 155 spnego_gss_acquire_cred(void *ctx,
 156                         OM_uint32 *minor_status,
 157                         gss_name_t desired_name,
 158                         OM_uint32 time_req,
 159                         gss_OID_set desired_mechs,
 160                         gss_cred_usage_t cred_usage,
 161                         gss_cred_id_t *output_cred_handle,
 162                         gss_OID_set *actual_mechs,
 163                         OM_uint32 *time_rec)
 164 {
 165         OM_uint32 status;
 166         gss_OID_set amechs;
 167         dsyslog("Entering spnego_gss_acquire_cred\n");
 168 
 169         if (actual_mechs)
 170                 *actual_mechs = NULL;
 171 
 172         if (time_rec)
 173                 *time_rec = 0;
 174 
 175         /*
 176          * If the user did not specify a list of mechs,
 177          * use get_available_mechs to collect a list of
 178          * mechs for which creds are available.
 179          */
 180         if (desired_mechs == GSS_C_NULL_OID_SET) {
 181                 status = get_available_mechs(minor_status,
 182                     desired_name, cred_usage,
 183                     output_cred_handle, &amechs);
 184         } else {
 185                 /*
 186                  * The caller gave a specific list of mechanisms,
 187                  * so just get whatever creds are available.
 188                  * gss_acquire_creds will return the subset of mechs for
 189                  * which the given 'output_cred_handle' is valid.
 190                  */
 191                 status = gss_acquire_cred(minor_status,
 192                     desired_name, time_req, desired_mechs, cred_usage,
 193                     output_cred_handle, &amechs, time_rec);
 194         }
 195 
 196         if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
 197                 (void) gss_copy_oid_set(minor_status, amechs, actual_mechs);
 198         }
 199         (void) gss_release_oid_set(minor_status, &amechs);
 200 
 201         dsyslog("Leaving spnego_gss_acquire_cred\n");
 202         return (status);
 203 }
 204 
 205 /*ARGSUSED*/
 206 OM_uint32
 207 spnego_gss_release_cred(void *ctx,
 208                         OM_uint32 *minor_status,
 209                         gss_cred_id_t *cred_handle)
 210 {
 211         OM_uint32 status;
 212 
 213         dsyslog("Entering spnego_gss_release_cred\n");
 214 
 215         if (minor_status == NULL || cred_handle == NULL)
 216                 return (GSS_S_CALL_INACCESSIBLE_WRITE);
 217 
 218         *minor_status = 0;
 219 
 220         if (*cred_handle == GSS_C_NO_CREDENTIAL)
 221                 return (GSS_S_COMPLETE);
 222 
 223         status = gss_release_cred(minor_status, cred_handle);
 224 
 225         dsyslog("Leaving spnego_gss_release_cred\n");
 226         return (status);
 227 }
 228 
 229 static void
 230 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
 231 {
 232         spnego_ctx->optionStr = __gss_get_modOptions(
 233             (const gss_OID)&spnego_oids[0]);
 234         if (spnego_ctx->optionStr != NULL &&
 235             strstr(spnego_ctx->optionStr, "msinterop")) {
 236                 spnego_ctx->MS_Interop = 1;
 237         } else {
 238                 spnego_ctx->MS_Interop = 0;
 239         }
 240 }
 241 
 242 static spnego_gss_ctx_id_t
 243 create_spnego_ctx(void)
 244 {
 245         spnego_gss_ctx_id_t spnego_ctx = NULL;
 246         spnego_ctx = (spnego_gss_ctx_id_t)
 247             malloc(sizeof (spnego_gss_ctx_id_rec));
 248 
 249         if (spnego_ctx == NULL) {
 250                 return (NULL);
 251         }
 252 
 253         spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
 254         spnego_ctx->internal_mech = NULL;
 255         spnego_ctx->optionStr = NULL;
 256         spnego_ctx->optimistic = 0;
 257         spnego_ctx->MS_Interop = 0;
 258         spnego_ctx->DER_mechTypes.length = NULL;
 259         spnego_ctx->DER_mechTypes.value = GSS_C_NO_BUFFER;
 260 
 261         check_spnego_options(spnego_ctx);
 262 
 263         return (spnego_ctx);
 264 }
 265 
 266 /*ARGSUSED*/
 267 OM_uint32
 268 spnego_gss_init_sec_context(void *ct,
 269                         OM_uint32 *minor_status,
 270                         gss_cred_id_t claimant_cred_handle,
 271                         gss_ctx_id_t *context_handle,
 272                         gss_name_t target_name,
 273                         gss_OID mech_type,
 274                         OM_uint32 req_flags,
 275                         OM_uint32 time_req,
 276                         gss_channel_bindings_t input_chan_bindings,
 277                         gss_buffer_t input_token,
 278                         gss_OID *actual_mech,
 279                         gss_buffer_t output_token,
 280                         OM_uint32 *ret_flags,
 281                         OM_uint32 *time_rec)
 282 {
 283         OM_uint32 ret = 0;
 284         OM_uint32 status = 0;
 285         OM_uint32 mstat;
 286         OM_uint32 local_ret_flags = 0;
 287 
 288         /*
 289          * send_token is used to indicate in later steps
 290          * what type of token, if any should be sent or processed.
 291          * NO_TOKEN_SEND = no token should be sent
 292          * INIT_TOKEN_SEND = initial token will be sent
 293          * CONT_TOKEN_SEND = continuing tokens to be sent
 294          * CHECK_MIC = no token to be sent, but have a MIC to check.
 295          */
 296         send_token_flag send_token = NO_TOKEN_SEND;
 297 
 298         gss_OID_set mechSet;
 299         spnego_gss_ctx_id_t spnego_ctx = NULL;
 300         gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
 301         gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
 302         gss_buffer_t mechListMIC = NULL;
 303         gss_cred_id_t *credlistptr = NULL, credlist;
 304         gss_qop_t *qop_state = NULL;
 305         unsigned char *ptr;
 306         int len;
 307 
 308         dsyslog("Entering init_sec_context\n");
 309 
 310         if (context_handle == NULL)
 311                 return (GSS_S_NO_CONTEXT);
 312 
 313         *minor_status = 0;
 314         output_token->length = 0;
 315         output_token->value = NULL;
 316 
 317         if (actual_mech)
 318                 *actual_mech = NULL;
 319 
 320         if (*context_handle == GSS_C_NO_CONTEXT) {
 321 
 322                 /* determine negotiation mech set */
 323                 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
 324                         credlistptr = &credlist;
 325 
 326                         mstat = get_available_mechs(minor_status,
 327                             GSS_C_NO_NAME, GSS_C_INITIATE,
 328                             credlistptr, &mechSet);
 329                 } else {
 330                         /*
 331                          * Use the list of mechs included in the
 332                          * cred that we were given.
 333                          */
 334                         mstat = gss_inquire_cred(minor_status,
 335                             claimant_cred_handle, NULL, NULL,
 336                             NULL, &mechSet);
 337                 }
 338                 if (mstat != GSS_S_COMPLETE)
 339                         return (mstat);
 340 
 341                 if ((spnego_ctx = create_spnego_ctx()) == NULL) {
 342                         ret = GSS_S_FAILURE;
 343                         goto cleanup;
 344                 }
 345 
 346                 /*
 347                  * need to pull the first mech from mechSet to do first
 348                  * init ctx
 349                  */
 350                 status = generic_gss_copy_oid(minor_status,
 351                     mechSet->elements, &spnego_ctx->internal_mech);
 352 
 353                 if (status != GSS_S_COMPLETE) {
 354                         ret = GSS_S_FAILURE;
 355                         goto cleanup;
 356                 }
 357 
 358                 if (input_token != NULL && input_token->value != NULL) {
 359                         ret = GSS_S_DEFECTIVE_TOKEN;
 360                         goto cleanup;
 361                 }
 362 
 363                 /*
 364                  * The actual context is not yet determined,
 365                  * set the output context_handle to refer to
 366                  * the spnego context itself.
 367                  */
 368                 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
 369                 *context_handle = (gss_ctx_id_t)spnego_ctx;
 370                 send_token = INIT_TOKEN_SEND;
 371                 ret = GSS_S_CONTINUE_NEEDED;
 372         } else {
 373                 mechSet = NULL;
 374                 spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
 375 
 376                 if (input_token == NULL || input_token->value == NULL) {
 377                         ret = GSS_S_DEFECTIVE_TOKEN;
 378                         goto cleanup;
 379                 }
 380                 ptr = (unsigned char *) input_token->value;
 381 
 382                 switch (get_negResult(&ptr, input_token->length)) {
 383                 case ACCEPT_DEFECTIVE_TOKEN:
 384                         *minor_status = 1;
 385                         ret = GSS_S_DEFECTIVE_TOKEN;
 386                         break;
 387                 case ACCEPT_INCOMPLETE: {
 388                         /* pull out mech from token */
 389                         gss_OID internal_mech =
 390                             get_mech_oid(minor_status, &ptr,
 391                             input_token->length -
 392                             (ptr - (uchar_t *)input_token->value));
 393 
 394                         /*
 395                          * check if first mech in neg set, if it isn't,
 396                          * release and copy chosen mech to context,
 397                          * delete internal context from prior mech
 398                          */
 399                         if (internal_mech != NULL &&
 400                             ((internal_mech->length !=
 401                             spnego_ctx->internal_mech->length) ||
 402                             /* CSTYLED */
 403                             memcmp(spnego_ctx->internal_mech->elements,
 404                             internal_mech->elements,
 405                             spnego_ctx->internal_mech->length))) {
 406 
 407                                 (void) gss_delete_sec_context(&mstat,
 408                                     &spnego_ctx->ctx_handle, NULL);
 409 
 410                                 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
 411                                 (void) generic_gss_release_oid(
 412                                     &mstat, &spnego_ctx->internal_mech);
 413 
 414                                 status = generic_gss_copy_oid(
 415                                     minor_status, internal_mech,
 416                                     &spnego_ctx->internal_mech);
 417 
 418                                 if (status != GSS_S_COMPLETE)
 419                                         ret = GSS_S_DEFECTIVE_TOKEN;
 420                                 else
 421                                         ret = GSS_S_COMPLETE;
 422 
 423                                 (void) generic_gss_release_oid(&mstat,
 424                                     &internal_mech);
 425                         } else if (internal_mech == NULL) {
 426                                 ret = GSS_S_DEFECTIVE_TOKEN;
 427                                 send_token = NO_TOKEN_SEND;
 428                         } else {
 429                                 ret = GSS_S_COMPLETE;
 430                         }
 431                         if (ret == GSS_S_COMPLETE) {
 432                                 /*
 433                                  * Check for a token, it may contain
 434                                  * an error message.
 435                                  */
 436                                 if (*ptr ==  (CONTEXT | 0x02)) {
 437                                         if (g_get_tag_and_length(&ptr,
 438                                             (CONTEXT | 0x02),
 439                                             input_token->length - (ptr -
 440                                             (uchar_t *)input_token->value),
 441                                             &len) < 0) {
 442                                                 ret = GSS_S_DEFECTIVE_TOKEN;
 443                                         } else {
 444                                                 i_input_token = get_input_token(
 445                                                     &ptr, len);
 446                                                 if (i_input_token  != NULL) {
 447                                                         /*CSTYLED*/
 448                                                         ret = GSS_S_CONTINUE_NEEDED;
 449                                                         send_token =
 450                                                             CONT_TOKEN_SEND;
 451                                                 } else {
 452                                                         /*CSTYLED*/
 453                                                         ret = GSS_S_DEFECTIVE_TOKEN;
 454                                                         send_token =
 455                                                             NO_TOKEN_SEND;
 456                                                 }
 457                                         }
 458                                 }
 459                         }
 460                         break;
 461                 }
 462                 case ACCEPT_COMPLETE:
 463                         /* pull out mech from token */
 464                         if (spnego_ctx->internal_mech != NULL)
 465                                 (void) generic_gss_release_oid(&mstat,
 466                                     &spnego_ctx->internal_mech);
 467 
 468                         spnego_ctx->internal_mech =
 469                             get_mech_oid(minor_status, &ptr,
 470                             input_token->length -
 471                             (ptr - (uchar_t *)input_token->value));
 472 
 473                         if (spnego_ctx->internal_mech == NULL) {
 474                                 /* CSTYLED */
 475                                 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
 476                                 ret = GSS_S_FAILURE;
 477                         }
 478 
 479                         if (ret != GSS_S_FAILURE && *ptr == (CONTEXT | 0x02)) {
 480                                 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
 481                                     input_token->length - (ptr -
 482                                     (uchar_t *)input_token->value), &len) < 0) {
 483                                         ret = GSS_S_DEFECTIVE_TOKEN;
 484                                 } else {
 485                                         i_input_token = get_input_token(&ptr,
 486                                             len);
 487                                         if (i_input_token  != NULL) {
 488                                                 ret = GSS_S_COMPLETE;
 489                                                 send_token = CHECK_MIC;
 490                                         } else {
 491                                                 ret = GSS_S_DEFECTIVE_TOKEN;
 492                                                 send_token = NO_TOKEN_SEND;
 493                                         }
 494                                 }
 495                         } else if (ret == GSS_S_CONTINUE_NEEDED ||
 496                             ret == GSS_S_COMPLETE) {
 497                                 send_token = CHECK_MIC;
 498                         }
 499 
 500                         /*
 501                          * If we sent "optimistic" initial token,
 502                          * but the acceptor did not send a response token,
 503                          * this is an error.
 504                          */
 505                         if (ret == GSS_S_COMPLETE &&
 506                             i_input_token == GSS_C_NO_BUFFER &&
 507                             spnego_ctx->last_status == GSS_S_CONTINUE_NEEDED &&
 508                             spnego_ctx->optimistic) {
 509                                 /* CSTYLED */
 510                                 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
 511                                 ret = GSS_S_DEFECTIVE_TOKEN;
 512                                 send_token = NO_TOKEN_SEND;
 513                         }
 514 
 515                         if (send_token != NO_TOKEN_SEND) {
 516                                 if (i_input_token == NULL)
 517                                         ret = GSS_S_COMPLETE;
 518                                 else
 519                                         ret = GSS_S_CONTINUE_NEEDED;
 520                                 send_token = CHECK_MIC;
 521                         }
 522                         break;
 523 
 524                 case REJECT:
 525                         ret = GSS_S_BAD_MECH;
 526                         *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
 527                         send_token = NO_TOKEN_SEND;
 528                         break;
 529 
 530                 default:
 531                         ret = GSS_S_FAILURE;
 532                         send_token = NO_TOKEN_SEND;
 533                         break;
 534                 }
 535         }
 536 
 537 
 538         if (send_token == NO_TOKEN_SEND) {
 539                 output_token->length = 0;
 540                 output_token->value = NULL;
 541                 goto cleanup;
 542         }
 543 
 544         i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
 545 
 546         if (i_output_token == NULL) {
 547                 ret = status = GSS_S_FAILURE;
 548                 goto cleanup;
 549         }
 550 
 551         i_output_token->length = 0;
 552         i_output_token->value = NULL;
 553 
 554         if (ret == GSS_S_CONTINUE_NEEDED) {
 555                 gss_OID inner_mech_type = GSS_C_NO_OID;
 556 
 557                 status = gss_init_sec_context(minor_status,
 558                     claimant_cred_handle,
 559                     &spnego_ctx->ctx_handle,
 560                     target_name,
 561                     spnego_ctx->internal_mech,
 562                     req_flags,
 563                     time_req,
 564                     NULL,
 565                     i_input_token,
 566                     &inner_mech_type,
 567                     i_output_token,
 568                     &local_ret_flags,
 569                     time_rec);
 570 
 571                 if (ret_flags)
 572                         *ret_flags = local_ret_flags;
 573 
 574                 spnego_ctx->last_status = status;
 575 
 576                 if (i_input_token != GSS_C_NO_BUFFER) {
 577                         (void) gss_release_buffer(&mstat, i_input_token);
 578                         free(i_input_token);
 579                 }
 580 
 581                 if ((status != GSS_S_COMPLETE) &&
 582                     (status != GSS_S_CONTINUE_NEEDED)) {
 583                         ret = status;
 584                 }
 585 
 586                 /* create mic/check mic */
 587                 if ((i_output_token->length == 0) &&
 588                     (status == GSS_S_COMPLETE) &&
 589                     (local_ret_flags & GSS_C_INTEG_FLAG)) {
 590                         if (*ptr == (CONTEXT | 0x03)) {
 591                                 if (g_get_tag_and_length(&ptr,
 592                                     (CONTEXT | 0x03), input_token->length -
 593                                     (ptr - (uchar_t *)input_token->value),
 594                                     &len) < 0) {
 595                                         ret = GSS_S_DEFECTIVE_TOKEN;
 596                                 } else {
 597                                         ret = GSS_S_COMPLETE;
 598                                         mechListMIC = get_input_token(&ptr,
 599                                             len);
 600                                         if (mechListMIC == NULL)
 601                                                 ret = GSS_S_DEFECTIVE_TOKEN;
 602                                         else if (!spnego_ctx->MS_Interop &&
 603                                             spnego_ctx->DER_mechTypes.length >
 604                                             0) {
 605                                                 status = gss_verify_mic(
 606                                                     minor_status,
 607                                                     spnego_ctx->ctx_handle,
 608                                                     &spnego_ctx->DER_mechTypes,
 609                                                     mechListMIC, qop_state);
 610                                         }
 611                                 }
 612                         } else if (!spnego_ctx->MS_Interop) {
 613                         /*
 614                          * If no MIC was sent and we are in
 615                          * "standard" mode (i.e. NOT MS_Interop),
 616                          * the MIC must be present.
 617                          */
 618                                 ret = GSS_S_DEFECTIVE_TOKEN;
 619                         } else {
 620                                 /* In "MS_Interop" mode, MIC is ignored. */
 621                                 ret = GSS_S_COMPLETE;
 622                         }
 623                 }
 624         }
 625 
 626         if ((status == GSS_S_COMPLETE) &&
 627             (ret == GSS_S_COMPLETE)) {
 628                 if (actual_mech) {
 629                         (void) generic_gss_release_oid(&mstat, actual_mech);
 630                         ret = generic_gss_copy_oid(&mstat,
 631                             spnego_ctx->internal_mech, actual_mech);
 632                         if (ret != GSS_S_COMPLETE)
 633                                 goto cleanup;
 634                 }
 635 
 636         } else if (ret == GSS_S_CONTINUE_NEEDED) {
 637                 if (make_spnego_tokenInit_msg(spnego_ctx,
 638                     mechSet, i_output_token, send_token,
 639                     output_token) < 0) {
 640                         ret = GSS_S_DEFECTIVE_TOKEN;
 641                 }
 642         }
 643 
 644 cleanup:
 645         if (status != GSS_S_COMPLETE)
 646                 ret = status;
 647         if (ret != GSS_S_COMPLETE &&
 648             ret != GSS_S_CONTINUE_NEEDED) {
 649                 if (spnego_ctx != NULL &&
 650                     spnego_ctx->ctx_handle != NULL)
 651                         gss_delete_sec_context(&mstat, &spnego_ctx->ctx_handle,
 652                             GSS_C_NO_BUFFER);
 653 
 654                 if (spnego_ctx != NULL)
 655                         release_spnego_ctx(&spnego_ctx);
 656 
 657                 *context_handle = GSS_C_NO_CONTEXT;
 658 
 659                 if (output_token)
 660                         (void) gss_release_buffer(&mstat, output_token);
 661         }
 662 
 663         if (i_output_token != GSS_C_NO_BUFFER) {
 664                 (void) gss_release_buffer(&mstat, i_output_token);
 665                 free(i_output_token);
 666         }
 667 
 668         if (mechListMIC != GSS_C_NO_BUFFER) {
 669                 (void) gss_release_buffer(&mstat, mechListMIC);
 670                 free(mechListMIC);
 671         }
 672 
 673         if (mechSet != NULL)
 674                 (void) gss_release_oid_set(&mstat, &mechSet);
 675 
 676         if (credlistptr != NULL)
 677                 (void) gss_release_cred(&mstat, credlistptr);
 678 
 679         return (ret);
 680 } /* init_sec_context */
 681 
 682 /*ARGSUSED*/
 683 OM_uint32
 684 spnego_gss_accept_sec_context(void *ct,
 685                             OM_uint32 *minor_status,
 686                             gss_ctx_id_t *context_handle,
 687                             gss_cred_id_t verifier_cred_handle,
 688                             gss_buffer_t input_token,
 689                             gss_channel_bindings_t input_chan_bindings,
 690                             gss_name_t *src_name,
 691                             gss_OID *mech_type,
 692                             gss_buffer_t output_token,
 693                             OM_uint32 *ret_flags,
 694                             OM_uint32 *time_rec,
 695                             gss_cred_id_t *delegated_cred_handle)
 696 {
 697         spnego_gss_ctx_id_t spnego_ctx = NULL;
 698         gss_OID mech_wanted = NULL;
 699         gss_OID_set mechSet = GSS_C_NO_OID_SET;
 700         gss_OID_set supported_mechSet = GSS_C_NO_OID_SET;
 701         gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
 702         gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
 703         gss_buffer_t mechListMIC = GSS_C_NO_BUFFER;
 704         gss_cred_id_t acquired_cred = NULL;
 705         gss_name_t internal_name = GSS_C_NO_NAME;
 706         OM_uint32 status = GSS_S_COMPLETE;
 707         OM_uint32 ret = GSS_S_COMPLETE;
 708         unsigned char *ptr;
 709         unsigned char *bufstart;
 710         int bodysize;
 711         int err, len;
 712         OM_uint32 negResult;
 713         OM_uint32 minor_stat;
 714         OM_uint32 mstat;
 715         OM_uint32 req_flags;
 716         OM_uint32 mechsetlen;
 717         gss_qop_t qop_state;
 718         send_token_flag return_token =  NO_TOKEN_SEND;
 719         bool_t firstMech;
 720         bool_t Need_Cred = FALSE;
 721         OM_uint32 local_ret_flags = 0;
 722         uchar_t *buf, *tmp;
 723 
 724         dsyslog("Entering accept_sec_context\n");
 725 
 726         if (context_handle == NULL)
 727                 return (GSS_S_NO_CONTEXT);
 728 
 729         if (src_name)
 730                 *src_name = (gss_name_t)NULL;
 731 
 732         output_token->length = 0;
 733         output_token->value = NULL;
 734         *minor_status = 0;
 735 
 736         if (mech_type)
 737                 *mech_type = GSS_C_NULL_OID;
 738 
 739         /* return a bogus cred handle */
 740         if (delegated_cred_handle)
 741                 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
 742 
 743         if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
 744                 Need_Cred = TRUE;
 745         }
 746 
 747         /* Check for defective input token. */
 748         ptr = bufstart = (unsigned char *) input_token->value;
 749         if (err = g_verify_token_header((gss_OID)gss_mech_spnego, &bodysize,
 750             &ptr, 0, input_token->length)) {
 751                 *minor_status = err;
 752                 ret = GSS_S_DEFECTIVE_TOKEN;
 753                 negResult = REJECT;
 754                 return_token = ERROR_TOKEN_SEND;
 755                 goto senderror;
 756         }
 757 
 758         /*
 759          * set up of context, determine mech to be used, save mechset
 760          * for use later in integrety check.
 761          */
 762         if (*context_handle == GSS_C_NO_CONTEXT) {
 763                 if ((spnego_ctx = create_spnego_ctx()) == NULL)
 764                         return (GSS_S_FAILURE);
 765 
 766                 /*
 767                  * Until the accept operation is complete, the
 768                  * context_handle returned should refer to
 769                  * the spnego context.
 770                  */
 771                 *context_handle = (gss_ctx_id_t)spnego_ctx;
 772                 minor_stat = get_available_mechs(minor_status,
 773                     GSS_C_NO_NAME, GSS_C_ACCEPT,
 774                     NULL, &supported_mechSet);
 775 
 776                 if (minor_stat != GSS_S_COMPLETE) {
 777                         release_spnego_ctx(&spnego_ctx);
 778                         *context_handle = GSS_C_NO_CONTEXT;
 779                         return (minor_stat);
 780                 }
 781 
 782                 if (Need_Cred) {
 783                         minor_stat = gss_acquire_cred(minor_status,
 784                             GSS_C_NO_NAME, NULL, supported_mechSet,
 785                             GSS_C_ACCEPT, &acquired_cred, NULL,
 786                             NULL);
 787 
 788                         if (minor_stat != GSS_S_COMPLETE) {
 789                                 (void) gss_release_oid_set(minor_status,
 790                                     &supported_mechSet);
 791                                 release_spnego_ctx(&spnego_ctx);
 792                                 *context_handle = GSS_C_NO_CONTEXT;
 793                                 return (minor_stat);
 794                         } else {
 795                                 verifier_cred_handle = acquired_cred;
 796                         }
 797                 }
 798 
 799                 if (err = g_verify_neg_token_init(&ptr, input_token->length)) {
 800                         *minor_status = err;
 801                         ret = GSS_S_DEFECTIVE_TOKEN;
 802                         negResult = REJECT;
 803                         return_token = ERROR_TOKEN_SEND;
 804                         goto senderror;
 805                 }
 806 
 807                 /*
 808                  * Allocate space to hold the mechTypes
 809                  * because we need it later.
 810                  */
 811                 mechsetlen = input_token->length - (ptr - bufstart);
 812                 buf = (uchar_t *)malloc(mechsetlen);
 813                 if (buf == NULL) {
 814                         ret = GSS_S_FAILURE;
 815                         goto cleanup;
 816                 }
 817                 (void) memcpy(buf, ptr, mechsetlen);
 818                 ptr = bufstart = buf;
 819 
 820                 /*
 821                  * Get pointers to the DER encoded MechSet so we
 822                  * can properly check and calculate a MIC later.
 823                  */
 824                 spnego_ctx->DER_mechTypes.value = ptr;
 825                 mechSet = get_mech_set(minor_status, &ptr, mechsetlen);
 826                 if (mechSet == NULL) {
 827                         ret = GSS_S_DEFECTIVE_TOKEN;
 828                         negResult = REJECT;
 829                         return_token = ERROR_TOKEN_SEND;
 830                         goto senderror;
 831                 }
 832                 spnego_ctx->DER_mechTypes.length = ptr - bufstart;
 833                 mechsetlen -= (ptr - bufstart);
 834 
 835                 /*
 836                  * Select the best match between the list of mechs
 837                  * that the initiator requested and the list that
 838                  * the acceptor will support.
 839                  */
 840                 mech_wanted = negotiate_mech_type(minor_status,
 841                     supported_mechSet, mechSet, &negResult,
 842                     &firstMech);
 843 
 844                 (void) gss_release_oid_set(&minor_stat, &supported_mechSet);
 845                 (void) gss_release_oid_set(&minor_stat, &mechSet);
 846                 supported_mechSet = NULL;
 847                 mechSet = NULL;
 848 
 849                 if (get_req_flags(&ptr, (int *)&mechsetlen, &req_flags) ==
 850                     ACCEPT_DEFECTIVE_TOKEN) {
 851                         negResult = REJECT;
 852                 }
 853 
 854                 tmp = ptr;
 855                 if (negResult == ACCEPT_COMPLETE) {
 856                         if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
 857                             mechsetlen, &len) < 0) {
 858                                 negResult = REJECT;
 859                         } else {
 860                                 i_input_token = get_input_token(&ptr, len);
 861                                 if (i_input_token == NULL) {
 862                                         negResult = REJECT;
 863                                 }
 864                         }
 865                         return_token = INIT_TOKEN_SEND;
 866                 }
 867                 if (negResult == REJECT) {
 868                         ret = GSS_S_DEFECTIVE_TOKEN;
 869                         return_token = ERROR_TOKEN_SEND;
 870                 } else {
 871                         ret = GSS_S_CONTINUE_NEEDED;
 872                         return_token = INIT_TOKEN_SEND;
 873                 }
 874 
 875                 mechsetlen -= ptr - tmp;
 876                 /*
 877                  * Check to see if there is a MechListMIC field
 878                  */
 879                 if (negResult == ACCEPT_COMPLETE && mechsetlen > 0) {
 880                         tmp = ptr;
 881                         if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
 882                             mechsetlen, &len) >= 0) {
 883                                 mechListMIC = get_input_token(&ptr, len);
 884                                 if (mechListMIC == GSS_C_NO_BUFFER) {
 885                                         negResult = REJECT;
 886                                         return_token = ERROR_TOKEN_SEND;
 887                                         ret = GSS_S_DEFECTIVE_TOKEN;
 888                                 }
 889                                 mechsetlen -= (ptr - tmp);
 890                         }
 891                 }
 892         } else {
 893                 /*
 894                  * get internal input token and context for continued
 895                  * calls of spnego_gss_init_sec_context.
 896                  */
 897                 i_input_token = get_input_token(&ptr,
 898                     input_token->length - (ptr -
 899                     (uchar_t *)input_token->value));
 900                 if (i_input_token == NULL) {
 901                         negResult = REJECT;
 902                         return_token = ERROR_TOKEN_SEND;
 903                         ret = GSS_S_DEFECTIVE_TOKEN;
 904                 } else {
 905                         spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
 906                         return_token = CONT_TOKEN_SEND;
 907                 }
 908         }
 909 
 910         /*
 911          * If we still don't have a cred, we have an error.
 912          */
 913         if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
 914                 ret = GSS_S_FAILURE;
 915                 goto cleanup;
 916         }
 917 
 918         /* If we have an error already, bail out */
 919         if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED)
 920                 goto senderror;
 921 
 922         if (i_input_token != GSS_C_NO_BUFFER) {
 923                 i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
 924 
 925                 if (i_output_token == NULL) {
 926                         ret = GSS_S_FAILURE;
 927                         goto cleanup;
 928                 }
 929 
 930                 i_output_token->length = 0;
 931                 i_output_token->value = NULL;
 932 
 933                 status = gss_accept_sec_context(&minor_stat,
 934                     &spnego_ctx->ctx_handle, verifier_cred_handle,
 935                     i_input_token, GSS_C_NO_CHANNEL_BINDINGS,
 936                     &internal_name, mech_type, i_output_token,
 937                     &local_ret_flags, time_rec, delegated_cred_handle);
 938 
 939                 if ((status != GSS_S_COMPLETE) &&
 940                     (status != GSS_S_CONTINUE_NEEDED)) {
 941                         *minor_status = minor_stat;
 942                         (void) gss_release_buffer(&mstat, i_input_token);
 943 
 944                         if (i_input_token != GSS_C_NO_BUFFER) {
 945                                 free(i_input_token);
 946                                 i_input_token = GSS_C_NO_BUFFER;
 947                         }
 948 
 949                         ret = status;
 950 
 951                         /*
 952                          * Reject the request with an error token.
 953                          */
 954                         negResult = REJECT;
 955                         return_token = ERROR_TOKEN_SEND;
 956 
 957                         goto senderror;
 958                 }
 959 
 960                 if (ret_flags)
 961                         *ret_flags = local_ret_flags;
 962 
 963                 if (i_input_token != GSS_C_NO_BUFFER) {
 964                         (void) gss_release_buffer(&mstat, i_input_token);
 965                         free(i_input_token);
 966                         i_input_token = GSS_C_NO_BUFFER;
 967                 }
 968 
 969                 /* If we got a MIC, verify it if possible */
 970                 if ((status == GSS_S_COMPLETE) &&
 971                     (local_ret_flags & GSS_C_INTEG_FLAG) &&
 972                     mechListMIC != GSS_C_NO_BUFFER &&
 973                     !spnego_ctx->MS_Interop) {
 974 
 975                         ret = gss_verify_mic(minor_status,
 976                             spnego_ctx->ctx_handle,
 977                             &spnego_ctx->DER_mechTypes,
 978                             mechListMIC, &qop_state);
 979 
 980                         (void) gss_release_buffer(&mstat, mechListMIC);
 981                         free(mechListMIC);
 982                         mechListMIC = GSS_C_NO_BUFFER;
 983 
 984                         if (ret != GSS_S_COMPLETE) {
 985                                 negResult = REJECT;
 986                                 return_token = ERROR_TOKEN_SEND;
 987                                 goto senderror;
 988                         }
 989                 }
 990 
 991                 /*
 992                  * If the MIC was verified OK, create a new MIC
 993                  * for the response message.
 994                  */
 995                 if (status == GSS_S_COMPLETE &&
 996                     (local_ret_flags & GSS_C_INTEG_FLAG) &&
 997                     !spnego_ctx->MS_Interop) {
 998                         mechListMIC = (gss_buffer_t)
 999                             malloc(sizeof (gss_buffer_desc));
1000 
1001                         if (mechListMIC == NULL ||
1002                             spnego_ctx->DER_mechTypes.length == 0) {
1003                                 ret = GSS_S_FAILURE;
1004                                 goto cleanup;
1005                         }
1006 
1007                         ret = gss_get_mic(minor_status,
1008                             spnego_ctx->ctx_handle,
1009                             GSS_C_QOP_DEFAULT,
1010                             &spnego_ctx->DER_mechTypes,
1011                             mechListMIC);
1012 
1013                         if (ret != GSS_S_COMPLETE) {
1014                                 negResult = REJECT;
1015                                 return_token = ERROR_TOKEN_SEND;
1016                                 goto senderror;
1017                         }
1018                 }
1019                 ret = status;
1020 
1021                 if (status == GSS_S_COMPLETE) {
1022                         if (internal_name != NULL && src_name != NULL)
1023                                 *src_name = internal_name;
1024                 }
1025 
1026 
1027                 if (status == GSS_S_CONTINUE_NEEDED) {
1028                         if (return_token == INIT_TOKEN_SEND)
1029                                 negResult = ACCEPT_INCOMPLETE;
1030                 }
1031         }
1032 
1033 senderror:
1034         if ((return_token == INIT_TOKEN_SEND) ||
1035             (return_token == CONT_TOKEN_SEND) ||
1036             (return_token == ERROR_TOKEN_SEND)) {
1037                 int MS_Interop = 0;
1038 
1039                 if (spnego_ctx)
1040                         MS_Interop = spnego_ctx->MS_Interop;
1041 
1042                 /*
1043                  * create response for the initiator.
1044                  */
1045                 err = make_spnego_tokenTarg_msg(negResult,
1046                     mech_wanted, i_output_token,
1047                     mechListMIC, return_token,
1048                     MS_Interop, output_token);
1049 
1050                 (void) gss_release_buffer(&mstat, mechListMIC);
1051                 free(mechListMIC);
1052 
1053                 /*
1054                  * If we could not make the response token,
1055                  * we will have to fail without sending a response.
1056                  */
1057                 if (err) {
1058                         (void) gss_release_buffer(&mstat, output_token);
1059                 }
1060         } else {
1061                 (void) gss_release_buffer(&mstat, output_token);
1062         }
1063 
1064 cleanup:
1065         if (ret != GSS_S_COMPLETE &&
1066             ret != GSS_S_CONTINUE_NEEDED) {
1067                 if (spnego_ctx != NULL) {
1068                         (void) gss_delete_sec_context(&mstat,
1069                             &spnego_ctx->ctx_handle, NULL);
1070 
1071                         spnego_ctx->ctx_handle = NULL;
1072 
1073                         release_spnego_ctx(&spnego_ctx);
1074                 }
1075                 *context_handle = GSS_C_NO_CONTEXT;
1076         }
1077         if (mech_wanted != NULL) {
1078                 generic_gss_release_oid(&mstat, &mech_wanted);
1079         }
1080 
1081         (void) gss_release_cred(&mstat, &acquired_cred);
1082         (void) gss_release_oid_set(&mstat, &supported_mechSet);
1083 
1084         (void) gss_release_buffer(&mstat, i_output_token);
1085         free(i_output_token);
1086 
1087         return (ret);
1088 }
1089 
1090 /*ARGSUSED*/
1091 OM_uint32
1092 spnego_gss_display_status(void *ctx,
1093                 OM_uint32 *minor_status,
1094                 OM_uint32 status_value,
1095                 int status_type,
1096                 gss_OID mech_type,
1097                 OM_uint32 *message_context,
1098                 gss_buffer_t status_string)
1099 {
1100         OM_uint32 ret = GSS_S_COMPLETE;
1101         dsyslog("Entering display_status\n");
1102 
1103         *message_context = 0;
1104         switch (status_value) {
1105                 case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1106                         *status_string = make_err_msg(gettext(
1107                             "SPNEGO cannot find mechanisms to negotiate"));
1108                         break;
1109                 case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1110                         *status_string = make_err_msg(gettext(
1111                             "SPNEGO failed to acquire creds"));
1112                         break;
1113                 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1114                         *status_string = make_err_msg(gettext(
1115                             "SPNEGO acceptor did not select a mechanism"));
1116                         break;
1117                 case ERR_SPNEGO_NEGOTIATION_FAILED:
1118                         *status_string = make_err_msg(gettext(
1119                             "SPNEGO failed to negotiate a mechanism"));
1120                         break;
1121                 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1122                         *status_string = make_err_msg(gettext(
1123                             "SPNEGO acceptor did not return a valid token"));
1124                         break;
1125                 case ERR_SPNEGO_BAD_INPUT_PARAMETER:
1126                         *status_string = make_err_msg(gettext(
1127                             "SPNEGO function received an incorrect input "
1128                             "parameter"));
1129                         break;
1130                 default:
1131                         status_string->length = 0;
1132                         status_string->value = "";
1133                         ret = GSS_S_BAD_STATUS;
1134                         break;
1135         }
1136 
1137         dsyslog("Leaving display_status\n");
1138         return (ret);
1139 }
1140 
1141 /*ARGSUSED*/
1142 OM_uint32
1143 spnego_gss_import_name(void *ctx,
1144                     OM_uint32 *minor_status,
1145                     gss_buffer_t input_name_buffer,
1146                     gss_OID input_name_type,
1147                     gss_name_t *output_name)
1148 {
1149         OM_uint32 status;
1150 
1151         dsyslog("Entering import_name\n");
1152 
1153         status = gss_import_name(minor_status, input_name_buffer,
1154             input_name_type, output_name);
1155 
1156         dsyslog("Leaving import_name\n");
1157         return (status);
1158 }
1159 
1160 /*ARGSUSED*/
1161 OM_uint32
1162 spnego_gss_release_name(void *ctx,
1163                         OM_uint32 *minor_status,
1164                         gss_name_t *input_name)
1165 {
1166         OM_uint32 status;
1167 
1168         dsyslog("Entering release_name\n");
1169 
1170         status = gss_release_name(minor_status, input_name);
1171 
1172         dsyslog("Leaving release_name\n");
1173         return (status);
1174 }
1175 
1176 /*ARGSUSED*/
1177 OM_uint32
1178 spnego_gss_display_name(void *ctx,
1179                         OM_uint32 *minor_status,
1180                         gss_name_t input_name,
1181                         gss_buffer_t output_name_buffer,
1182                         gss_OID *output_name_type)
1183 {
1184         OM_uint32 status = GSS_S_COMPLETE;
1185         dsyslog("Entering display_name\n");
1186 
1187         status = gss_display_name(minor_status, input_name,
1188             output_name_buffer, output_name_type);
1189 
1190         dsyslog("Leaving display_name\n");
1191         return (status);
1192 }
1193 
1194 /*ARGSUSED*/
1195 OM_uint32
1196 spnego_gss_inquire_cred(void *ctx,
1197         OM_uint32 *minor_status,
1198         const gss_cred_id_t cred_handle,
1199         gss_name_t *name,
1200         OM_uint32 *lifetime,
1201         gss_cred_usage_t *cred_usage,
1202         gss_OID_set *mechanisms)
1203 {
1204         OM_uint32 stat = GSS_S_COMPLETE;
1205         gss_cred_id_t *credlistptr = NULL, credlist = NULL;
1206         OM_uint32 init_lt, accept_lt;
1207         int i;
1208 
1209         if (cred_handle == GSS_C_NO_CREDENTIAL) {
1210                 OM_uint32 tstat;
1211                 credlistptr = &credlist;
1212 
1213                 /*
1214                  * Get a list of all non-SPNEGO
1215                  * mechanisms that are available and
1216                  * acquire a default cred.
1217                  */
1218                 stat = get_available_mechs(minor_status,
1219                     NULL, GSS_C_BOTH, credlistptr, mechanisms);
1220 
1221                 /*
1222                  * inquire about the default cred from the
1223                  * first non-SPNEGO mechanism that was found.
1224                  */
1225                 if (stat == GSS_S_COMPLETE && mechanisms &&
1226                     (*mechanisms)->count > 0) {
1227                         i = 0;
1228                         do {
1229                                 stat = gss_inquire_cred_by_mech(
1230                                     minor_status, credlist,
1231                                     &((*mechanisms)->elements[i]),
1232                                     name, &init_lt, &accept_lt,
1233                                     cred_usage);
1234 
1235                                 /*
1236                                  * Set the lifetime to the correct value.
1237                                  */
1238                                 if (stat == GSS_S_COMPLETE) {
1239                                         if (*cred_usage == GSS_C_INITIATE)
1240                                                 *lifetime = init_lt;
1241                                         else
1242                                                 *lifetime = accept_lt;
1243                                 }
1244                                 if (credlist != GSS_C_NO_CREDENTIAL)
1245                                         (void) gss_release_cred(&tstat,
1246                                             &credlist);
1247                         } while (stat != GSS_S_COMPLETE &&
1248                             (i < (*mechanisms)->count));
1249                 }
1250         } else {
1251                 /*
1252                  * This should not happen, it cannot be processed.
1253                  */
1254                 stat = GSS_S_FAILURE;
1255                 if (minor_status != NULL)
1256                         *minor_status = ERR_SPNEGO_BAD_INPUT_PARAMETER;
1257         }
1258         return (stat);
1259 }
1260 
1261 /*ARGSUSED*/
1262 OM_uint32
1263 spnego_gss_inquire_names_for_mech(void *ctx,
1264                                 OM_uint32       *minor_status,
1265                                 gss_OID         mechanism,
1266                                 gss_OID_set     *name_types)
1267 {
1268         OM_uint32   major, minor;
1269 
1270         dsyslog("Entering inquire_names_for_mech\n");
1271         /*
1272          * We only know how to handle our own mechanism.
1273          */
1274         if ((mechanism != GSS_C_NULL_OID) &&
1275             !g_OID_equal(gss_mech_spnego, mechanism)) {
1276                 *minor_status = 0;
1277                 return (GSS_S_FAILURE);
1278         }
1279 
1280         major = gss_create_empty_oid_set(minor_status, name_types);
1281         if (major == GSS_S_COMPLETE) {
1282                 /* Now add our members. */
1283                 if (((major = gss_add_oid_set_member(minor_status,
1284                     (gss_OID) GSS_C_NT_USER_NAME,
1285                     name_types)) == GSS_S_COMPLETE) &&
1286                     ((major = gss_add_oid_set_member(minor_status,
1287                     (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
1288                     name_types)) == GSS_S_COMPLETE) &&
1289                     ((major = gss_add_oid_set_member(minor_status,
1290                     (gss_OID) GSS_C_NT_STRING_UID_NAME,
1291                     name_types)) == GSS_S_COMPLETE)) {
1292                         major = gss_add_oid_set_member(minor_status,
1293                             (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
1294                             name_types);
1295                 }
1296 
1297                 if (major != GSS_S_COMPLETE)
1298                         (void) gss_release_oid_set(&minor, name_types);
1299         }
1300 
1301         dsyslog("Leaving inquire_names_for_mech\n");
1302         return (major);
1303 }
1304 
1305 OM_uint32
1306 spnego_gss_unseal(void *context,
1307                 OM_uint32 *minor_status,
1308                 gss_ctx_id_t context_handle,
1309                 gss_buffer_t input_message_buffer,
1310                 gss_buffer_t output_message_buffer,
1311                 int *conf_state,
1312                 int *qop_state)
1313 {
1314         OM_uint32 ret;
1315         spnego_gss_ctx_id_t ctx = (spnego_gss_ctx_id_t)context_handle;
1316 
1317         if (context_handle == NULL)
1318                 return (GSS_S_NO_CONTEXT);
1319 
1320         ret = gss_unseal(minor_status,
1321             ctx->ctx_handle, input_message_buffer,
1322             output_message_buffer, conf_state, qop_state);
1323 
1324         return (ret);
1325 }
1326 
1327 OM_uint32
1328 spnego_gss_seal(void *context,
1329                 OM_uint32 *minor_status,
1330                 gss_ctx_id_t context_handle,
1331                 int conf_req_flag,
1332                 int qop_req,
1333                 gss_buffer_t input_message_buffer,
1334                 int *conf_state,
1335                 gss_buffer_t output_message_buffer)
1336 {
1337         OM_uint32 ret;
1338         spnego_gss_ctx_id_t ctx =
1339             (spnego_gss_ctx_id_t)context_handle;
1340 
1341         if (context_handle == NULL)
1342                 return (GSS_S_NO_CONTEXT);
1343 
1344         ret = gss_seal(minor_status,
1345             ctx->ctx_handle, conf_req_flag,
1346             qop_req, input_message_buffer,
1347             conf_state, output_message_buffer);
1348 
1349         return (ret);
1350 }
1351 
1352 OM_uint32
1353 spnego_gss_process_context_token(void *context,
1354         OM_uint32       *minor_status,
1355         const gss_ctx_id_t context_handle,
1356         const gss_buffer_t token_buffer)
1357 {
1358         OM_uint32 ret;
1359         spnego_gss_ctx_id_t ctx =
1360             (spnego_gss_ctx_id_t)context_handle;
1361 
1362         if (context_handle == NULL)
1363                 return (GSS_S_NO_CONTEXT);
1364 
1365         ret = gss_process_context_token(minor_status,
1366             ctx->ctx_handle, token_buffer);
1367 
1368         return (ret);
1369 }
1370 
1371 OM_uint32
1372 spnego_gss_delete_sec_context(void *context,
1373                             OM_uint32 *minor_status,
1374                             gss_ctx_id_t *context_handle,
1375                             gss_buffer_t output_token)
1376 {
1377         OM_uint32 ret = GSS_S_COMPLETE;
1378         spnego_gss_ctx_id_t *ctx =
1379             (spnego_gss_ctx_id_t *)context_handle;
1380 
1381         if (context_handle == NULL || *ctx == NULL)
1382                 return (GSS_S_NO_CONTEXT);
1383 
1384         /*
1385          * If this is still a SPNEGO mech, release it locally.
1386          */
1387         if ((*ctx)->ctx_handle == GSS_C_NO_CONTEXT) {
1388                 (void) release_spnego_ctx(ctx);
1389         } else {
1390                 ret = gss_delete_sec_context(minor_status,
1391                     &(*ctx)->ctx_handle, output_token);
1392         }
1393 
1394         return (ret);
1395 }
1396 
1397 OM_uint32
1398 spnego_gss_context_time(void *context,
1399         OM_uint32       *minor_status,
1400         const gss_ctx_id_t context_handle,
1401         OM_uint32       *time_rec)
1402 {
1403         OM_uint32 ret;
1404         spnego_gss_ctx_id_t ctx =
1405             (spnego_gss_ctx_id_t)context_handle;
1406 
1407         if (context_handle == NULL)
1408                 return (GSS_S_NO_CONTEXT);
1409 
1410         ret = gss_context_time(minor_status,
1411             ctx->ctx_handle, time_rec);
1412 
1413         return (ret);
1414 }
1415 
1416 OM_uint32
1417 spnego_gss_export_sec_context(void *context,
1418         OM_uint32         *minor_status,
1419         gss_ctx_id_t *context_handle,
1420         gss_buffer_t interprocess_token)
1421 {
1422         OM_uint32 ret;
1423         spnego_gss_ctx_id_t *ctx =
1424             (spnego_gss_ctx_id_t *)context_handle;
1425 
1426         if (context_handle == NULL || *ctx == NULL)
1427                 return (GSS_S_NO_CONTEXT);
1428 
1429         ret = gss_export_sec_context(minor_status,
1430             &(*ctx)->ctx_handle, interprocess_token);
1431         return (ret);
1432 }
1433 
1434 OM_uint32
1435 spnego_gss_import_sec_context(void *context,
1436         OM_uint32               *minor_status,
1437         const gss_buffer_t      interprocess_token,
1438         gss_ctx_id_t            *context_handle)
1439 {
1440         OM_uint32 ret;
1441         spnego_gss_ctx_id_t ctx;
1442 
1443         if (context_handle == NULL)
1444                 return (GSS_S_NO_CONTEXT);
1445 
1446         if ((ctx = create_spnego_ctx()) == NULL) {
1447                 *minor_status = ENOMEM;
1448                 return (GSS_S_FAILURE);
1449         }
1450 
1451         ret = gss_import_sec_context(minor_status,
1452             interprocess_token, &(ctx->ctx_handle));
1453         if (GSS_ERROR(ret)) {
1454                 (void) release_spnego_ctx(&ctx);
1455                 return (ret);
1456         }
1457 
1458         *context_handle = (gss_ctx_id_t)ctx;
1459 
1460         return (ret);
1461 }
1462 
1463 OM_uint32
1464 spnego_gss_inquire_context(void *context,
1465         OM_uint32       *minor_status,
1466         const gss_ctx_id_t context_handle,
1467         gss_name_t      *src_name,
1468         gss_name_t      *targ_name,
1469         OM_uint32       *lifetime_rec,
1470         gss_OID         *mech_type,
1471         OM_uint32       *ctx_flags,
1472         int             *locally_initiated,
1473         int             *open)
1474 {
1475         OM_uint32 ret = GSS_S_COMPLETE;
1476         spnego_gss_ctx_id_t ctx =
1477             (spnego_gss_ctx_id_t)context_handle;
1478 
1479         if (context_handle == NULL)
1480                 return (GSS_S_NO_CONTEXT);
1481 
1482         ret = gss_inquire_context(minor_status,
1483             ctx->ctx_handle, src_name,
1484             targ_name, lifetime_rec,
1485             mech_type, ctx_flags,
1486             locally_initiated, open);
1487 
1488         return (ret);
1489 }
1490 
1491 OM_uint32
1492 spnego_gss_wrap_size_limit(void *context,
1493         OM_uint32       *minor_status,
1494         const gss_ctx_id_t context_handle,
1495         int             conf_req_flag,
1496         gss_qop_t       qop_req,
1497         OM_uint32       req_output_size,
1498         OM_uint32       *max_input_size)
1499 {
1500         OM_uint32 ret;
1501         spnego_gss_ctx_id_t ctx =
1502             (spnego_gss_ctx_id_t)context_handle;
1503 
1504         if (context_handle == NULL)
1505                 return (GSS_S_NO_CONTEXT);
1506 
1507         ret = gss_wrap_size_limit(minor_status,
1508             ctx->ctx_handle, conf_req_flag,
1509             qop_req, req_output_size,
1510             max_input_size);
1511         return (ret);
1512 }
1513 
1514 OM_uint32
1515 spnego_gss_sign(void *context,
1516                 OM_uint32 *minor_status,
1517                 const gss_ctx_id_t context_handle,
1518                 int  qop_req,
1519                 const gss_buffer_t message_buffer,
1520                 gss_buffer_t message_token)
1521 {
1522         OM_uint32 ret;
1523         spnego_gss_ctx_id_t ctx =
1524             (spnego_gss_ctx_id_t)context_handle;
1525 
1526         if (context_handle == NULL)
1527                 return (GSS_S_NO_CONTEXT);
1528 
1529         ret = gss_sign(minor_status,
1530             ctx->ctx_handle,
1531             qop_req,
1532             message_buffer,
1533             message_token);
1534 
1535         return (ret);
1536 }
1537 
1538 OM_uint32
1539 spnego_gss_verify(void *context,
1540         OM_uint32 *minor_status,
1541         const gss_ctx_id_t context_handle,
1542         const gss_buffer_t msg_buffer,
1543         const gss_buffer_t token_buffer,
1544         int *qop_state)
1545 {
1546         OM_uint32 ret;
1547         spnego_gss_ctx_id_t ctx =
1548             (spnego_gss_ctx_id_t)context_handle;
1549 
1550         if (context_handle == NULL)
1551                 return (GSS_S_NO_CONTEXT);
1552 
1553         ret = gss_verify_mic(minor_status,
1554             ctx->ctx_handle,
1555             msg_buffer,
1556             token_buffer,
1557             (uint32_t *)qop_state);
1558         return (ret);
1559 }
1560 
1561 /*
1562  * We will release everything but the ctx_handle so that it
1563  * can be passed back to init/accept context. This routine should
1564  * not be called until after the ctx_handle memory is assigned to
1565  * the supplied context handle from init/accept context.
1566  */
1567 static void
1568 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
1569 {
1570         spnego_gss_ctx_id_t context;
1571         OM_uint32 minor_stat;
1572 
1573         if (ctx != NULL)
1574                 context = *ctx;
1575         else
1576                 return;
1577 
1578         if (context != NULL) {
1579                 (void) gss_release_buffer(&minor_stat,
1580                     &context->DER_mechTypes);
1581 
1582                 (void) generic_gss_release_oid(&minor_stat,
1583                     &context->internal_mech);
1584 
1585                 if (context->optionStr != NULL) {
1586                         free(context->optionStr);
1587                         context->optionStr = NULL;
1588                 }
1589                 if (context->ctx_handle != GSS_C_NO_CONTEXT)
1590                         gss_delete_sec_context(&minor_stat,
1591                             &context->ctx_handle, GSS_C_NO_BUFFER);
1592 
1593                 free(context);
1594                 *ctx = NULL;
1595         }
1596 }
1597 
1598 /*
1599  * Can't use gss_indicate_mechs by itself to get available mechs for
1600  * SPNEGO because it will also return the SPNEGO mech and we do not
1601  * want to consider SPNEGO as an available security mech for
1602  * negotiation. For this reason, get_available_mechs will return
1603  * all available mechs except SPNEGO.
1604  *
1605  * If a ptr to a creds list is given, this function will attempt
1606  * to acquire creds for the creds given and trim the list of
1607  * returned mechanisms to only those for which creds are valid.
1608  *
1609  */
1610 static OM_uint32
1611 get_available_mechs(OM_uint32 *minor_status,
1612         gss_name_t name, gss_cred_usage_t usage,
1613         gss_cred_id_t *creds, gss_OID_set *rmechs)
1614 {
1615         int             i;
1616         int             found = 0;
1617         OM_uint32 stat = GSS_S_COMPLETE;
1618         gss_OID_set mechs, goodmechs;
1619 
1620         stat = gss_indicate_mechs(minor_status, &mechs);
1621 
1622         if (stat != GSS_S_COMPLETE) {
1623                 return (stat);
1624         }
1625 
1626         stat = gss_create_empty_oid_set(minor_status, rmechs);
1627 
1628         if (stat != GSS_S_COMPLETE) {
1629                 (void) gss_release_oid_set(minor_status, &mechs);
1630                 return (stat);
1631         }
1632 
1633         for (i = 0; i < mechs->count && stat == GSS_S_COMPLETE; i++) {
1634                 if ((mechs->elements[i].length
1635                     != spnego_mechanism.mech_type.length) ||
1636                     memcmp(mechs->elements[i].elements,
1637                     spnego_mechanism.mech_type.elements,
1638                     spnego_mechanism.mech_type.length)) {
1639 
1640                         stat = gss_add_oid_set_member(minor_status,
1641                             &mechs->elements[i], rmechs);
1642                         if (stat == GSS_S_COMPLETE)
1643                                 found++;
1644                 }
1645         }
1646 
1647         /*
1648          * If the caller wanted a list of creds returned,
1649          * trim the list of mechanisms down to only those
1650          * for which the creds are valid.
1651          */
1652         if (found > 0 && stat == GSS_S_COMPLETE && creds != NULL) {
1653                 stat = gss_acquire_cred(minor_status,
1654                     name, NULL, *rmechs, usage, creds,
1655                     &goodmechs, NULL);
1656 
1657                 /*
1658                  * Drop the old list in favor of the new
1659                  * "trimmed" list.
1660                  */
1661                 (void) gss_release_oid_set(minor_status, rmechs);
1662                 if (stat == GSS_S_COMPLETE) {
1663                         (void) gss_copy_oid_set(minor_status,
1664                             goodmechs, rmechs);
1665                         (void) gss_release_oid_set(minor_status, &goodmechs);
1666                 }
1667         }
1668 
1669         (void) gss_release_oid_set(minor_status, &mechs);
1670         if (found == 0 || stat != GSS_S_COMPLETE) {
1671                 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
1672                 if (stat == GSS_S_COMPLETE)
1673                         stat = GSS_S_FAILURE;
1674         }
1675 
1676         return (stat);
1677 }
1678 
1679 /* following are token creation and reading routines */
1680 
1681 /*
1682  * If buff_in is not pointing to a MECH_OID, then return NULL and do not
1683  * advance the buffer, otherwise, decode the mech_oid from the buffer and
1684  * place in gss_OID.
1685  */
1686 static gss_OID
1687 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
1688 {
1689         OM_uint32       status;
1690         gss_OID_desc    toid;
1691         gss_OID         mech_out = NULL;
1692         uchar_t         *start, *end;
1693 
1694         if (length < 1 || **buff_in != MECH_OID)
1695                 return (NULL);
1696 
1697         start = *buff_in;
1698         end = start + length;
1699 
1700         (*buff_in)++;
1701         toid.length = *(*buff_in)++;
1702 
1703         if ((*buff_in + toid.length) > end)
1704                 return (NULL);
1705 
1706         toid.elements = *buff_in;
1707         *buff_in += toid.length;
1708 
1709         status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
1710 
1711         if (status != GSS_S_COMPLETE)
1712                 mech_out = NULL;
1713 
1714         return (mech_out);
1715 }
1716 
1717 /*
1718  * der encode the given mechanism oid into buf_out, advancing the
1719  * buffer pointer.
1720  */
1721 
1722 static int
1723 put_mech_oid(unsigned char **buf_out, gss_OID_desc *mech, int buflen)
1724 {
1725         if (buflen < mech->length + 2)
1726                 return (-1);
1727         *(*buf_out)++ = MECH_OID;
1728         *(*buf_out)++ = (unsigned char) mech->length;
1729         memcpy((void *)(*buf_out), mech->elements, mech->length);
1730         *buf_out += mech->length;
1731         return (0);
1732 }
1733 
1734 /*
1735  * verify that buff_in points to an octet string, if it does not,
1736  * return NULL and don't advance the pointer. If it is an octet string
1737  * decode buff_in into a gss_buffer_t and return it, advancing the
1738  * buffer pointer.
1739  */
1740 static gss_buffer_t
1741 get_input_token(unsigned char **buff_in, int buff_length)
1742 {
1743         gss_buffer_t input_token;
1744         unsigned int bytes;
1745 
1746         if (**buff_in != OCTET_STRING)
1747                 return (NULL);
1748 
1749         (*buff_in)++;
1750         input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
1751 
1752         if (input_token == NULL)
1753                 return (NULL);
1754 
1755         input_token->length = get_der_length(buff_in, buff_length, &bytes);
1756         if ((int)input_token->length == -1) {
1757                 free(input_token);
1758                 return (NULL);
1759         }
1760         input_token->value = malloc(input_token->length);
1761 
1762         if (input_token->value == NULL) {
1763                 free(input_token);
1764                 return (NULL);
1765         }
1766 
1767         (void) memcpy(input_token->value, *buff_in, input_token->length);
1768         *buff_in += input_token->length;
1769         return (input_token);
1770 }
1771 
1772 /*
1773  * verify that the input token length is not 0. If it is, just return.
1774  * If the token length is greater than 0, der encode as an octet string
1775  * and place in buf_out, advancing buf_out.
1776  */
1777 
1778 static int
1779 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
1780                 int buflen)
1781 {
1782         int ret;
1783 
1784         /* if token length is 0, we do not want to send */
1785         if (input_token->length == 0)
1786                 return (0);
1787 
1788         if (input_token->length > buflen)
1789                 return (-1);
1790 
1791         *(*buf_out)++ = OCTET_STRING;
1792         if ((ret = put_der_length(input_token->length, buf_out,
1793             input_token->length)))
1794                 return (ret);
1795         TWRITE_STR(*buf_out, input_token->value, ((int)input_token->length));
1796         return (0);
1797 }
1798 
1799 /*
1800  * verify that buff_in points to a sequence of der encoding. The mech
1801  * set is the only sequence of encoded object in the token, so if it is
1802  * a sequence of encoding, decode the mechset into a gss_OID_set and
1803  * return it, advancing the buffer pointer.
1804  */
1805 static gss_OID_set
1806 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, int buff_length)
1807 {
1808         gss_OID_set returned_mechSet;
1809         OM_uint32 major_status;
1810         OM_uint32 length;
1811         OM_uint32 bytes;
1812         OM_uint32 set_length;
1813         uchar_t         *start;
1814         int i;
1815 
1816         if (**buff_in != SEQUENCE_OF)
1817                 return (NULL);
1818 
1819         start = *buff_in;
1820         (*buff_in)++;
1821 
1822         length = get_der_length(buff_in, buff_length, &bytes);
1823 
1824         major_status = gss_create_empty_oid_set(minor_status,
1825             &returned_mechSet);
1826         if (major_status != GSS_S_COMPLETE)
1827                 return (NULL);
1828 
1829         for (set_length = 0, i = 0; set_length < length; i++) {
1830                 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
1831                     buff_length - (*buff_in - start));
1832                 if (temp != NULL) {
1833                         major_status = gss_add_oid_set_member(minor_status,
1834                             temp, &returned_mechSet);
1835                         if (major_status == GSS_S_COMPLETE) {
1836                                 set_length +=
1837                                     returned_mechSet->elements[i].length +2;
1838                                 generic_gss_release_oid(minor_status, &temp);
1839                         }
1840                 }
1841         }
1842 
1843         return (returned_mechSet);
1844 }
1845 
1846 /*
1847  * der encode the passed mechSet and place it into buf_out,
1848  * advancing the buffer pointer.
1849  */
1850 static int
1851 put_mech_set(uchar_t **buf_out, gss_OID_set mechSet, int buflen)
1852 {
1853         int i, ret;
1854         OM_uint32 length = 0;
1855         uchar_t *start;
1856 
1857         if (buf_out == NULL || *buf_out == NULL)
1858                 return (-1);
1859 
1860         start = *buf_out;
1861 
1862         *(*buf_out)++ = SEQUENCE_OF;
1863 
1864         for (i = 0; i < mechSet->count; i++) {
1865                 /*
1866                  * Mech OID ASN.1 size = 2 + length.
1867                  * 1 = 0x06, 1 for length of OID
1868                  * typically, less than 128, so only 1 byte needed.
1869                  */
1870                 length += 1 + der_length_size(mechSet->elements[i].length) +
1871                     mechSet->elements[i].length;
1872         }
1873         if (length > (buflen-1))
1874                 return (-1);
1875 
1876         if (put_der_length(length, buf_out, buflen-1) < 0)
1877                 return (-1);
1878 
1879         for (i = 0; i < mechSet->count; i++) {
1880                 if ((ret = put_mech_oid(buf_out, &mechSet->elements[i],
1881                     buflen - (int)(*buf_out      - start))))
1882                         return (ret);
1883         }
1884         return (0);
1885 }
1886 
1887 /*
1888  * Verify that buff_in is pointing to a BIT_STRING with the correct
1889  * length and padding for the req_flags. If it is, decode req_flags
1890  * and return them, otherwise, return NULL.
1891  */
1892 static OM_uint32
1893 get_req_flags(unsigned char **buff_in, int *bodysize, OM_uint32 *req_flags)
1894 {
1895         int len;
1896         uchar_t *start = *buff_in;
1897 
1898         /* It is OK if no ReqFlags data is sent. */
1899         if (**buff_in != (CONTEXT | 0x01))
1900                 return (0);
1901 
1902         /* If they are sent, make sure the fields are correct. */
1903         if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1904             *bodysize, &len) < 0)
1905                 return (ACCEPT_DEFECTIVE_TOKEN);
1906 
1907         /* We don't care what the flags are. */
1908         (*buff_in) += len;
1909 
1910         /* Don't return any flags, this field is useless */
1911         *req_flags = 0;
1912 
1913         *bodysize -= *buff_in - start;
1914         return (0);
1915 }
1916 
1917 /*
1918  * get the negotiation results, decoding the ENUMERATED type result
1919  * from the buffer, advancing the buffer pointer.
1920  */
1921 static OM_uint32
1922 get_negResult(unsigned char **buff_in, int bodysize)
1923 {
1924         unsigned char *iptr = *buff_in;
1925         int len;
1926         unsigned int bytes;
1927         OM_uint32 result;
1928         /*
1929          * Verify that the data is ASN.1 encoded correctly
1930          */
1931         if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1932             bodysize, &len) < 0)
1933                 return (ACCEPT_DEFECTIVE_TOKEN);
1934 
1935         if (*(*buff_in)++ == SEQUENCE) {
1936                 if ((len = get_der_length(buff_in,
1937                     bodysize - (*buff_in - iptr), &bytes)) < 0)
1938                         return (ACCEPT_DEFECTIVE_TOKEN);
1939         } else {
1940                 return (ACCEPT_INCOMPLETE);
1941         }
1942 
1943         /*
1944          * if we find an octet string, we need to return
1945          * incomplete so that we process the token correctly.
1946          * Anything else unexpected, we reject.
1947          */
1948         if (*(*buff_in)++ == CONTEXT) {
1949                 if ((len = get_der_length(buff_in, bodysize -
1950                     (*buff_in - iptr), &bytes)) < 0)
1951                         return (ACCEPT_DEFECTIVE_TOKEN);
1952         } else {
1953                 return (ACCEPT_INCOMPLETE);
1954         }
1955 
1956         if (*(*buff_in) == OCTET_STRING)
1957                 return (ACCEPT_INCOMPLETE);
1958 
1959         if (*(*buff_in)++ != ENUMERATED)
1960                 return (ACCEPT_DEFECTIVE_TOKEN);
1961 
1962         if (*(*buff_in)++ != ENUMERATION_LENGTH)
1963                 return (ACCEPT_DEFECTIVE_TOKEN);
1964 
1965         /*
1966          * Save the result byte to return later.
1967          * This is the result
1968          */
1969         result = (OM_uint32)*(*buff_in)++;
1970 
1971         if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1972             bodysize - (*buff_in - iptr), &len) < 0)
1973                 result = ACCEPT_DEFECTIVE_TOKEN;
1974 
1975         return (result);
1976 }
1977 
1978 /*
1979  * der encode the passed negResults as an ENUMERATED type and
1980  * place it in buf_out, advancing the buffer.
1981  */
1982 
1983 static int
1984 put_negResult(uchar_t **buf_out, OM_uint32 negResult, int buflen)
1985 {
1986         if (buflen < 3)
1987                 return (-1);
1988         *(*buf_out)++ = ENUMERATED;
1989         *(*buf_out)++ = ENUMERATION_LENGTH;
1990         *(*buf_out)++ = (unsigned char) negResult;
1991         return (0);
1992 }
1993 
1994 /*
1995  * This routine compares the recieved mechset to the mechset that
1996  * this server can support. It looks sequentially through the mechset
1997  * and the first one that matches what the server can support is
1998  * chosen as the negotiated mechanism. If one is found, negResult
1999  * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult
2000  * is set to REJECT. Also, for purposes of determining latter behavior,
2001  * the flag, firstMech is used to indicate if the chosen mechanism is the
2002  * first of the mechset or not.
2003  */
2004 static gss_OID
2005 negotiate_mech_type(OM_uint32 *minor_status,
2006         gss_OID_set supported_mechSet,
2007         gss_OID_set mechset,
2008         OM_uint32 *negResult,
2009         bool_t *firstMech)
2010 {
2011         gss_OID returned_mech;
2012         OM_uint32 status;
2013         int present;
2014         int i;
2015 
2016         for (i = 0; i < mechset->count; i++) {
2017                 gss_test_oid_set_member(minor_status, &mechset->elements[i],
2018                     supported_mechSet, &present);
2019                 if (present == TRUE) {
2020                         *negResult = ACCEPT_COMPLETE;
2021 
2022                         if (i == 0)
2023                                 *firstMech = TRUE;
2024                         else
2025                                 *firstMech = FALSE;
2026 
2027                         status = generic_gss_copy_oid(minor_status,
2028                             &mechset->elements[i], &returned_mech);
2029 
2030                         if (status != GSS_S_COMPLETE) {
2031                                 *negResult = REJECT;
2032                                 return (NULL);
2033                         }
2034 
2035                         return (returned_mech);
2036                 }
2037         }
2038 
2039         *negResult = REJECT;
2040         return (NULL);
2041 }
2042 
2043 /*
2044  * the next two routines make a token buffer suitable for
2045  * spnego_gss_display_status. These currently take the string
2046  * in name and place it in the token. Eventually, if
2047  * spnego_gss_display_status returns valid error messages,
2048  * these routines will be changes to return the error string.
2049  */
2050 static spnego_token_t
2051 make_spnego_token(char *name)
2052 {
2053         spnego_token_t token;
2054 
2055         token = (spnego_token_t)malloc(strlen(name)+1);
2056 
2057         if (token == NULL)
2058                 return (NULL);
2059         strcpy(token, name);
2060         return (token);
2061 }
2062 
2063 static gss_buffer_desc
2064 make_err_msg(char *name)
2065 {
2066         gss_buffer_desc buffer;
2067 
2068         if (name == NULL) {
2069                 buffer.length = 0;
2070                 buffer.value = NULL;
2071         } else {
2072                 buffer.length = strlen(name)+1;
2073                 buffer.value = make_spnego_token(name);
2074         }
2075 
2076         return (buffer);
2077 }
2078 
2079 /*
2080  * Create the client side spnego token passed back to gss_init_sec_context
2081  * and eventually up to the application program and over to the server.
2082  *
2083  * Use DER rules, definite length method per RFC 2478
2084  */
2085 static int
2086 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
2087         gss_OID_set mechSet,
2088         gss_buffer_t data, send_token_flag sendtoken,
2089         gss_buffer_t outbuf)
2090 {
2091         OM_uint32 status, minor_stat;
2092         int tlen, dataLen = 0, ret = 0;
2093         int MechSetLen = 0;
2094         int negTokenInitSize = 0;
2095         int i;
2096         unsigned char *t;
2097         unsigned char *ptr;
2098         unsigned char *MechListPtr = NULL;
2099         gss_buffer_desc MICbuff;
2100 
2101         if (outbuf == GSS_C_NO_BUFFER)
2102                 return (-1);
2103 
2104         outbuf->length = 0;
2105         outbuf->value = NULL;
2106 
2107         /* calculate the data length */
2108 
2109         /* no token generated if sendtoken is not init or cont */
2110         if ((sendtoken < INIT_TOKEN_SEND) ||
2111             (sendtoken > CONT_TOKEN_SEND)) {
2112                 return (-1);
2113         }
2114 
2115         /*
2116          * if this is the init token, we will send the mechset
2117          * so include it's length.
2118          */
2119         if (sendtoken == INIT_TOKEN_SEND) {
2120                 /*
2121                  * Count bytes for the mechSet data
2122                  * Encoded in final output as:
2123                  * 0xa0 [DER LEN] 0x30 [DER LEN] [DATA]
2124                  */
2125                 for (i = 0; i < mechSet->count; i++)
2126                         MechSetLen += 1 +
2127                             der_length_size(mechSet->elements[i].length) +
2128                             mechSet->elements[i].length;
2129 
2130                 MechSetLen += 1 + der_length_size(MechSetLen);
2131                 dataLen += 1 + der_length_size(MechSetLen) + MechSetLen;
2132 
2133                 MechListPtr = (uchar_t *)malloc(dataLen);
2134                 ptr = (uchar_t *)MechListPtr;
2135 
2136                 if (MechListPtr != NULL) {
2137                         if ((ret = put_mech_set(&ptr, mechSet, dataLen))) {
2138                                 free(MechListPtr);
2139                                 goto errout;
2140                         }
2141                 } else {
2142                         ret = -1;
2143                         goto errout;
2144                 }
2145 
2146                 /*
2147                  * The MIC is done over the DER encoded mechSet.
2148                  */
2149                 spnego_ctx->DER_mechTypes.value = MechListPtr;
2150                 spnego_ctx->DER_mechTypes.length = ptr - MechListPtr;
2151 
2152                 /*
2153                  * Only send the MIC if we are *NOT* interoperating
2154                  * with Microsoft.
2155                  */
2156                 if (!spnego_ctx->MS_Interop) {
2157                         /*
2158                          * MechListMIC = DER(MIC(DER(MechSet)))
2159                          * Calculate it here, stick it in the buffer later.
2160                          */
2161                         MICbuff.length = 0;
2162                         MICbuff.value = NULL;
2163                         status = gss_get_mic(&minor_stat,
2164                             spnego_ctx->ctx_handle, GSS_C_QOP_DEFAULT,
2165                             &spnego_ctx->DER_mechTypes, &MICbuff);
2166                         /*
2167                          * If the MIC operation succeeded, use it,
2168                          * but don't fail if it did not succeed.
2169                          * MIC is optional and is not supported by all
2170                          * mechanisms all the time.
2171                          */
2172                         if (status  == GSS_S_COMPLETE) {
2173                                 /*
2174                                  * Encoded in final output as:
2175                                  * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
2176                                  *      --s--   -------tlen------------
2177                                  */
2178                                 tlen = 1 + der_length_size(MICbuff.length) +
2179                                     MICbuff.length;
2180 
2181                                 dataLen += 1 + der_length_size(tlen) + tlen;
2182                         }
2183                 }
2184         }
2185 
2186         /*
2187          * If a token from gss_init_sec_context exists,
2188          * add the length of the token + the ASN.1 overhead
2189          */
2190         if (data != NULL) {
2191                 /*
2192                  * Encoded in final output as:
2193                  * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
2194                  * -----s--------|--------s2----------
2195                  */
2196                 tlen = 1 + der_length_size(data->length) + data->length;
2197 
2198                 dataLen += 1 + der_length_size(tlen) + tlen;
2199         }
2200 
2201         /*
2202          * Add size of DER encoding
2203          * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
2204          *   0x30 [DER_LEN] [data]
2205          *
2206          */
2207         dataLen += 1 + der_length_size(dataLen);
2208 
2209         /*
2210          * negTokenInitSize indicates the bytes needed to
2211          * hold the ASN.1 encoding of the entire NegTokenInit
2212          * SEQUENCE.
2213          * 0xa0 [DER_LEN] + data
2214          *
2215          */
2216         negTokenInitSize = dataLen;
2217 
2218         tlen = g_token_size((gss_OID)gss_mech_spnego,
2219             negTokenInitSize + 1 +
2220             der_length_size(negTokenInitSize));
2221 
2222         t = (unsigned char *) malloc(tlen);
2223 
2224         if (t == NULL) {
2225                 return (-1);
2226         }
2227 
2228         ptr = t;
2229 
2230         /* create the message */
2231         if ((ret = g_make_token_header((gss_OID)gss_mech_spnego,
2232             1 + negTokenInitSize + der_length_size(negTokenInitSize),
2233             &ptr, tlen)))
2234                 goto errout;
2235 
2236         if (sendtoken == INIT_TOKEN_SEND) {
2237                 *ptr++ = CONTEXT; /* NegotiationToken identifier */
2238                 if ((ret = put_der_length(negTokenInitSize, &ptr, tlen)))
2239                         goto errout;
2240 
2241                 *ptr++ = SEQUENCE;
2242                 if ((ret = put_der_length(negTokenInitSize - 4, &ptr,
2243                     tlen - (int)(ptr-t))))
2244                         goto errout;
2245 
2246                 *ptr++ = CONTEXT; /* MechTypeList identifier */
2247                 if ((ret = put_der_length(spnego_ctx->DER_mechTypes.length,
2248                     &ptr, tlen - (int)(ptr-t))))
2249                         goto errout;
2250 
2251                 /* We already encoded the MechSetList */
2252                 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
2253                     spnego_ctx->DER_mechTypes.length);
2254 
2255                 ptr += spnego_ctx->DER_mechTypes.length;
2256 
2257         }
2258 
2259         if (data != NULL) {
2260                 *ptr++ = CONTEXT | 0x02;
2261                 if ((ret = put_der_length(data->length + 4,
2262                     &ptr, tlen - (int)(ptr - t))))
2263                         goto errout;
2264 
2265                 if ((ret = put_input_token(&ptr, data,
2266                     tlen - (int)(ptr - t))))
2267                         goto errout;
2268 
2269                 /*
2270                  * We are in "optimistic" mode if we send a token
2271                  * with out initial message.
2272                  */
2273                 spnego_ctx->optimistic = (sendtoken == INIT_TOKEN_SEND);
2274         }
2275 
2276         if (!spnego_ctx->MS_Interop && MICbuff.length > 0) {
2277                 /* We already calculated the MechListMIC above */
2278                 int len = 1 +  der_length_size(MICbuff.length) + MICbuff.length;
2279                 *ptr++ = CONTEXT | 0x03;
2280                 if ((ret = put_der_length(len, &ptr, tlen - (int)(ptr - t))))
2281                         goto errout;
2282 
2283                 if ((ret = put_input_token(&ptr, &MICbuff,
2284                     tlen - (int)(ptr - t))))
2285                         goto errout;
2286 
2287                 (void) gss_release_buffer(&minor_stat, &MICbuff);
2288         }
2289 
2290 errout:
2291         if (ret != 0) {
2292                 if (t)
2293                         free(t);
2294                 t = NULL;
2295                 tlen = 0;
2296         }
2297         outbuf->length = tlen;
2298         outbuf->value = (void *) t;
2299 
2300         return (ret);
2301 }
2302 
2303 /*
2304  * create the server side spnego token passed back to
2305  * gss_accept_sec_context and eventually up to the application program
2306  * and over to the client.
2307  */
2308 static int
2309 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
2310                         gss_buffer_t data, gss_buffer_t mechListMIC,
2311                         send_token_flag sendtoken, int MS_Flag,
2312                         gss_buffer_t outbuf)
2313 {
2314         int tlen;
2315         int ret;
2316         int NegTokenTargSize;
2317         int negresultTokenSize;
2318         int NegTokenSize;
2319         int rspTokenSize;
2320         int micTokenSize;
2321         int dataLen = 0;
2322         unsigned char *t;
2323         unsigned char *ptr;
2324 
2325         if (outbuf == GSS_C_NO_BUFFER)
2326                 return (GSS_S_DEFECTIVE_TOKEN);
2327 
2328         outbuf->length = 0;
2329         outbuf->value = NULL;
2330 
2331         /*
2332          * ASN.1 encoding of the negResult
2333          * ENUMERATED type is 3 bytes
2334          *  ENUMERATED TAG, Length, Value,
2335          * Plus 2 bytes for the CONTEXT id and length.
2336          */
2337         negresultTokenSize = 5;
2338 
2339         /*
2340          * calculate data length
2341          *
2342          * If this is the initial token, include length of
2343          * mech_type and the negotiation result fields.
2344          */
2345         if (sendtoken == INIT_TOKEN_SEND) {
2346 
2347                 if (mech_wanted != NULL) {
2348                         int mechlistTokenSize;
2349                         /*
2350                          * 1 byte for the CONTEXT ID(0xa0),
2351                          * 1 byte for the OID ID(0x06)
2352                          * 1 byte for OID Length field
2353                          * Plus the rest... (OID Length, OID value)
2354                          */
2355                         mechlistTokenSize = 3 + mech_wanted->length +
2356                             der_length_size(mech_wanted->length);
2357 
2358                         dataLen = negresultTokenSize + mechlistTokenSize;
2359                 }
2360         } else {
2361                 /*
2362                  * If this is a response from a server, count
2363                  * the space needed for the negResult field.
2364                  * LENGTH(2) + ENUM(2) + result
2365                  */
2366                 dataLen = negresultTokenSize;
2367         }
2368         if (data != NULL && data->length > 0) {
2369                 /* Length of the inner token */
2370                 rspTokenSize = 1 + der_length_size(data->length) +
2371                     data->length;
2372 
2373                 dataLen += rspTokenSize;
2374 
2375                 /* Length of the outer token */
2376                 dataLen += 1 + der_length_size(rspTokenSize);
2377         }
2378         if (mechListMIC != NULL) {
2379 
2380                 /* Length of the inner token */
2381                 micTokenSize = 1 + der_length_size(mechListMIC->length) +
2382                     mechListMIC->length;
2383 
2384                 dataLen += micTokenSize;
2385 
2386                 /* Length of the outer token */
2387                 dataLen += 1 + der_length_size(micTokenSize);
2388         } else if (data != NULL && data->length > 0 && MS_Flag) {
2389                 dataLen += rspTokenSize;
2390                 dataLen += 1 + der_length_size(rspTokenSize);
2391         }
2392 
2393         /*
2394          * Add size of DER encoded:
2395          * NegTokenTarg [ SEQUENCE ] of
2396          *    NegResult[0] ENUMERATED {
2397          *      accept_completed(0),
2398          *      accept_incomplete(1),
2399          *      reject(2) }
2400          *    supportedMech [1] MechType OPTIONAL,
2401          *    responseToken [2] OCTET STRING OPTIONAL,
2402          *    mechListMIC   [3] OCTET STRING OPTIONAL
2403          *
2404          * size = data->length + MechListMic + SupportedMech len +
2405          *      Result Length + ASN.1 overhead
2406          */
2407         NegTokenTargSize = dataLen;
2408         dataLen += 1 + der_length_size(NegTokenTargSize);
2409 
2410         /*
2411          * NegotiationToken [ CHOICE ]{
2412          *    negTokenInit  [0]  NegTokenInit,
2413          *    negTokenTarg  [1]  NegTokenTarg }
2414          */
2415         NegTokenSize = dataLen;
2416         dataLen += 1 + der_length_size(NegTokenSize);
2417 
2418         tlen = dataLen;
2419         t = (unsigned char *) malloc(tlen);
2420 
2421         if (t == NULL) {
2422                 ret = GSS_S_DEFECTIVE_TOKEN;
2423                 goto errout;
2424         }
2425 
2426         ptr = t;
2427 
2428         if (sendtoken == INIT_TOKEN_SEND ||
2429             sendtoken == ERROR_TOKEN_SEND) {
2430                 /*
2431                  * Indicate that we are sending CHOICE 1
2432                  * (NegTokenTarg)
2433                  */
2434                 *ptr++ = CONTEXT | 0x01;
2435                 if ((ret = put_der_length(NegTokenSize, &ptr, dataLen))) {
2436                         ret = GSS_S_DEFECTIVE_TOKEN;
2437                         goto errout;
2438                 }
2439 
2440                 *ptr++ = SEQUENCE;
2441                 if ((ret = put_der_length(NegTokenTargSize, &ptr,
2442                     tlen - (int)(ptr-t)))) {
2443                         ret = GSS_S_DEFECTIVE_TOKEN;
2444                         goto errout;
2445                 }
2446 
2447                 /*
2448                  * First field of the NegTokenTarg SEQUENCE
2449                  * is the ENUMERATED NegResult.
2450                  */
2451                 *ptr++ = CONTEXT;
2452                 if ((ret = put_der_length(3, &ptr,
2453                     tlen - (int)(ptr-t)))) {
2454                         ret = GSS_S_DEFECTIVE_TOKEN;
2455                         goto errout;
2456                 }
2457                 if ((ret = put_negResult(&ptr, status,
2458                     tlen - (int)(ptr - t)))) {
2459                         ret = GSS_S_DEFECTIVE_TOKEN;
2460                         goto errout;
2461                 }
2462 
2463                 if (sendtoken != ERROR_TOKEN_SEND && mech_wanted != NULL) {
2464                         /*
2465                          * Next, is the Supported MechType
2466                          */
2467                         *ptr++ = CONTEXT | 0x01;
2468                         if ((ret = put_der_length(mech_wanted->length + 2,
2469                             &ptr, tlen - (int)(ptr - t)))) {
2470                                 ret = GSS_S_DEFECTIVE_TOKEN;
2471                                 goto errout;
2472                         }
2473                         if ((ret = put_mech_oid(&ptr, mech_wanted,
2474                             tlen - (int)(ptr - t)))) {
2475                                 ret = GSS_S_DEFECTIVE_TOKEN;
2476                                 goto errout;
2477                         }
2478                 }
2479         }
2480 
2481         if (data != NULL && data->length > 0) {
2482                 *ptr++ = CONTEXT | 0x02;
2483                 if ((ret = put_der_length(rspTokenSize, &ptr,
2484                     tlen - (int)(ptr - t)))) {
2485                         ret = GSS_S_DEFECTIVE_TOKEN;
2486                         goto errout;
2487                 }
2488                 if ((ret = put_input_token(&ptr, data,
2489                     tlen - (int)(ptr - t)))) {
2490                         ret = GSS_S_DEFECTIVE_TOKEN;
2491                         goto errout;
2492                 }
2493         }
2494         if (mechListMIC != NULL) {
2495                 *ptr++ = CONTEXT | 0x03;
2496                 if ((ret = put_der_length(micTokenSize, &ptr,
2497                     tlen - (int)(ptr - t)))) {
2498                         ret = GSS_S_DEFECTIVE_TOKEN;
2499                         goto errout;
2500                 }
2501                 if ((ret = put_input_token(&ptr, mechListMIC,
2502                     tlen - (int)(ptr - t)))) {
2503                         ret = GSS_S_DEFECTIVE_TOKEN;
2504                         goto errout;
2505                 }
2506         } else if (data != NULL && data->length > 0 && MS_Flag) {
2507                 *ptr++ = CONTEXT | 0x03;
2508                 if ((ret = put_der_length(rspTokenSize, &ptr,
2509                     tlen - (int)(ptr - t)))) {
2510                         ret = GSS_S_DEFECTIVE_TOKEN;
2511                         goto errout;
2512                 }
2513                 if ((ret = put_input_token(&ptr, data,
2514                     tlen - (int)(ptr - t)))) {
2515                         ret = GSS_S_DEFECTIVE_TOKEN;
2516                 }
2517         }
2518 errout:
2519         if (ret != 0) {
2520                 if (t)
2521                         free(t);
2522         } else {
2523                 outbuf->length = ptr - t;
2524                 outbuf->value = (void *) t;
2525         }
2526 
2527         return (ret);
2528 }
2529 
2530 /* determine size of token */
2531 static int
2532 g_token_size(gss_OID mech, unsigned int body_size)
2533 {
2534         int hdrsize;
2535 
2536         /*
2537          * Initialize the header size to the
2538          * MECH_OID byte + the bytes needed to indicate the
2539          * length of the OID + the OID itself.
2540          *
2541          * 0x06 [MECHLENFIELD] MECHDATA
2542          */
2543         hdrsize = 1 + der_length_size(mech->length) + mech->length;
2544 
2545         /*
2546          * Now add the bytes needed for the initial header
2547          * token bytes:
2548          * 0x60 + [DER_LEN] + HDRSIZE
2549          */
2550         hdrsize += 1 + der_length_size(body_size + hdrsize);
2551 
2552         return (hdrsize + body_size);
2553 }
2554 
2555 /*
2556  * generate token header.
2557  *
2558  * Use DER Definite Length method per RFC2478
2559  * Use of indefinite length encoding will not be compatible
2560  * with Microsoft or others that actually follow the spec.
2561  */
2562 static int
2563 g_make_token_header(gss_OID mech,
2564         int body_size,
2565         unsigned char **buf,
2566         int totallen)
2567 {
2568         int hdrsize, ret = 0;
2569         unsigned char *p = *buf;
2570 
2571         hdrsize = 1 + der_length_size(mech->length) + mech->length;
2572 
2573         *(*buf)++ = HEADER_ID;
2574         if ((ret = put_der_length(hdrsize + body_size, buf, totallen)))
2575                 return (ret);
2576 
2577         *(*buf)++ = MECH_OID;
2578         if ((ret = put_der_length(mech->length, buf,
2579             totallen - (int)(p - *buf))))
2580                 return (ret);
2581         TWRITE_STR(*buf, mech->elements, ((int)mech->length));
2582         return (0);
2583 }
2584 
2585 static int
2586 g_get_tag_and_length(unsigned char **buf, uchar_t tag, int buflen, int *outlen)
2587 {
2588         unsigned char *ptr = *buf;
2589         int ret = -1; /* pessimists, assume failure ! */
2590         OM_uint32 encoded_len;
2591 
2592         if (buflen > 0 && *ptr == tag) {
2593                 ptr++;
2594                 *outlen = get_der_length(&ptr, buflen, &encoded_len);
2595                 if (*outlen < 0)
2596                         ret = *outlen;
2597                 if ((ptr + *outlen) > (*buf + buflen))
2598                         ret = -1;
2599                 else
2600                         ret = 0;
2601         }
2602 
2603         *buf = ptr;
2604         return (ret);
2605 }
2606 
2607 static int
2608 g_verify_neg_token_init(unsigned char **buf_in, int cur_size)
2609 {
2610         unsigned char *buf = *buf_in;
2611         unsigned char *endptr = buf + cur_size;
2612         int seqsize;
2613         int ret = 0;
2614         unsigned int bytes;
2615 
2616         /*
2617          * Verify this is a NegotiationToken type token
2618          * - check for a0(context specific identifier)
2619          * - get length and verify that enoughd ata exists
2620          */
2621         if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
2622                 return (G_BAD_TOK_HEADER);
2623 
2624         cur_size = seqsize; /* should indicate bytes remaining */
2625 
2626         /*
2627          * Verify the next piece, it should identify this as
2628          * a strucure of type NegTokenInit.
2629          */
2630         if (*buf++ == SEQUENCE) {
2631                 if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
2632                         return (G_BAD_TOK_HEADER);
2633                 /*
2634                  * Make sure we have the entire buffer as described
2635                  */
2636                 if (buf + seqsize > endptr)
2637                         return (G_BAD_TOK_HEADER);
2638         } else {
2639                 return (G_BAD_TOK_HEADER);
2640         }
2641 
2642         cur_size = seqsize; /* should indicate bytes remaining */
2643 
2644         /*
2645          * Verify that the first blob is a sequence of mechTypes
2646          */
2647         if (*buf++ == CONTEXT) {
2648                 if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
2649                         return (G_BAD_TOK_HEADER);
2650                 /*
2651                  * Make sure we have the entire buffer as described
2652                  */
2653                 if (buf + bytes > endptr)
2654                         return (G_BAD_TOK_HEADER);
2655         } else {
2656                 return (G_BAD_TOK_HEADER);
2657         }
2658 
2659         /*
2660          * At this point, *buf should be at the beginning of the
2661          * DER encoded list of mech types that are to be negotiated.
2662          */
2663         *buf_in = buf;
2664 
2665         return (ret);
2666 
2667 }
2668 
2669 /* verify token header. */
2670 static int
2671 g_verify_token_header(gss_OID mech,
2672         int *body_size,
2673         unsigned char **buf_in,
2674         int tok_type,
2675         int toksize)
2676 {
2677         unsigned char *buf = *buf_in;
2678         int seqsize;
2679         gss_OID_desc toid;
2680         int ret = 0;
2681         unsigned int bytes;
2682 
2683         if ((toksize -= 1) < 0)
2684                 return (G_BAD_TOK_HEADER);
2685 
2686         if (*buf++ != HEADER_ID)
2687                 return (G_BAD_TOK_HEADER);
2688 
2689         if ((seqsize = get_der_length(&buf, toksize, &bytes)) < 0)
2690                 return (G_BAD_TOK_HEADER);
2691 
2692         if ((seqsize + bytes) != toksize)
2693                 return (G_BAD_TOK_HEADER);
2694 
2695         if ((toksize -= 1) < 0)
2696                 return (G_BAD_TOK_HEADER);
2697 
2698 
2699         if (*buf++ != MECH_OID)
2700                 return (G_BAD_TOK_HEADER);
2701 
2702         if ((toksize -= 1) < 0)
2703                 return (G_BAD_TOK_HEADER);
2704 
2705         toid.length = *buf++;
2706 
2707         if ((toksize -= toid.length) < 0)
2708                 return (G_BAD_TOK_HEADER);
2709 
2710         toid.elements = buf;
2711         buf += toid.length;
2712 
2713         if (!g_OID_equal(&toid, mech))
2714                 ret = G_WRONG_MECH;
2715 
2716         /*
2717          * G_WRONG_MECH is not returned immediately because it's more important
2718          * to return G_BAD_TOK_HEADER if the token header is in fact bad
2719          */
2720         if ((toksize -= 2) < 0)
2721                 return (G_BAD_TOK_HEADER);
2722 
2723         if (!ret) {
2724                 *buf_in = buf;
2725                 *body_size = toksize;
2726         }
2727 
2728         return (ret);
2729 }