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