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