1/* $NetBSD: init_creds_pw.c,v 1.1.1.1 2011/04/13 18:15:34 elric Exp $ */ 2 3/* 4 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38#include "krb5_locl.h" 39 40typedef struct krb5_get_init_creds_ctx { 41 KDCOptions flags; 42 krb5_creds cred; 43 krb5_addresses *addrs; 44 krb5_enctype *etypes; 45 krb5_preauthtype *pre_auth_types; 46 char *in_tkt_service; 47 unsigned nonce; 48 unsigned pk_nonce; 49 50 krb5_data req_buffer; 51 AS_REQ as_req; 52 int pa_counter; 53 54 /* password and keytab_data is freed on completion */ 55 char *password; 56 krb5_keytab_key_proc_args *keytab_data; 57 58 krb5_pointer *keyseed; 59 krb5_s2k_proc keyproc; 60 61 krb5_get_init_creds_tristate req_pac; 62 63 krb5_pk_init_ctx pk_init_ctx; 64 int ic_flags; 65 66 int used_pa_types; 67#define USED_PKINIT 1 68#define USED_PKINIT_W2K 2 69#define USED_ENC_TS_GUESS 4 70#define USED_ENC_TS_INFO 8 71 72 METHOD_DATA md; 73 KRB_ERROR error; 74 AS_REP as_rep; 75 EncKDCRepPart enc_part; 76 77 krb5_prompter_fct prompter; 78 void *prompter_data; 79 80 struct pa_info_data *ppaid; 81 82} krb5_get_init_creds_ctx; 83 84 85struct pa_info_data { 86 krb5_enctype etype; 87 krb5_salt salt; 88 krb5_data *s2kparams; 89}; 90 91static void 92free_paid(krb5_context context, struct pa_info_data *ppaid) 93{ 94 krb5_free_salt(context, ppaid->salt); 95 if (ppaid->s2kparams) 96 krb5_free_data(context, ppaid->s2kparams); 97} 98 99static krb5_error_code KRB5_CALLCONV 100default_s2k_func(krb5_context context, krb5_enctype type, 101 krb5_const_pointer keyseed, 102 krb5_salt salt, krb5_data *s2kparms, 103 krb5_keyblock **key) 104{ 105 krb5_error_code ret; 106 krb5_data password; 107 krb5_data opaque; 108 109 _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func"); 110 111 password.data = rk_UNCONST(keyseed); 112 password.length = strlen(keyseed); 113 if (s2kparms) 114 opaque = *s2kparms; 115 else 116 krb5_data_zero(&opaque); 117 118 *key = malloc(sizeof(**key)); 119 if (*key == NULL) 120 return ENOMEM; 121 ret = krb5_string_to_key_data_salt_opaque(context, type, password, 122 salt, opaque, *key); 123 if (ret) { 124 free(*key); 125 *key = NULL; 126 } 127 return ret; 128} 129 130static void 131free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx) 132{ 133 if (ctx->etypes) 134 free(ctx->etypes); 135 if (ctx->pre_auth_types) 136 free (ctx->pre_auth_types); 137 if (ctx->in_tkt_service) 138 free(ctx->in_tkt_service); 139 if (ctx->keytab_data) 140 free(ctx->keytab_data); 141 if (ctx->password) { 142 memset(ctx->password, 0, strlen(ctx->password)); 143 free(ctx->password); 144 } 145 krb5_data_free(&ctx->req_buffer); 146 krb5_free_cred_contents(context, &ctx->cred); 147 free_METHOD_DATA(&ctx->md); 148 free_AS_REP(&ctx->as_rep); 149 free_EncKDCRepPart(&ctx->enc_part); 150 free_KRB_ERROR(&ctx->error); 151 free_AS_REQ(&ctx->as_req); 152 if (ctx->ppaid) { 153 free_paid(context, ctx->ppaid); 154 free(ctx->ppaid); 155 } 156 memset(ctx, 0, sizeof(*ctx)); 157} 158 159static int 160get_config_time (krb5_context context, 161 const char *realm, 162 const char *name, 163 int def) 164{ 165 int ret; 166 167 ret = krb5_config_get_time (context, NULL, 168 "realms", 169 realm, 170 name, 171 NULL); 172 if (ret >= 0) 173 return ret; 174 ret = krb5_config_get_time (context, NULL, 175 "libdefaults", 176 name, 177 NULL); 178 if (ret >= 0) 179 return ret; 180 return def; 181} 182 183static krb5_error_code 184init_cred (krb5_context context, 185 krb5_creds *cred, 186 krb5_principal client, 187 krb5_deltat start_time, 188 krb5_get_init_creds_opt *options) 189{ 190 krb5_error_code ret; 191 int tmp; 192 krb5_timestamp now; 193 194 krb5_timeofday (context, &now); 195 196 memset (cred, 0, sizeof(*cred)); 197 198 if (client) 199 krb5_copy_principal(context, client, &cred->client); 200 else { 201 ret = krb5_get_default_principal (context, 202 &cred->client); 203 if (ret) 204 goto out; 205 } 206 207 if (start_time) 208 cred->times.starttime = now + start_time; 209 210 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE) 211 tmp = options->tkt_life; 212 else 213 tmp = 10 * 60 * 60; 214 cred->times.endtime = now + tmp; 215 216 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) && 217 options->renew_life > 0) { 218 cred->times.renew_till = now + options->renew_life; 219 } 220 221 return 0; 222 223out: 224 krb5_free_cred_contents (context, cred); 225 return ret; 226} 227 228/* 229 * Print a message (str) to the user about the expiration in `lr' 230 */ 231 232static void 233report_expiration (krb5_context context, 234 krb5_prompter_fct prompter, 235 krb5_data *data, 236 const char *str, 237 time_t now) 238{ 239 char *p = NULL; 240 241 if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL) 242 return; 243 (*prompter)(context, data, NULL, p, 0, NULL); 244 free(p); 245} 246 247/* 248 * Check the context, and in the case there is a expiration warning, 249 * use the prompter to print the warning. 250 * 251 * @param context A Kerberos 5 context. 252 * @param options An GIC options structure 253 * @param ctx The krb5_init_creds_context check for expiration. 254 */ 255 256static krb5_error_code 257process_last_request(krb5_context context, 258 krb5_get_init_creds_opt *options, 259 krb5_init_creds_context ctx) 260{ 261 krb5_const_realm realm; 262 LastReq *lr; 263 krb5_boolean reported = FALSE; 264 krb5_timestamp sec; 265 time_t t; 266 size_t i; 267 268 /* 269 * First check if there is a API consumer. 270 */ 271 272 realm = krb5_principal_get_realm (context, ctx->cred.client); 273 lr = &ctx->enc_part.last_req; 274 275 if (options && options->opt_private && options->opt_private->lr.func) { 276 krb5_last_req_entry **lre; 277 278 lre = calloc(lr->len + 1, sizeof(**lre)); 279 if (lre == NULL) { 280 krb5_set_error_message(context, ENOMEM, 281 N_("malloc: out of memory", "")); 282 return ENOMEM; 283 } 284 for (i = 0; i < lr->len; i++) { 285 lre[i] = calloc(1, sizeof(*lre[i])); 286 if (lre[i] == NULL) 287 break; 288 lre[i]->lr_type = lr->val[i].lr_type; 289 lre[i]->value = lr->val[i].lr_value; 290 } 291 292 (*options->opt_private->lr.func)(context, lre, 293 options->opt_private->lr.ctx); 294 295 for (i = 0; i < lr->len; i++) 296 free(lre[i]); 297 free(lre); 298 } 299 300 /* 301 * Now check if we should prompt the user 302 */ 303 304 if (ctx->prompter == NULL) 305 return 0; 306 307 krb5_timeofday (context, &sec); 308 309 t = sec + get_config_time (context, 310 realm, 311 "warn_pwexpire", 312 7 * 24 * 60 * 60); 313 314 for (i = 0; i < lr->len; ++i) { 315 if (lr->val[i].lr_value <= t) { 316 switch (abs(lr->val[i].lr_type)) { 317 case LR_PW_EXPTIME : 318 report_expiration(context, ctx->prompter, 319 ctx->prompter_data, 320 "Your password will expire at ", 321 lr->val[i].lr_value); 322 reported = TRUE; 323 break; 324 case LR_ACCT_EXPTIME : 325 report_expiration(context, ctx->prompter, 326 ctx->prompter_data, 327 "Your account will expire at ", 328 lr->val[i].lr_value); 329 reported = TRUE; 330 break; 331 } 332 } 333 } 334 335 if (!reported 336 && ctx->enc_part.key_expiration 337 && *ctx->enc_part.key_expiration <= t) { 338 report_expiration(context, ctx->prompter, 339 ctx->prompter_data, 340 "Your password/account will expire at ", 341 *ctx->enc_part.key_expiration); 342 } 343 return 0; 344} 345 346static krb5_addresses no_addrs = { 0, NULL }; 347 348static krb5_error_code 349get_init_creds_common(krb5_context context, 350 krb5_principal client, 351 krb5_deltat start_time, 352 krb5_get_init_creds_opt *options, 353 krb5_init_creds_context ctx) 354{ 355 krb5_get_init_creds_opt *default_opt = NULL; 356 krb5_error_code ret; 357 krb5_enctype *etypes; 358 krb5_preauthtype *pre_auth_types; 359 360 memset(ctx, 0, sizeof(*ctx)); 361 362 if (options == NULL) { 363 const char *realm = krb5_principal_get_realm(context, client); 364 365 krb5_get_init_creds_opt_alloc (context, &default_opt); 366 options = default_opt; 367 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options); 368 } 369 370 if (options->opt_private) { 371 if (options->opt_private->password) { 372 ret = krb5_init_creds_set_password(context, ctx, 373 options->opt_private->password); 374 if (ret) 375 goto out; 376 } 377 378 ctx->keyproc = options->opt_private->key_proc; 379 ctx->req_pac = options->opt_private->req_pac; 380 ctx->pk_init_ctx = options->opt_private->pk_init_ctx; 381 ctx->ic_flags = options->opt_private->flags; 382 } else 383 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET; 384 385 if (ctx->keyproc == NULL) 386 ctx->keyproc = default_s2k_func; 387 388 /* Enterprise name implicitly turns on canonicalize */ 389 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) || 390 krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL) 391 ctx->flags.canonicalize = 1; 392 393 ctx->pre_auth_types = NULL; 394 ctx->addrs = NULL; 395 ctx->etypes = NULL; 396 ctx->pre_auth_types = NULL; 397 398 ret = init_cred(context, &ctx->cred, client, start_time, options); 399 if (ret) { 400 if (default_opt) 401 krb5_get_init_creds_opt_free(context, default_opt); 402 return ret; 403 } 404 405 ret = krb5_init_creds_set_service(context, ctx, NULL); 406 if (ret) 407 goto out; 408 409 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) 410 ctx->flags.forwardable = options->forwardable; 411 412 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) 413 ctx->flags.proxiable = options->proxiable; 414 415 if (start_time) 416 ctx->flags.postdated = 1; 417 if (ctx->cred.times.renew_till) 418 ctx->flags.renewable = 1; 419 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) { 420 ctx->addrs = options->address_list; 421 } else if (options->opt_private) { 422 switch (options->opt_private->addressless) { 423 case KRB5_INIT_CREDS_TRISTATE_UNSET: 424#if KRB5_ADDRESSLESS_DEFAULT == TRUE 425 ctx->addrs = &no_addrs; 426#else 427 ctx->addrs = NULL; 428#endif 429 break; 430 case KRB5_INIT_CREDS_TRISTATE_FALSE: 431 ctx->addrs = NULL; 432 break; 433 case KRB5_INIT_CREDS_TRISTATE_TRUE: 434 ctx->addrs = &no_addrs; 435 break; 436 } 437 } 438 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { 439 if (ctx->etypes) 440 free(ctx->etypes); 441 442 etypes = malloc((options->etype_list_length + 1) 443 * sizeof(krb5_enctype)); 444 if (etypes == NULL) { 445 ret = ENOMEM; 446 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 447 goto out; 448 } 449 memcpy (etypes, options->etype_list, 450 options->etype_list_length * sizeof(krb5_enctype)); 451 etypes[options->etype_list_length] = ETYPE_NULL; 452 ctx->etypes = etypes; 453 } 454 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { 455 pre_auth_types = malloc((options->preauth_list_length + 1) 456 * sizeof(krb5_preauthtype)); 457 if (pre_auth_types == NULL) { 458 ret = ENOMEM; 459 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 460 goto out; 461 } 462 memcpy (pre_auth_types, options->preauth_list, 463 options->preauth_list_length * sizeof(krb5_preauthtype)); 464 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE; 465 ctx->pre_auth_types = pre_auth_types; 466 } 467 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) 468 ctx->flags.request_anonymous = options->anonymous; 469 if (default_opt) 470 krb5_get_init_creds_opt_free(context, default_opt); 471 return 0; 472 out: 473 if (default_opt) 474 krb5_get_init_creds_opt_free(context, default_opt); 475 return ret; 476} 477 478static krb5_error_code 479change_password (krb5_context context, 480 krb5_principal client, 481 const char *password, 482 char *newpw, 483 size_t newpw_sz, 484 krb5_prompter_fct prompter, 485 void *data, 486 krb5_get_init_creds_opt *old_options) 487{ 488 krb5_prompt prompts[2]; 489 krb5_error_code ret; 490 krb5_creds cpw_cred; 491 char buf1[BUFSIZ], buf2[BUFSIZ]; 492 krb5_data password_data[2]; 493 int result_code; 494 krb5_data result_code_string; 495 krb5_data result_string; 496 char *p; 497 krb5_get_init_creds_opt *options; 498 499 memset (&cpw_cred, 0, sizeof(cpw_cred)); 500 501 ret = krb5_get_init_creds_opt_alloc(context, &options); 502 if (ret) 503 return ret; 504 krb5_get_init_creds_opt_set_tkt_life (options, 60); 505 krb5_get_init_creds_opt_set_forwardable (options, FALSE); 506 krb5_get_init_creds_opt_set_proxiable (options, FALSE); 507 if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) 508 krb5_get_init_creds_opt_set_preauth_list (options, 509 old_options->preauth_list, 510 old_options->preauth_list_length); 511 512 krb5_data_zero (&result_code_string); 513 krb5_data_zero (&result_string); 514 515 ret = krb5_get_init_creds_password (context, 516 &cpw_cred, 517 client, 518 password, 519 prompter, 520 data, 521 0, 522 "kadmin/changepw", 523 options); 524 krb5_get_init_creds_opt_free(context, options); 525 if (ret) 526 goto out; 527 528 for(;;) { 529 password_data[0].data = buf1; 530 password_data[0].length = sizeof(buf1); 531 532 prompts[0].hidden = 1; 533 prompts[0].prompt = "New password: "; 534 prompts[0].reply = &password_data[0]; 535 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD; 536 537 password_data[1].data = buf2; 538 password_data[1].length = sizeof(buf2); 539 540 prompts[1].hidden = 1; 541 prompts[1].prompt = "Repeat new password: "; 542 prompts[1].reply = &password_data[1]; 543 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN; 544 545 ret = (*prompter) (context, data, NULL, "Changing password", 546 2, prompts); 547 if (ret) { 548 memset (buf1, 0, sizeof(buf1)); 549 memset (buf2, 0, sizeof(buf2)); 550 goto out; 551 } 552 553 if (strcmp (buf1, buf2) == 0) 554 break; 555 memset (buf1, 0, sizeof(buf1)); 556 memset (buf2, 0, sizeof(buf2)); 557 } 558 559 ret = krb5_set_password (context, 560 &cpw_cred, 561 buf1, 562 client, 563 &result_code, 564 &result_code_string, 565 &result_string); 566 if (ret) 567 goto out; 568 if (asprintf(&p, "%s: %.*s\n", 569 result_code ? "Error" : "Success", 570 (int)result_string.length, 571 result_string.length > 0 ? (char*)result_string.data : "") < 0) 572 { 573 ret = ENOMEM; 574 goto out; 575 } 576 577 /* return the result */ 578 (*prompter) (context, data, NULL, p, 0, NULL); 579 580 free (p); 581 if (result_code == 0) { 582 strlcpy (newpw, buf1, newpw_sz); 583 ret = 0; 584 } else { 585 ret = ENOTTY; 586 krb5_set_error_message(context, ret, 587 N_("failed changing password", "")); 588 } 589 590out: 591 memset (buf1, 0, sizeof(buf1)); 592 memset (buf2, 0, sizeof(buf2)); 593 krb5_data_free (&result_string); 594 krb5_data_free (&result_code_string); 595 krb5_free_cred_contents (context, &cpw_cred); 596 return ret; 597} 598 599 600KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 601krb5_keyblock_key_proc (krb5_context context, 602 krb5_keytype type, 603 krb5_data *salt, 604 krb5_const_pointer keyseed, 605 krb5_keyblock **key) 606{ 607 return krb5_copy_keyblock (context, keyseed, key); 608} 609 610/* 611 * 612 */ 613 614static krb5_error_code 615init_as_req (krb5_context context, 616 KDCOptions opts, 617 const krb5_creds *creds, 618 const krb5_addresses *addrs, 619 const krb5_enctype *etypes, 620 AS_REQ *a) 621{ 622 krb5_error_code ret; 623 624 memset(a, 0, sizeof(*a)); 625 626 a->pvno = 5; 627 a->msg_type = krb_as_req; 628 a->req_body.kdc_options = opts; 629 a->req_body.cname = malloc(sizeof(*a->req_body.cname)); 630 if (a->req_body.cname == NULL) { 631 ret = ENOMEM; 632 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 633 goto fail; 634 } 635 a->req_body.sname = malloc(sizeof(*a->req_body.sname)); 636 if (a->req_body.sname == NULL) { 637 ret = ENOMEM; 638 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 639 goto fail; 640 } 641 642 ret = _krb5_principal2principalname (a->req_body.cname, creds->client); 643 if (ret) 644 goto fail; 645 ret = copy_Realm(&creds->client->realm, &a->req_body.realm); 646 if (ret) 647 goto fail; 648 649 ret = _krb5_principal2principalname (a->req_body.sname, creds->server); 650 if (ret) 651 goto fail; 652 653 if(creds->times.starttime) { 654 a->req_body.from = malloc(sizeof(*a->req_body.from)); 655 if (a->req_body.from == NULL) { 656 ret = ENOMEM; 657 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 658 goto fail; 659 } 660 *a->req_body.from = creds->times.starttime; 661 } 662 if(creds->times.endtime){ 663 ALLOC(a->req_body.till, 1); 664 *a->req_body.till = creds->times.endtime; 665 } 666 if(creds->times.renew_till){ 667 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime)); 668 if (a->req_body.rtime == NULL) { 669 ret = ENOMEM; 670 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 671 goto fail; 672 } 673 *a->req_body.rtime = creds->times.renew_till; 674 } 675 a->req_body.nonce = 0; 676 ret = krb5_init_etype (context, 677 &a->req_body.etype.len, 678 &a->req_body.etype.val, 679 etypes); 680 if (ret) 681 goto fail; 682 683 /* 684 * This means no addresses 685 */ 686 687 if (addrs && addrs->len == 0) { 688 a->req_body.addresses = NULL; 689 } else { 690 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses)); 691 if (a->req_body.addresses == NULL) { 692 ret = ENOMEM; 693 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 694 goto fail; 695 } 696 697 if (addrs) 698 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses); 699 else { 700 ret = krb5_get_all_client_addrs (context, a->req_body.addresses); 701 if(ret == 0 && a->req_body.addresses->len == 0) { 702 free(a->req_body.addresses); 703 a->req_body.addresses = NULL; 704 } 705 } 706 if (ret) 707 goto fail; 708 } 709 710 a->req_body.enc_authorization_data = NULL; 711 a->req_body.additional_tickets = NULL; 712 713 a->padata = NULL; 714 715 return 0; 716 fail: 717 free_AS_REQ(a); 718 memset(a, 0, sizeof(*a)); 719 return ret; 720} 721 722 723static krb5_error_code 724set_paid(struct pa_info_data *paid, krb5_context context, 725 krb5_enctype etype, 726 krb5_salttype salttype, void *salt_string, size_t salt_len, 727 krb5_data *s2kparams) 728{ 729 paid->etype = etype; 730 paid->salt.salttype = salttype; 731 paid->salt.saltvalue.data = malloc(salt_len + 1); 732 if (paid->salt.saltvalue.data == NULL) { 733 krb5_clear_error_message(context); 734 return ENOMEM; 735 } 736 memcpy(paid->salt.saltvalue.data, salt_string, salt_len); 737 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0'; 738 paid->salt.saltvalue.length = salt_len; 739 if (s2kparams) { 740 krb5_error_code ret; 741 742 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams); 743 if (ret) { 744 krb5_clear_error_message(context); 745 krb5_free_salt(context, paid->salt); 746 return ret; 747 } 748 } else 749 paid->s2kparams = NULL; 750 751 return 0; 752} 753 754static struct pa_info_data * 755pa_etype_info2(krb5_context context, 756 const krb5_principal client, 757 const AS_REQ *asreq, 758 struct pa_info_data *paid, 759 heim_octet_string *data) 760{ 761 krb5_error_code ret; 762 ETYPE_INFO2 e; 763 size_t sz; 764 int i, j; 765 766 memset(&e, 0, sizeof(e)); 767 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz); 768 if (ret) 769 goto out; 770 if (e.len == 0) 771 goto out; 772 for (j = 0; j < asreq->req_body.etype.len; j++) { 773 for (i = 0; i < e.len; i++) { 774 if (asreq->req_body.etype.val[j] == e.val[i].etype) { 775 krb5_salt salt; 776 if (e.val[i].salt == NULL) 777 ret = krb5_get_pw_salt(context, client, &salt); 778 else { 779 salt.saltvalue.data = *e.val[i].salt; 780 salt.saltvalue.length = strlen(*e.val[i].salt); 781 ret = 0; 782 } 783 if (ret == 0) 784 ret = set_paid(paid, context, e.val[i].etype, 785 KRB5_PW_SALT, 786 salt.saltvalue.data, 787 salt.saltvalue.length, 788 e.val[i].s2kparams); 789 if (e.val[i].salt == NULL) 790 krb5_free_salt(context, salt); 791 if (ret == 0) { 792 free_ETYPE_INFO2(&e); 793 return paid; 794 } 795 } 796 } 797 } 798 out: 799 free_ETYPE_INFO2(&e); 800 return NULL; 801} 802 803static struct pa_info_data * 804pa_etype_info(krb5_context context, 805 const krb5_principal client, 806 const AS_REQ *asreq, 807 struct pa_info_data *paid, 808 heim_octet_string *data) 809{ 810 krb5_error_code ret; 811 ETYPE_INFO e; 812 size_t sz; 813 int i, j; 814 815 memset(&e, 0, sizeof(e)); 816 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz); 817 if (ret) 818 goto out; 819 if (e.len == 0) 820 goto out; 821 for (j = 0; j < asreq->req_body.etype.len; j++) { 822 for (i = 0; i < e.len; i++) { 823 if (asreq->req_body.etype.val[j] == e.val[i].etype) { 824 krb5_salt salt; 825 salt.salttype = KRB5_PW_SALT; 826 if (e.val[i].salt == NULL) 827 ret = krb5_get_pw_salt(context, client, &salt); 828 else { 829 salt.saltvalue = *e.val[i].salt; 830 ret = 0; 831 } 832 if (e.val[i].salttype) 833 salt.salttype = *e.val[i].salttype; 834 if (ret == 0) { 835 ret = set_paid(paid, context, e.val[i].etype, 836 salt.salttype, 837 salt.saltvalue.data, 838 salt.saltvalue.length, 839 NULL); 840 if (e.val[i].salt == NULL) 841 krb5_free_salt(context, salt); 842 } 843 if (ret == 0) { 844 free_ETYPE_INFO(&e); 845 return paid; 846 } 847 } 848 } 849 } 850 out: 851 free_ETYPE_INFO(&e); 852 return NULL; 853} 854 855static struct pa_info_data * 856pa_pw_or_afs3_salt(krb5_context context, 857 const krb5_principal client, 858 const AS_REQ *asreq, 859 struct pa_info_data *paid, 860 heim_octet_string *data) 861{ 862 krb5_error_code ret; 863 if (paid->etype == ENCTYPE_NULL) 864 return NULL; 865 ret = set_paid(paid, context, 866 paid->etype, 867 paid->salt.salttype, 868 data->data, 869 data->length, 870 NULL); 871 if (ret) 872 return NULL; 873 return paid; 874} 875 876 877struct pa_info { 878 krb5_preauthtype type; 879 struct pa_info_data *(*salt_info)(krb5_context, 880 const krb5_principal, 881 const AS_REQ *, 882 struct pa_info_data *, 883 heim_octet_string *); 884}; 885 886static struct pa_info pa_prefs[] = { 887 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 }, 888 { KRB5_PADATA_ETYPE_INFO, pa_etype_info }, 889 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt }, 890 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt } 891}; 892 893static PA_DATA * 894find_pa_data(const METHOD_DATA *md, int type) 895{ 896 int i; 897 if (md == NULL) 898 return NULL; 899 for (i = 0; i < md->len; i++) 900 if (md->val[i].padata_type == type) 901 return &md->val[i]; 902 return NULL; 903} 904 905static struct pa_info_data * 906process_pa_info(krb5_context context, 907 const krb5_principal client, 908 const AS_REQ *asreq, 909 struct pa_info_data *paid, 910 METHOD_DATA *md) 911{ 912 struct pa_info_data *p = NULL; 913 int i; 914 915 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) { 916 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type); 917 if (pa == NULL) 918 continue; 919 paid->salt.salttype = pa_prefs[i].type; 920 p = (*pa_prefs[i].salt_info)(context, client, asreq, 921 paid, &pa->padata_value); 922 } 923 return p; 924} 925 926static krb5_error_code 927make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md, 928 krb5_enctype etype, krb5_keyblock *key) 929{ 930 PA_ENC_TS_ENC p; 931 unsigned char *buf; 932 size_t buf_size; 933 size_t len; 934 EncryptedData encdata; 935 krb5_error_code ret; 936 int32_t usec; 937 int usec2; 938 krb5_crypto crypto; 939 940 krb5_us_timeofday (context, &p.patimestamp, &usec); 941 usec2 = usec; 942 p.pausec = &usec2; 943 944 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); 945 if (ret) 946 return ret; 947 if(buf_size != len) 948 krb5_abortx(context, "internal error in ASN.1 encoder"); 949 950 ret = krb5_crypto_init(context, key, 0, &crypto); 951 if (ret) { 952 free(buf); 953 return ret; 954 } 955 ret = krb5_encrypt_EncryptedData(context, 956 crypto, 957 KRB5_KU_PA_ENC_TIMESTAMP, 958 buf, 959 len, 960 0, 961 &encdata); 962 free(buf); 963 krb5_crypto_destroy(context, crypto); 964 if (ret) 965 return ret; 966 967 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); 968 free_EncryptedData(&encdata); 969 if (ret) 970 return ret; 971 if(buf_size != len) 972 krb5_abortx(context, "internal error in ASN.1 encoder"); 973 974 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len); 975 if (ret) 976 free(buf); 977 return ret; 978} 979 980static krb5_error_code 981add_enc_ts_padata(krb5_context context, 982 METHOD_DATA *md, 983 krb5_principal client, 984 krb5_s2k_proc keyproc, 985 krb5_const_pointer keyseed, 986 krb5_enctype *enctypes, 987 unsigned netypes, 988 krb5_salt *salt, 989 krb5_data *s2kparams) 990{ 991 krb5_error_code ret; 992 krb5_salt salt2; 993 krb5_enctype *ep; 994 int i; 995 996 if(salt == NULL) { 997 /* default to standard salt */ 998 ret = krb5_get_pw_salt (context, client, &salt2); 999 if (ret) 1000 return ret; 1001 salt = &salt2; 1002 } 1003 if (!enctypes) { 1004 enctypes = context->etypes; 1005 netypes = 0; 1006 for (ep = enctypes; *ep != ETYPE_NULL; ep++) 1007 netypes++; 1008 } 1009 1010 for (i = 0; i < netypes; ++i) { 1011 krb5_keyblock *key; 1012 1013 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]); 1014 1015 ret = (*keyproc)(context, enctypes[i], keyseed, 1016 *salt, s2kparams, &key); 1017 if (ret) 1018 continue; 1019 ret = make_pa_enc_timestamp (context, md, enctypes[i], key); 1020 krb5_free_keyblock (context, key); 1021 if (ret) 1022 return ret; 1023 } 1024 if(salt == &salt2) 1025 krb5_free_salt(context, salt2); 1026 return 0; 1027} 1028 1029static krb5_error_code 1030pa_data_to_md_ts_enc(krb5_context context, 1031 const AS_REQ *a, 1032 const krb5_principal client, 1033 krb5_get_init_creds_ctx *ctx, 1034 struct pa_info_data *ppaid, 1035 METHOD_DATA *md) 1036{ 1037 if (ctx->keyproc == NULL || ctx->keyseed == NULL) 1038 return 0; 1039 1040 if (ppaid) { 1041 add_enc_ts_padata(context, md, client, 1042 ctx->keyproc, ctx->keyseed, 1043 &ppaid->etype, 1, 1044 &ppaid->salt, ppaid->s2kparams); 1045 } else { 1046 krb5_salt salt; 1047 1048 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt"); 1049 1050 /* make a v5 salted pa-data */ 1051 add_enc_ts_padata(context, md, client, 1052 ctx->keyproc, ctx->keyseed, 1053 a->req_body.etype.val, a->req_body.etype.len, 1054 NULL, NULL); 1055 1056 /* make a v4 salted pa-data */ 1057 salt.salttype = KRB5_PW_SALT; 1058 krb5_data_zero(&salt.saltvalue); 1059 add_enc_ts_padata(context, md, client, 1060 ctx->keyproc, ctx->keyseed, 1061 a->req_body.etype.val, a->req_body.etype.len, 1062 &salt, NULL); 1063 } 1064 return 0; 1065} 1066 1067static krb5_error_code 1068pa_data_to_key_plain(krb5_context context, 1069 const krb5_principal client, 1070 krb5_get_init_creds_ctx *ctx, 1071 krb5_salt salt, 1072 krb5_data *s2kparams, 1073 krb5_enctype etype, 1074 krb5_keyblock **key) 1075{ 1076 krb5_error_code ret; 1077 1078 ret = (*ctx->keyproc)(context, etype, ctx->keyseed, 1079 salt, s2kparams, key); 1080 return ret; 1081} 1082 1083 1084static krb5_error_code 1085pa_data_to_md_pkinit(krb5_context context, 1086 const AS_REQ *a, 1087 const krb5_principal client, 1088 int win2k, 1089 krb5_get_init_creds_ctx *ctx, 1090 METHOD_DATA *md) 1091{ 1092 if (ctx->pk_init_ctx == NULL) 1093 return 0; 1094#ifdef PKINIT 1095 return _krb5_pk_mk_padata(context, 1096 ctx->pk_init_ctx, 1097 ctx->ic_flags, 1098 win2k, 1099 &a->req_body, 1100 ctx->pk_nonce, 1101 md); 1102#else 1103 krb5_set_error_message(context, EINVAL, 1104 N_("no support for PKINIT compiled in", "")); 1105 return EINVAL; 1106#endif 1107} 1108 1109static krb5_error_code 1110pa_data_add_pac_request(krb5_context context, 1111 krb5_get_init_creds_ctx *ctx, 1112 METHOD_DATA *md) 1113{ 1114 size_t len, length; 1115 krb5_error_code ret; 1116 PA_PAC_REQUEST req; 1117 void *buf; 1118 1119 switch (ctx->req_pac) { 1120 case KRB5_INIT_CREDS_TRISTATE_UNSET: 1121 return 0; /* don't bother */ 1122 case KRB5_INIT_CREDS_TRISTATE_TRUE: 1123 req.include_pac = 1; 1124 break; 1125 case KRB5_INIT_CREDS_TRISTATE_FALSE: 1126 req.include_pac = 0; 1127 } 1128 1129 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length, 1130 &req, &len, ret); 1131 if (ret) 1132 return ret; 1133 if(len != length) 1134 krb5_abortx(context, "internal error in ASN.1 encoder"); 1135 1136 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len); 1137 if (ret) 1138 free(buf); 1139 1140 return 0; 1141} 1142 1143/* 1144 * Assumes caller always will free `out_md', even on error. 1145 */ 1146 1147static krb5_error_code 1148process_pa_data_to_md(krb5_context context, 1149 const krb5_creds *creds, 1150 const AS_REQ *a, 1151 krb5_get_init_creds_ctx *ctx, 1152 METHOD_DATA *in_md, 1153 METHOD_DATA **out_md, 1154 krb5_prompter_fct prompter, 1155 void *prompter_data) 1156{ 1157 krb5_error_code ret; 1158 1159 ALLOC(*out_md, 1); 1160 if (*out_md == NULL) { 1161 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 1162 return ENOMEM; 1163 } 1164 (*out_md)->len = 0; 1165 (*out_md)->val = NULL; 1166 1167 if (_krb5_have_debug(context, 5)) { 1168 unsigned i; 1169 _krb5_debug(context, 5, "KDC send %d patypes", in_md->len); 1170 for (i = 0; i < in_md->len; i++) 1171 _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type); 1172 } 1173 1174 /* 1175 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no 1176 * need to expose our password protecting our PKCS12 key. 1177 */ 1178 1179 if (ctx->pk_init_ctx) { 1180 1181 _krb5_debug(context, 5, "krb5_get_init_creds: " 1182 "prepareing PKINIT padata (%s)", 1183 (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf"); 1184 1185 if (ctx->used_pa_types & USED_PKINIT_W2K) { 1186 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1187 "Already tried pkinit, looping"); 1188 return KRB5_GET_IN_TKT_LOOP; 1189 } 1190 1191 ret = pa_data_to_md_pkinit(context, a, creds->client, 1192 (ctx->used_pa_types & USED_PKINIT), 1193 ctx, *out_md); 1194 if (ret) 1195 return ret; 1196 1197 if (ctx->used_pa_types & USED_PKINIT) 1198 ctx->used_pa_types |= USED_PKINIT_W2K; 1199 else 1200 ctx->used_pa_types |= USED_PKINIT; 1201 1202 } else if (in_md->len != 0) { 1203 struct pa_info_data *paid, *ppaid; 1204 unsigned flag; 1205 1206 paid = calloc(1, sizeof(*paid)); 1207 1208 paid->etype = ENCTYPE_NULL; 1209 ppaid = process_pa_info(context, creds->client, a, paid, in_md); 1210 1211 if (ppaid) 1212 flag = USED_ENC_TS_INFO; 1213 else 1214 flag = USED_ENC_TS_GUESS; 1215 1216 if (ctx->used_pa_types & flag) { 1217 if (ppaid) 1218 free_paid(context, ppaid); 1219 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1220 "Already tried ENC-TS-%s, looping", 1221 flag == USED_ENC_TS_INFO ? "info" : "guess"); 1222 return KRB5_GET_IN_TKT_LOOP; 1223 } 1224 1225 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md); 1226 1227 ctx->used_pa_types |= flag; 1228 1229 if (ppaid) { 1230 if (ctx->ppaid) { 1231 free_paid(context, ctx->ppaid); 1232 free(ctx->ppaid); 1233 } 1234 ctx->ppaid = ppaid; 1235 } else 1236 free(paid); 1237 } 1238 1239 pa_data_add_pac_request(context, ctx, *out_md); 1240 1241 if ((*out_md)->len == 0) { 1242 free(*out_md); 1243 *out_md = NULL; 1244 } 1245 1246 return 0; 1247} 1248 1249static krb5_error_code 1250process_pa_data_to_key(krb5_context context, 1251 krb5_get_init_creds_ctx *ctx, 1252 krb5_creds *creds, 1253 AS_REQ *a, 1254 AS_REP *rep, 1255 const krb5_krbhst_info *hi, 1256 krb5_keyblock **key) 1257{ 1258 struct pa_info_data paid, *ppaid = NULL; 1259 krb5_error_code ret; 1260 krb5_enctype etype; 1261 PA_DATA *pa; 1262 1263 memset(&paid, 0, sizeof(paid)); 1264 1265 etype = rep->enc_part.etype; 1266 1267 if (rep->padata) { 1268 paid.etype = etype; 1269 ppaid = process_pa_info(context, creds->client, a, &paid, 1270 rep->padata); 1271 } 1272 if (ppaid == NULL) 1273 ppaid = ctx->ppaid; 1274 if (ppaid == NULL) { 1275 ret = krb5_get_pw_salt (context, creds->client, &paid.salt); 1276 if (ret) 1277 return ret; 1278 paid.etype = etype; 1279 paid.s2kparams = NULL; 1280 ppaid = &paid; 1281 } 1282 1283 pa = NULL; 1284 if (rep->padata) { 1285 int idx = 0; 1286 pa = krb5_find_padata(rep->padata->val, 1287 rep->padata->len, 1288 KRB5_PADATA_PK_AS_REP, 1289 &idx); 1290 if (pa == NULL) { 1291 idx = 0; 1292 pa = krb5_find_padata(rep->padata->val, 1293 rep->padata->len, 1294 KRB5_PADATA_PK_AS_REP_19, 1295 &idx); 1296 } 1297 } 1298 if (pa && ctx->pk_init_ctx) { 1299#ifdef PKINIT 1300 _krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT"); 1301 1302 ret = _krb5_pk_rd_pa_reply(context, 1303 a->req_body.realm, 1304 ctx->pk_init_ctx, 1305 etype, 1306 hi, 1307 ctx->pk_nonce, 1308 &ctx->req_buffer, 1309 pa, 1310 key); 1311#else 1312 ret = EINVAL; 1313 krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", "")); 1314#endif 1315 } else if (ctx->keyseed) { 1316 _krb5_debug(context, 5, "krb5_get_init_creds: using keyproc"); 1317 ret = pa_data_to_key_plain(context, creds->client, ctx, 1318 ppaid->salt, ppaid->s2kparams, etype, key); 1319 } else { 1320 ret = EINVAL; 1321 krb5_set_error_message(context, ret, N_("No usable pa data type", "")); 1322 } 1323 1324 free_paid(context, &paid); 1325 return ret; 1326} 1327 1328/** 1329 * Start a new context to get a new initial credential. 1330 * 1331 * @param context A Kerberos 5 context. 1332 * @param client The Kerberos principal to get the credential for, if 1333 * NULL is given, the default principal is used as determined by 1334 * krb5_get_default_principal(). 1335 * @param prompter 1336 * @param prompter_data 1337 * @param start_time the time the ticket should start to be valid or 0 for now. 1338 * @param options a options structure, can be NULL for default options. 1339 * @param rctx A new allocated free with krb5_init_creds_free(). 1340 * 1341 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message(). 1342 * 1343 * @ingroup krb5_credential 1344 */ 1345 1346KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1347krb5_init_creds_init(krb5_context context, 1348 krb5_principal client, 1349 krb5_prompter_fct prompter, 1350 void *prompter_data, 1351 krb5_deltat start_time, 1352 krb5_get_init_creds_opt *options, 1353 krb5_init_creds_context *rctx) 1354{ 1355 krb5_init_creds_context ctx; 1356 krb5_error_code ret; 1357 1358 *rctx = NULL; 1359 1360 ctx = calloc(1, sizeof(*ctx)); 1361 if (ctx == NULL) { 1362 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 1363 return ENOMEM; 1364 } 1365 1366 ret = get_init_creds_common(context, client, start_time, options, ctx); 1367 if (ret) { 1368 free(ctx); 1369 return ret; 1370 } 1371 1372 /* Set a new nonce. */ 1373 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce)); 1374 ctx->nonce &= 0x7fffffff; 1375 /* XXX these just needs to be the same when using Windows PK-INIT */ 1376 ctx->pk_nonce = ctx->nonce; 1377 1378 ctx->prompter = prompter; 1379 ctx->prompter_data = prompter_data; 1380 1381 *rctx = ctx; 1382 1383 return ret; 1384} 1385 1386/** 1387 * Sets the service that the is requested. This call is only neede for 1388 * special initial tickets, by default the a krbtgt is fetched in the default realm. 1389 * 1390 * @param context a Kerberos 5 context. 1391 * @param ctx a krb5_init_creds_context context. 1392 * @param service the service given as a string, for example 1393 * "kadmind/admin". If NULL, the default krbtgt in the clients 1394 * realm is set. 1395 * 1396 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1397 * @ingroup krb5_credential 1398 */ 1399 1400KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1401krb5_init_creds_set_service(krb5_context context, 1402 krb5_init_creds_context ctx, 1403 const char *service) 1404{ 1405 krb5_const_realm client_realm; 1406 krb5_principal principal; 1407 krb5_error_code ret; 1408 1409 client_realm = krb5_principal_get_realm (context, ctx->cred.client); 1410 1411 if (service) { 1412 ret = krb5_parse_name (context, service, &principal); 1413 if (ret) 1414 return ret; 1415 krb5_principal_set_realm (context, principal, client_realm); 1416 } else { 1417 ret = krb5_make_principal(context, &principal, 1418 client_realm, KRB5_TGS_NAME, client_realm, 1419 NULL); 1420 if (ret) 1421 return ret; 1422 } 1423 1424 /* 1425 * This is for Windows RODC that are picky about what name type 1426 * the server principal have, and the really strange part is that 1427 * they are picky about the AS-REQ name type and not the TGS-REQ 1428 * later. Oh well. 1429 */ 1430 1431 if (krb5_principal_is_krbtgt(context, principal)) 1432 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST); 1433 1434 krb5_free_principal(context, ctx->cred.server); 1435 ctx->cred.server = principal; 1436 1437 return 0; 1438} 1439 1440/** 1441 * Sets the password that will use for the request. 1442 * 1443 * @param context a Kerberos 5 context. 1444 * @param ctx ctx krb5_init_creds_context context. 1445 * @param password the password to use. 1446 * 1447 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1448 * @ingroup krb5_credential 1449 */ 1450 1451KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1452krb5_init_creds_set_password(krb5_context context, 1453 krb5_init_creds_context ctx, 1454 const char *password) 1455{ 1456 if (ctx->password) { 1457 memset(ctx->password, 0, strlen(ctx->password)); 1458 free(ctx->password); 1459 } 1460 if (password) { 1461 ctx->password = strdup(password); 1462 if (ctx->password == NULL) { 1463 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); 1464 return ENOMEM; 1465 } 1466 ctx->keyseed = (void *) ctx->password; 1467 } else { 1468 ctx->keyseed = NULL; 1469 ctx->password = NULL; 1470 } 1471 1472 return 0; 1473} 1474 1475static krb5_error_code KRB5_CALLCONV 1476keytab_key_proc(krb5_context context, krb5_enctype enctype, 1477 krb5_const_pointer keyseed, 1478 krb5_salt salt, krb5_data *s2kparms, 1479 krb5_keyblock **key) 1480{ 1481 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed); 1482 krb5_keytab keytab = args->keytab; 1483 krb5_principal principal = args->principal; 1484 krb5_error_code ret; 1485 krb5_keytab real_keytab; 1486 krb5_keytab_entry entry; 1487 1488 if(keytab == NULL) 1489 krb5_kt_default(context, &real_keytab); 1490 else 1491 real_keytab = keytab; 1492 1493 ret = krb5_kt_get_entry (context, real_keytab, principal, 1494 0, enctype, &entry); 1495 1496 if (keytab == NULL) 1497 krb5_kt_close (context, real_keytab); 1498 1499 if (ret) 1500 return ret; 1501 1502 ret = krb5_copy_keyblock (context, &entry.keyblock, key); 1503 krb5_kt_free_entry(context, &entry); 1504 return ret; 1505} 1506 1507 1508/** 1509 * Set the keytab to use for authentication. 1510 * 1511 * @param context a Kerberos 5 context. 1512 * @param ctx ctx krb5_init_creds_context context. 1513 * @param keytab the keytab to read the key from. 1514 * 1515 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1516 * @ingroup krb5_credential 1517 */ 1518 1519KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1520krb5_init_creds_set_keytab(krb5_context context, 1521 krb5_init_creds_context ctx, 1522 krb5_keytab keytab) 1523{ 1524 krb5_keytab_key_proc_args *a; 1525 krb5_keytab_entry entry; 1526 krb5_kt_cursor cursor; 1527 krb5_enctype *etypes = NULL; 1528 krb5_error_code ret; 1529 size_t netypes = 0; 1530 int kvno = 0; 1531 1532 a = malloc(sizeof(*a)); 1533 if (a == NULL) { 1534 krb5_set_error_message(context, ENOMEM, 1535 N_("malloc: out of memory", "")); 1536 return ENOMEM; 1537 } 1538 1539 a->principal = ctx->cred.client; 1540 a->keytab = keytab; 1541 1542 ctx->keytab_data = a; 1543 ctx->keyseed = (void *)a; 1544 ctx->keyproc = keytab_key_proc; 1545 1546 /* 1547 * We need to the KDC what enctypes we support for this keytab, 1548 * esp if the keytab is really a password based entry, then the 1549 * KDC might have more enctypes in the database then what we have 1550 * in the keytab. 1551 */ 1552 1553 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 1554 if(ret) 1555 goto out; 1556 1557 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){ 1558 void *ptr; 1559 1560 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client)) 1561 goto next; 1562 1563 /* check if we ahve this kvno already */ 1564 if (entry.vno > kvno) { 1565 /* remove old list of etype */ 1566 if (etypes) 1567 free(etypes); 1568 etypes = NULL; 1569 netypes = 0; 1570 kvno = entry.vno; 1571 } else if (entry.vno != kvno) 1572 goto next; 1573 1574 /* check if enctype is supported */ 1575 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0) 1576 goto next; 1577 1578 /* add enctype to supported list */ 1579 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2)); 1580 if (ptr == NULL) 1581 goto next; 1582 1583 etypes = ptr; 1584 etypes[netypes] = entry.keyblock.keytype; 1585 etypes[netypes + 1] = ETYPE_NULL; 1586 netypes++; 1587 next: 1588 krb5_kt_free_entry(context, &entry); 1589 } 1590 krb5_kt_end_seq_get(context, keytab, &cursor); 1591 1592 if (etypes) { 1593 if (ctx->etypes) 1594 free(ctx->etypes); 1595 ctx->etypes = etypes; 1596 } 1597 1598 out: 1599 return 0; 1600} 1601 1602static krb5_error_code KRB5_CALLCONV 1603keyblock_key_proc(krb5_context context, krb5_enctype enctype, 1604 krb5_const_pointer keyseed, 1605 krb5_salt salt, krb5_data *s2kparms, 1606 krb5_keyblock **key) 1607{ 1608 return krb5_copy_keyblock (context, keyseed, key); 1609} 1610 1611KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1612krb5_init_creds_set_keyblock(krb5_context context, 1613 krb5_init_creds_context ctx, 1614 krb5_keyblock *keyblock) 1615{ 1616 ctx->keyseed = (void *)keyblock; 1617 ctx->keyproc = keyblock_key_proc; 1618 1619 return 0; 1620} 1621 1622/** 1623 * The core loop if krb5_get_init_creds() function family. Create the 1624 * packets and have the caller send them off to the KDC. 1625 * 1626 * If the caller want all work been done for them, use 1627 * krb5_init_creds_get() instead. 1628 * 1629 * @param context a Kerberos 5 context. 1630 * @param ctx ctx krb5_init_creds_context context. 1631 * @param in input data from KDC, first round it should be reset by krb5_data_zer(). 1632 * @param out reply to KDC. 1633 * @param hostinfo KDC address info, first round it can be NULL. 1634 * @param flags status of the round, if 1635 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round. 1636 * 1637 * @return 0 for success, or an Kerberos 5 error code, see 1638 * krb5_get_error_message(). 1639 * 1640 * @ingroup krb5_credential 1641 */ 1642 1643KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1644krb5_init_creds_step(krb5_context context, 1645 krb5_init_creds_context ctx, 1646 krb5_data *in, 1647 krb5_data *out, 1648 krb5_krbhst_info *hostinfo, 1649 unsigned int *flags) 1650{ 1651 krb5_error_code ret; 1652 size_t len; 1653 size_t size; 1654 1655 krb5_data_zero(out); 1656 1657 if (ctx->as_req.req_body.cname == NULL) { 1658 ret = init_as_req(context, ctx->flags, &ctx->cred, 1659 ctx->addrs, ctx->etypes, &ctx->as_req); 1660 if (ret) { 1661 free_init_creds_ctx(context, ctx); 1662 return ret; 1663 } 1664 } 1665 1666#define MAX_PA_COUNTER 10 1667 if (ctx->pa_counter > MAX_PA_COUNTER) { 1668 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1669 N_("Looping %d times while getting " 1670 "initial credentials", ""), 1671 ctx->pa_counter); 1672 return KRB5_GET_IN_TKT_LOOP; 1673 } 1674 ctx->pa_counter++; 1675 1676 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter); 1677 1678 /* Lets process the input packet */ 1679 if (in && in->length) { 1680 krb5_kdc_rep rep; 1681 1682 memset(&rep, 0, sizeof(rep)); 1683 1684 _krb5_debug(context, 5, "krb5_get_init_creds: processing input"); 1685 1686 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size); 1687 if (ret == 0) { 1688 krb5_keyblock *key = NULL; 1689 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC; 1690 1691 if (ctx->flags.canonicalize) { 1692 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; 1693 eflags |= EXTRACT_TICKET_MATCH_REALM; 1694 } 1695 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) 1696 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; 1697 1698 ret = process_pa_data_to_key(context, ctx, &ctx->cred, 1699 &ctx->as_req, &rep.kdc_rep, hostinfo, &key); 1700 if (ret) { 1701 free_AS_REP(&rep.kdc_rep); 1702 goto out; 1703 } 1704 1705 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket"); 1706 1707 ret = _krb5_extract_ticket(context, 1708 &rep, 1709 &ctx->cred, 1710 key, 1711 NULL, 1712 KRB5_KU_AS_REP_ENC_PART, 1713 NULL, 1714 ctx->nonce, 1715 eflags, 1716 NULL, 1717 NULL); 1718 krb5_free_keyblock(context, key); 1719 1720 *flags = 0; 1721 1722 if (ret == 0) 1723 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part); 1724 1725 free_AS_REP(&rep.kdc_rep); 1726 free_EncASRepPart(&rep.enc_part); 1727 1728 return ret; 1729 1730 } else { 1731 /* let's try to parse it as a KRB-ERROR */ 1732 1733 _krb5_debug(context, 5, "krb5_get_init_creds: got an error"); 1734 1735 free_KRB_ERROR(&ctx->error); 1736 1737 ret = krb5_rd_error(context, in, &ctx->error); 1738 if(ret && in->length && ((char*)in->data)[0] == 4) 1739 ret = KRB5KRB_AP_ERR_V4_REPLY; 1740 if (ret) { 1741 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error"); 1742 goto out; 1743 } 1744 1745 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred); 1746 1747 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret); 1748 1749 /* 1750 * If no preauth was set and KDC requires it, give it one 1751 * more try. 1752 */ 1753 1754 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) { 1755 1756 free_METHOD_DATA(&ctx->md); 1757 memset(&ctx->md, 0, sizeof(ctx->md)); 1758 1759 if (ctx->error.e_data) { 1760 ret = decode_METHOD_DATA(ctx->error.e_data->data, 1761 ctx->error.e_data->length, 1762 &ctx->md, 1763 NULL); 1764 if (ret) 1765 krb5_set_error_message(context, ret, 1766 N_("Failed to decode METHOD-DATA", "")); 1767 } else { 1768 krb5_set_error_message(context, ret, 1769 N_("Preauth required but no preauth " 1770 "options send by KDC", "")); 1771 } 1772 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) { 1773 /* 1774 * Try adapt to timeskrew when we are using pre-auth, and 1775 * if there was a time skew, try again. 1776 */ 1777 krb5_set_real_time(context, ctx->error.stime, -1); 1778 if (context->kdc_sec_offset) 1779 ret = 0; 1780 1781 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d", 1782 context->kdc_sec_offset); 1783 1784 ctx->used_pa_types = 0; 1785 1786 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) { 1787 /* client referal to a new realm */ 1788 1789 if (ctx->error.crealm == NULL) { 1790 krb5_set_error_message(context, ret, 1791 N_("Got a client referral, not but no realm", "")); 1792 goto out; 1793 } 1794 _krb5_debug(context, 5, 1795 "krb5_get_init_creds: got referal to realm %s", 1796 *ctx->error.crealm); 1797 1798 ret = krb5_principal_set_realm(context, 1799 ctx->cred.client, 1800 *ctx->error.crealm); 1801 1802 ctx->used_pa_types = 0; 1803 } 1804 if (ret) 1805 goto out; 1806 } 1807 } 1808 1809 if (ctx->as_req.padata) { 1810 free_METHOD_DATA(ctx->as_req.padata); 1811 free(ctx->as_req.padata); 1812 ctx->as_req.padata = NULL; 1813 } 1814 1815 /* Set a new nonce. */ 1816 ctx->as_req.req_body.nonce = ctx->nonce; 1817 1818 /* fill_in_md_data */ 1819 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx, 1820 &ctx->md, &ctx->as_req.padata, 1821 ctx->prompter, ctx->prompter_data); 1822 if (ret) 1823 goto out; 1824 1825 krb5_data_free(&ctx->req_buffer); 1826 1827 ASN1_MALLOC_ENCODE(AS_REQ, 1828 ctx->req_buffer.data, ctx->req_buffer.length, 1829 &ctx->as_req, &len, ret); 1830 if (ret) 1831 goto out; 1832 if(len != ctx->req_buffer.length) 1833 krb5_abortx(context, "internal error in ASN.1 encoder"); 1834 1835 out->data = ctx->req_buffer.data; 1836 out->length = ctx->req_buffer.length; 1837 1838 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; 1839 1840 return 0; 1841 out: 1842 return ret; 1843} 1844 1845/** 1846 * Extract the newly acquired credentials from krb5_init_creds_context 1847 * context. 1848 * 1849 * @param context A Kerberos 5 context. 1850 * @param ctx 1851 * @param cred credentials, free with krb5_free_cred_contents(). 1852 * 1853 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message(). 1854 */ 1855 1856KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1857krb5_init_creds_get_creds(krb5_context context, 1858 krb5_init_creds_context ctx, 1859 krb5_creds *cred) 1860{ 1861 return krb5_copy_creds_contents(context, &ctx->cred, cred); 1862} 1863 1864/** 1865 * Get the last error from the transaction. 1866 * 1867 * @return Returns 0 or an error code 1868 * 1869 * @ingroup krb5_credential 1870 */ 1871 1872KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1873krb5_init_creds_get_error(krb5_context context, 1874 krb5_init_creds_context ctx, 1875 KRB_ERROR *error) 1876{ 1877 krb5_error_code ret; 1878 1879 ret = copy_KRB_ERROR(&ctx->error, error); 1880 if (ret) 1881 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 1882 1883 return ret; 1884} 1885 1886/** 1887 * Free the krb5_init_creds_context allocated by krb5_init_creds_init(). 1888 * 1889 * @param context A Kerberos 5 context. 1890 * @param ctx The krb5_init_creds_context to free. 1891 * 1892 * @ingroup krb5_credential 1893 */ 1894 1895KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1896krb5_init_creds_free(krb5_context context, 1897 krb5_init_creds_context ctx) 1898{ 1899 free_init_creds_ctx(context, ctx); 1900 free(ctx); 1901} 1902 1903/** 1904 * Get new credentials as setup by the krb5_init_creds_context. 1905 * 1906 * @param context A Kerberos 5 context. 1907 * @param ctx The krb5_init_creds_context to process. 1908 * 1909 * @ingroup krb5_credential 1910 */ 1911 1912KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1913krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) 1914{ 1915 krb5_sendto_ctx stctx = NULL; 1916 krb5_krbhst_info *hostinfo = NULL; 1917 krb5_error_code ret; 1918 krb5_data in, out; 1919 unsigned int flags = 0; 1920 1921 krb5_data_zero(&in); 1922 krb5_data_zero(&out); 1923 1924 ret = krb5_sendto_ctx_alloc(context, &stctx); 1925 if (ret) 1926 goto out; 1927 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); 1928 1929 while (1) { 1930 flags = 0; 1931 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags); 1932 krb5_data_free(&in); 1933 if (ret) 1934 goto out; 1935 1936 if ((flags & 1) == 0) 1937 break; 1938 1939 ret = krb5_sendto_context (context, stctx, &out, 1940 ctx->cred.client->realm, &in); 1941 if (ret) 1942 goto out; 1943 1944 } 1945 1946 out: 1947 if (stctx) 1948 krb5_sendto_ctx_free(context, stctx); 1949 1950 return ret; 1951} 1952 1953/** 1954 * Get new credentials using password. 1955 * 1956 * @ingroup krb5_credential 1957 */ 1958 1959 1960KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1961krb5_get_init_creds_password(krb5_context context, 1962 krb5_creds *creds, 1963 krb5_principal client, 1964 const char *password, 1965 krb5_prompter_fct prompter, 1966 void *data, 1967 krb5_deltat start_time, 1968 const char *in_tkt_service, 1969 krb5_get_init_creds_opt *options) 1970{ 1971 krb5_init_creds_context ctx; 1972 char buf[BUFSIZ]; 1973 krb5_error_code ret; 1974 int chpw = 0; 1975 1976 again: 1977 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx); 1978 if (ret) 1979 goto out; 1980 1981 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 1982 if (ret) 1983 goto out; 1984 1985 if (prompter != NULL && ctx->password == NULL && password == NULL) { 1986 krb5_prompt prompt; 1987 krb5_data password_data; 1988 char *p, *q; 1989 1990 krb5_unparse_name (context, client, &p); 1991 asprintf (&q, "%s's Password: ", p); 1992 free (p); 1993 prompt.prompt = q; 1994 password_data.data = buf; 1995 password_data.length = sizeof(buf); 1996 prompt.hidden = 1; 1997 prompt.reply = &password_data; 1998 prompt.type = KRB5_PROMPT_TYPE_PASSWORD; 1999 2000 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); 2001 free (q); 2002 if (ret) { 2003 memset (buf, 0, sizeof(buf)); 2004 ret = KRB5_LIBOS_PWDINTR; 2005 krb5_clear_error_message (context); 2006 goto out; 2007 } 2008 password = password_data.data; 2009 } 2010 2011 if (password) { 2012 ret = krb5_init_creds_set_password(context, ctx, password); 2013 if (ret) 2014 goto out; 2015 } 2016 2017 ret = krb5_init_creds_get(context, ctx); 2018 2019 if (ret == 0) 2020 process_last_request(context, options, ctx); 2021 2022 2023 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) { 2024 char buf2[1024]; 2025 2026 /* try to avoid recursion */ 2027 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0) 2028 goto out; 2029 2030 /* don't try to change password where then where none */ 2031 if (prompter == NULL) 2032 goto out; 2033 2034 ret = change_password (context, 2035 client, 2036 ctx->password, 2037 buf2, 2038 sizeof(buf), 2039 prompter, 2040 data, 2041 options); 2042 if (ret) 2043 goto out; 2044 chpw = 1; 2045 krb5_init_creds_free(context, ctx); 2046 goto again; 2047 } 2048 2049 out: 2050 if (ret == 0) 2051 krb5_init_creds_get_creds(context, ctx, creds); 2052 2053 if (ctx) 2054 krb5_init_creds_free(context, ctx); 2055 2056 memset(buf, 0, sizeof(buf)); 2057 return ret; 2058} 2059 2060/** 2061 * Get new credentials using keyblock. 2062 * 2063 * @ingroup krb5_credential 2064 */ 2065 2066KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2067krb5_get_init_creds_keyblock(krb5_context context, 2068 krb5_creds *creds, 2069 krb5_principal client, 2070 krb5_keyblock *keyblock, 2071 krb5_deltat start_time, 2072 const char *in_tkt_service, 2073 krb5_get_init_creds_opt *options) 2074{ 2075 krb5_init_creds_context ctx; 2076 krb5_error_code ret; 2077 2078 memset(creds, 0, sizeof(*creds)); 2079 2080 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2081 if (ret) 2082 goto out; 2083 2084 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2085 if (ret) 2086 goto out; 2087 2088 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock); 2089 if (ret) 2090 goto out; 2091 2092 ret = krb5_init_creds_get(context, ctx); 2093 2094 if (ret == 0) 2095 process_last_request(context, options, ctx); 2096 2097 out: 2098 if (ret == 0) 2099 krb5_init_creds_get_creds(context, ctx, creds); 2100 2101 if (ctx) 2102 krb5_init_creds_free(context, ctx); 2103 2104 return ret; 2105} 2106 2107/** 2108 * Get new credentials using keytab. 2109 * 2110 * @ingroup krb5_credential 2111 */ 2112 2113KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2114krb5_get_init_creds_keytab(krb5_context context, 2115 krb5_creds *creds, 2116 krb5_principal client, 2117 krb5_keytab keytab, 2118 krb5_deltat start_time, 2119 const char *in_tkt_service, 2120 krb5_get_init_creds_opt *options) 2121{ 2122 krb5_init_creds_context ctx; 2123 krb5_error_code ret; 2124 2125 memset(creds, 0, sizeof(*creds)); 2126 2127 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2128 if (ret) 2129 goto out; 2130 2131 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2132 if (ret) 2133 goto out; 2134 2135 ret = krb5_init_creds_set_keytab(context, ctx, keytab); 2136 if (ret) 2137 goto out; 2138 2139 ret = krb5_init_creds_get(context, ctx); 2140 if (ret == 0) 2141 process_last_request(context, options, ctx); 2142 2143 out: 2144 if (ret == 0) 2145 krb5_init_creds_get_creds(context, ctx, creds); 2146 2147 if (ctx) 2148 krb5_init_creds_free(context, ctx); 2149 2150 return ret; 2151} 2152