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