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