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