1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* clients/kinit/kinit.c - Initialize a credential cache */
4 * Copyright 1990, 2008 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
29 #include "k5-platform.h" /* for asprintf and getopt */
40 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x))
42 #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x))
48 char * get_name_from_os()
51 if ((pw = getpwuid((int) getuid())))
55 #else /* HAVE_PWD_H */
58 char * get_name_from_os()
60 static char name[1024];
61 DWORD name_size = sizeof(name);
62 if (GetUserName(name, &name_size)) {
63 name[sizeof(name)-1] = 0; /* Just to be extra safe */
71 char * get_name_from_os()
76 #endif /* HAVE_PWD_H */
78 static char *progname;
80 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
85 krb5_deltat starttime;
102 char* principal_name;
105 char* k5_in_cache_name;
106 char* k5_out_cache_name;
110 int use_client_keytab;
113 krb5_gic_opt_pa_data *pa_opts;
122 krb5_ccache in_cc, out_cc;
125 krb5_boolean switch_to_cache;
128 /* if struct[2] == NULL, then long_getopt acts as if the short flag
129 struct[3] was specified. If struct[2] != NULL, then struct[3] is
130 stored in *(struct[2]), the array index which was specified is
131 stored in *index, and long_getopt() returns 0. */
133 const char *shopts = "r:fpFPn54aAVl:s:c:kit:T:RS:vX:CEI:";
138 #define USAGE_BREAK "\n\t"
140 #define USAGE_LONG_FORWARDABLE " | --forwardable | --noforwardable"
141 #define USAGE_LONG_PROXIABLE " | --proxiable | --noproxiable"
142 #define USAGE_LONG_ADDRESSES " | --addresses | --noaddresses"
143 #define USAGE_LONG_CANONICALIZE " | --canonicalize"
144 #define USAGE_LONG_ENTERPRISE " | --enterprise"
145 #define USAGE_LONG_REQUESTPAC "--request-pac | --no-request-pac"
146 #define USAGE_BREAK_LONG USAGE_BREAK
148 fprintf(stderr, "Usage: %s [-V] "
149 "[-l lifetime] [-s start_time] "
151 "[-r renewable_life] "
152 "[-f | -F" USAGE_LONG_FORWARDABLE "] "
154 "[-p | -P" USAGE_LONG_PROXIABLE "] "
157 "[-a | -A" USAGE_LONG_ADDRESSES "] "
159 "[" USAGE_LONG_REQUESTPAC "] "
161 "[-C" USAGE_LONG_CANONICALIZE "] "
163 "[-E" USAGE_LONG_ENTERPRISE "] "
166 "[-k [-i|-t keytab_file]] "
169 "[-S service_name] [-T ticket_armor_cache]"
171 "[-X <attribute>[=<value>]] [principal]"
175 fprintf(stderr, " options:\n");
176 fprintf(stderr, _("\t-V verbose\n"));
177 fprintf(stderr, _("\t-l lifetime\n"));
178 fprintf(stderr, _("\t-s start time\n"));
179 fprintf(stderr, _("\t-r renewable lifetime\n"));
180 fprintf(stderr, _("\t-f forwardable\n"));
181 fprintf(stderr, _("\t-F not forwardable\n"));
182 fprintf(stderr, _("\t-p proxiable\n"));
183 fprintf(stderr, _("\t-P not proxiable\n"));
184 fprintf(stderr, _("\t-n anonymous\n"));
185 fprintf(stderr, _("\t-a include addresses\n"));
186 fprintf(stderr, _("\t-A do not include addresses\n"));
187 fprintf(stderr, _("\t-v validate\n"));
188 fprintf(stderr, _("\t-R renew\n"));
189 fprintf(stderr, _("\t-C canonicalize\n"));
190 fprintf(stderr, _("\t-E client is enterprise principal name\n"));
191 fprintf(stderr, _("\t-k use keytab\n"));
192 fprintf(stderr, _("\t-i use default client keytab (with -k)\n"));
193 fprintf(stderr, _("\t-t filename of keytab to use\n"));
194 fprintf(stderr, _("\t-c Kerberos 5 cache name\n"));
195 fprintf(stderr, _("\t-S service\n"));
196 fprintf(stderr, _("\t-T armor credential cache\n"));
197 fprintf(stderr, _("\t-X <attribute>[=<value>]\n"));
201 static krb5_context errctx;
202 static void extended_com_err_fn (const char *myprog, errcode_t code,
203 const char *fmt, va_list args)
206 emsg = krb5_get_error_message (errctx, code);
207 fprintf (stderr, "%s: %s ", myprog, emsg);
208 krb5_free_error_message (errctx, emsg);
209 vfprintf (stderr, fmt, args);
210 fprintf (stderr, "\n");
214 add_preauth_opt(struct k_opts *opts, char *av)
217 krb5_gic_opt_pa_data *p, *x;
219 if (opts->num_pa_opts == 0) {
220 opts->pa_opts = malloc(sizeof(krb5_gic_opt_pa_data));
221 if (opts->pa_opts == NULL)
224 size_t newsize = (opts->num_pa_opts + 1) * sizeof(krb5_gic_opt_pa_data);
225 x = realloc(opts->pa_opts, newsize);
230 p = &opts->pa_opts[opts->num_pa_opts];
231 sep = strchr(av, '=');
245 parse_options(argc, argv, opts)
250 struct option long_options[] = {
251 { "noforwardable", 0, NULL, 'F' },
252 { "noproxiable", 0, NULL, 'P' },
253 { "addresses", 0, NULL, 'a'},
254 { "forwardable", 0, NULL, 'f' },
255 { "proxiable", 0, NULL, 'p' },
256 { "noaddresses", 0, NULL, 'A' },
257 { "canonicalize", 0, NULL, 'C' },
258 { "enterprise", 0, NULL, 'E' },
259 { "request-pac", 0, &opts->request_pac, 1 },
260 { "no-request-pac", 0, &opts->not_request_pac, 1 },
263 krb5_error_code code;
267 while ((i = getopt_long(argc, argv, shopts, long_options, 0)) != -1) {
274 code = krb5_string_to_deltat(optarg, &opts->lifetime);
275 if (code != 0 || opts->lifetime == 0) {
276 fprintf(stderr, _("Bad lifetime value %s\n"), optarg);
282 code = krb5_string_to_deltat(optarg, &opts->rlife);
283 if (code != 0 || opts->rlife == 0) {
284 fprintf(stderr, _("Bad lifetime value %s\n"), optarg);
289 opts->forwardable = 1;
292 opts->not_forwardable = 1;
298 opts->not_proxiable = 1;
307 opts->no_addresses = 1;
310 code = krb5_string_to_deltat(optarg, &opts->starttime);
311 if (code != 0 || opts->starttime == 0) {
312 /* Parse as an absolute time; intentionally undocumented
313 * but left for backwards compatibility. */
314 krb5_timestamp abs_starttime;
316 code = krb5_string_to_timestamp(optarg, &abs_starttime);
317 if (code != 0 || abs_starttime == 0) {
318 fprintf(stderr, _("Bad start time value %s\n"), optarg);
321 opts->starttime = abs_starttime - time(0);
326 opts->service_name = optarg;
329 opts->action = INIT_KT;
332 opts->use_client_keytab = 1;
335 if (opts->keytab_name)
337 fprintf(stderr, _("Only one -t option allowed.\n"));
340 opts->keytab_name = optarg;
344 if (opts->armor_ccache) {
345 fprintf(stderr, _("Only one armor_ccache\n"));
347 } else opts->armor_ccache = optarg;
350 opts->action = RENEW;
353 opts->action = VALIDATE;
356 if (opts->k5_out_cache_name)
358 fprintf(stderr, _("Only one -c option allowed\n"));
361 opts->k5_out_cache_name = optarg;
365 if (opts->k5_in_cache_name) {
366 fprintf(stderr, _("Only one -I option allowed\n"));
369 opts->k5_in_cache_name = optarg;
373 code = add_preauth_opt(opts, optarg);
376 com_err(progname, code, _("while adding preauth option"));
381 opts->canonicalize = 1;
384 opts->enterprise = 1;
387 fprintf(stderr, _("Kerberos 4 is no longer supported\n"));
393 /* If this option set a flag, do nothing else now. */
401 if (opts->forwardable && opts->not_forwardable)
403 fprintf(stderr, _("Only one of -f and -F allowed\n"));
406 if (opts->proxiable && opts->not_proxiable)
408 fprintf(stderr, _("Only one of -p and -P allowed\n"));
411 if (opts->request_pac && opts->not_request_pac)
413 fprintf(stderr, _("Only one of --request-pac and --no-request-pac "
417 if (opts->addresses && opts->no_addresses)
419 fprintf(stderr, _("Only one of -a and -A allowed\n"));
422 if (opts->keytab_name != NULL && opts->use_client_keytab == 1)
424 fprintf(stderr, _("Only one of -t and -i allowed\n"));
427 if ((opts->keytab_name != NULL || opts->use_client_keytab == 1) &&
428 opts->action != INIT_KT)
430 opts->action = INIT_KT;
431 fprintf(stderr, _("keytab specified, forcing -k\n"));
434 if (argc - optind > 1) {
435 fprintf(stderr, _("Extra arguments (starting with \"%s\").\n"),
444 opts->principal_name = (optind == argc-1) ? argv[optind] : 0;
445 return opts->principal_name;
453 krb5_error_code code = 0;
455 int flags = opts->enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
456 krb5_ccache defcache = NULL;
457 krb5_principal defcache_princ = NULL, princ;
459 const char *deftype = NULL;
460 char *defrealm, *name;
462 code = krb5_init_context(&k5->ctx);
464 com_err(progname, code, _("while initializing Kerberos 5 library"));
469 if (opts->k5_out_cache_name) {
470 code = krb5_cc_resolve(k5->ctx, opts->k5_out_cache_name, &k5->out_cc);
472 com_err(progname, code, _("resolving ccache %s"),
473 opts->k5_out_cache_name);
477 fprintf(stderr, _("Using specified cache: %s\n"),
478 opts->k5_out_cache_name);
481 /* Resolve the default ccache and get its type and default principal
482 * (if it is initialized). */
483 code = krb5_cc_default(k5->ctx, &defcache);
485 com_err(progname, code, _("while getting default ccache"));
488 deftype = krb5_cc_get_type(k5->ctx, defcache);
489 if (krb5_cc_get_principal(k5->ctx, defcache, &defcache_princ) != 0)
490 defcache_princ = NULL;
493 /* Choose a client principal name. */
494 if (opts->principal_name != NULL) {
495 /* Use the specified principal name. */
496 code = krb5_parse_name_flags(k5->ctx, opts->principal_name, flags,
499 com_err(progname, code, _("when parsing name %s"),
500 opts->principal_name);
503 } else if (opts->anonymous) {
504 /* Use the anonymous principal for the local realm. */
505 code = krb5_get_default_realm(k5->ctx, &defrealm);
507 com_err(progname, code, _("while getting default realm"));
510 code = krb5_build_principal_ext(k5->ctx, &k5->me,
511 strlen(defrealm), defrealm,
512 strlen(KRB5_WELLKNOWN_NAMESTR),
513 KRB5_WELLKNOWN_NAMESTR,
514 strlen(KRB5_ANONYMOUS_PRINCSTR),
515 KRB5_ANONYMOUS_PRINCSTR,
517 krb5_free_default_realm(k5->ctx, defrealm);
519 com_err(progname, code, _("while building principal"));
522 } else if (opts->action == INIT_KT && opts->use_client_keytab) {
523 /* Use the first entry from the client keytab. */
524 code = krb5_kt_client_default(k5->ctx, &keytab);
526 com_err(progname, code,
527 _("When resolving the default client keytab"));
530 code = k5_kt_get_principal(k5->ctx, keytab, &k5->me);
531 krb5_kt_close(k5->ctx, keytab);
533 com_err(progname, code,
534 _("When determining client principal name from keytab"));
537 } else if (opts->action == INIT_KT) {
538 /* Use the default host/service name. */
539 code = krb5_sname_to_principal(k5->ctx, NULL, NULL, KRB5_NT_SRV_HST,
542 com_err(progname, code,
543 _("when creating default server principal name"));
546 if (k5->me->realm.data[0] == 0) {
547 code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
549 com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
550 _("(principal %s)"), k5->name);
552 com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
553 _("for local services"));
557 } else if (k5->out_cc != NULL) {
558 /* If the output ccache is initialized, use its principal. */
559 if (krb5_cc_get_principal(k5->ctx, k5->out_cc, &princ) == 0)
561 } else if (defcache_princ != NULL) {
562 /* Use the default cache's principal, and use the default cache as the
564 k5->out_cc = defcache;
566 k5->me = defcache_princ;
567 defcache_princ = NULL;
570 /* If we still haven't chosen, use the local username. */
571 if (k5->me == NULL) {
572 name = get_name_from_os();
574 fprintf(stderr, _("Unable to identify user\n"));
577 code = krb5_parse_name_flags(k5->ctx, name, flags, &k5->me);
579 com_err(progname, code, _("when parsing name %s"),
585 if (k5->out_cc == NULL && krb5_cc_support_switch(k5->ctx, deftype)) {
586 /* Use an existing cache for the client principal if we can. */
587 code = krb5_cc_cache_match(k5->ctx, k5->me, &k5->out_cc);
588 if (code != 0 && code != KRB5_CC_NOTFOUND) {
589 com_err(progname, code, _("while searching for ccache for %s"),
590 opts->principal_name);
595 fprintf(stderr, _("Using existing cache: %s\n"),
596 krb5_cc_get_name(k5->ctx, k5->out_cc));
598 k5->switch_to_cache = 1;
599 } else if (defcache_princ != NULL) {
600 /* Create a new cache to avoid overwriting the initialized default
602 code = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->out_cc);
604 com_err(progname, code, _("while generating new ccache"));
608 fprintf(stderr, _("Using new cache: %s\n"),
609 krb5_cc_get_name(k5->ctx, k5->out_cc));
611 k5->switch_to_cache = 1;
615 /* Use the default cache if we haven't picked one yet. */
616 if (k5->out_cc == NULL) {
617 k5->out_cc = defcache;
620 fprintf(stderr, _("Using default cache: %s\n"),
621 krb5_cc_get_name(k5->ctx, k5->out_cc));
625 if (opts->k5_in_cache_name) {
626 code = krb5_cc_resolve(k5->ctx, opts->k5_in_cache_name, &k5->in_cc);
628 com_err(progname, code, _("resolving ccache %s"),
629 opts->k5_in_cache_name);
633 fprintf(stderr, _("Using specified input cache: %s\n"),
634 opts->k5_in_cache_name);
639 code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
641 com_err(progname, code, _("when unparsing name"));
645 fprintf(stderr, _("Using principal: %s\n"), k5->name);
647 opts->principal_name = k5->name;
652 if (defcache != NULL)
653 krb5_cc_close(k5->ctx, defcache);
654 krb5_free_principal(k5->ctx, defcache_princ);
663 krb5_free_unparsed_name(k5->ctx, k5->name);
665 krb5_free_principal(k5->ctx, k5->me);
667 krb5_cc_close(k5->ctx, k5->in_cc);
669 krb5_cc_close(k5->ctx, k5->out_cc);
671 krb5_free_context(k5->ctx);
673 memset(k5, 0, sizeof(*k5));
676 static krb5_error_code
684 krb5_prompt prompts[]
687 krb5_boolean *pwprompt = data;
688 krb5_prompt_type *ptypes;
691 /* Make a note if we receive a password prompt. */
692 ptypes = krb5_get_prompt_types(ctx);
693 for (i = 0; i < num_prompts; i++) {
694 if (ptypes != NULL && ptypes[i] == KRB5_PROMPT_TYPE_PASSWORD)
698 return krb5_prompter_posix(ctx, data, name, banner, num_prompts, prompts);
707 krb5_keytab keytab = 0;
709 krb5_error_code code = 0;
710 krb5_get_init_creds_opt *options = NULL;
711 krb5_boolean pwprompt = FALSE;
714 memset(&my_creds, 0, sizeof(my_creds));
716 code = krb5_get_init_creds_opt_alloc(k5->ctx, &options);
721 From this point on, we can goto cleanup because my_creds is
726 krb5_get_init_creds_opt_set_tkt_life(options, opts->lifetime);
728 krb5_get_init_creds_opt_set_renew_life(options, opts->rlife);
729 if (opts->forwardable)
730 krb5_get_init_creds_opt_set_forwardable(options, 1);
731 if (opts->not_forwardable)
732 krb5_get_init_creds_opt_set_forwardable(options, 0);
734 krb5_get_init_creds_opt_set_proxiable(options, 1);
735 if (opts->not_proxiable)
736 krb5_get_init_creds_opt_set_proxiable(options, 0);
737 if (opts->canonicalize)
738 krb5_get_init_creds_opt_set_canonicalize(options, 1);
740 krb5_get_init_creds_opt_set_anonymous(options, 1);
743 krb5_address **addresses = NULL;
744 code = krb5_os_localaddr(k5->ctx, &addresses);
746 com_err(progname, code, _("getting local addresses"));
749 krb5_get_init_creds_opt_set_address_list(options, addresses);
751 if (opts->no_addresses)
752 krb5_get_init_creds_opt_set_address_list(options, NULL);
753 if (opts->armor_ccache)
754 krb5_get_init_creds_opt_set_fast_ccache_name(k5->ctx, options, opts->armor_ccache);
755 if (opts->request_pac)
756 krb5_get_init_creds_opt_set_pac_request(k5->ctx, options, TRUE);
757 if (opts->not_request_pac)
758 krb5_get_init_creds_opt_set_pac_request(k5->ctx, options, FALSE);
761 if ((opts->action == INIT_KT) && opts->keytab_name)
764 if (strncmp(opts->keytab_name, "KDB:", 4) == 0) {
765 code = kinit_kdb_init(&k5->ctx,
766 krb5_princ_realm(k5->ctx, k5->me)->data);
768 com_err(progname, code,
769 _("while setting up KDB keytab for realm %s"),
770 krb5_princ_realm(k5->ctx, k5->me)->data);
776 code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab);
778 com_err(progname, code, _("resolving keytab %s"),
783 fprintf(stderr, _("Using keytab: %s\n"), opts->keytab_name);
784 } else if (opts->action == INIT_KT && opts->use_client_keytab) {
785 code = krb5_kt_client_default(k5->ctx, &keytab);
787 com_err(progname, code, _("resolving default client keytab"));
792 for (i = 0; i < opts->num_pa_opts; i++) {
793 code = krb5_get_init_creds_opt_set_pa(k5->ctx, options,
794 opts->pa_opts[i].attr,
795 opts->pa_opts[i].value);
797 com_err(progname, code, _("while setting '%s'='%s'"),
798 opts->pa_opts[i].attr, opts->pa_opts[i].value);
802 fprintf(stderr, _("PA Option %s = %s\n"), opts->pa_opts[i].attr,
803 opts->pa_opts[i].value);
807 code = krb5_get_init_creds_opt_set_in_ccache(k5->ctx, options,
812 code = krb5_get_init_creds_opt_set_out_ccache(k5->ctx, options,
817 switch (opts->action) {
819 code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me,
820 0, kinit_prompter, &pwprompt,
826 code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me,
833 code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->out_cc,
837 code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->out_cc,
844 switch (opts->action) {
847 doing = _("getting initial credentials");
850 doing = _("validating credentials");
853 doing = _("renewing credentials");
857 /* If reply decryption failed, or if pre-authentication failed and we
858 * were prompted for a password, assume the password was wrong. */
859 if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY ||
860 (pwprompt && code == KRB5KDC_ERR_PREAUTH_FAILED)) {
861 fprintf(stderr, _("%s: Password incorrect while %s\n"), progname,
864 com_err(progname, code, _("while %s"), doing);
869 if ((opts->action != INIT_PW) && (opts->action != INIT_KT)) {
870 code = krb5_cc_initialize(k5->ctx, k5->out_cc, opts->canonicalize ?
871 my_creds.client : k5->me);
873 com_err(progname, code, _("when initializing cache %s"),
874 opts->k5_out_cache_name?opts->k5_out_cache_name:"");
878 fprintf(stderr, _("Initialized cache\n"));
880 code = krb5_cc_store_cred(k5->ctx, k5->out_cc, &my_creds);
882 com_err(progname, code, _("while storing credentials"));
886 fprintf(stderr, _("Stored credentials\n"));
890 if (k5->switch_to_cache) {
891 code = krb5_cc_switch(k5->ctx, k5->out_cc);
893 com_err(progname, code, _("while switching to new ccache"));
903 krb5_get_init_creds_opt_free(k5->ctx, options);
904 if (my_creds.client == k5->me) {
909 opts->pa_opts = NULL;
910 opts->num_pa_opts = 0;
912 krb5_free_cred_contents(k5->ctx, &my_creds);
914 krb5_kt_close(k5->ctx, keytab);
927 setlocale(LC_ALL, "");
928 progname = GET_PROGNAME(argv[0]);
930 /* Ensure we can be driven from a pipe */
931 if(!isatty(fileno(stdin)))
932 setvbuf(stdin, 0, _IONBF, 0);
933 if(!isatty(fileno(stdout)))
934 setvbuf(stdout, 0, _IONBF, 0);
935 if(!isatty(fileno(stderr)))
936 setvbuf(stderr, 0, _IONBF, 0);
938 memset(&opts, 0, sizeof(opts));
939 opts.action = INIT_PW;
941 memset(&k5, 0, sizeof(k5));
943 set_com_err_hook (extended_com_err_fn);
945 parse_options(argc, argv, &opts);
947 if (k5_begin(&opts, &k5))
948 authed_k5 = k5_kinit(&opts, &k5);
950 if (authed_k5 && opts.verbose)
951 fprintf(stderr, _("Authenticated to Kerberos v5\n"));