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