1 /* 2 * Copyright (c) 1997 - 2003 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.55.2.1 2004/08/30 23:21:07 lha Exp $"); 37 38 static int 39 get_config_time (krb5_context context, 40 const char *realm, 41 const 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_error_code 63 init_cred (krb5_context context, 64 krb5_creds *cred, 65 krb5_principal client, 66 krb5_deltat start_time, 67 const char *in_tkt_service, 68 krb5_get_init_creds_opt *options) 69 { 70 krb5_error_code ret; 71 krb5_realm *client_realm; 72 int tmp; 73 krb5_timestamp now; 74 75 krb5_timeofday (context, &now); 76 77 memset (cred, 0, sizeof(*cred)); 78 79 if (client) 80 krb5_copy_principal(context, client, &cred->client); 81 else { 82 ret = krb5_get_default_principal (context, 83 &cred->client); 84 if (ret) 85 goto out; 86 } 87 88 client_realm = krb5_princ_realm (context, cred->client); 89 90 if (start_time) 91 cred->times.starttime = now + start_time; 92 93 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE) 94 tmp = options->tkt_life; 95 else 96 tmp = 10 * 60 * 60; 97 cred->times.endtime = now + tmp; 98 99 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) && 100 options->renew_life > 0) { 101 cred->times.renew_till = now + options->renew_life; 102 } 103 104 if (in_tkt_service) { 105 krb5_realm server_realm; 106 107 ret = krb5_parse_name (context, in_tkt_service, &cred->server); 108 if (ret) 109 goto out; 110 server_realm = strdup (*client_realm); 111 free (*krb5_princ_realm(context, cred->server)); 112 krb5_princ_set_realm (context, cred->server, &server_realm); 113 } else { 114 ret = krb5_make_principal(context, &cred->server, 115 *client_realm, KRB5_TGS_NAME, *client_realm, 116 NULL); 117 if (ret) 118 goto out; 119 } 120 return 0; 121 122 out: 123 krb5_free_creds_contents (context, cred); 124 return ret; 125 } 126 127 /* 128 * Print a message (str) to the user about the expiration in `lr' 129 */ 130 131 static void 132 report_expiration (krb5_context context, 133 krb5_prompter_fct prompter, 134 krb5_data *data, 135 const char *str, 136 time_t time) 137 { 138 char *p; 139 140 asprintf (&p, "%s%s", str, ctime(&time)); 141 (*prompter) (context, data, NULL, p, 0, NULL); 142 free (p); 143 } 144 145 /* 146 * Parse the last_req data and show it to the user if it's interesting 147 */ 148 149 static void 150 print_expire (krb5_context context, 151 krb5_realm *realm, 152 krb5_kdc_rep *rep, 153 krb5_prompter_fct prompter, 154 krb5_data *data) 155 { 156 int i; 157 LastReq *lr = &rep->enc_part.last_req; 158 krb5_timestamp sec; 159 time_t t; 160 krb5_boolean reported = FALSE; 161 162 krb5_timeofday (context, &sec); 163 164 t = sec + get_config_time (context, 165 *realm, 166 "warn_pwexpire", 167 7 * 24 * 60 * 60); 168 169 for (i = 0; i < lr->len; ++i) { 170 if (lr->val[i].lr_value <= t) { 171 switch (abs(lr->val[i].lr_type)) { 172 case LR_PW_EXPTIME : 173 report_expiration(context, prompter, data, 174 "Your password will expire at ", 175 lr->val[i].lr_value); 176 reported = TRUE; 177 break; 178 case LR_ACCT_EXPTIME : 179 report_expiration(context, prompter, data, 180 "Your account will expire at ", 181 lr->val[i].lr_value); 182 reported = TRUE; 183 break; 184 } 185 } 186 } 187 188 if (!reported 189 && rep->enc_part.key_expiration 190 && *rep->enc_part.key_expiration <= t) { 191 report_expiration(context, prompter, data, 192 "Your password/account will expire at ", 193 *rep->enc_part.key_expiration); 194 } 195 } 196 197 static krb5_error_code 198 get_init_creds_common(krb5_context context, 199 krb5_creds *creds, 200 krb5_principal client, 201 krb5_deltat start_time, 202 const char *in_tkt_service, 203 krb5_get_init_creds_opt *options, 204 krb5_addresses **addrs, 205 krb5_enctype **etypes, 206 krb5_creds *cred, 207 krb5_preauthtype **pre_auth_types, 208 krb5_kdc_flags *flags) 209 { 210 krb5_error_code ret; 211 krb5_realm *client_realm; 212 krb5_get_init_creds_opt default_opt; 213 214 if (options == NULL) { 215 krb5_get_init_creds_opt_init (&default_opt); 216 options = &default_opt; 217 } 218 219 ret = init_cred (context, cred, client, start_time, 220 in_tkt_service, options); 221 if (ret) 222 return ret; 223 224 client_realm = krb5_princ_realm (context, cred->client); 225 226 flags->i = 0; 227 228 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) 229 flags->b.forwardable = options->forwardable; 230 231 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) 232 flags->b.proxiable = options->proxiable; 233 234 if (start_time) 235 flags->b.postdated = 1; 236 if (cred->times.renew_till) 237 flags->b.renewable = 1; 238 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) 239 *addrs = options->address_list; 240 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { 241 *etypes = malloc((options->etype_list_length + 1) 242 * sizeof(krb5_enctype)); 243 if (*etypes == NULL) { 244 krb5_set_error_string(context, "malloc: out of memory"); 245 return ENOMEM; 246 } 247 memcpy (*etypes, options->etype_list, 248 options->etype_list_length * sizeof(krb5_enctype)); 249 (*etypes)[options->etype_list_length] = ETYPE_NULL; 250 } 251 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { 252 *pre_auth_types = malloc((options->preauth_list_length + 1) 253 * sizeof(krb5_preauthtype)); 254 if (*pre_auth_types == NULL) { 255 krb5_set_error_string(context, "malloc: out of memory"); 256 return ENOMEM; 257 } 258 memcpy (*pre_auth_types, options->preauth_list, 259 options->preauth_list_length * sizeof(krb5_preauthtype)); 260 (*pre_auth_types)[options->preauth_list_length] = KRB5_PADATA_NONE; 261 } 262 if (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT) 263 ; /* XXX */ 264 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) 265 flags->b.request_anonymous = options->anonymous; 266 return 0; 267 } 268 269 static krb5_error_code 270 change_password (krb5_context context, 271 krb5_principal client, 272 const char *password, 273 char *newpw, 274 size_t newpw_sz, 275 krb5_prompter_fct prompter, 276 void *data, 277 krb5_get_init_creds_opt *old_options) 278 { 279 krb5_prompt prompts[2]; 280 krb5_error_code ret; 281 krb5_creds cpw_cred; 282 char buf1[BUFSIZ], buf2[BUFSIZ]; 283 krb5_data password_data[2]; 284 int result_code; 285 krb5_data result_code_string; 286 krb5_data result_string; 287 char *p; 288 krb5_get_init_creds_opt options; 289 290 memset (&cpw_cred, 0, sizeof(cpw_cred)); 291 292 krb5_get_init_creds_opt_init (&options); 293 krb5_get_init_creds_opt_set_tkt_life (&options, 60); 294 krb5_get_init_creds_opt_set_forwardable (&options, FALSE); 295 krb5_get_init_creds_opt_set_proxiable (&options, FALSE); 296 if (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) 297 krb5_get_init_creds_opt_set_preauth_list (&options, 298 old_options->preauth_list, 299 old_options->preauth_list_length); 300 301 krb5_data_zero (&result_code_string); 302 krb5_data_zero (&result_string); 303 304 ret = krb5_get_init_creds_password (context, 305 &cpw_cred, 306 client, 307 password, 308 prompter, 309 data, 310 0, 311 "kadmin/changepw", 312 &options); 313 if (ret) 314 goto out; 315 316 for(;;) { 317 password_data[0].data = buf1; 318 password_data[0].length = sizeof(buf1); 319 320 prompts[0].hidden = 1; 321 prompts[0].prompt = "New password: "; 322 prompts[0].reply = &password_data[0]; 323 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD; 324 325 password_data[1].data = buf2; 326 password_data[1].length = sizeof(buf2); 327 328 prompts[1].hidden = 1; 329 prompts[1].prompt = "Repeat new password: "; 330 prompts[1].reply = &password_data[1]; 331 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN; 332 333 ret = (*prompter) (context, data, NULL, "Changing password", 334 2, prompts); 335 if (ret) { 336 memset (buf1, 0, sizeof(buf1)); 337 memset (buf2, 0, sizeof(buf2)); 338 goto out; 339 } 340 341 if (strcmp (buf1, buf2) == 0) 342 break; 343 memset (buf1, 0, sizeof(buf1)); 344 memset (buf2, 0, sizeof(buf2)); 345 } 346 347 ret = krb5_change_password (context, 348 &cpw_cred, 349 buf1, 350 &result_code, 351 &result_code_string, 352 &result_string); 353 if (ret) 354 goto out; 355 asprintf (&p, "%s: %.*s\n", 356 result_code ? "Error" : "Success", 357 (int)result_string.length, 358 (char*)result_string.data); 359 360 ret = (*prompter) (context, data, NULL, p, 0, NULL); 361 free (p); 362 if (result_code == 0) { 363 strlcpy (newpw, buf1, newpw_sz); 364 ret = 0; 365 } else { 366 krb5_set_error_string (context, "failed changing password"); 367 ret = ENOTTY; 368 } 369 370 out: 371 memset (buf1, 0, sizeof(buf1)); 372 memset (buf2, 0, sizeof(buf2)); 373 krb5_data_free (&result_string); 374 krb5_data_free (&result_code_string); 375 krb5_free_creds_contents (context, &cpw_cred); 376 return ret; 377 } 378 379 krb5_error_code 380 krb5_get_init_creds_password(krb5_context context, 381 krb5_creds *creds, 382 krb5_principal client, 383 const char *password, 384 krb5_prompter_fct prompter, 385 void *data, 386 krb5_deltat start_time, 387 const char *in_tkt_service, 388 krb5_get_init_creds_opt *options) 389 { 390 krb5_error_code ret; 391 krb5_kdc_flags flags; 392 krb5_addresses *addrs = NULL; 393 krb5_enctype *etypes = NULL; 394 krb5_preauthtype *pre_auth_types = NULL; 395 krb5_creds this_cred; 396 krb5_kdc_rep kdc_reply; 397 char buf[BUFSIZ]; 398 krb5_data password_data; 399 int done; 400 401 memset(&kdc_reply, 0, sizeof(kdc_reply)); 402 403 ret = get_init_creds_common(context, creds, client, start_time, 404 in_tkt_service, options, 405 &addrs, &etypes, &this_cred, &pre_auth_types, 406 &flags); 407 if(ret) 408 goto out; 409 410 if (password == NULL) { 411 krb5_prompt prompt; 412 char *p, *q; 413 414 krb5_unparse_name (context, this_cred.client, &p); 415 asprintf (&q, "%s's Password: ", p); 416 free (p); 417 prompt.prompt = q; 418 password_data.data = buf; 419 password_data.length = sizeof(buf); 420 prompt.hidden = 1; 421 prompt.reply = &password_data; 422 prompt.type = KRB5_PROMPT_TYPE_PASSWORD; 423 424 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); 425 free (q); 426 if (ret) { 427 memset (buf, 0, sizeof(buf)); 428 ret = KRB5_LIBOS_PWDINTR; 429 krb5_clear_error_string (context); 430 goto out; 431 } 432 password = password_data.data; 433 } 434 435 done = 0; 436 while(!done) { 437 memset(&kdc_reply, 0, sizeof(kdc_reply)); 438 ret = krb5_get_in_cred (context, 439 flags.i, 440 addrs, 441 etypes, 442 pre_auth_types, 443 NULL, 444 krb5_password_key_proc, 445 password, 446 NULL, 447 NULL, 448 &this_cred, 449 &kdc_reply); 450 switch (ret) { 451 case 0 : 452 done = 1; 453 break; 454 case KRB5KDC_ERR_KEY_EXPIRED : 455 /* try to avoid recursion */ 456 457 if (prompter == NULL) 458 goto out; 459 460 krb5_clear_error_string (context); 461 462 if (in_tkt_service != NULL 463 && strcmp (in_tkt_service, "kadmin/changepw") == 0) 464 goto out; 465 466 ret = change_password (context, 467 client, 468 password, 469 buf, 470 sizeof(buf), 471 prompter, 472 data, 473 options); 474 if (ret) 475 goto out; 476 password = buf; 477 break; 478 default: 479 goto out; 480 } 481 } 482 483 if (prompter) 484 print_expire (context, 485 krb5_princ_realm (context, this_cred.client), 486 &kdc_reply, 487 prompter, 488 data); 489 out: 490 memset (buf, 0, sizeof(buf)); 491 492 krb5_free_kdc_rep (context, &kdc_reply); 493 494 free (pre_auth_types); 495 free (etypes); 496 if (ret == 0 && creds) 497 *creds = this_cred; 498 else 499 krb5_free_creds_contents (context, &this_cred); 500 return ret; 501 } 502 503 krb5_error_code 504 krb5_keyblock_key_proc (krb5_context context, 505 krb5_keytype type, 506 krb5_data *salt, 507 krb5_const_pointer keyseed, 508 krb5_keyblock **key) 509 { 510 return krb5_copy_keyblock (context, keyseed, key); 511 } 512 513 krb5_error_code 514 krb5_get_init_creds_keytab(krb5_context context, 515 krb5_creds *creds, 516 krb5_principal client, 517 krb5_keytab keytab, 518 krb5_deltat start_time, 519 const char *in_tkt_service, 520 krb5_get_init_creds_opt *options) 521 { 522 krb5_error_code ret; 523 krb5_kdc_flags flags; 524 krb5_addresses *addrs = NULL; 525 krb5_enctype *etypes = NULL; 526 krb5_preauthtype *pre_auth_types = NULL; 527 krb5_creds this_cred; 528 krb5_keytab_key_proc_args *a; 529 530 ret = get_init_creds_common(context, creds, client, start_time, 531 in_tkt_service, options, 532 &addrs, &etypes, &this_cred, &pre_auth_types, 533 &flags); 534 if(ret) 535 goto out; 536 537 a = malloc (sizeof(*a)); 538 if (a == NULL) { 539 krb5_set_error_string(context, "malloc: out of memory"); 540 ret = ENOMEM; 541 goto out; 542 } 543 a->principal = this_cred.client; 544 a->keytab = keytab; 545 546 ret = krb5_get_in_cred (context, 547 flags.i, 548 addrs, 549 etypes, 550 pre_auth_types, 551 NULL, 552 krb5_keytab_key_proc, 553 a, 554 NULL, 555 NULL, 556 &this_cred, 557 NULL); 558 free (a); 559 560 if (ret) 561 goto out; 562 free (pre_auth_types); 563 free (etypes); 564 if (creds) 565 *creds = this_cred; 566 else 567 krb5_free_creds_contents (context, &this_cred); 568 return 0; 569 570 out: 571 free (pre_auth_types); 572 free (etypes); 573 krb5_free_creds_contents (context, &this_cred); 574 return ret; 575 } 576