1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * clients/kinit/kinit.c 9 * 10 * Copyright 1990 by the Massachusetts Institute of Technology. 11 * All Rights Reserved. 12 * 13 * Export of this software from the United States of America may 14 * require a specific license from the United States Government. 15 * It is the responsibility of any person or organization contemplating 16 * export to obtain such a license before exporting. 17 * 18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 19 * distribute this software and its documentation for any purpose and 20 * without fee is hereby granted, provided that the above copyright 21 * notice appear in all copies and that both that copyright notice and 22 * this permission notice appear in supporting documentation, and that 23 * the name of M.I.T. not be used in advertising or publicity pertaining 24 * to distribution of the software without specific, written prior 25 * permission. Furthermore if you modify this software you must label 26 * your software as modified software and not distribute it in such a 27 * fashion that it might be confused with the original M.I.T. software. 28 * M.I.T. makes no representations about the suitability of 29 * this software for any purpose. It is provided "as is" without express 30 * or implied warranty. 31 * 32 * 33 * Initialize a credentials cache. 34 */ 35 #include <k5-int.h> 36 #include <profile/prof_int.h> 37 #include <com_err.h> 38 #include <libintl.h> 39 40 #include <krb5.h> 41 #ifdef KRB5_KRB4_COMPAT 42 #include <kerberosIV/krb.h> 43 #define HAVE_KRB524 44 #else 45 #undef HAVE_KRB524 46 #endif 47 #include <string.h> 48 #include <stdio.h> 49 #include <time.h> 50 #include <errno.h> 51 #include <com_err.h> 52 #include <netdb.h> 53 #include <locale.h> 54 55 #ifdef GETOPT_LONG 56 #include <getopt.h> 57 #else 58 #ifdef HAVE_UNISTD_H 59 #include <unistd.h> 60 #ifdef sun 61 /* SunOS4 unistd didn't declare these; okay to make unconditional? */ 62 extern int optind; 63 extern char *optarg; 64 #endif /* sun */ 65 #else 66 extern int optind; 67 extern char *optarg; 68 extern int getopt(); 69 #endif /* HAVE_UNISTD_H */ 70 #endif /* GETOPT_LONG */ 71 72 #ifndef _WIN32 73 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x)) 74 #else 75 #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x)) 76 #endif 77 78 #ifdef HAVE_PWD_H 79 #include <pwd.h> 80 static 81 char * get_name_from_os() 82 { 83 struct passwd *pw; 84 if ((pw = getpwuid((int) getuid()))) 85 return pw->pw_name; 86 return 0; 87 } 88 #else /* HAVE_PWD_H */ 89 #ifdef _WIN32 90 static 91 char * get_name_from_os() 92 { 93 static char name[1024]; 94 DWORD name_size = sizeof(name); 95 if (GetUserName(name, &name_size)) { 96 name[sizeof(name)-1] = 0; /* Just to be extra safe */ 97 return name; 98 } else { 99 return 0; 100 } 101 } 102 #else /* _WIN32 */ 103 static 104 char * get_name_from_os() 105 { 106 return 0; 107 } 108 #endif /* _WIN32 */ 109 #endif /* HAVE_PWD_H */ 110 111 static char* progname_v5 = 0; 112 #ifdef KRB5_KRB4_COMPAT 113 static char* progname_v4 = 0; 114 static char* progname_v524 = 0; 115 #endif 116 #include <locale.h> 117 118 static int got_k5 = 0; 119 static int got_k4 = 0; 120 121 static int default_k5 = 1; 122 #if defined(KRB5_KRB4_COMPAT) && defined(KINIT_DEFAULT_BOTH) 123 static int default_k4 = 1; 124 #else 125 static int default_k4 = 0; 126 #endif 127 128 static int authed_k5 = 0; 129 static int authed_k4 = 0; 130 131 #define KRB4_BACKUP_DEFAULT_LIFE_SECS 24*60*60 /* 1 day */ 132 #define ROOT_UNAME "root" 133 134 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type; 135 136 struct k_opts 137 { 138 /* in seconds */ 139 krb5_deltat starttime; 140 krb5_deltat lifetime; 141 krb5_deltat rlife; 142 143 int forwardable; 144 int proxiable; 145 int addresses; 146 147 int not_forwardable; 148 int not_proxiable; 149 int no_addresses; 150 151 int verbose; 152 153 char* principal_name; 154 char* service_name; 155 char* keytab_name; 156 char* k5_cache_name; 157 char* k4_cache_name; 158 159 action_type action; 160 161 int num_pa_opts; 162 krb5_gic_opt_pa_data *pa_opts; 163 }; 164 165 int forwardable_flag = 0; 166 int renewable_flag = 0; 167 int proxiable_flag = 0; 168 int no_address_flag = 0; 169 profile_options_boolean config_option[] = { 170 { "forwardable", &forwardable_flag, 0 }, 171 { "renewable", &renewable_flag, 0 }, 172 { "proxiable", &proxiable_flag, 0 }, 173 { "no_addresses", &no_address_flag, 0 }, 174 { NULL, NULL, 0 } 175 }; 176 177 char *renew_timeval=NULL; 178 char *life_timeval=NULL; 179 int lifetime_specified; 180 int renewtime_specified; 181 profile_option_strings config_times[] = { 182 { "max_life", &life_timeval, 0 }, 183 { "max_renewable_life", &renew_timeval, 0 }, 184 { NULL, NULL, 0 } 185 }; 186 187 struct k5_data 188 { 189 krb5_context ctx; 190 krb5_ccache cc; 191 krb5_principal me; 192 char* name; 193 }; 194 195 struct k4_data 196 { 197 krb5_deltat lifetime; 198 #ifdef KRB5_KRB4_COMPAT 199 char aname[ANAME_SZ + 1]; 200 char inst[INST_SZ + 1]; 201 char realm[REALM_SZ + 1]; 202 char name[ANAME_SZ + 1 + INST_SZ + 1 + REALM_SZ + 1]; 203 #endif 204 }; 205 206 char *realmdef[] = { "realms", NULL, "kinit", NULL }; 207 char *appdef[] = { "appdefaults", "kinit", NULL }; 208 209 #define krb_realm (*(realmdef + 1)) 210 211 #define lifetime_specified config_times[0].found 212 #define renewtime_specified config_times[1].found 213 214 /* 215 * Try no preauthentication first; then try the encrypted timestamp 216 */ 217 krb5_preauthtype * preauth = NULL; 218 krb5_preauthtype preauth_list[2] = { 0, -1 }; 219 220 static void _kwarnd_add_warning(char *, char *, time_t); 221 static void _kwarnd_del_warning(char *, char *); 222 223 #ifdef GETOPT_LONG 224 /* if struct[2] == NULL, then long_getopt acts as if the short flag 225 struct[3] was specified. If struct[2] != NULL, then struct[3] is 226 stored in *(struct[2]), the array index which was specified is 227 stored in *index, and long_getopt() returns 0. */ 228 229 struct option long_options[] = { 230 { "noforwardable", 0, NULL, 'F' }, 231 { "noproxiable", 0, NULL, 'P' }, 232 { "addresses", 0, NULL, 'a'}, 233 { "forwardable", 0, NULL, 'f' }, 234 { "proxiable", 0, NULL, 'p' }, 235 { "noaddresses", 0, NULL, 'A' }, 236 { NULL, 0, NULL, 0 } 237 }; 238 239 #define GETOPT(argc, argv, str) getopt_long(argc, argv, str, long_options, 0) 240 #else 241 #define GETOPT(argc, argv, str) getopt(argc, argv, str) 242 #endif 243 244 static void 245 usage(progname) 246 char *progname; 247 { 248 #define USAGE_BREAK "\n\t" 249 250 #ifdef GETOPT_LONG 251 #define USAGE_LONG_FORWARDABLE " | --forwardable | --noforwardable" 252 #define USAGE_LONG_PROXIABLE " | --proxiable | --noproxiable" 253 #define USAGE_LONG_ADDRESSES " | --addresses | --noaddresses" 254 #define USAGE_BREAK_LONG USAGE_BREAK 255 #else 256 #define USAGE_LONG_FORWARDABLE "" 257 #define USAGE_LONG_PROXIABLE "" 258 #define USAGE_LONG_ADDRESSES "" 259 #define USAGE_BREAK_LONG "" 260 #endif 261 262 fprintf(stderr, "%s : %s [-V] " 263 "[-l lifetime] [-s start_time] " 264 USAGE_BREAK 265 "[-r renewable_life] " 266 "[-f | -F" USAGE_LONG_FORWARDABLE "] " 267 USAGE_BREAK_LONG 268 "[-p | -P" USAGE_LONG_PROXIABLE "] " 269 USAGE_BREAK_LONG 270 "[-a | -A" USAGE_LONG_ADDRESSES "] " 271 USAGE_BREAK 272 "[-v] [-R] " 273 "[-k [-t keytab_file]] " 274 "[-c cachename] " 275 USAGE_BREAK 276 "[-S service_name]" 277 "[-X <attribute>[=<value>]] [principal]" 278 "\n\n", 279 gettext("Usage"), progname); 280 281 #define KRB_AVAIL_STRING(x) ((x)?gettext("available"):gettext("not available")) 282 283 #define OPTTYPE_KRB5 "5" 284 #define OPTTYPE_KRB4 "4" 285 #define OPTTYPE_EITHER "Either 4 or 5" 286 #ifdef HAVE_KRB524 287 #define OPTTYPE_BOTH "5, or both 5 and 4" 288 #else 289 #define OPTTYPE_BOTH "5" 290 #endif 291 292 #ifdef KRB5_KRB4_COMPAT 293 #define USAGE_OPT_FMT "%s%-50s%s\n" 294 #define ULINE(indent, col1, col2) \ 295 fprintf(stderr, USAGE_OPT_FMT, indent, col1, col2) 296 #else 297 #define USAGE_OPT_FMT "%s%s\n" 298 #define ULINE(indent, col1, col2) \ 299 fprintf(stderr, USAGE_OPT_FMT, indent, col1) 300 #endif 301 302 ULINE(" ", "options:", "valid with Kerberos:"); 303 fprintf(stderr, "\t-5 Kerberos 5 (%s)\n", KRB_AVAIL_STRING(got_k5)); 304 fprintf(stderr, "\t-4 Kerberos 4 (%s)\n", KRB_AVAIL_STRING(got_k4)); 305 fprintf(stderr, "\t (Default behavior is to try %s%s%s%s)\n", 306 default_k5?"Kerberos 5":"", 307 (default_k5 && default_k4)?gettext(" and "):"", 308 default_k4?"Kerberos 4":"", 309 (!default_k5 && !default_k4)?gettext("neither"):""); 310 ULINE("\t", gettext("-V verbose"), OPTTYPE_EITHER); 311 ULINE("\t", gettext("-l lifetime"), OPTTYPE_EITHER); 312 ULINE("\t", gettext("-s start time"), OPTTYPE_KRB5); 313 ULINE("\t", gettext("-r renewable lifetime"), OPTTYPE_KRB5); 314 ULINE("\t", gettext("-f forwardable"), OPTTYPE_KRB5); 315 ULINE("\t", gettext("-F not forwardable"), OPTTYPE_KRB5); 316 ULINE("\t", gettext("-p proxiable"), OPTTYPE_KRB5); 317 ULINE("\t", gettext("-P not proxiable"), OPTTYPE_KRB5); 318 ULINE("\t", gettext("-A do not include addresses"), OPTTYPE_KRB5); 319 ULINE("\t", gettext("-a include addresses"), OPTTYPE_KRB5); 320 ULINE("\t", gettext("-v validate"), OPTTYPE_KRB5); 321 ULINE("\t", gettext("-R renew"), OPTTYPE_BOTH); 322 ULINE("\t", gettext("-k use keytab"), OPTTYPE_BOTH); 323 ULINE("\t", gettext("-t filename of keytab to use"), OPTTYPE_BOTH); 324 ULINE("\t", gettext("-c Kerberos 5 cache name"), OPTTYPE_KRB5); 325 /* This options is not yet available: */ 326 /* ULINE("\t", "-C Kerberos 4 cache name", OPTTYPE_KRB4); */ 327 ULINE("\t", gettext("-S service"), OPTTYPE_BOTH); 328 ULINE("\t", gettext("-X <attribute>[=<value>]"), OPTTYPE_KRB5); 329 exit(2); 330 } 331 332 static krb5_context errctx; 333 static void extended_com_err_fn (const char *myprog, errcode_t code, 334 const char *fmt, va_list args) 335 { 336 const char *emsg; 337 emsg = krb5_get_error_message (errctx, code); 338 fprintf (stderr, "%s: %s ", myprog, emsg); 339 krb5_free_error_message (errctx, emsg); 340 vfprintf (stderr, fmt, args); 341 fprintf (stderr, "\n"); 342 } 343 344 static int 345 add_preauth_opt(struct k_opts *opts, char *av) 346 { 347 char *sep, *v; 348 krb5_gic_opt_pa_data *p, *x; 349 350 if (opts->num_pa_opts == 0) { 351 opts->pa_opts = malloc(sizeof(krb5_gic_opt_pa_data)); 352 if (opts->pa_opts == NULL) 353 return ENOMEM; 354 } else { 355 size_t newsize = (opts->num_pa_opts + 1) * sizeof(krb5_gic_opt_pa_data); 356 x = realloc(opts->pa_opts, newsize); 357 if (x == NULL) 358 return ENOMEM; 359 opts->pa_opts = x; 360 } 361 p = &opts->pa_opts[opts->num_pa_opts]; 362 sep = strchr(av, '='); 363 if (sep) { 364 *sep = '\0'; 365 v = ++sep; 366 p->value = v; 367 } else { 368 p->value = "yes"; 369 } 370 p->attr = av; 371 opts->num_pa_opts++; 372 return 0; 373 } 374 375 static char * 376 parse_options(argc, argv, opts, progname) 377 int argc; 378 char **argv; 379 struct k_opts* opts; 380 char *progname; 381 { 382 krb5_error_code code; 383 int errflg = 0; 384 int use_k4 = 0; 385 int use_k5 = 0; 386 int i; 387 388 while ((i = GETOPT(argc, argv, "r:fpFP54aAVl:s:c:kt:RS:vX:")) 389 != -1) { 390 switch (i) { 391 case 'V': 392 opts->verbose = 1; 393 break; 394 case 'l': 395 /* Lifetime */ 396 code = krb5_string_to_deltat(optarg, &opts->lifetime); 397 if (code != 0 || opts->lifetime == 0) { 398 fprintf(stderr, gettext("Bad lifetime value %s\n"), optarg); 399 errflg++; 400 } 401 break; 402 case 'r': 403 /* Renewable Time */ 404 code = krb5_string_to_deltat(optarg, &opts->rlife); 405 if (code != 0 || opts->rlife == 0) { 406 fprintf(stderr, gettext("Bad lifetime value %s\n"), optarg); 407 errflg++; 408 } 409 break; 410 case 'f': 411 opts->forwardable = 1; 412 break; 413 case 'F': 414 opts->not_forwardable = 1; 415 break; 416 case 'p': 417 opts->proxiable = 1; 418 break; 419 case 'P': 420 opts->not_proxiable = 1; 421 break; 422 case 'a': 423 /* Note: This is supported only with GETOPT_LONG */ 424 opts->addresses = 1; 425 break; 426 case 'A': 427 opts->no_addresses = 1; 428 break; 429 case 's': 430 code = krb5_string_to_deltat(optarg, &opts->starttime); 431 if (code != 0 || opts->starttime == 0) { 432 krb5_timestamp abs_starttime; 433 434 code = krb5_string_to_timestamp(optarg, &abs_starttime); 435 if (code != 0 || abs_starttime == 0) { 436 fprintf(stderr, gettext("Bad start time value %s\n"), optarg); 437 errflg++; 438 } else { 439 opts->starttime = abs_starttime - time(0); 440 } 441 } 442 break; 443 case 'S': 444 opts->service_name = optarg; 445 break; 446 case 'k': 447 opts->action = INIT_KT; 448 break; 449 case 't': 450 if (opts->keytab_name) 451 { 452 fprintf(stderr, gettext("Only one -t option allowed.\n")); 453 errflg++; 454 } else { 455 opts->keytab_name = optarg; 456 } 457 break; 458 case 'R': 459 opts->action = RENEW; 460 break; 461 case 'v': 462 opts->action = VALIDATE; 463 break; 464 case 'c': 465 if (opts->k5_cache_name) 466 { 467 fprintf(stderr, gettext("Only one -c option allowed\n")); 468 errflg++; 469 } else { 470 opts->k5_cache_name = optarg; 471 } 472 break; 473 case 'X': 474 code = add_preauth_opt(opts, optarg); 475 if (code) 476 { 477 com_err(progname, code, "while adding preauth option"); 478 errflg++; 479 } 480 break; 481 #if 0 482 /* 483 A little more work is needed before we can enable this 484 option. 485 */ 486 case 'C': 487 if (opts->k4_cache_name) 488 { 489 fprintf(stderr, "Only one -C option allowed\n"); 490 errflg++; 491 } else { 492 opts->k4_cache_name = optarg; 493 } 494 break; 495 #endif 496 case '4': 497 if (!got_k4) 498 { 499 #ifdef KRB5_KRB4_COMPAT 500 fprintf(stderr, "Kerberos 4 support could not be loaded\n"); 501 #else 502 fprintf(stderr, gettext("This was not built with Kerberos 4 support\n")); 503 #endif 504 exit(3); 505 } 506 use_k4 = 1; 507 break; 508 case '5': 509 if (!got_k5) 510 { 511 fprintf(stderr, gettext("Kerberos 5 support could not be loaded\n")); 512 exit(3); 513 } 514 use_k5 = 1; 515 break; 516 default: 517 errflg++; 518 break; 519 } 520 } 521 522 if (opts->forwardable && opts->not_forwardable) 523 { 524 fprintf(stderr, gettext("Only one of -f and -F allowed\n")); 525 errflg++; 526 } 527 if (opts->proxiable && opts->not_proxiable) 528 { 529 fprintf(stderr, gettext("Only one of -p and -P allowed\n")); 530 errflg++; 531 } 532 if (opts->addresses && opts->no_addresses) 533 { 534 fprintf(stderr, gettext("Only one of -a and -A allowed\n")); 535 errflg++; 536 } 537 538 if (argc - optind > 1) { 539 fprintf(stderr, gettext("Extra arguments (starting with \"%s\").\n"), 540 argv[optind+1]); 541 errflg++; 542 } 543 544 /* At this point, if errorless, we know we only have one option 545 selection */ 546 if (!use_k5 && !use_k4) { 547 use_k5 = default_k5; 548 use_k4 = default_k4; 549 } 550 551 /* Now, we encode the OPTTYPE stuff here... */ 552 if (!use_k5 && 553 (opts->starttime || opts->rlife || opts->forwardable || 554 opts->proxiable || opts->addresses || opts->not_forwardable || 555 opts->not_proxiable || opts->no_addresses || 556 (opts->action == VALIDATE) || opts->k5_cache_name)) 557 { 558 fprintf(stderr, gettext("Specified option that requires Kerberos 5\n")); 559 errflg++; 560 } 561 if (!use_k4 && 562 opts->k4_cache_name) 563 { 564 fprintf(stderr, gettext("Specified option that require Kerberos 4\n")); 565 errflg++; 566 } 567 if ( 568 #ifdef HAVE_KRB524 569 !use_k5 570 #else 571 use_k4 572 #endif 573 && (opts->service_name || opts->keytab_name || 574 (opts->action == INIT_KT) || (opts->action == RENEW)) 575 ) 576 { 577 fprintf(stderr, gettext("Specified option that requires Kerberos 5\n")); 578 errflg++; 579 } 580 581 if (errflg) { 582 usage(progname); 583 } 584 585 got_k5 = got_k5 && use_k5; 586 got_k4 = got_k4 && use_k4; 587 588 opts->principal_name = (optind == argc-1) ? argv[optind] : 0; 589 return opts->principal_name; 590 } 591 592 static int 593 k5_begin(opts, k5, k4) 594 struct k_opts* opts; 595 struct k5_data* k5; 596 struct k4_data* k4; 597 { 598 char* progname = progname_v5; 599 krb5_error_code code = 0; 600 601 if (!got_k5) 602 return 0; 603 604 code = krb5_init_context(&k5->ctx); 605 if (code) { 606 com_err(progname, code, gettext("while initializing Kerberos 5 library")); 607 return 0; 608 } 609 errctx = k5->ctx; 610 if (opts->k5_cache_name) 611 { 612 code = krb5_cc_resolve(k5->ctx, opts->k5_cache_name, &k5->cc); 613 if (code != 0) { 614 com_err(progname, code, gettext("resolving ccache %s"), 615 opts->k5_cache_name); 616 return 0; 617 } 618 } 619 else 620 { 621 if ((code = krb5_cc_default(k5->ctx, &k5->cc))) { 622 com_err(progname, code, gettext("while getting default ccache")); 623 return 0; 624 } 625 } 626 627 if (opts->principal_name) 628 { 629 /* Use specified name */ 630 if ((code = krb5_parse_name(k5->ctx, opts->principal_name, 631 &k5->me))) { 632 com_err(progname, code, gettext("when parsing name %s"), 633 opts->principal_name); 634 return 0; 635 } 636 } 637 else 638 { 639 /* No principal name specified */ 640 if (opts->action == INIT_KT) { 641 /* Use the default host/service name */ 642 code = krb5_sname_to_principal(k5->ctx, NULL, NULL, 643 KRB5_NT_SRV_HST, &k5->me); 644 if (code) { 645 com_err(progname, code, gettext( 646 "when creating default server principal name")); 647 return 0; 648 } 649 } else { 650 /* Get default principal from cache if one exists */ 651 code = krb5_cc_get_principal(k5->ctx, k5->cc, 652 &k5->me); 653 if (code) 654 { 655 char *name = get_name_from_os(); 656 if (!name) 657 { 658 fprintf(stderr, gettext("Unable to identify user\n")); 659 return 0; 660 } 661 /* use strcmp to ensure only "root" is matched */ 662 if (strcmp(name, ROOT_UNAME) == 0) 663 { 664 if (code = krb5_sname_to_principal(k5->ctx, NULL, ROOT_UNAME, 665 KRB5_NT_SRV_HST, &k5->me)) { 666 com_err(progname, code, gettext( 667 "when creating default server principal name")); 668 return 0; 669 } 670 } else 671 if ((code = krb5_parse_name(k5->ctx, name, 672 &k5->me))) 673 { 674 com_err(progname, code, gettext("when parsing name %s"), 675 name); 676 return 0; 677 } 678 } 679 } 680 } 681 682 code = krb5_unparse_name(k5->ctx, k5->me, &k5->name); 683 if (code) { 684 com_err(progname, code, gettext("when unparsing name")); 685 return 0; 686 } 687 opts->principal_name = k5->name; 688 689 #ifdef KRB5_KRB4_COMPAT 690 if (got_k4) 691 { 692 /* Translate to a Kerberos 4 principal */ 693 code = krb5_524_conv_principal(k5->ctx, k5->me, 694 k4->aname, k4->inst, k4->realm); 695 if (code) { 696 k4->aname[0] = 0; 697 k4->inst[0] = 0; 698 k4->realm[0] = 0; 699 } 700 } 701 #endif 702 return 1; 703 } 704 705 static void 706 k5_end(k5) 707 struct k5_data* k5; 708 { 709 if (k5->name) 710 krb5_free_unparsed_name(k5->ctx, k5->name); 711 if (k5->me) 712 krb5_free_principal(k5->ctx, k5->me); 713 if (k5->cc) 714 krb5_cc_close(k5->ctx, k5->cc); 715 if (k5->ctx) 716 krb5_free_context(k5->ctx); 717 errctx = NULL; 718 memset(k5, 0, sizeof(*k5)); 719 } 720 721 static int 722 k4_begin(opts, k4) 723 struct k_opts* opts; 724 struct k4_data* k4; 725 { 726 #ifdef KRB5_KRB4_COMPAT 727 char* progname = progname_v4; 728 int k_errno = 0; 729 #endif 730 731 if (!got_k4) 732 return 0; 733 734 #ifdef KRB5_KRB4_COMPAT 735 if (k4->aname[0]) 736 goto skip; 737 738 if (opts->principal_name) 739 { 740 /* Use specified name */ 741 k_errno = kname_parse(k4->aname, k4->inst, k4->realm, 742 opts->principal_name); 743 if (k_errno) 744 { 745 fprintf(stderr, "%s: %s\n", progname, 746 krb_get_err_text(k_errno)); 747 return 0; 748 } 749 } else { 750 /* No principal name specified */ 751 if (opts->action == INIT_KT) { 752 /* Use the default host/service name */ 753 /* XXX - need to add this functionality */ 754 fprintf(stderr, "%s: Kerberos 4 srvtab support is not " 755 "implemented\n", progname); 756 return 0; 757 } else { 758 /* Get default principal from cache if one exists */ 759 k_errno = krb_get_tf_fullname(tkt_string(), k4->aname, 760 k4->inst, k4->realm); 761 if (k_errno) 762 { 763 char *name = get_name_from_os(); 764 if (!name) 765 { 766 fprintf(stderr, "Unable to identify user\n"); 767 return 0; 768 } 769 k_errno = kname_parse(k4->aname, k4->inst, k4->realm, 770 name); 771 if (k_errno) 772 { 773 fprintf(stderr, "%s: %s\n", progname, 774 krb_get_err_text(k_errno)); 775 return 0; 776 } 777 } 778 } 779 } 780 781 if (!k4->realm[0]) 782 krb_get_lrealm(k4->realm, 1); 783 784 if (k4->inst[0]) 785 sprintf(k4->name, "%s.%s@%s", k4->aname, k4->inst, k4->realm); 786 else 787 sprintf(k4->name, "%s@%s", k4->aname, k4->realm); 788 opts->principal_name = k4->name; 789 790 skip: 791 if (k4->aname[0] && !k_isname(k4->aname)) 792 { 793 fprintf(stderr, "%s: bad Kerberos 4 name format\n", progname); 794 return 0; 795 } 796 797 if (k4->inst[0] && !k_isinst(k4->inst)) 798 { 799 fprintf(stderr, "%s: bad Kerberos 4 instance format\n", progname); 800 return 0; 801 } 802 803 if (k4->realm[0] && !k_isrealm(k4->realm)) 804 { 805 fprintf(stderr, "%s: bad Kerberos 4 realm format\n", progname); 806 return 0; 807 } 808 #endif /* KRB5_KRB4_COMPAT */ 809 return 1; 810 } 811 812 static void 813 k4_end(k4) 814 struct k4_data* k4; 815 { 816 memset(k4, 0, sizeof(*k4)); 817 } 818 819 #ifdef KRB5_KRB4_COMPAT 820 static char stash_password[1024]; 821 static int got_password = 0; 822 #endif /* KRB5_KRB4_COMPAT */ 823 824 static krb5_error_code 825 KRB5_CALLCONV 826 kinit_prompter( 827 krb5_context ctx, 828 void *data, 829 const char *name, 830 const char *banner, 831 int num_prompts, 832 krb5_prompt prompts[] 833 ) 834 { 835 int i; 836 krb5_prompt_type *types; 837 krb5_error_code rc = 838 krb5_prompter_posix(ctx, data, name, banner, num_prompts, prompts); 839 if (!rc && (types = krb5_get_prompt_types(ctx))) 840 for (i = 0; i < num_prompts; i++) 841 if ((types[i] == KRB5_PROMPT_TYPE_PASSWORD) || 842 (types[i] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN)) 843 { 844 #ifdef KRB5_KRB4_COMPAT 845 strncpy(stash_password, prompts[i].reply->data, 846 sizeof(stash_password)); 847 got_password = 1; 848 #endif 849 } 850 return rc; 851 } 852 853 static int 854 k5_kinit(opts, k5) 855 struct k_opts* opts; 856 struct k5_data* k5; 857 { 858 char* progname = progname_v5; 859 int notix = 1; 860 krb5_keytab keytab = 0; 861 krb5_creds my_creds; 862 krb5_error_code code = 0; 863 krb5_get_init_creds_opt *options = NULL; 864 int i; 865 krb5_timestamp now; 866 krb5_deltat lifetime = 0, rlife = 0, krb5_max_duration; 867 868 if (!got_k5) 869 return 0; 870 871 code = krb5_get_init_creds_opt_alloc(k5->ctx, &options); 872 if (code) 873 goto cleanup; 874 memset(&my_creds, 0, sizeof(my_creds)); 875 876 /* 877 * Solaris Kerberos: added support for max_life and max_renewable_life 878 * which should be removed in the next minor release. See PSARC 2003/545 879 * for more info. 880 * 881 * Also, check krb5.conf for proxiable/forwardable/renewable/no_address 882 * parameter values. 883 */ 884 /* If either tkt life or renew life weren't set earlier take common steps to 885 * get the krb5.conf parameter values. 886 */ 887 888 if ((code = krb5_timeofday(k5->ctx, &now))) { 889 com_err(progname, code, gettext("while getting time of day")); 890 exit(1); 891 } 892 krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60; 893 894 if (opts->lifetime == 0 || opts->rlife == 0) { 895 896 krb_realm = krb5_princ_realm(k5->ctx, k5->me)->data; 897 /* realm params take precedence */ 898 profile_get_options_string(k5->ctx->profile, realmdef, config_times); 899 profile_get_options_string(k5->ctx->profile, appdef, config_times); 900 901 /* if the input opts doesn't have lifetime set and the krb5.conf 902 * parameter has been set, use that. 903 */ 904 if (opts->lifetime == 0 && life_timeval != NULL) { 905 code = krb5_string_to_deltat(life_timeval, &lifetime); 906 if (code != 0 || lifetime == 0 || lifetime > krb5_max_duration) { 907 fprintf(stderr, gettext("Bad max_life " 908 "value in Kerberos config file %s\n"), 909 life_timeval); 910 exit(1); 911 } 912 opts->lifetime = lifetime; 913 } 914 if (opts->rlife == 0 && renew_timeval != NULL) { 915 code = krb5_string_to_deltat(renew_timeval, &rlife); 916 if (code != 0 || rlife == 0 || rlife > krb5_max_duration) { 917 fprintf(stderr, gettext("Bad max_renewable_life " 918 "value in Kerberos config file %s\n"), 919 renew_timeval); 920 exit(1); 921 } 922 opts->rlife = rlife; 923 } 924 } 925 926 /* 927 * If lifetime is not set on the cmdline or in the krb5.conf 928 * file, default to max. 929 */ 930 if (opts->lifetime == 0) 931 opts->lifetime = krb5_max_duration; 932 933 934 profile_get_options_boolean(k5->ctx->profile, 935 realmdef, config_option); 936 profile_get_options_boolean(k5->ctx->profile, 937 appdef, config_option); 938 939 940 /* cmdline opts take precedence over krb5.conf file values */ 941 if (!opts->not_proxiable && proxiable_flag) { 942 krb5_get_init_creds_opt_set_proxiable(options, 1); 943 } 944 if (!opts->not_forwardable && forwardable_flag) { 945 krb5_get_init_creds_opt_set_forwardable(options, 1); 946 } 947 if (renewable_flag) { 948 /* 949 * If this flag is set in krb5.conf, but rlife is 0, then 950 * set it to the max (and let the KDC sort it out). 951 */ 952 opts->rlife = opts->rlife ? opts->rlife : krb5_max_duration; 953 } 954 if (no_address_flag) { 955 /* cmdline opts will overwrite this below if needbe */ 956 krb5_get_init_creds_opt_set_address_list(options, NULL); 957 } 958 959 960 /* 961 From this point on, we can goto cleanup because my_creds is 962 initialized. 963 */ 964 965 if (opts->lifetime) 966 krb5_get_init_creds_opt_set_tkt_life(options, opts->lifetime); 967 if (opts->rlife) 968 krb5_get_init_creds_opt_set_renew_life(options, opts->rlife); 969 if (opts->forwardable) 970 krb5_get_init_creds_opt_set_forwardable(options, 1); 971 if (opts->not_forwardable) 972 krb5_get_init_creds_opt_set_forwardable(options, 0); 973 if (opts->proxiable) 974 krb5_get_init_creds_opt_set_proxiable(options, 1); 975 if (opts->not_proxiable) 976 krb5_get_init_creds_opt_set_proxiable(options, 0); 977 if (opts->addresses) 978 { 979 krb5_address **addresses = NULL; 980 code = krb5_os_localaddr(k5->ctx, &addresses); 981 if (code != 0) { 982 com_err(progname, code, gettext("getting local addresses")); 983 goto cleanup; 984 } 985 krb5_get_init_creds_opt_set_address_list(options, addresses); 986 } 987 if (opts->no_addresses) 988 krb5_get_init_creds_opt_set_address_list(options, NULL); 989 990 if ((opts->action == INIT_KT) && opts->keytab_name) 991 { 992 code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab); 993 if (code != 0) { 994 com_err(progname, code, gettext("resolving keytab %s"), 995 opts->keytab_name); 996 goto cleanup; 997 } 998 } 999 1000 for (i = 0; i < opts->num_pa_opts; i++) { 1001 code = krb5_get_init_creds_opt_set_pa(k5->ctx, options, 1002 opts->pa_opts[i].attr, 1003 opts->pa_opts[i].value); 1004 if (code != 0) { 1005 com_err(progname, code, "while setting '%s'='%s'", 1006 opts->pa_opts[i].attr, opts->pa_opts[i].value); 1007 goto cleanup; 1008 } 1009 } 1010 1011 switch (opts->action) { 1012 case INIT_PW: 1013 code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me, 1014 0, kinit_prompter, 0, 1015 opts->starttime, 1016 opts->service_name, 1017 options); 1018 break; 1019 case INIT_KT: 1020 code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me, 1021 keytab, 1022 opts->starttime, 1023 opts->service_name, 1024 options); 1025 break; 1026 case VALIDATE: 1027 code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->cc, 1028 opts->service_name); 1029 break; 1030 case RENEW: 1031 code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->cc, 1032 opts->service_name); 1033 break; 1034 } 1035 1036 if (code) { 1037 char *doing = 0; 1038 switch (opts->action) { 1039 case INIT_PW: 1040 case INIT_KT: 1041 doing = gettext("getting initial credentials"); 1042 break; 1043 case VALIDATE: 1044 doing = gettext("validating credentials"); 1045 break; 1046 case RENEW: 1047 doing = gettext("renewing credentials"); 1048 break; 1049 } 1050 1051 /* If got code == KRB5_AP_ERR_V4_REPLY && got_k4, we should 1052 let the user know that maybe he/she wants -4. */ 1053 if (code == KRB5KRB_AP_ERR_V4_REPLY && got_k4) 1054 com_err(progname, code, "while %s\n" 1055 "The KDC doesn't support v5. " 1056 "You may want the -4 option in the future", 1057 doing); 1058 else if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) 1059 fprintf(stderr, gettext("%s: Password incorrect while %s\n"), progname, 1060 doing); 1061 else 1062 com_err(progname, code, gettext("while %s"), doing); 1063 goto cleanup; 1064 } 1065 1066 if (!opts->lifetime) { 1067 /* We need to figure out what lifetime to use for Kerberos 4. */ 1068 opts->lifetime = my_creds.times.endtime - my_creds.times.authtime; 1069 } 1070 1071 code = krb5_cc_initialize(k5->ctx, k5->cc, k5->me); 1072 if (code) { 1073 com_err(progname, code, gettext("when initializing cache %s"), 1074 opts->k5_cache_name?opts->k5_cache_name:""); 1075 goto cleanup; 1076 } 1077 1078 code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds); 1079 if (code) { 1080 com_err(progname, code, gettext("while storing credentials")); 1081 goto cleanup; 1082 } 1083 1084 if (opts->action == RENEW) { 1085 _kwarnd_del_warning(progname, opts->principal_name); 1086 _kwarnd_add_warning(progname, opts->principal_name, my_creds.times.endtime); 1087 } else if ((opts->action == INIT_KT) || (opts->action == INIT_PW)) { 1088 _kwarnd_add_warning(progname, opts->principal_name, my_creds.times.endtime); 1089 } 1090 1091 notix = 0; 1092 1093 cleanup: 1094 if (options) 1095 krb5_get_init_creds_opt_free(k5->ctx, options); 1096 if (my_creds.client == k5->me) { 1097 my_creds.client = 0; 1098 } 1099 if (opts->pa_opts) { 1100 free(opts->pa_opts); 1101 opts->pa_opts = NULL; 1102 opts->num_pa_opts = 0; 1103 } 1104 krb5_free_cred_contents(k5->ctx, &my_creds); 1105 if (keytab) 1106 krb5_kt_close(k5->ctx, keytab); 1107 return notix?0:1; 1108 } 1109 1110 static int 1111 k4_kinit(opts, k4, ctx) 1112 struct k_opts* opts; 1113 struct k4_data* k4; 1114 krb5_context ctx; 1115 { 1116 #ifdef KRB5_KRB4_COMPAT 1117 char* progname = progname_v4; 1118 int k_errno = 0; 1119 #endif 1120 1121 if (!got_k4) 1122 return 0; 1123 1124 if (opts->starttime) 1125 return 0; 1126 1127 #ifdef KRB5_KRB4_COMPAT 1128 if (!k4->lifetime) 1129 k4->lifetime = opts->lifetime; 1130 if (!k4->lifetime) 1131 k4->lifetime = KRB4_BACKUP_DEFAULT_LIFE_SECS; 1132 1133 k4->lifetime = krb_time_to_life(0, k4->lifetime); 1134 1135 switch (opts->action) 1136 { 1137 case INIT_PW: 1138 if (!got_password) { 1139 unsigned int pwsize = sizeof(stash_password); 1140 krb5_error_code code; 1141 char prompt[1024]; 1142 1143 sprintf(prompt, gettext("Password for %s: "), opts->principal_name); 1144 stash_password[0] = 0; 1145 /* 1146 Note: krb5_read_password does not actually look at the 1147 context, so we're ok even if we don't have a context. If 1148 we cannot dynamically load krb5, we can substitute any 1149 decent read password function instead of the krb5 one. 1150 */ 1151 code = krb5_read_password(ctx, prompt, 0, stash_password, &pwsize); 1152 if (code || pwsize == 0) 1153 { 1154 fprintf(stderr, gettext("Error while reading password for '%s'\n"), 1155 opts->principal_name); 1156 memset(stash_password, 0, sizeof(stash_password)); 1157 return 0; 1158 } 1159 got_password = 1; 1160 } 1161 k_errno = krb_get_pw_in_tkt(k4->aname, k4->inst, k4->realm, "krbtgt", 1162 k4->realm, k4->lifetime, stash_password); 1163 1164 if (k_errno) { 1165 fprintf(stderr, "%s: %s\n", progname, 1166 krb_get_err_text(k_errno)); 1167 if (authed_k5) 1168 fprintf(stderr, gettext("Maybe your KDC does not support v4. " 1169 "Try the -5 option next time.\n")); 1170 return 0; 1171 } 1172 return 1; 1173 #ifndef HAVE_KRB524 1174 case INIT_KT: 1175 fprintf(stderr, gettext("%s: srvtabs are not supported\n"), progname); 1176 return 0; 1177 case RENEW: 1178 fprintf(stderr, gettext("%s: renewal of krb4 tickets is not supported\n"), 1179 progname); 1180 return 0; 1181 #else 1182 /* These cases are handled by the 524 code - this prevents the compiler 1183 warnings of not using all the enumerated types. 1184 */ 1185 case INIT_KT: 1186 case RENEW: 1187 case VALIDATE: 1188 return 0; 1189 #endif 1190 } 1191 #endif 1192 return 0; 1193 } 1194 1195 static char* 1196 getvprogname(v, progname) 1197 char *v, *progname; 1198 { 1199 unsigned int len = strlen(progname) + 2 + strlen(v) + 2; 1200 char *ret = malloc(len); 1201 if (ret) 1202 sprintf(ret, "%s(v%s)", progname, v); 1203 else 1204 ret = progname; 1205 return ret; 1206 } 1207 1208 #ifdef HAVE_KRB524 1209 /* Convert krb5 tickets to krb4. */ 1210 static int try_convert524(k5) 1211 struct k5_data* k5; 1212 { 1213 char * progname = progname_v524; 1214 krb5_error_code code = 0; 1215 int icode = 0; 1216 krb5_principal kpcserver = 0; 1217 krb5_creds *v5creds = 0; 1218 krb5_creds increds; 1219 CREDENTIALS v4creds; 1220 1221 if (!got_k4 || !got_k5) 1222 return 0; 1223 1224 memset((char *) &increds, 0, sizeof(increds)); 1225 /* 1226 From this point on, we can goto cleanup because increds is 1227 initialized. 1228 */ 1229 1230 if ((code = krb5_build_principal(k5->ctx, 1231 &kpcserver, 1232 krb5_princ_realm(k5->ctx, k5->me)->length, 1233 krb5_princ_realm(k5->ctx, k5->me)->data, 1234 "krbtgt", 1235 krb5_princ_realm(k5->ctx, k5->me)->data, 1236 NULL))) { 1237 com_err(progname, code, gettext( 1238 "while creating service principal name")); 1239 goto cleanup; 1240 } 1241 1242 increds.client = k5->me; 1243 increds.server = kpcserver; 1244 /* Prevent duplicate free calls. */ 1245 kpcserver = 0; 1246 1247 increds.times.endtime = 0; 1248 increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; 1249 if ((code = krb5_get_credentials(k5->ctx, 0, 1250 k5->cc, 1251 &increds, 1252 &v5creds))) { 1253 com_err(progname, code, 1254 gettext("getting V5 credentials")); 1255 goto cleanup; 1256 } 1257 if ((icode = krb524_convert_creds_kdc(k5->ctx, 1258 v5creds, 1259 &v4creds))) { 1260 com_err(progname, icode, 1261 gettext("converting to V4 credentials")); 1262 goto cleanup; 1263 } 1264 /* this is stolen from the v4 kinit */ 1265 /* initialize ticket cache */ 1266 if ((icode = in_tkt(v4creds.pname, v4creds.pinst) 1267 != KSUCCESS)) { 1268 com_err(progname, icode, gettext( 1269 "trying to create the V4 ticket file")); 1270 goto cleanup; 1271 } 1272 /* stash ticket, session key, etc. for future use */ 1273 if ((icode = krb_save_credentials(v4creds.service, 1274 v4creds.instance, 1275 v4creds.realm, 1276 v4creds.session, 1277 v4creds.lifetime, 1278 v4creds.kvno, 1279 &(v4creds.ticket_st), 1280 v4creds.issue_date))) { 1281 com_err(progname, icode, gettext( 1282 "trying to save the V4 ticket")); 1283 goto cleanup; 1284 } 1285 1286 cleanup: 1287 memset(&v4creds, 0, sizeof(v4creds)); 1288 if (v5creds) 1289 krb5_free_creds(k5->ctx, v5creds); 1290 increds.client = 0; 1291 krb5_free_cred_contents(k5->ctx, &increds); 1292 if (kpcserver) 1293 krb5_free_principal(k5->ctx, kpcserver); 1294 return !(code || icode); 1295 } 1296 #endif /* HAVE_KRB524 */ 1297 1298 int 1299 main(argc, argv) 1300 int argc; 1301 char **argv; 1302 { 1303 struct k_opts opts; 1304 struct k5_data k5; 1305 struct k4_data k4; 1306 char *progname; 1307 1308 (void) setlocale(LC_ALL, ""); 1309 1310 #if !defined(TEXT_DOMAIN) 1311 #define TEXT_DOMAIN "SYS_TEST" 1312 #endif 1313 1314 (void) textdomain(TEXT_DOMAIN); 1315 1316 progname = GET_PROGNAME(argv[0]); 1317 progname_v5 = getvprogname("5", progname); 1318 #ifdef KRB5_KRB4_COMPAT 1319 progname_v4 = getvprogname("4", progname); 1320 progname_v524 = getvprogname("524", progname); 1321 #endif 1322 1323 /* Ensure we can be driven from a pipe */ 1324 if(!isatty(fileno(stdin))) 1325 setvbuf(stdin, 0, _IONBF, 0); 1326 if(!isatty(fileno(stdout))) 1327 setvbuf(stdout, 0, _IONBF, 0); 1328 if(!isatty(fileno(stderr))) 1329 setvbuf(stderr, 0, _IONBF, 0); 1330 1331 /* 1332 This is where we would put in code to dynamically load Kerberos 1333 libraries. Currenlty, we just get them implicitly. 1334 */ 1335 got_k5 = 1; 1336 #ifdef KRB5_KRB4_COMPAT 1337 got_k4 = 1; 1338 #endif 1339 1340 memset(&opts, 0, sizeof(opts)); 1341 opts.action = INIT_PW; 1342 1343 memset(&k5, 0, sizeof(k5)); 1344 memset(&k4, 0, sizeof(k4)); 1345 1346 set_com_err_hook (extended_com_err_fn); 1347 1348 parse_options(argc, argv, &opts, progname); 1349 1350 got_k5 = k5_begin(&opts, &k5, &k4); 1351 got_k4 = k4_begin(&opts, &k4); 1352 1353 authed_k5 = k5_kinit(&opts, &k5); 1354 #ifdef HAVE_KRB524 1355 if (authed_k5) 1356 authed_k4 = try_convert524(&k5); 1357 #endif 1358 if (!authed_k4) 1359 authed_k4 = k4_kinit(&opts, &k4, k5.ctx); 1360 #ifdef KRB5_KRB4_COMPAT 1361 memset(stash_password, 0, sizeof(stash_password)); 1362 #endif 1363 1364 if (authed_k5 && opts.verbose) 1365 fprintf(stderr, gettext("Authenticated to Kerberos v5\n")); 1366 if (authed_k4 && opts.verbose) 1367 fprintf(stderr, gettext("Authenticated to Kerberos v4\n")); 1368 1369 k5_end(&k5); 1370 k4_end(&k4); 1371 1372 if ((got_k5 && !authed_k5) || (got_k4 && !authed_k4) || 1373 (!got_k5 && !got_k4)) 1374 exit(1); 1375 return 0; 1376 } 1377 1378 static void 1379 _kwarnd_add_warning(char *progname, char *me, time_t endtime) 1380 { 1381 if (kwarn_add_warning(me, endtime) != 0) 1382 fprintf(stderr, gettext( 1383 "%s: no ktkt_warnd warning possible\n"), progname); 1384 return; 1385 } 1386 1387 1388 static void 1389 _kwarnd_del_warning(char *progname, char *me) 1390 { 1391 if (kwarn_del_warning(me) != 0) 1392 fprintf(stderr, gettext( 1393 "%s: unable to delete ktkt_warnd message for %s\n"), 1394 progname, me); 1395 return; 1396 } 1397