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.69 2001/01/05 16:32:55 joda 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]"); 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 flags.b.forwardable = forwardable_flag; 294 flags.b.proxiable = proxiable_flag; 295 flags.b.request_anonymous = anonymous_flag; 296 if(life) 297 in.times.endtime = time(NULL) + life; 298 299 ret = krb5_get_kdc_cred(context, 300 cache, 301 flags, 302 NULL, 303 NULL, 304 &in, 305 &out); 306 if(ret) { 307 krb5_warn(context, ret, "krb5_get_kdc_cred"); 308 goto out; 309 } 310 ret = krb5_cc_initialize(context, cache, in.client); 311 if(ret) { 312 krb5_free_creds (context, out); 313 krb5_warn(context, ret, "krb5_cc_initialize"); 314 goto out; 315 } 316 ret = krb5_cc_store_cred(context, cache, out); 317 krb5_free_creds (context, out); 318 if(ret) { 319 krb5_warn(context, ret, "krb5_cc_store_cred"); 320 goto out; 321 } 322 out: 323 krb5_free_creds_contents(context, &in); 324 return ret; 325 } 326 327 int 328 main (int argc, char **argv) 329 { 330 krb5_error_code ret; 331 krb5_context context; 332 krb5_ccache ccache; 333 krb5_principal principal; 334 krb5_creds cred; 335 int optind = 0; 336 krb5_get_init_creds_opt opt; 337 krb5_deltat start_time = 0; 338 krb5_deltat ticket_life = 0; 339 krb5_addresses no_addrs; 340 char passwd[256]; 341 342 set_progname (argv[0]); 343 memset(&cred, 0, sizeof(cred)); 344 345 ret = krb5_init_context (&context); 346 if (ret) 347 errx(1, "krb5_init_context failed: %d", ret); 348 349 /* XXX no way to figure out if set without explict test */ 350 if(krb5_config_get_string(context, NULL, "libdefaults", 351 "forwardable", NULL)) 352 forwardable_flag = krb5_config_get_bool (context, NULL, 353 "libdefaults", 354 "forwardable", 355 NULL); 356 357 #ifdef KRB4 358 get_v4_tgt = krb5_config_get_bool_default (context, NULL, 359 get_v4_tgt, 360 "libdefaults", 361 "krb4_get_tickets", 362 NULL); 363 #endif 364 365 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind)) 366 usage(1); 367 368 if (help_flag) 369 usage (0); 370 371 if(version_flag) { 372 print_version(NULL); 373 exit(0); 374 } 375 376 argc -= optind; 377 argv += optind; 378 379 if (argc > 1) 380 usage (1); 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 ret = krb5_cc_default (context, &ccache); 399 if (ret) 400 krb5_err (context, 1, ret, "resolving credentials cache"); 401 402 if (lifetime) { 403 int tmp = parse_time (lifetime, "s"); 404 if (tmp < 0) 405 errx (1, "unparsable time: %s", lifetime); 406 407 ticket_life = tmp; 408 } 409 if(renew_flag || validate_flag) { 410 ret = renew_validate(context, renew_flag, validate_flag, 411 ccache, server, ticket_life); 412 exit(ret != 0); 413 } 414 415 krb5_get_init_creds_opt_init (&opt); 416 417 krb5_get_init_creds_opt_set_default_flags(context, "kinit", 418 /* XXX */principal->realm, &opt); 419 420 if(forwardable_flag != -1) 421 krb5_get_init_creds_opt_set_forwardable (&opt, forwardable_flag); 422 if(proxiable_flag != -1) 423 krb5_get_init_creds_opt_set_proxiable (&opt, proxiable_flag); 424 if(anonymous_flag != -1) 425 krb5_get_init_creds_opt_set_anonymous (&opt, anonymous_flag); 426 427 if (!addrs_flag) { 428 no_addrs.len = 0; 429 no_addrs.val = NULL; 430 431 krb5_get_init_creds_opt_set_address_list (&opt, &no_addrs); 432 } 433 434 if(renew_life) { 435 int tmp = parse_time (renew_life, "s"); 436 if (tmp < 0) 437 errx (1, "unparsable time: %s", renew_life); 438 439 krb5_get_init_creds_opt_set_renew_life (&opt, tmp); 440 } else if (renewable_flag) 441 krb5_get_init_creds_opt_set_renew_life (&opt, 1 << 30); 442 443 if(ticket_life != 0) 444 krb5_get_init_creds_opt_set_tkt_life (&opt, ticket_life); 445 446 if(start_str) { 447 int tmp = parse_time (start_str, "s"); 448 if (tmp < 0) 449 errx (1, "unparsable time: %s", start_str); 450 451 start_time = tmp; 452 } 453 454 if(etype_str.num_strings) { 455 krb5_enctype *enctype = NULL; 456 int i; 457 enctype = malloc(etype_str.num_strings * sizeof(*enctype)); 458 if(enctype == NULL) 459 errx(1, "out of memory"); 460 for(i = 0; i < etype_str.num_strings; i++) { 461 ret = krb5_string_to_enctype(context, 462 etype_str.strings[i], 463 &enctype[i]); 464 if(ret) 465 errx(1, "unrecognized enctype: %s", etype_str.strings[i]); 466 } 467 krb5_get_init_creds_opt_set_etype_list(&opt, enctype, 468 etype_str.num_strings); 469 } 470 471 #ifdef KRB4 472 get_v4_tgt = krb5_config_get_bool_default (context, 473 NULL, 474 get_v4_tgt, 475 "realms", 476 krb5_princ_realm(context, 477 principal), 478 "krb4_get_tickets", 479 NULL); 480 #endif 481 482 if(use_keytab || keytab_str) { 483 krb5_keytab kt; 484 if(keytab_str) 485 ret = krb5_kt_resolve(context, keytab_str, &kt); 486 else 487 ret = krb5_kt_default(context, &kt); 488 if (ret) 489 krb5_err (context, 1, ret, "resolving keytab"); 490 ret = krb5_get_init_creds_keytab (context, 491 &cred, 492 principal, 493 kt, 494 start_time, 495 server, 496 &opt); 497 krb5_kt_close(context, kt); 498 } else { 499 char *p, *prompt; 500 501 krb5_unparse_name (context, principal, &p); 502 asprintf (&prompt, "%s's Password: ", p); 503 free (p); 504 505 if (des_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){ 506 memset(passwd, 0, sizeof(passwd)); 507 exit(1); 508 } 509 510 free (prompt); 511 512 ret = krb5_get_init_creds_password (context, 513 &cred, 514 principal, 515 passwd, 516 krb5_prompter_posix, 517 NULL, 518 start_time, 519 server, 520 &opt); 521 } 522 #ifdef KRB4 523 if (ret == KRB5KRB_AP_ERR_V4_REPLY || ret == KRB5_KDC_UNREACH) { 524 int exit_val; 525 526 exit_val = do_v4_fallback (context, principal, ticket_life, 527 use_keytab, keytab_str, 528 passwd, sizeof(passwd)); 529 memset(passwd, 0, sizeof(passwd)); 530 if (exit_val == 0 || ret == KRB5KRB_AP_ERR_V4_REPLY) { 531 krb5_free_context (context); 532 return exit_val; 533 } 534 } 535 #endif 536 memset(passwd, 0, sizeof(passwd)); 537 538 switch(ret){ 539 case 0: 540 break; 541 case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ 542 memset(passwd, 0, sizeof(passwd)); 543 exit(1); 544 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 545 case KRB5KRB_AP_ERR_MODIFIED: 546 memset(passwd, 0, sizeof(passwd)); 547 krb5_errx(context, 1, "Password incorrect"); 548 break; 549 default: 550 krb5_err(context, 1, ret, "krb5_get_init_creds"); 551 } 552 553 ret = krb5_cc_initialize (context, ccache, cred.client); 554 if (ret) 555 krb5_err (context, 1, ret, "krb5_cc_initialize"); 556 557 ret = krb5_cc_store_cred (context, ccache, &cred); 558 if (ret) 559 krb5_err (context, 1, ret, "krb5_cc_store_cred"); 560 561 #ifdef KRB4 562 if(get_v4_tgt) { 563 CREDENTIALS c; 564 ret = krb524_convert_creds_kdc(context, ccache, &cred, &c); 565 if(ret) 566 krb5_warn(context, ret, "converting creds"); 567 else 568 tf_setup(&c, c.pname, c.pinst); 569 memset(&c, 0, sizeof(c)); 570 } 571 if(do_afslog && k_hasafs()) 572 krb5_afslog(context, ccache, NULL, NULL); 573 #endif 574 krb5_free_creds_contents (context, &cred); 575 krb5_cc_close (context, ccache); 576 krb5_free_context (context); 577 return 0; 578 } 579