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