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