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
get_name_from_os()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
get_name_from_os()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
get_name_from_os()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
usage(progname)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;
extended_com_err_fn(const char * myprog,errcode_t code,const char * fmt,va_list args)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
add_preauth_opt(struct k_opts * opts,char * av)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 *
parse_options(argc,argv,opts,progname)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
k5_begin(opts,k5,k4)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
k5_end(k5)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
k4_begin(opts,k4)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
k4_end(k4)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
kinit_prompter(krb5_context ctx,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])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
k5_kinit(opts,k5)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
k4_kinit(opts,k4,ctx)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*
getvprogname(v,progname)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. */
try_convert524(k5)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
main(argc,argv)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
_kwarnd_add_warning(char * progname,char * me,time_t endtime)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
_kwarnd_del_warning(char * progname,char * me)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