init_creds_pw.c revision 1.4
1/* $NetBSD: init_creds_pw.c,v 1.4 2023/06/19 21:41:44 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 43typedef 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 110struct pa_info_data { 111 krb5_enctype etype; 112 krb5_salt salt; 113 krb5_data *s2kparams; 114}; 115 116static void 117free_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 124static krb5_error_code KRB5_CALLCONV 125default_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 155static void 156free_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 199static int 200get_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 223static krb5_error_code 224init_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 264out: 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 273static void 274report_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 297krb5_error_code 298krb5_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 386static krb5_addresses no_addrs = { 0, NULL }; 387 388static krb5_error_code 389get_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 516static krb5_error_code 517change_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 635out: 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 645KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 646krb5_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 659static krb5_error_code 660init_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 768static krb5_error_code 769set_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 799static struct pa_info_data * 800pa_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 848static struct pa_info_data * 849pa_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 900static struct pa_info_data * 901pa_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 922struct 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 931static 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 938static PA_DATA * 939find_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 950static struct pa_info_data * 951process_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 971static krb5_error_code 972make_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 1025static krb5_error_code 1026add_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 1074static krb5_error_code 1075pa_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 1112static krb5_error_code 1113pa_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 1129static krb5_error_code 1130pa_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 1154static krb5_error_code 1155pa_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 1192static krb5_error_code 1193process_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 1302static krb5_error_code 1303process_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 1399KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1400krb5_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 1451KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1452krb5_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 1502KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1503krb5_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 1526static krb5_error_code KRB5_CALLCONV 1527keytab_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 if (ret == 0) { 1547 ret = krb5_copy_keyblock(context, &entry.keyblock, key); 1548 krb5_kt_free_entry(context, &entry); 1549 } 1550 1551 if (keytab == NULL) 1552 krb5_kt_close (context, real_keytab); 1553 return ret; 1554} 1555 1556 1557/** 1558 * Set the keytab to use for authentication. 1559 * 1560 * @param context a Kerberos 5 context. 1561 * @param ctx ctx krb5_init_creds_context context. 1562 * @param keytab the keytab to read the key from. 1563 * 1564 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1565 * @ingroup krb5_credential 1566 */ 1567 1568KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1569krb5_init_creds_set_keytab(krb5_context context, 1570 krb5_init_creds_context ctx, 1571 krb5_keytab keytab) 1572{ 1573 krb5_keytab_key_proc_args *a; 1574 krb5_keytab_entry entry; 1575 krb5_kt_cursor cursor; 1576 krb5_enctype *etypes = NULL; 1577 krb5_error_code ret; 1578 size_t netypes = 0; 1579 int kvno = 0, found = 0; 1580 1581 a = malloc(sizeof(*a)); 1582 if (a == NULL) 1583 return krb5_enomem(context); 1584 1585 a->principal = ctx->cred.client; 1586 a->keytab = keytab; 1587 1588 ctx->keytab_data = a; 1589 ctx->keyseed = (void *)a; 1590 ctx->keyproc = keytab_key_proc; 1591 1592 /* 1593 * We need to the KDC what enctypes we support for this keytab, 1594 * esp if the keytab is really a password based entry, then the 1595 * KDC might have more enctypes in the database then what we have 1596 * in the keytab. 1597 */ 1598 1599 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 1600 if(ret) 1601 goto out; 1602 1603 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){ 1604 void *ptr; 1605 1606 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client)) 1607 goto next; 1608 1609 found = 1; 1610 1611 /* check if we ahve this kvno already */ 1612 if (entry.vno > kvno) { 1613 /* remove old list of etype */ 1614 if (etypes) 1615 free(etypes); 1616 etypes = NULL; 1617 netypes = 0; 1618 kvno = entry.vno; 1619 } else if (entry.vno != kvno) 1620 goto next; 1621 1622 /* check if enctype is supported */ 1623 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0) 1624 goto next; 1625 1626 /* add enctype to supported list */ 1627 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2)); 1628 if (ptr == NULL) { 1629 free(etypes); 1630 ret = krb5_enomem(context); 1631 goto out; 1632 } 1633 1634 etypes = ptr; 1635 etypes[netypes] = entry.keyblock.keytype; 1636 etypes[netypes + 1] = ETYPE_NULL; 1637 netypes++; 1638 next: 1639 krb5_kt_free_entry(context, &entry); 1640 } 1641 krb5_kt_end_seq_get(context, keytab, &cursor); 1642 1643 if (etypes) { 1644 if (ctx->etypes) 1645 free(ctx->etypes); 1646 ctx->etypes = etypes; 1647 } 1648 1649 out: 1650 if (!found) { 1651 if (ret == 0) 1652 ret = KRB5_KT_NOTFOUND; 1653 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0); 1654 } 1655 1656 return ret; 1657} 1658 1659static krb5_error_code KRB5_CALLCONV 1660keyblock_key_proc(krb5_context context, krb5_enctype enctype, 1661 krb5_const_pointer keyseed, 1662 krb5_salt salt, krb5_data *s2kparms, 1663 krb5_keyblock **key) 1664{ 1665 return krb5_copy_keyblock (context, keyseed, key); 1666} 1667 1668KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1669krb5_init_creds_set_keyblock(krb5_context context, 1670 krb5_init_creds_context ctx, 1671 krb5_keyblock *keyblock) 1672{ 1673 ctx->keyseed = (void *)keyblock; 1674 ctx->keyproc = keyblock_key_proc; 1675 1676 return 0; 1677} 1678 1679KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1680krb5_init_creds_set_fast_ccache(krb5_context context, 1681 krb5_init_creds_context ctx, 1682 krb5_ccache fast_ccache) 1683{ 1684 ctx->fast_state.armor_ccache = fast_ccache; 1685 ctx->fast_state.flags |= KRB5_FAST_REQUIRED; 1686 return 0; 1687} 1688 1689KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1690krb5_init_creds_set_fast_ap_armor_service(krb5_context context, 1691 krb5_init_creds_context ctx, 1692 krb5_const_principal armor_service) 1693{ 1694 krb5_error_code ret; 1695 1696 if (ctx->fast_state.armor_service) 1697 krb5_free_principal(context, ctx->fast_state.armor_service); 1698 if (armor_service) { 1699 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service); 1700 if (ret) 1701 return ret; 1702 } else { 1703 ctx->fast_state.armor_service = NULL; 1704 } 1705 ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE; 1706 return 0; 1707} 1708 1709/* 1710 * FAST 1711 */ 1712 1713static krb5_error_code 1714check_fast(krb5_context context, struct fast_state *state) 1715{ 1716 if (state->flags & KRB5_FAST_EXPECTED) { 1717 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 1718 "Expected FAST, but no FAST " 1719 "was in the response from the KDC"); 1720 return KRB5KRB_AP_ERR_MODIFIED; 1721 } 1722 return 0; 1723} 1724 1725 1726static krb5_error_code 1727fast_unwrap_as_rep(krb5_context context, int32_t nonce, 1728 krb5_data *chksumdata, 1729 struct fast_state *state, AS_REP *rep) 1730{ 1731 PA_FX_FAST_REPLY fxfastrep; 1732 KrbFastResponse fastrep; 1733 krb5_error_code ret; 1734 PA_DATA *pa = NULL; 1735 int idx = 0; 1736 1737 if (state->armor_crypto == NULL || rep->padata == NULL) 1738 return check_fast(context, state); 1739 1740 /* find PA_FX_FAST_REPLY */ 1741 1742 pa = krb5_find_padata(rep->padata->val, rep->padata->len, 1743 KRB5_PADATA_FX_FAST, &idx); 1744 if (pa == NULL) 1745 return check_fast(context, state); 1746 1747 memset(&fxfastrep, 0, sizeof(fxfastrep)); 1748 memset(&fastrep, 0, sizeof(fastrep)); 1749 1750 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL); 1751 if (ret) 1752 return ret; 1753 1754 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) { 1755 krb5_data data; 1756 ret = krb5_decrypt_EncryptedData(context, 1757 state->armor_crypto, 1758 KRB5_KU_FAST_REP, 1759 &fxfastrep.u.armored_data.enc_fast_rep, 1760 &data); 1761 if (ret) 1762 goto out; 1763 1764 ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL); 1765 krb5_data_free(&data); 1766 if (ret) 1767 goto out; 1768 1769 } else { 1770 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1771 goto out; 1772 } 1773 1774 free_METHOD_DATA(rep->padata); 1775 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata); 1776 if (ret) 1777 goto out; 1778 1779 if (fastrep.strengthen_key) { 1780 if (state->strengthen_key) 1781 krb5_free_keyblock(context, state->strengthen_key); 1782 1783 ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key); 1784 if (ret) 1785 goto out; 1786 } 1787 1788 if (nonce != fastrep.nonce) { 1789 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1790 goto out; 1791 } 1792 if (fastrep.finished) { 1793 PrincipalName cname; 1794 krb5_realm crealm = NULL; 1795 1796 if (chksumdata == NULL) { 1797 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1798 goto out; 1799 } 1800 1801 ret = krb5_verify_checksum(context, state->armor_crypto, 1802 KRB5_KU_FAST_FINISHED, 1803 chksumdata->data, chksumdata->length, 1804 &fastrep.finished->ticket_checksum); 1805 if (ret) 1806 goto out; 1807 1808 /* update */ 1809 ret = copy_Realm(&fastrep.finished->crealm, &crealm); 1810 if (ret) 1811 goto out; 1812 free_Realm(&rep->crealm); 1813 rep->crealm = crealm; 1814 1815 ret = copy_PrincipalName(&fastrep.finished->cname, &cname); 1816 if (ret) 1817 goto out; 1818 free_PrincipalName(&rep->cname); 1819 rep->cname = cname; 1820 1821#if 0 /* store authenticated checksum as kdc-offset */ 1822 fastrep->finished.timestamp; 1823 fastrep->finished.usec = 0; 1824#endif 1825 1826 } else if (chksumdata) { 1827 /* expected fastrep.finish but didn't get it */ 1828 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1829 } 1830 1831 out: 1832 free_PA_FX_FAST_REPLY(&fxfastrep); 1833 1834 return ret; 1835} 1836 1837static krb5_error_code 1838fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error) 1839{ 1840 if (state->armor_crypto == NULL) 1841 return check_fast(context, state); 1842 1843 return 0; 1844} 1845 1846krb5_error_code 1847_krb5_make_fast_ap_fxarmor(krb5_context context, 1848 krb5_ccache armor_ccache, 1849 krb5_data *armor_value, 1850 krb5_keyblock *armor_key, 1851 krb5_crypto *armor_crypto) 1852{ 1853 krb5_auth_context auth_context = NULL; 1854 krb5_creds cred, *credp = NULL; 1855 krb5_error_code ret; 1856 krb5_data empty; 1857 1858 krb5_data_zero(&empty); 1859 1860 memset(&cred, 0, sizeof(cred)); 1861 1862 ret = krb5_auth_con_init (context, &auth_context); 1863 if (ret) 1864 goto out; 1865 1866 ret = krb5_cc_get_principal(context, armor_ccache, &cred.client); 1867 if (ret) 1868 goto out; 1869 1870 ret = krb5_make_principal(context, &cred.server, 1871 cred.client->realm, 1872 KRB5_TGS_NAME, 1873 cred.client->realm, 1874 NULL); 1875 if (ret) { 1876 krb5_free_principal(context, cred.client); 1877 goto out; 1878 } 1879 1880 ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp); 1881 krb5_free_principal(context, cred.server); 1882 krb5_free_principal(context, cred.client); 1883 if (ret) 1884 goto out; 1885 1886 ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty); 1887 if (ret) 1888 goto out; 1889 1890 ret = krb5_mk_req_extended(context, 1891 &auth_context, 1892 AP_OPTS_USE_SUBKEY, 1893 NULL, 1894 credp, 1895 armor_value); 1896 krb5_free_creds(context, credp); 1897 if (ret) 1898 goto out; 1899 1900 ret = _krb5_fast_armor_key(context, 1901 auth_context->local_subkey, 1902 auth_context->keyblock, 1903 armor_key, 1904 armor_crypto); 1905 if (ret) 1906 goto out; 1907 1908 out: 1909 krb5_auth_con_free(context, auth_context); 1910 return ret; 1911} 1912 1913#ifndef WIN32 1914static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT; 1915static heim_ipc armor_service = NULL; 1916 1917static void 1918fast_armor_init_ipc(void *ctx) 1919{ 1920 heim_ipc *ipc = ctx; 1921 heim_ipc_init_context("ANY:org.h5l.armor-service", ipc); 1922} 1923#endif /* WIN32 */ 1924 1925 1926static krb5_error_code 1927make_fast_ap_fxarmor(krb5_context context, 1928 struct fast_state *state, 1929 const char *realm, 1930 KrbFastArmor **armor) 1931{ 1932 KrbFastArmor *fxarmor = NULL; 1933 krb5_error_code ret; 1934 1935 if (state->armor_crypto) 1936 krb5_crypto_destroy(context, state->armor_crypto); 1937 krb5_free_keyblock_contents(context, &state->armor_key); 1938 1939 1940 ALLOC(fxarmor, 1); 1941 if (fxarmor == NULL) 1942 return krb5_enomem(context); 1943 1944 if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) { 1945#ifdef WIN32 1946 krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows"); 1947 ret = ENOTSUP; 1948 goto out; 1949#else /* WIN32 */ 1950 KERB_ARMOR_SERVICE_REPLY msg; 1951 krb5_data request, reply; 1952 1953 heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc); 1954 if (armor_service == NULL) { 1955 krb5_set_error_message(context, ENOENT, "Failed to open fast armor service"); 1956 ret = ENOENT; 1957 goto out; 1958 } 1959 1960 krb5_data_zero(&reply); 1961 1962 request.data = rk_UNCONST(realm); 1963 request.length = strlen(realm); 1964 1965 ret = heim_ipc_call(armor_service, &request, &reply, NULL); 1966 heim_release(send); 1967 if (ret) { 1968 krb5_set_error_message(context, ret, "Failed to get armor service credential"); 1969 goto out; 1970 } 1971 1972 ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL); 1973 krb5_data_free(&reply); 1974 if (ret) 1975 goto out; 1976 1977 ret = copy_KrbFastArmor(fxarmor, &msg.armor); 1978 if (ret) { 1979 free_KERB_ARMOR_SERVICE_REPLY(&msg); 1980 goto out; 1981 } 1982 1983 ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key); 1984 free_KERB_ARMOR_SERVICE_REPLY(&msg); 1985 if (ret) 1986 goto out; 1987 1988 ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto); 1989 if (ret) 1990 goto out; 1991#endif /* WIN32 */ 1992 } else { 1993 1994 fxarmor->armor_type = 1; 1995 1996 ret = _krb5_make_fast_ap_fxarmor(context, 1997 state->armor_ccache, 1998 &fxarmor->armor_value, 1999 &state->armor_key, 2000 &state->armor_crypto); 2001 if (ret) 2002 goto out; 2003 } 2004 2005 2006 *armor = fxarmor; 2007 fxarmor = NULL; 2008 out: 2009 if (fxarmor) { 2010 free_KrbFastArmor(fxarmor); 2011 free(fxarmor); 2012 } 2013 return ret; 2014} 2015 2016static krb5_error_code 2017fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req) 2018{ 2019 KrbFastArmor *fxarmor = NULL; 2020 PA_FX_FAST_REQUEST fxreq; 2021 krb5_error_code ret; 2022 KrbFastReq fastreq; 2023 krb5_data data; 2024 size_t size; 2025 2026 if (state->flags & KRB5_FAST_DISABLED) { 2027 _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping"); 2028 return 0; 2029 } 2030 2031 memset(&fxreq, 0, sizeof(fxreq)); 2032 memset(&fastreq, 0, sizeof(fastreq)); 2033 krb5_data_zero(&data); 2034 2035 if (state->armor_crypto == NULL) { 2036 if (state->armor_ccache) { 2037 /* 2038 * Instead of keeping state in FX_COOKIE in the KDC, we 2039 * rebuild a new armor key for every request, because this 2040 * is what the MIT KDC expect and RFC6113 is vage about 2041 * what the behavior should be. 2042 */ 2043 state->type = choice_PA_FX_FAST_REQUEST_armored_data; 2044 } else { 2045 return check_fast(context, state); 2046 } 2047 } 2048 2049 state->flags |= KRB5_FAST_EXPECTED; 2050 2051 fastreq.fast_options.hide_client_names = 1; 2052 2053 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body); 2054 free_KDC_REQ_BODY(&req->req_body); 2055 2056 req->req_body.realm = strdup(KRB5_ANON_REALM); 2057 if ((ALLOC(req->req_body.cname, 1)) != NULL) { 2058 req->req_body.cname->name_type = KRB5_NT_WELLKNOWN; 2059 if ((ALLOC(req->req_body.cname->name_string.val, 2)) != NULL) { 2060 req->req_body.cname->name_string.len = 2; 2061 req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME); 2062 req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME); 2063 if (req->req_body.cname->name_string.val[0] == NULL || 2064 req->req_body.cname->name_string.val[1] == NULL) 2065 ret = krb5_enomem(context); 2066 } else 2067 ret = krb5_enomem(context); 2068 } else 2069 ret = krb5_enomem(context); 2070 if ((ALLOC(req->req_body.till, 1)) != NULL) 2071 *req->req_body.till = 0; 2072 else 2073 ret = krb5_enomem(context); 2074 if (ret) 2075 goto out; 2076 2077 if (req->padata) { 2078 ret = copy_METHOD_DATA(req->padata, &fastreq.padata); 2079 free_METHOD_DATA(req->padata); 2080 } else { 2081 if ((ALLOC(req->padata, 1)) == NULL) 2082 ret = krb5_enomem(context); 2083 } 2084 if (ret) 2085 goto out; 2086 2087 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret); 2088 if (ret) 2089 goto out; 2090 heim_assert(data.length == size, "ASN.1 internal error"); 2091 2092 fxreq.element = state->type; 2093 2094 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) { 2095 size_t len; 2096 void *buf; 2097 2098 ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor); 2099 if (ret) 2100 goto out; 2101 2102 heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started"); 2103 2104 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret); 2105 if (ret) 2106 goto out; 2107 heim_assert(len == size, "ASN.1 internal error"); 2108 2109 ret = krb5_create_checksum(context, state->armor_crypto, 2110 KRB5_KU_FAST_REQ_CHKSUM, 0, 2111 buf, len, 2112 &fxreq.u.armored_data.req_checksum); 2113 free(buf); 2114 if (ret) 2115 goto out; 2116 2117 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto, 2118 KRB5_KU_FAST_ENC, 2119 data.data, 2120 data.length, 2121 0, 2122 &fxreq.u.armored_data.enc_fast_req); 2123 krb5_data_free(&data); 2124 if (ret) 2125 goto out; 2126 2127 } else { 2128 krb5_data_free(&data); 2129 heim_assert(false, "unknown FAST type, internal error"); 2130 } 2131 2132 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret); 2133 if (ret) 2134 goto out; 2135 heim_assert(data.length == size, "ASN.1 internal error"); 2136 2137 2138 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length); 2139 if (ret) 2140 goto out; 2141 krb5_data_zero(&data); 2142 2143 out: 2144 free_PA_FX_FAST_REQUEST(&fxreq); 2145 free_KrbFastReq(&fastreq); 2146 if (fxarmor) { 2147 free_KrbFastArmor(fxarmor); 2148 free(fxarmor); 2149 } 2150 krb5_data_free(&data); 2151 2152 return ret; 2153} 2154 2155 2156/** 2157 * The core loop if krb5_get_init_creds() function family. Create the 2158 * packets and have the caller send them off to the KDC. 2159 * 2160 * If the caller want all work been done for them, use 2161 * krb5_init_creds_get() instead. 2162 * 2163 * @param context a Kerberos 5 context. 2164 * @param ctx ctx krb5_init_creds_context context. 2165 * @param in input data from KDC, first round it should be reset by krb5_data_zer(). 2166 * @param out reply to KDC. 2167 * @param hostinfo KDC address info, first round it can be NULL. 2168 * @param flags status of the round, if 2169 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round. 2170 * 2171 * @return 0 for success, or an Kerberos 5 error code, see 2172 * krb5_get_error_message(). 2173 * 2174 * @ingroup krb5_credential 2175 */ 2176 2177KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2178krb5_init_creds_step(krb5_context context, 2179 krb5_init_creds_context ctx, 2180 krb5_data *in, 2181 krb5_data *out, 2182 krb5_krbhst_info *hostinfo, 2183 unsigned int *flags) 2184{ 2185 krb5_error_code ret; 2186 size_t len = 0; 2187 size_t size; 2188 AS_REQ req2; 2189 2190 krb5_data_zero(out); 2191 2192 if (ctx->as_req.req_body.cname == NULL) { 2193 ret = init_as_req(context, ctx->flags, &ctx->cred, 2194 ctx->addrs, ctx->etypes, &ctx->as_req); 2195 if (ret) { 2196 free_init_creds_ctx(context, ctx); 2197 return ret; 2198 } 2199 } 2200 2201#define MAX_PA_COUNTER 10 2202 if (ctx->pa_counter > MAX_PA_COUNTER) { 2203 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 2204 N_("Looping %d times while getting " 2205 "initial credentials", ""), 2206 ctx->pa_counter); 2207 return KRB5_GET_IN_TKT_LOOP; 2208 } 2209 ctx->pa_counter++; 2210 2211 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter); 2212 2213 /* Lets process the input packet */ 2214 if (in && in->length) { 2215 krb5_kdc_rep rep; 2216 2217 memset(&rep, 0, sizeof(rep)); 2218 2219 _krb5_debug(context, 5, "krb5_get_init_creds: processing input"); 2220 2221 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size); 2222 if (ret == 0) { 2223 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC; 2224 krb5_data data; 2225 2226 /* 2227 * Unwrap AS-REP 2228 */ 2229 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, 2230 &rep.kdc_rep.ticket, &size, ret); 2231 if (ret) 2232 goto out; 2233 heim_assert(data.length == size, "ASN.1 internal error"); 2234 2235 ret = fast_unwrap_as_rep(context, ctx->nonce, &data, 2236 &ctx->fast_state, &rep.kdc_rep); 2237 krb5_data_free(&data); 2238 if (ret) 2239 goto out; 2240 2241 /* 2242 * Now check and extract the ticket 2243 */ 2244 2245 if (ctx->flags.canonicalize) { 2246 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; 2247 eflags |= EXTRACT_TICKET_MATCH_REALM; 2248 } 2249 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) 2250 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; 2251 if (ctx->flags.request_anonymous) 2252 eflags |= EXTRACT_TICKET_MATCH_ANON; 2253 2254 ret = process_pa_data_to_key(context, ctx, &ctx->cred, 2255 &ctx->as_req, &rep.kdc_rep, 2256 hostinfo, &ctx->fast_state.reply_key); 2257 if (ret) { 2258 free_AS_REP(&rep.kdc_rep); 2259 goto out; 2260 } 2261 2262 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket"); 2263 2264 ret = _krb5_extract_ticket(context, 2265 &rep, 2266 &ctx->cred, 2267 ctx->fast_state.reply_key, 2268 NULL, 2269 KRB5_KU_AS_REP_ENC_PART, 2270 NULL, 2271 ctx->nonce, 2272 eflags, 2273 &ctx->req_buffer, 2274 NULL, 2275 NULL); 2276 if (ret == 0 && ctx->pk_init_ctx) { 2277 PA_DATA *pa_pkinit_kx; 2278 int idx = 0; 2279 2280 pa_pkinit_kx = 2281 krb5_find_padata(rep.kdc_rep.padata->val, 2282 rep.kdc_rep.padata->len, 2283 KRB5_PADATA_PKINIT_KX, 2284 &idx); 2285 2286 ret = _krb5_pk_kx_confirm(context, ctx->pk_init_ctx, 2287 ctx->fast_state.reply_key, 2288 &ctx->cred.session, 2289 pa_pkinit_kx); 2290 if (ret) 2291 krb5_set_error_message(context, ret, 2292 N_("Failed to confirm PA-PKINIT-KX", "")); 2293 else if (pa_pkinit_kx != NULL) 2294 ctx->ic_flags |= KRB5_INIT_CREDS_PKINIT_KX_VALID; 2295 } 2296 if (ret == 0) 2297 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part); 2298 2299 krb5_free_keyblock(context, ctx->fast_state.reply_key); 2300 ctx->fast_state.reply_key = NULL; 2301 *flags = 0; 2302 2303 free_AS_REP(&rep.kdc_rep); 2304 free_EncASRepPart(&rep.enc_part); 2305 2306 return ret; 2307 2308 } else { 2309 /* let's try to parse it as a KRB-ERROR */ 2310 2311 _krb5_debug(context, 5, "krb5_get_init_creds: got an error"); 2312 2313 free_KRB_ERROR(&ctx->error); 2314 2315 ret = krb5_rd_error(context, in, &ctx->error); 2316 if(ret && in->length && ((char*)in->data)[0] == 4) 2317 ret = KRB5KRB_AP_ERR_V4_REPLY; 2318 if (ret) { 2319 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error"); 2320 goto out; 2321 } 2322 2323 /* 2324 * Unwrap KRB-ERROR 2325 */ 2326 ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error); 2327 if (ret) 2328 goto out; 2329 2330 /* 2331 * 2332 */ 2333 2334 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred); 2335 2336 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret); 2337 2338 /* 2339 * If no preauth was set and KDC requires it, give it one 2340 * more try. 2341 */ 2342 2343 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) { 2344 2345 free_METHOD_DATA(&ctx->md); 2346 memset_s(&ctx->md, sizeof(ctx->md), 0, sizeof(ctx->md)); 2347 2348 if (ctx->error.e_data) { 2349 ret = decode_METHOD_DATA(ctx->error.e_data->data, 2350 ctx->error.e_data->length, 2351 &ctx->md, 2352 NULL); 2353 if (ret) 2354 krb5_set_error_message(context, ret, 2355 N_("Failed to decode METHOD-DATA", "")); 2356 } else { 2357 krb5_set_error_message(context, ret, 2358 N_("Preauth required but no preauth " 2359 "options send by KDC", "")); 2360 } 2361 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) { 2362 /* 2363 * Try adapt to timeskrew when we are using pre-auth, and 2364 * if there was a time skew, try again. 2365 */ 2366 krb5_set_real_time(context, ctx->error.stime, -1); 2367 if (context->kdc_sec_offset) 2368 ret = 0; 2369 2370 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d", 2371 context->kdc_sec_offset); 2372 2373 ctx->used_pa_types = 0; 2374 2375 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) { 2376 /* client referal to a new realm */ 2377 2378 if (ctx->error.crealm == NULL) { 2379 krb5_set_error_message(context, ret, 2380 N_("Got a client referral, not but no realm", "")); 2381 goto out; 2382 } 2383 _krb5_debug(context, 5, 2384 "krb5_get_init_creds: got referal to realm %s", 2385 *ctx->error.crealm); 2386 2387 ret = krb5_principal_set_realm(context, 2388 ctx->cred.client, 2389 *ctx->error.crealm); 2390 if (ret) 2391 goto out; 2392 2393 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) { 2394 ret = krb5_init_creds_set_service(context, ctx, NULL); 2395 if (ret) 2396 goto out; 2397 } 2398 2399 free_AS_REQ(&ctx->as_req); 2400 memset_s(&ctx->as_req, sizeof(ctx->as_req), 0, sizeof(ctx->as_req)); 2401 2402 ctx->used_pa_types = 0; 2403 } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) { 2404 char buf2[1024]; 2405 2406 ctx->runflags.change_password = 1; 2407 2408 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL); 2409 2410 2411 /* try to avoid recursion */ 2412 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0) 2413 goto out; 2414 2415 /* don't try to change password where then where none */ 2416 if (ctx->prompter == NULL) 2417 goto out; 2418 2419 ret = change_password(context, 2420 ctx->cred.client, 2421 ctx->password, 2422 buf2, 2423 sizeof(buf2), 2424 ctx->prompter, 2425 ctx->prompter_data, 2426 NULL); 2427 if (ret) 2428 goto out; 2429 2430 krb5_init_creds_set_password(context, ctx, buf2); 2431 2432 ctx->used_pa_types = 0; 2433 ret = 0; 2434 2435 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) { 2436 2437 if (ctx->fast_state.flags & KRB5_FAST_DISABLED) 2438 goto out; 2439 if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED)) 2440 goto out; 2441 2442 _krb5_debug(context, 10, "preauth failed with FAST, " 2443 "and told by KD or user, trying w/o FAST"); 2444 2445 ctx->fast_state.flags |= KRB5_FAST_DISABLED; 2446 ctx->used_pa_types = 0; 2447 ret = 0; 2448 } 2449 if (ret) 2450 goto out; 2451 } 2452 } 2453 2454 if (ctx->as_req.req_body.cname == NULL) { 2455 ret = init_as_req(context, ctx->flags, &ctx->cred, 2456 ctx->addrs, ctx->etypes, &ctx->as_req); 2457 if (ret) { 2458 free_init_creds_ctx(context, ctx); 2459 return ret; 2460 } 2461 } 2462 2463 if (ctx->as_req.padata) { 2464 free_METHOD_DATA(ctx->as_req.padata); 2465 free(ctx->as_req.padata); 2466 ctx->as_req.padata = NULL; 2467 } 2468 2469 /* Set a new nonce. */ 2470 ctx->as_req.req_body.nonce = ctx->nonce; 2471 2472 /* fill_in_md_data */ 2473 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx, 2474 &ctx->md, &ctx->as_req.padata, 2475 ctx->prompter, ctx->prompter_data); 2476 if (ret) 2477 goto out; 2478 2479 /* 2480 * Wrap with FAST 2481 */ 2482 copy_AS_REQ(&ctx->as_req, &req2); 2483 2484 ret = fast_wrap_req(context, &ctx->fast_state, &req2); 2485 if (ret) { 2486 free_AS_REQ(&req2); 2487 goto out; 2488 } 2489 2490 krb5_data_free(&ctx->req_buffer); 2491 2492 ASN1_MALLOC_ENCODE(AS_REQ, 2493 ctx->req_buffer.data, ctx->req_buffer.length, 2494 &req2, &len, ret); 2495 free_AS_REQ(&req2); 2496 if (ret) 2497 goto out; 2498 if(len != ctx->req_buffer.length) 2499 krb5_abortx(context, "internal error in ASN.1 encoder"); 2500 2501 out->data = ctx->req_buffer.data; 2502 out->length = ctx->req_buffer.length; 2503 2504 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; 2505 2506 return 0; 2507 out: 2508 return ret; 2509} 2510 2511/** 2512 * Extract the newly acquired credentials from krb5_init_creds_context 2513 * context. 2514 * 2515 * @param context A Kerberos 5 context. 2516 * @param ctx 2517 * @param cred credentials, free with krb5_free_cred_contents(). 2518 * 2519 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message(). 2520 */ 2521 2522KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2523krb5_init_creds_get_creds(krb5_context context, 2524 krb5_init_creds_context ctx, 2525 krb5_creds *cred) 2526{ 2527 return krb5_copy_creds_contents(context, &ctx->cred, cred); 2528} 2529 2530/** 2531 * Get the last error from the transaction. 2532 * 2533 * @return Returns 0 or an error code 2534 * 2535 * @ingroup krb5_credential 2536 */ 2537 2538KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2539krb5_init_creds_get_error(krb5_context context, 2540 krb5_init_creds_context ctx, 2541 KRB_ERROR *error) 2542{ 2543 krb5_error_code ret; 2544 2545 ret = copy_KRB_ERROR(&ctx->error, error); 2546 if (ret) 2547 krb5_enomem(context); 2548 2549 return ret; 2550} 2551 2552/** 2553 * 2554 * @ingroup krb5_credential 2555 */ 2556 2557krb5_error_code 2558krb5_init_creds_store(krb5_context context, 2559 krb5_init_creds_context ctx, 2560 krb5_ccache id) 2561{ 2562 krb5_error_code ret; 2563 2564 if (ctx->cred.client == NULL) { 2565 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; 2566 krb5_set_error_message(context, ret, "init creds not completed yet"); 2567 return ret; 2568 } 2569 2570 ret = krb5_cc_initialize(context, id, ctx->cred.client); 2571 if (ret) 2572 return ret; 2573 2574 ret = krb5_cc_store_cred(context, id, &ctx->cred); 2575 if (ret) 2576 return ret; 2577 2578 if (ctx->cred.flags.b.enc_pa_rep) { 2579 krb5_data data = { 3, rk_UNCONST("yes") }; 2580 ret = krb5_cc_set_config(context, id, ctx->cred.server, 2581 "fast_avail", &data); 2582 if (ret) 2583 return ret; 2584 } 2585 2586 return ret; 2587} 2588 2589/** 2590 * Free the krb5_init_creds_context allocated by krb5_init_creds_init(). 2591 * 2592 * @param context A Kerberos 5 context. 2593 * @param ctx The krb5_init_creds_context to free. 2594 * 2595 * @ingroup krb5_credential 2596 */ 2597 2598KRB5_LIB_FUNCTION void KRB5_LIB_CALL 2599krb5_init_creds_free(krb5_context context, 2600 krb5_init_creds_context ctx) 2601{ 2602 free_init_creds_ctx(context, ctx); 2603 free(ctx); 2604} 2605 2606/** 2607 * Get new credentials as setup by the krb5_init_creds_context. 2608 * 2609 * @param context A Kerberos 5 context. 2610 * @param ctx The krb5_init_creds_context to process. 2611 * 2612 * @ingroup krb5_credential 2613 */ 2614 2615KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2616krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) 2617{ 2618 krb5_sendto_ctx stctx = NULL; 2619 krb5_krbhst_info *hostinfo = NULL; 2620 krb5_error_code ret; 2621 krb5_data in, out; 2622 unsigned int flags = 0; 2623 2624 krb5_data_zero(&in); 2625 krb5_data_zero(&out); 2626 2627 ret = krb5_sendto_ctx_alloc(context, &stctx); 2628 if (ret) 2629 goto out; 2630 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); 2631 2632 while (1) { 2633 flags = 0; 2634 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags); 2635 krb5_data_free(&in); 2636 if (ret) 2637 goto out; 2638 2639 if ((flags & 1) == 0) 2640 break; 2641 2642 ret = krb5_sendto_context (context, stctx, &out, 2643 ctx->cred.client->realm, &in); 2644 if (ret) 2645 goto out; 2646 2647 } 2648 2649 out: 2650 if (stctx) 2651 krb5_sendto_ctx_free(context, stctx); 2652 2653 return ret; 2654} 2655 2656/** 2657 * Get new credentials using password. 2658 * 2659 * @ingroup krb5_credential 2660 */ 2661 2662 2663KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2664krb5_get_init_creds_password(krb5_context context, 2665 krb5_creds *creds, 2666 krb5_principal client, 2667 const char *password, 2668 krb5_prompter_fct prompter, 2669 void *data, 2670 krb5_deltat start_time, 2671 const char *in_tkt_service, 2672 krb5_get_init_creds_opt *options) 2673{ 2674 krb5_init_creds_context ctx; 2675 char buf[BUFSIZ], buf2[BUFSIZ]; 2676 krb5_error_code ret; 2677 int chpw = 0; 2678 2679 again: 2680 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx); 2681 if (ret) 2682 goto out; 2683 2684 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2685 if (ret) 2686 goto out; 2687 2688 if (prompter != NULL && ctx->password == NULL && password == NULL) { 2689 krb5_prompt prompt; 2690 krb5_data password_data; 2691 char *p, *q = NULL; 2692 int aret; 2693 2694 ret = krb5_unparse_name(context, client, &p); 2695 if (ret) 2696 goto out; 2697 2698 aret = asprintf(&q, "%s's Password: ", p); 2699 free (p); 2700 if (aret == -1 || q == NULL) { 2701 ret = krb5_enomem(context); 2702 goto out; 2703 } 2704 prompt.prompt = q; 2705 password_data.data = buf; 2706 password_data.length = sizeof(buf); 2707 prompt.hidden = 1; 2708 prompt.reply = &password_data; 2709 prompt.type = KRB5_PROMPT_TYPE_PASSWORD; 2710 2711 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); 2712 free (q); 2713 if (ret) { 2714 memset_s(buf, sizeof(buf), 0, sizeof(buf)); 2715 ret = KRB5_LIBOS_PWDINTR; 2716 krb5_clear_error_message (context); 2717 goto out; 2718 } 2719 password = password_data.data; 2720 } 2721 2722 if (password) { 2723 ret = krb5_init_creds_set_password(context, ctx, password); 2724 if (ret) 2725 goto out; 2726 } 2727 2728 ret = krb5_init_creds_get(context, ctx); 2729 2730 if (ret == 0) 2731 krb5_process_last_request(context, options, ctx); 2732 2733 2734 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) { 2735 /* try to avoid recursion */ 2736 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0) 2737 goto out; 2738 2739 /* don't try to change password where then where none */ 2740 if (prompter == NULL) 2741 goto out; 2742 2743 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) && 2744 !options->change_password_prompt) 2745 goto out; 2746 2747 ret = change_password (context, 2748 client, 2749 ctx->password, 2750 buf2, 2751 sizeof(buf2), 2752 prompter, 2753 data, 2754 options); 2755 if (ret) 2756 goto out; 2757 password = buf2; 2758 chpw = 1; 2759 krb5_init_creds_free(context, ctx); 2760 goto again; 2761 } 2762 2763 out: 2764 if (ret == 0) 2765 krb5_init_creds_get_creds(context, ctx, creds); 2766 2767 if (ctx) 2768 krb5_init_creds_free(context, ctx); 2769 2770 memset_s(buf, sizeof(buf), 0, sizeof(buf)); 2771 memset_s(buf2, sizeof(buf), 0, sizeof(buf2)); 2772 return ret; 2773} 2774 2775/** 2776 * Get new credentials using keyblock. 2777 * 2778 * @ingroup krb5_credential 2779 */ 2780 2781KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2782krb5_get_init_creds_keyblock(krb5_context context, 2783 krb5_creds *creds, 2784 krb5_principal client, 2785 krb5_keyblock *keyblock, 2786 krb5_deltat start_time, 2787 const char *in_tkt_service, 2788 krb5_get_init_creds_opt *options) 2789{ 2790 krb5_init_creds_context ctx; 2791 krb5_error_code ret; 2792 2793 memset(creds, 0, sizeof(*creds)); 2794 2795 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2796 if (ret) 2797 goto out; 2798 2799 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2800 if (ret) 2801 goto out; 2802 2803 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock); 2804 if (ret) 2805 goto out; 2806 2807 ret = krb5_init_creds_get(context, ctx); 2808 2809 if (ret == 0) 2810 krb5_process_last_request(context, options, ctx); 2811 2812 out: 2813 if (ret == 0) 2814 krb5_init_creds_get_creds(context, ctx, creds); 2815 2816 if (ctx) 2817 krb5_init_creds_free(context, ctx); 2818 2819 return ret; 2820} 2821 2822/** 2823 * Get new credentials using keytab. 2824 * 2825 * @ingroup krb5_credential 2826 */ 2827 2828KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2829krb5_get_init_creds_keytab(krb5_context context, 2830 krb5_creds *creds, 2831 krb5_principal client, 2832 krb5_keytab keytab, 2833 krb5_deltat start_time, 2834 const char *in_tkt_service, 2835 krb5_get_init_creds_opt *options) 2836{ 2837 krb5_init_creds_context ctx; 2838 krb5_keytab_entry ktent; 2839 krb5_error_code ret; 2840 2841 memset(&ktent, 0, sizeof(ktent)); 2842 memset(creds, 0, sizeof(*creds)); 2843 2844 if (strcmp(client->realm, "") == 0) { 2845 /* 2846 * Referral realm. We have a keytab, so pick a realm by 2847 * matching in the keytab. 2848 */ 2849 ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent); 2850 if (ret == 0) 2851 client = ktent.principal; 2852 } 2853 2854 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2855 if (ret) 2856 goto out; 2857 2858 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2859 if (ret) 2860 goto out; 2861 2862 ret = krb5_init_creds_set_keytab(context, ctx, keytab); 2863 if (ret) 2864 goto out; 2865 2866 ret = krb5_init_creds_get(context, ctx); 2867 if (ret == 0) 2868 krb5_process_last_request(context, options, ctx); 2869 2870 out: 2871 krb5_kt_free_entry(context, &ktent); 2872 if (ret == 0) 2873 krb5_init_creds_get_creds(context, ctx, creds); 2874 2875 if (ctx) 2876 krb5_init_creds_free(context, ctx); 2877 2878 return ret; 2879} 2880