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