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