1/* 2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "krb5_locl.h" 37#include <pkinit_asn1.h> 38 39struct pa_info_data { 40 krb5_enctype etype; 41 krb5_salt salt; 42 krb5_data *s2kparams; 43}; 44 45struct krb5_init_creds_context_data { 46 KDCOptions flags; 47 krb5_creds cred; 48 krb5_addresses *addrs; 49 krb5_enctype *etypes; 50 krb5_preauthtype *pre_auth_types; 51 char *in_tkt_service; 52 unsigned nonce; 53 unsigned pk_nonce; 54 55 krb5_data req_buffer; 56 AS_REQ as_req; 57 int pa_counter; 58 59 /* password and keytab_data is freed on completion */ 60 char *password; 61 krb5_keytab_key_proc_args *keytab_data; 62 63 krb5_pointer *keyseed; 64 krb5_s2k_proc keyproc; 65 66 krb5_get_init_creds_tristate req_pac; 67 68 krb5_pk_init_ctx pk_init_ctx; 69 int ic_flags; 70 71 char *kdc_hostname; 72 73 int used_pa_types; 74 75#define USED_ENC_TS_INFO 8 76#define USED_ENC_TS_RENEG 16 77 struct { 78 unsigned int change_password:1; 79 unsigned int allow_enc_pa_rep:1; 80 } runflags; 81 82 struct pa_info_data paid; 83 84 METHOD_DATA md; 85 KRB_ERROR error; 86 AS_REP as_rep; 87 EncKDCRepPart enc_part; 88 89 krb5_prompter_fct prompter; 90 void *prompter_data; 91 int warned_user; 92 93 struct pa_info_data *ppaid; 94 95 struct krb5_fast_state fast_state; 96 97 /* current and available pa mechansm in this exchange */ 98 struct pa_auth_mech *pa_mech; 99 heim_array_t available_pa_mechs; 100 101#ifdef PKINIT 102 hx509_cert client_cert; 103 krb5_data *pku2u_assertion; 104#endif 105 106 struct { 107 struct timeval run_time; 108 } stats; 109}; 110 111static void 112free_paid(krb5_context context, struct pa_info_data *ppaid) 113{ 114 krb5_free_salt(context, ppaid->salt); 115 if (ppaid->s2kparams) 116 krb5_free_data(context, ppaid->s2kparams); 117 memset(ppaid, 0, sizeof(*ppaid)); 118} 119 120static krb5_error_code KRB5_CALLCONV 121default_s2k_func(krb5_context context, krb5_enctype type, 122 krb5_const_pointer keyseed, 123 krb5_salt salt, krb5_data *s2kparms, 124 krb5_keyblock **key) 125{ 126 krb5_error_code ret; 127 krb5_data password; 128 krb5_data opaque; 129 130 if (_krb5_have_debug(context, 5)) { 131 char *str = NULL; 132 ret = krb5_enctype_to_string(context, type, &str); 133 if (ret) 134 return ret; 135 136 _krb5_debugx(context, 5, "krb5_get_init_creds: using default_s2k_func: %s (%d)", str, (int)type); 137 free(str); 138 } 139 140 password.data = rk_UNCONST(keyseed); 141 password.length = strlen(keyseed); 142 if (s2kparms) 143 opaque = *s2kparms; 144 else 145 krb5_data_zero(&opaque); 146 147 *key = malloc(sizeof(**key)); 148 if (*key == NULL) 149 return ENOMEM; 150 ret = krb5_string_to_key_data_salt_opaque(context, type, password, 151 salt, opaque, *key); 152 if (ret) { 153 free(*key); 154 *key = NULL; 155 } 156 return ret; 157} 158 159static void 160free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx) 161{ 162 if (ctx->etypes) 163 free(ctx->etypes); 164 if (ctx->pre_auth_types) 165 free (ctx->pre_auth_types); 166 if (ctx->in_tkt_service) 167 free(ctx->in_tkt_service); 168 if (ctx->keytab_data) 169 free(ctx->keytab_data); 170 if (ctx->password) { 171 memset(ctx->password, 0, strlen(ctx->password)); 172 free(ctx->password); 173 } 174 /* 175 * FAST state 176 */ 177 _krb5_fast_free(context, &ctx->fast_state); 178 179 krb5_data_free(&ctx->req_buffer); 180 krb5_free_cred_contents(context, &ctx->cred); 181 free_METHOD_DATA(&ctx->md); 182 free_AS_REP(&ctx->as_rep); 183 free_EncKDCRepPart(&ctx->enc_part); 184 free_KRB_ERROR(&ctx->error); 185 free_AS_REQ(&ctx->as_req); 186 187 heim_release(ctx->available_pa_mechs); 188#ifdef PKINIT 189 if (ctx->client_cert) 190 hx509_cert_free(ctx->client_cert); 191 if (ctx->pku2u_assertion) 192 krb5_free_data(context, ctx->pku2u_assertion); 193#endif 194 if (ctx->kdc_hostname) 195 free(ctx->kdc_hostname); 196 free_paid(context, &ctx->paid); 197 memset(ctx, 0, sizeof(*ctx)); 198} 199 200static krb5_deltat 201get_config_time (krb5_context context, 202 const char *realm, 203 const char *name, 204 int def) 205{ 206 krb5_deltat ret; 207 208 ret = krb5_config_get_time (context, NULL, 209 "realms", 210 realm, 211 name, 212 NULL); 213 if (ret >= 0) 214 return ret; 215 ret = krb5_config_get_time (context, NULL, 216 "libdefaults", 217 name, 218 NULL); 219 if (ret >= 0) 220 return ret; 221 return def; 222} 223 224static krb5_error_code 225init_cred (krb5_context context, 226 krb5_creds *cred, 227 krb5_principal client, 228 krb5_deltat start_time, 229 krb5_get_init_creds_opt *options) 230{ 231 krb5_error_code ret; 232 krb5_deltat tmp; 233 krb5_timestamp now; 234 235 krb5_timeofday (context, &now); 236 237 memset (cred, 0, sizeof(*cred)); 238 239 if (client) 240 krb5_copy_principal(context, client, &cred->client); 241 else { 242 ret = krb5_get_default_principal (context, 243 &cred->client); 244 if (ret) 245 goto out; 246 } 247 248 if (start_time) 249 cred->times.starttime = now + start_time; 250 251 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE) 252 tmp = options->tkt_life; 253 else 254 tmp = 10 * 60 * 60; 255 cred->times.endtime = now + tmp; 256 257 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) && 258 options->renew_life > 0) { 259 cred->times.renew_till = now + options->renew_life; 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 LastReq *lr; 303 size_t i; 304 305 /* 306 * First check if there is a API consumer. 307 */ 308 309 lr = &ctx->enc_part.last_req; 310 311 if (options && options->opt_private && options->opt_private->lr.func) { 312 krb5_last_req_entry **lre; 313 314 lre = calloc(lr->len + 1, sizeof(*lre)); 315 if (lre == NULL) { 316 krb5_set_error_message(context, ENOMEM, 317 N_("malloc: out of memory", "")); 318 return ENOMEM; 319 } 320 for (i = 0; i < lr->len; i++) { 321 lre[i] = calloc(1, sizeof(*lre[i])); 322 if (lre[i] == NULL) 323 break; 324 lre[i]->lr_type = lr->val[i].lr_type; 325 lre[i]->value = lr->val[i].lr_value; 326 } 327 328 (*options->opt_private->lr.func)(context, lre, 329 options->opt_private->lr.ctx); 330 331 for (i = 0; i < lr->len; i++) 332 free(lre[i]); 333 free(lre); 334 } 335 336 return krb5_init_creds_warn_user(context, ctx); 337} 338 339/** 340 * Warn the user using prompter in the krb5_init_creds_context about 341 * possible password and account expiration. 342 * 343 * @param context a Kerberos 5 context. 344 * @param ctx a krb5_init_creds_context context. 345 * 346 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 347 * @ingroup krb5_credential 348 */ 349 350KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 351krb5_init_creds_warn_user(krb5_context context, 352 krb5_init_creds_context ctx) 353{ 354 krb5_timestamp sec; 355 krb5_const_realm realm; 356 LastReq *lr; 357 unsigned i; 358 time_t t; 359 360 if (ctx->prompter == NULL) 361 return 0; 362 363 if (ctx->warned_user) 364 return 0; 365 366 ctx->warned_user = 1; 367 368 krb5_timeofday (context, &sec); 369 370 realm = krb5_principal_get_realm (context, ctx->cred.client); 371 lr = &ctx->enc_part.last_req; 372 373 t = sec + get_config_time (context, 374 realm, 375 "warn_pwexpire", 376 7 * 24 * 60 * 60); 377 378 for (i = 0; i < lr->len; ++i) { 379 if (lr->val[i].lr_value <= t) { 380 switch (abs(lr->val[i].lr_type)) { 381 case LR_PW_EXPTIME : 382 report_expiration(context, ctx->prompter, 383 ctx->prompter_data, 384 "Your password will expire at ", 385 lr->val[i].lr_value); 386 break; 387 case LR_ACCT_EXPTIME : 388 report_expiration(context, ctx->prompter, 389 ctx->prompter_data, 390 "Your account will expire at ", 391 lr->val[i].lr_value); 392 break; 393 } 394 } 395 } 396 397 return 0; 398} 399 400static krb5_addresses no_addrs = { 0, NULL }; 401 402static krb5_error_code 403get_init_creds_common(krb5_context context, 404 krb5_principal client, 405 krb5_deltat start_time, 406 krb5_get_init_creds_opt *options, 407 krb5_init_creds_context ctx) 408{ 409 krb5_get_init_creds_opt *default_opt = NULL; 410 krb5_error_code ret; 411 krb5_enctype *etypes; 412 krb5_preauthtype *pre_auth_types; 413 414 memset(ctx, 0, sizeof(*ctx)); 415 416 if (options == NULL) { 417 const char *realm = krb5_principal_get_realm(context, client); 418 419 krb5_get_init_creds_opt_alloc (context, &default_opt); 420 options = default_opt; 421 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options); 422 } 423 424 if (options->opt_private) { 425 if (options->opt_private->password) { 426 ret = krb5_init_creds_set_password(context, ctx, 427 options->opt_private->password); 428 if (ret) 429 goto out; 430 } 431 432 ctx->keyproc = options->opt_private->key_proc; 433 ctx->req_pac = options->opt_private->req_pac; 434 ctx->pk_init_ctx = options->opt_private->pk_init_ctx; 435 ctx->ic_flags = options->opt_private->flags; 436 } else 437 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET; 438 439 if (ctx->keyproc == NULL) 440 ctx->keyproc = default_s2k_func; 441 442 /* Enterprise name implicitly turns on canonicalize */ 443 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) || 444 krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL) 445 ctx->flags.canonicalize = 1; 446 447 ctx->pre_auth_types = NULL; 448 ctx->addrs = NULL; 449 ctx->etypes = NULL; 450 ctx->pre_auth_types = NULL; 451 452 ret = init_cred(context, &ctx->cred, client, start_time, options); 453 if (ret) { 454 if (default_opt) 455 krb5_get_init_creds_opt_free(context, default_opt); 456 return ret; 457 } 458 459 ret = krb5_init_creds_set_service(context, ctx, NULL); 460 if (ret) 461 goto out; 462 463 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) 464 ctx->flags.forwardable = options->forwardable; 465 466 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) 467 ctx->flags.proxiable = options->proxiable; 468 469 if (start_time) 470 ctx->flags.postdated = 1; 471 if (ctx->cred.times.renew_till) 472 ctx->flags.renewable = 1; 473 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) { 474 ctx->addrs = options->address_list; 475 } else if (options->opt_private) { 476 switch (options->opt_private->addressless) { 477 case KRB5_INIT_CREDS_TRISTATE_UNSET: 478#if KRB5_ADDRESSLESS_DEFAULT == TRUE 479 ctx->addrs = &no_addrs; 480#else 481 ctx->addrs = NULL; 482#endif 483 break; 484 case KRB5_INIT_CREDS_TRISTATE_FALSE: 485 ctx->addrs = NULL; 486 break; 487 case KRB5_INIT_CREDS_TRISTATE_TRUE: 488 ctx->addrs = &no_addrs; 489 break; 490 } 491 } 492 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { 493 if (ctx->etypes) 494 free(ctx->etypes); 495 496 etypes = malloc((options->etype_list_length + 1) 497 * sizeof(krb5_enctype)); 498 if (etypes == NULL) { 499 ret = ENOMEM; 500 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 501 goto out; 502 } 503 memcpy (etypes, options->etype_list, 504 options->etype_list_length * sizeof(krb5_enctype)); 505 etypes[options->etype_list_length] = ETYPE_NULL; 506 ctx->etypes = etypes; 507 } 508 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { 509 pre_auth_types = malloc((options->preauth_list_length + 1) 510 * sizeof(krb5_preauthtype)); 511 if (pre_auth_types == NULL) { 512 ret = ENOMEM; 513 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 514 goto out; 515 } 516 memcpy (pre_auth_types, options->preauth_list, 517 options->preauth_list_length * sizeof(krb5_preauthtype)); 518 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE; 519 ctx->pre_auth_types = pre_auth_types; 520 } 521 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) 522 ctx->flags.request_anonymous = options->anonymous; 523 if (default_opt) 524 krb5_get_init_creds_opt_free(context, default_opt); 525 526 return 0; 527 out: 528 if (default_opt) 529 krb5_get_init_creds_opt_free(context, default_opt); 530 return ret; 531} 532 533static krb5_error_code 534change_password (krb5_context context, 535 krb5_principal client, 536 const char *password, 537 char *newpw, 538 size_t newpw_sz, 539 krb5_prompter_fct prompter, 540 void *data, 541 krb5_get_init_creds_opt *old_options) 542{ 543 krb5_prompt prompts[2]; 544 krb5_error_code ret; 545 krb5_creds cpw_cred; 546 char buf1[BUFSIZ], buf2[BUFSIZ]; 547 krb5_data password_data[2]; 548 int result_code; 549 krb5_data result_code_string; 550 krb5_data result_string; 551 char *p; 552 krb5_get_init_creds_opt *options; 553 554 memset (&cpw_cred, 0, sizeof(cpw_cred)); 555 556 ret = krb5_get_init_creds_opt_alloc(context, &options); 557 if (ret) 558 return ret; 559 krb5_get_init_creds_opt_set_tkt_life (options, 60); 560 krb5_get_init_creds_opt_set_forwardable (options, FALSE); 561 krb5_get_init_creds_opt_set_proxiable (options, FALSE); 562 if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) 563 krb5_get_init_creds_opt_set_preauth_list (options, 564 old_options->preauth_list, 565 old_options->preauth_list_length); 566 567 krb5_data_zero (&result_code_string); 568 krb5_data_zero (&result_string); 569 570 ret = krb5_get_init_creds_password (context, 571 &cpw_cred, 572 client, 573 password, 574 prompter, 575 data, 576 0, 577 "kadmin/changepw", 578 options); 579 krb5_get_init_creds_opt_free(context, options); 580 if (ret) 581 goto out; 582 583 for(;;) { 584 password_data[0].data = buf1; 585 password_data[0].length = sizeof(buf1); 586 587 prompts[0].hidden = 1; 588 prompts[0].prompt = "New password: "; 589 prompts[0].reply = &password_data[0]; 590 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD; 591 592 password_data[1].data = buf2; 593 password_data[1].length = sizeof(buf2); 594 595 prompts[1].hidden = 1; 596 prompts[1].prompt = "Repeat new password: "; 597 prompts[1].reply = &password_data[1]; 598 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN; 599 600 ret = (*prompter) (context, data, NULL, "Changing password", 601 2, prompts); 602 if (ret) { 603 ret = KRB5_LIBOS_PWDINTR; 604 memset (buf1, 0, sizeof(buf1)); 605 memset (buf2, 0, sizeof(buf2)); 606 goto out; 607 } 608 609 if (strcmp (buf1, buf2) == 0) 610 break; 611 memset (buf1, 0, sizeof(buf1)); 612 memset (buf2, 0, sizeof(buf2)); 613 } 614 615 ret = krb5_set_password (context, 616 &cpw_cred, 617 buf1, 618 NULL, 619 &result_code, 620 &result_code_string, 621 &result_string); 622 if (ret) 623 goto out; 624 625 if (result_code == 0) { 626 p = strdup("Success"); 627 } else if (asprintf(&p, "Failed: %.*s %.*s: %d\n", 628 (int)result_code_string.length, 629 result_code_string.length > 0 ? (char*)result_code_string.data : "", 630 (int)result_string.length, 631 result_string.length > 0 ? (char*)result_string.data : "", 632 result_code) < 0) 633 { 634 ret = ENOMEM; 635 goto out; 636 } 637 638 /* return the result */ 639 (*prompter) (context, data, NULL, p, 0, NULL); 640 641 if (result_code == 0) { 642 strlcpy (newpw, buf1, newpw_sz); 643 ret = 0; 644 } else { 645 ret = ENOTTY; 646 krb5_set_error_message(context, ret, 647 N_("failed changing password: %s", ""), p); 648 } 649 free (p); 650 651out: 652 memset (buf1, 0, sizeof(buf1)); 653 memset (buf2, 0, sizeof(buf2)); 654 krb5_data_free (&result_string); 655 krb5_data_free (&result_code_string); 656 krb5_free_cred_contents (context, &cpw_cred); 657 return ret; 658} 659 660 661KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 662krb5_keyblock_key_proc (krb5_context context, 663 krb5_keytype type, 664 krb5_data *salt, 665 krb5_const_pointer keyseed, 666 krb5_keyblock **key) 667{ 668 return krb5_copy_keyblock (context, keyseed, key); 669} 670 671/* 672 * 673 */ 674 675static krb5_error_code 676init_as_req (krb5_context context, 677 KDCOptions opts, 678 const krb5_creds *creds, 679 const krb5_addresses *addrs, 680 const krb5_enctype *etypes, 681 AS_REQ *a) 682{ 683 krb5_error_code ret; 684 685 memset(a, 0, sizeof(*a)); 686 687 a->pvno = 5; 688 a->msg_type = krb_as_req; 689 a->req_body.kdc_options = opts; 690 a->req_body.cname = malloc(sizeof(*a->req_body.cname)); 691 if (a->req_body.cname == NULL) { 692 ret = ENOMEM; 693 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 694 goto fail; 695 } 696 a->req_body.sname = calloc(1, sizeof(*a->req_body.sname)); 697 if (a->req_body.sname == NULL) { 698 ret = ENOMEM; 699 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 700 goto fail; 701 } 702 703 ret = _krb5_principal2principalname (a->req_body.cname, creds->client); 704 if (ret) 705 goto fail; 706 ret = copy_Realm(&creds->client->realm, &a->req_body.realm); 707 if (ret) 708 goto fail; 709 710 ret = _krb5_principal2principalname (a->req_body.sname, creds->server); 711 if (ret) 712 goto fail; 713 714 if(creds->times.starttime) { 715 a->req_body.from = malloc(sizeof(*a->req_body.from)); 716 if (a->req_body.from == NULL) { 717 ret = ENOMEM; 718 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 719 goto fail; 720 } 721 *a->req_body.from = creds->times.starttime; 722 } 723 if(creds->times.endtime){ 724 ALLOC(a->req_body.till, 1); 725 *a->req_body.till = creds->times.endtime; 726 } 727 if(creds->times.renew_till){ 728 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime)); 729 if (a->req_body.rtime == NULL) { 730 ret = ENOMEM; 731 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 732 goto fail; 733 } 734 *a->req_body.rtime = creds->times.renew_till; 735 } 736 a->req_body.nonce = 0; 737 ret = _krb5_init_etype(context, 738 KRB5_PDU_AS_REQUEST, 739 &a->req_body.etype.len, 740 &a->req_body.etype.val, 741 etypes); 742 if (ret) 743 goto fail; 744 745 /* 746 * This means no addresses 747 */ 748 749 if (addrs && addrs->len == 0) { 750 a->req_body.addresses = NULL; 751 } else { 752 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses)); 753 if (a->req_body.addresses == NULL) { 754 ret = ENOMEM; 755 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 756 goto fail; 757 } 758 759 if (addrs) 760 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses); 761 else { 762 ret = krb5_get_all_client_addrs (context, a->req_body.addresses); 763 if(ret == 0 && a->req_body.addresses->len == 0) { 764 free(a->req_body.addresses); 765 a->req_body.addresses = NULL; 766 } 767 } 768 if (ret) 769 goto fail; 770 } 771 772 a->req_body.enc_authorization_data = NULL; 773 a->req_body.additional_tickets = NULL; 774 775 a->padata = NULL; 776 777 return 0; 778 fail: 779 free_AS_REQ(a); 780 memset(a, 0, sizeof(*a)); 781 return ret; 782} 783 784static krb5_error_code 785set_paid(struct pa_info_data *paid, krb5_context context, 786 krb5_enctype etype, 787 krb5_salttype salttype, void *salt_string, size_t salt_len, 788 krb5_data *s2kparams) 789{ 790 paid->etype = etype; 791 paid->salt.salttype = salttype; 792 paid->salt.saltvalue.data = malloc(salt_len + 1); 793 if (paid->salt.saltvalue.data == NULL) { 794 krb5_clear_error_message(context); 795 return ENOMEM; 796 } 797 memcpy(paid->salt.saltvalue.data, salt_string, salt_len); 798 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0'; 799 paid->salt.saltvalue.length = salt_len; 800 if (s2kparams) { 801 krb5_error_code ret; 802 803 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams); 804 if (ret) { 805 krb5_clear_error_message(context); 806 krb5_free_salt(context, paid->salt); 807 return ret; 808 } 809 } else 810 paid->s2kparams = NULL; 811 812 return 0; 813} 814 815static struct pa_info_data * 816pa_etype_info2(krb5_context context, 817 const krb5_principal client, 818 const AS_REQ *asreq, 819 struct pa_info_data *paid, 820 heim_octet_string *data) 821{ 822 krb5_error_code ret; 823 ETYPE_INFO2 e; 824 size_t sz; 825 size_t i, j; 826 827 memset(&e, 0, sizeof(e)); 828 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz); 829 if (ret) 830 goto out; 831 if (e.len == 0) 832 goto out; 833 for (j = 0; j < asreq->req_body.etype.len; j++) { 834 for (i = 0; i < e.len; i++) { 835 if (asreq->req_body.etype.val[j] == e.val[i].etype) { 836 krb5_salt salt; 837 if (e.val[i].salt == NULL) 838 ret = krb5_get_pw_salt(context, client, &salt); 839 else { 840 salt.saltvalue.data = *e.val[i].salt; 841 salt.saltvalue.length = strlen(*e.val[i].salt); 842 ret = 0; 843 } 844 if (ret == 0) 845 ret = set_paid(paid, context, e.val[i].etype, 846 KRB5_PW_SALT, 847 salt.saltvalue.data, 848 salt.saltvalue.length, 849 e.val[i].s2kparams); 850 if (e.val[i].salt == NULL) 851 krb5_free_salt(context, salt); 852 if (ret == 0) { 853 free_ETYPE_INFO2(&e); 854 return paid; 855 } 856 } 857 } 858 } 859 out: 860 free_ETYPE_INFO2(&e); 861 return NULL; 862} 863 864static struct pa_info_data * 865pa_etype_info(krb5_context context, 866 const krb5_principal client, 867 const AS_REQ *asreq, 868 struct pa_info_data *paid, 869 heim_octet_string *data) 870{ 871 krb5_error_code ret; 872 ETYPE_INFO e; 873 size_t sz; 874 size_t i, j; 875 876 memset(&e, 0, sizeof(e)); 877 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz); 878 if (ret) 879 goto out; 880 if (e.len == 0) 881 goto out; 882 for (j = 0; j < asreq->req_body.etype.len; j++) { 883 for (i = 0; i < e.len; i++) { 884 if (asreq->req_body.etype.val[j] == e.val[i].etype) { 885 krb5_salt salt; 886 salt.salttype = KRB5_PW_SALT; 887 if (e.val[i].salt == NULL) 888 ret = krb5_get_pw_salt(context, client, &salt); 889 else { 890 salt.saltvalue = *e.val[i].salt; 891 ret = 0; 892 } 893 if (e.val[i].salttype) 894 salt.salttype = *e.val[i].salttype; 895 if (ret == 0) { 896 ret = set_paid(paid, context, e.val[i].etype, 897 salt.salttype, 898 salt.saltvalue.data, 899 salt.saltvalue.length, 900 NULL); 901 if (e.val[i].salt == NULL) 902 krb5_free_salt(context, salt); 903 } 904 if (ret == 0) { 905 free_ETYPE_INFO(&e); 906 return paid; 907 } 908 } 909 } 910 } 911 out: 912 free_ETYPE_INFO(&e); 913 return NULL; 914} 915 916static struct pa_info_data * 917pa_pw_or_afs3_salt(krb5_context context, 918 const krb5_principal client, 919 const AS_REQ *asreq, 920 struct pa_info_data *paid, 921 heim_octet_string *data) 922{ 923 krb5_error_code ret; 924 if (paid->etype == KRB5_ENCTYPE_NULL) 925 return NULL; 926 ret = set_paid(paid, context, 927 paid->etype, 928 paid->salt.salttype, 929 data->data, 930 data->length, 931 NULL); 932 if (ret) 933 return NULL; 934 return paid; 935} 936 937 938static krb5_error_code 939make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md, 940 krb5_enctype etype, krb5_keyblock *key) 941{ 942 PA_ENC_TS_ENC p; 943 unsigned char *buf; 944 size_t buf_size; 945 size_t len = 0; 946 EncryptedData encdata; 947 krb5_error_code ret; 948 int32_t usec; 949 int usec2; 950 krb5_crypto crypto; 951 952 krb5_us_timeofday (context, &p.patimestamp, &usec); 953 usec2 = usec; 954 p.pausec = &usec2; 955 956 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); 957 if (ret) 958 return ret; 959 if(buf_size != len) 960 krb5_abortx(context, "internal error in ASN.1 encoder"); 961 962 ret = krb5_crypto_init(context, key, 0, &crypto); 963 if (ret) { 964 free(buf); 965 return ret; 966 } 967 ret = krb5_encrypt_EncryptedData(context, 968 crypto, 969 KRB5_KU_PA_ENC_TIMESTAMP, 970 buf, 971 len, 972 0, 973 &encdata); 974 free(buf); 975 krb5_crypto_destroy(context, crypto); 976 if (ret) 977 return ret; 978 979 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); 980 free_EncryptedData(&encdata); 981 if (ret) 982 return ret; 983 if(buf_size != len) 984 krb5_abortx(context, "internal error in ASN.1 encoder"); 985 986 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len); 987 if (ret) 988 free(buf); 989 return ret; 990} 991 992static krb5_error_code 993add_enc_ts_padata(krb5_context context, 994 METHOD_DATA *md, 995 krb5_principal client, 996 krb5_s2k_proc keyproc, 997 krb5_const_pointer keyseed, 998 krb5_enctype *enctypes, 999 unsigned netypes, 1000 krb5_salt *salt, 1001 krb5_data *s2kparams) 1002{ 1003 krb5_error_code ret; 1004 krb5_salt salt2; 1005 krb5_enctype *ep; 1006 size_t i; 1007 1008 memset(&salt2, 0, sizeof(salt2)); 1009 1010 if(salt == NULL) { 1011 /* default to standard salt */ 1012 ret = krb5_get_pw_salt (context, client, &salt2); 1013 if (ret) 1014 return ret; 1015 salt = &salt2; 1016 } 1017 if (!enctypes) { 1018 enctypes = context->etypes; 1019 netypes = 0; 1020 for (ep = enctypes; *ep != ETYPE_NULL; ep++) 1021 netypes++; 1022 } 1023 1024 for (i = 0; i < netypes; ++i) { 1025 krb5_keyblock *key; 1026 1027 _krb5_debugx(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]); 1028 1029 ret = (*keyproc)(context, enctypes[i], keyseed, 1030 *salt, s2kparams, &key); 1031 if (ret) 1032 continue; 1033 ret = make_pa_enc_timestamp (context, md, enctypes[i], key); 1034 krb5_free_keyblock (context, key); 1035 if (ret) 1036 return ret; 1037 } 1038 if(salt == &salt2) 1039 krb5_free_salt(context, salt2); 1040 return 0; 1041} 1042 1043static krb5_error_code 1044pa_data_to_md_ts_enc(krb5_context context, 1045 const AS_REQ *a, 1046 const krb5_principal client, 1047 krb5_init_creds_context ctx, 1048 struct pa_info_data *ppaid, 1049 METHOD_DATA *md) 1050{ 1051 if (ctx->keyproc == NULL || ctx->keyseed == NULL) { 1052 _krb5_debugx(context, 5, "krb5_get_init_creds: no keyproc or keyseed"); 1053 return 0; 1054 } 1055 1056 if (ppaid) { 1057 _krb5_debugx(context, 5, "krb5_get_init_creds: pa-info found, using %d", (int)ppaid->etype); 1058 1059 add_enc_ts_padata(context, md, client, 1060 ctx->keyproc, ctx->keyseed, 1061 &ppaid->etype, 1, 1062 &ppaid->salt, ppaid->s2kparams); 1063 } else { 1064 krb5_salt salt; 1065 1066 _krb5_debugx(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt"); 1067 1068 /* make a v5 salted pa-data */ 1069 add_enc_ts_padata(context, md, client, 1070 ctx->keyproc, ctx->keyseed, 1071 a->req_body.etype.val, a->req_body.etype.len, 1072 NULL, NULL); 1073 1074 /* make a v4 salted pa-data */ 1075 salt.salttype = KRB5_PW_SALT; 1076 krb5_data_zero(&salt.saltvalue); 1077 add_enc_ts_padata(context, md, client, 1078 ctx->keyproc, ctx->keyseed, 1079 a->req_body.etype.val, a->req_body.etype.len, 1080 &salt, NULL); 1081 } 1082 return 0; 1083} 1084 1085static krb5_error_code 1086pa_data_to_key_plain(krb5_context context, 1087 const krb5_principal client, 1088 krb5_init_creds_context ctx, 1089 krb5_salt salt, 1090 krb5_data *s2kparams, 1091 krb5_enctype etype, 1092 krb5_keyblock **key) 1093{ 1094 krb5_error_code ret; 1095 1096 ret = (*ctx->keyproc)(context, etype, ctx->keyseed, 1097 salt, s2kparams, key); 1098 return ret; 1099} 1100 1101 1102static krb5_error_code 1103pa_data_to_md_pkinit(krb5_context context, 1104 const AS_REQ *a, 1105 const krb5_principal client, 1106 int win2k, 1107 krb5_init_creds_context ctx, 1108 METHOD_DATA *md) 1109{ 1110 if (ctx->pk_init_ctx == NULL) 1111 return 0; 1112#ifdef PKINIT 1113 return _krb5_pk_mk_padata(context, 1114 ctx->pk_init_ctx, 1115 ctx->ic_flags, 1116 win2k, 1117 &a->req_body, 1118 ctx->pk_nonce, 1119 md); 1120#else 1121 krb5_set_error_message(context, EINVAL, 1122 N_("no support for PKINIT compiled in", "")); 1123 return EINVAL; 1124#endif 1125} 1126 1127static krb5_error_code 1128pkinit_configure(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx) 1129{ 1130 if (ctx->pk_init_ctx == NULL) 1131 return HEIM_ERR_PA_CANT_CONTINUE; 1132 1133 return 0; 1134} 1135 1136static krb5_error_code 1137pkinit_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, 1138 const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) 1139{ 1140 if (rep == NULL) { 1141 krb5_error_code ret; 1142 ret = pa_data_to_md_pkinit(context, a, ctx->cred.client, 0, ctx, out_md); 1143 if (ret == 0) 1144 ret = HEIM_ERR_PA_CONTINUE_NEEDED; 1145 return ret; 1146 } else { 1147 return _krb5_pk_rd_pa_reply(context, 1148 a->req_body.realm, 1149 ctx->pk_init_ctx, 1150 rep->enc_part.etype, 1151 hi, 1152 ctx->pk_nonce, 1153 &ctx->req_buffer, 1154 pa, 1155 &ctx->fast_state.reply_key); 1156 } 1157} 1158 1159static krb5_error_code 1160pkinit_step_win(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, 1161 const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) 1162{ 1163 if (rep == NULL) { 1164 krb5_error_code ret; 1165 ret = pa_data_to_md_pkinit(context, a, ctx->cred.client, 1, ctx, out_md); 1166 if (ret == 0) 1167 ret = HEIM_ERR_PA_CONTINUE_NEEDED; 1168 return ret; 1169 } else { 1170 return _krb5_pk_rd_pa_reply(context, 1171 a->req_body.realm, 1172 ctx->pk_init_ctx, 1173 rep->enc_part.etype, 1174 hi, 1175 ctx->pk_nonce, 1176 &ctx->req_buffer, 1177 pa, 1178 &ctx->fast_state.reply_key); 1179 } 1180} 1181 1182static void 1183pkinit_release(void *pa_ctx) 1184{ 1185} 1186 1187krb5_error_code 1188_krb5_make_pa_enc_challange(krb5_context context, 1189 krb5_crypto crypto, 1190 krb5_key_usage usage, 1191 METHOD_DATA *md) 1192{ 1193 PA_ENC_TS_ENC p; 1194 unsigned char *buf; 1195 size_t buf_size; 1196 size_t len = 0; 1197 EncryptedData encdata; 1198 krb5_error_code ret; 1199 int32_t usec; 1200 int usec2; 1201 1202 krb5_us_timeofday (context, &p.patimestamp, &usec); 1203 usec2 = usec; 1204 p.pausec = &usec2; 1205 1206 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); 1207 if (ret) 1208 return ret; 1209 if(buf_size != len) 1210 krb5_abortx(context, "internal error in ASN.1 encoder"); 1211 1212 ret = krb5_encrypt_EncryptedData(context, 1213 crypto, 1214 usage, 1215 buf, 1216 len, 1217 0, 1218 &encdata); 1219 free(buf); 1220 if (ret) 1221 return ret; 1222 1223 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); 1224 free_EncryptedData(&encdata); 1225 if (ret) 1226 return ret; 1227 if(buf_size != len) 1228 krb5_abortx(context, "internal error in ASN.1 encoder"); 1229 1230 ret = krb5_padata_add(context, md, KRB5_PADATA_ENCRYPTED_CHALLENGE, buf, len); 1231 if (ret) 1232 free(buf); 1233 return ret; 1234} 1235 1236krb5_error_code 1237_krb5_validate_pa_enc_challange(krb5_context context, 1238 krb5_crypto crypto, 1239 krb5_key_usage usage, 1240 EncryptedData *enc_data, 1241 const char *peer_name) 1242{ 1243 krb5_error_code ret; 1244 krb5_data ts_data; 1245 PA_ENC_TS_ENC p; 1246 time_t timestamp; 1247 int32_t usec; 1248 size_t size; 1249 1250 ret = krb5_decrypt_EncryptedData(context, crypto, usage, enc_data, &ts_data); 1251 if (ret) 1252 return ret; 1253 1254 ret = decode_PA_ENC_TS_ENC(ts_data.data, 1255 ts_data.length, 1256 &p, 1257 &size); 1258 krb5_data_free(&ts_data); 1259 if(ret){ 1260 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1261 _krb5_debugx(context, 5, "Failed to decode PA-ENC-TS_ENC - %s", peer_name); 1262 goto out; 1263 } 1264 1265 krb5_us_timeofday(context, ×tamp, &usec); 1266 1267 if (krb5_time_abs(timestamp, p.patimestamp) > context->max_skew) { 1268 char client_time[100]; 1269 1270 krb5_format_time(context, p.patimestamp, 1271 client_time, sizeof(client_time), TRUE); 1272 1273 ret = KRB5KRB_AP_ERR_SKEW; 1274 _krb5_debugx(context, 0, "Too large time skew, " 1275 "client time %s is out by %u > %d seconds -- %s", 1276 client_time, 1277 (unsigned)krb5_time_abs(timestamp, p.patimestamp), 1278 (int)context->max_skew, 1279 peer_name); 1280 } else { 1281 ret = 0; 1282 } 1283 1284 out: 1285 free_PA_ENC_TS_ENC(&p); 1286 1287 return ret; 1288} 1289 1290 1291static struct pa_info_data * 1292process_pa_info(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, METHOD_DATA *); 1293 1294 1295static krb5_error_code 1296enc_chal_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, 1297 const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) 1298{ 1299 struct pa_info_data paid, *ppaid; 1300 krb5_keyblock challengekey; 1301 krb5_data pepper1, pepper2; 1302 krb5_crypto crypto = NULL; 1303 krb5_enctype aenctype; 1304 krb5_error_code ret; 1305 1306 memset(&paid, 0, sizeof(paid)); 1307 1308 if (rep == NULL) 1309 paid.etype = KRB5_ENCTYPE_NULL; 1310 else 1311 paid.etype = rep->enc_part.etype; 1312 ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md); 1313 1314 /* 1315 * If we don't have ppaid, ts because the KDC have not sent any 1316 * salt info, lets to the first roundtrip so the KDC have a chance 1317 * to send any. 1318 */ 1319 if (ppaid == NULL) { 1320 _krb5_debugx(context, 5, "no ppaid found"); 1321 return HEIM_ERR_PA_CONTINUE_NEEDED; 1322 } 1323 if (ppaid->etype == ETYPE_NULL) { 1324 return HEIM_ERR_PA_CANT_CONTINUE; 1325 } 1326 1327 if (ctx->fast_state.reply_key) 1328 krb5_free_keyblock(context, ctx->fast_state.reply_key); 1329 1330 ret = pa_data_to_key_plain(context, ctx->cred.client, ctx, 1331 ppaid->salt, ppaid->s2kparams, ppaid->etype, 1332 &ctx->fast_state.reply_key); 1333 free_paid(context, &paid); 1334 if (ret) { 1335 _krb5_debugx(context, 5, "enc-chal: failed to build key"); 1336 return ret; 1337 } 1338 1339 ret = krb5_crypto_init(context, ctx->fast_state.reply_key, 0, &crypto); 1340 if (ret) 1341 return ret; 1342 1343 krb5_crypto_getenctype(context, ctx->fast_state.armor_crypto, &aenctype); 1344 1345 pepper1.data = rep ? "kdcchallengearmor" : "clientchallengearmor"; 1346 pepper1.length = strlen(pepper1.data); 1347 pepper2.data = "challengelongterm"; 1348 pepper2.length = strlen(pepper2.data); 1349 1350 ret = krb5_crypto_fx_cf2(context, ctx->fast_state.armor_crypto, crypto, 1351 &pepper1, &pepper2, aenctype, 1352 &challengekey); 1353 krb5_crypto_destroy(context, crypto); 1354 1355 ret = krb5_crypto_init(context, &challengekey, 0, &crypto); 1356 krb5_free_keyblock_contents(context, &challengekey); 1357 if (ret) 1358 return ret; 1359 1360 if (rep) { 1361 EncryptedData enc_data; 1362 size_t size; 1363 1364 if (ret) { 1365 _krb5_debugx(context, 5, "enc-chal: failed to create reply key"); 1366 return ret; 1367 } 1368 1369 _krb5_debugx(context, 5, "ENC_CHAL rep key"); 1370 1371 if (ctx->fast_state.strengthen_key == NULL) { 1372 krb5_crypto_destroy(context, crypto); 1373 _krb5_debugx(context, 5, "ENC_CHAL w/o strengthen_key"); 1374 return KRB5_KDCREP_MODIFIED; 1375 } 1376 1377 if (pa == NULL) { 1378 krb5_crypto_destroy(context, crypto); 1379 _krb5_debugx(context, 0, "KDC response missing"); 1380 return HEIM_ERR_PA_CANT_CONTINUE; 1381 } 1382 1383 ret = decode_EncryptedData(pa->padata_value.data, 1384 pa->padata_value.length, 1385 &enc_data, 1386 &size); 1387 if (ret) { 1388 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; 1389 _krb5_debugx(context, 5, "Failed to decode ENC_CHAL KDC reply"); 1390 return ret; 1391 } 1392 1393 ret = _krb5_validate_pa_enc_challange(context, crypto, 1394 KRB5_KU_ENC_CHALLENGE_KDC, 1395 &enc_data, 1396 "KDC"); 1397 free_EncryptedData(&enc_data); 1398 krb5_crypto_destroy(context, crypto); 1399 1400 return ret; 1401 1402 } else { 1403 1404 ret = _krb5_make_pa_enc_challange(context, crypto, 1405 KRB5_KU_ENC_CHALLENGE_CLIENT, 1406 out_md); 1407 krb5_crypto_destroy(context, crypto); 1408 if (ret) { 1409 _krb5_debugx(context, 5, "enc-chal: failed build enc challange"); 1410 return ret; 1411 } 1412 1413 return HEIM_ERR_PA_CONTINUE_NEEDED; 1414 } 1415} 1416 1417#ifdef __APPLE_PRIVATE__ 1418 1419/* 1420 * SRP 1421 */ 1422 1423#include <corecrypto/ccsrp.h> 1424#include <corecrypto/ccsrp_gp.h> 1425#include <corecrypto/ccdh.h> 1426#include <corecrypto/ccsha2.h> 1427#include <corecrypto/ccpbkdf2.h> 1428#include <CommonCrypto/CommonRandomSPI.h> 1429 1430/* 1431 * ordered in preference 1432 */ 1433static const struct _krb5_srp_group { 1434 enum KRB5_SRP_GROUP group; 1435 ccdh_const_gp_t (*gp)(void); 1436 const struct ccdigest_info *(*di)(void); 1437 const heim_oid *oid; 1438} srpgroups[] = { 1439 { 1440 KRB5_SRP_GROUP_RFC5054_4096_PBKDF2_SHA512, 1441 ccsrp_gp_rfc5054_4096, 1442 ccsha512_di, 1443 &asn1_oid_id_pkinit_kdf_ah_sha512 1444 } 1445}; 1446 1447/* 1448 * 1449 */ 1450 1451const struct _krb5_srp_group * 1452_krb5_srp_validate_group(KRB5_SRP_GROUP group) 1453{ 1454 size_t n; 1455 1456 for (n = 0; n < sizeof(srpgroups)/sizeof(srpgroups[0]); n++) 1457 if (srpgroups[n].group == group) 1458 return &srpgroups[n]; 1459 1460 return NULL; 1461} 1462 1463 1464size_t 1465_krb5_srp_pkisize(const struct _krb5_srp_group *group) 1466{ 1467 return ccdh_gp_prime_size(group->gp()); 1468} 1469 1470size_t 1471_krb5_srp_keysize(const struct _krb5_srp_group *group) 1472{ 1473 return group->di()->output_size; 1474} 1475 1476struct _krb5_srp * 1477_krb5_srp_create(const struct _krb5_srp_group *group) 1478{ 1479 const struct ccdigest_info *di = group->di(); 1480 ccsrp_const_gp_t gp = group->gp(); 1481 ccsrp_ctx * srp; 1482 1483 srp = malloc(ccsrp_sizeof_srp(di, gp)); 1484 if (srp == NULL) 1485 return NULL; 1486 1487 ccsrp_ctx_init(srp, di, gp); 1488 1489 return (struct _krb5_srp *)srp; 1490} 1491 1492krb5_error_code 1493_krb5_srp_create_pa(krb5_context context, 1494 const struct _krb5_srp_group *group, 1495 krb5_const_principal principal, 1496 const char *password, 1497 const KRB5_SRP_PA *spa, 1498 krb5_data *verifier) 1499{ 1500 krb5_error_code ret; 1501 char *username; 1502 ccsrp_ctx *srpctx; 1503 krb5_data key; 1504 1505 ret = krb5_data_alloc(verifier, _krb5_srp_pkisize(group)); 1506 if (ret) 1507 return ret; 1508 1509 ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &username); 1510 if (ret) { 1511 krb5_data_free(verifier); 1512 return ret; 1513 } 1514 1515 ret = krb5_data_alloc(&key, _krb5_srp_keysize(group)); 1516 if (ret) { 1517 free(username); 1518 krb5_data_free(verifier); 1519 return ret; 1520 } 1521 1522 ret = ccpbkdf2_hmac(group->di(), 1523 strlen(password), password, 1524 spa->salt.length, spa->salt.data, 1525 spa->iterations, _krb5_srp_keysize(group), 1526 key.data); 1527 1528 srpctx = (ccsrp_ctx *)_krb5_srp_create(group); 1529 if (srpctx == NULL) { 1530 krb5_data_free(verifier); 1531 krb5_data_free(&key); 1532 krb5_xfree(username); 1533 return ENOMEM; 1534 } 1535 1536 ret = ccsrp_generate_verifier(srpctx, username, 1537 key.length, key.data, 1538 spa->salt.length, spa->salt.data, 1539 verifier->data); 1540 krb5_data_free(&key); 1541 krb5_xfree(srpctx); 1542 krb5_xfree(username); 1543 if (ret) 1544 return EINVAL; 1545 1546 return 0; 1547} 1548 1549krb5_error_code 1550_krb5_srp_reply_key(krb5_context context, 1551 const struct _krb5_srp_group *srpgroup, 1552 krb5_enctype enctype, 1553 const void *session_key, 1554 size_t session_key_length, 1555 krb5_const_principal client, 1556 krb5_data *req_buffer, 1557 krb5_data *announce, 1558 krb5_keyblock *reply_key) 1559{ 1560 AlgorithmIdentifier ai; 1561 1562 ai.algorithm = *srpgroup->oid; 1563 ai.parameters = NULL; 1564 1565 return _krb5_pk_kdf(context, 1566 &ai, 1567 session_key, 1568 session_key_length, 1569 client, 1570 NULL, 1571 enctype, 1572 req_buffer, 1573 announce, 1574 NULL, 1575 reply_key); 1576} 1577 1578 1579 1580/* 1581 * Client only bits below 1582 */ 1583 1584enum KRB5_SRP_STATE { 1585 KRB5_SRP_STATE_FIRST = 0, 1586 KRB5_SRP_STATE_INIT, 1587 KRB5_SRP_STATE_SERVER_CHALLENGE, 1588 KRB5_SRP_STATE_SERVER_VERIFIER, 1589 KRB5_SRP_STATE_DONE 1590}; 1591 1592typedef struct srp_state_data { 1593 enum KRB5_SRP_STATE state; 1594 const struct _krb5_srp_group *group; 1595 ccsrp_ctx *srp; 1596 size_t keylength; 1597 size_t pkilength; 1598 krb5_data key; 1599 krb5_data announce; 1600 KRB5_SRP_PA spa; 1601} *srp_state_t; 1602 1603static krb5_error_code 1604srp_configure(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx) 1605{ 1606 srp_state_t state = pa_ctx; 1607 1608 if (ctx->password == NULL) 1609 return HEIM_ERR_PA_CANT_CONTINUE; 1610 1611 state->state = KRB5_SRP_STATE_INIT; 1612 state->group = NULL; 1613 1614 return 0; 1615} 1616 1617static krb5_error_code 1618srp_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, 1619 const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) 1620{ 1621 srp_state_t state = pa_ctx; 1622 krb5_error_code ret; 1623 1624 if (pa == NULL) { 1625 _krb5_debugx(context, 0, "KDC didn't return any SRP pa data"); 1626 state->state = KRB5_SRP_STATE_DONE; 1627 return HEIM_ERR_PA_CANT_CONTINUE; 1628 } 1629 1630 if (state->state == KRB5_SRP_STATE_INIT) { 1631 KRB5_SRP_PA_ANNOUNCE sa; 1632 KRB5_SRP_PA *spa = NULL; 1633 KRB5_SRP_PA_INIT ci; 1634 krb5_data data; 1635 size_t size, n; 1636 1637 memset(&ci, 0, sizeof(ci)); 1638 1639 ret = decode_KRB5_SRP_PA_ANNOUNCE(pa->padata_value.data, pa->padata_value.length, &sa, &size); 1640 if (ret) { 1641 state->state = KRB5_SRP_STATE_DONE; 1642 return HEIM_ERR_PA_CANT_CONTINUE; 1643 } 1644 1645 ret = krb5_data_copy(&state->announce, pa->padata_value.data, pa->padata_value.length); 1646 if (ret) { 1647 state->state = KRB5_SRP_STATE_DONE; 1648 free_KRB5_SRP_PA_ANNOUNCE(&sa); 1649 return HEIM_ERR_PA_CANT_CONTINUE; 1650 } 1651 1652 /* pick first group KDC sends, the list is validated by mixing it into the kdf */ 1653 for (n = 0; n < sa.groups.len; n++) { 1654 state->group = _krb5_srp_validate_group(sa.groups.val[n].group); 1655 if (state->group != NULL) { 1656 spa = &sa.groups.val[n]; 1657 break; 1658 } 1659 } 1660 if (state->group == NULL) { 1661 _krb5_debugx(context, 0, "KDC didn't send a good SRP group for us, sent %u group(s)", 1662 (unsigned)sa.groups.len); 1663 state->state = KRB5_SRP_STATE_DONE; 1664 free_KRB5_SRP_PA_ANNOUNCE(&sa); 1665 return HEIM_ERR_PA_CANT_CONTINUE; 1666 } 1667 1668 ret = copy_KRB5_SRP_PA(spa, &state->spa); 1669 if (ret) { 1670 state->state = KRB5_SRP_STATE_DONE; 1671 free_KRB5_SRP_PA_ANNOUNCE(&sa); 1672 return HEIM_ERR_PA_CANT_CONTINUE; 1673 } 1674 1675 state->srp = (ccsrp_ctx *)_krb5_srp_create(state->group); 1676 if (state->srp == NULL) { 1677 state->state = KRB5_SRP_STATE_DONE; 1678 free_KRB5_SRP_PA_ANNOUNCE(&sa); 1679 return HEIM_ERR_PA_CANT_CONTINUE; 1680 } 1681 state->keylength = _krb5_srp_keysize(state->group); 1682 state->pkilength = _krb5_srp_pkisize(state->group); 1683 1684 ret = krb5_data_alloc(&state->key, state->keylength); 1685 if (ret) { 1686 state->state = KRB5_SRP_STATE_DONE; 1687 free_KRB5_SRP_PA_ANNOUNCE(&sa); 1688 return HEIM_ERR_PA_CANT_CONTINUE; 1689 } 1690 1691 ret = ccpbkdf2_hmac(state->group->di(), 1692 strlen(ctx->password), ctx->password, 1693 spa->salt.length, spa->salt.data, 1694 spa->iterations, state->keylength, 1695 state->key.data); 1696 free_KRB5_SRP_PA_ANNOUNCE(&sa); 1697 if (ret) { 1698 state->state = KRB5_SRP_STATE_DONE; 1699 return HEIM_ERR_PA_CANT_CONTINUE; 1700 } 1701 1702 ret = krb5_data_alloc(&ci.a, state->pkilength); 1703 if (ret) { 1704 state->state = KRB5_SRP_STATE_DONE; 1705 return HEIM_ERR_PA_CANT_CONTINUE; 1706 } 1707 1708 /* 1709 * 1710 */ 1711 ci.group = state->group->group; 1712 1713 ccsrp_client_start_authentication(state->srp, ccDevRandomGetRngState(), ci.a.data); 1714 1715 ASN1_MALLOC_ENCODE(KRB5_SRP_PA_INIT, data.data, data.length, &ci, &size, ret); 1716 free_KRB5_SRP_PA_INIT(&ci); 1717 if (ret) { 1718 state->state = KRB5_SRP_STATE_DONE; 1719 return HEIM_ERR_PA_CANT_CONTINUE; 1720 } 1721 heim_assert(data.length == size, "ASN1.1 Internal error"); 1722 1723 ret = krb5_padata_add(context, out_md, KRB5_PADATA_SRP, data.data, data.length); 1724 if (ret) { 1725 free(data.data); 1726 state->state = KRB5_SRP_STATE_DONE; 1727 return HEIM_ERR_PA_CANT_CONTINUE; 1728 } 1729 1730 state->state = KRB5_SRP_STATE_SERVER_CHALLENGE; 1731 1732 return HEIM_ERR_PA_CONTINUE_NEEDED; 1733 1734 } else if (state->state == KRB5_SRP_STATE_SERVER_CHALLENGE) { 1735 KRB5_SRP_PA_SERVER_CHALLENGE sc; 1736 KRB5_SRP_PA_CLIENT_RESPONSE cr; 1737 krb5_principal principal = NULL; 1738 krb5_data data; 1739 char *username; 1740 size_t size; 1741 1742 memset(&cr, 0, sizeof(cr)); 1743 1744 ret = decode_KRB5_SRP_PA_SERVER_CHALLENGE(pa->padata_value.data, pa->padata_value.length, &sc, &size); 1745 if (ret) { 1746 state->state = KRB5_SRP_STATE_DONE; 1747 return HEIM_ERR_PA_CANT_CONTINUE; 1748 } 1749 1750 if (sc.length != state->pkilength) { 1751 state->state = KRB5_SRP_STATE_DONE; 1752 free_KRB5_SRP_PA_SERVER_CHALLENGE(&sc); 1753 return HEIM_ERR_PA_CANT_CONTINUE; 1754 } 1755 heim_assert(a->req_body.cname != NULL, "should not get here since we could not have found the hdb entry otherwise"); 1756 1757 ret = _krb5_principalname2krb5_principal(context, &principal, 1758 *a->req_body.cname, 1759 a->req_body.realm); 1760 if (ret) { 1761 state->state = KRB5_SRP_STATE_DONE; 1762 free_KRB5_SRP_PA_SERVER_CHALLENGE(&sc); 1763 return HEIM_ERR_PA_CANT_CONTINUE; 1764 } 1765 1766 1767 ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &username); 1768 krb5_free_principal(context, principal); 1769 if (ret) { 1770 state->state = KRB5_SRP_STATE_DONE; 1771 free_KRB5_SRP_PA_SERVER_CHALLENGE(&sc); 1772 return HEIM_ERR_PA_CANT_CONTINUE; 1773 } 1774 1775 ret = krb5_data_alloc(&cr, state->keylength); 1776 if (ret) { 1777 free(username); 1778 state->state = KRB5_SRP_STATE_DONE; 1779 free_KRB5_SRP_PA_SERVER_CHALLENGE(&sc); 1780 return HEIM_ERR_PA_CANT_CONTINUE; 1781 } 1782 1783 _krb5_debugx(context, 5, "ccsrp client start for user: %s", username); 1784 1785 ret = ccsrp_client_process_challenge(state->srp, username, 1786 state->key.length, 1787 state->key.data, 1788 state->spa.salt.length, 1789 state->spa.salt.data, 1790 sc.data, cr.data); 1791 free_KRB5_SRP_PA_SERVER_CHALLENGE(&sc); 1792 if (ret) { 1793 state->state = KRB5_SRP_STATE_DONE; 1794 free_KRB5_SRP_PA_CLIENT_RESPONSE(&cr); 1795 return HEIM_ERR_PA_CANT_CONTINUE; 1796 } 1797 1798 ASN1_MALLOC_ENCODE(KRB5_SRP_PA_CLIENT_RESPONSE, data.data, data.length, &cr, &size, ret); 1799 free_KRB5_SRP_PA_CLIENT_RESPONSE(&cr); 1800 if (ret) { 1801 state->state = KRB5_SRP_STATE_DONE; 1802 return HEIM_ERR_PA_CANT_CONTINUE; 1803 } 1804 heim_assert(data.length == size, "ASN.1 internal error"); 1805 1806 ret = krb5_padata_add(context, out_md, KRB5_PADATA_SRP, data.data, data.length); 1807 if (ret) { 1808 free(data.data); 1809 state->state = KRB5_SRP_STATE_DONE; 1810 return HEIM_ERR_PA_CANT_CONTINUE; 1811 } 1812 1813 state->state = KRB5_SRP_STATE_SERVER_VERIFIER; 1814 return HEIM_ERR_PA_CONTINUE_NEEDED; 1815 1816 } else if (state->state == KRB5_SRP_STATE_SERVER_VERIFIER) { 1817 KRB5_SRP_PA_SERVER_VERIFIER sv; 1818 krb5_principal client = NULL; 1819 size_t size; 1820 bool boolres; 1821 1822 if (rep == NULL) { 1823 _krb5_debugx(context, 0, "KDC didn't return an AS-REP in last step of verifier"); 1824 state->state = KRB5_SRP_STATE_DONE; 1825 return HEIM_ERR_PA_CANT_CONTINUE; 1826 } 1827 1828 ret = decode_KRB5_SRP_PA_SERVER_VERIFIER(pa->padata_value.data, pa->padata_value.length, &sv, &size); 1829 if (ret) { 1830 state->state = KRB5_SRP_STATE_DONE; 1831 return HEIM_ERR_PA_CANT_CONTINUE; 1832 } 1833 1834 if (sv.length != state->keylength) { 1835 state->state = KRB5_SRP_STATE_DONE; 1836 free_KRB5_SRP_PA_SERVER_VERIFIER(&sv); 1837 return HEIM_ERR_PA_CANT_CONTINUE; 1838 } 1839 1840 boolres = ccsrp_client_verify_session(state->srp, sv.data); 1841 free_KRB5_SRP_PA_SERVER_VERIFIER(&sv); 1842 if (!boolres) { 1843 _krb5_debugx(context, 0, "Failed to validate the KDC"); 1844 state->state = KRB5_SRP_STATE_DONE; 1845 return HEIM_ERR_PA_CANT_CONTINUE; 1846 } 1847 1848 if (ctx->fast_state.reply_key) 1849 krb5_free_keyblock(context, ctx->fast_state.reply_key); 1850 1851 ctx->fast_state.reply_key = calloc(1, sizeof(*ctx->fast_state.reply_key)); 1852 if (ctx->fast_state.reply_key == NULL) { 1853 state->state = KRB5_SRP_STATE_DONE; 1854 return HEIM_ERR_PA_CANT_CONTINUE; 1855 } 1856 1857 ret = _krb5_principalname2krb5_principal(context, &client, 1858 rep->cname, rep->crealm); 1859 if (ret) { 1860 state->state = KRB5_SRP_STATE_DONE; 1861 return HEIM_ERR_PA_CANT_CONTINUE; 1862 } 1863 1864 ret = _krb5_srp_reply_key(context, 1865 state->group, 1866 rep->enc_part.etype, 1867 ccsrp_ctx_K(state->srp), 1868 state->keylength, 1869 client, 1870 &ctx->req_buffer, 1871 &state->announce, 1872 ctx->fast_state.reply_key); 1873 krb5_free_principal(context, client); 1874 if (ret) { 1875 state->state = KRB5_SRP_STATE_DONE; 1876 return HEIM_ERR_PA_CANT_CONTINUE; 1877 } 1878 1879 state->state = KRB5_SRP_STATE_DONE; 1880 1881 return 0; 1882 1883 } else if (state->state == KRB5_SRP_STATE_DONE) { 1884 /* Called too many files */ 1885 return HEIM_ERR_PA_CANT_CONTINUE; 1886 } 1887 1888 krb5_abortx(context, "internal state machine error"); 1889} 1890 1891static void 1892srp_release(void *pa_ctx) 1893{ 1894 srp_state_t state = pa_ctx; 1895 free(state->srp); 1896 krb5_data_free(&state->announce); 1897 krb5_data_free(&state->key); 1898 free_KRB5_SRP_PA(&state->spa); 1899} 1900 1901#endif 1902 1903struct enc_ts_context { 1904 int used_pa_types; 1905#define USED_ENC_TS_GUESS 4 1906#define USED_ENC_TS_INFO 8 1907#define USED_ENC_TS_RENEG 16 1908 krb5_principal user; 1909}; 1910 1911static krb5_error_code 1912enc_ts_restart(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx) 1913{ 1914 struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx; 1915 pactx->used_pa_types = 0; 1916 krb5_free_principal(context, pactx->user); 1917 pactx->user = NULL; 1918 return 0; 1919} 1920 1921static krb5_error_code 1922enc_ts_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, 1923 const AS_REQ *a, 1924 const AS_REP *rep, 1925 const krb5_krbhst_info *hi, 1926 METHOD_DATA *in_md, METHOD_DATA *out_md) 1927{ 1928 struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx; 1929 struct pa_info_data paid, *ppaid; 1930 krb5_error_code ret; 1931 const char *state; 1932 unsigned flag; 1933 1934 /* 1935 * Keep track of the user we used so that we can restart 1936 * authentication when we get referals. 1937 */ 1938 1939 if (pactx->user && !krb5_principal_compare(context, pactx->user, ctx->cred.client)) { 1940 pactx->used_pa_types = 0; 1941 krb5_free_principal(context, pactx->user); 1942 pactx->user = NULL; 1943 } 1944 1945 if (pactx->user == NULL) { 1946 ret = krb5_copy_principal(context, ctx->cred.client, &pactx->user); 1947 if (ret) 1948 return ret; 1949 } 1950 1951 memset(&paid, 0, sizeof(paid)); 1952 1953 if (rep == NULL) 1954 paid.etype = KRB5_ENCTYPE_NULL; 1955 else 1956 paid.etype = rep->enc_part.etype; 1957 1958 ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md); 1959 1960 if (rep) { 1961 /* 1962 * Some KDC's don't send salt info in the reply when there is 1963 * success pre-auth happned before, so use cached copy (or 1964 * even better, if there is just one pre-auth, save reply-key). 1965 */ 1966 if (ppaid == NULL && ctx->paid.etype != KRB5_ENCTYPE_NULL) { 1967 ppaid = &ctx->paid; 1968 1969 } else if (ppaid == NULL) { 1970 _krb5_debugx(context, 0, "no paid when building key, build a default salt structure ?"); 1971 return HEIM_ERR_PA_CANT_CONTINUE; 1972 } 1973 1974 ret = pa_data_to_key_plain(context, ctx->cred.client, ctx, 1975 ppaid->salt, ppaid->s2kparams, rep->enc_part.etype, 1976 &ctx->fast_state.reply_key); 1977 free_paid(context, &paid); 1978 return ret; 1979 } 1980 1981 /* 1982 * If we don't have ppaid, ts because the KDC have not sent any 1983 * salt info, lets to the first roundtrip so the KDC have a chance 1984 * to send any. 1985 * 1986 * Don't bother guessing, it sounds like a good idea until you run 1987 * into KDCs that are doing failed auth counting based on the 1988 * ENC_TS tries. 1989 * 1990 * Stashing the salt for the next run is a diffrent issue and 1991 * could be considered in the future. 1992 */ 1993 1994 if (ppaid == NULL) { 1995 _krb5_debugx(context, 5, 1996 "TS-ENC: waiting for KDC to set pw-salt/etype_info{,2}"); 1997 return HEIM_ERR_PA_CONTINUE_NEEDED; 1998 } 1999 if (ppaid->etype == ETYPE_NULL) { 2000 free_paid(context, &paid); 2001 _krb5_debugx(context, 5, 2002 "TS-ENC: kdc proposes enctype NULL ?"); 2003 return HEIM_ERR_PA_CANT_CONTINUE; 2004 } 2005 2006 /* 2007 * We have to allow the KDC to re-negotiate the PA-TS data 2008 * once, this is since the in the case of a windows read only 2009 * KDC that doesn't have the keys simply guesses what the 2010 * master is supposed to support. In the case where this 2011 * breaks in when the RO-KDC is a newer version the the RW-KDC 2012 * and the RO-KDC announced a enctype that the older doesn't 2013 * support. 2014 */ 2015 if (pactx->used_pa_types & USED_ENC_TS_INFO) { 2016 flag = USED_ENC_TS_RENEG; 2017 state = "reneg"; 2018 } else { 2019 flag = USED_ENC_TS_INFO; 2020 state = "info"; 2021 } 2022 2023 if (pactx->used_pa_types & flag) { 2024 free_paid(context, &paid); 2025 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 2026 "Already tried ENC-TS-%s, looping", state); 2027 return KRB5_GET_IN_TKT_LOOP; 2028 } 2029 2030 pactx->used_pa_types |= flag; 2031 2032 free_paid(context, &ctx->paid); 2033 ctx->paid = *ppaid; 2034 2035 2036 ret = pa_data_to_md_ts_enc(context, a, ctx->cred.client, ctx, ppaid, out_md); 2037 if (ret) 2038 return ret; 2039 2040 return HEIM_ERR_PA_CONTINUE_NEEDED; 2041} 2042 2043static void 2044enc_ts_release(void *pa_ctx) 2045{ 2046 struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx; 2047 2048 if (pactx->user) 2049 krb5_free_principal(NULL, pactx->user); 2050 2051} 2052 2053static krb5_error_code 2054pa_pac_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, 2055 const AS_REP *rep, const krb5_krbhst_info *hi, 2056 METHOD_DATA *in_md, METHOD_DATA *out_md) 2057{ 2058 size_t len = 0, length; 2059 krb5_error_code ret; 2060 PA_PAC_REQUEST req; 2061 void *buf; 2062 2063 switch (ctx->req_pac) { 2064 case KRB5_INIT_CREDS_TRISTATE_UNSET: 2065 return 0; /* don't bother */ 2066 case KRB5_INIT_CREDS_TRISTATE_TRUE: 2067 req.include_pac = 1; 2068 break; 2069 case KRB5_INIT_CREDS_TRISTATE_FALSE: 2070 req.include_pac = 0; 2071 } 2072 2073 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length, 2074 &req, &len, ret); 2075 if (ret) 2076 return ret; 2077 heim_assert(len == length, "internal error in ASN.1 encoder"); 2078 2079 ret = krb5_padata_add(context, out_md, KRB5_PADATA_PA_PAC_REQUEST, buf, len); 2080 if (ret) 2081 free(buf); 2082 2083 return 0; 2084} 2085 2086static krb5_error_code 2087pa_pku2u_name_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, 2088 const AS_REP *rep, const krb5_krbhst_info *hi, 2089 METHOD_DATA *in_md, METHOD_DATA *out_md) 2090{ 2091#ifdef PKINIT 2092 if (ctx->pku2u_assertion) { 2093 krb5_error_code ret; 2094 krb5_data data; 2095 2096 ret = krb5_data_copy(&data, 2097 ctx->pku2u_assertion->data, 2098 ctx->pku2u_assertion->length); 2099 if (ret) 2100 return ret; 2101 ret = krb5_padata_add(context, out_md, KRB5_PADATA_PKU2U_NAME, 2102 data.data, data.length); 2103 if (ret) { 2104 krb5_data_free(&data); 2105 return ret; 2106 } 2107 } 2108#endif 2109 return 0; 2110} 2111 2112static krb5_error_code 2113pa_enc_pa_rep_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, 2114 const AS_REP *rep, const krb5_krbhst_info *hi, 2115 METHOD_DATA *in_md, METHOD_DATA *out_md) 2116{ 2117#ifdef PKINIT 2118 if (ctx->runflags.allow_enc_pa_rep) 2119 return krb5_padata_add(context, out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0); 2120#endif 2121 return 0; 2122} 2123 2124static krb5_error_code 2125pa_fx_cookie_step(krb5_context context, 2126 krb5_init_creds_context ctx, 2127 void *pa_ctx, 2128 PA_DATA *pa, 2129 const AS_REQ *a, 2130 const AS_REP *rep, 2131 const krb5_krbhst_info *hi, 2132 METHOD_DATA *in_md, 2133 METHOD_DATA *out_md) 2134{ 2135 krb5_error_code ret; 2136 void *cookie; 2137 PA_DATA *pad; 2138 int idx = 0; 2139 2140 pad = krb5_find_padata(in_md->val, in_md->len, KRB5_PADATA_FX_COOKIE, &idx); 2141 if (pad == NULL) 2142 return 0; 2143 2144 cookie = malloc(pad->padata_value.length); 2145 if (cookie == NULL) 2146 return ENOMEM; 2147 2148 memcpy(cookie, pad->padata_value.data, pad->padata_value.length); 2149 2150 ret = krb5_padata_add(context, out_md, KRB5_PADATA_FX_COOKIE, cookie, pad->padata_value.length); 2151 if (ret) 2152 free(cookie); 2153 else { 2154 _krb5_debugx(context, 5, "Mirrored FX-COOKIE to KDC"); 2155 } 2156 2157 return ret; 2158} 2159 2160 2161 2162 2163typedef struct pa_info_data *(*pa_salt_info_f)(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, heim_octet_string *); 2164typedef krb5_error_code (*pa_configure_f)(krb5_context, krb5_init_creds_context, void *); 2165typedef krb5_error_code (*pa_restart_f)(krb5_context, krb5_init_creds_context, void *); 2166typedef krb5_error_code (*pa_step_f)(krb5_context, krb5_init_creds_context, void *, PA_DATA *, const AS_REQ *, const AS_REP *, const krb5_krbhst_info *, METHOD_DATA *, METHOD_DATA *); 2167typedef void (*pa_release_f)(void *); 2168 2169struct patype { 2170 int type; 2171 char *name; 2172 int flags; 2173#define PA_F_ANNOUNCE 1 2174#define PA_F_CONFIG 2 2175#define PA_F_FAST 4 /* available inside FAST */ 2176#define PA_F_NOT_FAST 8 /* only available without FAST */ 2177 size_t pa_ctx_size; 2178 pa_salt_info_f salt_info; 2179 /** 2180 * Return 0 if the PA-mechanism is available and optionally set pa_ctx pointer to non-NULL. 2181 */ 2182 pa_configure_f configure; 2183 /** 2184 * Return 0 if the PA-mechanism can be restarted (time skew, referrals, etc) 2185 */ 2186 pa_restart_f restart; 2187 /** 2188 * Return 0 if the when complete, KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED if more steps are require 2189 */ 2190 pa_step_f step; 2191 pa_release_f release; 2192} patypes[] = { 2193 { 2194 KRB5_PADATA_PK_AS_REP, 2195 "PKINIT(IETF)", 2196 PA_F_FAST | PA_F_NOT_FAST, 2197 0, 2198 NULL, 2199 pkinit_configure, 2200 NULL, 2201 pkinit_step, 2202 pkinit_release 2203 }, 2204 { 2205 KRB5_PADATA_PK_AS_REP_19, 2206 "PKINIT(win)", 2207 PA_F_FAST | PA_F_NOT_FAST, 2208 0, 2209 NULL, 2210 pkinit_configure, 2211 NULL, 2212 pkinit_step_win, 2213 pkinit_release 2214 }, 2215#ifdef __APPLE_PRIVATE__ 2216 { 2217 KRB5_PADATA_SRP, 2218 "SRP", 2219 PA_F_FAST | PA_F_NOT_FAST, 2220 sizeof(struct srp_state_data), 2221 NULL, 2222 srp_configure, 2223 NULL, 2224 srp_step, 2225 srp_release 2226 }, 2227#endif 2228 { 2229 KRB5_PADATA_ENCRYPTED_CHALLENGE, 2230 "ENCRYPTED_CHALLENGE", 2231 PA_F_FAST, 2232 0, 2233 NULL, 2234 NULL, 2235 NULL, 2236 enc_chal_step, 2237 NULL 2238 }, 2239 { 2240 KRB5_PADATA_ENC_TIMESTAMP, 2241 "ENCRYPTED_TIMESTAMP", 2242 PA_F_NOT_FAST, 2243 sizeof(struct enc_ts_context), 2244 NULL, 2245 NULL, 2246 enc_ts_restart, 2247 enc_ts_step, 2248 enc_ts_release 2249 }, 2250 { 2251 KRB5_PADATA_PA_PAC_REQUEST, 2252 "PA_PAC_REQUEST", 2253 PA_F_CONFIG, 2254 0, 2255 NULL, 2256 NULL, 2257 NULL, 2258 pa_pac_step, 2259 NULL 2260 }, 2261 { 2262 KRB5_PADATA_PKU2U_NAME, 2263 "PKU2U-NAME", 2264 PA_F_CONFIG, 2265 0, 2266 NULL, 2267 NULL, 2268 NULL, 2269 pa_pku2u_name_step, 2270 NULL 2271 }, 2272 { 2273 KRB5_PADATA_REQ_ENC_PA_REP, 2274 "REQ-ENC-PA-REP", 2275 PA_F_CONFIG, 2276 0, 2277 NULL, 2278 NULL, 2279 NULL, 2280 pa_enc_pa_rep_step, 2281 NULL 2282 }, 2283 { 2284 KRB5_PADATA_FX_COOKIE, 2285 "FX-COOKIE", 2286 PA_F_CONFIG, 2287 0, 2288 NULL, 2289 NULL, 2290 NULL, 2291 pa_fx_cookie_step, 2292 NULL 2293 }, 2294#define patype_salt(n, f) { KRB5_PADATA_##n, #n, 0, 0, f, NULL, NULL, NULL, NULL } 2295 patype_salt(ETYPE_INFO2, pa_etype_info2), 2296 patype_salt(ETYPE_INFO, pa_etype_info), 2297 patype_salt(PW_SALT, pa_pw_or_afs3_salt), 2298 patype_salt(AFS3_SALT, pa_pw_or_afs3_salt), 2299#undef patype_salt 2300 /* below are just for pretty printing */ 2301#define patype_info(n) { KRB5_PADATA_##n, #n, 0, 0, NULL, NULL, NULL, NULL, NULL } 2302 patype_info(AUTHENTICATION_SET), 2303 patype_info(AUTH_SET_SELECTED), 2304 patype_info(FX_FAST), 2305 patype_info(FX_ERROR), 2306 patype_info(PKINIT_KX), 2307 patype_info(PK_AS_REQ) 2308#undef patype_info 2309}; 2310 2311static const char * 2312get_pa_type_name(int type) 2313{ 2314 size_t n; 2315 for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++) 2316 if (type == patypes[n].type) 2317 return patypes[n].name; 2318 return "unknown"; 2319} 2320 2321/* 2322 * 2323 */ 2324 2325struct pa_auth_mech { 2326 struct heim_base_uniq base; 2327 struct patype *patype; 2328 heim_object_t next; /* when doing authentication sets */ 2329 char pactx[1]; 2330}; 2331 2332/* 2333 * 2334 */ 2335 2336static struct pa_info_data * 2337process_pa_info(krb5_context context, 2338 const krb5_principal client, 2339 const AS_REQ *asreq, 2340 struct pa_info_data *paid, 2341 METHOD_DATA *md) 2342{ 2343 struct pa_info_data *p = NULL; 2344 PA_DATA *pa; 2345 size_t i; 2346 2347 if (md == NULL) 2348 return NULL; 2349 2350 for (i = 0; p == NULL && i < sizeof(patypes)/sizeof(patypes[0]); i++) { 2351 int idx = 0; 2352 2353 if (patypes[i].salt_info == NULL) 2354 continue; 2355 2356 pa = krb5_find_padata(md->val, md->len, patypes[i].type, &idx); 2357 if (pa == NULL) 2358 continue; 2359 2360 paid->salt.salttype = (krb5_salttype)patypes[i].type; 2361 p = patypes[i].salt_info(context, client, asreq, paid, &pa->padata_value); 2362 } 2363 return p; 2364} 2365 2366static void 2367pa_announce(krb5_context context, 2368 int types, 2369 krb5_init_creds_context ctx, 2370 METHOD_DATA *in_md, 2371 METHOD_DATA *out_md) 2372{ 2373 size_t n; 2374 2375 for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++) { 2376 if ((patypes[n].flags & types) == 0) 2377 continue; 2378 2379 if (patypes[n].step) 2380 patypes[n].step(context, ctx, NULL, NULL, NULL, NULL, NULL, in_md, out_md); 2381 else 2382 krb5_padata_add(context, out_md, patypes[n].type, NULL, 0); 2383 } 2384} 2385 2386 2387static void 2388mech_release(void *ctx) 2389{ 2390 struct pa_auth_mech *pa_mech = ctx; 2391 if (pa_mech->patype->release) 2392 pa_mech->patype->release((void *)&pa_mech->pactx[0]); 2393} 2394 2395static struct pa_auth_mech * 2396pa_mech_create(krb5_context context, krb5_init_creds_context ctx, int pa_type) 2397{ 2398 struct pa_auth_mech *pa_mech; 2399 struct patype *patype = NULL; 2400 size_t n; 2401 2402 for (n = 0; patype == NULL && n < sizeof(patypes)/sizeof(patypes[0]); n++) { 2403 if (patypes[n].type == pa_type) 2404 patype = &patypes[n]; 2405 } 2406 if (patype == NULL) 2407 return NULL; 2408 2409 pa_mech = heim_uniq_alloc(sizeof(*pa_mech) - 1 + patype->pa_ctx_size, "heim-pa-mech-ctx", mech_release); 2410 if (pa_mech == NULL) 2411 return NULL; 2412 2413 pa_mech->patype = patype; 2414 2415 if (pa_mech->patype->configure) { 2416 krb5_error_code ret; 2417 2418 ret = pa_mech->patype->configure(context, ctx, &pa_mech->pactx[0]); 2419 if (ret) { 2420 heim_release(pa_mech); 2421 return NULL; 2422 } 2423 } 2424 2425 _krb5_debugx(context, 5, "Adding PA mech: %s", patype->name); 2426 2427 return pa_mech; 2428} 2429 2430static void 2431pa_mech_add(krb5_context context, krb5_init_creds_context ctx, int pa_type) 2432{ 2433 struct pa_auth_mech *mech; 2434 2435 mech = pa_mech_create(context, ctx, pa_type); 2436 if (mech) { 2437 heim_array_append_value(ctx->available_pa_mechs, mech); 2438 heim_release(mech); 2439 } 2440} 2441 2442static krb5_error_code 2443pa_configure(krb5_context context, 2444 krb5_init_creds_context ctx, 2445 METHOD_DATA *in_md) 2446{ 2447 ctx->available_pa_mechs = heim_array_create(); 2448 2449 if ((ctx->keyproc || ctx->keyseed || ctx->prompter) && !ctx->pk_init_ctx) { 2450 pa_mech_add(context, ctx, KRB5_PADATA_SRP); 2451 pa_mech_add(context, ctx, KRB5_PADATA_ENCRYPTED_CHALLENGE); 2452 pa_mech_add(context, ctx, KRB5_PADATA_ENC_TIMESTAMP); 2453 } 2454 2455 if (ctx->pk_init_ctx) { 2456 pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP); 2457 pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP_19); 2458 } 2459 2460 /* XXX setup context based on KDC reply */ 2461 2462 return 0; 2463} 2464 2465static krb5_error_code 2466pa_restart(krb5_context context, 2467 krb5_init_creds_context ctx) 2468{ 2469 krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE; 2470 2471 if (ctx->pa_mech && ctx->pa_mech->patype->restart) 2472 ret = ctx->pa_mech->patype->restart(context, ctx, (void *)&ctx->pa_mech->pactx[0]); 2473 2474 return ret; 2475} 2476 2477 2478static krb5_error_code 2479pa_step(krb5_context context, 2480 krb5_init_creds_context ctx, 2481 const AS_REQ *a, 2482 const AS_REP *rep, 2483 const krb5_krbhst_info *hi, 2484 METHOD_DATA *in_md, 2485 METHOD_DATA *out_md) 2486{ 2487 krb5_error_code ret; 2488 PA_DATA *pa = NULL; 2489 int idx; 2490 2491 //heim_assert(in_md != NULL, "pa_step w/o input padata"); 2492 2493 next: 2494 do { 2495 if (ctx->pa_mech == NULL) { 2496 size_t len = heim_array_get_length(ctx->available_pa_mechs); 2497 if (len == 0) { 2498 _krb5_debugx(context, 0, "no more available_pa_mechs to try"); 2499 return HEIM_ERR_NO_MORE_PA_MECHS; 2500 } 2501 2502 ctx->pa_mech = heim_array_copy_value(ctx->available_pa_mechs, 0); 2503 heim_array_delete_value(ctx->available_pa_mechs, 0); 2504 } 2505 2506 if (ctx->fast_state.armor_crypto) { 2507 if ((ctx->pa_mech->patype->flags & PA_F_FAST) == 0) { 2508 _krb5_debugx(context, 0, "pa-mech %s dropped under FAST (not supported)", 2509 ctx->pa_mech->patype->name); 2510 heim_release(ctx->pa_mech); 2511 ctx->pa_mech = NULL; 2512 continue; 2513 } 2514 } else { 2515 if ((ctx->pa_mech->patype->flags & PA_F_NOT_FAST) == 0) { 2516 _krb5_debugx(context, 0, "dropped pa-mech %s since not running under FAST", 2517 ctx->pa_mech->patype->name); 2518 heim_release(ctx->pa_mech); 2519 ctx->pa_mech = NULL; 2520 continue; 2521 } 2522 } 2523 2524 _krb5_debugx(context, 0, "pa-mech trying: %s, searching for %d", 2525 ctx->pa_mech->patype->name, ctx->pa_mech->patype->type); 2526 2527 idx = 0; 2528 if (in_md) 2529 pa = krb5_find_padata(in_md->val, in_md->len, ctx->pa_mech->patype->type, &idx); 2530 else 2531 pa = NULL; 2532 2533 } while (ctx->pa_mech == NULL); 2534 2535 _krb5_debugx(context, 5, "Stepping pa-mech: %s", ctx->pa_mech->patype->name); 2536 2537 ret = ctx->pa_mech->patype->step(context, ctx, (void *)&ctx->pa_mech->pactx[0], pa, a, rep, hi, in_md, out_md); 2538 _krb5_debug(context, 10, ret, "PA type %s returned %d", ctx->pa_mech->patype->name, ret); 2539 if (ret == 0) { 2540 struct pa_auth_mech *next_pa = ctx->pa_mech->next; 2541 2542 if (next_pa) { 2543 _krb5_debugx(context, 5, "Next PA type in set is: %s", 2544 next_pa->patype->name); 2545 ret = HEIM_ERR_PA_CONTINUE_NEEDED; 2546 } else if (rep == NULL) { 2547 _krb5_debugx(context, 5, "PA %s done, but no ticket in sight!!!", 2548 ctx->pa_mech->patype->name); 2549 ret = HEIM_ERR_PA_CANT_CONTINUE; 2550 } 2551 2552 heim_retain(next_pa); 2553 heim_release(ctx->pa_mech); 2554 ctx->pa_mech = next_pa; 2555 } 2556 2557 if (ret == HEIM_ERR_PA_CANT_CONTINUE) { 2558 _krb5_debugx(context, 5, "Dropping PA type %s", ctx->pa_mech->patype->name); 2559 heim_release(ctx->pa_mech); 2560 ctx->pa_mech = NULL; 2561 goto next; 2562 } else if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) { 2563 _krb5_debugx(context, 5, "Continue needed for %s", ctx->pa_mech->patype->name); 2564 } else if (ret != 0) { 2565 _krb5_debugx(context, 5, "Other error from mech %s: %d", ctx->pa_mech->patype->name, ret); 2566 } 2567 2568 return ret; 2569} 2570 2571static void 2572log_kdc_pa_types(krb5_context context, METHOD_DATA *in_md) 2573{ 2574 if (_krb5_have_debug(context, 5)) { 2575 unsigned i; 2576 _krb5_debugx(context, 5, "KDC sent %d patypes", in_md->len); 2577 for (i = 0; i < in_md->len; i++) 2578 _krb5_debugx(context, 5, "KDC sent PA-DATA type: %d (%s)", 2579 in_md->val[i].padata_type, 2580 get_pa_type_name(in_md->val[i].padata_type)); 2581 } 2582} 2583 2584/* 2585 * Assumes caller always will free `out_md', even on error. 2586 */ 2587 2588static krb5_error_code 2589process_pa_data_to_md(krb5_context context, 2590 const krb5_creds *creds, 2591 int first, 2592 const AS_REQ *a, 2593 krb5_init_creds_context ctx, 2594 METHOD_DATA *in_md, 2595 METHOD_DATA **out_md) 2596{ 2597 krb5_error_code ret; 2598 2599 ALLOC(*out_md, 1); 2600 if (*out_md == NULL) { 2601 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 2602 return ENOMEM; 2603 } 2604 (*out_md)->len = 0; 2605 (*out_md)->val = NULL; 2606 2607 log_kdc_pa_types(context, in_md); 2608 2609 if (!first) { 2610 ret = pa_step(context, ctx, a, NULL, NULL, in_md, *out_md); 2611 if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) { 2612 ret = 0; 2613 } else if (ret == 0) { 2614 _krb5_debugx(context, 0, "pamech done step"); 2615 } else { 2616 return ret; 2617 } 2618 } 2619 2620 /* 2621 * Send announcement (what we support) and configuration (user 2622 * introduced behavior change) 2623 */ 2624 2625 pa_announce(context, PA_F_ANNOUNCE|PA_F_CONFIG, ctx, in_md, *out_md); 2626 2627 /* 2628 * 2629 */ 2630 2631 if ((*out_md)->len == 0) { 2632 free(*out_md); 2633 *out_md = NULL; 2634 } 2635 2636 return 0; 2637} 2638 2639static krb5_error_code 2640process_pa_data_to_key(krb5_context context, 2641 krb5_init_creds_context ctx, 2642 krb5_creds *creds, 2643 AS_REQ *a, 2644 AS_REP *rep, 2645 const krb5_krbhst_info *hi, 2646 krb5_keyblock **key) 2647{ 2648 struct pa_info_data paid, *ppaid = NULL; 2649 krb5_error_code ret; 2650 krb5_enctype etype; 2651 2652 memset(&paid, 0, sizeof(paid)); 2653 2654 if (rep->padata) 2655 log_kdc_pa_types(context, rep->padata); 2656 2657 etype = rep->enc_part.etype; 2658 2659 if (rep->padata) { 2660 paid.etype = etype; 2661 ppaid = process_pa_info(context, creds->client, a, &paid, 2662 rep->padata); 2663 } 2664 if (ppaid == NULL ) { 2665 if (ctx->paid.etype == KRB5_ENCTYPE_NULL) { 2666 ctx->paid.etype = etype; 2667 ctx->paid.s2kparams = NULL; 2668 ret = krb5_get_pw_salt (context, creds->client, &ctx->paid.salt); 2669 if (ret) 2670 return ret; 2671 } 2672 ppaid = &ctx->paid; 2673 } 2674 2675 ret = pa_step(context, ctx, a, rep, hi, rep->padata, NULL); 2676 if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) { 2677 _krb5_debugx(context, 0, "In final stretch and pa require more stepping ?"); 2678 return ret; 2679 } else if (ret == 0) { 2680 _krb5_debugx(context, 0, "final pamech done step"); 2681 goto out; 2682 } else { 2683 return ret; 2684 } 2685 out: 2686 free_paid(context, &paid); 2687 return ret; 2688} 2689 2690/* 2691 * 2692 */ 2693 2694static krb5_error_code 2695capture_lkdc_domain(krb5_context context, 2696 krb5_init_creds_context ctx) 2697{ 2698 size_t len; 2699 2700 len = strlen(_krb5_wellknown_lkdc); 2701 2702 if (ctx->kdc_hostname != NULL || 2703 strncmp(ctx->cred.client->realm, _krb5_wellknown_lkdc, len) != 0 || 2704 ctx->cred.client->realm[len] != ':') 2705 return 0; 2706 2707 ctx->kdc_hostname = strdup(&ctx->cred.client->realm[len + 1]); 2708 2709 _krb5_debugx(context, 5, "krb5_get_init_creds: setting LKDC hostname to: %s", 2710 ctx->kdc_hostname); 2711 return 0; 2712} 2713 2714/** 2715 * Start a new context to get a new initial credential. 2716 * 2717 * @param context A Kerberos 5 context. 2718 * @param client The Kerberos principal to get the credential for, if 2719 * NULL is given, the default principal is used as determined by 2720 * krb5_get_default_principal(). 2721 * @param prompter prompter to use if needed 2722 * @param prompter_data data passed to prompter function 2723 * @param start_time the time the ticket should start to be valid or 0 for now. 2724 * @param options a options structure, can be NULL for default options. 2725 * @param rctx A new allocated free with krb5_init_creds_free(). 2726 * 2727 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message(). 2728 * 2729 * @ingroup krb5_credential 2730 */ 2731 2732KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2733krb5_init_creds_init(krb5_context context, 2734 krb5_principal client, 2735 krb5_prompter_fct prompter, 2736 void *prompter_data, 2737 krb5_deltat start_time, 2738 krb5_get_init_creds_opt *options, 2739 krb5_init_creds_context *rctx) 2740{ 2741 krb5_init_creds_context ctx; 2742 krb5_error_code ret; 2743 2744 *rctx = NULL; 2745 2746 ctx = calloc(1, sizeof(*ctx)); 2747 if (ctx == NULL) { 2748 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 2749 return ENOMEM; 2750 } 2751 2752 ret = get_init_creds_common(context, client, start_time, options, ctx); 2753 if (ret) { 2754 free(ctx); 2755 return ret; 2756 } 2757 2758 /* Set a new nonce. */ 2759 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce)); 2760 ctx->nonce &= 0x7fffffff; 2761 /* XXX these just needs to be the same when using Windows PK-INIT */ 2762 ctx->pk_nonce = ctx->nonce; 2763 2764 ctx->prompter = prompter; 2765 ctx->prompter_data = prompter_data; 2766 2767 /* pick up hostname from LKDC realm name */ 2768 ret = capture_lkdc_domain(context, ctx); 2769 if (ret) { 2770 free_init_creds_ctx(context, ctx); 2771 return ret; 2772 } 2773 2774 ctx->runflags.allow_enc_pa_rep = 1; 2775 2776 ctx->fast_state.flags |= KRB5_FAST_AS_REQ; 2777 2778 *rctx = ctx; 2779 2780 return ret; 2781} 2782 2783/** 2784 * The set KDC hostname for the initial request, it will not be 2785 * considered in referrals to another KDC. 2786 * 2787 * @param context a Kerberos 5 context. 2788 * @param ctx a krb5_init_creds_context context. 2789 * @param hostname the hostname for the KDC of realm 2790 * 2791 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 2792 * @ingroup krb5_credential 2793 */ 2794 2795krb5_error_code KRB5_LIB_FUNCTION 2796krb5_init_creds_set_kdc_hostname(krb5_context context, 2797 krb5_init_creds_context ctx, 2798 const char *hostname) 2799{ 2800 if (ctx->kdc_hostname) 2801 free(ctx->kdc_hostname); 2802 ctx->kdc_hostname = strdup(hostname); 2803 if (ctx->kdc_hostname == NULL) 2804 return ENOMEM; 2805 return 0; 2806} 2807 2808/** 2809 * Sets the service that the is requested. This call is only neede for 2810 * special initial tickets, by default the a krbtgt is fetched in the default realm. 2811 * 2812 * @param context a Kerberos 5 context. 2813 * @param ctx a krb5_init_creds_context context. 2814 * @param service the service given as a string, for example 2815 * "kadmind/admin". If NULL, the default krbtgt in the clients 2816 * realm is set. 2817 * 2818 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 2819 * @ingroup krb5_credential 2820 */ 2821 2822KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2823krb5_init_creds_set_service(krb5_context context, 2824 krb5_init_creds_context ctx, 2825 const char *service) 2826{ 2827 krb5_const_realm client_realm; 2828 krb5_principal principal; 2829 krb5_error_code ret; 2830 2831 client_realm = krb5_principal_get_realm (context, ctx->cred.client); 2832 2833 if (service) { 2834 ret = krb5_parse_name (context, service, &principal); 2835 if (ret) 2836 return ret; 2837 krb5_principal_set_realm (context, principal, client_realm); 2838 } else { 2839 ret = krb5_make_principal(context, &principal, 2840 client_realm, KRB5_TGS_NAME, client_realm, 2841 NULL); 2842 if (ret) 2843 return ret; 2844 } 2845 2846 /* 2847 * This is for Windows RODC that are picky about what name type 2848 * the server principal have, and the really strange part is that 2849 * they are picky about the AS-REQ name type and not the TGS-REQ 2850 * later. Oh well. 2851 */ 2852 2853 if (krb5_principal_is_krbtgt(context, principal)) 2854 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST); 2855 2856 krb5_free_principal(context, ctx->cred.server); 2857 ctx->cred.server = principal; 2858 2859 return 0; 2860} 2861 2862/** 2863 * Sets the password that will use for the request. 2864 * 2865 * @param context a Kerberos 5 context. 2866 * @param ctx a krb5_init_creds_context context. 2867 * @param cert client cert 2868 * 2869 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 2870 * @ingroup krb5_credential 2871 */ 2872 2873krb5_error_code KRB5_LIB_FUNCTION 2874krb5_init_creds_set_pkinit_client_cert(krb5_context context, 2875 krb5_init_creds_context ctx, 2876 struct hx509_cert_data *cert) 2877{ 2878#ifdef PKINIT 2879 if (ctx->client_cert) 2880 hx509_cert_free(ctx->client_cert); 2881 ctx->client_cert = hx509_cert_ref(cert); 2882 return 0; 2883#else 2884 return EINVAL; 2885#endif 2886} 2887 2888/** 2889 * Sets the password that will use for the request. 2890 * 2891 * @param context a Kerberos 5 context. 2892 * @param ctx krb5_init_creds_context context. 2893 * @param password the password to use. 2894 * 2895 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 2896 * @ingroup krb5_credential 2897 */ 2898 2899KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2900krb5_init_creds_set_password(krb5_context context, 2901 krb5_init_creds_context ctx, 2902 const char *password) 2903{ 2904 if (ctx->password) { 2905 memset(ctx->password, 0, strlen(ctx->password)); 2906 free(ctx->password); 2907 } 2908 if (password) { 2909 ctx->password = strdup(password); 2910 if (ctx->password == NULL) { 2911 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 2912 return ENOMEM; 2913 } 2914 ctx->keyseed = (void *) ctx->password; 2915 } else { 2916 ctx->keyseed = NULL; 2917 ctx->password = NULL; 2918 } 2919 2920 return 0; 2921} 2922 2923static krb5_error_code KRB5_CALLCONV 2924keytab_key_proc(krb5_context context, krb5_enctype enctype, 2925 krb5_const_pointer keyseed, 2926 krb5_salt salt, krb5_data *s2kparms, 2927 krb5_keyblock **key) 2928{ 2929 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed); 2930 krb5_keytab keytab = args->keytab; 2931 krb5_principal principal = args->principal; 2932 krb5_error_code ret; 2933 krb5_keytab real_keytab; 2934 krb5_keytab_entry entry; 2935 2936 if (keytab == NULL) { 2937 ret = krb5_kt_default(context, &real_keytab); 2938 if (ret) 2939 return ret; 2940 } else 2941 real_keytab = keytab; 2942 2943 ret = krb5_kt_get_entry (context, real_keytab, principal, 2944 0, enctype, &entry); 2945 2946 if (keytab == NULL) 2947 krb5_kt_close (context, real_keytab); 2948 2949 if (ret) 2950 return ret; 2951 2952 ret = krb5_copy_keyblock (context, &entry.keyblock, key); 2953 krb5_kt_free_entry(context, &entry); 2954 return ret; 2955} 2956 2957 2958/** 2959 * Set the keytab to use for authentication. 2960 * 2961 * @param context a Kerberos 5 context. 2962 * @param ctx ctx krb5_init_creds_context context. 2963 * @param keytab the keytab to read the key from. 2964 * 2965 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 2966 * @ingroup krb5_credential 2967 */ 2968 2969KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2970krb5_init_creds_set_keytab(krb5_context context, 2971 krb5_init_creds_context ctx, 2972 krb5_keytab keytab) 2973{ 2974 krb5_keytab_key_proc_args *a; 2975 krb5_keytab_entry entry; 2976 krb5_kt_cursor cursor; 2977 krb5_enctype *etypes = NULL; 2978 krb5_error_code ret; 2979 size_t netypes = 0; 2980 int kvno = 0, found = 0; 2981 2982 a = malloc(sizeof(*a)); 2983 if (a == NULL) { 2984 krb5_set_error_message(context, ENOMEM, 2985 N_("malloc: out of memory", "")); 2986 return ENOMEM; 2987 } 2988 2989 a->principal = ctx->cred.client; 2990 a->keytab = keytab; 2991 2992 ctx->keytab_data = a; 2993 ctx->keyseed = (void *)a; 2994 ctx->keyproc = keytab_key_proc; 2995 2996 /* 2997 * We need to the KDC what enctypes we support for this keytab, 2998 * esp if the keytab is really a password based entry, then the 2999 * KDC might have more enctypes in the database then what we have 3000 * in the keytab. 3001 */ 3002 3003 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 3004 if(ret) 3005 goto out; 3006 3007 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){ 3008 void *ptr; 3009 3010 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client)) 3011 goto next; 3012 3013 found = 1; 3014 3015 /* check if we ahve this kvno already */ 3016 if (entry.vno > kvno) { 3017 /* remove old list of etype */ 3018 if (etypes) 3019 free(etypes); 3020 etypes = NULL; 3021 netypes = 0; 3022 kvno = entry.vno; 3023 } else if (entry.vno != kvno) 3024 goto next; 3025 3026 /* check if enctype is supported */ 3027 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0) 3028 goto next; 3029 3030 /* add enctype to supported list */ 3031 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2)); 3032 if (ptr == NULL) { 3033 free(etypes); 3034 ret = krb5_enomem(context); 3035 goto out; 3036 } 3037 3038 etypes = ptr; 3039 etypes[netypes] = entry.keyblock.keytype; 3040 etypes[netypes + 1] = ETYPE_NULL; 3041 netypes++; 3042 next: 3043 krb5_kt_free_entry(context, &entry); 3044 } 3045 krb5_kt_end_seq_get(context, keytab, &cursor); 3046 3047 if (etypes) { 3048 if (ctx->etypes) 3049 free(ctx->etypes); 3050 ctx->etypes = etypes; 3051 } 3052 3053 out: 3054 if (!found) { 3055 if (ret == 0) 3056 ret = KRB5_KT_NOTFOUND; 3057 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0); 3058 } 3059 3060 return ret; 3061} 3062 3063krb5_error_code KRB5_LIB_FUNCTION 3064_krb5_init_creds_set_pku2u(krb5_context context, 3065 krb5_init_creds_context ctx, 3066 krb5_data *data) 3067{ 3068#ifdef PKINIT 3069 krb5_error_code ret; 3070 3071 ctx->ic_flags |= KRB5_INIT_CREDS_PKU2U; 3072 ctx->flags = int2KDCOptions(0); 3073 3074 if (ctx->pku2u_assertion) 3075 krb5_free_data(context, ctx->pku2u_assertion); 3076 3077 if (data) { 3078 ret = krb5_copy_data(context, data, &ctx->pku2u_assertion); 3079 if (ret) 3080 return ret; 3081 } else { 3082 ctx->pku2u_assertion = NULL; 3083 } 3084 3085 return 0; 3086#else 3087 return EINVAL; 3088#endif 3089} 3090 3091 3092static krb5_error_code KRB5_CALLCONV 3093keyblock_key_proc(krb5_context context, krb5_enctype enctype, 3094 krb5_const_pointer keyseed, 3095 krb5_salt salt, krb5_data *s2kparms, 3096 krb5_keyblock **key) 3097{ 3098 return krb5_copy_keyblock (context, keyseed, key); 3099} 3100 3101KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 3102krb5_init_creds_set_keyblock(krb5_context context, 3103 krb5_init_creds_context ctx, 3104 krb5_keyblock *keyblock) 3105{ 3106 ctx->keyseed = (void *)keyblock; 3107 ctx->keyproc = keyblock_key_proc; 3108 3109 return 0; 3110} 3111 3112KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 3113krb5_init_creds_set_fast_ccache(krb5_context context, 3114 krb5_init_creds_context ctx, 3115 krb5_ccache fast_ccache) 3116{ 3117 krb5_creds *cred = NULL; 3118 krb5_error_code ret; 3119 krb5_data data; 3120 3121 ret = _krb5_get_krbtgt(context, fast_ccache, NULL, &cred); 3122 if (ret) 3123 return ret; 3124 3125 ret = krb5_cc_get_config(context, fast_ccache, cred->server, 3126 "fast_avail", &data); 3127 krb5_free_creds(context, cred); 3128 if (ret == 0) { 3129 ctx->fast_state.armor_ccache = fast_ccache; 3130 ctx->fast_state.flags |= KRB5_FAST_REQUIRED; 3131 } else { 3132 krb5_set_error_message(context, EINVAL, N_("FAST not available for the KDC in the armor ccache", "")); 3133 return EINVAL; 3134 } 3135 return 0; 3136} 3137 3138KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 3139krb5_init_creds_set_fast_ap_armor_service(krb5_context context, 3140 krb5_init_creds_context ctx, 3141 krb5_const_principal armor_service) 3142{ 3143 krb5_error_code ret; 3144 3145 if (ctx->fast_state.armor_service) 3146 krb5_free_principal(context, ctx->fast_state.armor_service); 3147 if (armor_service) { 3148 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service); 3149 if (ret) 3150 return ret; 3151 } else { 3152 ctx->fast_state.armor_service = NULL; 3153 } 3154 ctx->fast_state.flags |= KRB5_FAST_AP_ARMOR_SERVICE; 3155 return 0; 3156} 3157 3158static krb5_error_code 3159validate_pkinit_fx(krb5_context context, 3160 krb5_init_creds_context ctx, 3161 AS_REP *rep, 3162 krb5_keyblock *ticket_sessionkey) 3163{ 3164 krb5_crypto reply_crypto = NULL, kxkey_crypto = NULL; 3165 krb5_keyblock kxkey, sessionkey; 3166 krb5_error_code ret; 3167 EncryptedData ed; 3168 krb5_data data, pepper1, pepper2; 3169 PA_DATA *pa = NULL; 3170 size_t size; 3171 int idx = 0; 3172 3173 krb5_keyblock_zero(&sessionkey); 3174 krb5_keyblock_zero(&kxkey); 3175 3176 if (rep->padata) 3177 pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PKINIT_KX, &idx); 3178 3179 if (pa == NULL) { 3180 if (ctx->flags.request_anonymous && ctx->pk_init_ctx) { 3181 /* XXX handle the case where pkinit is not used */ 3182 krb5_set_error_message(context, KRB5_KDCREP_MODIFIED, N_("Requested anonymous with PKINIT and KDC didn't set PKINIT_KX", "")); 3183 return KRB5_KDCREP_MODIFIED; 3184 } 3185 3186 return 0; 3187 } 3188 3189 heim_assert(ctx->fast_state.reply_key != NULL, "must have a reply key at this stage"); 3190 3191 ret = krb5_crypto_init(context, ctx->fast_state.reply_key, 0, &reply_crypto); 3192 if (ret) 3193 goto out; 3194 3195 ret = decode_EncryptedData(pa->padata_value.data, 3196 pa->padata_value.length, 3197 &ed, &size); 3198 if (ret) 3199 goto out; 3200 3201 ret = krb5_decrypt_EncryptedData(context, 3202 reply_crypto, 3203 KRB5_KU_PA_PKINIT_KX, 3204 &ed, 3205 &data); 3206 free_EncryptedData(&ed); 3207 3208 ret = decode_EncryptionKey(data.data, data.length, &kxkey, &size); 3209 if (ret) 3210 goto out; 3211 3212 ret = krb5_crypto_init(context, &kxkey, 0, &kxkey_crypto); 3213 if (ret) 3214 goto out; 3215 3216 pepper1.data = "PKINIT"; 3217 pepper1.length = strlen(pepper1.data); 3218 3219 /* 3220 * MIT uses KEYEXCHANGE, RFC-6112 say KeyExchange, use what MIT 3221 * uses since that is what we agreed to on <kitten@ietf.org>. 3222 */ 3223 pepper2.data = "KEYEXCHANGE"; 3224 pepper2.length = strlen(pepper2.data); 3225 3226 ret = krb5_crypto_fx_cf2(context, kxkey_crypto, reply_crypto, 3227 &pepper1, &pepper2, kxkey.keytype, 3228 &sessionkey); 3229 if (ret) 3230 goto out; 3231 3232 if (sessionkey.keytype != ticket_sessionkey->keytype || 3233 krb5_data_ct_cmp(&sessionkey.keyvalue, &ticket_sessionkey->keyvalue) != 0) 3234 { 3235 ret = KRB5_KDCREP_MODIFIED; 3236 krb5_set_error_message(context, ret, N_("PKINIT-KX session key doesn't match", "")); 3237 goto out; 3238 } 3239 3240 ctx->ic_flags |= KRB5_INIT_CREDS_PKINIT_KX_VALID; 3241 3242 out: 3243 krb5_free_keyblock_contents(context, &kxkey); 3244 krb5_free_keyblock_contents(context, &sessionkey); 3245 if (reply_crypto) 3246 krb5_crypto_destroy(context, reply_crypto); 3247 if (kxkey_crypto) 3248 krb5_crypto_destroy(context, kxkey_crypto); 3249 3250 return ret; 3251} 3252 3253/** 3254 * The core loop if krb5_get_init_creds() function family. Create the 3255 * packets and have the caller send them off to the KDC. 3256 * 3257 * If the caller want all work been done for them, use 3258 * krb5_init_creds_get() instead. 3259 * 3260 * @param context a Kerberos 5 context. 3261 * @param ctx ctx krb5_init_creds_context context. 3262 * @param in input data from KDC, first round it should be reset by krb5_data_zer(). 3263 * @param out reply to KDC. 3264 * @param hostinfo KDC address info, first round it can be NULL. 3265 * @param realm realm is a pointer the realm to send the packet, it have the 3266 * lifetime the next call to krb5_init_creds_step() or krb5_init_creds_free(). 3267 * @param flags status of the round, if 3268 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round. 3269 * 3270 * @return 0 for success, or an Kerberos 5 error code, see 3271 * krb5_get_error_message(). 3272 * 3273 * @ingroup krb5_credential 3274 */ 3275 3276KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 3277krb5_init_creds_step(krb5_context context, 3278 krb5_init_creds_context ctx, 3279 krb5_data *in, 3280 krb5_data *out, 3281 krb5_krbhst_info *hostinfo, 3282 krb5_realm *realm, 3283 unsigned int *flags) 3284{ 3285 struct timeval start_time, end_time; 3286 krb5_data checksum_data; 3287 krb5_error_code ret; 3288 size_t len = 0; 3289 size_t size; 3290 AS_REQ req2; 3291 int first = 0; 3292 3293 gettimeofday(&start_time, NULL); 3294 3295 krb5_data_zero(out); 3296 krb5_data_zero(&checksum_data); 3297 3298 if (realm) 3299 *realm = NULL; 3300 3301 if (ctx->as_req.req_body.cname == NULL) { 3302 3303#ifdef PKINIT 3304 if (ctx->pk_init_ctx && ctx->client_cert) 3305 _krb5_pk_set_user_id(context, ctx->pk_init_ctx, ctx->client_cert); 3306#endif 3307 3308 ret = init_as_req(context, ctx->flags, &ctx->cred, 3309 ctx->addrs, ctx->etypes, &ctx->as_req); 3310 if (ret) { 3311 free_init_creds_ctx(context, ctx); 3312 return ret; 3313 } 3314 if (ctx->fast_state.flags & KRB5_FAST_REQUIRED) 3315 ; 3316 else if (ctx->fast_state.flags & KRB5_FAST_AP_ARMOR_SERVICE) 3317 /* Check with armor service if there is FAST */; 3318 else 3319 ctx->fast_state.flags |= KRB5_FAST_DISABLED; 3320 3321 3322 /* XXX should happen after we get back reply from KDC */ 3323 pa_configure(context, ctx, NULL); 3324 3325 first = 1; 3326 } 3327 3328#define MAX_PA_COUNTER 10 3329 if (ctx->pa_counter > MAX_PA_COUNTER) { 3330 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 3331 N_("Looping %d times while getting " 3332 "initial credentials", ""), 3333 ctx->pa_counter); 3334 return KRB5_GET_IN_TKT_LOOP; 3335 } 3336 ctx->pa_counter++; 3337 3338 _krb5_debugx(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter); 3339 3340 /* Lets process the input packet */ 3341 if (in && in->length) { 3342 krb5_kdc_rep rep; 3343 3344 memset(&rep, 0, sizeof(rep)); 3345 3346 _krb5_debugx(context, 5, "krb5_get_init_creds: processing input"); 3347 3348 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size); 3349 if (ret == 0) { 3350 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC; 3351 krb5_data data; 3352 3353 /* 3354 * Unwrap AS-REP 3355 */ 3356 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, 3357 &rep.kdc_rep.ticket, &size, ret); 3358 if (ret) 3359 goto out; 3360 heim_assert(data.length == size, "ASN.1 internal error"); 3361 3362 ret = _krb5_fast_unwrap_kdc_rep(context, ctx->nonce, &data, 3363 &ctx->fast_state, &rep.kdc_rep); 3364 krb5_data_free(&data); 3365 if (ret) 3366 goto out; 3367 3368 /* 3369 * Now check and extract the ticket 3370 */ 3371 3372 if (ctx->flags.canonicalize) { 3373 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; 3374 eflags |= EXTRACT_TICKET_MATCH_REALM; 3375 } 3376 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) 3377 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; 3378 3379 ret = process_pa_data_to_key(context, ctx, &ctx->cred, 3380 &ctx->as_req, &rep.kdc_rep, 3381 hostinfo, &ctx->fast_state.reply_key); 3382 if (ret) { 3383 free_AS_REP(&rep.kdc_rep); 3384 goto out; 3385 } 3386 3387 if (ctx->fast_state.strengthen_key) { 3388 krb5_keyblock result; 3389 3390 _krb5_debugx(context, 5, "krb5_get_init_creds: FAST strengthen_key"); 3391 3392 ret = _krb5_fast_cf2(context, 3393 ctx->fast_state.strengthen_key, 3394 "strengthenkey", 3395 ctx->fast_state.reply_key, 3396 "replykey", 3397 &result, 3398 NULL); 3399 if (ret) { 3400 free_AS_REP(&rep.kdc_rep); 3401 goto out; 3402 } 3403 3404 krb5_free_keyblock_contents(context, ctx->fast_state.reply_key); 3405 *ctx->fast_state.reply_key = result; 3406 } 3407 3408 _krb5_debugx(context, 5, "krb5_get_init_creds: extracting ticket"); 3409 3410 ret = _krb5_extract_ticket(context, 3411 &rep, 3412 &ctx->cred, 3413 ctx->fast_state.reply_key, 3414 KRB5_KU_AS_REP_ENC_PART, 3415 NULL, 3416 ctx->nonce, 3417 eflags, 3418 &ctx->req_buffer, 3419 NULL, 3420 NULL); 3421 3422 if (ret == 0) 3423 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part); 3424 if (ret == 0) 3425 ret = validate_pkinit_fx(context, ctx, &rep.kdc_rep, &ctx->cred.session); 3426 3427 krb5_free_keyblock(context, ctx->fast_state.reply_key); 3428 ctx->fast_state.reply_key = NULL; 3429 ctx->ic_flags |= KRB5_INIT_CREDS_DONE; 3430 *flags = 0; 3431 3432 free_AS_REP(&rep.kdc_rep); 3433 free_EncASRepPart(&rep.enc_part); 3434 3435 gettimeofday(&end_time, NULL); 3436 timevalsub(&end_time, &start_time); 3437 timevaladd(&ctx->stats.run_time, &end_time); 3438 3439 _krb5_debugx(context, 1, "krb5_get_init_creds: wc: %ld.%06d", 3440 ctx->stats.run_time.tv_sec, ctx->stats.run_time.tv_usec); 3441 return ret; 3442 3443 } else { 3444 /* let's try to parse it as a KRB-ERROR */ 3445 3446 _krb5_debugx(context, 5, "krb5_get_init_creds: got an KRB-ERROR from KDC"); 3447 3448 free_KRB_ERROR(&ctx->error); 3449 3450 ret = krb5_rd_error(context, in, &ctx->error); 3451 if(ret && in->length && ((char*)in->data)[0] == 4) 3452 ret = KRB5KRB_AP_ERR_V4_REPLY; 3453 if (ret) { 3454 _krb5_debugx(context, 5, "krb5_get_init_creds: failed to read error"); 3455 goto out; 3456 } 3457 3458 /* 3459 * Unwrap method-data, if there is any, 3460 * fast_unwrap_error() below might replace it with a 3461 * wrapped version if we are using FAST. 3462 */ 3463 3464 free_METHOD_DATA(&ctx->md); 3465 memset(&ctx->md, 0, sizeof(ctx->md)); 3466 3467 if (ctx->error.e_data) { 3468 krb5_error_code ret2; 3469 3470 ret2 = decode_METHOD_DATA(ctx->error.e_data->data, 3471 ctx->error.e_data->length, 3472 &ctx->md, 3473 NULL); 3474 if (ret) { 3475 /* 3476 * Just ignore any error, the error will be pushed 3477 * out from krb5_error_from_rd_error() if there 3478 * was one. 3479 */ 3480 _krb5_debug(context, 5, ret, N_("Failed to decode METHOD-DATA", "")); 3481 } 3482 } 3483 3484 /* 3485 * Unwrap KRB-ERROR, we are always calling this so that 3486 * FAST can tell us if your peer KDC suddenly dropped FAST 3487 * wrapping and its really an attacker's packet (or a bug 3488 * in the KDC). 3489 */ 3490 ret = _krb5_fast_unwrap_error(context, &ctx->fast_state, 3491 &ctx->md, &ctx->error); 3492 if (ret) 3493 goto out; 3494 3495 /* 3496 * 3497 */ 3498 3499 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred); 3500 3501 /* log the failure */ 3502 if (_krb5_have_debug(context, 5)) { 3503 const char *str = krb5_get_error_message(context, ret); 3504 _krb5_debugx(context, 5, "krb5_get_init_creds: KRB-ERROR %d/%s", ret, str); 3505 krb5_free_error_message(context, str); 3506 } 3507 3508 /* 3509 * Handle special error codes 3510 */ 3511 3512 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED 3513 || ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED 3514 || ret == KRB5KDC_ERR_ETYPE_NOSUPP) 3515 { 3516 /* 3517 * If no preauth was set and KDC requires it, give it one 3518 * more try. 3519 * 3520 * If the KDC returned KRB5KDC_ERR_ETYPE_NOSUPP, just loop 3521 * one more time since that might mean we are dealing with 3522 * a Windows KDC that is confused about what enctypes are 3523 * available. 3524 */ 3525 3526 if (ctx->md.len == 0) { 3527 krb5_set_error_message(context, ret, 3528 N_("Preauth required but no preauth " 3529 "options send by KDC", "")); 3530 goto out; 3531 } 3532 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) { 3533 /* 3534 * Try adapt to timeskrew when we are using pre-auth, and 3535 * if there was a time skew, try again. 3536 */ 3537 krb5_set_real_time(context, ctx->error.stime, -1); 3538 if (context->kdc_sec_offset) 3539 ret = 0; 3540 3541 _krb5_debugx(context, 10, "init_creds: err skew updateing kdc offset to %d", 3542 context->kdc_sec_offset); 3543 if (ret) 3544 goto out; 3545 3546 pa_restart(context, ctx); 3547 3548 first = 1; 3549 3550 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) { 3551 /* client referal to a new realm */ 3552 char *ref_realm; 3553 3554 if (ctx->error.crealm == NULL) { 3555 krb5_set_error_message(context, ret, 3556 N_("Got a client referral, not but no realm", "")); 3557 goto out; 3558 } 3559 ref_realm = *ctx->error.crealm; 3560 3561 _krb5_debugx(context, 5, "krb5_get_init_creds: referral to realm %s", 3562 ref_realm); 3563 3564 /* 3565 * If its a krbtgt, lets updat the requested krbtgt too 3566 */ 3567 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) { 3568 3569 free(ctx->cred.server->name.name_string.val[1]); 3570 ctx->cred.server->name.name_string.val[1] = strdup(ref_realm); 3571 if (ctx->cred.server->name.name_string.val[1] == NULL) { 3572 ret = ENOMEM; 3573 goto out; 3574 } 3575 3576 free_PrincipalName(ctx->as_req.req_body.sname); 3577 ret = _krb5_principal2principalname(ctx->as_req.req_body.sname, ctx->cred.server); 3578 if (ret) 3579 goto out; 3580 } 3581 3582 free(ctx->as_req.req_body.realm); 3583 ret = copy_Realm(&ref_realm, &ctx->as_req.req_body.realm); 3584 if (ret) 3585 goto out; 3586 3587 ret = krb5_principal_set_realm(context, 3588 ctx->cred.client, 3589 *ctx->error.crealm); 3590 if (ret) 3591 goto out; 3592 3593 ret = krb5_unparse_name(context, ctx->cred.client, &ref_realm); 3594 if (ret == 0) { 3595 _krb5_debugx(context, 5, "krb5_get_init_creds: got referal to %s", ref_realm); 3596 krb5_xfree(ref_realm); 3597 } 3598 3599 pa_restart(context, ctx); 3600 3601 first = 1; 3602 3603 } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) { 3604 char buf2[1024]; 3605 3606 ctx->runflags.change_password = 1; 3607 3608 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL); 3609 3610 3611 /* try to avoid recursion */ 3612 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0) 3613 goto out; 3614 3615 /* don't include prompter in runtime */ 3616 gettimeofday(&end_time, NULL); 3617 timevalsub(&end_time, &start_time); 3618 timevaladd(&ctx->stats.run_time, &end_time); 3619 3620 ret = change_password(context, 3621 ctx->cred.client, 3622 ctx->password, 3623 buf2, 3624 sizeof(buf2), 3625 ctx->prompter, 3626 ctx->prompter_data, 3627 NULL); 3628 if (ret) 3629 goto out; 3630 3631 gettimeofday(&start_time, NULL); 3632 3633 krb5_init_creds_set_password(context, ctx, buf2); 3634 3635 pa_restart(context, ctx); 3636 3637 first = 1; 3638 3639 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) { 3640 3641 /* 3642 * Old MIT KDC can't handle KRB5_PADATA_REQ_ENC_PA_REP, 3643 * so drop it and try again. But only try that for MIT 3644 * Kerberos servers by keying of no METHOD-DATA. 3645 */ 3646 if (ctx->runflags.allow_enc_pa_rep) { 3647 if (ctx->md.len != 0) { 3648 _krb5_debugx(context, 10, "Server send PA data with KRB-ERROR, " 3649 "so not a pre 1.7 MIT KDC and wont retry w/o ENC-PA-REQ"); 3650 goto out; 3651 } 3652 _krb5_debugx(context, 10, "Disable allow_enc_pa_rep and trying again"); 3653 ctx->runflags.allow_enc_pa_rep = 0; 3654 goto retry; 3655 } 3656 3657 if (ctx->fast_state.flags & KRB5_FAST_DISABLED) { 3658 _krb5_debugx(context, 10, "FAST disabled and got preauth failed"); 3659 goto out; 3660 } 3661 3662 if ((ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) == 0) { 3663 _krb5_debugx(context, 10, "Preauth failed"); 3664 goto out; 3665 } 3666 3667 _krb5_debugx(context, 10, "preauth failed with Optimistic FAST, trying w/o FAST"); 3668 3669 ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC; 3670 ctx->fast_state.flags |= KRB5_FAST_DISABLED; 3671 3672 retry: 3673 pa_restart(context, ctx); 3674 3675 first = 1; 3676 3677 } else { 3678 if (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) { 3679 _krb5_debugx(context, 10, 3680 "Some other error %d failed with Optimistic FAST, trying w/o FAST", ret); 3681 3682 ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC; 3683 ctx->fast_state.flags |= KRB5_FAST_DISABLED; 3684 pa_restart(context, ctx); 3685 3686 first = 1; 3687 } else { 3688 /* some other error code from the KDC, lets return it to the user */ 3689 goto out; 3690 } 3691 } 3692 } 3693 } 3694 3695 if (ctx->as_req.padata) { 3696 free_METHOD_DATA(ctx->as_req.padata); 3697 free(ctx->as_req.padata); 3698 ctx->as_req.padata = NULL; 3699 } 3700 3701 ret = _krb5_fast_create_armor(context, &ctx->fast_state, 3702 ctx->cred.client->realm); 3703 if (ret) 3704 goto out; 3705 3706 /* Set a new nonce. */ 3707 ctx->as_req.req_body.nonce = ctx->nonce; 3708 3709 3710 /* 3711 * Step and announce PA-DATA 3712 */ 3713 3714 ret = process_pa_data_to_md(context, &ctx->cred, first, &ctx->as_req, ctx, 3715 &ctx->md, &ctx->as_req.padata); 3716 if (ret) 3717 goto out; 3718 3719 3720 /* 3721 * Wrap with FAST 3722 */ 3723 ret = copy_AS_REQ(&ctx->as_req, &req2); 3724 if (ret) 3725 goto out; 3726 3727 ret = _krb5_fast_wrap_req(context, 3728 &ctx->fast_state, 3729 NULL, 3730 &req2); 3731 3732 krb5_data_free(&checksum_data); 3733 if (ret) { 3734 free_AS_REQ(&req2); 3735 goto out; 3736 } 3737 3738 krb5_data_free(&ctx->req_buffer); 3739 3740 ASN1_MALLOC_ENCODE(AS_REQ, 3741 ctx->req_buffer.data, ctx->req_buffer.length, 3742 &req2, &len, ret); 3743 free_AS_REQ(&req2); 3744 if (ret) 3745 goto out; 3746 if(len != ctx->req_buffer.length) 3747 krb5_abortx(context, "internal error in ASN.1 encoder"); 3748 3749 out->data = ctx->req_buffer.data; 3750 out->length = ctx->req_buffer.length; 3751 3752 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; 3753 3754 if (realm) 3755 *realm = ctx->cred.client->realm; 3756 3757 3758 gettimeofday(&end_time, NULL); 3759 timevalsub(&end_time, &start_time); 3760 timevaladd(&ctx->stats.run_time, &end_time); 3761 3762 return 0; 3763 out: 3764 return ret; 3765} 3766 3767/** 3768 * Extract the newly acquired credentials from krb5_init_creds_context 3769 * context. 3770 * 3771 * @param context A Kerberos 5 context. 3772 * @param ctx ctx krb5_init_creds_context context. 3773 * @param cred credentials, free with krb5_free_cred_contents(). 3774 * 3775 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message(). 3776 */ 3777 3778KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 3779krb5_init_creds_get_creds(krb5_context context, 3780 krb5_init_creds_context ctx, 3781 krb5_creds *cred) 3782{ 3783 return krb5_copy_creds_contents(context, &ctx->cred, cred); 3784} 3785 3786KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL 3787_krb5_init_creds_get_cred_endtime(krb5_context context, krb5_init_creds_context ctx) 3788{ 3789 return ctx->cred.times.endtime; 3790} 3791 3792KRB5_LIB_FUNCTION krb5_principal KRB5_LIB_CALL 3793_krb5_init_creds_get_cred_client(krb5_context context, krb5_init_creds_context ctx) 3794{ 3795 return ctx->cred.client; 3796} 3797 3798 3799 3800/** 3801 * Get the last error from the transaction. 3802 * 3803 * @return Returns 0 or an error code 3804 * 3805 * @ingroup krb5_credential 3806 */ 3807 3808KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 3809krb5_init_creds_get_error(krb5_context context, 3810 krb5_init_creds_context ctx, 3811 KRB_ERROR *error) 3812{ 3813 krb5_error_code ret; 3814 3815 ret = copy_KRB_ERROR(&ctx->error, error); 3816 if (ret) 3817 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 3818 3819 return ret; 3820} 3821 3822/** 3823 * Store config 3824 * 3825 * @param context A Kerberos 5 context. 3826 * @param ctx The krb5_init_creds_context to free. 3827 * @param id store 3828 * 3829 * @return Returns 0 or an error code 3830 * 3831 * @ingroup krb5_credential 3832 */ 3833 3834krb5_error_code KRB5_LIB_FUNCTION 3835krb5_init_creds_store_config(krb5_context context, 3836 krb5_init_creds_context ctx, 3837 krb5_ccache id) 3838{ 3839 krb5_error_code ret; 3840 3841 if (ctx->kdc_hostname) { 3842 krb5_data data; 3843 data.length = strlen(ctx->kdc_hostname); 3844 data.data = ctx->kdc_hostname; 3845 3846 ret = krb5_cc_set_config(context, id, NULL, "lkdc-hostname", &data); 3847 if (ret) 3848 return ret; 3849 } 3850 return 0; 3851} 3852 3853 3854krb5_error_code 3855krb5_init_creds_store(krb5_context context, 3856 krb5_init_creds_context ctx, 3857 krb5_ccache id) 3858{ 3859 krb5_error_code ret; 3860 3861 if (ctx->cred.client == NULL) { 3862 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; 3863 krb5_set_error_message(context, ret, "init creds not completed yet"); 3864 return ret; 3865 } 3866 3867 ret = krb5_cc_initialize(context, id, ctx->cred.client); 3868 if (ret) 3869 return ret; 3870 3871 ret = krb5_cc_store_cred(context, id, &ctx->cred); 3872 if (ret) 3873 return ret; 3874 3875 if (ctx->cred.flags.b.enc_pa_rep) { 3876 krb5_data data = { 3, rk_UNCONST("yes") }; 3877 ret = krb5_cc_set_config(context, id, ctx->cred.server, 3878 "fast_avail", &data); 3879 if (ret) 3880 return ret; 3881 } 3882 3883 return 0; 3884} 3885 3886/** 3887 * Free the krb5_init_creds_context allocated by krb5_init_creds_init(). 3888 * 3889 * @param context A Kerberos 5 context. 3890 * @param ctx The krb5_init_creds_context to free. 3891 * 3892 * @ingroup krb5_credential 3893 */ 3894 3895KRB5_LIB_FUNCTION void KRB5_LIB_CALL 3896krb5_init_creds_free(krb5_context context, 3897 krb5_init_creds_context ctx) 3898{ 3899 free_init_creds_ctx(context, ctx); 3900 free(ctx); 3901} 3902 3903/** 3904 * Get new credentials as setup by the krb5_init_creds_context. 3905 * 3906 * @param context A Kerberos 5 context. 3907 * @param ctx The krb5_init_creds_context to process. 3908 * 3909 * @ingroup krb5_credential 3910 */ 3911 3912KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 3913krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) 3914{ 3915 krb5_sendto_ctx stctx = NULL; 3916 krb5_krbhst_info *hostinfo = NULL; 3917 krb5_error_code ret; 3918 krb5_data in, out; 3919 unsigned int flags = 0; 3920 3921 krb5_data_zero(&in); 3922 krb5_data_zero(&out); 3923 3924 ret = krb5_sendto_ctx_alloc(context, &stctx); 3925 if (ret) 3926 goto out; 3927 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); 3928 3929 if (ctx->kdc_hostname) 3930 krb5_sendto_set_hostname(context, stctx, ctx->kdc_hostname); 3931 3932 while (1) { 3933 krb5_realm realm = NULL; 3934 struct timeval nstart, nend; 3935 3936 flags = 0; 3937 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &realm, &flags); 3938 krb5_data_free(&in); 3939 if (ret) 3940 goto out; 3941 3942 if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0) 3943 break; 3944 3945 gettimeofday(&nstart, NULL); 3946 3947 ret = krb5_sendto_context (context, stctx, &out, realm, &in); 3948 if (ret) 3949 goto out; 3950 3951 gettimeofday(&nend, NULL); 3952 timevalsub(&nend, &nstart); 3953 timevaladd(&ctx->stats.run_time, &nend); 3954 } 3955 3956 out: 3957 if (stctx) 3958 krb5_sendto_ctx_free(context, stctx); 3959 3960 return ret; 3961} 3962 3963/** 3964 * Get new credentials using password. 3965 * 3966 * @ingroup krb5_credential 3967 */ 3968 3969 3970KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 3971krb5_get_init_creds_password(krb5_context context, 3972 krb5_creds *creds, 3973 krb5_principal client, 3974 const char *password, 3975 krb5_prompter_fct prompter, 3976 void *data, 3977 krb5_deltat start_time, 3978 const char *in_tkt_service, 3979 krb5_get_init_creds_opt *options) 3980{ 3981 krb5_init_creds_context ctx; 3982 char buf[BUFSIZ]; 3983 krb5_error_code ret; 3984 int chpw = 0; 3985 3986 again: 3987 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx); 3988 if (ret) 3989 goto out; 3990 3991 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 3992 if (ret) 3993 goto out; 3994 3995 if (prompter != NULL && ctx->password == NULL && password == NULL) { 3996 krb5_prompt prompt; 3997 krb5_data password_data; 3998 char *p, *q; 3999 4000 krb5_unparse_name (context, client, &p); 4001 asprintf (&q, "%s's Password: ", p); 4002 free (p); 4003 prompt.prompt = q; 4004 password_data.data = buf; 4005 password_data.length = sizeof(buf); 4006 prompt.hidden = 1; 4007 prompt.reply = &password_data; 4008 prompt.type = KRB5_PROMPT_TYPE_PASSWORD; 4009 4010 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); 4011 free (q); 4012 if (ret) { 4013 memset (buf, 0, sizeof(buf)); 4014 ret = KRB5_LIBOS_PWDINTR; 4015 krb5_clear_error_message (context); 4016 goto out; 4017 } 4018 password = password_data.data; 4019 } 4020 4021 if (password) { 4022 ret = krb5_init_creds_set_password(context, ctx, password); 4023 if (ret) 4024 goto out; 4025 } 4026 4027 ret = krb5_init_creds_get(context, ctx); 4028 4029 if (ret == 0) 4030 krb5_process_last_request(context, options, ctx); 4031 4032 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) { 4033 char buf2[1024]; 4034 4035 /* try to avoid recursion */ 4036 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0) 4037 goto out; 4038 4039 /* don't try to change password where then where none */ 4040 if (prompter == NULL) 4041 goto out; 4042 4043 ret = change_password (context, 4044 client, 4045 ctx->password, 4046 buf2, 4047 sizeof(buf2), 4048 prompter, 4049 data, 4050 options); 4051 if (ret) 4052 goto out; 4053 chpw = 1; 4054 krb5_init_creds_free(context, ctx); 4055 4056 goto again; 4057 } 4058 4059 out: 4060 if (ret == 0) 4061 krb5_init_creds_get_creds(context, ctx, creds); 4062 4063 if (ctx) 4064 krb5_init_creds_free(context, ctx); 4065 4066 memset(buf, 0, sizeof(buf)); 4067 return ret; 4068} 4069 4070/** 4071 * Get new credentials using keyblock. 4072 * 4073 * @ingroup krb5_credential 4074 */ 4075 4076KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 4077krb5_get_init_creds_keyblock(krb5_context context, 4078 krb5_creds *creds, 4079 krb5_principal client, 4080 krb5_keyblock *keyblock, 4081 krb5_deltat start_time, 4082 const char *in_tkt_service, 4083 krb5_get_init_creds_opt *options) 4084{ 4085 krb5_init_creds_context ctx; 4086 krb5_error_code ret; 4087 4088 memset(creds, 0, sizeof(*creds)); 4089 4090 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 4091 if (ret) 4092 goto out; 4093 4094 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 4095 if (ret) 4096 goto out; 4097 4098 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock); 4099 if (ret) 4100 goto out; 4101 4102 ret = krb5_init_creds_get(context, ctx); 4103 4104 if (ret == 0) 4105 krb5_process_last_request(context, options, ctx); 4106 4107 out: 4108 if (ret == 0) 4109 krb5_init_creds_get_creds(context, ctx, creds); 4110 4111 if (ctx) 4112 krb5_init_creds_free(context, ctx); 4113 4114 return ret; 4115} 4116 4117/** 4118 * Get new credentials using keytab. 4119 * 4120 * @ingroup krb5_credential 4121 */ 4122 4123KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 4124krb5_get_init_creds_keytab(krb5_context context, 4125 krb5_creds *creds, 4126 krb5_principal client, 4127 krb5_keytab keytab, 4128 krb5_deltat start_time, 4129 const char *in_tkt_service, 4130 krb5_get_init_creds_opt *options) 4131{ 4132 krb5_init_creds_context ctx; 4133 krb5_error_code ret; 4134 4135 memset(creds, 0, sizeof(*creds)); 4136 4137 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 4138 if (ret) 4139 goto out; 4140 4141 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 4142 if (ret) 4143 goto out; 4144 4145 ret = krb5_init_creds_set_keytab(context, ctx, keytab); 4146 if (ret) 4147 goto out; 4148 4149 ret = krb5_init_creds_get(context, ctx); 4150 if (ret == 0) 4151 krb5_process_last_request(context, options, ctx); 4152 4153 out: 4154 if (ret == 0) 4155 krb5_init_creds_get_creds(context, ctx, creds); 4156 4157 if (ctx) 4158 krb5_init_creds_free(context, ctx); 4159 4160 return ret; 4161} 4162