1 /* $NetBSD: init_creds_pw.c,v 1.2 2017/01/28 21:31:49 christos 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 #ifndef WIN32 40 #include <heim-ipc.h> 41 #endif /* WIN32 */ 42 43 typedef struct krb5_get_init_creds_ctx { 44 KDCOptions flags; 45 krb5_creds cred; 46 krb5_addresses *addrs; 47 krb5_enctype *etypes; 48 krb5_preauthtype *pre_auth_types; 49 char *in_tkt_service; 50 unsigned nonce; 51 unsigned pk_nonce; 52 53 krb5_data req_buffer; 54 AS_REQ as_req; 55 int pa_counter; 56 57 /* password and keytab_data is freed on completion */ 58 char *password; 59 krb5_keytab_key_proc_args *keytab_data; 60 61 krb5_pointer *keyseed; 62 krb5_s2k_proc keyproc; 63 64 krb5_get_init_creds_tristate req_pac; 65 66 krb5_pk_init_ctx pk_init_ctx; 67 int ic_flags; 68 69 struct { 70 unsigned change_password:1; 71 } runflags; 72 73 int used_pa_types; 74 #define USED_PKINIT 1 75 #define USED_PKINIT_W2K 2 76 #define USED_ENC_TS_GUESS 4 77 #define USED_ENC_TS_INFO 8 78 79 METHOD_DATA md; 80 KRB_ERROR error; 81 AS_REP as_rep; 82 EncKDCRepPart enc_part; 83 84 krb5_prompter_fct prompter; 85 void *prompter_data; 86 87 struct pa_info_data *ppaid; 88 struct fast_state { 89 enum PA_FX_FAST_REQUEST_enum type; 90 unsigned int flags; 91 #define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1 92 #define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2 93 #define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4 94 #define KRB5_FAST_REPLY_REPLY_VERIFED 8 95 #define KRB5_FAST_STRONG 16 96 #define KRB5_FAST_EXPECTED 32 /* in exchange with KDC, fast was discovered */ 97 #define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */ 98 #define KRB5_FAST_DISABLED 128 99 #define KRB5_FAST_AP_ARMOR_SERVICE 256 100 krb5_keyblock *reply_key; 101 krb5_ccache armor_ccache; 102 krb5_principal armor_service; 103 krb5_crypto armor_crypto; 104 krb5_keyblock armor_key; 105 krb5_keyblock *strengthen_key; 106 } fast_state; 107 } krb5_get_init_creds_ctx; 108 109 110 struct pa_info_data { 111 krb5_enctype etype; 112 krb5_salt salt; 113 krb5_data *s2kparams; 114 }; 115 116 static void 117 free_paid(krb5_context context, struct pa_info_data *ppaid) 118 { 119 krb5_free_salt(context, ppaid->salt); 120 if (ppaid->s2kparams) 121 krb5_free_data(context, ppaid->s2kparams); 122 } 123 124 static krb5_error_code KRB5_CALLCONV 125 default_s2k_func(krb5_context context, krb5_enctype type, 126 krb5_const_pointer keyseed, 127 krb5_salt salt, krb5_data *s2kparms, 128 krb5_keyblock **key) 129 { 130 krb5_error_code ret; 131 krb5_data password; 132 krb5_data opaque; 133 134 _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func"); 135 136 password.data = rk_UNCONST(keyseed); 137 password.length = strlen(keyseed); 138 if (s2kparms) 139 opaque = *s2kparms; 140 else 141 krb5_data_zero(&opaque); 142 143 *key = malloc(sizeof(**key)); 144 if (*key == NULL) 145 return ENOMEM; 146 ret = krb5_string_to_key_data_salt_opaque(context, type, password, 147 salt, opaque, *key); 148 if (ret) { 149 free(*key); 150 *key = NULL; 151 } 152 return ret; 153 } 154 155 static void 156 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx) 157 { 158 if (ctx->etypes) 159 free(ctx->etypes); 160 if (ctx->pre_auth_types) 161 free (ctx->pre_auth_types); 162 if (ctx->in_tkt_service) 163 free(ctx->in_tkt_service); 164 if (ctx->keytab_data) 165 free(ctx->keytab_data); 166 if (ctx->password) { 167 memset(ctx->password, 0, strlen(ctx->password)); 168 free(ctx->password); 169 } 170 /* 171 * FAST state (we don't close the armor_ccache because we might have 172 * to destroy it, and how would we know? also, the caller should 173 * take care of cleaning up the armor_ccache). 174 */ 175 if (ctx->fast_state.armor_service) 176 krb5_free_principal(context, ctx->fast_state.armor_service); 177 if (ctx->fast_state.armor_crypto) 178 krb5_crypto_destroy(context, ctx->fast_state.armor_crypto); 179 if (ctx->fast_state.strengthen_key) 180 krb5_free_keyblock(context, ctx->fast_state.strengthen_key); 181 krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key); 182 183 krb5_data_free(&ctx->req_buffer); 184 krb5_free_cred_contents(context, &ctx->cred); 185 free_METHOD_DATA(&ctx->md); 186 free_AS_REP(&ctx->as_rep); 187 free_EncKDCRepPart(&ctx->enc_part); 188 free_KRB_ERROR(&ctx->error); 189 free_AS_REQ(&ctx->as_req); 190 if (ctx->ppaid) { 191 free_paid(context, ctx->ppaid); 192 free(ctx->ppaid); 193 } 194 memset(ctx, 0, sizeof(*ctx)); 195 } 196 197 static int 198 get_config_time (krb5_context context, 199 const char *realm, 200 const char *name, 201 int def) 202 { 203 int ret; 204 205 ret = krb5_config_get_time (context, NULL, 206 "realms", 207 realm, 208 name, 209 NULL); 210 if (ret >= 0) 211 return ret; 212 ret = krb5_config_get_time (context, NULL, 213 "libdefaults", 214 name, 215 NULL); 216 if (ret >= 0) 217 return ret; 218 return def; 219 } 220 221 static krb5_error_code 222 init_cred (krb5_context context, 223 krb5_creds *cred, 224 krb5_principal client, 225 krb5_deltat start_time, 226 krb5_get_init_creds_opt *options) 227 { 228 krb5_error_code ret; 229 int tmp; 230 krb5_timestamp now; 231 232 krb5_timeofday (context, &now); 233 234 memset (cred, 0, sizeof(*cred)); 235 236 if (client) 237 ret = krb5_copy_principal(context, client, &cred->client); 238 else 239 ret = krb5_get_default_principal(context, &cred->client); 240 if (ret) 241 goto out; 242 243 if (start_time) 244 cred->times.starttime = now + start_time; 245 246 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE) 247 tmp = options->tkt_life; 248 else 249 tmp = KRB5_TKT_LIFETIME_DEFAULT; 250 cred->times.endtime = now + tmp; 251 252 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) { 253 if (options->renew_life > 0) 254 tmp = options->renew_life; 255 else 256 tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT; 257 cred->times.renew_till = now + tmp; 258 } 259 260 return 0; 261 262 out: 263 krb5_free_cred_contents (context, cred); 264 return ret; 265 } 266 267 /* 268 * Print a message (str) to the user about the expiration in `lr' 269 */ 270 271 static void 272 report_expiration (krb5_context context, 273 krb5_prompter_fct prompter, 274 krb5_data *data, 275 const char *str, 276 time_t now) 277 { 278 char *p = NULL; 279 280 if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL) 281 return; 282 (*prompter)(context, data, NULL, p, 0, NULL); 283 free(p); 284 } 285 286 /* 287 * Check the context, and in the case there is a expiration warning, 288 * use the prompter to print the warning. 289 * 290 * @param context A Kerberos 5 context. 291 * @param options An GIC options structure 292 * @param ctx The krb5_init_creds_context check for expiration. 293 */ 294 295 krb5_error_code 296 krb5_process_last_request(krb5_context context, 297 krb5_get_init_creds_opt *options, 298 krb5_init_creds_context ctx) 299 { 300 krb5_const_realm realm; 301 LastReq *lr; 302 krb5_boolean reported = FALSE; 303 krb5_timestamp sec; 304 time_t t; 305 size_t i; 306 307 /* 308 * First check if there is a API consumer. 309 */ 310 311 realm = krb5_principal_get_realm (context, ctx->cred.client); 312 lr = &ctx->enc_part.last_req; 313 314 if (options && options->opt_private && options->opt_private->lr.func) { 315 krb5_last_req_entry **lre; 316 317 lre = calloc(lr->len + 1, sizeof(*lre)); 318 if (lre == NULL) 319 return krb5_enomem(context); 320 for (i = 0; i < lr->len; i++) { 321 lre[i] = calloc(1, sizeof(*lre[i])); 322 if (lre[i] == NULL) 323 break; 324 lre[i]->lr_type = lr->val[i].lr_type; 325 lre[i]->value = lr->val[i].lr_value; 326 } 327 328 (*options->opt_private->lr.func)(context, lre, 329 options->opt_private->lr.ctx); 330 331 for (i = 0; i < lr->len; i++) 332 free(lre[i]); 333 free(lre); 334 } 335 336 /* 337 * Now check if we should prompt the user 338 */ 339 340 if (ctx->prompter == NULL) 341 return 0; 342 343 krb5_timeofday (context, &sec); 344 345 t = sec + get_config_time (context, 346 realm, 347 "warn_pwexpire", 348 7 * 24 * 60 * 60); 349 350 for (i = 0; i < lr->len; ++i) { 351 if (lr->val[i].lr_value <= t) { 352 switch (lr->val[i].lr_type) { 353 case LR_PW_EXPTIME : 354 report_expiration(context, ctx->prompter, 355 ctx->prompter_data, 356 "Your password will expire at ", 357 lr->val[i].lr_value); 358 reported = TRUE; 359 break; 360 case LR_ACCT_EXPTIME : 361 report_expiration(context, ctx->prompter, 362 ctx->prompter_data, 363 "Your account will expire at ", 364 lr->val[i].lr_value); 365 reported = TRUE; 366 break; 367 default: 368 break; 369 } 370 } 371 } 372 373 if (!reported 374 && ctx->enc_part.key_expiration 375 && *ctx->enc_part.key_expiration <= t) { 376 report_expiration(context, ctx->prompter, 377 ctx->prompter_data, 378 "Your password/account will expire at ", 379 *ctx->enc_part.key_expiration); 380 } 381 return 0; 382 } 383 384 static krb5_addresses no_addrs = { 0, NULL }; 385 386 static krb5_error_code 387 get_init_creds_common(krb5_context context, 388 krb5_principal client, 389 krb5_deltat start_time, 390 krb5_get_init_creds_opt *options, 391 krb5_init_creds_context ctx) 392 { 393 krb5_get_init_creds_opt *default_opt = NULL; 394 krb5_error_code ret; 395 krb5_enctype *etypes; 396 krb5_preauthtype *pre_auth_types; 397 398 memset(ctx, 0, sizeof(*ctx)); 399 400 if (options == NULL) { 401 const char *realm = krb5_principal_get_realm(context, client); 402 403 krb5_get_init_creds_opt_alloc (context, &default_opt); 404 options = default_opt; 405 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options); 406 } 407 408 if (options->opt_private) { 409 if (options->opt_private->password) { 410 ret = krb5_init_creds_set_password(context, ctx, 411 options->opt_private->password); 412 if (ret) 413 goto out; 414 } 415 416 ctx->keyproc = options->opt_private->key_proc; 417 ctx->req_pac = options->opt_private->req_pac; 418 ctx->pk_init_ctx = options->opt_private->pk_init_ctx; 419 ctx->ic_flags = options->opt_private->flags; 420 } else 421 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET; 422 423 if (ctx->keyproc == NULL) 424 ctx->keyproc = default_s2k_func; 425 426 /* Enterprise name implicitly turns on canonicalize */ 427 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) || 428 krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL) 429 ctx->flags.canonicalize = 1; 430 431 ctx->pre_auth_types = NULL; 432 ctx->addrs = NULL; 433 ctx->etypes = NULL; 434 ctx->pre_auth_types = NULL; 435 436 ret = init_cred(context, &ctx->cred, client, start_time, options); 437 if (ret) { 438 if (default_opt) 439 krb5_get_init_creds_opt_free(context, default_opt); 440 return ret; 441 } 442 443 ret = krb5_init_creds_set_service(context, ctx, NULL); 444 if (ret) 445 goto out; 446 447 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) 448 ctx->flags.forwardable = options->forwardable; 449 450 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) 451 ctx->flags.proxiable = options->proxiable; 452 453 if (start_time) 454 ctx->flags.postdated = 1; 455 if (ctx->cred.times.renew_till) 456 ctx->flags.renewable = 1; 457 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) { 458 ctx->addrs = options->address_list; 459 } else if (options->opt_private) { 460 switch (options->opt_private->addressless) { 461 case KRB5_INIT_CREDS_TRISTATE_UNSET: 462 #if KRB5_ADDRESSLESS_DEFAULT == TRUE 463 ctx->addrs = &no_addrs; 464 #else 465 ctx->addrs = NULL; 466 #endif 467 break; 468 case KRB5_INIT_CREDS_TRISTATE_FALSE: 469 ctx->addrs = NULL; 470 break; 471 case KRB5_INIT_CREDS_TRISTATE_TRUE: 472 ctx->addrs = &no_addrs; 473 break; 474 } 475 } 476 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { 477 if (ctx->etypes) 478 free(ctx->etypes); 479 480 etypes = malloc((options->etype_list_length + 1) 481 * sizeof(krb5_enctype)); 482 if (etypes == NULL) { 483 ret = krb5_enomem(context); 484 goto out; 485 } 486 memcpy (etypes, options->etype_list, 487 options->etype_list_length * sizeof(krb5_enctype)); 488 etypes[options->etype_list_length] = ETYPE_NULL; 489 ctx->etypes = etypes; 490 } 491 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { 492 pre_auth_types = malloc((options->preauth_list_length + 1) 493 * sizeof(krb5_preauthtype)); 494 if (pre_auth_types == NULL) { 495 ret = krb5_enomem(context); 496 goto out; 497 } 498 memcpy (pre_auth_types, options->preauth_list, 499 options->preauth_list_length * sizeof(krb5_preauthtype)); 500 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE; 501 ctx->pre_auth_types = pre_auth_types; 502 } 503 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) 504 ctx->flags.request_anonymous = options->anonymous; 505 if (default_opt) 506 krb5_get_init_creds_opt_free(context, default_opt); 507 return 0; 508 out: 509 if (default_opt) 510 krb5_get_init_creds_opt_free(context, default_opt); 511 return ret; 512 } 513 514 static krb5_error_code 515 change_password (krb5_context context, 516 krb5_principal client, 517 const char *password, 518 char *newpw, 519 size_t newpw_sz, 520 krb5_prompter_fct prompter, 521 void *data, 522 krb5_get_init_creds_opt *old_options) 523 { 524 krb5_prompt prompts[2]; 525 krb5_error_code ret; 526 krb5_creds cpw_cred; 527 char buf1[BUFSIZ], buf2[BUFSIZ]; 528 krb5_data password_data[2]; 529 int result_code; 530 krb5_data result_code_string; 531 krb5_data result_string; 532 char *p; 533 krb5_get_init_creds_opt *options; 534 535 heim_assert(prompter != NULL, "unexpected NULL prompter"); 536 537 memset (&cpw_cred, 0, sizeof(cpw_cred)); 538 539 ret = krb5_get_init_creds_opt_alloc(context, &options); 540 if (ret) 541 return ret; 542 krb5_get_init_creds_opt_set_tkt_life (options, 60); 543 krb5_get_init_creds_opt_set_forwardable (options, FALSE); 544 krb5_get_init_creds_opt_set_proxiable (options, FALSE); 545 if (old_options && 546 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) 547 krb5_get_init_creds_opt_set_preauth_list(options, 548 old_options->preauth_list, 549 old_options->preauth_list_length); 550 if (old_options && 551 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT)) 552 krb5_get_init_creds_opt_set_change_password_prompt(options, 553 old_options->change_password_prompt); 554 555 krb5_data_zero (&result_code_string); 556 krb5_data_zero (&result_string); 557 558 ret = krb5_get_init_creds_password (context, 559 &cpw_cred, 560 client, 561 password, 562 prompter, 563 data, 564 0, 565 "kadmin/changepw", 566 options); 567 krb5_get_init_creds_opt_free(context, options); 568 if (ret) 569 goto out; 570 571 for(;;) { 572 password_data[0].data = buf1; 573 password_data[0].length = sizeof(buf1); 574 575 prompts[0].hidden = 1; 576 prompts[0].prompt = "New password: "; 577 prompts[0].reply = &password_data[0]; 578 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD; 579 580 password_data[1].data = buf2; 581 password_data[1].length = sizeof(buf2); 582 583 prompts[1].hidden = 1; 584 prompts[1].prompt = "Repeat new password: "; 585 prompts[1].reply = &password_data[1]; 586 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN; 587 588 ret = (*prompter) (context, data, NULL, "Changing password", 589 2, prompts); 590 if (ret) { 591 memset (buf1, 0, sizeof(buf1)); 592 memset (buf2, 0, sizeof(buf2)); 593 goto out; 594 } 595 596 if (strcmp (buf1, buf2) == 0) 597 break; 598 memset (buf1, 0, sizeof(buf1)); 599 memset (buf2, 0, sizeof(buf2)); 600 } 601 602 ret = krb5_set_password (context, 603 &cpw_cred, 604 buf1, 605 client, 606 &result_code, 607 &result_code_string, 608 &result_string); 609 if (ret) 610 goto out; 611 if (asprintf(&p, "%s: %.*s\n", 612 result_code ? "Error" : "Success", 613 (int)result_string.length, 614 result_string.length > 0 ? (char*)result_string.data : "") < 0) 615 { 616 ret = ENOMEM; 617 goto out; 618 } 619 620 /* return the result */ 621 (*prompter) (context, data, NULL, p, 0, NULL); 622 623 free (p); 624 if (result_code == 0) { 625 strlcpy (newpw, buf1, newpw_sz); 626 ret = 0; 627 } else { 628 ret = ENOTTY; 629 krb5_set_error_message(context, ret, 630 N_("failed changing password", "")); 631 } 632 633 out: 634 memset (buf1, 0, sizeof(buf1)); 635 memset (buf2, 0, sizeof(buf2)); 636 krb5_data_free (&result_string); 637 krb5_data_free (&result_code_string); 638 krb5_free_cred_contents (context, &cpw_cred); 639 return ret; 640 } 641 642 643 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 644 krb5_keyblock_key_proc (krb5_context context, 645 krb5_keytype type, 646 krb5_data *salt, 647 krb5_const_pointer keyseed, 648 krb5_keyblock **key) 649 { 650 return krb5_copy_keyblock (context, keyseed, key); 651 } 652 653 /* 654 * 655 */ 656 657 static krb5_error_code 658 init_as_req (krb5_context context, 659 KDCOptions opts, 660 const krb5_creds *creds, 661 const krb5_addresses *addrs, 662 const krb5_enctype *etypes, 663 AS_REQ *a) 664 { 665 krb5_error_code ret; 666 667 memset(a, 0, sizeof(*a)); 668 669 a->pvno = 5; 670 a->msg_type = krb_as_req; 671 a->req_body.kdc_options = opts; 672 a->req_body.cname = malloc(sizeof(*a->req_body.cname)); 673 if (a->req_body.cname == NULL) { 674 ret = krb5_enomem(context); 675 goto fail; 676 } 677 a->req_body.sname = malloc(sizeof(*a->req_body.sname)); 678 if (a->req_body.sname == NULL) { 679 ret = krb5_enomem(context); 680 goto fail; 681 } 682 683 ret = _krb5_principal2principalname (a->req_body.cname, creds->client); 684 if (ret) 685 goto fail; 686 ret = copy_Realm(&creds->client->realm, &a->req_body.realm); 687 if (ret) 688 goto fail; 689 690 ret = _krb5_principal2principalname (a->req_body.sname, creds->server); 691 if (ret) 692 goto fail; 693 694 if(creds->times.starttime) { 695 a->req_body.from = malloc(sizeof(*a->req_body.from)); 696 if (a->req_body.from == NULL) { 697 ret = krb5_enomem(context); 698 goto fail; 699 } 700 *a->req_body.from = creds->times.starttime; 701 } 702 if(creds->times.endtime){ 703 if ((ALLOC(a->req_body.till, 1)) != NULL) 704 *a->req_body.till = creds->times.endtime; 705 else { 706 ret = krb5_enomem(context); 707 goto fail; 708 } 709 } 710 if(creds->times.renew_till){ 711 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime)); 712 if (a->req_body.rtime == NULL) { 713 ret = krb5_enomem(context); 714 goto fail; 715 } 716 *a->req_body.rtime = creds->times.renew_till; 717 } 718 a->req_body.nonce = 0; 719 ret = _krb5_init_etype(context, 720 KRB5_PDU_AS_REQUEST, 721 &a->req_body.etype.len, 722 &a->req_body.etype.val, 723 etypes); 724 if (ret) 725 goto fail; 726 727 /* 728 * This means no addresses 729 */ 730 731 if (addrs && addrs->len == 0) { 732 a->req_body.addresses = NULL; 733 } else { 734 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses)); 735 if (a->req_body.addresses == NULL) { 736 ret = krb5_enomem(context); 737 goto fail; 738 } 739 740 if (addrs) 741 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses); 742 else { 743 ret = krb5_get_all_client_addrs (context, a->req_body.addresses); 744 if(ret == 0 && a->req_body.addresses->len == 0) { 745 free(a->req_body.addresses); 746 a->req_body.addresses = NULL; 747 } 748 } 749 if (ret) 750 goto fail; 751 } 752 753 a->req_body.enc_authorization_data = NULL; 754 a->req_body.additional_tickets = NULL; 755 756 a->padata = NULL; 757 758 return 0; 759 fail: 760 free_AS_REQ(a); 761 memset(a, 0, sizeof(*a)); 762 return ret; 763 } 764 765 766 static krb5_error_code 767 set_paid(struct pa_info_data *paid, krb5_context context, 768 krb5_enctype etype, 769 krb5_salttype salttype, void *salt_string, size_t salt_len, 770 krb5_data *s2kparams) 771 { 772 paid->etype = etype; 773 paid->salt.salttype = salttype; 774 paid->salt.saltvalue.data = malloc(salt_len + 1); 775 if (paid->salt.saltvalue.data == NULL) { 776 krb5_clear_error_message(context); 777 return ENOMEM; 778 } 779 memcpy(paid->salt.saltvalue.data, salt_string, salt_len); 780 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0'; 781 paid->salt.saltvalue.length = salt_len; 782 if (s2kparams) { 783 krb5_error_code ret; 784 785 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams); 786 if (ret) { 787 krb5_clear_error_message(context); 788 krb5_free_salt(context, paid->salt); 789 return ret; 790 } 791 } else 792 paid->s2kparams = NULL; 793 794 return 0; 795 } 796 797 static struct pa_info_data * 798 pa_etype_info2(krb5_context context, 799 const krb5_principal client, 800 const AS_REQ *asreq, 801 struct pa_info_data *paid, 802 heim_octet_string *data) 803 { 804 krb5_error_code ret; 805 ETYPE_INFO2 e; 806 size_t sz; 807 size_t i, j; 808 809 memset(&e, 0, sizeof(e)); 810 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz); 811 if (ret) 812 goto out; 813 if (e.len == 0) 814 goto out; 815 for (j = 0; j < asreq->req_body.etype.len; j++) { 816 for (i = 0; i < e.len; i++) { 817 if (asreq->req_body.etype.val[j] == e.val[i].etype) { 818 krb5_salt salt; 819 if (e.val[i].salt == NULL) 820 ret = krb5_get_pw_salt(context, client, &salt); 821 else { 822 salt.saltvalue.data = *e.val[i].salt; 823 salt.saltvalue.length = strlen(*e.val[i].salt); 824 ret = 0; 825 } 826 if (ret == 0) 827 ret = set_paid(paid, context, e.val[i].etype, 828 KRB5_PW_SALT, 829 salt.saltvalue.data, 830 salt.saltvalue.length, 831 e.val[i].s2kparams); 832 if (e.val[i].salt == NULL) 833 krb5_free_salt(context, salt); 834 if (ret == 0) { 835 free_ETYPE_INFO2(&e); 836 return paid; 837 } 838 } 839 } 840 } 841 out: 842 free_ETYPE_INFO2(&e); 843 return NULL; 844 } 845 846 static struct pa_info_data * 847 pa_etype_info(krb5_context context, 848 const krb5_principal client, 849 const AS_REQ *asreq, 850 struct pa_info_data *paid, 851 heim_octet_string *data) 852 { 853 krb5_error_code ret; 854 ETYPE_INFO e; 855 size_t sz; 856 size_t i, j; 857 858 memset(&e, 0, sizeof(e)); 859 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz); 860 if (ret) 861 goto out; 862 if (e.len == 0) 863 goto out; 864 for (j = 0; j < asreq->req_body.etype.len; j++) { 865 for (i = 0; i < e.len; i++) { 866 if (asreq->req_body.etype.val[j] == e.val[i].etype) { 867 krb5_salt salt; 868 salt.salttype = KRB5_PW_SALT; 869 if (e.val[i].salt == NULL) 870 ret = krb5_get_pw_salt(context, client, &salt); 871 else { 872 salt.saltvalue = *e.val[i].salt; 873 ret = 0; 874 } 875 if (e.val[i].salttype) 876 salt.salttype = *e.val[i].salttype; 877 if (ret == 0) { 878 ret = set_paid(paid, context, e.val[i].etype, 879 salt.salttype, 880 salt.saltvalue.data, 881 salt.saltvalue.length, 882 NULL); 883 if (e.val[i].salt == NULL) 884 krb5_free_salt(context, salt); 885 } 886 if (ret == 0) { 887 free_ETYPE_INFO(&e); 888 return paid; 889 } 890 } 891 } 892 } 893 out: 894 free_ETYPE_INFO(&e); 895 return NULL; 896 } 897 898 static struct pa_info_data * 899 pa_pw_or_afs3_salt(krb5_context context, 900 const krb5_principal client, 901 const AS_REQ *asreq, 902 struct pa_info_data *paid, 903 heim_octet_string *data) 904 { 905 krb5_error_code ret; 906 if (paid->etype == KRB5_ENCTYPE_NULL) 907 return NULL; 908 ret = set_paid(paid, context, 909 paid->etype, 910 paid->salt.salttype, 911 data->data, 912 data->length, 913 NULL); 914 if (ret) 915 return NULL; 916 return paid; 917 } 918 919 920 struct pa_info { 921 krb5_preauthtype type; 922 struct pa_info_data *(*salt_info)(krb5_context, 923 const krb5_principal, 924 const AS_REQ *, 925 struct pa_info_data *, 926 heim_octet_string *); 927 }; 928 929 static struct pa_info pa_prefs[] = { 930 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 }, 931 { KRB5_PADATA_ETYPE_INFO, pa_etype_info }, 932 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt }, 933 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt } 934 }; 935 936 static PA_DATA * 937 find_pa_data(const METHOD_DATA *md, unsigned type) 938 { 939 size_t i; 940 if (md == NULL) 941 return NULL; 942 for (i = 0; i < md->len; i++) 943 if (md->val[i].padata_type == type) 944 return &md->val[i]; 945 return NULL; 946 } 947 948 static struct pa_info_data * 949 process_pa_info(krb5_context context, 950 const krb5_principal client, 951 const AS_REQ *asreq, 952 struct pa_info_data *paid, 953 METHOD_DATA *md) 954 { 955 struct pa_info_data *p = NULL; 956 size_t i; 957 958 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) { 959 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type); 960 if (pa == NULL) 961 continue; 962 paid->salt.salttype = (krb5_salttype)pa_prefs[i].type; 963 p = (*pa_prefs[i].salt_info)(context, client, asreq, 964 paid, &pa->padata_value); 965 } 966 return p; 967 } 968 969 static krb5_error_code 970 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md, 971 krb5_enctype etype, krb5_keyblock *key) 972 { 973 PA_ENC_TS_ENC p; 974 unsigned char *buf; 975 size_t buf_size; 976 size_t len = 0; 977 EncryptedData encdata; 978 krb5_error_code ret; 979 int32_t usec; 980 int usec2; 981 krb5_crypto crypto; 982 983 krb5_us_timeofday (context, &p.patimestamp, &usec); 984 usec2 = usec; 985 p.pausec = &usec2; 986 987 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); 988 if (ret) 989 return ret; 990 if(buf_size != len) 991 krb5_abortx(context, "internal error in ASN.1 encoder"); 992 993 ret = krb5_crypto_init(context, key, 0, &crypto); 994 if (ret) { 995 free(buf); 996 return ret; 997 } 998 ret = krb5_encrypt_EncryptedData(context, 999 crypto, 1000 KRB5_KU_PA_ENC_TIMESTAMP, 1001 buf, 1002 len, 1003 0, 1004 &encdata); 1005 free(buf); 1006 krb5_crypto_destroy(context, crypto); 1007 if (ret) 1008 return ret; 1009 1010 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); 1011 free_EncryptedData(&encdata); 1012 if (ret) 1013 return ret; 1014 if(buf_size != len) 1015 krb5_abortx(context, "internal error in ASN.1 encoder"); 1016 1017 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len); 1018 if (ret) 1019 free(buf); 1020 return ret; 1021 } 1022 1023 static krb5_error_code 1024 add_enc_ts_padata(krb5_context context, 1025 METHOD_DATA *md, 1026 krb5_principal client, 1027 krb5_s2k_proc keyproc, 1028 krb5_const_pointer keyseed, 1029 krb5_enctype *enctypes, 1030 unsigned netypes, 1031 krb5_salt *salt, 1032 krb5_data *s2kparams) 1033 { 1034 krb5_error_code ret; 1035 krb5_salt salt2; 1036 krb5_enctype *ep; 1037 size_t i; 1038 1039 if(salt == NULL) { 1040 /* default to standard salt */ 1041 ret = krb5_get_pw_salt (context, client, &salt2); 1042 if (ret) 1043 return ret; 1044 salt = &salt2; 1045 } 1046 if (!enctypes) { 1047 enctypes = context->etypes; 1048 netypes = 0; 1049 for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++) 1050 netypes++; 1051 } 1052 1053 for (i = 0; i < netypes; ++i) { 1054 krb5_keyblock *key; 1055 1056 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]); 1057 1058 ret = (*keyproc)(context, enctypes[i], keyseed, 1059 *salt, s2kparams, &key); 1060 if (ret) 1061 continue; 1062 ret = make_pa_enc_timestamp (context, md, enctypes[i], key); 1063 krb5_free_keyblock (context, key); 1064 if (ret) 1065 return ret; 1066 } 1067 if(salt == &salt2) 1068 krb5_free_salt(context, salt2); 1069 return 0; 1070 } 1071 1072 static krb5_error_code 1073 pa_data_to_md_ts_enc(krb5_context context, 1074 const AS_REQ *a, 1075 const krb5_principal client, 1076 krb5_get_init_creds_ctx *ctx, 1077 struct pa_info_data *ppaid, 1078 METHOD_DATA *md) 1079 { 1080 if (ctx->keyproc == NULL || ctx->keyseed == NULL) 1081 return 0; 1082 1083 if (ppaid) { 1084 add_enc_ts_padata(context, md, client, 1085 ctx->keyproc, ctx->keyseed, 1086 &ppaid->etype, 1, 1087 &ppaid->salt, ppaid->s2kparams); 1088 } else { 1089 krb5_salt salt; 1090 1091 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt"); 1092 1093 /* make a v5 salted pa-data */ 1094 add_enc_ts_padata(context, md, client, 1095 ctx->keyproc, ctx->keyseed, 1096 a->req_body.etype.val, a->req_body.etype.len, 1097 NULL, NULL); 1098 1099 /* make a v4 salted pa-data */ 1100 salt.salttype = KRB5_PW_SALT; 1101 krb5_data_zero(&salt.saltvalue); 1102 add_enc_ts_padata(context, md, client, 1103 ctx->keyproc, ctx->keyseed, 1104 a->req_body.etype.val, a->req_body.etype.len, 1105 &salt, NULL); 1106 } 1107 return 0; 1108 } 1109 1110 static krb5_error_code 1111 pa_data_to_key_plain(krb5_context context, 1112 const krb5_principal client, 1113 krb5_get_init_creds_ctx *ctx, 1114 krb5_salt salt, 1115 krb5_data *s2kparams, 1116 krb5_enctype etype, 1117 krb5_keyblock **key) 1118 { 1119 krb5_error_code ret; 1120 1121 ret = (*ctx->keyproc)(context, etype, ctx->keyseed, 1122 salt, s2kparams, key); 1123 return ret; 1124 } 1125 1126 1127 static krb5_error_code 1128 pa_data_to_md_pkinit(krb5_context context, 1129 const AS_REQ *a, 1130 const krb5_principal client, 1131 int win2k, 1132 krb5_get_init_creds_ctx *ctx, 1133 METHOD_DATA *md) 1134 { 1135 if (ctx->pk_init_ctx == NULL) 1136 return 0; 1137 #ifdef PKINIT 1138 return _krb5_pk_mk_padata(context, 1139 ctx->pk_init_ctx, 1140 ctx->ic_flags, 1141 win2k, 1142 &a->req_body, 1143 ctx->pk_nonce, 1144 md); 1145 #else 1146 krb5_set_error_message(context, EINVAL, 1147 N_("no support for PKINIT compiled in", "")); 1148 return EINVAL; 1149 #endif 1150 } 1151 1152 static krb5_error_code 1153 pa_data_add_pac_request(krb5_context context, 1154 krb5_get_init_creds_ctx *ctx, 1155 METHOD_DATA *md) 1156 { 1157 size_t len = 0, length; 1158 krb5_error_code ret; 1159 PA_PAC_REQUEST req; 1160 void *buf; 1161 1162 switch (ctx->req_pac) { 1163 case KRB5_INIT_CREDS_TRISTATE_UNSET: 1164 return 0; /* don't bother */ 1165 case KRB5_INIT_CREDS_TRISTATE_TRUE: 1166 req.include_pac = 1; 1167 break; 1168 case KRB5_INIT_CREDS_TRISTATE_FALSE: 1169 req.include_pac = 0; 1170 } 1171 1172 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length, 1173 &req, &len, ret); 1174 if (ret) 1175 return ret; 1176 if(len != length) 1177 krb5_abortx(context, "internal error in ASN.1 encoder"); 1178 1179 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len); 1180 if (ret) 1181 free(buf); 1182 1183 return 0; 1184 } 1185 1186 /* 1187 * Assumes caller always will free `out_md', even on error. 1188 */ 1189 1190 static krb5_error_code 1191 process_pa_data_to_md(krb5_context context, 1192 const krb5_creds *creds, 1193 const AS_REQ *a, 1194 krb5_get_init_creds_ctx *ctx, 1195 METHOD_DATA *in_md, 1196 METHOD_DATA **out_md, 1197 krb5_prompter_fct prompter, 1198 void *prompter_data) 1199 { 1200 krb5_error_code ret; 1201 1202 ALLOC(*out_md, 1); 1203 if (*out_md == NULL) 1204 return krb5_enomem(context); 1205 1206 (*out_md)->len = 0; 1207 (*out_md)->val = NULL; 1208 1209 if (_krb5_have_debug(context, 5)) { 1210 unsigned i; 1211 _krb5_debug(context, 5, "KDC send %d patypes", in_md->len); 1212 for (i = 0; i < in_md->len; i++) 1213 _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type); 1214 } 1215 1216 /* 1217 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no 1218 * need to expose our password protecting our PKCS12 key. 1219 */ 1220 1221 if (ctx->pk_init_ctx) { 1222 1223 _krb5_debug(context, 5, "krb5_get_init_creds: " 1224 "prepareing PKINIT padata (%s)", 1225 (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf"); 1226 1227 if (ctx->used_pa_types & USED_PKINIT_W2K) { 1228 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1229 "Already tried pkinit, looping"); 1230 return KRB5_GET_IN_TKT_LOOP; 1231 } 1232 1233 ret = pa_data_to_md_pkinit(context, a, creds->client, 1234 (ctx->used_pa_types & USED_PKINIT), 1235 ctx, *out_md); 1236 if (ret) 1237 return ret; 1238 1239 if (ctx->used_pa_types & USED_PKINIT) 1240 ctx->used_pa_types |= USED_PKINIT_W2K; 1241 else 1242 ctx->used_pa_types |= USED_PKINIT; 1243 1244 } else if (in_md->len != 0) { 1245 struct pa_info_data *paid, *ppaid; 1246 unsigned flag; 1247 1248 paid = calloc(1, sizeof(*paid)); 1249 if (paid == NULL) 1250 return krb5_enomem(context); 1251 1252 paid->etype = KRB5_ENCTYPE_NULL; 1253 ppaid = process_pa_info(context, creds->client, a, paid, in_md); 1254 1255 if (ppaid) 1256 flag = USED_ENC_TS_INFO; 1257 else 1258 flag = USED_ENC_TS_GUESS; 1259 1260 if (ctx->used_pa_types & flag) { 1261 if (ppaid) 1262 free_paid(context, ppaid); 1263 free(paid); 1264 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1265 "Already tried ENC-TS-%s, looping", 1266 flag == USED_ENC_TS_INFO ? "info" : "guess"); 1267 return KRB5_GET_IN_TKT_LOOP; 1268 } 1269 1270 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md); 1271 1272 ctx->used_pa_types |= flag; 1273 1274 if (ppaid) { 1275 if (ctx->ppaid) { 1276 free_paid(context, ctx->ppaid); 1277 free(ctx->ppaid); 1278 } 1279 ctx->ppaid = ppaid; 1280 } else 1281 free(paid); 1282 } 1283 1284 pa_data_add_pac_request(context, ctx, *out_md); 1285 1286 if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) { 1287 ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0); 1288 if (ret) 1289 return ret; 1290 } 1291 1292 if ((*out_md)->len == 0) { 1293 free(*out_md); 1294 *out_md = NULL; 1295 } 1296 1297 return 0; 1298 } 1299 1300 static krb5_error_code 1301 process_pa_data_to_key(krb5_context context, 1302 krb5_get_init_creds_ctx *ctx, 1303 krb5_creds *creds, 1304 AS_REQ *a, 1305 AS_REP *rep, 1306 const krb5_krbhst_info *hi, 1307 krb5_keyblock **key) 1308 { 1309 struct pa_info_data paid, *ppaid = NULL; 1310 krb5_error_code ret; 1311 krb5_enctype etype; 1312 PA_DATA *pa; 1313 1314 memset(&paid, 0, sizeof(paid)); 1315 1316 etype = rep->enc_part.etype; 1317 1318 if (rep->padata) { 1319 paid.etype = etype; 1320 ppaid = process_pa_info(context, creds->client, a, &paid, 1321 rep->padata); 1322 } 1323 if (ppaid == NULL) 1324 ppaid = ctx->ppaid; 1325 if (ppaid == NULL) { 1326 ret = krb5_get_pw_salt (context, creds->client, &paid.salt); 1327 if (ret) 1328 return ret; 1329 paid.etype = etype; 1330 paid.s2kparams = NULL; 1331 ppaid = &paid; 1332 } 1333 1334 pa = NULL; 1335 if (rep->padata) { 1336 int idx = 0; 1337 pa = krb5_find_padata(rep->padata->val, 1338 rep->padata->len, 1339 KRB5_PADATA_PK_AS_REP, 1340 &idx); 1341 if (pa == NULL) { 1342 idx = 0; 1343 pa = krb5_find_padata(rep->padata->val, 1344 rep->padata->len, 1345 KRB5_PADATA_PK_AS_REP_19, 1346 &idx); 1347 } 1348 } 1349 if (pa && ctx->pk_init_ctx) { 1350 #ifdef PKINIT 1351 _krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT"); 1352 1353 ret = _krb5_pk_rd_pa_reply(context, 1354 a->req_body.realm, 1355 ctx->pk_init_ctx, 1356 etype, 1357 hi, 1358 ctx->pk_nonce, 1359 &ctx->req_buffer, 1360 pa, 1361 key); 1362 #else 1363 ret = EINVAL; 1364 krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", "")); 1365 #endif 1366 } else if (ctx->keyseed) { 1367 _krb5_debug(context, 5, "krb5_get_init_creds: using keyproc"); 1368 ret = pa_data_to_key_plain(context, creds->client, ctx, 1369 ppaid->salt, ppaid->s2kparams, etype, key); 1370 } else { 1371 ret = EINVAL; 1372 krb5_set_error_message(context, ret, N_("No usable pa data type", "")); 1373 } 1374 1375 free_paid(context, &paid); 1376 return ret; 1377 } 1378 1379 /** 1380 * Start a new context to get a new initial credential. 1381 * 1382 * @param context A Kerberos 5 context. 1383 * @param client The Kerberos principal to get the credential for, if 1384 * NULL is given, the default principal is used as determined by 1385 * krb5_get_default_principal(). 1386 * @param prompter 1387 * @param prompter_data 1388 * @param start_time the time the ticket should start to be valid or 0 for now. 1389 * @param options a options structure, can be NULL for default options. 1390 * @param rctx A new allocated free with krb5_init_creds_free(). 1391 * 1392 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message(). 1393 * 1394 * @ingroup krb5_credential 1395 */ 1396 1397 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1398 krb5_init_creds_init(krb5_context context, 1399 krb5_principal client, 1400 krb5_prompter_fct prompter, 1401 void *prompter_data, 1402 krb5_deltat start_time, 1403 krb5_get_init_creds_opt *options, 1404 krb5_init_creds_context *rctx) 1405 { 1406 krb5_init_creds_context ctx; 1407 krb5_error_code ret; 1408 1409 *rctx = NULL; 1410 1411 ctx = calloc(1, sizeof(*ctx)); 1412 if (ctx == NULL) 1413 return krb5_enomem(context); 1414 1415 ret = get_init_creds_common(context, client, start_time, options, ctx); 1416 if (ret) { 1417 free(ctx); 1418 return ret; 1419 } 1420 1421 /* Set a new nonce. */ 1422 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce)); 1423 ctx->nonce &= 0x7fffffff; 1424 /* XXX these just needs to be the same when using Windows PK-INIT */ 1425 ctx->pk_nonce = ctx->nonce; 1426 1427 ctx->prompter = prompter; 1428 ctx->prompter_data = prompter_data; 1429 1430 *rctx = ctx; 1431 1432 return ret; 1433 } 1434 1435 /** 1436 * Sets the service that the is requested. This call is only neede for 1437 * special initial tickets, by default the a krbtgt is fetched in the default realm. 1438 * 1439 * @param context a Kerberos 5 context. 1440 * @param ctx a krb5_init_creds_context context. 1441 * @param service the service given as a string, for example 1442 * "kadmind/admin". If NULL, the default krbtgt in the clients 1443 * realm is set. 1444 * 1445 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1446 * @ingroup krb5_credential 1447 */ 1448 1449 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1450 krb5_init_creds_set_service(krb5_context context, 1451 krb5_init_creds_context ctx, 1452 const char *service) 1453 { 1454 krb5_const_realm client_realm; 1455 krb5_principal principal; 1456 krb5_error_code ret; 1457 1458 client_realm = krb5_principal_get_realm (context, ctx->cred.client); 1459 1460 if (service) { 1461 ret = krb5_parse_name (context, service, &principal); 1462 if (ret) 1463 return ret; 1464 krb5_principal_set_realm (context, principal, client_realm); 1465 } else { 1466 ret = krb5_make_principal(context, &principal, 1467 client_realm, KRB5_TGS_NAME, client_realm, 1468 NULL); 1469 if (ret) 1470 return ret; 1471 } 1472 1473 /* 1474 * This is for Windows RODC that are picky about what name type 1475 * the server principal have, and the really strange part is that 1476 * they are picky about the AS-REQ name type and not the TGS-REQ 1477 * later. Oh well. 1478 */ 1479 1480 if (krb5_principal_is_krbtgt(context, principal)) 1481 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST); 1482 1483 krb5_free_principal(context, ctx->cred.server); 1484 ctx->cred.server = principal; 1485 1486 return 0; 1487 } 1488 1489 /** 1490 * Sets the password that will use for the request. 1491 * 1492 * @param context a Kerberos 5 context. 1493 * @param ctx ctx krb5_init_creds_context context. 1494 * @param password the password to use. 1495 * 1496 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1497 * @ingroup krb5_credential 1498 */ 1499 1500 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1501 krb5_init_creds_set_password(krb5_context context, 1502 krb5_init_creds_context ctx, 1503 const char *password) 1504 { 1505 if (ctx->password) { 1506 memset(ctx->password, 0, strlen(ctx->password)); 1507 free(ctx->password); 1508 } 1509 if (password) { 1510 ctx->password = strdup(password); 1511 if (ctx->password == NULL) 1512 return krb5_enomem(context); 1513 ctx->keyseed = (void *) ctx->password; 1514 } else { 1515 ctx->keyseed = NULL; 1516 ctx->password = NULL; 1517 } 1518 1519 return 0; 1520 } 1521 1522 static krb5_error_code KRB5_CALLCONV 1523 keytab_key_proc(krb5_context context, krb5_enctype enctype, 1524 krb5_const_pointer keyseed, 1525 krb5_salt salt, krb5_data *s2kparms, 1526 krb5_keyblock **key) 1527 { 1528 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed); 1529 krb5_keytab keytab = args->keytab; 1530 krb5_principal principal = args->principal; 1531 krb5_error_code ret; 1532 krb5_keytab real_keytab; 1533 krb5_keytab_entry entry; 1534 1535 if(keytab == NULL) 1536 krb5_kt_default(context, &real_keytab); 1537 else 1538 real_keytab = keytab; 1539 1540 ret = krb5_kt_get_entry (context, real_keytab, principal, 1541 0, enctype, &entry); 1542 1543 if (keytab == NULL) 1544 krb5_kt_close (context, real_keytab); 1545 1546 if (ret) 1547 return ret; 1548 1549 ret = krb5_copy_keyblock (context, &entry.keyblock, key); 1550 krb5_kt_free_entry(context, &entry); 1551 return ret; 1552 } 1553 1554 1555 /** 1556 * Set the keytab to use for authentication. 1557 * 1558 * @param context a Kerberos 5 context. 1559 * @param ctx ctx krb5_init_creds_context context. 1560 * @param keytab the keytab to read the key from. 1561 * 1562 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). 1563 * @ingroup krb5_credential 1564 */ 1565 1566 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1567 krb5_init_creds_set_keytab(krb5_context context, 1568 krb5_init_creds_context ctx, 1569 krb5_keytab keytab) 1570 { 1571 krb5_keytab_key_proc_args *a; 1572 krb5_keytab_entry entry; 1573 krb5_kt_cursor cursor; 1574 krb5_enctype *etypes = NULL; 1575 krb5_error_code ret; 1576 size_t netypes = 0; 1577 int kvno = 0, found = 0; 1578 1579 a = malloc(sizeof(*a)); 1580 if (a == NULL) 1581 return krb5_enomem(context); 1582 1583 a->principal = ctx->cred.client; 1584 a->keytab = keytab; 1585 1586 ctx->keytab_data = a; 1587 ctx->keyseed = (void *)a; 1588 ctx->keyproc = keytab_key_proc; 1589 1590 /* 1591 * We need to the KDC what enctypes we support for this keytab, 1592 * esp if the keytab is really a password based entry, then the 1593 * KDC might have more enctypes in the database then what we have 1594 * in the keytab. 1595 */ 1596 1597 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 1598 if(ret) 1599 goto out; 1600 1601 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){ 1602 void *ptr; 1603 1604 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client)) 1605 goto next; 1606 1607 found = 1; 1608 1609 /* check if we ahve this kvno already */ 1610 if (entry.vno > kvno) { 1611 /* remove old list of etype */ 1612 if (etypes) 1613 free(etypes); 1614 etypes = NULL; 1615 netypes = 0; 1616 kvno = entry.vno; 1617 } else if (entry.vno != kvno) 1618 goto next; 1619 1620 /* check if enctype is supported */ 1621 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0) 1622 goto next; 1623 1624 /* add enctype to supported list */ 1625 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2)); 1626 if (ptr == NULL) { 1627 free(etypes); 1628 ret = krb5_enomem(context); 1629 goto out; 1630 } 1631 1632 etypes = ptr; 1633 etypes[netypes] = entry.keyblock.keytype; 1634 etypes[netypes + 1] = ETYPE_NULL; 1635 netypes++; 1636 next: 1637 krb5_kt_free_entry(context, &entry); 1638 } 1639 krb5_kt_end_seq_get(context, keytab, &cursor); 1640 1641 if (etypes) { 1642 if (ctx->etypes) 1643 free(ctx->etypes); 1644 ctx->etypes = etypes; 1645 } 1646 1647 out: 1648 if (!found) { 1649 if (ret == 0) 1650 ret = KRB5_KT_NOTFOUND; 1651 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0); 1652 } 1653 1654 return ret; 1655 } 1656 1657 static krb5_error_code KRB5_CALLCONV 1658 keyblock_key_proc(krb5_context context, krb5_enctype enctype, 1659 krb5_const_pointer keyseed, 1660 krb5_salt salt, krb5_data *s2kparms, 1661 krb5_keyblock **key) 1662 { 1663 return krb5_copy_keyblock (context, keyseed, key); 1664 } 1665 1666 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1667 krb5_init_creds_set_keyblock(krb5_context context, 1668 krb5_init_creds_context ctx, 1669 krb5_keyblock *keyblock) 1670 { 1671 ctx->keyseed = (void *)keyblock; 1672 ctx->keyproc = keyblock_key_proc; 1673 1674 return 0; 1675 } 1676 1677 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1678 krb5_init_creds_set_fast_ccache(krb5_context context, 1679 krb5_init_creds_context ctx, 1680 krb5_ccache fast_ccache) 1681 { 1682 ctx->fast_state.armor_ccache = fast_ccache; 1683 ctx->fast_state.flags |= KRB5_FAST_REQUIRED; 1684 return 0; 1685 } 1686 1687 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1688 krb5_init_creds_set_fast_ap_armor_service(krb5_context context, 1689 krb5_init_creds_context ctx, 1690 krb5_const_principal armor_service) 1691 { 1692 krb5_error_code ret; 1693 1694 if (ctx->fast_state.armor_service) 1695 krb5_free_principal(context, ctx->fast_state.armor_service); 1696 if (armor_service) { 1697 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service); 1698 if (ret) 1699 return ret; 1700 } else { 1701 ctx->fast_state.armor_service = NULL; 1702 } 1703 ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE; 1704 return 0; 1705 } 1706 1707 /* 1708 * FAST 1709 */ 1710 1711 static krb5_error_code 1712 check_fast(krb5_context context, struct fast_state *state) 1713 { 1714 if (state->flags & KRB5_FAST_EXPECTED) { 1715 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 1716 "Expected FAST, but no FAST " 1717 "was in the response from the KDC"); 1718 return KRB5KRB_AP_ERR_MODIFIED; 1719 } 1720 return 0; 1721 } 1722 1723 1724 static krb5_error_code 1725 fast_unwrap_as_rep(krb5_context context, int32_t nonce, 1726 krb5_data *chksumdata, 1727 struct fast_state *state, AS_REP *rep) 1728 { 1729 PA_FX_FAST_REPLY fxfastrep; 1730 KrbFastResponse fastrep; 1731 krb5_error_code ret; 1732 PA_DATA *pa = NULL; 1733 int idx = 0; 1734 1735 if (state->armor_crypto == NULL || rep->padata == NULL) 1736 return check_fast(context, state); 1737 1738 /* find PA_FX_FAST_REPLY */ 1739 1740 pa = krb5_find_padata(rep->padata->val, rep->padata->len, 1741 KRB5_PADATA_FX_FAST, &idx); 1742 if (pa == NULL) 1743 return check_fast(context, state); 1744 1745 memset(&fxfastrep, 0, sizeof(fxfastrep)); 1746 memset(&fastrep, 0, sizeof(fastrep)); 1747 1748 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL); 1749 if (ret) 1750 return ret; 1751 1752 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) { 1753 krb5_data data; 1754 ret = krb5_decrypt_EncryptedData(context, 1755 state->armor_crypto, 1756 KRB5_KU_FAST_REP, 1757 &fxfastrep.u.armored_data.enc_fast_rep, 1758 &data); 1759 if (ret) 1760 goto out; 1761 1762 ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL); 1763 krb5_data_free(&data); 1764 if (ret) 1765 goto out; 1766 1767 } else { 1768 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1769 goto out; 1770 } 1771 1772 free_METHOD_DATA(rep->padata); 1773 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata); 1774 if (ret) 1775 goto out; 1776 1777 if (fastrep.strengthen_key) { 1778 if (state->strengthen_key) 1779 krb5_free_keyblock(context, state->strengthen_key); 1780 1781 ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key); 1782 if (ret) 1783 goto out; 1784 } 1785 1786 if (nonce != fastrep.nonce) { 1787 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1788 goto out; 1789 } 1790 if (fastrep.finished) { 1791 PrincipalName cname; 1792 krb5_realm crealm = NULL; 1793 1794 if (chksumdata == NULL) { 1795 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1796 goto out; 1797 } 1798 1799 ret = krb5_verify_checksum(context, state->armor_crypto, 1800 KRB5_KU_FAST_FINISHED, 1801 chksumdata->data, chksumdata->length, 1802 &fastrep.finished->ticket_checksum); 1803 if (ret) 1804 goto out; 1805 1806 /* update */ 1807 ret = copy_Realm(&fastrep.finished->crealm, &crealm); 1808 if (ret) 1809 goto out; 1810 free_Realm(&rep->crealm); 1811 rep->crealm = crealm; 1812 1813 ret = copy_PrincipalName(&fastrep.finished->cname, &cname); 1814 if (ret) 1815 goto out; 1816 free_PrincipalName(&rep->cname); 1817 rep->cname = cname; 1818 1819 #if 0 /* store authenticated checksum as kdc-offset */ 1820 fastrep->finished.timestamp; 1821 fastrep->finished.usec = 0; 1822 #endif 1823 1824 } else if (chksumdata) { 1825 /* expected fastrep.finish but didn't get it */ 1826 ret = KRB5KDC_ERR_PREAUTH_FAILED; 1827 } 1828 1829 out: 1830 free_PA_FX_FAST_REPLY(&fxfastrep); 1831 1832 return ret; 1833 } 1834 1835 static krb5_error_code 1836 fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error) 1837 { 1838 if (state->armor_crypto == NULL) 1839 return check_fast(context, state); 1840 1841 return 0; 1842 } 1843 1844 krb5_error_code 1845 _krb5_make_fast_ap_fxarmor(krb5_context context, 1846 krb5_ccache armor_ccache, 1847 krb5_data *armor_value, 1848 krb5_keyblock *armor_key, 1849 krb5_crypto *armor_crypto) 1850 { 1851 krb5_auth_context auth_context = NULL; 1852 krb5_creds cred, *credp = NULL; 1853 krb5_error_code ret; 1854 krb5_data empty; 1855 1856 krb5_data_zero(&empty); 1857 1858 memset(&cred, 0, sizeof(cred)); 1859 1860 ret = krb5_auth_con_init (context, &auth_context); 1861 if (ret) 1862 goto out; 1863 1864 ret = krb5_cc_get_principal(context, armor_ccache, &cred.client); 1865 if (ret) 1866 goto out; 1867 1868 ret = krb5_make_principal(context, &cred.server, 1869 cred.client->realm, 1870 KRB5_TGS_NAME, 1871 cred.client->realm, 1872 NULL); 1873 if (ret) { 1874 krb5_free_principal(context, cred.client); 1875 goto out; 1876 } 1877 1878 ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp); 1879 krb5_free_principal(context, cred.server); 1880 krb5_free_principal(context, cred.client); 1881 if (ret) 1882 goto out; 1883 1884 ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty); 1885 if (ret) 1886 goto out; 1887 1888 ret = krb5_mk_req_extended(context, 1889 &auth_context, 1890 AP_OPTS_USE_SUBKEY, 1891 NULL, 1892 credp, 1893 armor_value); 1894 krb5_free_creds(context, credp); 1895 if (ret) 1896 goto out; 1897 1898 ret = _krb5_fast_armor_key(context, 1899 auth_context->local_subkey, 1900 auth_context->keyblock, 1901 armor_key, 1902 armor_crypto); 1903 if (ret) 1904 goto out; 1905 1906 out: 1907 krb5_auth_con_free(context, auth_context); 1908 return ret; 1909 } 1910 1911 #ifndef WIN32 1912 static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT; 1913 static heim_ipc armor_service = NULL; 1914 1915 static void 1916 fast_armor_init_ipc(void *ctx) 1917 { 1918 heim_ipc *ipc = ctx; 1919 heim_ipc_init_context("ANY:org.h5l.armor-service", ipc); 1920 } 1921 #endif /* WIN32 */ 1922 1923 1924 static krb5_error_code 1925 make_fast_ap_fxarmor(krb5_context context, 1926 struct fast_state *state, 1927 const char *realm, 1928 KrbFastArmor **armor) 1929 { 1930 KrbFastArmor *fxarmor = NULL; 1931 krb5_error_code ret; 1932 1933 if (state->armor_crypto) 1934 krb5_crypto_destroy(context, state->armor_crypto); 1935 krb5_free_keyblock_contents(context, &state->armor_key); 1936 1937 1938 ALLOC(fxarmor, 1); 1939 if (fxarmor == NULL) 1940 return krb5_enomem(context); 1941 1942 if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) { 1943 #ifdef WIN32 1944 krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows"); 1945 ret = ENOTSUP; 1946 goto out; 1947 #else /* WIN32 */ 1948 KERB_ARMOR_SERVICE_REPLY msg; 1949 krb5_data request, reply; 1950 1951 heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc); 1952 if (armor_service == NULL) { 1953 krb5_set_error_message(context, ENOENT, "Failed to open fast armor service"); 1954 ret = ENOENT; 1955 goto out; 1956 } 1957 1958 krb5_data_zero(&reply); 1959 1960 request.data = rk_UNCONST(realm); 1961 request.length = strlen(realm); 1962 1963 ret = heim_ipc_call(armor_service, &request, &reply, NULL); 1964 heim_release(send); 1965 if (ret) { 1966 krb5_set_error_message(context, ret, "Failed to get armor service credential"); 1967 goto out; 1968 } 1969 1970 ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL); 1971 krb5_data_free(&reply); 1972 if (ret) 1973 goto out; 1974 1975 ret = copy_KrbFastArmor(fxarmor, &msg.armor); 1976 if (ret) { 1977 free_KERB_ARMOR_SERVICE_REPLY(&msg); 1978 goto out; 1979 } 1980 1981 ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key); 1982 free_KERB_ARMOR_SERVICE_REPLY(&msg); 1983 if (ret) 1984 goto out; 1985 1986 ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto); 1987 if (ret) 1988 goto out; 1989 #endif /* WIN32 */ 1990 } else { 1991 1992 fxarmor->armor_type = 1; 1993 1994 ret = _krb5_make_fast_ap_fxarmor(context, 1995 state->armor_ccache, 1996 &fxarmor->armor_value, 1997 &state->armor_key, 1998 &state->armor_crypto); 1999 if (ret) 2000 goto out; 2001 } 2002 2003 2004 *armor = fxarmor; 2005 fxarmor = NULL; 2006 out: 2007 if (fxarmor) { 2008 free_KrbFastArmor(fxarmor); 2009 free(fxarmor); 2010 } 2011 return ret; 2012 } 2013 2014 static krb5_error_code 2015 fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req) 2016 { 2017 KrbFastArmor *fxarmor = NULL; 2018 PA_FX_FAST_REQUEST fxreq; 2019 krb5_error_code ret; 2020 KrbFastReq fastreq; 2021 krb5_data data; 2022 size_t size; 2023 2024 if (state->flags & KRB5_FAST_DISABLED) { 2025 _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping"); 2026 return 0; 2027 } 2028 2029 memset(&fxreq, 0, sizeof(fxreq)); 2030 memset(&fastreq, 0, sizeof(fastreq)); 2031 krb5_data_zero(&data); 2032 2033 if (state->armor_crypto == NULL) { 2034 if (state->armor_ccache) { 2035 /* 2036 * Instead of keeping state in FX_COOKIE in the KDC, we 2037 * rebuild a new armor key for every request, because this 2038 * is what the MIT KDC expect and RFC6113 is vage about 2039 * what the behavior should be. 2040 */ 2041 state->type = choice_PA_FX_FAST_REQUEST_armored_data; 2042 } else { 2043 return check_fast(context, state); 2044 } 2045 } 2046 2047 state->flags |= KRB5_FAST_EXPECTED; 2048 2049 fastreq.fast_options.hide_client_names = 1; 2050 2051 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body); 2052 free_KDC_REQ_BODY(&req->req_body); 2053 2054 req->req_body.realm = strdup(KRB5_ANON_REALM); 2055 if ((ALLOC(req->req_body.cname, 1)) != NULL) { 2056 req->req_body.cname->name_type = KRB5_NT_WELLKNOWN; 2057 if ((ALLOC(req->req_body.cname->name_string.val, 2)) != NULL) { 2058 req->req_body.cname->name_string.len = 2; 2059 req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME); 2060 req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME); 2061 if (req->req_body.cname->name_string.val[0] == NULL || 2062 req->req_body.cname->name_string.val[1] == NULL) 2063 ret = krb5_enomem(context); 2064 } else 2065 ret = krb5_enomem(context); 2066 } else 2067 ret = krb5_enomem(context); 2068 if ((ALLOC(req->req_body.till, 1)) != NULL) 2069 *req->req_body.till = 0; 2070 else 2071 ret = krb5_enomem(context); 2072 if (ret) 2073 goto out; 2074 2075 if (req->padata) { 2076 ret = copy_METHOD_DATA(req->padata, &fastreq.padata); 2077 free_METHOD_DATA(req->padata); 2078 } else { 2079 if ((ALLOC(req->padata, 1)) == NULL) 2080 ret = krb5_enomem(context); 2081 } 2082 if (ret) 2083 goto out; 2084 2085 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret); 2086 if (ret) 2087 goto out; 2088 heim_assert(data.length == size, "ASN.1 internal error"); 2089 2090 fxreq.element = state->type; 2091 2092 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) { 2093 size_t len; 2094 void *buf; 2095 2096 ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor); 2097 if (ret) 2098 goto out; 2099 2100 heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started"); 2101 2102 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret); 2103 if (ret) 2104 goto out; 2105 heim_assert(len == size, "ASN.1 internal error"); 2106 2107 ret = krb5_create_checksum(context, state->armor_crypto, 2108 KRB5_KU_FAST_REQ_CHKSUM, 0, 2109 buf, len, 2110 &fxreq.u.armored_data.req_checksum); 2111 free(buf); 2112 if (ret) 2113 goto out; 2114 2115 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto, 2116 KRB5_KU_FAST_ENC, 2117 data.data, 2118 data.length, 2119 0, 2120 &fxreq.u.armored_data.enc_fast_req); 2121 krb5_data_free(&data); 2122 if (ret) 2123 goto out; 2124 2125 } else { 2126 krb5_data_free(&data); 2127 heim_assert(false, "unknown FAST type, internal error"); 2128 } 2129 2130 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret); 2131 if (ret) 2132 goto out; 2133 heim_assert(data.length == size, "ASN.1 internal error"); 2134 2135 2136 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length); 2137 if (ret) 2138 goto out; 2139 krb5_data_zero(&data); 2140 2141 out: 2142 free_PA_FX_FAST_REQUEST(&fxreq); 2143 free_KrbFastReq(&fastreq); 2144 if (fxarmor) { 2145 free_KrbFastArmor(fxarmor); 2146 free(fxarmor); 2147 } 2148 krb5_data_free(&data); 2149 2150 return ret; 2151 } 2152 2153 2154 /** 2155 * The core loop if krb5_get_init_creds() function family. Create the 2156 * packets and have the caller send them off to the KDC. 2157 * 2158 * If the caller want all work been done for them, use 2159 * krb5_init_creds_get() instead. 2160 * 2161 * @param context a Kerberos 5 context. 2162 * @param ctx ctx krb5_init_creds_context context. 2163 * @param in input data from KDC, first round it should be reset by krb5_data_zer(). 2164 * @param out reply to KDC. 2165 * @param hostinfo KDC address info, first round it can be NULL. 2166 * @param flags status of the round, if 2167 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round. 2168 * 2169 * @return 0 for success, or an Kerberos 5 error code, see 2170 * krb5_get_error_message(). 2171 * 2172 * @ingroup krb5_credential 2173 */ 2174 2175 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2176 krb5_init_creds_step(krb5_context context, 2177 krb5_init_creds_context ctx, 2178 krb5_data *in, 2179 krb5_data *out, 2180 krb5_krbhst_info *hostinfo, 2181 unsigned int *flags) 2182 { 2183 krb5_error_code ret; 2184 size_t len = 0; 2185 size_t size; 2186 AS_REQ req2; 2187 2188 krb5_data_zero(out); 2189 2190 if (ctx->as_req.req_body.cname == NULL) { 2191 ret = init_as_req(context, ctx->flags, &ctx->cred, 2192 ctx->addrs, ctx->etypes, &ctx->as_req); 2193 if (ret) { 2194 free_init_creds_ctx(context, ctx); 2195 return ret; 2196 } 2197 } 2198 2199 #define MAX_PA_COUNTER 10 2200 if (ctx->pa_counter > MAX_PA_COUNTER) { 2201 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 2202 N_("Looping %d times while getting " 2203 "initial credentials", ""), 2204 ctx->pa_counter); 2205 return KRB5_GET_IN_TKT_LOOP; 2206 } 2207 ctx->pa_counter++; 2208 2209 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter); 2210 2211 /* Lets process the input packet */ 2212 if (in && in->length) { 2213 krb5_kdc_rep rep; 2214 2215 memset(&rep, 0, sizeof(rep)); 2216 2217 _krb5_debug(context, 5, "krb5_get_init_creds: processing input"); 2218 2219 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size); 2220 if (ret == 0) { 2221 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC; 2222 krb5_data data; 2223 2224 /* 2225 * Unwrap AS-REP 2226 */ 2227 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, 2228 &rep.kdc_rep.ticket, &size, ret); 2229 if (ret) 2230 goto out; 2231 heim_assert(data.length == size, "ASN.1 internal error"); 2232 2233 ret = fast_unwrap_as_rep(context, ctx->nonce, &data, 2234 &ctx->fast_state, &rep.kdc_rep); 2235 krb5_data_free(&data); 2236 if (ret) 2237 goto out; 2238 2239 /* 2240 * Now check and extract the ticket 2241 */ 2242 2243 if (ctx->flags.canonicalize) { 2244 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; 2245 eflags |= EXTRACT_TICKET_MATCH_REALM; 2246 } 2247 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) 2248 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; 2249 2250 ret = process_pa_data_to_key(context, ctx, &ctx->cred, 2251 &ctx->as_req, &rep.kdc_rep, 2252 hostinfo, &ctx->fast_state.reply_key); 2253 if (ret) { 2254 free_AS_REP(&rep.kdc_rep); 2255 goto out; 2256 } 2257 2258 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket"); 2259 2260 ret = _krb5_extract_ticket(context, 2261 &rep, 2262 &ctx->cred, 2263 ctx->fast_state.reply_key, 2264 NULL, 2265 KRB5_KU_AS_REP_ENC_PART, 2266 NULL, 2267 ctx->nonce, 2268 eflags, 2269 &ctx->req_buffer, 2270 NULL, 2271 NULL); 2272 if (ret == 0) 2273 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part); 2274 2275 krb5_free_keyblock(context, ctx->fast_state.reply_key); 2276 ctx->fast_state.reply_key = NULL; 2277 *flags = 0; 2278 2279 free_AS_REP(&rep.kdc_rep); 2280 free_EncASRepPart(&rep.enc_part); 2281 2282 return ret; 2283 2284 } else { 2285 /* let's try to parse it as a KRB-ERROR */ 2286 2287 _krb5_debug(context, 5, "krb5_get_init_creds: got an error"); 2288 2289 free_KRB_ERROR(&ctx->error); 2290 2291 ret = krb5_rd_error(context, in, &ctx->error); 2292 if(ret && in->length && ((char*)in->data)[0] == 4) 2293 ret = KRB5KRB_AP_ERR_V4_REPLY; 2294 if (ret) { 2295 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error"); 2296 goto out; 2297 } 2298 2299 /* 2300 * Unwrap KRB-ERROR 2301 */ 2302 ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error); 2303 if (ret) 2304 goto out; 2305 2306 /* 2307 * 2308 */ 2309 2310 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred); 2311 2312 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret); 2313 2314 /* 2315 * If no preauth was set and KDC requires it, give it one 2316 * more try. 2317 */ 2318 2319 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) { 2320 2321 free_METHOD_DATA(&ctx->md); 2322 memset(&ctx->md, 0, sizeof(ctx->md)); 2323 2324 if (ctx->error.e_data) { 2325 ret = decode_METHOD_DATA(ctx->error.e_data->data, 2326 ctx->error.e_data->length, 2327 &ctx->md, 2328 NULL); 2329 if (ret) 2330 krb5_set_error_message(context, ret, 2331 N_("Failed to decode METHOD-DATA", "")); 2332 } else { 2333 krb5_set_error_message(context, ret, 2334 N_("Preauth required but no preauth " 2335 "options send by KDC", "")); 2336 } 2337 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) { 2338 /* 2339 * Try adapt to timeskrew when we are using pre-auth, and 2340 * if there was a time skew, try again. 2341 */ 2342 krb5_set_real_time(context, ctx->error.stime, -1); 2343 if (context->kdc_sec_offset) 2344 ret = 0; 2345 2346 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d", 2347 context->kdc_sec_offset); 2348 2349 ctx->used_pa_types = 0; 2350 2351 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) { 2352 /* client referal to a new realm */ 2353 2354 if (ctx->error.crealm == NULL) { 2355 krb5_set_error_message(context, ret, 2356 N_("Got a client referral, not but no realm", "")); 2357 goto out; 2358 } 2359 _krb5_debug(context, 5, 2360 "krb5_get_init_creds: got referal to realm %s", 2361 *ctx->error.crealm); 2362 2363 ret = krb5_principal_set_realm(context, 2364 ctx->cred.client, 2365 *ctx->error.crealm); 2366 if (ret) 2367 goto out; 2368 2369 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) { 2370 ret = krb5_init_creds_set_service(context, ctx, NULL); 2371 if (ret) 2372 goto out; 2373 } 2374 2375 free_AS_REQ(&ctx->as_req); 2376 memset(&ctx->as_req, 0, sizeof(ctx->as_req)); 2377 2378 ctx->used_pa_types = 0; 2379 } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) { 2380 char buf2[1024]; 2381 2382 ctx->runflags.change_password = 1; 2383 2384 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL); 2385 2386 2387 /* try to avoid recursion */ 2388 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0) 2389 goto out; 2390 2391 /* don't try to change password where then where none */ 2392 if (ctx->prompter == NULL) 2393 goto out; 2394 2395 ret = change_password(context, 2396 ctx->cred.client, 2397 ctx->password, 2398 buf2, 2399 sizeof(buf2), 2400 ctx->prompter, 2401 ctx->prompter_data, 2402 NULL); 2403 if (ret) 2404 goto out; 2405 2406 krb5_init_creds_set_password(context, ctx, buf2); 2407 2408 ctx->used_pa_types = 0; 2409 ret = 0; 2410 2411 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) { 2412 2413 if (ctx->fast_state.flags & KRB5_FAST_DISABLED) 2414 goto out; 2415 if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED)) 2416 goto out; 2417 2418 _krb5_debug(context, 10, "preauth failed with FAST, " 2419 "and told by KD or user, trying w/o FAST"); 2420 2421 ctx->fast_state.flags |= KRB5_FAST_DISABLED; 2422 ctx->used_pa_types = 0; 2423 ret = 0; 2424 } 2425 if (ret) 2426 goto out; 2427 } 2428 } 2429 2430 if (ctx->as_req.req_body.cname == NULL) { 2431 ret = init_as_req(context, ctx->flags, &ctx->cred, 2432 ctx->addrs, ctx->etypes, &ctx->as_req); 2433 if (ret) { 2434 free_init_creds_ctx(context, ctx); 2435 return ret; 2436 } 2437 } 2438 2439 if (ctx->as_req.padata) { 2440 free_METHOD_DATA(ctx->as_req.padata); 2441 free(ctx->as_req.padata); 2442 ctx->as_req.padata = NULL; 2443 } 2444 2445 /* Set a new nonce. */ 2446 ctx->as_req.req_body.nonce = ctx->nonce; 2447 2448 /* fill_in_md_data */ 2449 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx, 2450 &ctx->md, &ctx->as_req.padata, 2451 ctx->prompter, ctx->prompter_data); 2452 if (ret) 2453 goto out; 2454 2455 /* 2456 * Wrap with FAST 2457 */ 2458 copy_AS_REQ(&ctx->as_req, &req2); 2459 2460 ret = fast_wrap_req(context, &ctx->fast_state, &req2); 2461 if (ret) { 2462 free_AS_REQ(&req2); 2463 goto out; 2464 } 2465 2466 krb5_data_free(&ctx->req_buffer); 2467 2468 ASN1_MALLOC_ENCODE(AS_REQ, 2469 ctx->req_buffer.data, ctx->req_buffer.length, 2470 &req2, &len, ret); 2471 free_AS_REQ(&req2); 2472 if (ret) 2473 goto out; 2474 if(len != ctx->req_buffer.length) 2475 krb5_abortx(context, "internal error in ASN.1 encoder"); 2476 2477 out->data = ctx->req_buffer.data; 2478 out->length = ctx->req_buffer.length; 2479 2480 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; 2481 2482 return 0; 2483 out: 2484 return ret; 2485 } 2486 2487 /** 2488 * Extract the newly acquired credentials from krb5_init_creds_context 2489 * context. 2490 * 2491 * @param context A Kerberos 5 context. 2492 * @param ctx 2493 * @param cred credentials, free with krb5_free_cred_contents(). 2494 * 2495 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message(). 2496 */ 2497 2498 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2499 krb5_init_creds_get_creds(krb5_context context, 2500 krb5_init_creds_context ctx, 2501 krb5_creds *cred) 2502 { 2503 return krb5_copy_creds_contents(context, &ctx->cred, cred); 2504 } 2505 2506 /** 2507 * Get the last error from the transaction. 2508 * 2509 * @return Returns 0 or an error code 2510 * 2511 * @ingroup krb5_credential 2512 */ 2513 2514 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2515 krb5_init_creds_get_error(krb5_context context, 2516 krb5_init_creds_context ctx, 2517 KRB_ERROR *error) 2518 { 2519 krb5_error_code ret; 2520 2521 ret = copy_KRB_ERROR(&ctx->error, error); 2522 if (ret) 2523 krb5_enomem(context); 2524 2525 return ret; 2526 } 2527 2528 /** 2529 * 2530 * @ingroup krb5_credential 2531 */ 2532 2533 krb5_error_code 2534 krb5_init_creds_store(krb5_context context, 2535 krb5_init_creds_context ctx, 2536 krb5_ccache id) 2537 { 2538 krb5_error_code ret; 2539 2540 if (ctx->cred.client == NULL) { 2541 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; 2542 krb5_set_error_message(context, ret, "init creds not completed yet"); 2543 return ret; 2544 } 2545 2546 ret = krb5_cc_initialize(context, id, ctx->cred.client); 2547 if (ret) 2548 return ret; 2549 2550 ret = krb5_cc_store_cred(context, id, &ctx->cred); 2551 if (ret) 2552 return ret; 2553 2554 if (ctx->cred.flags.b.enc_pa_rep) { 2555 krb5_data data = { 3, rk_UNCONST("yes") }; 2556 ret = krb5_cc_set_config(context, id, ctx->cred.server, 2557 "fast_avail", &data); 2558 if (ret) 2559 return ret; 2560 } 2561 2562 return ret; 2563 } 2564 2565 /** 2566 * Free the krb5_init_creds_context allocated by krb5_init_creds_init(). 2567 * 2568 * @param context A Kerberos 5 context. 2569 * @param ctx The krb5_init_creds_context to free. 2570 * 2571 * @ingroup krb5_credential 2572 */ 2573 2574 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 2575 krb5_init_creds_free(krb5_context context, 2576 krb5_init_creds_context ctx) 2577 { 2578 free_init_creds_ctx(context, ctx); 2579 free(ctx); 2580 } 2581 2582 /** 2583 * Get new credentials as setup by the krb5_init_creds_context. 2584 * 2585 * @param context A Kerberos 5 context. 2586 * @param ctx The krb5_init_creds_context to process. 2587 * 2588 * @ingroup krb5_credential 2589 */ 2590 2591 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2592 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) 2593 { 2594 krb5_sendto_ctx stctx = NULL; 2595 krb5_krbhst_info *hostinfo = NULL; 2596 krb5_error_code ret; 2597 krb5_data in, out; 2598 unsigned int flags = 0; 2599 2600 krb5_data_zero(&in); 2601 krb5_data_zero(&out); 2602 2603 ret = krb5_sendto_ctx_alloc(context, &stctx); 2604 if (ret) 2605 goto out; 2606 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); 2607 2608 while (1) { 2609 flags = 0; 2610 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags); 2611 krb5_data_free(&in); 2612 if (ret) 2613 goto out; 2614 2615 if ((flags & 1) == 0) 2616 break; 2617 2618 ret = krb5_sendto_context (context, stctx, &out, 2619 ctx->cred.client->realm, &in); 2620 if (ret) 2621 goto out; 2622 2623 } 2624 2625 out: 2626 if (stctx) 2627 krb5_sendto_ctx_free(context, stctx); 2628 2629 return ret; 2630 } 2631 2632 /** 2633 * Get new credentials using password. 2634 * 2635 * @ingroup krb5_credential 2636 */ 2637 2638 2639 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2640 krb5_get_init_creds_password(krb5_context context, 2641 krb5_creds *creds, 2642 krb5_principal client, 2643 const char *password, 2644 krb5_prompter_fct prompter, 2645 void *data, 2646 krb5_deltat start_time, 2647 const char *in_tkt_service, 2648 krb5_get_init_creds_opt *options) 2649 { 2650 krb5_init_creds_context ctx; 2651 char buf[BUFSIZ], buf2[BUFSIZ]; 2652 krb5_error_code ret; 2653 int chpw = 0; 2654 2655 again: 2656 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx); 2657 if (ret) 2658 goto out; 2659 2660 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2661 if (ret) 2662 goto out; 2663 2664 if (prompter != NULL && ctx->password == NULL && password == NULL) { 2665 krb5_prompt prompt; 2666 krb5_data password_data; 2667 char *p, *q = NULL; 2668 int aret; 2669 2670 ret = krb5_unparse_name(context, client, &p); 2671 if (ret) 2672 goto out; 2673 2674 aret = asprintf(&q, "%s's Password: ", p); 2675 free (p); 2676 if (aret == -1 || q == NULL) { 2677 ret = krb5_enomem(context); 2678 goto out; 2679 } 2680 prompt.prompt = q; 2681 password_data.data = buf; 2682 password_data.length = sizeof(buf); 2683 prompt.hidden = 1; 2684 prompt.reply = &password_data; 2685 prompt.type = KRB5_PROMPT_TYPE_PASSWORD; 2686 2687 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); 2688 free (q); 2689 if (ret) { 2690 memset (buf, 0, sizeof(buf)); 2691 ret = KRB5_LIBOS_PWDINTR; 2692 krb5_clear_error_message (context); 2693 goto out; 2694 } 2695 password = password_data.data; 2696 } 2697 2698 if (password) { 2699 ret = krb5_init_creds_set_password(context, ctx, password); 2700 if (ret) 2701 goto out; 2702 } 2703 2704 ret = krb5_init_creds_get(context, ctx); 2705 2706 if (ret == 0) 2707 krb5_process_last_request(context, options, ctx); 2708 2709 2710 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) { 2711 /* try to avoid recursion */ 2712 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0) 2713 goto out; 2714 2715 /* don't try to change password where then where none */ 2716 if (prompter == NULL) 2717 goto out; 2718 2719 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) && 2720 !options->change_password_prompt) 2721 goto out; 2722 2723 ret = change_password (context, 2724 client, 2725 ctx->password, 2726 buf2, 2727 sizeof(buf2), 2728 prompter, 2729 data, 2730 options); 2731 if (ret) 2732 goto out; 2733 password = buf2; 2734 chpw = 1; 2735 krb5_init_creds_free(context, ctx); 2736 goto again; 2737 } 2738 2739 out: 2740 if (ret == 0) 2741 krb5_init_creds_get_creds(context, ctx, creds); 2742 2743 if (ctx) 2744 krb5_init_creds_free(context, ctx); 2745 2746 memset(buf, 0, sizeof(buf)); 2747 memset(buf2, 0, sizeof(buf2)); 2748 return ret; 2749 } 2750 2751 /** 2752 * Get new credentials using keyblock. 2753 * 2754 * @ingroup krb5_credential 2755 */ 2756 2757 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2758 krb5_get_init_creds_keyblock(krb5_context context, 2759 krb5_creds *creds, 2760 krb5_principal client, 2761 krb5_keyblock *keyblock, 2762 krb5_deltat start_time, 2763 const char *in_tkt_service, 2764 krb5_get_init_creds_opt *options) 2765 { 2766 krb5_init_creds_context ctx; 2767 krb5_error_code ret; 2768 2769 memset(creds, 0, sizeof(*creds)); 2770 2771 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2772 if (ret) 2773 goto out; 2774 2775 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2776 if (ret) 2777 goto out; 2778 2779 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock); 2780 if (ret) 2781 goto out; 2782 2783 ret = krb5_init_creds_get(context, ctx); 2784 2785 if (ret == 0) 2786 krb5_process_last_request(context, options, ctx); 2787 2788 out: 2789 if (ret == 0) 2790 krb5_init_creds_get_creds(context, ctx, creds); 2791 2792 if (ctx) 2793 krb5_init_creds_free(context, ctx); 2794 2795 return ret; 2796 } 2797 2798 /** 2799 * Get new credentials using keytab. 2800 * 2801 * @ingroup krb5_credential 2802 */ 2803 2804 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2805 krb5_get_init_creds_keytab(krb5_context context, 2806 krb5_creds *creds, 2807 krb5_principal client, 2808 krb5_keytab keytab, 2809 krb5_deltat start_time, 2810 const char *in_tkt_service, 2811 krb5_get_init_creds_opt *options) 2812 { 2813 krb5_init_creds_context ctx; 2814 krb5_keytab_entry ktent; 2815 krb5_error_code ret; 2816 2817 memset(&ktent, 0, sizeof(ktent)); 2818 memset(creds, 0, sizeof(*creds)); 2819 2820 if (strcmp(client->realm, "") == 0) { 2821 /* 2822 * Referral realm. We have a keytab, so pick a realm by 2823 * matching in the keytab. 2824 */ 2825 ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent); 2826 if (ret == 0) 2827 client = ktent.principal; 2828 } 2829 2830 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); 2831 if (ret) 2832 goto out; 2833 2834 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); 2835 if (ret) 2836 goto out; 2837 2838 ret = krb5_init_creds_set_keytab(context, ctx, keytab); 2839 if (ret) 2840 goto out; 2841 2842 ret = krb5_init_creds_get(context, ctx); 2843 if (ret == 0) 2844 krb5_process_last_request(context, options, ctx); 2845 2846 out: 2847 krb5_kt_free_entry(context, &ktent); 2848 if (ret == 0) 2849 krb5_init_creds_get_creds(context, ctx, creds); 2850 2851 if (ctx) 2852 krb5_init_creds_free(context, ctx); 2853 2854 return ret; 2855 } 2856