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 if (ret == 0) { 1495 ret = krb5_copy_keyblock(context, &entry.keyblock, key); 1496 krb5_kt_free_entry(context, &entry); 1497 } 1498 1499 if (keytab == NULL) 1500 krb5_kt_close (context, real_keytab); 1501 return ret; 1502 } 1503 1504 1505 /** 1506 * Set the keytab to use for authentication. 1507 * 1508 * @param context a Kerberos 5 context. 1509 * @param ctx ctx krb5_init_creds_context context. 1510 * @param keytab the keytab to read the key from. 1511 * 1512 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1513 * @ingroup krb5_credential 1514 */ 1515 1516 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1517 krb5_init_creds_set_keytab(krb5_context context, 1518 krb5_init_creds_context ctx, 1519 krb5_keytab keytab) 1520 { 1521 krb5_keytab_key_proc_args *a; 1522 krb5_keytab_entry entry; 1523 krb5_kt_cursor cursor; 1524 krb5_enctype *etypes = NULL; 1525 krb5_error_code ret; 1526 size_t netypes = 0; 1527 int kvno = 0; 1528 1529 a = malloc(sizeof(*a)); 1530 if (a == NULL) { 1531 krb5_set_error_message(context, ENOMEM, 1532 N_("malloc: out of memory", "")); 1533 return ENOMEM; 1534 } 1535 1536 a->principal = ctx->cred.client; 1537 a->keytab = keytab; 1538 1539 ctx->keytab_data = a; 1540 ctx->keyseed = (void *)a; 1541 ctx->keyproc = keytab_key_proc; 1542 1543 /* 1544 * We need to the KDC what enctypes we support for this keytab, 1545 * esp if the keytab is really a password based entry, then the 1546 * KDC might have more enctypes in the database then what we have 1547 * in the keytab. 1548 */ 1549 1550 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 1551 if(ret) 1552 goto out; 1553 1554 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){ 1555 void *ptr; 1556 1557 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client)) 1558 goto next; 1559 1560 /* check if we ahve this kvno already */ 1561 if (entry.vno > kvno) { 1562 /* remove old list of etype */ 1563 if (etypes) 1564 free(etypes); 1565 etypes = NULL; 1566 netypes = 0; 1567 kvno = entry.vno; 1568 } else if (entry.vno != kvno) 1569 goto next; 1570 1571 /* check if enctype is supported */ 1572 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0) 1573 goto next; 1574 1575 /* add enctype to supported list */ 1576 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2)); 1577 if (ptr == NULL) 1578 goto next; 1579 1580 etypes = ptr; 1581 etypes[netypes] = entry.keyblock.keytype; 1582 etypes[netypes + 1] = ETYPE_NULL; 1583 netypes++; 1584 next: 1585 krb5_kt_free_entry(context, &entry); 1586 } 1587 krb5_kt_end_seq_get(context, keytab, &cursor); 1588 1589 if (etypes) { 1590 if (ctx->etypes) 1591 free(ctx->etypes); 1592 ctx->etypes = etypes; 1593 } 1594 1595 out: 1596 return 0; 1597 } 1598 1599 static krb5_error_code KRB5_CALLCONV 1600 keyblock_key_proc(krb5_context context, krb5_enctype enctype, 1601 krb5_const_pointer keyseed, 1602 krb5_salt salt, krb5_data *s2kparms, 1603 krb5_keyblock **key) 1604 { 1605 return krb5_copy_keyblock (context, keyseed, key); 1606 } 1607 1608 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1609 krb5_init_creds_set_keyblock(krb5_context context, 1610 krb5_init_creds_context ctx, 1611 krb5_keyblock *keyblock) 1612 { 1613 ctx->keyseed = (void *)keyblock; 1614 ctx->keyproc = keyblock_key_proc; 1615 1616 return 0; 1617 } 1618 1619 /** 1620 * The core loop if krb5_get_init_creds() function family. Create the 1621 * packets and have the caller send them off to the KDC. 1622 * 1623 * If the caller want all work been done for them, use 1624 * krb5_init_creds_get() instead. 1625 * 1626 * @param context a Kerberos 5 context. 1627 * @param ctx ctx krb5_init_creds_context context. 1628 * @param in input data from KDC, first round it should be reset by krb5_data_zer(). 1629 * @param out reply to KDC. 1630 * @param hostinfo KDC address info, first round it can be NULL. 1631 * @param flags status of the round, if 1632 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round. 1633 * 1634 * @return 0 for success, or an Kerberos 5 error code, see 1635 * krb5_get_error_message(). 1636 * 1637 * @ingroup krb5_credential 1638 */ 1639 1640 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1641 krb5_init_creds_step(krb5_context context, 1642 krb5_init_creds_context ctx, 1643 krb5_data *in, 1644 krb5_data *out, 1645 krb5_krbhst_info *hostinfo, 1646 unsigned int *flags) 1647 { 1648 krb5_error_code ret; 1649 size_t len = 0; 1650 size_t size; 1651 1652 krb5_data_zero(out); 1653 1654 if (ctx->as_req.req_body.cname == NULL) { 1655 ret = init_as_req(context, ctx->flags, &ctx->cred, 1656 ctx->addrs, ctx->etypes, &ctx->as_req); 1657 if (ret) { 1658 free_init_creds_ctx(context, ctx); 1659 return ret; 1660 } 1661 } 1662 1663 #define MAX_PA_COUNTER 10 1664 if (ctx->pa_counter > MAX_PA_COUNTER) { 1665 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1666 N_("Looping %d times while getting " 1667 "initial credentials", ""), 1668 ctx->pa_counter); 1669 return KRB5_GET_IN_TKT_LOOP; 1670 } 1671 ctx->pa_counter++; 1672 1673 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter); 1674 1675 /* Lets process the input packet */ 1676 if (in && in->length) { 1677 krb5_kdc_rep rep; 1678 1679 memset(&rep, 0, sizeof(rep)); 1680 1681 _krb5_debug(context, 5, "krb5_get_init_creds: processing input"); 1682 1683 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size); 1684 if (ret == 0) { 1685 krb5_keyblock *key = NULL; 1686 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC; 1687 1688 if (ctx->flags.canonicalize) { 1689 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; 1690 eflags |= EXTRACT_TICKET_MATCH_REALM; 1691 } 1692 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) 1693 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; 1694 1695 ret = process_pa_data_to_key(context, ctx, &ctx->cred, 1696 &ctx->as_req, &rep.kdc_rep, hostinfo, &key); 1697 if (ret) { 1698 free_AS_REP(&rep.kdc_rep); 1699 goto out; 1700 } 1701 1702 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket"); 1703 1704 ret = _krb5_extract_ticket(context, 1705 &rep, 1706 &ctx->cred, 1707 key, 1708 NULL, 1709 KRB5_KU_AS_REP_ENC_PART, 1710 NULL, 1711 ctx->nonce, 1712 eflags, 1713 NULL, 1714 NULL); 1715 krb5_free_keyblock(context, key); 1716 1717 *flags = 0; 1718 1719 if (ret == 0) 1720 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part); 1721 1722 free_AS_REP(&rep.kdc_rep); 1723 free_EncASRepPart(&rep.enc_part); 1724 1725 return ret; 1726 1727 } else { 1728 /* let's try to parse it as a KRB-ERROR */ 1729 1730 _krb5_debug(context, 5, "krb5_get_init_creds: got an error"); 1731 1732 free_KRB_ERROR(&ctx->error); 1733 1734 ret = krb5_rd_error(context, in, &ctx->error); 1735 if(ret && in->length && ((char*)in->data)[0] == 4) 1736 ret = KRB5KRB_AP_ERR_V4_REPLY; 1737 if (ret) { 1738 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error"); 1739 goto out; 1740 } 1741 1742 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred); 1743 1744 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret); 1745 1746 /* 1747 * If no preauth was set and KDC requires it, give it one 1748 * more try. 1749 */ 1750 1751 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) { 1752 1753 free_METHOD_DATA(&ctx->md); 1754 memset(&ctx->md, 0, sizeof(ctx->md)); 1755 1756 if (ctx->error.e_data) { 1757 ret = decode_METHOD_DATA(ctx->error.e_data->data, 1758 ctx->error.e_data->length, 1759 &ctx->md, 1760 NULL); 1761 if (ret) 1762 krb5_set_error_message(context, ret, 1763 N_("Failed to decode METHOD-DATA", "")); 1764 } else { 1765 krb5_set_error_message(context, ret, 1766 N_("Preauth required but no preauth " 1767 "options send by KDC", "")); 1768 } 1769 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) { 1770 /* 1771 * Try adapt to timeskrew when we are using pre-auth, and 1772 * if there was a time skew, try again. 1773 */ 1774 krb5_set_real_time(context, ctx->error.stime, -1); 1775 if (context->kdc_sec_offset) 1776 ret = 0; 1777 1778 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d", 1779 context->kdc_sec_offset); 1780 1781 ctx->used_pa_types = 0; 1782 1783 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) { 1784 /* client referal to a new realm */ 1785 1786 if (ctx->error.crealm == NULL) { 1787 krb5_set_error_message(context, ret, 1788 N_("Got a client referral, not but no realm", "")); 1789 goto out; 1790 } 1791 _krb5_debug(context, 5, 1792 "krb5_get_init_creds: got referal to realm %s", 1793 *ctx->error.crealm); 1794 1795 ret = krb5_principal_set_realm(context, 1796 ctx->cred.client, 1797 *ctx->error.crealm); 1798 1799 ctx->used_pa_types = 0; 1800 } 1801 if (ret) 1802 goto out; 1803 } 1804 } 1805 1806 if (ctx->as_req.padata) { 1807 free_METHOD_DATA(ctx->as_req.padata); 1808 free(ctx->as_req.padata); 1809 ctx->as_req.padata = NULL; 1810 } 1811 1812 /* Set a new nonce. */ 1813 ctx->as_req.req_body.nonce = ctx->nonce; 1814 1815 /* fill_in_md_data */ 1816 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx, 1817 &ctx->md, &ctx->as_req.padata, 1818 ctx->prompter, ctx->prompter_data); 1819 if (ret) 1820 goto out; 1821 1822 krb5_data_free(&ctx->req_buffer); 1823 1824 ASN1_MALLOC_ENCODE(AS_REQ, 1825 ctx->req_buffer.data, ctx->req_buffer.length, 1826 &ctx->as_req, &len, ret); 1827 if (ret) 1828 goto out; 1829 if(len != ctx->req_buffer.length) 1830 krb5_abortx(context, "internal error in ASN.1 encoder"); 1831 1832 out->data = ctx->req_buffer.data; 1833 out->length = ctx->req_buffer.length; 1834 1835 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; 1836 1837 return 0; 1838 out: 1839 return ret; 1840 } 1841 1842 /** 1843 * Extract the newly acquired credentials from krb5_init_creds_context 1844 * context. 1845 * 1846 * @param context A Kerberos 5 context. 1847 * @param ctx 1848 * @param cred credentials, free with krb5_free_cred_contents(). 1849 * 1850 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message(). 1851 */ 1852 1853 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1854 krb5_init_creds_get_creds(krb5_context context, 1855 krb5_init_creds_context ctx, 1856 krb5_creds *cred) 1857 { 1858 return krb5_copy_creds_contents(context, &ctx->cred, cred); 1859 } 1860 1861 /** 1862 * Get the last error from the transaction. 1863 * 1864 * @return Returns 0 or an error code 1865 * 1866 * @ingroup krb5_credential 1867 */ 1868 1869 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1870 krb5_init_creds_get_error(krb5_context context, 1871 krb5_init_creds_context ctx, 1872 KRB_ERROR *error) 1873 { 1874 krb5_error_code ret; 1875 1876 ret = copy_KRB_ERROR(&ctx->error, error); 1877 if (ret) 1878 krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 1879 1880 return ret; 1881 } 1882 1883 /** 1884 * Free the krb5_init_creds_context allocated by krb5_init_creds_init(). 1885 * 1886 * @param context A Kerberos 5 context. 1887 * @param ctx The krb5_init_creds_context to free. 1888 * 1889 * @ingroup krb5_credential 1890 */ 1891 1892 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1893 krb5_init_creds_free(krb5_context context, 1894 krb5_init_creds_context ctx) 1895 { 1896 free_init_creds_ctx(context, ctx); 1897 free(ctx); 1898 } 1899 1900 /** 1901 * Get new credentials as setup by the krb5_init_creds_context. 1902 * 1903 * @param context A Kerberos 5 context. 1904 * @param ctx The krb5_init_creds_context to process. 1905 * 1906 * @ingroup krb5_credential 1907 */ 1908 1909 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1910 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) 1911 { 1912 krb5_sendto_ctx stctx = NULL; 1913 krb5_krbhst_info *hostinfo = NULL; 1914 krb5_error_code ret; 1915 krb5_data in, out; 1916 unsigned int flags = 0; 1917 1918 krb5_data_zero(&in); 1919 krb5_data_zero(&out); 1920 1921 ret = krb5_sendto_ctx_alloc(context, &stctx); 1922 if (ret) 1923 goto out; 1924 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); 1925 1926 while (1) { 1927 flags = 0; 1928 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags); 1929 krb5_data_free(&in); 1930 if (ret) 1931 goto out; 1932 1933 if ((flags & 1) == 0) 1934 break; 1935 1936 ret = krb5_sendto_context (context, stctx, &out, 1937 ctx->cred.client->realm, &in); 1938 if (ret) 1939 goto out; 1940 1941 } 1942 1943 out: 1944 if (stctx) 1945 krb5_sendto_ctx_free(context, stctx); 1946 1947 return ret; 1948 } 1949 1950 /** 1951 * Get new credentials using password. 1952 * 1953 * @ingroup krb5_credential 1954 */ 1955 1956 1957 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1958 krb5_get_init_creds_password(krb5_context context, 1959 krb5_creds *creds, 1960 krb5_principal client, 1961 const char *password, 1962 krb5_prompter_fct prompter, 1963 void *data, 1964 krb5_deltat start_time, 1965 const char *in_tkt_service, 1966 krb5_get_init_creds_opt *options) 1967 { 1968 krb5_init_creds_context ctx; 1969 char buf[BUFSIZ]; 1970 krb5_error_code ret; 1971 int chpw = 0; 1972 1973 again: 1974 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx); 1975 if (ret) 1976 goto out; 1977 1978 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 1979 if (ret) 1980 goto out; 1981 1982 if (prompter != NULL && ctx->password == NULL && password == NULL) { 1983 krb5_prompt prompt; 1984 krb5_data password_data; 1985 char *p, *q; 1986 1987 krb5_unparse_name (context, client, &p); 1988 asprintf (&q, "%s's Password: ", p); 1989 free (p); 1990 prompt.prompt = q; 1991 password_data.data = buf; 1992 password_data.length = sizeof(buf); 1993 prompt.hidden = 1; 1994 prompt.reply = &password_data; 1995 prompt.type = KRB5_PROMPT_TYPE_PASSWORD; 1996 1997 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); 1998 free (q); 1999 if (ret) { 2000 memset (buf, 0, sizeof(buf)); 2001 ret = KRB5_LIBOS_PWDINTR; 2002 krb5_clear_error_message (context); 2003 goto out; 2004 } 2005 password = password_data.data; 2006 } 2007 2008 if (password) { 2009 ret = krb5_init_creds_set_password(context, ctx, password); 2010 if (ret) 2011 goto out; 2012 } 2013 2014 ret = krb5_init_creds_get(context, ctx); 2015 2016 if (ret == 0) 2017 process_last_request(context, options, ctx); 2018 2019 2020 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) { 2021 char buf2[1024]; 2022 2023 /* try to avoid recursion */ 2024 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0) 2025 goto out; 2026 2027 /* don't try to change password where then where none */ 2028 if (prompter == NULL) 2029 goto out; 2030 2031 ret = change_password (context, 2032 client, 2033 ctx->password, 2034 buf2, 2035 sizeof(buf), 2036 prompter, 2037 data, 2038 options); 2039 if (ret) 2040 goto out; 2041 chpw = 1; 2042 krb5_init_creds_free(context, ctx); 2043 goto again; 2044 } 2045 2046 out: 2047 if (ret == 0) 2048 krb5_init_creds_get_creds(context, ctx, creds); 2049 2050 if (ctx) 2051 krb5_init_creds_free(context, ctx); 2052 2053 memset(buf, 0, sizeof(buf)); 2054 return ret; 2055 } 2056 2057 /** 2058 * Get new credentials using keyblock. 2059 * 2060 * @ingroup krb5_credential 2061 */ 2062 2063 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2064 krb5_get_init_creds_keyblock(krb5_context context, 2065 krb5_creds *creds, 2066 krb5_principal client, 2067 krb5_keyblock *keyblock, 2068 krb5_deltat start_time, 2069 const char *in_tkt_service, 2070 krb5_get_init_creds_opt *options) 2071 { 2072 krb5_init_creds_context ctx; 2073 krb5_error_code ret; 2074 2075 memset(creds, 0, sizeof(*creds)); 2076 2077 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2078 if (ret) 2079 goto out; 2080 2081 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2082 if (ret) 2083 goto out; 2084 2085 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock); 2086 if (ret) 2087 goto out; 2088 2089 ret = krb5_init_creds_get(context, ctx); 2090 2091 if (ret == 0) 2092 process_last_request(context, options, ctx); 2093 2094 out: 2095 if (ret == 0) 2096 krb5_init_creds_get_creds(context, ctx, creds); 2097 2098 if (ctx) 2099 krb5_init_creds_free(context, ctx); 2100 2101 return ret; 2102 } 2103 2104 /** 2105 * Get new credentials using keytab. 2106 * 2107 * @ingroup krb5_credential 2108 */ 2109 2110 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2111 krb5_get_init_creds_keytab(krb5_context context, 2112 krb5_creds *creds, 2113 krb5_principal client, 2114 krb5_keytab keytab, 2115 krb5_deltat start_time, 2116 const char *in_tkt_service, 2117 krb5_get_init_creds_opt *options) 2118 { 2119 krb5_init_creds_context ctx; 2120 krb5_error_code ret; 2121 2122 memset(creds, 0, sizeof(*creds)); 2123 2124 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2125 if (ret) 2126 goto out; 2127 2128 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2129 if (ret) 2130 goto out; 2131 2132 ret = krb5_init_creds_set_keytab(context, ctx, keytab); 2133 if (ret) 2134 goto out; 2135 2136 ret = krb5_init_creds_get(context, ctx); 2137 if (ret == 0) 2138 process_last_request(context, options, ctx); 2139 2140 out: 2141 if (ret == 0) 2142 krb5_init_creds_get_creds(context, ctx, creds); 2143 2144 if (ctx) 2145 krb5_init_creds_free(context, ctx); 2146 2147 return ret; 2148 } 2149