1 /* 2 * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "krb5_locl.h" 35 36 RCSID("$Id: init_creds_pw.c,v 1.47 2001/05/14 06:14:48 assar Exp $"); 37 38 static int 39 get_config_time (krb5_context context, 40 char *realm, 41 char *name, 42 int def) 43 { 44 int ret; 45 46 ret = krb5_config_get_time (context, NULL, 47 "realms", 48 realm, 49 name, 50 NULL); 51 if (ret >= 0) 52 return ret; 53 ret = krb5_config_get_time (context, NULL, 54 "libdefaults", 55 name, 56 NULL); 57 if (ret >= 0) 58 return ret; 59 return def; 60 } 61 62 static krb5_boolean 63 get_config_bool (krb5_context context, 64 char *realm, 65 char *name) 66 { 67 return krb5_config_get_bool (context, 68 NULL, 69 "realms", 70 realm, 71 name, 72 NULL) 73 || krb5_config_get_bool (context, 74 NULL, 75 "libdefaults", 76 name, 77 NULL); 78 } 79 80 static krb5_error_code 81 init_cred (krb5_context context, 82 krb5_creds *cred, 83 krb5_principal client, 84 krb5_deltat start_time, 85 const char *in_tkt_service, 86 krb5_get_init_creds_opt *options) 87 { 88 krb5_error_code ret; 89 krb5_realm *client_realm; 90 int tmp; 91 krb5_timestamp now; 92 93 krb5_timeofday (context, &now); 94 95 memset (cred, 0, sizeof(*cred)); 96 97 if (client) 98 krb5_copy_principal(context, client, &cred->client); 99 else { 100 ret = krb5_get_default_principal (context, 101 &cred->client); 102 if (ret) 103 goto out; 104 } 105 106 client_realm = krb5_princ_realm (context, cred->client); 107 108 if (start_time) 109 cred->times.starttime = now + start_time; 110 111 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE) 112 tmp = options->tkt_life; 113 else 114 tmp = get_config_time (context, 115 *client_realm, 116 "ticket_lifetime", 117 10 * 60 * 60); 118 cred->times.endtime = now + tmp; 119 120 tmp = 0; 121 if (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) 122 tmp = options->renew_life; 123 else 124 tmp = get_config_time (context, 125 *client_realm, 126 "renew_lifetime", 127 0); 128 if (tmp) 129 cred->times.renew_till = now + tmp; 130 131 if (in_tkt_service) { 132 krb5_realm server_realm; 133 134 ret = krb5_parse_name (context, in_tkt_service, &cred->server); 135 if (ret) 136 goto out; 137 server_realm = strdup (*client_realm); 138 free (cred->server->realm); 139 krb5_princ_set_realm (context, cred->server, &server_realm); 140 } else { 141 ret = krb5_make_principal(context, &cred->server, 142 *client_realm, KRB5_TGS_NAME, *client_realm, 143 NULL); 144 if (ret) 145 goto out; 146 } 147 return 0; 148 149 out: 150 krb5_free_creds_contents (context, cred); 151 return ret; 152 } 153 154 /* 155 * Parse the last_req data and show it to the user if it's interesting 156 */ 157 158 static void 159 print_expire (krb5_context context, 160 krb5_realm *realm, 161 krb5_kdc_rep *rep, 162 krb5_prompter_fct prompter, 163 krb5_data *data) 164 { 165 int i; 166 LastReq *lr = &rep->enc_part.last_req; 167 krb5_timestamp sec; 168 time_t t; 169 170 krb5_timeofday (context, &sec); 171 172 t = sec + get_config_time (context, 173 *realm, 174 "warn_pwexpire", 175 7 * 24 * 60 * 60); 176 177 for (i = 0; i < lr->len; ++i) { 178 if (abs(lr->val[i].lr_type) == LR_PW_EXPTIME 179 && lr->val[i].lr_value <= t) { 180 char *p; 181 time_t tmp = lr->val[i].lr_value; 182 183 asprintf (&p, "Your password will expire at %s", ctime(&tmp)); 184 (*prompter) (context, data, NULL, p, 0, NULL); 185 free (p); 186 return; 187 } 188 } 189 190 if (rep->enc_part.key_expiration 191 && *rep->enc_part.key_expiration <= t) { 192 char *p; 193 time_t t = *rep->enc_part.key_expiration; 194 195 asprintf (&p, "Your password/account will expire at %s", ctime(&t)); 196 (*prompter) (context, data, NULL, p, 0, NULL); 197 free (p); 198 } 199 } 200 201 static krb5_error_code 202 get_init_creds_common(krb5_context context, 203 krb5_creds *creds, 204 krb5_principal client, 205 krb5_deltat start_time, 206 const char *in_tkt_service, 207 krb5_get_init_creds_opt *options, 208 krb5_addresses **addrs, 209 krb5_enctype **etypes, 210 krb5_creds *cred, 211 krb5_preauthtype **pre_auth_types, 212 krb5_kdc_flags *flags) 213 { 214 krb5_error_code ret; 215 krb5_realm *client_realm; 216 krb5_get_init_creds_opt default_opt; 217 218 if (options == NULL) { 219 krb5_get_init_creds_opt_init (&default_opt); 220 options = &default_opt; 221 } 222 223 ret = init_cred (context, cred, client, start_time, 224 in_tkt_service, options); 225 if (ret) 226 return ret; 227 228 client_realm = krb5_princ_realm (context, cred->client); 229 230 flags->i = 0; 231 232 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) 233 flags->b.forwardable = options->forwardable; 234 else 235 flags->b.forwardable = get_config_bool (context, 236 *client_realm, 237 "forwardable"); 238 239 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) 240 flags->b.proxiable = options->proxiable; 241 else 242 flags->b.proxiable = get_config_bool (context, 243 *client_realm, 244 "proxiable"); 245 246 if (start_time) 247 flags->b.postdated = 1; 248 if (cred->times.renew_till) 249 flags->b.renewable = 1; 250 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) 251 *addrs = options->address_list; 252 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { 253 *etypes = malloc((options->etype_list_length + 1) 254 * sizeof(krb5_enctype)); 255 if (*etypes == NULL) { 256 krb5_set_error_string(context, "malloc: out of memory"); 257 return ENOMEM; 258 } 259 memcpy (*etypes, options->etype_list, 260 options->etype_list_length * sizeof(krb5_enctype)); 261 (*etypes)[options->etype_list_length] = ETYPE_NULL; 262 } 263 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { 264 *pre_auth_types = malloc((options->preauth_list_length + 1) 265 * sizeof(krb5_preauthtype)); 266 if (*pre_auth_types == NULL) { 267 krb5_set_error_string(context, "malloc: out of memory"); 268 return ENOMEM; 269 } 270 memcpy (*pre_auth_types, options->preauth_list, 271 options->preauth_list_length * sizeof(krb5_preauthtype)); 272 (*pre_auth_types)[options->preauth_list_length] = KRB5_PADATA_NONE; 273 } 274 if (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT) 275 ; /* XXX */ 276 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) 277 flags->b.request_anonymous = options->anonymous; 278 return 0; 279 } 280 281 static krb5_error_code 282 change_password (krb5_context context, 283 krb5_principal client, 284 const char *password, 285 char *newpw, 286 size_t newpw_sz, 287 krb5_prompter_fct prompter, 288 void *data, 289 krb5_get_init_creds_opt *old_options) 290 { 291 krb5_prompt prompts[2]; 292 krb5_error_code ret; 293 krb5_creds cpw_cred; 294 char buf1[BUFSIZ], buf2[BUFSIZ]; 295 krb5_data password_data; 296 int result_code; 297 krb5_data result_code_string; 298 krb5_data result_string; 299 char *p; 300 krb5_get_init_creds_opt options; 301 302 memset (&cpw_cred, 0, sizeof(cpw_cred)); 303 304 krb5_get_init_creds_opt_init (&options); 305 krb5_get_init_creds_opt_set_tkt_life (&options, 60); 306 krb5_get_init_creds_opt_set_forwardable (&options, FALSE); 307 krb5_get_init_creds_opt_set_proxiable (&options, FALSE); 308 if (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) 309 krb5_get_init_creds_opt_set_preauth_list (&options, 310 old_options->preauth_list, 311 old_options->preauth_list_length); 312 313 krb5_data_zero (&result_code_string); 314 krb5_data_zero (&result_string); 315 316 ret = krb5_get_init_creds_password (context, 317 &cpw_cred, 318 client, 319 password, 320 prompter, 321 data, 322 0, 323 "kadmin/changepw", 324 &options); 325 if (ret) 326 goto out; 327 328 for(;;) { 329 password_data.data = buf1; 330 password_data.length = sizeof(buf1); 331 332 prompts[0].hidden = 1; 333 prompts[0].prompt = "New password: "; 334 prompts[0].reply = &password_data; 335 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD; 336 337 password_data.data = buf2; 338 password_data.length = sizeof(buf2); 339 340 prompts[1].hidden = 1; 341 prompts[1].prompt = "Repeat new password: "; 342 prompts[1].reply = &password_data; 343 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN; 344 345 ret = (*prompter) (context, data, NULL, "Changing password", 346 2, prompts); 347 if (ret) { 348 memset (buf1, 0, sizeof(buf1)); 349 memset (buf2, 0, sizeof(buf2)); 350 goto out; 351 } 352 353 if (strcmp (buf1, buf2) == 0) 354 break; 355 memset (buf1, 0, sizeof(buf1)); 356 memset (buf2, 0, sizeof(buf2)); 357 } 358 359 ret = krb5_change_password (context, 360 &cpw_cred, 361 buf1, 362 &result_code, 363 &result_code_string, 364 &result_string); 365 if (ret) 366 goto out; 367 asprintf (&p, "%s: %.*s\n", 368 result_code ? "Error" : "Success", 369 (int)result_string.length, 370 (char*)result_string.data); 371 372 ret = (*prompter) (context, data, NULL, p, 0, NULL); 373 free (p); 374 if (result_code == 0) { 375 strlcpy (newpw, buf1, newpw_sz); 376 ret = 0; 377 } else { 378 krb5_set_error_string (context, "failed changing password"); 379 ret = ENOTTY; 380 } 381 382 out: 383 memset (buf1, 0, sizeof(buf1)); 384 memset (buf2, 0, sizeof(buf2)); 385 krb5_data_free (&result_string); 386 krb5_data_free (&result_code_string); 387 krb5_free_creds_contents (context, &cpw_cred); 388 return ret; 389 } 390 391 krb5_error_code 392 krb5_get_init_creds_password(krb5_context context, 393 krb5_creds *creds, 394 krb5_principal client, 395 const char *password, 396 krb5_prompter_fct prompter, 397 void *data, 398 krb5_deltat start_time, 399 const char *in_tkt_service, 400 krb5_get_init_creds_opt *options) 401 { 402 krb5_error_code ret; 403 krb5_kdc_flags flags; 404 krb5_addresses *addrs = NULL; 405 krb5_enctype *etypes = NULL; 406 krb5_preauthtype *pre_auth_types = NULL; 407 krb5_creds this_cred; 408 krb5_kdc_rep kdc_reply; 409 char buf[BUFSIZ]; 410 krb5_data password_data; 411 int done; 412 413 ret = get_init_creds_common(context, creds, client, start_time, 414 in_tkt_service, options, 415 &addrs, &etypes, &this_cred, &pre_auth_types, 416 &flags); 417 if(ret) 418 goto out; 419 420 if (password == NULL) { 421 krb5_prompt prompt; 422 char *p; 423 424 krb5_unparse_name (context, this_cred.client, &p); 425 asprintf (&prompt.prompt, "%s's Password: ", p); 426 free (p); 427 password_data.data = buf; 428 password_data.length = sizeof(buf); 429 prompt.hidden = 1; 430 prompt.reply = &password_data; 431 prompt.type = KRB5_PROMPT_TYPE_PASSWORD; 432 433 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); 434 free (prompt.prompt); 435 if (ret) { 436 memset (buf, 0, sizeof(buf)); 437 ret = KRB5_LIBOS_PWDINTR; 438 krb5_clear_error_string (context); 439 goto out; 440 } 441 password = password_data.data; 442 } 443 444 done = 0; 445 while(!done) { 446 memset(&kdc_reply, 0, sizeof(kdc_reply)); 447 ret = krb5_get_in_cred (context, 448 flags.i, 449 addrs, 450 etypes, 451 pre_auth_types, 452 NULL, 453 krb5_password_key_proc, 454 password, 455 NULL, 456 NULL, 457 &this_cred, 458 &kdc_reply); 459 switch (ret) { 460 case 0 : 461 done = 1; 462 break; 463 case KRB5KDC_ERR_KEY_EXPIRED : 464 /* try to avoid recursion */ 465 466 krb5_clear_error_string (context); 467 468 if (in_tkt_service != NULL 469 && strcmp (in_tkt_service, "kadmin/changepw") == 0) 470 goto out; 471 472 ret = change_password (context, 473 client, 474 password, 475 buf, 476 sizeof(buf), 477 prompter, 478 data, 479 options); 480 if (ret) 481 goto out; 482 password = buf; 483 break; 484 default: 485 goto out; 486 } 487 } 488 489 if (prompter) 490 print_expire (context, 491 krb5_princ_realm (context, this_cred.client), 492 &kdc_reply, 493 prompter, 494 data); 495 out: 496 memset (buf, 0, sizeof(buf)); 497 if (ret == 0) 498 krb5_free_kdc_rep (context, &kdc_reply); 499 500 free (pre_auth_types); 501 free (etypes); 502 if (ret == 0 && creds) 503 *creds = this_cred; 504 else 505 krb5_free_creds_contents (context, &this_cred); 506 return ret; 507 } 508 509 krb5_error_code 510 krb5_keyblock_key_proc (krb5_context context, 511 krb5_keytype type, 512 krb5_data *salt, 513 krb5_const_pointer keyseed, 514 krb5_keyblock **key) 515 { 516 return krb5_copy_keyblock (context, keyseed, key); 517 } 518 519 krb5_error_code 520 krb5_get_init_creds_keytab(krb5_context context, 521 krb5_creds *creds, 522 krb5_principal client, 523 krb5_keytab keytab, 524 krb5_deltat start_time, 525 const char *in_tkt_service, 526 krb5_get_init_creds_opt *options) 527 { 528 krb5_error_code ret; 529 krb5_kdc_flags flags; 530 krb5_addresses *addrs = NULL; 531 krb5_enctype *etypes = NULL; 532 krb5_preauthtype *pre_auth_types = NULL; 533 krb5_creds this_cred; 534 krb5_keytab_key_proc_args *a; 535 536 ret = get_init_creds_common(context, creds, client, start_time, 537 in_tkt_service, options, 538 &addrs, &etypes, &this_cred, &pre_auth_types, 539 &flags); 540 if(ret) 541 goto out; 542 543 a = malloc (sizeof(*a)); 544 if (a == NULL) { 545 krb5_set_error_string(context, "malloc: out of memory"); 546 ret = ENOMEM; 547 goto out; 548 } 549 a->principal = this_cred.client; 550 a->keytab = keytab; 551 552 ret = krb5_get_in_cred (context, 553 flags.i, 554 addrs, 555 etypes, 556 pre_auth_types, 557 NULL, 558 krb5_keytab_key_proc, 559 a, 560 NULL, 561 NULL, 562 &this_cred, 563 NULL); 564 if (ret) 565 goto out; 566 free (pre_auth_types); 567 free (etypes); 568 if (creds) 569 *creds = this_cred; 570 else 571 krb5_free_creds_contents (context, &this_cred); 572 return 0; 573 574 out: 575 free (pre_auth_types); 576 free (etypes); 577 krb5_free_creds_contents (context, &this_cred); 578 return ret; 579 } 580