1 /* $NetBSD: init_creds_pw.c,v 1.3 2019/12/15 22:50:50 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include "krb5_locl.h" 39 #ifndef WIN32 40 #include <heim-ipc.h> 41 #endif /* WIN32 */ 42 43 typedef struct krb5_get_init_creds_ctx { 44 KDCOptions flags; 45 krb5_creds cred; 46 krb5_addresses *addrs; 47 krb5_enctype *etypes; 48 krb5_preauthtype *pre_auth_types; 49 char *in_tkt_service; 50 unsigned nonce; 51 unsigned pk_nonce; 52 53 krb5_data req_buffer; 54 AS_REQ as_req; 55 int pa_counter; 56 57 /* password and keytab_data is freed on completion */ 58 char *password; 59 krb5_keytab_key_proc_args *keytab_data; 60 61 krb5_pointer *keyseed; 62 krb5_s2k_proc keyproc; 63 64 krb5_get_init_creds_tristate req_pac; 65 66 krb5_pk_init_ctx pk_init_ctx; 67 int ic_flags; 68 69 struct { 70 unsigned change_password:1; 71 } runflags; 72 73 int used_pa_types; 74 #define USED_PKINIT 1 75 #define USED_PKINIT_W2K 2 76 #define USED_ENC_TS_GUESS 4 77 #define USED_ENC_TS_INFO 8 78 79 METHOD_DATA md; 80 KRB_ERROR error; 81 AS_REP as_rep; 82 EncKDCRepPart enc_part; 83 84 krb5_prompter_fct prompter; 85 void *prompter_data; 86 87 struct pa_info_data *ppaid; 88 struct fast_state { 89 enum PA_FX_FAST_REQUEST_enum type; 90 unsigned int flags; 91 #define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1 92 #define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2 93 #define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4 94 #define KRB5_FAST_REPLY_REPLY_VERIFED 8 95 #define KRB5_FAST_STRONG 16 96 #define KRB5_FAST_EXPECTED 32 /* in exchange with KDC, fast was discovered */ 97 #define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */ 98 #define KRB5_FAST_DISABLED 128 99 #define KRB5_FAST_AP_ARMOR_SERVICE 256 100 krb5_keyblock *reply_key; 101 krb5_ccache armor_ccache; 102 krb5_principal armor_service; 103 krb5_crypto armor_crypto; 104 krb5_keyblock armor_key; 105 krb5_keyblock *strengthen_key; 106 } fast_state; 107 } krb5_get_init_creds_ctx; 108 109 110 struct pa_info_data { 111 krb5_enctype etype; 112 krb5_salt salt; 113 krb5_data *s2kparams; 114 }; 115 116 static void 117 free_paid(krb5_context context, struct pa_info_data *ppaid) 118 { 119 krb5_free_salt(context, ppaid->salt); 120 if (ppaid->s2kparams) 121 krb5_free_data(context, ppaid->s2kparams); 122 } 123 124 static krb5_error_code KRB5_CALLCONV 125 default_s2k_func(krb5_context context, krb5_enctype type, 126 krb5_const_pointer keyseed, 127 krb5_salt salt, krb5_data *s2kparms, 128 krb5_keyblock **key) 129 { 130 krb5_error_code ret; 131 krb5_data password; 132 krb5_data opaque; 133 134 _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func"); 135 136 password.data = rk_UNCONST(keyseed); 137 password.length = strlen(keyseed); 138 if (s2kparms) 139 opaque = *s2kparms; 140 else 141 krb5_data_zero(&opaque); 142 143 *key = malloc(sizeof(**key)); 144 if (*key == NULL) 145 return ENOMEM; 146 ret = krb5_string_to_key_data_salt_opaque(context, type, password, 147 salt, opaque, *key); 148 if (ret) { 149 free(*key); 150 *key = NULL; 151 } 152 return ret; 153 } 154 155 static void 156 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx) 157 { 158 if (ctx->etypes) 159 free(ctx->etypes); 160 if (ctx->pre_auth_types) 161 free (ctx->pre_auth_types); 162 if (ctx->in_tkt_service) 163 free(ctx->in_tkt_service); 164 if (ctx->keytab_data) 165 free(ctx->keytab_data); 166 if (ctx->password) { 167 size_t len; 168 len = strlen(ctx->password); 169 memset_s(ctx->password, len, 0, len); 170 free(ctx->password); 171 } 172 /* 173 * FAST state (we don't close the armor_ccache because we might have 174 * to destroy it, and how would we know? also, the caller should 175 * take care of cleaning up the armor_ccache). 176 */ 177 if (ctx->fast_state.armor_service) 178 krb5_free_principal(context, ctx->fast_state.armor_service); 179 if (ctx->fast_state.armor_crypto) 180 krb5_crypto_destroy(context, ctx->fast_state.armor_crypto); 181 if (ctx->fast_state.strengthen_key) 182 krb5_free_keyblock(context, ctx->fast_state.strengthen_key); 183 krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key); 184 185 krb5_data_free(&ctx->req_buffer); 186 krb5_free_cred_contents(context, &ctx->cred); 187 free_METHOD_DATA(&ctx->md); 188 free_AS_REP(&ctx->as_rep); 189 free_EncKDCRepPart(&ctx->enc_part); 190 free_KRB_ERROR(&ctx->error); 191 free_AS_REQ(&ctx->as_req); 192 if (ctx->ppaid) { 193 free_paid(context, ctx->ppaid); 194 free(ctx->ppaid); 195 } 196 memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx)); 197 } 198 199 static int 200 get_config_time (krb5_context context, 201 const char *realm, 202 const char *name, 203 int def) 204 { 205 int ret; 206 207 ret = krb5_config_get_time (context, NULL, 208 "realms", 209 realm, 210 name, 211 NULL); 212 if (ret >= 0) 213 return ret; 214 ret = krb5_config_get_time (context, NULL, 215 "libdefaults", 216 name, 217 NULL); 218 if (ret >= 0) 219 return ret; 220 return def; 221 } 222 223 static krb5_error_code 224 init_cred (krb5_context context, 225 krb5_creds *cred, 226 krb5_principal client, 227 krb5_deltat start_time, 228 krb5_get_init_creds_opt *options) 229 { 230 krb5_error_code ret; 231 int tmp; 232 krb5_timestamp now; 233 234 krb5_timeofday (context, &now); 235 236 memset (cred, 0, sizeof(*cred)); 237 238 if (client) 239 ret = krb5_copy_principal(context, client, &cred->client); 240 else 241 ret = krb5_get_default_principal(context, &cred->client); 242 if (ret) 243 goto out; 244 245 if (start_time) 246 cred->times.starttime = now + start_time; 247 248 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE) 249 tmp = options->tkt_life; 250 else 251 tmp = KRB5_TKT_LIFETIME_DEFAULT; 252 cred->times.endtime = now + tmp; 253 254 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) { 255 if (options->renew_life > 0) 256 tmp = options->renew_life; 257 else 258 tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT; 259 cred->times.renew_till = now + tmp; 260 } 261 262 return 0; 263 264 out: 265 krb5_free_cred_contents (context, cred); 266 return ret; 267 } 268 269 /* 270 * Print a message (str) to the user about the expiration in `lr' 271 */ 272 273 static void 274 report_expiration (krb5_context context, 275 krb5_prompter_fct prompter, 276 krb5_data *data, 277 const char *str, 278 time_t now) 279 { 280 char *p = NULL; 281 282 if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL) 283 return; 284 (*prompter)(context, data, NULL, p, 0, NULL); 285 free(p); 286 } 287 288 /* 289 * Check the context, and in the case there is a expiration warning, 290 * use the prompter to print the warning. 291 * 292 * @param context A Kerberos 5 context. 293 * @param options An GIC options structure 294 * @param ctx The krb5_init_creds_context check for expiration. 295 */ 296 297 krb5_error_code 298 krb5_process_last_request(krb5_context context, 299 krb5_get_init_creds_opt *options, 300 krb5_init_creds_context ctx) 301 { 302 krb5_const_realm realm; 303 LastReq *lr; 304 krb5_boolean reported = FALSE; 305 krb5_timestamp sec; 306 time_t t; 307 size_t i; 308 309 /* 310 * First check if there is a API consumer. 311 */ 312 313 realm = krb5_principal_get_realm (context, ctx->cred.client); 314 lr = &ctx->enc_part.last_req; 315 316 if (options && options->opt_private && options->opt_private->lr.func) { 317 krb5_last_req_entry **lre; 318 319 lre = calloc(lr->len + 1, sizeof(*lre)); 320 if (lre == NULL) 321 return krb5_enomem(context); 322 for (i = 0; i < lr->len; i++) { 323 lre[i] = calloc(1, sizeof(*lre[i])); 324 if (lre[i] == NULL) 325 break; 326 lre[i]->lr_type = lr->val[i].lr_type; 327 lre[i]->value = lr->val[i].lr_value; 328 } 329 330 (*options->opt_private->lr.func)(context, lre, 331 options->opt_private->lr.ctx); 332 333 for (i = 0; i < lr->len; i++) 334 free(lre[i]); 335 free(lre); 336 } 337 338 /* 339 * Now check if we should prompt the user 340 */ 341 342 if (ctx->prompter == NULL) 343 return 0; 344 345 krb5_timeofday (context, &sec); 346 347 t = sec + get_config_time (context, 348 realm, 349 "warn_pwexpire", 350 7 * 24 * 60 * 60); 351 352 for (i = 0; i < lr->len; ++i) { 353 if (lr->val[i].lr_value <= t) { 354 switch (lr->val[i].lr_type) { 355 case LR_PW_EXPTIME : 356 report_expiration(context, ctx->prompter, 357 ctx->prompter_data, 358 "Your password will expire at ", 359 lr->val[i].lr_value); 360 reported = TRUE; 361 break; 362 case LR_ACCT_EXPTIME : 363 report_expiration(context, ctx->prompter, 364 ctx->prompter_data, 365 "Your account will expire at ", 366 lr->val[i].lr_value); 367 reported = TRUE; 368 break; 369 default: 370 break; 371 } 372 } 373 } 374 375 if (!reported 376 && ctx->enc_part.key_expiration 377 && *ctx->enc_part.key_expiration <= t) { 378 report_expiration(context, ctx->prompter, 379 ctx->prompter_data, 380 "Your password/account will expire at ", 381 *ctx->enc_part.key_expiration); 382 } 383 return 0; 384 } 385 386 static krb5_addresses no_addrs = { 0, NULL }; 387 388 static krb5_error_code 389 get_init_creds_common(krb5_context context, 390 krb5_principal client, 391 krb5_deltat start_time, 392 krb5_get_init_creds_opt *options, 393 krb5_init_creds_context ctx) 394 { 395 krb5_get_init_creds_opt *default_opt = NULL; 396 krb5_error_code ret; 397 krb5_enctype *etypes; 398 krb5_preauthtype *pre_auth_types; 399 400 memset(ctx, 0, sizeof(*ctx)); 401 402 if (options == NULL) { 403 const char *realm = krb5_principal_get_realm(context, client); 404 405 krb5_get_init_creds_opt_alloc (context, &default_opt); 406 options = default_opt; 407 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options); 408 } 409 410 if (options->opt_private) { 411 if (options->opt_private->password) { 412 ret = krb5_init_creds_set_password(context, ctx, 413 options->opt_private->password); 414 if (ret) 415 goto out; 416 } 417 418 ctx->keyproc = options->opt_private->key_proc; 419 ctx->req_pac = options->opt_private->req_pac; 420 ctx->pk_init_ctx = options->opt_private->pk_init_ctx; 421 ctx->ic_flags = options->opt_private->flags; 422 } else 423 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET; 424 425 if (ctx->keyproc == NULL) 426 ctx->keyproc = default_s2k_func; 427 428 /* Enterprise name implicitly turns on canonicalize */ 429 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) || 430 krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL) 431 ctx->flags.canonicalize = 1; 432 433 ctx->pre_auth_types = NULL; 434 ctx->addrs = NULL; 435 ctx->etypes = NULL; 436 ctx->pre_auth_types = NULL; 437 438 ret = init_cred(context, &ctx->cred, client, start_time, options); 439 if (ret) { 440 if (default_opt) 441 krb5_get_init_creds_opt_free(context, default_opt); 442 return ret; 443 } 444 445 ret = krb5_init_creds_set_service(context, ctx, NULL); 446 if (ret) 447 goto out; 448 449 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) 450 ctx->flags.forwardable = options->forwardable; 451 452 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) 453 ctx->flags.proxiable = options->proxiable; 454 455 if (start_time) 456 ctx->flags.postdated = 1; 457 if (ctx->cred.times.renew_till) 458 ctx->flags.renewable = 1; 459 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) { 460 ctx->addrs = options->address_list; 461 } else if (options->opt_private) { 462 switch (options->opt_private->addressless) { 463 case KRB5_INIT_CREDS_TRISTATE_UNSET: 464 #if KRB5_ADDRESSLESS_DEFAULT == TRUE 465 ctx->addrs = &no_addrs; 466 #else 467 ctx->addrs = NULL; 468 #endif 469 break; 470 case KRB5_INIT_CREDS_TRISTATE_FALSE: 471 ctx->addrs = NULL; 472 break; 473 case KRB5_INIT_CREDS_TRISTATE_TRUE: 474 ctx->addrs = &no_addrs; 475 break; 476 } 477 } 478 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { 479 if (ctx->etypes) 480 free(ctx->etypes); 481 482 etypes = malloc((options->etype_list_length + 1) 483 * sizeof(krb5_enctype)); 484 if (etypes == NULL) { 485 ret = krb5_enomem(context); 486 goto out; 487 } 488 memcpy (etypes, options->etype_list, 489 options->etype_list_length * sizeof(krb5_enctype)); 490 etypes[options->etype_list_length] = ETYPE_NULL; 491 ctx->etypes = etypes; 492 } 493 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { 494 pre_auth_types = malloc((options->preauth_list_length + 1) 495 * sizeof(krb5_preauthtype)); 496 if (pre_auth_types == NULL) { 497 ret = krb5_enomem(context); 498 goto out; 499 } 500 memcpy (pre_auth_types, options->preauth_list, 501 options->preauth_list_length * sizeof(krb5_preauthtype)); 502 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE; 503 ctx->pre_auth_types = pre_auth_types; 504 } 505 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) 506 ctx->flags.request_anonymous = options->anonymous; 507 if (default_opt) 508 krb5_get_init_creds_opt_free(context, default_opt); 509 return 0; 510 out: 511 if (default_opt) 512 krb5_get_init_creds_opt_free(context, default_opt); 513 return ret; 514 } 515 516 static krb5_error_code 517 change_password (krb5_context context, 518 krb5_principal client, 519 const char *password, 520 char *newpw, 521 size_t newpw_sz, 522 krb5_prompter_fct prompter, 523 void *data, 524 krb5_get_init_creds_opt *old_options) 525 { 526 krb5_prompt prompts[2]; 527 krb5_error_code ret; 528 krb5_creds cpw_cred; 529 char buf1[BUFSIZ], buf2[BUFSIZ]; 530 krb5_data password_data[2]; 531 int result_code; 532 krb5_data result_code_string; 533 krb5_data result_string; 534 char *p; 535 krb5_get_init_creds_opt *options; 536 537 heim_assert(prompter != NULL, "unexpected NULL prompter"); 538 539 memset (&cpw_cred, 0, sizeof(cpw_cred)); 540 541 ret = krb5_get_init_creds_opt_alloc(context, &options); 542 if (ret) 543 return ret; 544 krb5_get_init_creds_opt_set_tkt_life (options, 60); 545 krb5_get_init_creds_opt_set_forwardable (options, FALSE); 546 krb5_get_init_creds_opt_set_proxiable (options, FALSE); 547 if (old_options && 548 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) 549 krb5_get_init_creds_opt_set_preauth_list(options, 550 old_options->preauth_list, 551 old_options->preauth_list_length); 552 if (old_options && 553 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT)) 554 krb5_get_init_creds_opt_set_change_password_prompt(options, 555 old_options->change_password_prompt); 556 557 krb5_data_zero (&result_code_string); 558 krb5_data_zero (&result_string); 559 560 ret = krb5_get_init_creds_password (context, 561 &cpw_cred, 562 client, 563 password, 564 prompter, 565 data, 566 0, 567 "kadmin/changepw", 568 options); 569 krb5_get_init_creds_opt_free(context, options); 570 if (ret) 571 goto out; 572 573 for(;;) { 574 password_data[0].data = buf1; 575 password_data[0].length = sizeof(buf1); 576 577 prompts[0].hidden = 1; 578 prompts[0].prompt = "New password: "; 579 prompts[0].reply = &password_data[0]; 580 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD; 581 582 password_data[1].data = buf2; 583 password_data[1].length = sizeof(buf2); 584 585 prompts[1].hidden = 1; 586 prompts[1].prompt = "Repeat new password: "; 587 prompts[1].reply = &password_data[1]; 588 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN; 589 590 ret = (*prompter) (context, data, NULL, "Changing password", 591 2, prompts); 592 if (ret) { 593 memset (buf1, 0, sizeof(buf1)); 594 memset (buf2, 0, sizeof(buf2)); 595 goto out; 596 } 597 598 if (strcmp (buf1, buf2) == 0) 599 break; 600 memset (buf1, 0, sizeof(buf1)); 601 memset (buf2, 0, sizeof(buf2)); 602 } 603 604 ret = krb5_set_password (context, 605 &cpw_cred, 606 buf1, 607 client, 608 &result_code, 609 &result_code_string, 610 &result_string); 611 if (ret) 612 goto out; 613 if (asprintf(&p, "%s: %.*s\n", 614 result_code ? "Error" : "Success", 615 (int)result_string.length, 616 result_string.length > 0 ? (char*)result_string.data : "") < 0) 617 { 618 ret = ENOMEM; 619 goto out; 620 } 621 622 /* return the result */ 623 (*prompter) (context, data, NULL, p, 0, NULL); 624 625 free (p); 626 if (result_code == 0) { 627 strlcpy (newpw, buf1, newpw_sz); 628 ret = 0; 629 } else { 630 ret = ENOTTY; 631 krb5_set_error_message(context, ret, 632 N_("failed changing password", "")); 633 } 634 635 out: 636 memset_s(buf1, sizeof(buf1), 0, sizeof(buf1)); 637 memset_s(buf2, sizeof(buf2), 0, sizeof(buf2)); 638 krb5_data_free (&result_string); 639 krb5_data_free (&result_code_string); 640 krb5_free_cred_contents (context, &cpw_cred); 641 return ret; 642 } 643 644 645 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 646 krb5_keyblock_key_proc (krb5_context context, 647 krb5_keytype type, 648 krb5_data *salt, 649 krb5_const_pointer keyseed, 650 krb5_keyblock **key) 651 { 652 return krb5_copy_keyblock (context, keyseed, key); 653 } 654 655 /* 656 * 657 */ 658 659 static krb5_error_code 660 init_as_req (krb5_context context, 661 KDCOptions opts, 662 const krb5_creds *creds, 663 const krb5_addresses *addrs, 664 const krb5_enctype *etypes, 665 AS_REQ *a) 666 { 667 krb5_error_code ret; 668 669 memset(a, 0, sizeof(*a)); 670 671 a->pvno = 5; 672 a->msg_type = krb_as_req; 673 a->req_body.kdc_options = opts; 674 a->req_body.cname = malloc(sizeof(*a->req_body.cname)); 675 if (a->req_body.cname == NULL) { 676 ret = krb5_enomem(context); 677 goto fail; 678 } 679 a->req_body.sname = malloc(sizeof(*a->req_body.sname)); 680 if (a->req_body.sname == NULL) { 681 ret = krb5_enomem(context); 682 goto fail; 683 } 684 685 ret = _krb5_principal2principalname (a->req_body.cname, creds->client); 686 if (ret) 687 goto fail; 688 ret = copy_Realm(&creds->client->realm, &a->req_body.realm); 689 if (ret) 690 goto fail; 691 692 ret = _krb5_principal2principalname (a->req_body.sname, creds->server); 693 if (ret) 694 goto fail; 695 696 if(creds->times.starttime) { 697 a->req_body.from = malloc(sizeof(*a->req_body.from)); 698 if (a->req_body.from == NULL) { 699 ret = krb5_enomem(context); 700 goto fail; 701 } 702 *a->req_body.from = creds->times.starttime; 703 } 704 if(creds->times.endtime){ 705 if ((ALLOC(a->req_body.till, 1)) != NULL) 706 *a->req_body.till = creds->times.endtime; 707 else { 708 ret = krb5_enomem(context); 709 goto fail; 710 } 711 } 712 if(creds->times.renew_till){ 713 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime)); 714 if (a->req_body.rtime == NULL) { 715 ret = krb5_enomem(context); 716 goto fail; 717 } 718 *a->req_body.rtime = creds->times.renew_till; 719 } 720 a->req_body.nonce = 0; 721 ret = _krb5_init_etype(context, 722 KRB5_PDU_AS_REQUEST, 723 &a->req_body.etype.len, 724 &a->req_body.etype.val, 725 etypes); 726 if (ret) 727 goto fail; 728 729 /* 730 * This means no addresses 731 */ 732 733 if (addrs && addrs->len == 0) { 734 a->req_body.addresses = NULL; 735 } else { 736 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses)); 737 if (a->req_body.addresses == NULL) { 738 ret = krb5_enomem(context); 739 goto fail; 740 } 741 742 if (addrs) 743 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses); 744 else { 745 ret = krb5_get_all_client_addrs (context, a->req_body.addresses); 746 if(ret == 0 && a->req_body.addresses->len == 0) { 747 free(a->req_body.addresses); 748 a->req_body.addresses = NULL; 749 } 750 } 751 if (ret) 752 goto fail; 753 } 754 755 a->req_body.enc_authorization_data = NULL; 756 a->req_body.additional_tickets = NULL; 757 758 a->padata = NULL; 759 760 return 0; 761 fail: 762 free_AS_REQ(a); 763 memset_s(a, sizeof(*a), 0, sizeof(*a)); 764 return ret; 765 } 766 767 768 static krb5_error_code 769 set_paid(struct pa_info_data *paid, krb5_context context, 770 krb5_enctype etype, 771 krb5_salttype salttype, void *salt_string, size_t salt_len, 772 krb5_data *s2kparams) 773 { 774 paid->etype = etype; 775 paid->salt.salttype = salttype; 776 paid->salt.saltvalue.data = malloc(salt_len + 1); 777 if (paid->salt.saltvalue.data == NULL) { 778 krb5_clear_error_message(context); 779 return ENOMEM; 780 } 781 memcpy(paid->salt.saltvalue.data, salt_string, salt_len); 782 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0'; 783 paid->salt.saltvalue.length = salt_len; 784 if (s2kparams) { 785 krb5_error_code ret; 786 787 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams); 788 if (ret) { 789 krb5_clear_error_message(context); 790 krb5_free_salt(context, paid->salt); 791 return ret; 792 } 793 } else 794 paid->s2kparams = NULL; 795 796 return 0; 797 } 798 799 static struct pa_info_data * 800 pa_etype_info2(krb5_context context, 801 const krb5_principal client, 802 const AS_REQ *asreq, 803 struct pa_info_data *paid, 804 heim_octet_string *data) 805 { 806 krb5_error_code ret; 807 ETYPE_INFO2 e; 808 size_t sz; 809 size_t i, j; 810 811 memset(&e, 0, sizeof(e)); 812 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz); 813 if (ret) 814 goto out; 815 if (e.len == 0) 816 goto out; 817 for (j = 0; j < asreq->req_body.etype.len; j++) { 818 for (i = 0; i < e.len; i++) { 819 if (asreq->req_body.etype.val[j] == e.val[i].etype) { 820 krb5_salt salt; 821 if (e.val[i].salt == NULL) 822 ret = krb5_get_pw_salt(context, client, &salt); 823 else { 824 salt.saltvalue.data = *e.val[i].salt; 825 salt.saltvalue.length = strlen(*e.val[i].salt); 826 ret = 0; 827 } 828 if (ret == 0) 829 ret = set_paid(paid, context, e.val[i].etype, 830 KRB5_PW_SALT, 831 salt.saltvalue.data, 832 salt.saltvalue.length, 833 e.val[i].s2kparams); 834 if (e.val[i].salt == NULL) 835 krb5_free_salt(context, salt); 836 if (ret == 0) { 837 free_ETYPE_INFO2(&e); 838 return paid; 839 } 840 } 841 } 842 } 843 out: 844 free_ETYPE_INFO2(&e); 845 return NULL; 846 } 847 848 static struct pa_info_data * 849 pa_etype_info(krb5_context context, 850 const krb5_principal client, 851 const AS_REQ *asreq, 852 struct pa_info_data *paid, 853 heim_octet_string *data) 854 { 855 krb5_error_code ret; 856 ETYPE_INFO e; 857 size_t sz; 858 size_t i, j; 859 860 memset(&e, 0, sizeof(e)); 861 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz); 862 if (ret) 863 goto out; 864 if (e.len == 0) 865 goto out; 866 for (j = 0; j < asreq->req_body.etype.len; j++) { 867 for (i = 0; i < e.len; i++) { 868 if (asreq->req_body.etype.val[j] == e.val[i].etype) { 869 krb5_salt salt; 870 salt.salttype = KRB5_PW_SALT; 871 if (e.val[i].salt == NULL) 872 ret = krb5_get_pw_salt(context, client, &salt); 873 else { 874 salt.saltvalue = *e.val[i].salt; 875 ret = 0; 876 } 877 if (e.val[i].salttype) 878 salt.salttype = *e.val[i].salttype; 879 if (ret == 0) { 880 ret = set_paid(paid, context, e.val[i].etype, 881 salt.salttype, 882 salt.saltvalue.data, 883 salt.saltvalue.length, 884 NULL); 885 if (e.val[i].salt == NULL) 886 krb5_free_salt(context, salt); 887 } 888 if (ret == 0) { 889 free_ETYPE_INFO(&e); 890 return paid; 891 } 892 } 893 } 894 } 895 out: 896 free_ETYPE_INFO(&e); 897 return NULL; 898 } 899 900 static struct pa_info_data * 901 pa_pw_or_afs3_salt(krb5_context context, 902 const krb5_principal client, 903 const AS_REQ *asreq, 904 struct pa_info_data *paid, 905 heim_octet_string *data) 906 { 907 krb5_error_code ret; 908 if (paid->etype == KRB5_ENCTYPE_NULL) 909 return NULL; 910 ret = set_paid(paid, context, 911 paid->etype, 912 paid->salt.salttype, 913 data->data, 914 data->length, 915 NULL); 916 if (ret) 917 return NULL; 918 return paid; 919 } 920 921 922 struct pa_info { 923 krb5_preauthtype type; 924 struct pa_info_data *(*salt_info)(krb5_context, 925 const krb5_principal, 926 const AS_REQ *, 927 struct pa_info_data *, 928 heim_octet_string *); 929 }; 930 931 static struct pa_info pa_prefs[] = { 932 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 }, 933 { KRB5_PADATA_ETYPE_INFO, pa_etype_info }, 934 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt }, 935 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt } 936 }; 937 938 static PA_DATA * 939 find_pa_data(const METHOD_DATA *md, unsigned type) 940 { 941 size_t i; 942 if (md == NULL) 943 return NULL; 944 for (i = 0; i < md->len; i++) 945 if (md->val[i].padata_type == type) 946 return &md->val[i]; 947 return NULL; 948 } 949 950 static struct pa_info_data * 951 process_pa_info(krb5_context context, 952 const krb5_principal client, 953 const AS_REQ *asreq, 954 struct pa_info_data *paid, 955 METHOD_DATA *md) 956 { 957 struct pa_info_data *p = NULL; 958 size_t i; 959 960 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) { 961 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type); 962 if (pa == NULL) 963 continue; 964 paid->salt.salttype = (krb5_salttype)pa_prefs[i].type; 965 p = (*pa_prefs[i].salt_info)(context, client, asreq, 966 paid, &pa->padata_value); 967 } 968 return p; 969 } 970 971 static krb5_error_code 972 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md, 973 krb5_enctype etype, krb5_keyblock *key) 974 { 975 PA_ENC_TS_ENC p; 976 unsigned char *buf; 977 size_t buf_size; 978 size_t len = 0; 979 EncryptedData encdata; 980 krb5_error_code ret; 981 int32_t usec; 982 int usec2; 983 krb5_crypto crypto; 984 985 krb5_us_timeofday (context, &p.patimestamp, &usec); 986 usec2 = usec; 987 p.pausec = &usec2; 988 989 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); 990 if (ret) 991 return ret; 992 if(buf_size != len) 993 krb5_abortx(context, "internal error in ASN.1 encoder"); 994 995 ret = krb5_crypto_init(context, key, 0, &crypto); 996 if (ret) { 997 free(buf); 998 return ret; 999 } 1000 ret = krb5_encrypt_EncryptedData(context, 1001 crypto, 1002 KRB5_KU_PA_ENC_TIMESTAMP, 1003 buf, 1004 len, 1005 0, 1006 &encdata); 1007 free(buf); 1008 krb5_crypto_destroy(context, crypto); 1009 if (ret) 1010 return ret; 1011 1012 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); 1013 free_EncryptedData(&encdata); 1014 if (ret) 1015 return ret; 1016 if(buf_size != len) 1017 krb5_abortx(context, "internal error in ASN.1 encoder"); 1018 1019 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len); 1020 if (ret) 1021 free(buf); 1022 return ret; 1023 } 1024 1025 static krb5_error_code 1026 add_enc_ts_padata(krb5_context context, 1027 METHOD_DATA *md, 1028 krb5_principal client, 1029 krb5_s2k_proc keyproc, 1030 krb5_const_pointer keyseed, 1031 krb5_enctype *enctypes, 1032 unsigned netypes, 1033 krb5_salt *salt, 1034 krb5_data *s2kparams) 1035 { 1036 krb5_error_code ret; 1037 krb5_salt salt2; 1038 krb5_enctype *ep; 1039 size_t i; 1040 1041 if(salt == NULL) { 1042 /* default to standard salt */ 1043 ret = krb5_get_pw_salt (context, client, &salt2); 1044 if (ret) 1045 return ret; 1046 salt = &salt2; 1047 } 1048 if (!enctypes) { 1049 enctypes = context->etypes; 1050 netypes = 0; 1051 for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++) 1052 netypes++; 1053 } 1054 1055 for (i = 0; i < netypes; ++i) { 1056 krb5_keyblock *key; 1057 1058 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]); 1059 1060 ret = (*keyproc)(context, enctypes[i], keyseed, 1061 *salt, s2kparams, &key); 1062 if (ret) 1063 continue; 1064 ret = make_pa_enc_timestamp (context, md, enctypes[i], key); 1065 krb5_free_keyblock (context, key); 1066 if (ret) 1067 return ret; 1068 } 1069 if(salt == &salt2) 1070 krb5_free_salt(context, salt2); 1071 return 0; 1072 } 1073 1074 static krb5_error_code 1075 pa_data_to_md_ts_enc(krb5_context context, 1076 const AS_REQ *a, 1077 const krb5_principal client, 1078 krb5_get_init_creds_ctx *ctx, 1079 struct pa_info_data *ppaid, 1080 METHOD_DATA *md) 1081 { 1082 if (ctx->keyproc == NULL || ctx->keyseed == NULL) 1083 return 0; 1084 1085 if (ppaid) { 1086 add_enc_ts_padata(context, md, client, 1087 ctx->keyproc, ctx->keyseed, 1088 &ppaid->etype, 1, 1089 &ppaid->salt, ppaid->s2kparams); 1090 } else { 1091 krb5_salt salt; 1092 1093 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt"); 1094 1095 /* make a v5 salted pa-data */ 1096 add_enc_ts_padata(context, md, client, 1097 ctx->keyproc, ctx->keyseed, 1098 a->req_body.etype.val, a->req_body.etype.len, 1099 NULL, NULL); 1100 1101 /* make a v4 salted pa-data */ 1102 salt.salttype = KRB5_PW_SALT; 1103 krb5_data_zero(&salt.saltvalue); 1104 add_enc_ts_padata(context, md, client, 1105 ctx->keyproc, ctx->keyseed, 1106 a->req_body.etype.val, a->req_body.etype.len, 1107 &salt, NULL); 1108 } 1109 return 0; 1110 } 1111 1112 static krb5_error_code 1113 pa_data_to_key_plain(krb5_context context, 1114 const krb5_principal client, 1115 krb5_get_init_creds_ctx *ctx, 1116 krb5_salt salt, 1117 krb5_data *s2kparams, 1118 krb5_enctype etype, 1119 krb5_keyblock **key) 1120 { 1121 krb5_error_code ret; 1122 1123 ret = (*ctx->keyproc)(context, etype, ctx->keyseed, 1124 salt, s2kparams, key); 1125 return ret; 1126 } 1127 1128 1129 static krb5_error_code 1130 pa_data_to_md_pkinit(krb5_context context, 1131 const AS_REQ *a, 1132 const krb5_principal client, 1133 int win2k, 1134 krb5_get_init_creds_ctx *ctx, 1135 METHOD_DATA *md) 1136 { 1137 if (ctx->pk_init_ctx == NULL) 1138 return 0; 1139 #ifdef PKINIT 1140 return _krb5_pk_mk_padata(context, 1141 ctx->pk_init_ctx, 1142 ctx->ic_flags, 1143 win2k, 1144 &a->req_body, 1145 ctx->pk_nonce, 1146 md); 1147 #else 1148 krb5_set_error_message(context, EINVAL, 1149 N_("no support for PKINIT compiled in", "")); 1150 return EINVAL; 1151 #endif 1152 } 1153 1154 static krb5_error_code 1155 pa_data_add_pac_request(krb5_context context, 1156 krb5_get_init_creds_ctx *ctx, 1157 METHOD_DATA *md) 1158 { 1159 size_t len = 0, length; 1160 krb5_error_code ret; 1161 PA_PAC_REQUEST req; 1162 void *buf; 1163 1164 switch (ctx->req_pac) { 1165 case KRB5_INIT_CREDS_TRISTATE_UNSET: 1166 return 0; /* don't bother */ 1167 case KRB5_INIT_CREDS_TRISTATE_TRUE: 1168 req.include_pac = 1; 1169 break; 1170 case KRB5_INIT_CREDS_TRISTATE_FALSE: 1171 req.include_pac = 0; 1172 } 1173 1174 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length, 1175 &req, &len, ret); 1176 if (ret) 1177 return ret; 1178 if(len != length) 1179 krb5_abortx(context, "internal error in ASN.1 encoder"); 1180 1181 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len); 1182 if (ret) 1183 free(buf); 1184 1185 return 0; 1186 } 1187 1188 /* 1189 * Assumes caller always will free `out_md', even on error. 1190 */ 1191 1192 static krb5_error_code 1193 process_pa_data_to_md(krb5_context context, 1194 const krb5_creds *creds, 1195 const AS_REQ *a, 1196 krb5_get_init_creds_ctx *ctx, 1197 METHOD_DATA *in_md, 1198 METHOD_DATA **out_md, 1199 krb5_prompter_fct prompter, 1200 void *prompter_data) 1201 { 1202 krb5_error_code ret; 1203 1204 ALLOC(*out_md, 1); 1205 if (*out_md == NULL) 1206 return krb5_enomem(context); 1207 1208 (*out_md)->len = 0; 1209 (*out_md)->val = NULL; 1210 1211 if (_krb5_have_debug(context, 5)) { 1212 unsigned i; 1213 _krb5_debug(context, 5, "KDC send %d patypes", in_md->len); 1214 for (i = 0; i < in_md->len; i++) 1215 _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type); 1216 } 1217 1218 /* 1219 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no 1220 * need to expose our password protecting our PKCS12 key. 1221 */ 1222 1223 if (ctx->pk_init_ctx) { 1224 1225 _krb5_debug(context, 5, "krb5_get_init_creds: " 1226 "prepareing PKINIT padata (%s)", 1227 (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf"); 1228 1229 if (ctx->used_pa_types & USED_PKINIT_W2K) { 1230 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1231 "Already tried pkinit, looping"); 1232 return KRB5_GET_IN_TKT_LOOP; 1233 } 1234 1235 ret = pa_data_to_md_pkinit(context, a, creds->client, 1236 (ctx->used_pa_types & USED_PKINIT), 1237 ctx, *out_md); 1238 if (ret) 1239 return ret; 1240 1241 if (ctx->used_pa_types & USED_PKINIT) 1242 ctx->used_pa_types |= USED_PKINIT_W2K; 1243 else 1244 ctx->used_pa_types |= USED_PKINIT; 1245 1246 } else if (in_md->len != 0) { 1247 struct pa_info_data *paid, *ppaid; 1248 unsigned flag; 1249 1250 paid = calloc(1, sizeof(*paid)); 1251 if (paid == NULL) 1252 return krb5_enomem(context); 1253 1254 paid->etype = KRB5_ENCTYPE_NULL; 1255 ppaid = process_pa_info(context, creds->client, a, paid, in_md); 1256 1257 if (ppaid) 1258 flag = USED_ENC_TS_INFO; 1259 else 1260 flag = USED_ENC_TS_GUESS; 1261 1262 if (ctx->used_pa_types & flag) { 1263 if (ppaid) 1264 free_paid(context, ppaid); 1265 free(paid); 1266 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1267 "Already tried ENC-TS-%s, looping", 1268 flag == USED_ENC_TS_INFO ? "info" : "guess"); 1269 return KRB5_GET_IN_TKT_LOOP; 1270 } 1271 1272 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md); 1273 1274 ctx->used_pa_types |= flag; 1275 1276 if (ppaid) { 1277 if (ctx->ppaid) { 1278 free_paid(context, ctx->ppaid); 1279 free(ctx->ppaid); 1280 } 1281 ctx->ppaid = ppaid; 1282 } else 1283 free(paid); 1284 } 1285 1286 pa_data_add_pac_request(context, ctx, *out_md); 1287 1288 if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) { 1289 ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0); 1290 if (ret) 1291 return ret; 1292 } 1293 1294 if ((*out_md)->len == 0) { 1295 free(*out_md); 1296 *out_md = NULL; 1297 } 1298 1299 return 0; 1300 } 1301 1302 static krb5_error_code 1303 process_pa_data_to_key(krb5_context context, 1304 krb5_get_init_creds_ctx *ctx, 1305 krb5_creds *creds, 1306 AS_REQ *a, 1307 AS_REP *rep, 1308 const krb5_krbhst_info *hi, 1309 krb5_keyblock **key) 1310 { 1311 struct pa_info_data paid, *ppaid = NULL; 1312 krb5_error_code ret; 1313 krb5_enctype etype; 1314 PA_DATA *pa; 1315 1316 memset(&paid, 0, sizeof(paid)); 1317 1318 etype = rep->enc_part.etype; 1319 1320 if (rep->padata) { 1321 paid.etype = etype; 1322 ppaid = process_pa_info(context, creds->client, a, &paid, 1323 rep->padata); 1324 } 1325 if (ppaid == NULL) 1326 ppaid = ctx->ppaid; 1327 if (ppaid == NULL) { 1328 ret = krb5_get_pw_salt (context, creds->client, &paid.salt); 1329 if (ret) 1330 return ret; 1331 paid.etype = etype; 1332 paid.s2kparams = NULL; 1333 ppaid = &paid; 1334 } 1335 1336 pa = NULL; 1337 if (rep->padata) { 1338 int idx = 0; 1339 pa = krb5_find_padata(rep->padata->val, 1340 rep->padata->len, 1341 KRB5_PADATA_PK_AS_REP, 1342 &idx); 1343 if (pa == NULL) { 1344 idx = 0; 1345 pa = krb5_find_padata(rep->padata->val, 1346 rep->padata->len, 1347 KRB5_PADATA_PK_AS_REP_19, 1348 &idx); 1349 } 1350 } 1351 if (pa && ctx->pk_init_ctx) { 1352 #ifdef PKINIT 1353 _krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT"); 1354 1355 ret = _krb5_pk_rd_pa_reply(context, 1356 a->req_body.realm, 1357 ctx->pk_init_ctx, 1358 etype, 1359 hi, 1360 ctx->pk_nonce, 1361 &ctx->req_buffer, 1362 pa, 1363 key); 1364 #else 1365 ret = EINVAL; 1366 krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", "")); 1367 #endif 1368 } else if (ctx->keyseed) { 1369 _krb5_debug(context, 5, "krb5_get_init_creds: using keyproc"); 1370 ret = pa_data_to_key_plain(context, creds->client, ctx, 1371 ppaid->salt, ppaid->s2kparams, etype, key); 1372 } else { 1373 ret = EINVAL; 1374 krb5_set_error_message(context, ret, N_("No usable pa data type", "")); 1375 } 1376 1377 free_paid(context, &paid); 1378 return ret; 1379 } 1380 1381 /** 1382 * Start a new context to get a new initial credential. 1383 * 1384 * @param context A Kerberos 5 context. 1385 * @param client The Kerberos principal to get the credential for, if 1386 * NULL is given, the default principal is used as determined by 1387 * krb5_get_default_principal(). 1388 * @param prompter 1389 * @param prompter_data 1390 * @param start_time the time the ticket should start to be valid or 0 for now. 1391 * @param options a options structure, can be NULL for default options. 1392 * @param rctx A new allocated free with krb5_init_creds_free(). 1393 * 1394 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message(). 1395 * 1396 * @ingroup krb5_credential 1397 */ 1398 1399 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1400 krb5_init_creds_init(krb5_context context, 1401 krb5_principal client, 1402 krb5_prompter_fct prompter, 1403 void *prompter_data, 1404 krb5_deltat start_time, 1405 krb5_get_init_creds_opt *options, 1406 krb5_init_creds_context *rctx) 1407 { 1408 krb5_init_creds_context ctx; 1409 krb5_error_code ret; 1410 1411 *rctx = NULL; 1412 1413 ctx = calloc(1, sizeof(*ctx)); 1414 if (ctx == NULL) 1415 return krb5_enomem(context); 1416 1417 ret = get_init_creds_common(context, client, start_time, options, ctx); 1418 if (ret) { 1419 free(ctx); 1420 return ret; 1421 } 1422 1423 /* Set a new nonce. */ 1424 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce)); 1425 ctx->nonce &= 0x7fffffff; 1426 /* XXX these just needs to be the same when using Windows PK-INIT */ 1427 ctx->pk_nonce = ctx->nonce; 1428 1429 ctx->prompter = prompter; 1430 ctx->prompter_data = prompter_data; 1431 1432 *rctx = ctx; 1433 1434 return ret; 1435 } 1436 1437 /** 1438 * Sets the service that the is requested. This call is only neede for 1439 * special initial tickets, by default the a krbtgt is fetched in the default realm. 1440 * 1441 * @param context a Kerberos 5 context. 1442 * @param ctx a krb5_init_creds_context context. 1443 * @param service the service given as a string, for example 1444 * "kadmind/admin". If NULL, the default krbtgt in the clients 1445 * realm is set. 1446 * 1447 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1448 * @ingroup krb5_credential 1449 */ 1450 1451 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1452 krb5_init_creds_set_service(krb5_context context, 1453 krb5_init_creds_context ctx, 1454 const char *service) 1455 { 1456 krb5_const_realm client_realm; 1457 krb5_principal principal; 1458 krb5_error_code ret; 1459 1460 client_realm = krb5_principal_get_realm (context, ctx->cred.client); 1461 1462 if (service) { 1463 ret = krb5_parse_name (context, service, &principal); 1464 if (ret) 1465 return ret; 1466 krb5_principal_set_realm (context, principal, client_realm); 1467 } else { 1468 ret = krb5_make_principal(context, &principal, 1469 client_realm, KRB5_TGS_NAME, client_realm, 1470 NULL); 1471 if (ret) 1472 return ret; 1473 } 1474 1475 /* 1476 * This is for Windows RODC that are picky about what name type 1477 * the server principal have, and the really strange part is that 1478 * they are picky about the AS-REQ name type and not the TGS-REQ 1479 * later. Oh well. 1480 */ 1481 1482 if (krb5_principal_is_krbtgt(context, principal)) 1483 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST); 1484 1485 krb5_free_principal(context, ctx->cred.server); 1486 ctx->cred.server = principal; 1487 1488 return 0; 1489 } 1490 1491 /** 1492 * Sets the password that will use for the request. 1493 * 1494 * @param context a Kerberos 5 context. 1495 * @param ctx ctx krb5_init_creds_context context. 1496 * @param password the password to use. 1497 * 1498 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1499 * @ingroup krb5_credential 1500 */ 1501 1502 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1503 krb5_init_creds_set_password(krb5_context context, 1504 krb5_init_creds_context ctx, 1505 const char *password) 1506 { 1507 if (ctx->password) { 1508 size_t len; 1509 len = strlen(ctx->password); 1510 memset_s(ctx->password, len, 0, len); 1511 free(ctx->password); 1512 } 1513 if (password) { 1514 ctx->password = strdup(password); 1515 if (ctx->password == NULL) 1516 return krb5_enomem(context); 1517 ctx->keyseed = (void *) ctx->password; 1518 } else { 1519 ctx->keyseed = NULL; 1520 ctx->password = NULL; 1521 } 1522 1523 return 0; 1524 } 1525 1526 static krb5_error_code KRB5_CALLCONV 1527 keytab_key_proc(krb5_context context, krb5_enctype enctype, 1528 krb5_const_pointer keyseed, 1529 krb5_salt salt, krb5_data *s2kparms, 1530 krb5_keyblock **key) 1531 { 1532 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed); 1533 krb5_keytab keytab = args->keytab; 1534 krb5_principal principal = args->principal; 1535 krb5_error_code ret; 1536 krb5_keytab real_keytab; 1537 krb5_keytab_entry entry; 1538 1539 if(keytab == NULL) 1540 krb5_kt_default(context, &real_keytab); 1541 else 1542 real_keytab = keytab; 1543 1544 ret = krb5_kt_get_entry (context, real_keytab, principal, 1545 0, enctype, &entry); 1546 1547 if (keytab == NULL) 1548 krb5_kt_close (context, real_keytab); 1549 1550 if (ret) 1551 return ret; 1552 1553 ret = krb5_copy_keyblock (context, &entry.keyblock, key); 1554 krb5_kt_free_entry(context, &entry); 1555 return ret; 1556 } 1557 1558 1559 /** 1560 * Set the keytab to use for authentication. 1561 * 1562 * @param context a Kerberos 5 context. 1563 * @param ctx ctx krb5_init_creds_context context. 1564 * @param keytab the keytab to read the key from. 1565 * 1566 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1567 * @ingroup krb5_credential 1568 */ 1569 1570 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1571 krb5_init_creds_set_keytab(krb5_context context, 1572 krb5_init_creds_context ctx, 1573 krb5_keytab keytab) 1574 { 1575 krb5_keytab_key_proc_args *a; 1576 krb5_keytab_entry entry; 1577 krb5_kt_cursor cursor; 1578 krb5_enctype *etypes = NULL; 1579 krb5_error_code ret; 1580 size_t netypes = 0; 1581 int kvno = 0, found = 0; 1582 1583 a = malloc(sizeof(*a)); 1584 if (a == NULL) 1585 return krb5_enomem(context); 1586 1587 a->principal = ctx->cred.client; 1588 a->keytab = keytab; 1589 1590 ctx->keytab_data = a; 1591 ctx->keyseed = (void *)a; 1592 ctx->keyproc = keytab_key_proc; 1593 1594 /* 1595 * We need to the KDC what enctypes we support for this keytab, 1596 * esp if the keytab is really a password based entry, then the 1597 * KDC might have more enctypes in the database then what we have 1598 * in the keytab. 1599 */ 1600 1601 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 1602 if(ret) 1603 goto out; 1604 1605 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){ 1606 void *ptr; 1607 1608 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client)) 1609 goto next; 1610 1611 found = 1; 1612 1613 /* check if we ahve this kvno already */ 1614 if (entry.vno > kvno) { 1615 /* remove old list of etype */ 1616 if (etypes) 1617 free(etypes); 1618 etypes = NULL; 1619 netypes = 0; 1620 kvno = entry.vno; 1621 } else if (entry.vno != kvno) 1622 goto next; 1623 1624 /* check if enctype is supported */ 1625 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0) 1626 goto next; 1627 1628 /* add enctype to supported list */ 1629 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2)); 1630 if (ptr == NULL) { 1631 free(etypes); 1632 ret = krb5_enomem(context); 1633 goto out; 1634 } 1635 1636 etypes = ptr; 1637 etypes[netypes] = entry.keyblock.keytype; 1638 etypes[netypes + 1] = ETYPE_NULL; 1639 netypes++; 1640 next: 1641 krb5_kt_free_entry(context, &entry); 1642 } 1643 krb5_kt_end_seq_get(context, keytab, &cursor); 1644 1645 if (etypes) { 1646 if (ctx->etypes) 1647 free(ctx->etypes); 1648 ctx->etypes = etypes; 1649 } 1650 1651 out: 1652 if (!found) { 1653 if (ret == 0) 1654 ret = KRB5_KT_NOTFOUND; 1655 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0); 1656 } 1657 1658 return ret; 1659 } 1660 1661 static krb5_error_code KRB5_CALLCONV 1662 keyblock_key_proc(krb5_context context, krb5_enctype enctype, 1663 krb5_const_pointer keyseed, 1664 krb5_salt salt, krb5_data *s2kparms, 1665 krb5_keyblock **key) 1666 { 1667 return krb5_copy_keyblock (context, keyseed, key); 1668 } 1669 1670 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1671 krb5_init_creds_set_keyblock(krb5_context context, 1672 krb5_init_creds_context ctx, 1673 krb5_keyblock *keyblock) 1674 { 1675 ctx->keyseed = (void *)keyblock; 1676 ctx->keyproc = keyblock_key_proc; 1677 1678 return 0; 1679 } 1680 1681 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1682 krb5_init_creds_set_fast_ccache(krb5_context context, 1683 krb5_init_creds_context ctx, 1684 krb5_ccache fast_ccache) 1685 { 1686 ctx->fast_state.armor_ccache = fast_ccache; 1687 ctx->fast_state.flags |= KRB5_FAST_REQUIRED; 1688 return 0; 1689 } 1690 1691 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1692 krb5_init_creds_set_fast_ap_armor_service(krb5_context context, 1693 krb5_init_creds_context ctx, 1694 krb5_const_principal armor_service) 1695 { 1696 krb5_error_code ret; 1697 1698 if (ctx->fast_state.armor_service) 1699 krb5_free_principal(context, ctx->fast_state.armor_service); 1700 if (armor_service) { 1701 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service); 1702 if (ret) 1703 return ret; 1704 } else { 1705 ctx->fast_state.armor_service = NULL; 1706 } 1707 ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE; 1708 return 0; 1709 } 1710 1711 /* 1712 * FAST 1713 */ 1714 1715 static krb5_error_code 1716 check_fast(krb5_context context, struct fast_state *state) 1717 { 1718 if (state->flags & KRB5_FAST_EXPECTED) { 1719 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 1720 "Expected FAST, but no FAST " 1721 "was in the response from the KDC"); 1722 return KRB5KRB_AP_ERR_MODIFIED; 1723 } 1724 return 0; 1725 } 1726 1727 1728 static krb5_error_code 1729 fast_unwrap_as_rep(krb5_context context, int32_t nonce, 1730 krb5_data *chksumdata, 1731 struct fast_state *state, AS_REP *rep) 1732 { 1733 PA_FX_FAST_REPLY fxfastrep; 1734 KrbFastResponse fastrep; 1735 krb5_error_code ret; 1736 PA_DATA *pa = NULL; 1737 int idx = 0; 1738 1739 if (state->armor_crypto == NULL || rep->padata == NULL) 1740 return check_fast(context, state); 1741 1742 /* find PA_FX_FAST_REPLY */ 1743 1744 pa = krb5_find_padata(rep->padata->val, rep->padata->len, 1745 KRB5_PADATA_FX_FAST, &idx); 1746 if (pa == NULL) 1747 return check_fast(context, state); 1748 1749 memset(&fxfastrep, 0, sizeof(fxfastrep)); 1750 memset(&fastrep, 0, sizeof(fastrep)); 1751 1752 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL); 1753 if (ret) 1754 return ret; 1755 1756 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) { 1757 krb5_data data; 1758 ret = krb5_decrypt_EncryptedData(context, 1759 state->armor_crypto, 1760 KRB5_KU_FAST_REP, 1761 &fxfastrep.u.armored_data.enc_fast_rep, 1762 &data); 1763 if (ret) 1764 goto out; 1765 1766 ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL); 1767 krb5_data_free(&data); 1768 if (ret) 1769 goto out; 1770 1771 } else { 1772 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1773 goto out; 1774 } 1775 1776 free_METHOD_DATA(rep->padata); 1777 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata); 1778 if (ret) 1779 goto out; 1780 1781 if (fastrep.strengthen_key) { 1782 if (state->strengthen_key) 1783 krb5_free_keyblock(context, state->strengthen_key); 1784 1785 ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key); 1786 if (ret) 1787 goto out; 1788 } 1789 1790 if (nonce != fastrep.nonce) { 1791 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1792 goto out; 1793 } 1794 if (fastrep.finished) { 1795 PrincipalName cname; 1796 krb5_realm crealm = NULL; 1797 1798 if (chksumdata == NULL) { 1799 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1800 goto out; 1801 } 1802 1803 ret = krb5_verify_checksum(context, state->armor_crypto, 1804 KRB5_KU_FAST_FINISHED, 1805 chksumdata->data, chksumdata->length, 1806 &fastrep.finished->ticket_checksum); 1807 if (ret) 1808 goto out; 1809 1810 /* update */ 1811 ret = copy_Realm(&fastrep.finished->crealm, &crealm); 1812 if (ret) 1813 goto out; 1814 free_Realm(&rep->crealm); 1815 rep->crealm = crealm; 1816 1817 ret = copy_PrincipalName(&fastrep.finished->cname, &cname); 1818 if (ret) 1819 goto out; 1820 free_PrincipalName(&rep->cname); 1821 rep->cname = cname; 1822 1823 #if 0 /* store authenticated checksum as kdc-offset */ 1824 fastrep->finished.timestamp; 1825 fastrep->finished.usec = 0; 1826 #endif 1827 1828 } else if (chksumdata) { 1829 /* expected fastrep.finish but didn't get it */ 1830 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1831 } 1832 1833 out: 1834 free_PA_FX_FAST_REPLY(&fxfastrep); 1835 1836 return ret; 1837 } 1838 1839 static krb5_error_code 1840 fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error) 1841 { 1842 if (state->armor_crypto == NULL) 1843 return check_fast(context, state); 1844 1845 return 0; 1846 } 1847 1848 krb5_error_code 1849 _krb5_make_fast_ap_fxarmor(krb5_context context, 1850 krb5_ccache armor_ccache, 1851 krb5_data *armor_value, 1852 krb5_keyblock *armor_key, 1853 krb5_crypto *armor_crypto) 1854 { 1855 krb5_auth_context auth_context = NULL; 1856 krb5_creds cred, *credp = NULL; 1857 krb5_error_code ret; 1858 krb5_data empty; 1859 1860 krb5_data_zero(&empty); 1861 1862 memset(&cred, 0, sizeof(cred)); 1863 1864 ret = krb5_auth_con_init (context, &auth_context); 1865 if (ret) 1866 goto out; 1867 1868 ret = krb5_cc_get_principal(context, armor_ccache, &cred.client); 1869 if (ret) 1870 goto out; 1871 1872 ret = krb5_make_principal(context, &cred.server, 1873 cred.client->realm, 1874 KRB5_TGS_NAME, 1875 cred.client->realm, 1876 NULL); 1877 if (ret) { 1878 krb5_free_principal(context, cred.client); 1879 goto out; 1880 } 1881 1882 ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp); 1883 krb5_free_principal(context, cred.server); 1884 krb5_free_principal(context, cred.client); 1885 if (ret) 1886 goto out; 1887 1888 ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty); 1889 if (ret) 1890 goto out; 1891 1892 ret = krb5_mk_req_extended(context, 1893 &auth_context, 1894 AP_OPTS_USE_SUBKEY, 1895 NULL, 1896 credp, 1897 armor_value); 1898 krb5_free_creds(context, credp); 1899 if (ret) 1900 goto out; 1901 1902 ret = _krb5_fast_armor_key(context, 1903 auth_context->local_subkey, 1904 auth_context->keyblock, 1905 armor_key, 1906 armor_crypto); 1907 if (ret) 1908 goto out; 1909 1910 out: 1911 krb5_auth_con_free(context, auth_context); 1912 return ret; 1913 } 1914 1915 #ifndef WIN32 1916 static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT; 1917 static heim_ipc armor_service = NULL; 1918 1919 static void 1920 fast_armor_init_ipc(void *ctx) 1921 { 1922 heim_ipc *ipc = ctx; 1923 heim_ipc_init_context("ANY:org.h5l.armor-service", ipc); 1924 } 1925 #endif /* WIN32 */ 1926 1927 1928 static krb5_error_code 1929 make_fast_ap_fxarmor(krb5_context context, 1930 struct fast_state *state, 1931 const char *realm, 1932 KrbFastArmor **armor) 1933 { 1934 KrbFastArmor *fxarmor = NULL; 1935 krb5_error_code ret; 1936 1937 if (state->armor_crypto) 1938 krb5_crypto_destroy(context, state->armor_crypto); 1939 krb5_free_keyblock_contents(context, &state->armor_key); 1940 1941 1942 ALLOC(fxarmor, 1); 1943 if (fxarmor == NULL) 1944 return krb5_enomem(context); 1945 1946 if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) { 1947 #ifdef WIN32 1948 krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows"); 1949 ret = ENOTSUP; 1950 goto out; 1951 #else /* WIN32 */ 1952 KERB_ARMOR_SERVICE_REPLY msg; 1953 krb5_data request, reply; 1954 1955 heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc); 1956 if (armor_service == NULL) { 1957 krb5_set_error_message(context, ENOENT, "Failed to open fast armor service"); 1958 ret = ENOENT; 1959 goto out; 1960 } 1961 1962 krb5_data_zero(&reply); 1963 1964 request.data = rk_UNCONST(realm); 1965 request.length = strlen(realm); 1966 1967 ret = heim_ipc_call(armor_service, &request, &reply, NULL); 1968 heim_release(send); 1969 if (ret) { 1970 krb5_set_error_message(context, ret, "Failed to get armor service credential"); 1971 goto out; 1972 } 1973 1974 ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL); 1975 krb5_data_free(&reply); 1976 if (ret) 1977 goto out; 1978 1979 ret = copy_KrbFastArmor(fxarmor, &msg.armor); 1980 if (ret) { 1981 free_KERB_ARMOR_SERVICE_REPLY(&msg); 1982 goto out; 1983 } 1984 1985 ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key); 1986 free_KERB_ARMOR_SERVICE_REPLY(&msg); 1987 if (ret) 1988 goto out; 1989 1990 ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto); 1991 if (ret) 1992 goto out; 1993 #endif /* WIN32 */ 1994 } else { 1995 1996 fxarmor->armor_type = 1; 1997 1998 ret = _krb5_make_fast_ap_fxarmor(context, 1999 state->armor_ccache, 2000 &fxarmor->armor_value, 2001 &state->armor_key, 2002 &state->armor_crypto); 2003 if (ret) 2004 goto out; 2005 } 2006 2007 2008 *armor = fxarmor; 2009 fxarmor = NULL; 2010 out: 2011 if (fxarmor) { 2012 free_KrbFastArmor(fxarmor); 2013 free(fxarmor); 2014 } 2015 return ret; 2016 } 2017 2018 static krb5_error_code 2019 fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req) 2020 { 2021 KrbFastArmor *fxarmor = NULL; 2022 PA_FX_FAST_REQUEST fxreq; 2023 krb5_error_code ret; 2024 KrbFastReq fastreq; 2025 krb5_data data; 2026 size_t size; 2027 2028 if (state->flags & KRB5_FAST_DISABLED) { 2029 _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping"); 2030 return 0; 2031 } 2032 2033 memset(&fxreq, 0, sizeof(fxreq)); 2034 memset(&fastreq, 0, sizeof(fastreq)); 2035 krb5_data_zero(&data); 2036 2037 if (state->armor_crypto == NULL) { 2038 if (state->armor_ccache) { 2039 /* 2040 * Instead of keeping state in FX_COOKIE in the KDC, we 2041 * rebuild a new armor key for every request, because this 2042 * is what the MIT KDC expect and RFC6113 is vage about 2043 * what the behavior should be. 2044 */ 2045 state->type = choice_PA_FX_FAST_REQUEST_armored_data; 2046 } else { 2047 return check_fast(context, state); 2048 } 2049 } 2050 2051 state->flags |= KRB5_FAST_EXPECTED; 2052 2053 fastreq.fast_options.hide_client_names = 1; 2054 2055 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body); 2056 free_KDC_REQ_BODY(&req->req_body); 2057 2058 req->req_body.realm = strdup(KRB5_ANON_REALM); 2059 if ((ALLOC(req->req_body.cname, 1)) != NULL) { 2060 req->req_body.cname->name_type = KRB5_NT_WELLKNOWN; 2061 if ((ALLOC(req->req_body.cname->name_string.val, 2)) != NULL) { 2062 req->req_body.cname->name_string.len = 2; 2063 req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME); 2064 req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME); 2065 if (req->req_body.cname->name_string.val[0] == NULL || 2066 req->req_body.cname->name_string.val[1] == NULL) 2067 ret = krb5_enomem(context); 2068 } else 2069 ret = krb5_enomem(context); 2070 } else 2071 ret = krb5_enomem(context); 2072 if ((ALLOC(req->req_body.till, 1)) != NULL) 2073 *req->req_body.till = 0; 2074 else 2075 ret = krb5_enomem(context); 2076 if (ret) 2077 goto out; 2078 2079 if (req->padata) { 2080 ret = copy_METHOD_DATA(req->padata, &fastreq.padata); 2081 free_METHOD_DATA(req->padata); 2082 } else { 2083 if ((ALLOC(req->padata, 1)) == NULL) 2084 ret = krb5_enomem(context); 2085 } 2086 if (ret) 2087 goto out; 2088 2089 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret); 2090 if (ret) 2091 goto out; 2092 heim_assert(data.length == size, "ASN.1 internal error"); 2093 2094 fxreq.element = state->type; 2095 2096 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) { 2097 size_t len; 2098 void *buf; 2099 2100 ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor); 2101 if (ret) 2102 goto out; 2103 2104 heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started"); 2105 2106 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret); 2107 if (ret) 2108 goto out; 2109 heim_assert(len == size, "ASN.1 internal error"); 2110 2111 ret = krb5_create_checksum(context, state->armor_crypto, 2112 KRB5_KU_FAST_REQ_CHKSUM, 0, 2113 buf, len, 2114 &fxreq.u.armored_data.req_checksum); 2115 free(buf); 2116 if (ret) 2117 goto out; 2118 2119 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto, 2120 KRB5_KU_FAST_ENC, 2121 data.data, 2122 data.length, 2123 0, 2124 &fxreq.u.armored_data.enc_fast_req); 2125 krb5_data_free(&data); 2126 if (ret) 2127 goto out; 2128 2129 } else { 2130 krb5_data_free(&data); 2131 heim_assert(false, "unknown FAST type, internal error"); 2132 } 2133 2134 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret); 2135 if (ret) 2136 goto out; 2137 heim_assert(data.length == size, "ASN.1 internal error"); 2138 2139 2140 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length); 2141 if (ret) 2142 goto out; 2143 krb5_data_zero(&data); 2144 2145 out: 2146 free_PA_FX_FAST_REQUEST(&fxreq); 2147 free_KrbFastReq(&fastreq); 2148 if (fxarmor) { 2149 free_KrbFastArmor(fxarmor); 2150 free(fxarmor); 2151 } 2152 krb5_data_free(&data); 2153 2154 return ret; 2155 } 2156 2157 2158 /** 2159 * The core loop if krb5_get_init_creds() function family. Create the 2160 * packets and have the caller send them off to the KDC. 2161 * 2162 * If the caller want all work been done for them, use 2163 * krb5_init_creds_get() instead. 2164 * 2165 * @param context a Kerberos 5 context. 2166 * @param ctx ctx krb5_init_creds_context context. 2167 * @param in input data from KDC, first round it should be reset by krb5_data_zer(). 2168 * @param out reply to KDC. 2169 * @param hostinfo KDC address info, first round it can be NULL. 2170 * @param flags status of the round, if 2171 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round. 2172 * 2173 * @return 0 for success, or an Kerberos 5 error code, see 2174 * krb5_get_error_message(). 2175 * 2176 * @ingroup krb5_credential 2177 */ 2178 2179 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2180 krb5_init_creds_step(krb5_context context, 2181 krb5_init_creds_context ctx, 2182 krb5_data *in, 2183 krb5_data *out, 2184 krb5_krbhst_info *hostinfo, 2185 unsigned int *flags) 2186 { 2187 krb5_error_code ret; 2188 size_t len = 0; 2189 size_t size; 2190 AS_REQ req2; 2191 2192 krb5_data_zero(out); 2193 2194 if (ctx->as_req.req_body.cname == NULL) { 2195 ret = init_as_req(context, ctx->flags, &ctx->cred, 2196 ctx->addrs, ctx->etypes, &ctx->as_req); 2197 if (ret) { 2198 free_init_creds_ctx(context, ctx); 2199 return ret; 2200 } 2201 } 2202 2203 #define MAX_PA_COUNTER 10 2204 if (ctx->pa_counter > MAX_PA_COUNTER) { 2205 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 2206 N_("Looping %d times while getting " 2207 "initial credentials", ""), 2208 ctx->pa_counter); 2209 return KRB5_GET_IN_TKT_LOOP; 2210 } 2211 ctx->pa_counter++; 2212 2213 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter); 2214 2215 /* Lets process the input packet */ 2216 if (in && in->length) { 2217 krb5_kdc_rep rep; 2218 2219 memset(&rep, 0, sizeof(rep)); 2220 2221 _krb5_debug(context, 5, "krb5_get_init_creds: processing input"); 2222 2223 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size); 2224 if (ret == 0) { 2225 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC; 2226 krb5_data data; 2227 2228 /* 2229 * Unwrap AS-REP 2230 */ 2231 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, 2232 &rep.kdc_rep.ticket, &size, ret); 2233 if (ret) 2234 goto out; 2235 heim_assert(data.length == size, "ASN.1 internal error"); 2236 2237 ret = fast_unwrap_as_rep(context, ctx->nonce, &data, 2238 &ctx->fast_state, &rep.kdc_rep); 2239 krb5_data_free(&data); 2240 if (ret) 2241 goto out; 2242 2243 /* 2244 * Now check and extract the ticket 2245 */ 2246 2247 if (ctx->flags.canonicalize) { 2248 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; 2249 eflags |= EXTRACT_TICKET_MATCH_REALM; 2250 } 2251 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) 2252 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; 2253 if (ctx->flags.request_anonymous) 2254 eflags |= EXTRACT_TICKET_MATCH_ANON; 2255 2256 ret = process_pa_data_to_key(context, ctx, &ctx->cred, 2257 &ctx->as_req, &rep.kdc_rep, 2258 hostinfo, &ctx->fast_state.reply_key); 2259 if (ret) { 2260 free_AS_REP(&rep.kdc_rep); 2261 goto out; 2262 } 2263 2264 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket"); 2265 2266 ret = _krb5_extract_ticket(context, 2267 &rep, 2268 &ctx->cred, 2269 ctx->fast_state.reply_key, 2270 NULL, 2271 KRB5_KU_AS_REP_ENC_PART, 2272 NULL, 2273 ctx->nonce, 2274 eflags, 2275 &ctx->req_buffer, 2276 NULL, 2277 NULL); 2278 if (ret == 0 && ctx->pk_init_ctx) { 2279 PA_DATA *pa_pkinit_kx; 2280 int idx = 0; 2281 2282 pa_pkinit_kx = 2283 krb5_find_padata(rep.kdc_rep.padata->val, 2284 rep.kdc_rep.padata->len, 2285 KRB5_PADATA_PKINIT_KX, 2286 &idx); 2287 2288 ret = _krb5_pk_kx_confirm(context, ctx->pk_init_ctx, 2289 ctx->fast_state.reply_key, 2290 &ctx->cred.session, 2291 pa_pkinit_kx); 2292 if (ret) 2293 krb5_set_error_message(context, ret, 2294 N_("Failed to confirm PA-PKINIT-KX", "")); 2295 else if (pa_pkinit_kx != NULL) 2296 ctx->ic_flags |= KRB5_INIT_CREDS_PKINIT_KX_VALID; 2297 } 2298 if (ret == 0) 2299 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part); 2300 2301 krb5_free_keyblock(context, ctx->fast_state.reply_key); 2302 ctx->fast_state.reply_key = NULL; 2303 *flags = 0; 2304 2305 free_AS_REP(&rep.kdc_rep); 2306 free_EncASRepPart(&rep.enc_part); 2307 2308 return ret; 2309 2310 } else { 2311 /* let's try to parse it as a KRB-ERROR */ 2312 2313 _krb5_debug(context, 5, "krb5_get_init_creds: got an error"); 2314 2315 free_KRB_ERROR(&ctx->error); 2316 2317 ret = krb5_rd_error(context, in, &ctx->error); 2318 if(ret && in->length && ((char*)in->data)[0] == 4) 2319 ret = KRB5KRB_AP_ERR_V4_REPLY; 2320 if (ret) { 2321 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error"); 2322 goto out; 2323 } 2324 2325 /* 2326 * Unwrap KRB-ERROR 2327 */ 2328 ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error); 2329 if (ret) 2330 goto out; 2331 2332 /* 2333 * 2334 */ 2335 2336 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred); 2337 2338 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret); 2339 2340 /* 2341 * If no preauth was set and KDC requires it, give it one 2342 * more try. 2343 */ 2344 2345 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) { 2346 2347 free_METHOD_DATA(&ctx->md); 2348 memset_s(&ctx->md, sizeof(ctx->md), 0, sizeof(ctx->md)); 2349 2350 if (ctx->error.e_data) { 2351 ret = decode_METHOD_DATA(ctx->error.e_data->data, 2352 ctx->error.e_data->length, 2353 &ctx->md, 2354 NULL); 2355 if (ret) 2356 krb5_set_error_message(context, ret, 2357 N_("Failed to decode METHOD-DATA", "")); 2358 } else { 2359 krb5_set_error_message(context, ret, 2360 N_("Preauth required but no preauth " 2361 "options send by KDC", "")); 2362 } 2363 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) { 2364 /* 2365 * Try adapt to timeskrew when we are using pre-auth, and 2366 * if there was a time skew, try again. 2367 */ 2368 krb5_set_real_time(context, ctx->error.stime, -1); 2369 if (context->kdc_sec_offset) 2370 ret = 0; 2371 2372 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d", 2373 context->kdc_sec_offset); 2374 2375 ctx->used_pa_types = 0; 2376 2377 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) { 2378 /* client referal to a new realm */ 2379 2380 if (ctx->error.crealm == NULL) { 2381 krb5_set_error_message(context, ret, 2382 N_("Got a client referral, not but no realm", "")); 2383 goto out; 2384 } 2385 _krb5_debug(context, 5, 2386 "krb5_get_init_creds: got referal to realm %s", 2387 *ctx->error.crealm); 2388 2389 ret = krb5_principal_set_realm(context, 2390 ctx->cred.client, 2391 *ctx->error.crealm); 2392 if (ret) 2393 goto out; 2394 2395 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) { 2396 ret = krb5_init_creds_set_service(context, ctx, NULL); 2397 if (ret) 2398 goto out; 2399 } 2400 2401 free_AS_REQ(&ctx->as_req); 2402 memset_s(&ctx->as_req, sizeof(ctx->as_req), 0, sizeof(ctx->as_req)); 2403 2404 ctx->used_pa_types = 0; 2405 } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) { 2406 char buf2[1024]; 2407 2408 ctx->runflags.change_password = 1; 2409 2410 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL); 2411 2412 2413 /* try to avoid recursion */ 2414 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0) 2415 goto out; 2416 2417 /* don't try to change password where then where none */ 2418 if (ctx->prompter == NULL) 2419 goto out; 2420 2421 ret = change_password(context, 2422 ctx->cred.client, 2423 ctx->password, 2424 buf2, 2425 sizeof(buf2), 2426 ctx->prompter, 2427 ctx->prompter_data, 2428 NULL); 2429 if (ret) 2430 goto out; 2431 2432 krb5_init_creds_set_password(context, ctx, buf2); 2433 2434 ctx->used_pa_types = 0; 2435 ret = 0; 2436 2437 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) { 2438 2439 if (ctx->fast_state.flags & KRB5_FAST_DISABLED) 2440 goto out; 2441 if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED)) 2442 goto out; 2443 2444 _krb5_debug(context, 10, "preauth failed with FAST, " 2445 "and told by KD or user, trying w/o FAST"); 2446 2447 ctx->fast_state.flags |= KRB5_FAST_DISABLED; 2448 ctx->used_pa_types = 0; 2449 ret = 0; 2450 } 2451 if (ret) 2452 goto out; 2453 } 2454 } 2455 2456 if (ctx->as_req.req_body.cname == NULL) { 2457 ret = init_as_req(context, ctx->flags, &ctx->cred, 2458 ctx->addrs, ctx->etypes, &ctx->as_req); 2459 if (ret) { 2460 free_init_creds_ctx(context, ctx); 2461 return ret; 2462 } 2463 } 2464 2465 if (ctx->as_req.padata) { 2466 free_METHOD_DATA(ctx->as_req.padata); 2467 free(ctx->as_req.padata); 2468 ctx->as_req.padata = NULL; 2469 } 2470 2471 /* Set a new nonce. */ 2472 ctx->as_req.req_body.nonce = ctx->nonce; 2473 2474 /* fill_in_md_data */ 2475 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx, 2476 &ctx->md, &ctx->as_req.padata, 2477 ctx->prompter, ctx->prompter_data); 2478 if (ret) 2479 goto out; 2480 2481 /* 2482 * Wrap with FAST 2483 */ 2484 copy_AS_REQ(&ctx->as_req, &req2); 2485 2486 ret = fast_wrap_req(context, &ctx->fast_state, &req2); 2487 if (ret) { 2488 free_AS_REQ(&req2); 2489 goto out; 2490 } 2491 2492 krb5_data_free(&ctx->req_buffer); 2493 2494 ASN1_MALLOC_ENCODE(AS_REQ, 2495 ctx->req_buffer.data, ctx->req_buffer.length, 2496 &req2, &len, ret); 2497 free_AS_REQ(&req2); 2498 if (ret) 2499 goto out; 2500 if(len != ctx->req_buffer.length) 2501 krb5_abortx(context, "internal error in ASN.1 encoder"); 2502 2503 out->data = ctx->req_buffer.data; 2504 out->length = ctx->req_buffer.length; 2505 2506 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; 2507 2508 return 0; 2509 out: 2510 return ret; 2511 } 2512 2513 /** 2514 * Extract the newly acquired credentials from krb5_init_creds_context 2515 * context. 2516 * 2517 * @param context A Kerberos 5 context. 2518 * @param ctx 2519 * @param cred credentials, free with krb5_free_cred_contents(). 2520 * 2521 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message(). 2522 */ 2523 2524 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2525 krb5_init_creds_get_creds(krb5_context context, 2526 krb5_init_creds_context ctx, 2527 krb5_creds *cred) 2528 { 2529 return krb5_copy_creds_contents(context, &ctx->cred, cred); 2530 } 2531 2532 /** 2533 * Get the last error from the transaction. 2534 * 2535 * @return Returns 0 or an error code 2536 * 2537 * @ingroup krb5_credential 2538 */ 2539 2540 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2541 krb5_init_creds_get_error(krb5_context context, 2542 krb5_init_creds_context ctx, 2543 KRB_ERROR *error) 2544 { 2545 krb5_error_code ret; 2546 2547 ret = copy_KRB_ERROR(&ctx->error, error); 2548 if (ret) 2549 krb5_enomem(context); 2550 2551 return ret; 2552 } 2553 2554 /** 2555 * 2556 * @ingroup krb5_credential 2557 */ 2558 2559 krb5_error_code 2560 krb5_init_creds_store(krb5_context context, 2561 krb5_init_creds_context ctx, 2562 krb5_ccache id) 2563 { 2564 krb5_error_code ret; 2565 2566 if (ctx->cred.client == NULL) { 2567 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; 2568 krb5_set_error_message(context, ret, "init creds not completed yet"); 2569 return ret; 2570 } 2571 2572 ret = krb5_cc_initialize(context, id, ctx->cred.client); 2573 if (ret) 2574 return ret; 2575 2576 ret = krb5_cc_store_cred(context, id, &ctx->cred); 2577 if (ret) 2578 return ret; 2579 2580 if (ctx->cred.flags.b.enc_pa_rep) { 2581 krb5_data data = { 3, rk_UNCONST("yes") }; 2582 ret = krb5_cc_set_config(context, id, ctx->cred.server, 2583 "fast_avail", &data); 2584 if (ret) 2585 return ret; 2586 } 2587 2588 return ret; 2589 } 2590 2591 /** 2592 * Free the krb5_init_creds_context allocated by krb5_init_creds_init(). 2593 * 2594 * @param context A Kerberos 5 context. 2595 * @param ctx The krb5_init_creds_context to free. 2596 * 2597 * @ingroup krb5_credential 2598 */ 2599 2600 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 2601 krb5_init_creds_free(krb5_context context, 2602 krb5_init_creds_context ctx) 2603 { 2604 free_init_creds_ctx(context, ctx); 2605 free(ctx); 2606 } 2607 2608 /** 2609 * Get new credentials as setup by the krb5_init_creds_context. 2610 * 2611 * @param context A Kerberos 5 context. 2612 * @param ctx The krb5_init_creds_context to process. 2613 * 2614 * @ingroup krb5_credential 2615 */ 2616 2617 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2618 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) 2619 { 2620 krb5_sendto_ctx stctx = NULL; 2621 krb5_krbhst_info *hostinfo = NULL; 2622 krb5_error_code ret; 2623 krb5_data in, out; 2624 unsigned int flags = 0; 2625 2626 krb5_data_zero(&in); 2627 krb5_data_zero(&out); 2628 2629 ret = krb5_sendto_ctx_alloc(context, &stctx); 2630 if (ret) 2631 goto out; 2632 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); 2633 2634 while (1) { 2635 flags = 0; 2636 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags); 2637 krb5_data_free(&in); 2638 if (ret) 2639 goto out; 2640 2641 if ((flags & 1) == 0) 2642 break; 2643 2644 ret = krb5_sendto_context (context, stctx, &out, 2645 ctx->cred.client->realm, &in); 2646 if (ret) 2647 goto out; 2648 2649 } 2650 2651 out: 2652 if (stctx) 2653 krb5_sendto_ctx_free(context, stctx); 2654 2655 return ret; 2656 } 2657 2658 /** 2659 * Get new credentials using password. 2660 * 2661 * @ingroup krb5_credential 2662 */ 2663 2664 2665 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2666 krb5_get_init_creds_password(krb5_context context, 2667 krb5_creds *creds, 2668 krb5_principal client, 2669 const char *password, 2670 krb5_prompter_fct prompter, 2671 void *data, 2672 krb5_deltat start_time, 2673 const char *in_tkt_service, 2674 krb5_get_init_creds_opt *options) 2675 { 2676 krb5_init_creds_context ctx; 2677 char buf[BUFSIZ], buf2[BUFSIZ]; 2678 krb5_error_code ret; 2679 int chpw = 0; 2680 2681 again: 2682 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx); 2683 if (ret) 2684 goto out; 2685 2686 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2687 if (ret) 2688 goto out; 2689 2690 if (prompter != NULL && ctx->password == NULL && password == NULL) { 2691 krb5_prompt prompt; 2692 krb5_data password_data; 2693 char *p, *q = NULL; 2694 int aret; 2695 2696 ret = krb5_unparse_name(context, client, &p); 2697 if (ret) 2698 goto out; 2699 2700 aret = asprintf(&q, "%s's Password: ", p); 2701 free (p); 2702 if (aret == -1 || q == NULL) { 2703 ret = krb5_enomem(context); 2704 goto out; 2705 } 2706 prompt.prompt = q; 2707 password_data.data = buf; 2708 password_data.length = sizeof(buf); 2709 prompt.hidden = 1; 2710 prompt.reply = &password_data; 2711 prompt.type = KRB5_PROMPT_TYPE_PASSWORD; 2712 2713 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); 2714 free (q); 2715 if (ret) { 2716 memset_s(buf, sizeof(buf), 0, sizeof(buf)); 2717 ret = KRB5_LIBOS_PWDINTR; 2718 krb5_clear_error_message (context); 2719 goto out; 2720 } 2721 password = password_data.data; 2722 } 2723 2724 if (password) { 2725 ret = krb5_init_creds_set_password(context, ctx, password); 2726 if (ret) 2727 goto out; 2728 } 2729 2730 ret = krb5_init_creds_get(context, ctx); 2731 2732 if (ret == 0) 2733 krb5_process_last_request(context, options, ctx); 2734 2735 2736 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) { 2737 /* try to avoid recursion */ 2738 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0) 2739 goto out; 2740 2741 /* don't try to change password where then where none */ 2742 if (prompter == NULL) 2743 goto out; 2744 2745 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) && 2746 !options->change_password_prompt) 2747 goto out; 2748 2749 ret = change_password (context, 2750 client, 2751 ctx->password, 2752 buf2, 2753 sizeof(buf2), 2754 prompter, 2755 data, 2756 options); 2757 if (ret) 2758 goto out; 2759 password = buf2; 2760 chpw = 1; 2761 krb5_init_creds_free(context, ctx); 2762 goto again; 2763 } 2764 2765 out: 2766 if (ret == 0) 2767 krb5_init_creds_get_creds(context, ctx, creds); 2768 2769 if (ctx) 2770 krb5_init_creds_free(context, ctx); 2771 2772 memset_s(buf, sizeof(buf), 0, sizeof(buf)); 2773 memset_s(buf2, sizeof(buf), 0, sizeof(buf2)); 2774 return ret; 2775 } 2776 2777 /** 2778 * Get new credentials using keyblock. 2779 * 2780 * @ingroup krb5_credential 2781 */ 2782 2783 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2784 krb5_get_init_creds_keyblock(krb5_context context, 2785 krb5_creds *creds, 2786 krb5_principal client, 2787 krb5_keyblock *keyblock, 2788 krb5_deltat start_time, 2789 const char *in_tkt_service, 2790 krb5_get_init_creds_opt *options) 2791 { 2792 krb5_init_creds_context ctx; 2793 krb5_error_code ret; 2794 2795 memset(creds, 0, sizeof(*creds)); 2796 2797 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2798 if (ret) 2799 goto out; 2800 2801 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2802 if (ret) 2803 goto out; 2804 2805 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock); 2806 if (ret) 2807 goto out; 2808 2809 ret = krb5_init_creds_get(context, ctx); 2810 2811 if (ret == 0) 2812 krb5_process_last_request(context, options, ctx); 2813 2814 out: 2815 if (ret == 0) 2816 krb5_init_creds_get_creds(context, ctx, creds); 2817 2818 if (ctx) 2819 krb5_init_creds_free(context, ctx); 2820 2821 return ret; 2822 } 2823 2824 /** 2825 * Get new credentials using keytab. 2826 * 2827 * @ingroup krb5_credential 2828 */ 2829 2830 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2831 krb5_get_init_creds_keytab(krb5_context context, 2832 krb5_creds *creds, 2833 krb5_principal client, 2834 krb5_keytab keytab, 2835 krb5_deltat start_time, 2836 const char *in_tkt_service, 2837 krb5_get_init_creds_opt *options) 2838 { 2839 krb5_init_creds_context ctx; 2840 krb5_keytab_entry ktent; 2841 krb5_error_code ret; 2842 2843 memset(&ktent, 0, sizeof(ktent)); 2844 memset(creds, 0, sizeof(*creds)); 2845 2846 if (strcmp(client->realm, "") == 0) { 2847 /* 2848 * Referral realm. We have a keytab, so pick a realm by 2849 * matching in the keytab. 2850 */ 2851 ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent); 2852 if (ret == 0) 2853 client = ktent.principal; 2854 } 2855 2856 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2857 if (ret) 2858 goto out; 2859 2860 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2861 if (ret) 2862 goto out; 2863 2864 ret = krb5_init_creds_set_keytab(context, ctx, keytab); 2865 if (ret) 2866 goto out; 2867 2868 ret = krb5_init_creds_get(context, ctx); 2869 if (ret == 0) 2870 krb5_process_last_request(context, options, ctx); 2871 2872 out: 2873 krb5_kt_free_entry(context, &ktent); 2874 if (ret == 0) 2875 krb5_init_creds_get_creds(context, ctx, creds); 2876 2877 if (ctx) 2878 krb5_init_creds_free(context, ctx); 2879 2880 return ret; 2881 } 2882