1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 2009 by the Massachusetts Institute of Technology. 4 * All Rights Reserved. 5 * 6 * Export of this software from the United States of America may 7 * require a specific license from the United States Government. 8 * It is the responsibility of any person or organization contemplating 9 * export to obtain such a license before exporting. 10 * 11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 12 * distribute this software and its documentation for any purpose and 13 * without fee is hereby granted, provided that the above copyright 14 * notice appear in all copies and that both that copyright notice and 15 * this permission notice appear in supporting documentation, and that 16 * the name of M.I.T. not be used in advertising or publicity pertaining 17 * to distribution of the software without specific, written prior 18 * permission. Furthermore if you modify this software you must label 19 * your software as modified software and not distribute it in such a 20 * fashion that it might be confused with the original M.I.T. software. 21 * M.I.T. makes no representations about the suitability of 22 * this software for any purpose. It is provided "as is" without express 23 * or implied warranty. 24 */ 25 #include "k5-int.h" 26 #include "k5-der.h" 27 #include "gssapiP_krb5.h" 28 29 /* 30 * IAKERB implementation 31 */ 32 33 enum iakerb_state { 34 IAKERB_AS_REQ, /* acquiring ticket with initial creds */ 35 IAKERB_TGS_REQ, /* acquiring ticket with TGT */ 36 IAKERB_AP_REQ /* hand-off to normal GSS AP-REQ exchange */ 37 }; 38 39 struct _iakerb_ctx_id_rec { 40 krb5_magic magic; /* KG_IAKERB_CONTEXT */ 41 krb5_context k5c; 42 gss_cred_id_t defcred; /* Initiator only */ 43 enum iakerb_state state; /* Initiator only */ 44 krb5_init_creds_context icc; /* Initiator only */ 45 krb5_tkt_creds_context tcc; /* Initiator only */ 46 gss_ctx_id_t gssc; 47 krb5_data conv; /* conversation for checksumming */ 48 unsigned int count; /* number of round trips */ 49 int initiate; 50 int established; 51 krb5_get_init_creds_opt *gic_opts; 52 }; 53 54 #define IAKERB_MAX_HOPS ( 16 /* MAX_IN_TKT_LOOPS */ + KRB5_REFERRAL_MAXHOPS ) 55 56 typedef struct _iakerb_ctx_id_rec iakerb_ctx_id_rec; 57 typedef iakerb_ctx_id_rec *iakerb_ctx_id_t; 58 59 /* 60 * Release an IAKERB context 61 */ 62 static void 63 iakerb_release_context(iakerb_ctx_id_t ctx) 64 { 65 OM_uint32 tmp; 66 67 if (ctx == NULL) 68 return; 69 70 krb5_gss_release_cred(&tmp, &ctx->defcred); 71 krb5_init_creds_free(ctx->k5c, ctx->icc); 72 krb5_tkt_creds_free(ctx->k5c, ctx->tcc); 73 krb5_gss_delete_sec_context(&tmp, &ctx->gssc, NULL); 74 krb5_free_data_contents(ctx->k5c, &ctx->conv); 75 krb5_get_init_creds_opt_free(ctx->k5c, ctx->gic_opts); 76 krb5_free_context(ctx->k5c); 77 free(ctx); 78 } 79 80 /* 81 * Create a IAKERB-FINISHED structure containing a checksum of 82 * the entire IAKERB exchange. 83 */ 84 krb5_error_code 85 iakerb_make_finished(krb5_context context, 86 krb5_key key, 87 const krb5_data *conv, 88 krb5_data **finished) 89 { 90 krb5_error_code code; 91 krb5_iakerb_finished iaf; 92 93 *finished = NULL; 94 95 memset(&iaf, 0, sizeof(iaf)); 96 97 if (key == NULL) 98 return KRB5KDC_ERR_NULL_KEY; 99 100 code = krb5_k_make_checksum(context, 0, key, KRB5_KEYUSAGE_IAKERB_FINISHED, 101 conv, &iaf.checksum); 102 if (code != 0) 103 return code; 104 105 code = encode_krb5_iakerb_finished(&iaf, finished); 106 107 krb5_free_checksum_contents(context, &iaf.checksum); 108 109 return code; 110 } 111 112 /* 113 * Verify a IAKERB-FINISHED structure submitted by the initiator 114 */ 115 krb5_error_code 116 iakerb_verify_finished(krb5_context context, 117 krb5_key key, 118 const krb5_data *conv, 119 const krb5_data *finished) 120 { 121 krb5_error_code code; 122 krb5_iakerb_finished *iaf; 123 krb5_boolean valid = FALSE; 124 125 if (key == NULL) 126 return KRB5KDC_ERR_NULL_KEY; 127 128 code = decode_krb5_iakerb_finished(finished, &iaf); 129 if (code != 0) 130 return code; 131 132 code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_IAKERB_FINISHED, 133 conv, &iaf->checksum, &valid); 134 if (code == 0 && valid == FALSE) 135 code = KRB5KRB_AP_ERR_BAD_INTEGRITY; 136 137 krb5_free_iakerb_finished(context, iaf); 138 139 return code; 140 } 141 142 /* 143 * Save a token for future checksumming. 144 */ 145 static krb5_error_code 146 iakerb_save_token(iakerb_ctx_id_t ctx, const gss_buffer_t token) 147 { 148 char *p; 149 150 p = realloc(ctx->conv.data, ctx->conv.length + token->length); 151 if (p == NULL) 152 return ENOMEM; 153 154 memcpy(p + ctx->conv.length, token->value, token->length); 155 ctx->conv.data = p; 156 ctx->conv.length += token->length; 157 158 return 0; 159 } 160 161 /* 162 * Parse a token into IAKERB-HEADER and KRB-KDC-REQ/REP 163 */ 164 static krb5_error_code 165 iakerb_parse_token(iakerb_ctx_id_t ctx, 166 int initialContextToken, 167 const gss_buffer_t token, 168 krb5_data *realm, 169 krb5_data **cookie, 170 krb5_data *request) 171 { 172 krb5_error_code code; 173 krb5_iakerb_header *iah = NULL; 174 unsigned int bodysize; 175 uint8_t *body; 176 int flags = 0; 177 krb5_data data; 178 struct k5input in, seq; 179 180 if (token == GSS_C_NO_BUFFER || token->length == 0) { 181 code = KRB5_BAD_MSIZE; 182 goto cleanup; 183 } 184 185 if (initialContextToken) 186 flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED; 187 188 body = token->value; 189 code = g_verify_token_header(gss_mech_iakerb, &bodysize, &body, 190 IAKERB_TOK_PROXY, token->length, flags); 191 if (code != 0) 192 goto cleanup; 193 194 /* Find the end of the DER sequence tag and decode it (with the tag) as the 195 * IAKERB jeader. */ 196 k5_input_init(&in, body, bodysize); 197 if (!k5_der_get_value(&in, 0x30, &seq)) { 198 code = ASN1_BAD_ID; 199 goto cleanup; 200 } 201 data = make_data(body, seq.ptr + seq.len - body); 202 code = decode_krb5_iakerb_header(&data, &iah); 203 if (code != 0) 204 goto cleanup; 205 206 if (realm != NULL) { 207 *realm = iah->target_realm; 208 iah->target_realm.data = NULL; 209 } 210 211 if (cookie != NULL) { 212 *cookie = iah->cookie; 213 iah->cookie = NULL; 214 } 215 216 /* The remainder of the token body is the request. */ 217 *request = make_data((uint8_t *)in.ptr, in.len); 218 assert(request->data + request->length == 219 (char *)token->value + token->length); 220 221 cleanup: 222 krb5_free_iakerb_header(ctx->k5c, iah); 223 224 return code; 225 } 226 227 /* 228 * Create a token from IAKERB-HEADER and KRB-KDC-REQ/REP 229 */ 230 static krb5_error_code 231 iakerb_make_token(iakerb_ctx_id_t ctx, 232 krb5_data *realm, 233 krb5_data *cookie, 234 krb5_data *request, 235 int initialContextToken, 236 gss_buffer_t token) 237 { 238 krb5_error_code code; 239 krb5_iakerb_header iah; 240 krb5_data *data = NULL; 241 char *p; 242 unsigned int tokenSize; 243 struct k5buf buf; 244 245 token->value = NULL; 246 token->length = 0; 247 248 /* 249 * Assemble the IAKERB-HEADER from the realm and cookie 250 */ 251 iah.target_realm = *realm; 252 iah.cookie = cookie; 253 254 code = encode_krb5_iakerb_header(&iah, &data); 255 if (code != 0) 256 goto cleanup; 257 258 /* 259 * Concatenate Kerberos request. 260 */ 261 p = realloc(data->data, data->length + request->length); 262 if (p == NULL) { 263 code = ENOMEM; 264 goto cleanup; 265 } 266 data->data = p; 267 268 if (request->length > 0) 269 memcpy(data->data + data->length, request->data, request->length); 270 data->length += request->length; 271 272 if (initialContextToken) 273 tokenSize = g_token_size(gss_mech_iakerb, data->length); 274 else 275 tokenSize = 2 + data->length; 276 277 token->value = gssalloc_malloc(tokenSize); 278 if (token->value == NULL) { 279 code = ENOMEM; 280 goto cleanup; 281 } 282 token->length = tokenSize; 283 k5_buf_init_fixed(&buf, token->value, token->length); 284 285 if (initialContextToken) { 286 g_make_token_header(&buf, gss_mech_iakerb, data->length, 287 IAKERB_TOK_PROXY); 288 } else { 289 k5_buf_add_uint16_be(&buf, IAKERB_TOK_PROXY); 290 } 291 k5_buf_add_len(&buf, data->data, data->length); 292 assert(buf.len == token->length); 293 294 cleanup: 295 krb5_free_data(ctx->k5c, data); 296 297 return code; 298 } 299 300 /* 301 * Parse the IAKERB token in input_token and send the contained KDC 302 * request to the KDC for the realm. 303 * 304 * Wrap the KDC reply in output_token. 305 */ 306 static krb5_error_code 307 iakerb_acceptor_step(iakerb_ctx_id_t ctx, 308 int initialContextToken, 309 const gss_buffer_t input_token, 310 gss_buffer_t output_token) 311 { 312 krb5_error_code code; 313 krb5_data request = empty_data(), reply = empty_data(); 314 krb5_data realm = empty_data(); 315 OM_uint32 tmp; 316 int tcp_only, use_primary; 317 krb5_ui_4 kdc_code; 318 319 output_token->length = 0; 320 output_token->value = NULL; 321 322 if (ctx->count >= IAKERB_MAX_HOPS) { 323 code = KRB5_KDC_UNREACH; 324 goto cleanup; 325 } 326 327 code = iakerb_parse_token(ctx, initialContextToken, input_token, &realm, 328 NULL, &request); 329 if (code != 0) 330 goto cleanup; 331 332 if (realm.length == 0 || request.length == 0) { 333 code = KRB5_BAD_MSIZE; 334 goto cleanup; 335 } 336 337 code = iakerb_save_token(ctx, input_token); 338 if (code != 0) 339 goto cleanup; 340 341 for (tcp_only = 0; tcp_only <= 1; tcp_only++) { 342 use_primary = 0; 343 code = krb5_sendto_kdc(ctx->k5c, &request, &realm, 344 &reply, &use_primary, tcp_only); 345 if (code == 0 && krb5_is_krb_error(&reply)) { 346 krb5_error *error; 347 348 code = decode_krb5_error(&reply, &error); 349 if (code != 0) 350 goto cleanup; 351 kdc_code = error->error; 352 krb5_free_error(ctx->k5c, error); 353 if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) { 354 krb5_free_data_contents(ctx->k5c, &reply); 355 reply = empty_data(); 356 continue; 357 } 358 } 359 break; 360 } 361 362 if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) { 363 krb5_error error; 364 365 memset(&error, 0, sizeof(error)); 366 if (code == KRB5_KDC_UNREACH) 367 error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE; 368 else if (code == KRB5_REALM_UNKNOWN) 369 error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND; 370 371 code = krb5_mk_error(ctx->k5c, &error, &reply); 372 if (code != 0) 373 goto cleanup; 374 } else if (code != 0) 375 goto cleanup; 376 377 code = iakerb_make_token(ctx, &realm, NULL, &reply, 0, output_token); 378 if (code != 0) 379 goto cleanup; 380 381 code = iakerb_save_token(ctx, output_token); 382 if (code != 0) 383 goto cleanup; 384 385 ctx->count++; 386 387 cleanup: 388 if (code != 0) 389 gss_release_buffer(&tmp, output_token); 390 /* request is a pointer into input_token, no need to free */ 391 krb5_free_data_contents(ctx->k5c, &realm); 392 krb5_free_data_contents(ctx->k5c, &reply); 393 394 return code; 395 } 396 397 /* 398 * Initialise the krb5_init_creds context for the IAKERB context 399 */ 400 static krb5_error_code 401 iakerb_init_creds_ctx(iakerb_ctx_id_t ctx, 402 krb5_gss_cred_id_t cred, 403 OM_uint32 time_req) 404 { 405 krb5_error_code code; 406 407 if (cred->iakerb_mech == 0) { 408 code = EINVAL; 409 goto cleanup; 410 } 411 412 assert(cred->name != NULL); 413 assert(cred->name->princ != NULL); 414 415 code = krb5_get_init_creds_opt_alloc(ctx->k5c, &ctx->gic_opts); 416 if (code != 0) 417 goto cleanup; 418 419 if (time_req != 0 && time_req != GSS_C_INDEFINITE) 420 krb5_get_init_creds_opt_set_tkt_life(ctx->gic_opts, time_req); 421 422 code = krb5_get_init_creds_opt_set_out_ccache(ctx->k5c, ctx->gic_opts, 423 cred->ccache); 424 if (code != 0) 425 goto cleanup; 426 427 code = krb5_init_creds_init(ctx->k5c, 428 cred->name->princ, 429 NULL, /* prompter */ 430 NULL, /* data */ 431 0, /* start_time */ 432 ctx->gic_opts, 433 &ctx->icc); 434 if (code != 0) 435 goto cleanup; 436 437 if (cred->password != NULL) { 438 code = krb5_init_creds_set_password(ctx->k5c, ctx->icc, 439 cred->password); 440 } else if (cred->client_keytab != NULL) { 441 code = krb5_init_creds_set_keytab(ctx->k5c, ctx->icc, 442 cred->client_keytab); 443 } else { 444 code = KRB5_KT_NOTFOUND; 445 } 446 if (code != 0) 447 goto cleanup; 448 449 cleanup: 450 return code; 451 } 452 453 /* 454 * Initialise the krb5_tkt_creds context for the IAKERB context 455 */ 456 static krb5_error_code 457 iakerb_tkt_creds_ctx(iakerb_ctx_id_t ctx, 458 krb5_gss_cred_id_t cred, 459 krb5_gss_name_t name, 460 OM_uint32 time_req) 461 462 { 463 krb5_error_code code; 464 krb5_creds creds; 465 krb5_timestamp now; 466 467 assert(cred->name != NULL); 468 assert(cred->name->princ != NULL); 469 470 memset(&creds, 0, sizeof(creds)); 471 472 creds.client = cred->name->princ; 473 creds.server = name->princ; 474 475 if (time_req != 0 && time_req != GSS_C_INDEFINITE) { 476 code = krb5_timeofday(ctx->k5c, &now); 477 if (code != 0) 478 goto cleanup; 479 480 creds.times.endtime = ts_incr(now, time_req); 481 } 482 483 if (cred->name->ad_context != NULL) { 484 code = krb5_authdata_export_authdata(ctx->k5c, 485 cred->name->ad_context, 486 AD_USAGE_TGS_REQ, 487 &creds.authdata); 488 if (code != 0) 489 goto cleanup; 490 } 491 492 code = krb5_tkt_creds_init(ctx->k5c, cred->ccache, &creds, 0, &ctx->tcc); 493 if (code != 0) 494 goto cleanup; 495 496 cleanup: 497 krb5_free_authdata(ctx->k5c, creds.authdata); 498 499 return code; 500 } 501 502 /* 503 * Parse the IAKERB token in input_token and process the KDC 504 * response. 505 * 506 * Emit the next KDC request, if any, in output_token. 507 */ 508 static krb5_error_code 509 iakerb_initiator_step(iakerb_ctx_id_t ctx, 510 krb5_gss_cred_id_t cred, 511 krb5_gss_name_t name, 512 OM_uint32 time_req, 513 const gss_buffer_t input_token, 514 gss_buffer_t output_token) 515 { 516 krb5_error_code code = 0; 517 krb5_data in = empty_data(), out = empty_data(), realm = empty_data(); 518 krb5_data *cookie = NULL; 519 OM_uint32 tmp; 520 unsigned int flags = 0; 521 krb5_ticket_times times; 522 523 output_token->length = 0; 524 output_token->value = NULL; 525 526 if (input_token != GSS_C_NO_BUFFER) { 527 code = iakerb_parse_token(ctx, 0, input_token, NULL, &cookie, &in); 528 if (code != 0) 529 goto cleanup; 530 531 code = iakerb_save_token(ctx, input_token); 532 if (code != 0) 533 goto cleanup; 534 } 535 536 switch (ctx->state) { 537 case IAKERB_AS_REQ: 538 if (ctx->icc == NULL) { 539 code = iakerb_init_creds_ctx(ctx, cred, time_req); 540 if (code != 0) 541 goto cleanup; 542 } 543 544 code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm, 545 &flags); 546 if (code != 0) { 547 if (cred->have_tgt) { 548 /* We were trying to refresh; keep going with current creds. */ 549 ctx->state = IAKERB_TGS_REQ; 550 krb5_clear_error_message(ctx->k5c); 551 } else { 552 goto cleanup; 553 } 554 } else if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) { 555 krb5_init_creds_get_times(ctx->k5c, ctx->icc, ×); 556 kg_cred_set_initial_refresh(ctx->k5c, cred, ×); 557 cred->expire = times.endtime; 558 559 krb5_init_creds_free(ctx->k5c, ctx->icc); 560 ctx->icc = NULL; 561 562 ctx->state = IAKERB_TGS_REQ; 563 } else 564 break; 565 in = empty_data(); 566 /* Done with AS request; fall through to TGS request. */ 567 case IAKERB_TGS_REQ: 568 if (ctx->tcc == NULL) { 569 code = iakerb_tkt_creds_ctx(ctx, cred, name, time_req); 570 if (code != 0) 571 goto cleanup; 572 } 573 574 code = krb5_tkt_creds_step(ctx->k5c, ctx->tcc, &in, &out, &realm, 575 &flags); 576 if (code != 0) 577 goto cleanup; 578 if (!(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) { 579 krb5_tkt_creds_get_times(ctx->k5c, ctx->tcc, ×); 580 cred->expire = times.endtime; 581 582 krb5_tkt_creds_free(ctx->k5c, ctx->tcc); 583 ctx->tcc = NULL; 584 585 ctx->state = IAKERB_AP_REQ; 586 } else 587 break; 588 /* Done with TGS request; fall through to AP request. */ 589 case IAKERB_AP_REQ: 590 break; 591 } 592 593 if (out.length != 0) { 594 assert(ctx->state != IAKERB_AP_REQ); 595 596 code = iakerb_make_token(ctx, &realm, cookie, &out, 597 (input_token == GSS_C_NO_BUFFER), 598 output_token); 599 if (code != 0) 600 goto cleanup; 601 602 /* Save the token for generating a future checksum */ 603 code = iakerb_save_token(ctx, output_token); 604 if (code != 0) 605 goto cleanup; 606 607 ctx->count++; 608 } 609 610 cleanup: 611 if (code != 0) 612 gss_release_buffer(&tmp, output_token); 613 krb5_free_data(ctx->k5c, cookie); 614 krb5_free_data_contents(ctx->k5c, &out); 615 krb5_free_data_contents(ctx->k5c, &realm); 616 617 return code; 618 } 619 620 /* 621 * Determine the starting IAKERB state for a context. If we already 622 * have a ticket, we may not need to do IAKERB at all. 623 */ 624 static krb5_error_code 625 iakerb_get_initial_state(iakerb_ctx_id_t ctx, 626 krb5_gss_cred_id_t cred, 627 krb5_gss_name_t target, 628 OM_uint32 time_req, 629 enum iakerb_state *state) 630 { 631 krb5_creds in_creds, *out_creds = NULL; 632 krb5_error_code code; 633 634 memset(&in_creds, 0, sizeof(in_creds)); 635 636 in_creds.client = cred->name->princ; 637 in_creds.server = target->princ; 638 639 if (cred->name->ad_context != NULL) { 640 code = krb5_authdata_export_authdata(ctx->k5c, 641 cred->name->ad_context, 642 AD_USAGE_TGS_REQ, 643 &in_creds.authdata); 644 if (code != 0) 645 goto cleanup; 646 } 647 648 if (time_req != 0 && time_req != GSS_C_INDEFINITE) { 649 krb5_timestamp now; 650 651 code = krb5_timeofday(ctx->k5c, &now); 652 if (code != 0) 653 goto cleanup; 654 655 in_creds.times.endtime = ts_incr(now, time_req); 656 } 657 658 /* Make an AS request if we have no creds or it's time to refresh them. */ 659 if (cred->expire == 0 || kg_cred_time_to_refresh(ctx->k5c, cred)) { 660 *state = IAKERB_AS_REQ; 661 code = 0; 662 goto cleanup; 663 } 664 665 code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED, cred->ccache, 666 &in_creds, &out_creds); 667 if (code == KRB5_CC_NOTFOUND || code == KRB5_CC_NOT_KTYPE) { 668 *state = cred->have_tgt ? IAKERB_TGS_REQ : IAKERB_AS_REQ; 669 code = 0; 670 } else if (code == 0) { 671 *state = IAKERB_AP_REQ; 672 krb5_free_creds(ctx->k5c, out_creds); 673 } 674 675 cleanup: 676 krb5_free_authdata(ctx->k5c, in_creds.authdata); 677 678 return code; 679 } 680 681 /* 682 * Allocate and initialise an IAKERB context 683 */ 684 static krb5_error_code 685 iakerb_alloc_context(iakerb_ctx_id_t *pctx, int initiate) 686 { 687 iakerb_ctx_id_t ctx; 688 krb5_error_code code; 689 690 *pctx = NULL; 691 692 ctx = k5alloc(sizeof(*ctx), &code); 693 if (ctx == NULL) 694 goto cleanup; 695 ctx->defcred = GSS_C_NO_CREDENTIAL; 696 ctx->magic = KG_IAKERB_CONTEXT; 697 ctx->state = IAKERB_AS_REQ; 698 ctx->count = 0; 699 ctx->initiate = initiate; 700 ctx->established = 0; 701 702 code = krb5_gss_init_context(&ctx->k5c); 703 if (code != 0) 704 goto cleanup; 705 706 *pctx = ctx; 707 708 cleanup: 709 if (code != 0) 710 iakerb_release_context(ctx); 711 712 return code; 713 } 714 715 OM_uint32 KRB5_CALLCONV 716 iakerb_gss_delete_sec_context(OM_uint32 *minor_status, 717 gss_ctx_id_t *context_handle, 718 gss_buffer_t output_token) 719 { 720 iakerb_ctx_id_t iakerb_ctx = (iakerb_ctx_id_t)*context_handle; 721 722 if (output_token != GSS_C_NO_BUFFER) { 723 output_token->length = 0; 724 output_token->value = NULL; 725 } 726 727 *minor_status = 0; 728 *context_handle = GSS_C_NO_CONTEXT; 729 iakerb_release_context(iakerb_ctx); 730 731 return GSS_S_COMPLETE; 732 } 733 734 static krb5_boolean 735 iakerb_is_iakerb_token(const gss_buffer_t token) 736 { 737 krb5_error_code code; 738 unsigned int bodysize = token->length; 739 unsigned char *ptr = token->value; 740 741 code = g_verify_token_header(gss_mech_iakerb, 742 &bodysize, &ptr, 743 IAKERB_TOK_PROXY, 744 token->length, 0); 745 746 return (code == 0); 747 } 748 749 static void 750 iakerb_make_exts(iakerb_ctx_id_t ctx, krb5_gss_ctx_ext_rec *exts) 751 { 752 memset(exts, 0, sizeof(*exts)); 753 754 if (ctx->conv.length != 0) 755 exts->iakerb.conv = &ctx->conv; 756 } 757 758 OM_uint32 KRB5_CALLCONV 759 iakerb_gss_accept_sec_context(OM_uint32 *minor_status, 760 gss_ctx_id_t *context_handle, 761 gss_cred_id_t verifier_cred_handle, 762 gss_buffer_t input_token, 763 gss_channel_bindings_t input_chan_bindings, 764 gss_name_t *src_name, 765 gss_OID *mech_type, 766 gss_buffer_t output_token, 767 OM_uint32 *ret_flags, 768 OM_uint32 *time_rec, 769 gss_cred_id_t *delegated_cred_handle) 770 { 771 OM_uint32 major_status = GSS_S_FAILURE; 772 OM_uint32 code; 773 iakerb_ctx_id_t ctx; 774 int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT); 775 776 if (initialContextToken) { 777 code = iakerb_alloc_context(&ctx, 0); 778 if (code != 0) 779 goto cleanup; 780 781 } else 782 ctx = (iakerb_ctx_id_t)*context_handle; 783 784 if (iakerb_is_iakerb_token(input_token)) { 785 if (ctx->gssc != GSS_C_NO_CONTEXT) { 786 /* We shouldn't get an IAKERB token now. */ 787 code = G_WRONG_TOKID; 788 major_status = GSS_S_DEFECTIVE_TOKEN; 789 goto cleanup; 790 } 791 code = iakerb_acceptor_step(ctx, initialContextToken, 792 input_token, output_token); 793 if (code == (OM_uint32)KRB5_BAD_MSIZE) 794 major_status = GSS_S_DEFECTIVE_TOKEN; 795 if (code != 0) 796 goto cleanup; 797 if (initialContextToken) { 798 *context_handle = (gss_ctx_id_t)ctx; 799 ctx = NULL; 800 } 801 if (src_name != NULL) 802 *src_name = GSS_C_NO_NAME; 803 if (ret_flags != NULL) 804 *ret_flags = 0; 805 if (time_rec != NULL) 806 *time_rec = 0; 807 if (delegated_cred_handle != NULL) 808 *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 809 major_status = GSS_S_CONTINUE_NEEDED; 810 } else { 811 krb5_gss_ctx_ext_rec exts; 812 813 iakerb_make_exts(ctx, &exts); 814 815 major_status = krb5_gss_accept_sec_context_ext(&code, 816 &ctx->gssc, 817 verifier_cred_handle, 818 input_token, 819 input_chan_bindings, 820 src_name, 821 NULL, 822 output_token, 823 ret_flags, 824 time_rec, 825 delegated_cred_handle, 826 &exts); 827 if (major_status == GSS_S_COMPLETE) 828 ctx->established = 1; 829 } 830 831 if (mech_type != NULL) 832 *mech_type = gss_mech_iakerb; 833 834 cleanup: 835 if (initialContextToken && GSS_ERROR(major_status)) { 836 iakerb_release_context(ctx); 837 *context_handle = GSS_C_NO_CONTEXT; 838 } 839 840 *minor_status = code; 841 return major_status; 842 } 843 844 OM_uint32 KRB5_CALLCONV 845 iakerb_gss_init_sec_context(OM_uint32 *minor_status, 846 gss_cred_id_t claimant_cred_handle, 847 gss_ctx_id_t *context_handle, 848 gss_name_t target_name, 849 gss_OID mech_type, 850 OM_uint32 req_flags, 851 OM_uint32 time_req, 852 gss_channel_bindings_t input_chan_bindings, 853 gss_buffer_t input_token, 854 gss_OID *actual_mech_type, 855 gss_buffer_t output_token, 856 OM_uint32 *ret_flags, 857 OM_uint32 *time_rec) 858 { 859 OM_uint32 major_status = GSS_S_FAILURE; 860 krb5_error_code code; 861 iakerb_ctx_id_t ctx; 862 krb5_gss_cred_id_t kcred; 863 krb5_gss_name_t kname; 864 krb5_boolean cred_locked = FALSE; 865 int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT); 866 867 if (initialContextToken) { 868 code = iakerb_alloc_context(&ctx, 1); 869 if (code != 0) { 870 *minor_status = code; 871 goto cleanup; 872 } 873 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { 874 major_status = iakerb_gss_acquire_cred(minor_status, NULL, 875 GSS_C_INDEFINITE, 876 GSS_C_NULL_OID_SET, 877 GSS_C_INITIATE, 878 &ctx->defcred, NULL, NULL); 879 if (GSS_ERROR(major_status)) 880 goto cleanup; 881 claimant_cred_handle = ctx->defcred; 882 } 883 } else { 884 ctx = (iakerb_ctx_id_t)*context_handle; 885 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) 886 claimant_cred_handle = ctx->defcred; 887 } 888 889 kname = (krb5_gss_name_t)target_name; 890 891 major_status = kg_cred_resolve(minor_status, ctx->k5c, 892 claimant_cred_handle, target_name); 893 if (GSS_ERROR(major_status)) 894 goto cleanup; 895 cred_locked = TRUE; 896 kcred = (krb5_gss_cred_id_t)claimant_cred_handle; 897 898 major_status = GSS_S_FAILURE; 899 900 if (initialContextToken) { 901 code = iakerb_get_initial_state(ctx, kcred, kname, time_req, 902 &ctx->state); 903 if (code != 0) { 904 *minor_status = code; 905 goto cleanup; 906 } 907 *context_handle = (gss_ctx_id_t)ctx; 908 } 909 910 if (ctx->state != IAKERB_AP_REQ) { 911 /* We need to do IAKERB. */ 912 code = iakerb_initiator_step(ctx, 913 kcred, 914 kname, 915 time_req, 916 input_token, 917 output_token); 918 if (code == KRB5_BAD_MSIZE) 919 major_status = GSS_S_DEFECTIVE_TOKEN; 920 if (code != 0) { 921 *minor_status = code; 922 goto cleanup; 923 } 924 } 925 926 if (ctx->state == IAKERB_AP_REQ) { 927 krb5_gss_ctx_ext_rec exts; 928 929 if (cred_locked) { 930 k5_mutex_unlock(&kcred->lock); 931 cred_locked = FALSE; 932 } 933 934 iakerb_make_exts(ctx, &exts); 935 936 if (ctx->gssc == GSS_C_NO_CONTEXT) 937 input_token = GSS_C_NO_BUFFER; 938 939 /* IAKERB is finished, or we skipped to Kerberos directly. */ 940 major_status = krb5_gss_init_sec_context_ext(minor_status, 941 (gss_cred_id_t) kcred, 942 &ctx->gssc, 943 target_name, 944 (gss_OID)gss_mech_iakerb, 945 req_flags, 946 time_req, 947 input_chan_bindings, 948 input_token, 949 NULL, 950 output_token, 951 ret_flags, 952 time_rec, 953 &exts); 954 if (major_status == GSS_S_COMPLETE) 955 ctx->established = 1; 956 } else { 957 if (ret_flags != NULL) 958 *ret_flags = 0; 959 if (time_rec != NULL) 960 *time_rec = 0; 961 major_status = GSS_S_CONTINUE_NEEDED; 962 } 963 964 if (actual_mech_type != NULL) 965 *actual_mech_type = gss_mech_iakerb; 966 967 cleanup: 968 if (cred_locked) 969 k5_mutex_unlock(&kcred->lock); 970 if (initialContextToken && GSS_ERROR(major_status)) { 971 iakerb_release_context(ctx); 972 *context_handle = GSS_C_NO_CONTEXT; 973 } 974 975 return major_status; 976 } 977 978 OM_uint32 KRB5_CALLCONV 979 iakerb_gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 980 gss_buffer_t input_message_buffer, 981 gss_buffer_t output_message_buffer, int *conf_state, 982 gss_qop_t *qop_state) 983 { 984 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 985 986 if (ctx->gssc == GSS_C_NO_CONTEXT) 987 return GSS_S_NO_CONTEXT; 988 989 return krb5_gss_unwrap(minor_status, ctx->gssc, input_message_buffer, 990 output_message_buffer, conf_state, qop_state); 991 } 992 993 OM_uint32 KRB5_CALLCONV 994 iakerb_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 995 int conf_req_flag, gss_qop_t qop_req, 996 gss_buffer_t input_message_buffer, int *conf_state, 997 gss_buffer_t output_message_buffer) 998 { 999 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1000 1001 if (ctx->gssc == GSS_C_NO_CONTEXT) 1002 return GSS_S_NO_CONTEXT; 1003 1004 return krb5_gss_wrap(minor_status, ctx->gssc, conf_req_flag, qop_req, 1005 input_message_buffer, conf_state, 1006 output_message_buffer); 1007 } 1008 1009 OM_uint32 KRB5_CALLCONV 1010 iakerb_gss_process_context_token(OM_uint32 *minor_status, 1011 const gss_ctx_id_t context_handle, 1012 const gss_buffer_t token_buffer) 1013 { 1014 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1015 1016 if (ctx->gssc == GSS_C_NO_CONTEXT) 1017 return GSS_S_DEFECTIVE_TOKEN; 1018 1019 return krb5_gss_process_context_token(minor_status, ctx->gssc, 1020 token_buffer); 1021 } 1022 1023 OM_uint32 KRB5_CALLCONV 1024 iakerb_gss_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 1025 OM_uint32 *time_rec) 1026 { 1027 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1028 1029 if (ctx->gssc == GSS_C_NO_CONTEXT) 1030 return GSS_S_NO_CONTEXT; 1031 1032 return krb5_gss_context_time(minor_status, ctx->gssc, time_rec); 1033 } 1034 1035 #ifndef LEAN_CLIENT 1036 1037 OM_uint32 KRB5_CALLCONV 1038 iakerb_gss_export_sec_context(OM_uint32 *minor_status, 1039 gss_ctx_id_t *context_handle, 1040 gss_buffer_t interprocess_token) 1041 { 1042 OM_uint32 maj; 1043 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle; 1044 1045 /* We don't currently support exporting partially established contexts. */ 1046 if (!ctx->established) 1047 return GSS_S_UNAVAILABLE; 1048 1049 maj = krb5_gss_export_sec_context(minor_status, &ctx->gssc, 1050 interprocess_token); 1051 if (ctx->gssc == GSS_C_NO_CONTEXT) { 1052 iakerb_release_context(ctx); 1053 *context_handle = GSS_C_NO_CONTEXT; 1054 } 1055 return maj; 1056 } 1057 1058 OM_uint32 KRB5_CALLCONV 1059 iakerb_gss_import_sec_context(OM_uint32 *minor_status, 1060 gss_buffer_t interprocess_token, 1061 gss_ctx_id_t *context_handle) 1062 { 1063 OM_uint32 maj, tmpmin; 1064 krb5_error_code code; 1065 gss_ctx_id_t gssc; 1066 krb5_gss_ctx_id_t kctx; 1067 iakerb_ctx_id_t ctx; 1068 1069 maj = krb5_gss_import_sec_context(minor_status, interprocess_token, &gssc); 1070 if (maj != GSS_S_COMPLETE) 1071 return maj; 1072 kctx = (krb5_gss_ctx_id_t)gssc; 1073 1074 if (!kctx->established) { 1075 /* We don't currently support importing partially established 1076 * contexts. */ 1077 krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER); 1078 return GSS_S_FAILURE; 1079 } 1080 1081 code = iakerb_alloc_context(&ctx, kctx->initiate); 1082 if (code != 0) { 1083 krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER); 1084 *minor_status = code; 1085 return GSS_S_FAILURE; 1086 } 1087 1088 ctx->gssc = gssc; 1089 ctx->established = 1; 1090 *context_handle = (gss_ctx_id_t)ctx; 1091 return GSS_S_COMPLETE; 1092 } 1093 #endif /* LEAN_CLIENT */ 1094 1095 OM_uint32 KRB5_CALLCONV 1096 iakerb_gss_inquire_context(OM_uint32 *minor_status, 1097 gss_ctx_id_t context_handle, gss_name_t *src_name, 1098 gss_name_t *targ_name, OM_uint32 *lifetime_rec, 1099 gss_OID *mech_type, OM_uint32 *ctx_flags, 1100 int *initiate, int *opened) 1101 { 1102 OM_uint32 ret; 1103 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1104 1105 if (src_name != NULL) 1106 *src_name = GSS_C_NO_NAME; 1107 if (targ_name != NULL) 1108 *targ_name = GSS_C_NO_NAME; 1109 if (lifetime_rec != NULL) 1110 *lifetime_rec = 0; 1111 if (mech_type != NULL) 1112 *mech_type = (gss_OID)gss_mech_iakerb; 1113 if (ctx_flags != NULL) 1114 *ctx_flags = 0; 1115 if (initiate != NULL) 1116 *initiate = ctx->initiate; 1117 if (opened != NULL) 1118 *opened = ctx->established; 1119 1120 if (ctx->gssc == GSS_C_NO_CONTEXT) 1121 return GSS_S_COMPLETE; 1122 1123 ret = krb5_gss_inquire_context(minor_status, ctx->gssc, src_name, 1124 targ_name, lifetime_rec, mech_type, 1125 ctx_flags, initiate, opened); 1126 1127 if (!ctx->established) { 1128 /* Report IAKERB as the mech OID until the context is established. */ 1129 if (mech_type != NULL) 1130 *mech_type = (gss_OID)gss_mech_iakerb; 1131 1132 /* We don't support exporting partially-established contexts. */ 1133 if (ctx_flags != NULL) 1134 *ctx_flags &= ~GSS_C_TRANS_FLAG; 1135 } 1136 1137 return ret; 1138 } 1139 1140 OM_uint32 KRB5_CALLCONV 1141 iakerb_gss_wrap_size_limit(OM_uint32 *minor_status, 1142 gss_ctx_id_t context_handle, int conf_req_flag, 1143 gss_qop_t qop_req, OM_uint32 req_output_size, 1144 OM_uint32 *max_input_size) 1145 { 1146 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1147 1148 if (ctx->gssc == GSS_C_NO_CONTEXT) 1149 return GSS_S_NO_CONTEXT; 1150 1151 return krb5_gss_wrap_size_limit(minor_status, ctx->gssc, conf_req_flag, 1152 qop_req, req_output_size, max_input_size); 1153 } 1154 1155 OM_uint32 KRB5_CALLCONV 1156 iakerb_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 1157 gss_qop_t qop_req, gss_buffer_t message_buffer, 1158 gss_buffer_t message_token) 1159 { 1160 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1161 1162 if (ctx->gssc == GSS_C_NO_CONTEXT) 1163 return GSS_S_NO_CONTEXT; 1164 1165 return krb5_gss_get_mic(minor_status, ctx->gssc, qop_req, message_buffer, 1166 message_token); 1167 } 1168 1169 OM_uint32 KRB5_CALLCONV 1170 iakerb_gss_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 1171 gss_buffer_t msg_buffer, gss_buffer_t token_buffer, 1172 gss_qop_t *qop_state) 1173 { 1174 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1175 1176 if (ctx->gssc == GSS_C_NO_CONTEXT) 1177 return GSS_S_NO_CONTEXT; 1178 1179 return krb5_gss_verify_mic(minor_status, ctx->gssc, msg_buffer, 1180 token_buffer, qop_state); 1181 } 1182 1183 OM_uint32 KRB5_CALLCONV 1184 iakerb_gss_inquire_sec_context_by_oid(OM_uint32 *minor_status, 1185 const gss_ctx_id_t context_handle, 1186 const gss_OID desired_object, 1187 gss_buffer_set_t *data_set) 1188 { 1189 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1190 1191 if (ctx->gssc == GSS_C_NO_CONTEXT) 1192 return GSS_S_UNAVAILABLE; 1193 1194 return krb5_gss_inquire_sec_context_by_oid(minor_status, ctx->gssc, 1195 desired_object, data_set); 1196 } 1197 1198 OM_uint32 KRB5_CALLCONV 1199 iakerb_gss_set_sec_context_option(OM_uint32 *minor_status, 1200 gss_ctx_id_t *context_handle, 1201 const gss_OID desired_object, 1202 const gss_buffer_t value) 1203 { 1204 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle; 1205 1206 if (ctx == NULL || ctx->gssc == GSS_C_NO_CONTEXT) 1207 return GSS_S_UNAVAILABLE; 1208 1209 return krb5_gss_set_sec_context_option(minor_status, &ctx->gssc, 1210 desired_object, value); 1211 } 1212 1213 OM_uint32 KRB5_CALLCONV 1214 iakerb_gss_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 1215 int conf_req_flag, gss_qop_t qop_req, int *conf_state, 1216 gss_iov_buffer_desc *iov, int iov_count) 1217 { 1218 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1219 1220 if (ctx->gssc == GSS_C_NO_CONTEXT) 1221 return GSS_S_NO_CONTEXT; 1222 1223 return krb5_gss_wrap_iov(minor_status, ctx->gssc, conf_req_flag, qop_req, 1224 conf_state, iov, iov_count); 1225 } 1226 1227 OM_uint32 KRB5_CALLCONV 1228 iakerb_gss_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 1229 int *conf_state, gss_qop_t *qop_state, 1230 gss_iov_buffer_desc *iov, int iov_count) 1231 { 1232 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1233 1234 if (ctx->gssc == GSS_C_NO_CONTEXT) 1235 return GSS_S_NO_CONTEXT; 1236 1237 return krb5_gss_unwrap_iov(minor_status, ctx->gssc, conf_state, qop_state, 1238 iov, iov_count); 1239 } 1240 1241 OM_uint32 KRB5_CALLCONV 1242 iakerb_gss_wrap_iov_length(OM_uint32 *minor_status, 1243 gss_ctx_id_t context_handle, int conf_req_flag, 1244 gss_qop_t qop_req, int *conf_state, 1245 gss_iov_buffer_desc *iov, int iov_count) 1246 { 1247 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1248 1249 if (ctx->gssc == GSS_C_NO_CONTEXT) 1250 return GSS_S_NO_CONTEXT; 1251 1252 return krb5_gss_wrap_iov_length(minor_status, ctx->gssc, conf_req_flag, 1253 qop_req, conf_state, iov, iov_count); 1254 } 1255 1256 OM_uint32 KRB5_CALLCONV 1257 iakerb_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 1258 int prf_key, const gss_buffer_t prf_in, 1259 ssize_t desired_output_len, gss_buffer_t prf_out) 1260 { 1261 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1262 1263 if (ctx->gssc == GSS_C_NO_CONTEXT) 1264 return GSS_S_NO_CONTEXT; 1265 1266 return krb5_gss_pseudo_random(minor_status, ctx->gssc, prf_key, prf_in, 1267 desired_output_len, prf_out); 1268 } 1269 1270 OM_uint32 KRB5_CALLCONV 1271 iakerb_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 1272 gss_qop_t qop_req, gss_iov_buffer_desc *iov, 1273 int iov_count) 1274 { 1275 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1276 1277 if (ctx->gssc == GSS_C_NO_CONTEXT) 1278 return GSS_S_NO_CONTEXT; 1279 1280 return krb5_gss_get_mic_iov(minor_status, ctx->gssc, qop_req, iov, 1281 iov_count); 1282 } 1283 1284 OM_uint32 KRB5_CALLCONV 1285 iakerb_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, 1286 gss_qop_t *qop_state, gss_iov_buffer_desc *iov, 1287 int iov_count) 1288 { 1289 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1290 1291 if (ctx->gssc == GSS_C_NO_CONTEXT) 1292 return GSS_S_NO_CONTEXT; 1293 1294 return krb5_gss_verify_mic_iov(minor_status, ctx->gssc, qop_state, iov, 1295 iov_count); 1296 } 1297 1298 OM_uint32 KRB5_CALLCONV 1299 iakerb_gss_get_mic_iov_length(OM_uint32 *minor_status, 1300 gss_ctx_id_t context_handle, gss_qop_t qop_req, 1301 gss_iov_buffer_desc *iov, int iov_count) 1302 { 1303 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; 1304 1305 if (ctx->gssc == GSS_C_NO_CONTEXT) 1306 return GSS_S_NO_CONTEXT; 1307 1308 return krb5_gss_get_mic_iov_length(minor_status, ctx->gssc, qop_req, iov, 1309 iov_count); 1310 } 1311