1 /* 2 * Copyright (c) 1997 - 2000 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.44 2000/07/24 03:46:40 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 (lr->val[i].lr_type == 6 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, 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, 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 217 ret = init_cred (context, cred, client, start_time, 218 in_tkt_service, options); 219 if (ret) 220 return ret; 221 222 client_realm = krb5_princ_realm (context, cred->client); 223 224 flags->i = 0; 225 226 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) 227 flags->b.forwardable = options->forwardable; 228 else 229 flags->b.forwardable = get_config_bool (context, 230 *client_realm, 231 "forwardable"); 232 233 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) 234 flags->b.proxiable = options->proxiable; 235 else 236 flags->b.proxiable = get_config_bool (context, 237 *client_realm, 238 "proxiable"); 239 240 if (start_time) 241 flags->b.postdated = 1; 242 if (cred->times.renew_till) 243 flags->b.renewable = 1; 244 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) 245 *addrs = options->address_list; 246 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { 247 *etypes = malloc((options->etype_list_length + 1) 248 * sizeof(krb5_enctype)); 249 if (*etypes == NULL) 250 return ENOMEM; 251 memcpy (*etypes, options->etype_list, 252 options->etype_list_length * sizeof(krb5_enctype)); 253 (*etypes)[options->etype_list_length] = ETYPE_NULL; 254 } 255 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { 256 *pre_auth_types = malloc((options->preauth_list_length + 1) 257 * sizeof(krb5_preauthtype)); 258 if (*pre_auth_types == NULL) 259 return ENOMEM; 260 memcpy (*pre_auth_types, options->preauth_list, 261 options->preauth_list_length * sizeof(krb5_preauthtype)); 262 (*pre_auth_types)[options->preauth_list_length] = KRB5_PADATA_NONE; 263 } 264 if (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT) 265 ; /* XXX */ 266 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) 267 flags->b.request_anonymous = options->anonymous; 268 return 0; 269 } 270 271 static krb5_error_code 272 change_password (krb5_context context, 273 krb5_principal client, 274 const char *password, 275 char *newpw, 276 size_t newpw_sz, 277 krb5_prompter_fct prompter, 278 void *data, 279 krb5_get_init_creds_opt *old_options) 280 { 281 krb5_prompt prompt; 282 krb5_error_code ret; 283 krb5_creds cpw_cred; 284 char buf1[BUFSIZ], buf2[BUFSIZ]; 285 krb5_data password_data; 286 int result_code; 287 krb5_data result_code_string; 288 krb5_data result_string; 289 char *p; 290 krb5_get_init_creds_opt options; 291 292 memset (&cpw_cred, 0, sizeof(cpw_cred)); 293 294 krb5_get_init_creds_opt_init (&options); 295 krb5_get_init_creds_opt_set_tkt_life (&options, 60); 296 krb5_get_init_creds_opt_set_forwardable (&options, FALSE); 297 krb5_get_init_creds_opt_set_proxiable (&options, FALSE); 298 if (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) 299 krb5_get_init_creds_opt_set_preauth_list (&options, 300 old_options->preauth_list, 301 old_options->preauth_list_length); 302 303 krb5_data_zero (&result_code_string); 304 krb5_data_zero (&result_string); 305 306 ret = krb5_get_init_creds_password (context, 307 &cpw_cred, 308 client, 309 password, 310 prompter, 311 data, 312 0, 313 "kadmin/changepw", 314 &options); 315 if (ret) 316 goto out; 317 318 for(;;) { 319 password_data.data = buf1; 320 password_data.length = sizeof(buf1); 321 322 prompt.hidden = 1; 323 prompt.prompt = "New password: "; 324 prompt.reply = &password_data; 325 326 ret = (*prompter) (context, data, "Changing password", 1, &prompt); 327 if (ret) 328 goto out; 329 330 password_data.data = buf2; 331 password_data.length = sizeof(buf2); 332 333 prompt.hidden = 1; 334 prompt.prompt = "Repeat new password: "; 335 prompt.reply = &password_data; 336 337 ret = (*prompter) (context, data, "Changing password", 1, &prompt); 338 if (ret) 339 goto out; 340 341 if (strcmp (buf1, buf2) == 0) 342 break; 343 } 344 345 ret = krb5_change_password (context, 346 &cpw_cred, 347 buf1, 348 &result_code, 349 &result_code_string, 350 &result_string); 351 if (ret) 352 goto out; 353 asprintf (&p, "%s: %.*s\n", 354 result_code ? "Error" : "Success", 355 (int)result_string.length, 356 (char*)result_string.data); 357 358 ret = (*prompter) (context, data, p, 0, NULL); 359 free (p); 360 if (result_code == 0) { 361 strlcpy (newpw, buf1, newpw_sz); 362 ret = 0; 363 } else 364 ret = ENOTTY; 365 366 out: 367 memset (buf1, 0, sizeof(buf1)); 368 memset (buf2, 0, sizeof(buf2)); 369 krb5_data_free (&result_string); 370 krb5_data_free (&result_code_string); 371 krb5_free_creds_contents (context, &cpw_cred); 372 return ret; 373 } 374 375 krb5_error_code 376 krb5_get_init_creds_password(krb5_context context, 377 krb5_creds *creds, 378 krb5_principal client, 379 const char *password, 380 krb5_prompter_fct prompter, 381 void *data, 382 krb5_deltat start_time, 383 const char *in_tkt_service, 384 krb5_get_init_creds_opt *options) 385 { 386 krb5_error_code ret; 387 krb5_kdc_flags flags; 388 krb5_addresses *addrs = NULL; 389 krb5_enctype *etypes = NULL; 390 krb5_preauthtype *pre_auth_types = NULL; 391 krb5_creds this_cred; 392 krb5_kdc_rep kdc_reply; 393 char buf[BUFSIZ]; 394 krb5_data password_data; 395 int done; 396 397 ret = get_init_creds_common(context, creds, client, start_time, 398 in_tkt_service, options, 399 &addrs, &etypes, &this_cred, &pre_auth_types, 400 &flags); 401 if(ret) 402 goto out; 403 404 if (password == NULL) { 405 krb5_prompt prompt; 406 char *p; 407 408 krb5_unparse_name (context, this_cred.client, &p); 409 asprintf (&prompt.prompt, "%s's Password: ", p); 410 free (p); 411 password_data.data = buf; 412 password_data.length = sizeof(buf); 413 prompt.hidden = 1; 414 prompt.reply = &password_data; 415 416 ret = (*prompter) (context, data, NULL, 1, &prompt); 417 free (prompt.prompt); 418 if (ret) { 419 memset (buf, 0, sizeof(buf)); 420 ret = KRB5_LIBOS_PWDINTR; 421 goto out; 422 } 423 password = password_data.data; 424 } 425 426 done = 0; 427 while(!done) { 428 memset(&kdc_reply, 0, sizeof(kdc_reply)); 429 ret = krb5_get_in_cred (context, 430 flags.i, 431 addrs, 432 etypes, 433 pre_auth_types, 434 NULL, 435 krb5_password_key_proc, 436 password, 437 NULL, 438 NULL, 439 &this_cred, 440 &kdc_reply); 441 switch (ret) { 442 case 0 : 443 done = 1; 444 break; 445 case KRB5KDC_ERR_KEY_EXPIRED : 446 /* try to avoid recursion */ 447 448 if (in_tkt_service != NULL 449 && strcmp (in_tkt_service, "kadmin/changepw") == 0) 450 goto out; 451 452 ret = change_password (context, 453 client, 454 password, 455 buf, 456 sizeof(buf), 457 prompter, 458 data, 459 options); 460 if (ret) 461 goto out; 462 password = buf; 463 break; 464 default: 465 goto out; 466 } 467 } 468 469 if (prompter) 470 print_expire (context, 471 krb5_princ_realm (context, this_cred.client), 472 &kdc_reply, 473 prompter, 474 data); 475 out: 476 memset (buf, 0, sizeof(buf)); 477 if (ret == 0) 478 krb5_free_kdc_rep (context, &kdc_reply); 479 480 free (pre_auth_types); 481 free (etypes); 482 if (ret == 0 && creds) 483 *creds = this_cred; 484 else 485 krb5_free_creds_contents (context, &this_cred); 486 return ret; 487 } 488 489 krb5_error_code 490 krb5_keyblock_key_proc (krb5_context context, 491 krb5_keytype type, 492 krb5_data *salt, 493 krb5_const_pointer keyseed, 494 krb5_keyblock **key) 495 { 496 return krb5_copy_keyblock (context, keyseed, key); 497 } 498 499 krb5_error_code 500 krb5_get_init_creds_keytab(krb5_context context, 501 krb5_creds *creds, 502 krb5_principal client, 503 krb5_keytab keytab, 504 krb5_deltat start_time, 505 const char *in_tkt_service, 506 krb5_get_init_creds_opt *options) 507 { 508 krb5_error_code ret; 509 krb5_kdc_flags flags; 510 krb5_addresses *addrs = NULL; 511 krb5_enctype *etypes = NULL; 512 krb5_preauthtype *pre_auth_types = NULL; 513 krb5_creds this_cred; 514 krb5_keytab_key_proc_args *a; 515 516 ret = get_init_creds_common(context, creds, client, start_time, 517 in_tkt_service, options, 518 &addrs, &etypes, &this_cred, &pre_auth_types, 519 &flags); 520 if(ret) 521 goto out; 522 523 a = malloc (sizeof(*a)); 524 if (a == NULL) { 525 ret = ENOMEM; 526 goto out; 527 } 528 a->principal = this_cred.client; 529 a->keytab = keytab; 530 531 ret = krb5_get_in_cred (context, 532 flags.i, 533 addrs, 534 etypes, 535 pre_auth_types, 536 NULL, 537 krb5_keytab_key_proc, 538 a, 539 NULL, 540 NULL, 541 &this_cred, 542 NULL); 543 if (ret) 544 goto out; 545 free (pre_auth_types); 546 free (etypes); 547 if (creds) 548 *creds = this_cred; 549 else 550 krb5_free_creds_contents (context, &this_cred); 551 return 0; 552 553 out: 554 free (pre_auth_types); 555 free (etypes); 556 krb5_free_creds_contents (context, &this_cred); 557 return ret; 558 } 559