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