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 "kuser_locl.h" 35 RCSID("$Id: kinit.c,v 1.86 2001/09/29 15:59:08 assar Exp $"); 36 37 int forwardable_flag = -1; 38 int proxiable_flag = -1; 39 int renewable_flag = -1; 40 int renew_flag = 0; 41 int validate_flag = 0; 42 int version_flag = 0; 43 int help_flag = 0; 44 int addrs_flag = 1; 45 int anonymous_flag = 0; 46 char *lifetime = NULL; 47 char *renew_life = NULL; 48 char *server = NULL; 49 char *cred_cache = NULL; 50 char *start_str = NULL; 51 struct getarg_strings etype_str; 52 int use_keytab = 0; 53 char *keytab_str = NULL; 54 #ifdef KRB4 55 int get_v4_tgt = -1; 56 int do_afslog = -1; 57 int convert_524; 58 #endif 59 int fcache_version; 60 61 static struct getargs args[] = { 62 #ifdef KRB4 63 { "524init", '4', arg_flag, &get_v4_tgt, 64 "obtain version 4 TGT" }, 65 66 { "524convert", '9', arg_flag, &convert_524, 67 "only convert ticket to version 4" }, 68 69 { "afslog", 0 , arg_flag, &do_afslog, 70 "obtain afs tokens" }, 71 #endif 72 { "cache", 'c', arg_string, &cred_cache, 73 "credentials cache", "cachename" }, 74 75 { "forwardable", 'f', arg_flag, &forwardable_flag, 76 "get forwardable tickets"}, 77 78 { "keytab", 't', arg_string, &keytab_str, 79 "keytab to use", "keytabname" }, 80 81 { "lifetime", 'l', arg_string, &lifetime, 82 "lifetime of tickets", "time"}, 83 84 { "proxiable", 'p', arg_flag, &proxiable_flag, 85 "get proxiable tickets" }, 86 87 { "renew", 'R', arg_flag, &renew_flag, 88 "renew TGT" }, 89 90 { "renewable", 0, arg_flag, &renewable_flag, 91 "get renewable tickets" }, 92 93 { "renewable-life", 'r', arg_string, &renew_life, 94 "renewable lifetime of tickets", "time" }, 95 96 { "server", 'S', arg_string, &server, 97 "server to get ticket for", "principal" }, 98 99 { "start-time", 's', arg_string, &start_str, 100 "when ticket gets valid", "time" }, 101 102 { "use-keytab", 'k', arg_flag, &use_keytab, 103 "get key from keytab" }, 104 105 { "validate", 'v', arg_flag, &validate_flag, 106 "validate TGT" }, 107 108 { "enctypes", 'e', arg_strings, &etype_str, 109 "encryption types to use", "enctypes" }, 110 111 { "fcache-version", 0, arg_integer, &fcache_version, 112 "file cache version to create" }, 113 114 { "addresses", 0, arg_negative_flag, &addrs_flag, 115 "request a ticket with no addresses" }, 116 117 { "anonymous", 0, arg_flag, &anonymous_flag, 118 "request an anonymous ticket" }, 119 120 { "version", 0, arg_flag, &version_flag }, 121 { "help", 0, arg_flag, &help_flag } 122 }; 123 124 static void 125 usage (int ret) 126 { 127 arg_printusage (args, 128 sizeof(args)/sizeof(*args), 129 NULL, 130 "[principal [command]]"); 131 exit (ret); 132 } 133 134 #ifdef KRB4 135 /* for when the KDC tells us it's a v4 one, we try to talk that */ 136 137 static int 138 key_to_key(const char *user, 139 char *instance, 140 const char *realm, 141 const void *arg, 142 des_cblock *key) 143 { 144 memcpy(key, arg, sizeof(des_cblock)); 145 return 0; 146 } 147 148 static int 149 do_v4_fallback (krb5_context context, 150 const krb5_principal principal, 151 int lifetime, 152 int use_srvtab, const char *srvtab_str, 153 const char *passwd) 154 { 155 int ret; 156 krb_principal princ; 157 des_cblock key; 158 krb5_error_code kret; 159 160 if (lifetime == 0) 161 lifetime = DEFAULT_TKT_LIFE; 162 else 163 lifetime = krb_time_to_life (0, lifetime); 164 165 kret = krb5_524_conv_principal (context, principal, 166 princ.name, 167 princ.instance, 168 princ.realm); 169 if (kret) { 170 krb5_warn (context, kret, "krb5_524_conv_principal"); 171 return 1; 172 } 173 174 if (use_srvtab || srvtab_str) { 175 if (srvtab_str == NULL) 176 srvtab_str = KEYFILE; 177 178 ret = read_service_key (princ.name, princ.instance, princ.realm, 179 0, srvtab_str, (char *)&key); 180 if (ret) { 181 warnx ("read_service_key %s: %s", srvtab_str, 182 krb_get_err_text (ret)); 183 return 1; 184 } 185 ret = krb_get_in_tkt (princ.name, princ.instance, princ.realm, 186 KRB_TICKET_GRANTING_TICKET, princ.realm, 187 lifetime, key_to_key, NULL, key); 188 } else { 189 ret = krb_get_pw_in_tkt2(princ.name, princ.instance, princ.realm, 190 KRB_TICKET_GRANTING_TICKET, princ.realm, 191 lifetime, passwd, &key); 192 } 193 memset (key, 0, sizeof(key)); 194 if (ret) { 195 warnx ("%s", krb_get_err_text(ret)); 196 return 1; 197 } 198 if (do_afslog && k_hasafs()) { 199 if ((ret = krb_afslog(NULL, NULL)) != 0 && ret != KDC_PR_UNKNOWN) { 200 if(ret > 0) 201 warnx ("%s", krb_get_err_text(ret)); 202 else 203 warnx ("failed to store AFS token"); 204 } 205 } 206 return 0; 207 } 208 209 210 /* 211 * the special version of get_default_principal that takes v4 into account 212 */ 213 214 static krb5_error_code 215 kinit_get_default_principal (krb5_context context, 216 krb5_principal *princ) 217 { 218 krb5_error_code ret; 219 krb5_ccache id; 220 krb_principal v4_princ; 221 int kret; 222 223 ret = krb5_cc_default (context, &id); 224 if (ret == 0) { 225 ret = krb5_cc_get_principal (context, id, princ); 226 krb5_cc_close (context, id); 227 if (ret == 0) 228 return 0; 229 } 230 231 kret = krb_get_tf_fullname (tkt_string(), 232 v4_princ.name, 233 v4_princ.instance, 234 v4_princ.realm); 235 if (kret == KSUCCESS) { 236 ret = krb5_425_conv_principal (context, 237 v4_princ.name, 238 v4_princ.instance, 239 v4_princ.realm, 240 princ); 241 if (ret == 0) 242 return 0; 243 } 244 return krb5_get_default_principal (context, princ); 245 } 246 247 #else /* !KRB4 */ 248 249 static krb5_error_code 250 kinit_get_default_principal (krb5_context context, 251 krb5_principal *princ) 252 { 253 return krb5_get_default_principal (context, princ); 254 } 255 256 #endif /* !KRB4 */ 257 258 static krb5_error_code 259 get_server(krb5_context context, 260 krb5_principal client, 261 const char *server, 262 krb5_principal *princ) 263 { 264 krb5_realm *client_realm; 265 if(server) 266 return krb5_parse_name(context, server, princ); 267 268 client_realm = krb5_princ_realm (context, client); 269 return krb5_make_principal(context, princ, *client_realm, 270 KRB5_TGS_NAME, *client_realm, NULL); 271 } 272 273 #ifdef KRB4 274 static krb5_error_code 275 do_524init(krb5_context context, krb5_ccache ccache, 276 krb5_creds *creds, const char *server) 277 { 278 krb5_error_code ret; 279 CREDENTIALS c; 280 krb5_creds in_creds, *real_creds; 281 282 if(creds != NULL) 283 real_creds = creds; 284 else { 285 krb5_principal client; 286 krb5_cc_get_principal(context, ccache, &client); 287 memset(&in_creds, 0, sizeof(in_creds)); 288 ret = get_server(context, client, server, &in_creds.server); 289 if(ret) 290 return ret; 291 ret = krb5_get_credentials(context, 0, ccache, &in_creds, &real_creds); 292 if(ret) 293 return ret; 294 } 295 ret = krb524_convert_creds_kdc_ccache(context, ccache, real_creds, &c); 296 if(ret) 297 krb5_warn(context, ret, "converting creds"); 298 else { 299 int tret = tf_setup(&c, c.pname, c.pinst); 300 if(tret) 301 krb5_warnx(context, "saving v4 creds: %s", krb_get_err_text(tret)); 302 } 303 304 if(creds == NULL) 305 krb5_free_creds(context, real_creds); 306 memset(&c, 0, sizeof(c)); 307 308 return ret; 309 } 310 #endif 311 312 static int 313 renew_validate(krb5_context context, 314 int renew, 315 int validate, 316 krb5_ccache cache, 317 const char *server, 318 krb5_deltat life) 319 { 320 krb5_error_code ret; 321 krb5_creds in, *out; 322 krb5_kdc_flags flags; 323 324 memset(&in, 0, sizeof(in)); 325 326 ret = krb5_cc_get_principal(context, cache, &in.client); 327 if(ret) { 328 krb5_warn(context, ret, "krb5_cc_get_principal"); 329 return ret; 330 } 331 ret = get_server(context, in.client, server, &in.server); 332 if(ret) { 333 krb5_warn(context, ret, "get_server"); 334 goto out; 335 } 336 flags.i = 0; 337 flags.b.renewable = flags.b.renew = renew; 338 flags.b.validate = validate; 339 if (forwardable_flag != -1) 340 flags.b.forwardable = forwardable_flag; 341 if (proxiable_flag != -1) 342 flags.b.proxiable = proxiable_flag; 343 if (anonymous_flag != -1) 344 flags.b.request_anonymous = anonymous_flag; 345 if(life) 346 in.times.endtime = time(NULL) + life; 347 348 ret = krb5_get_kdc_cred(context, 349 cache, 350 flags, 351 NULL, 352 NULL, 353 &in, 354 &out); 355 if(ret) { 356 krb5_warn(context, ret, "krb5_get_kdc_cred"); 357 goto out; 358 } 359 ret = krb5_cc_initialize(context, cache, in.client); 360 if(ret) { 361 krb5_free_creds (context, out); 362 krb5_warn(context, ret, "krb5_cc_initialize"); 363 goto out; 364 } 365 ret = krb5_cc_store_cred(context, cache, out); 366 367 #ifdef KRB4 368 if(ret == 0 && server == NULL) { 369 /* only do this if it's a general renew-my-tgt request */ 370 if(get_v4_tgt) 371 do_524init(context, cache, out, NULL); 372 373 if(do_afslog && k_hasafs()) 374 krb5_afslog(context, cache, NULL, NULL); 375 } 376 #endif 377 378 krb5_free_creds (context, out); 379 if(ret) { 380 krb5_warn(context, ret, "krb5_cc_store_cred"); 381 goto out; 382 } 383 out: 384 krb5_free_creds_contents(context, &in); 385 return ret; 386 } 387 388 static krb5_error_code 389 get_new_tickets(krb5_context context, 390 krb5_principal principal, 391 krb5_ccache ccache, 392 krb5_deltat ticket_life) 393 { 394 krb5_error_code ret; 395 krb5_get_init_creds_opt opt; 396 krb5_addresses no_addrs; 397 krb5_creds cred; 398 char passwd[256]; 399 krb5_deltat start_time = 0; 400 401 memset(&cred, 0, sizeof(cred)); 402 403 krb5_get_init_creds_opt_init (&opt); 404 405 krb5_get_init_creds_opt_set_default_flags(context, "kinit", 406 /* XXX */principal->realm, &opt); 407 408 if(forwardable_flag != -1) 409 krb5_get_init_creds_opt_set_forwardable (&opt, forwardable_flag); 410 if(proxiable_flag != -1) 411 krb5_get_init_creds_opt_set_proxiable (&opt, proxiable_flag); 412 if(anonymous_flag != -1) 413 krb5_get_init_creds_opt_set_anonymous (&opt, anonymous_flag); 414 415 if (!addrs_flag) { 416 no_addrs.len = 0; 417 no_addrs.val = NULL; 418 419 krb5_get_init_creds_opt_set_address_list (&opt, &no_addrs); 420 } 421 422 if(renew_life) { 423 int tmp = parse_time (renew_life, "s"); 424 if (tmp < 0) 425 errx (1, "unparsable time: %s", renew_life); 426 427 krb5_get_init_creds_opt_set_renew_life (&opt, tmp); 428 } else if (renewable_flag == 1) 429 krb5_get_init_creds_opt_set_renew_life (&opt, 1 << 30); 430 431 if(ticket_life != 0) 432 krb5_get_init_creds_opt_set_tkt_life (&opt, ticket_life); 433 434 if(start_str) { 435 int tmp = parse_time (start_str, "s"); 436 if (tmp < 0) 437 errx (1, "unparsable time: %s", start_str); 438 439 start_time = tmp; 440 } 441 442 if(etype_str.num_strings) { 443 krb5_enctype *enctype = NULL; 444 int i; 445 enctype = malloc(etype_str.num_strings * sizeof(*enctype)); 446 if(enctype == NULL) 447 errx(1, "out of memory"); 448 for(i = 0; i < etype_str.num_strings; i++) { 449 ret = krb5_string_to_enctype(context, 450 etype_str.strings[i], 451 &enctype[i]); 452 if(ret) 453 errx(1, "unrecognized enctype: %s", etype_str.strings[i]); 454 } 455 krb5_get_init_creds_opt_set_etype_list(&opt, enctype, 456 etype_str.num_strings); 457 } 458 459 if(use_keytab || keytab_str) { 460 krb5_keytab kt; 461 if(keytab_str) 462 ret = krb5_kt_resolve(context, keytab_str, &kt); 463 else 464 ret = krb5_kt_default(context, &kt); 465 if (ret) 466 krb5_err (context, 1, ret, "resolving keytab"); 467 ret = krb5_get_init_creds_keytab (context, 468 &cred, 469 principal, 470 kt, 471 start_time, 472 server, 473 &opt); 474 krb5_kt_close(context, kt); 475 } else { 476 char *p, *prompt; 477 478 krb5_unparse_name (context, principal, &p); 479 asprintf (&prompt, "%s's Password: ", p); 480 free (p); 481 482 if (des_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){ 483 memset(passwd, 0, sizeof(passwd)); 484 exit(1); 485 } 486 487 free (prompt); 488 489 ret = krb5_get_init_creds_password (context, 490 &cred, 491 principal, 492 passwd, 493 krb5_prompter_posix, 494 NULL, 495 start_time, 496 server, 497 &opt); 498 } 499 #ifdef KRB4 500 if (ret == KRB5KRB_AP_ERR_V4_REPLY || ret == KRB5_KDC_UNREACH) { 501 int exit_val; 502 503 exit_val = do_v4_fallback (context, principal, ticket_life, 504 use_keytab, keytab_str, passwd); 505 get_v4_tgt = 0; 506 do_afslog = 0; 507 memset(passwd, 0, sizeof(passwd)); 508 if (exit_val == 0 || ret == KRB5KRB_AP_ERR_V4_REPLY) 509 return exit_val; 510 } 511 #endif 512 memset(passwd, 0, sizeof(passwd)); 513 514 switch(ret){ 515 case 0: 516 break; 517 case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ 518 exit(1); 519 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 520 case KRB5KRB_AP_ERR_MODIFIED: 521 krb5_errx(context, 1, "Password incorrect"); 522 break; 523 default: 524 krb5_err(context, 1, ret, "krb5_get_init_creds"); 525 } 526 527 ret = krb5_cc_initialize (context, ccache, cred.client); 528 if (ret) 529 krb5_err (context, 1, ret, "krb5_cc_initialize"); 530 531 ret = krb5_cc_store_cred (context, ccache, &cred); 532 if (ret) 533 krb5_err (context, 1, ret, "krb5_cc_store_cred"); 534 535 krb5_free_creds_contents (context, &cred); 536 537 return 0; 538 } 539 540 int 541 main (int argc, char **argv) 542 { 543 krb5_error_code ret; 544 krb5_context context; 545 krb5_ccache ccache; 546 krb5_principal principal; 547 int optind = 0; 548 krb5_deltat ticket_life = 0; 549 550 setprogname (argv[0]); 551 552 ret = krb5_init_context (&context); 553 if (ret) 554 errx(1, "krb5_init_context failed: %d", ret); 555 556 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind)) 557 usage(1); 558 559 if (help_flag) 560 usage (0); 561 562 if(version_flag) { 563 print_version(NULL); 564 exit(0); 565 } 566 567 argc -= optind; 568 argv += optind; 569 570 if (argv[0]) { 571 ret = krb5_parse_name (context, argv[0], &principal); 572 if (ret) 573 krb5_err (context, 1, ret, "krb5_parse_name"); 574 } else { 575 ret = kinit_get_default_principal (context, &principal); 576 if (ret) 577 krb5_err (context, 1, ret, "krb5_get_default_principal"); 578 } 579 580 if(fcache_version) 581 krb5_set_fcache_version(context, fcache_version); 582 583 if(cred_cache) 584 ret = krb5_cc_resolve(context, cred_cache, &ccache); 585 else { 586 if(argc > 1) { 587 char s[1024]; 588 ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache); 589 if(ret) 590 krb5_err(context, 1, ret, "creating cred cache"); 591 snprintf(s, sizeof(s), "%s:%s", 592 krb5_cc_get_type(context, ccache), 593 krb5_cc_get_name(context, ccache)); 594 setenv("KRB5CCNAME", s, 1); 595 #ifdef KRB4 596 { 597 int fd; 598 snprintf(s, sizeof(s), "%s_XXXXXX", TKT_ROOT); 599 if((fd = mkstemp(s)) >= 0) { 600 close(fd); 601 setenv("KRBTKFILE", s, 1); 602 if (k_hasafs ()) 603 k_setpag(); 604 } 605 } 606 #endif 607 } else 608 ret = krb5_cc_default (context, &ccache); 609 } 610 if (ret) 611 krb5_err (context, 1, ret, "resolving credentials cache"); 612 613 if (lifetime) { 614 int tmp = parse_time (lifetime, "s"); 615 if (tmp < 0) 616 errx (1, "unparsable time: %s", lifetime); 617 618 ticket_life = tmp; 619 } 620 #ifdef KRB4 621 if(get_v4_tgt == -1) 622 krb5_appdefault_boolean(context, "kinit", 623 krb5_principal_get_realm(context, principal), 624 "krb4_get_tickets", TRUE, &get_v4_tgt); 625 if(do_afslog == -1) 626 krb5_appdefault_boolean(context, "kinit", 627 krb5_principal_get_realm(context, principal), 628 "afslog", TRUE, &do_afslog); 629 #endif 630 631 632 if(renew_flag || validate_flag) { 633 ret = renew_validate(context, renew_flag, validate_flag, 634 ccache, server, ticket_life); 635 exit(ret != 0); 636 } 637 638 #ifdef KRB4 639 if(!convert_524) 640 #endif 641 get_new_tickets(context, principal, ccache, ticket_life); 642 643 #ifdef KRB4 644 if(get_v4_tgt) 645 do_524init(context, ccache, NULL, server); 646 if(do_afslog && k_hasafs()) 647 krb5_afslog(context, ccache, NULL, NULL); 648 #endif 649 if(argc > 1) { 650 simple_execvp(argv[1], argv+1); 651 krb5_cc_destroy(context, ccache); 652 #ifdef KRB4 653 dest_tkt(); 654 if(k_hasafs()) 655 k_unlog(); 656 #endif 657 } else 658 krb5_cc_close (context, ccache); 659 krb5_free_principal(context, principal); 660 krb5_free_context (context); 661 return 0; 662 } 663