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