xref: /freebsd/crypto/krb5/src/clients/kinit/kinit.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* clients/kinit/kinit.c - Initialize a credential cache */
3 /*
4  * Copyright 1990, 2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
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.
11  *
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.
25  */
26 
27 #include "autoconf.h"
28 #include <k5-int.h>
29 #include "k5-platform.h"        /* For asprintf and getopt */
30 #include <krb5.h>
31 #include "extern.h"
32 #include <locale.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <time.h>
36 #include <errno.h>
37 #include <com_err.h>
38 
39 #ifndef _WIN32
40 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/') + 1 : (x))
41 #else
42 #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x))
43 #endif
44 
45 #ifdef HAVE_PWD_H
46 #include <pwd.h>
47 static char *
get_name_from_os()48 get_name_from_os()
49 {
50     struct passwd *pw;
51 
52     pw = getpwuid(getuid());
53     return (pw != NULL) ? pw->pw_name : NULL;
54 }
55 #else /* HAVE_PWD_H */
56 #ifdef _WIN32
57 static char *
get_name_from_os()58 get_name_from_os()
59 {
60     static char name[1024];
61     DWORD name_size = sizeof(name);
62 
63     if (GetUserName(name, &name_size)) {
64         name[sizeof(name) - 1] = '\0'; /* Just to be extra safe */
65         return name;
66     } else {
67         return NULL;
68     }
69 }
70 #else /* _WIN32 */
71 static char *
get_name_from_os()72 get_name_from_os()
73 {
74     return NULL;
75 }
76 #endif /* _WIN32 */
77 #endif /* HAVE_PWD_H */
78 
79 static char *progname;
80 
81 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
82 
83 struct k_opts
84 {
85     /* In seconds */
86     krb5_deltat starttime;
87     krb5_deltat lifetime;
88     krb5_deltat rlife;
89 
90     int forwardable;
91     int proxiable;
92     int request_pac;
93     int anonymous;
94     int addresses;
95 
96     int not_forwardable;
97     int not_proxiable;
98     int not_request_pac;
99     int no_addresses;
100 
101     int verbose;
102 
103     char *principal_name;
104     char *service_name;
105     char *keytab_name;
106     char *k5_in_cache_name;
107     char *k5_out_cache_name;
108     char *armor_ccache;
109 
110     action_type action;
111     int use_client_keytab;
112 
113     int num_pa_opts;
114     krb5_gic_opt_pa_data *pa_opts;
115 
116     int canonicalize;
117     int enterprise;
118 };
119 
120 struct k5_data
121 {
122     krb5_context ctx;
123     krb5_ccache in_cc, out_cc;
124     krb5_principal me;
125     char *name;
126     krb5_boolean switch_to_cache;
127 };
128 
129 /*
130  * If struct[2] == NULL, then long_getopt acts as if the short flag struct[3]
131  * were specified.  If struct[2] != NULL, then struct[3] is stored in
132  * *(struct[2]), the array index which was specified is stored in *index, and
133  * long_getopt() returns 0.
134  */
135 const char *shopts = "r:fpFPn54aAVl:s:c:kit:T:RS:vX:CEI:";
136 
137 #define USAGE_BREAK "\n\t"
138 
139 static void
usage()140 usage()
141 {
142     fprintf(stderr,
143             _("Usage: %s [-V] [-l lifetime] [-s start_time] "
144               "[-r renewable_life]\n"
145               "\t[-f | -F] [-p | -P] [-n] [-a | -A] [-C] [-E]\n"
146               "\t[--request-pac | --no-request-pac]\n"
147               "\t[-v] [-R] [-k [-i|-t keytab_file]] [-c cachename]\n"
148               "\t[-S service_name] [-I input_ccache] [-T ticket_armor_cache]\n"
149               "\t[-X <attribute>[=<value>]] [principal]\n"
150               "\n"), progname);
151 
152     fprintf(stderr, "    options:\n");
153     fprintf(stderr, _("\t-V verbose\n"));
154     fprintf(stderr, _("\t-l lifetime\n"));
155     fprintf(stderr, _("\t-s start time\n"));
156     fprintf(stderr, _("\t-r renewable lifetime\n"));
157     fprintf(stderr, _("\t-f forwardable\n"));
158     fprintf(stderr, _("\t-F not forwardable\n"));
159     fprintf(stderr, _("\t-p proxiable\n"));
160     fprintf(stderr, _("\t-P not proxiable\n"));
161     fprintf(stderr, _("\t-n anonymous\n"));
162     fprintf(stderr, _("\t-a include addresses\n"));
163     fprintf(stderr, _("\t-A do not include addresses\n"));
164     fprintf(stderr, _("\t-v validate\n"));
165     fprintf(stderr, _("\t-R renew\n"));
166     fprintf(stderr, _("\t-C canonicalize\n"));
167     fprintf(stderr, _("\t-E client is enterprise principal name\n"));
168     fprintf(stderr, _("\t-k use keytab\n"));
169     fprintf(stderr, _("\t-i use default client keytab (with -k)\n"));
170     fprintf(stderr, _("\t-t filename of keytab to use\n"));
171     fprintf(stderr, _("\t-c Kerberos 5 cache name\n"));
172     fprintf(stderr, _("\t-S service\n"));
173     fprintf(stderr, _("\t-I input credential cache\n"));
174     fprintf(stderr, _("\t-T armor credential cache\n"));
175     fprintf(stderr, _("\t-X <attribute>[=<value>]\n"));
176     fprintf(stderr,
177             _("\t--{,no}-request-pac request KDC include/exclude a PAC\n"));
178     exit(2);
179 }
180 
181 static krb5_context errctx;
182 static void
extended_com_err_fn(const char * myprog,errcode_t code,const char * fmt,va_list args)183 extended_com_err_fn(const char *myprog, errcode_t code, const char *fmt,
184                     va_list args)
185 {
186     const char *emsg;
187 
188     emsg = krb5_get_error_message(errctx, code);
189     fprintf(stderr, "%s: %s ", myprog, emsg);
190     krb5_free_error_message(errctx, emsg);
191     vfprintf(stderr, fmt, args);
192     fprintf(stderr, "\n");
193 }
194 
195 static int
add_preauth_opt(struct k_opts * opts,char * av)196 add_preauth_opt(struct k_opts *opts, char *av)
197 {
198     char *sep, *v;
199     krb5_gic_opt_pa_data *p, *x;
200     size_t newsize = (opts->num_pa_opts + 1) * sizeof(*opts->pa_opts);
201 
202     x = realloc(opts->pa_opts, newsize);
203     if (x == NULL)
204         return ENOMEM;
205     opts->pa_opts = x;
206 
207     p = &opts->pa_opts[opts->num_pa_opts];
208     sep = strchr(av, '=');
209     if (sep) {
210         *sep = '\0';
211         v = ++sep;
212         p->value = v;
213     } else {
214         p->value = "yes";
215     }
216     p->attr = av;
217     opts->num_pa_opts++;
218     return 0;
219 }
220 
221 static char *
parse_options(int argc,char ** argv,struct k_opts * opts)222 parse_options(int argc, char **argv, struct k_opts *opts)
223 {
224     struct option long_options[] = {
225         { "noforwardable", 0, NULL, 'F' },
226         { "noproxiable", 0, NULL, 'P' },
227         { "addresses", 0, NULL, 'a'},
228         { "forwardable", 0, NULL, 'f' },
229         { "proxiable", 0, NULL, 'p' },
230         { "noaddresses", 0, NULL, 'A' },
231         { "canonicalize", 0, NULL, 'C' },
232         { "enterprise", 0, NULL, 'E' },
233         { "request-pac", 0, &opts->request_pac, 1 },
234         { "no-request-pac", 0, &opts->not_request_pac, 1 },
235         { NULL, 0, NULL, 0 }
236     };
237     krb5_error_code ret;
238     int errflg = 0;
239     int i;
240 
241     while ((i = getopt_long(argc, argv, shopts, long_options, 0)) != -1) {
242         switch (i) {
243         case 'V':
244             opts->verbose = 1;
245             break;
246         case 'l':
247             /* Lifetime */
248             ret = krb5_string_to_deltat(optarg, &opts->lifetime);
249             if (ret || opts->lifetime == 0) {
250                 fprintf(stderr, _("Bad lifetime value %s\n"), optarg);
251                 errflg++;
252             }
253             break;
254         case 'r':
255             /* Renewable Time */
256             ret = krb5_string_to_deltat(optarg, &opts->rlife);
257             if (ret || opts->rlife == 0) {
258                 fprintf(stderr, _("Bad lifetime value %s\n"), optarg);
259                 errflg++;
260             }
261             break;
262         case 'f':
263             opts->forwardable = 1;
264             break;
265         case 'F':
266             opts->not_forwardable = 1;
267             break;
268         case 'p':
269             opts->proxiable = 1;
270             break;
271         case 'P':
272             opts->not_proxiable = 1;
273             break;
274         case 'n':
275             opts->anonymous = 1;
276             break;
277         case 'a':
278             opts->addresses = 1;
279             break;
280         case 'A':
281             opts->no_addresses = 1;
282             break;
283         case 's':
284             ret = krb5_string_to_deltat(optarg, &opts->starttime);
285             if (ret || opts->starttime == 0) {
286                 /* Parse as an absolute time; intentionally undocumented
287                  * but left for backwards compatibility. */
288                 krb5_timestamp abs_starttime;
289 
290                 ret = krb5_string_to_timestamp(optarg, &abs_starttime);
291                 if (ret || abs_starttime == 0) {
292                     fprintf(stderr, _("Bad start time value %s\n"), optarg);
293                     errflg++;
294                 } else {
295                     opts->starttime = ts_delta(abs_starttime, time(NULL));
296                 }
297             }
298             break;
299         case 'S':
300             opts->service_name = optarg;
301             break;
302         case 'k':
303             opts->action = INIT_KT;
304             break;
305         case 'i':
306             opts->use_client_keytab = 1;
307             break;
308         case 't':
309             if (opts->keytab_name != NULL) {
310                 fprintf(stderr, _("Only one -t option allowed.\n"));
311                 errflg++;
312             } else {
313                 opts->keytab_name = optarg;
314             }
315             break;
316         case 'T':
317             if (opts->armor_ccache != NULL) {
318                 fprintf(stderr, _("Only one armor_ccache\n"));
319                 errflg++;
320             } else {
321                 opts->armor_ccache = optarg;
322             }
323             break;
324         case 'R':
325             opts->action = RENEW;
326             break;
327         case 'v':
328             opts->action = VALIDATE;
329             break;
330         case 'c':
331             if (opts->k5_out_cache_name != NULL) {
332                 fprintf(stderr, _("Only one -c option allowed\n"));
333                 errflg++;
334             } else {
335                 opts->k5_out_cache_name = optarg;
336             }
337             break;
338         case 'I':
339             if (opts->k5_in_cache_name != NULL) {
340                 fprintf(stderr, _("Only one -I option allowed\n"));
341                 errflg++;
342             } else {
343                 opts->k5_in_cache_name = optarg;
344             }
345             break;
346         case 'X':
347             ret = add_preauth_opt(opts, optarg);
348             if (ret) {
349                 com_err(progname, ret, _("while adding preauth option"));
350                 errflg++;
351             }
352             break;
353         case 'C':
354             opts->canonicalize = 1;
355             break;
356         case 'E':
357             opts->enterprise = 1;
358             break;
359         case '4':
360             fprintf(stderr, _("Kerberos 4 is no longer supported\n"));
361             exit(3);
362             break;
363         case '5':
364             break;
365         case 0:
366             /* If this option set a flag, do nothing else now. */
367             break;
368         default:
369             errflg++;
370             break;
371         }
372     }
373 
374     if (opts->forwardable && opts->not_forwardable) {
375         fprintf(stderr, _("Only one of -f and -F allowed\n"));
376         errflg++;
377     }
378     if (opts->proxiable && opts->not_proxiable) {
379         fprintf(stderr, _("Only one of -p and -P allowed\n"));
380         errflg++;
381     }
382     if (opts->request_pac && opts->not_request_pac) {
383         fprintf(stderr, _("Only one of --request-pac and --no-request-pac "
384                           "allowed\n"));
385         errflg++;
386     }
387     if (opts->addresses && opts->no_addresses) {
388         fprintf(stderr, _("Only one of -a and -A allowed\n"));
389         errflg++;
390     }
391     if (opts->keytab_name != NULL && opts->use_client_keytab == 1) {
392         fprintf(stderr, _("Only one of -t and -i allowed\n"));
393         errflg++;
394     }
395     if ((opts->keytab_name != NULL || opts->use_client_keytab == 1) &&
396         opts->action != INIT_KT) {
397         opts->action = INIT_KT;
398         fprintf(stderr, _("keytab specified, forcing -k\n"));
399     }
400     if (argc - optind > 1) {
401         fprintf(stderr, _("Extra arguments (starting with \"%s\").\n"),
402                 argv[optind + 1]);
403         errflg++;
404     }
405 
406     if (errflg)
407         usage();
408 
409     opts->principal_name = (optind == argc - 1) ? argv[optind] : 0;
410     return opts->principal_name;
411 }
412 
413 static int
k5_begin(struct k_opts * opts,struct k5_data * k5)414 k5_begin(struct k_opts *opts, struct k5_data *k5)
415 {
416     krb5_error_code ret;
417     int success = 0;
418     int flags = opts->enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
419     krb5_ccache defcache = NULL;
420     krb5_principal defcache_princ = NULL, princ;
421     krb5_keytab keytab;
422     const char *deftype = NULL;
423     char *defrealm, *name;
424 
425     ret = krb5_init_context(&k5->ctx);
426     if (ret) {
427         com_err(progname, ret, _("while initializing Kerberos 5 library"));
428         return 0;
429     }
430     errctx = k5->ctx;
431 
432     if (opts->k5_out_cache_name) {
433         ret = krb5_cc_resolve(k5->ctx, opts->k5_out_cache_name, &k5->out_cc);
434         if (ret) {
435             com_err(progname, ret, _("resolving ccache %s"),
436                     opts->k5_out_cache_name);
437             goto cleanup;
438         }
439         if (opts->verbose) {
440             fprintf(stderr, _("Using specified cache: %s\n"),
441                     opts->k5_out_cache_name);
442         }
443     } else {
444         /* Resolve the default ccache and get its type and default principal
445          * (if it is initialized). */
446         ret = krb5_cc_default(k5->ctx, &defcache);
447         if (ret) {
448             com_err(progname, ret, _("while getting default ccache"));
449             goto cleanup;
450         }
451         deftype = krb5_cc_get_type(k5->ctx, defcache);
452         if (krb5_cc_get_principal(k5->ctx, defcache, &defcache_princ) != 0)
453             defcache_princ = NULL;
454     }
455 
456     /* Choose a client principal name. */
457     if (opts->principal_name != NULL) {
458         /* Use the specified principal name. */
459         ret = krb5_parse_name_flags(k5->ctx, opts->principal_name, flags,
460                                     &k5->me);
461         if (ret) {
462             com_err(progname, ret, _("when parsing name %s"),
463                     opts->principal_name);
464             goto cleanup;
465         }
466     } else if (opts->anonymous) {
467         /* Use the anonymous principal for the local realm. */
468         ret = krb5_get_default_realm(k5->ctx, &defrealm);
469         if (ret) {
470             com_err(progname, ret, _("while getting default realm"));
471             goto cleanup;
472         }
473         ret = krb5_build_principal_ext(k5->ctx, &k5->me,
474                                        strlen(defrealm), defrealm,
475                                        strlen(KRB5_WELLKNOWN_NAMESTR),
476                                        KRB5_WELLKNOWN_NAMESTR,
477                                        strlen(KRB5_ANONYMOUS_PRINCSTR),
478                                        KRB5_ANONYMOUS_PRINCSTR, 0);
479         krb5_free_default_realm(k5->ctx, defrealm);
480         if (ret) {
481             com_err(progname, ret, _("while building principal"));
482             goto cleanup;
483         }
484     } else if (opts->action == INIT_KT && opts->use_client_keytab) {
485         /* Use the first entry from the client keytab. */
486         ret = krb5_kt_client_default(k5->ctx, &keytab);
487         if (ret) {
488             com_err(progname, ret,
489                     _("When resolving the default client keytab"));
490             goto cleanup;
491         }
492         ret = k5_kt_get_principal(k5->ctx, keytab, &k5->me);
493         krb5_kt_close(k5->ctx, keytab);
494         if (ret) {
495             com_err(progname, ret,
496                     _("When determining client principal name from keytab"));
497             goto cleanup;
498         }
499     } else if (opts->action == INIT_KT) {
500         /* Use the default host/service name. */
501         ret = krb5_sname_to_principal(k5->ctx, NULL, NULL, KRB5_NT_SRV_HST,
502                                       &k5->me);
503         if (ret) {
504             com_err(progname, ret,
505                     _("when creating default server principal name"));
506             goto cleanup;
507         }
508     } else if (k5->out_cc != NULL) {
509         /* If the output ccache is initialized, use its principal. */
510         if (krb5_cc_get_principal(k5->ctx, k5->out_cc, &princ) == 0)
511             k5->me = princ;
512     } else if (defcache_princ != NULL) {
513         /* Use the default cache's principal, and use the default cache as the
514          * output cache. */
515         k5->out_cc = defcache;
516         defcache = NULL;
517         k5->me = defcache_princ;
518         defcache_princ = NULL;
519     }
520 
521     /* If we still haven't chosen, use the local username. */
522     if (k5->me == NULL) {
523         name = get_name_from_os();
524         if (name == NULL) {
525             fprintf(stderr, _("Unable to identify user\n"));
526             goto cleanup;
527         }
528         ret = krb5_parse_name_flags(k5->ctx, name, flags, &k5->me);
529         if (ret) {
530             com_err(progname, ret, _("when parsing name %s"), name);
531             goto cleanup;
532         }
533     }
534 
535     if (k5->out_cc == NULL && krb5_cc_support_switch(k5->ctx, deftype)) {
536         /* Use an existing cache for the client principal if we can. */
537         ret = krb5_cc_cache_match(k5->ctx, k5->me, &k5->out_cc);
538         if (ret && ret != KRB5_CC_NOTFOUND) {
539             com_err(progname, ret, _("while searching for ccache for %s"),
540                     opts->principal_name);
541             goto cleanup;
542         }
543         if (!ret) {
544             if (opts->verbose) {
545                 fprintf(stderr, _("Using existing cache: %s\n"),
546                         krb5_cc_get_name(k5->ctx, k5->out_cc));
547             }
548             k5->switch_to_cache = 1;
549         } else if (defcache_princ != NULL) {
550             /* Create a new cache to avoid overwriting the initialized default
551              * cache. */
552             ret = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->out_cc);
553             if (ret) {
554                 com_err(progname, ret, _("while generating new ccache"));
555                 goto cleanup;
556             }
557             if (opts->verbose) {
558                 fprintf(stderr, _("Using new cache: %s\n"),
559                         krb5_cc_get_name(k5->ctx, k5->out_cc));
560             }
561             k5->switch_to_cache = 1;
562         }
563     }
564 
565     /* Use the default cache if we haven't picked one yet. */
566     if (k5->out_cc == NULL) {
567         k5->out_cc = defcache;
568         defcache = NULL;
569         if (opts->verbose) {
570             fprintf(stderr, _("Using default cache: %s\n"),
571                     krb5_cc_get_name(k5->ctx, k5->out_cc));
572         }
573     }
574 
575     if (opts->k5_in_cache_name) {
576         ret = krb5_cc_resolve(k5->ctx, opts->k5_in_cache_name, &k5->in_cc);
577         if (ret) {
578             com_err(progname, ret, _("resolving ccache %s"),
579                     opts->k5_in_cache_name);
580             goto cleanup;
581         }
582         if (opts->verbose) {
583             fprintf(stderr, _("Using specified input cache: %s\n"),
584                     opts->k5_in_cache_name);
585         }
586     }
587 
588     ret = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
589     if (ret) {
590         com_err(progname, ret, _("when unparsing name"));
591         goto cleanup;
592     }
593     if (opts->verbose)
594         fprintf(stderr, _("Using principal: %s\n"), k5->name);
595 
596     opts->principal_name = k5->name;
597 
598     success = 1;
599 
600 cleanup:
601     if (defcache != NULL)
602         krb5_cc_close(k5->ctx, defcache);
603     krb5_free_principal(k5->ctx, defcache_princ);
604     return success;
605 }
606 
607 static void
k5_end(struct k5_data * k5)608 k5_end(struct k5_data *k5)
609 {
610     krb5_free_unparsed_name(k5->ctx, k5->name);
611     krb5_free_principal(k5->ctx, k5->me);
612     if (k5->in_cc != NULL)
613         krb5_cc_close(k5->ctx, k5->in_cc);
614     if (k5->out_cc != NULL)
615         krb5_cc_close(k5->ctx, k5->out_cc);
616     krb5_free_context(k5->ctx);
617     errctx = NULL;
618     memset(k5, 0, sizeof(*k5));
619 }
620 
621 static krb5_error_code KRB5_CALLCONV
kinit_prompter(krb5_context ctx,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])622 kinit_prompter(krb5_context ctx, void *data, const char *name,
623                const char *banner, int num_prompts, krb5_prompt prompts[])
624 {
625     krb5_boolean *pwprompt = data;
626     krb5_prompt_type *ptypes;
627     int i;
628 
629     /* Make a note if we receive a password prompt. */
630     ptypes = krb5_get_prompt_types(ctx);
631     for (i = 0; i < num_prompts; i++) {
632         if (ptypes != NULL && ptypes[i] == KRB5_PROMPT_TYPE_PASSWORD)
633             *pwprompt = TRUE;
634     }
635     return krb5_prompter_posix(ctx, data, name, banner, num_prompts, prompts);
636 }
637 
638 static int
k5_kinit(struct k_opts * opts,struct k5_data * k5)639 k5_kinit(struct k_opts *opts, struct k5_data *k5)
640 {
641     int notix = 1;
642     krb5_keytab keytab = 0;
643     krb5_creds my_creds;
644     krb5_error_code ret;
645     krb5_get_init_creds_opt *options = NULL;
646     krb5_boolean pwprompt = FALSE;
647     krb5_address **addresses = NULL;
648     krb5_principal cprinc;
649     krb5_ccache mcc = NULL;
650     int i;
651 
652     memset(&my_creds, 0, sizeof(my_creds));
653 
654     ret = krb5_get_init_creds_opt_alloc(k5->ctx, &options);
655     if (ret)
656         goto cleanup;
657 
658     if (opts->lifetime)
659         krb5_get_init_creds_opt_set_tkt_life(options, opts->lifetime);
660     if (opts->rlife)
661         krb5_get_init_creds_opt_set_renew_life(options, opts->rlife);
662     if (opts->forwardable)
663         krb5_get_init_creds_opt_set_forwardable(options, 1);
664     if (opts->not_forwardable)
665         krb5_get_init_creds_opt_set_forwardable(options, 0);
666     if (opts->proxiable)
667         krb5_get_init_creds_opt_set_proxiable(options, 1);
668     if (opts->not_proxiable)
669         krb5_get_init_creds_opt_set_proxiable(options, 0);
670     if (opts->canonicalize)
671         krb5_get_init_creds_opt_set_canonicalize(options, 1);
672     if (opts->anonymous)
673         krb5_get_init_creds_opt_set_anonymous(options, 1);
674     if (opts->addresses) {
675         ret = krb5_os_localaddr(k5->ctx, &addresses);
676         if (ret) {
677             com_err(progname, ret, _("getting local addresses"));
678             goto cleanup;
679         }
680         krb5_get_init_creds_opt_set_address_list(options, addresses);
681     }
682     if (opts->no_addresses)
683         krb5_get_init_creds_opt_set_address_list(options, NULL);
684     if (opts->armor_ccache != NULL) {
685         krb5_get_init_creds_opt_set_fast_ccache_name(k5->ctx, options,
686                                                      opts->armor_ccache);
687     }
688     if (opts->request_pac)
689         krb5_get_init_creds_opt_set_pac_request(k5->ctx, options, TRUE);
690     if (opts->not_request_pac)
691         krb5_get_init_creds_opt_set_pac_request(k5->ctx, options, FALSE);
692 
693 
694     if (opts->action == INIT_KT && opts->keytab_name != NULL) {
695 #ifndef _WIN32
696         if (strncmp(opts->keytab_name, "KDB:", 4) == 0) {
697             ret = kinit_kdb_init(&k5->ctx, k5->me->realm.data);
698             errctx = k5->ctx;
699             if (ret) {
700                 com_err(progname, ret,
701                         _("while setting up KDB keytab for realm %s"),
702                         k5->me->realm.data);
703                 goto cleanup;
704             }
705         }
706 #endif
707 
708         ret = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab);
709         if (ret) {
710             com_err(progname, ret, _("resolving keytab %s"),
711                     opts->keytab_name);
712             goto cleanup;
713         }
714         if (opts->verbose)
715             fprintf(stderr, _("Using keytab: %s\n"), opts->keytab_name);
716     } else if (opts->action == INIT_KT && opts->use_client_keytab) {
717         ret = krb5_kt_client_default(k5->ctx, &keytab);
718         if (ret) {
719             com_err(progname, ret, _("resolving default client keytab"));
720             goto cleanup;
721         }
722     }
723 
724     for (i = 0; i < opts->num_pa_opts; i++) {
725         ret = krb5_get_init_creds_opt_set_pa(k5->ctx, options,
726                                              opts->pa_opts[i].attr,
727                                              opts->pa_opts[i].value);
728         if (ret) {
729             com_err(progname, ret, _("while setting '%s'='%s'"),
730                     opts->pa_opts[i].attr, opts->pa_opts[i].value);
731             goto cleanup;
732         }
733         if (opts->verbose) {
734             fprintf(stderr, _("PA Option %s = %s\n"), opts->pa_opts[i].attr,
735                     opts->pa_opts[i].value);
736         }
737     }
738     if (k5->in_cc) {
739         ret = krb5_get_init_creds_opt_set_in_ccache(k5->ctx, options,
740                                                     k5->in_cc);
741         if (ret)
742             goto cleanup;
743     }
744     ret = krb5_get_init_creds_opt_set_out_ccache(k5->ctx, options, k5->out_cc);
745     if (ret)
746         goto cleanup;
747 
748     switch (opts->action) {
749     case INIT_PW:
750         ret = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me, 0,
751                                            kinit_prompter, &pwprompt,
752                                            opts->starttime, opts->service_name,
753                                            options);
754         break;
755     case INIT_KT:
756         ret = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me, keytab,
757                                          opts->starttime, opts->service_name,
758                                          options);
759         break;
760     case VALIDATE:
761         ret = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->out_cc,
762                                        opts->service_name);
763         break;
764     case RENEW:
765         ret = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->out_cc,
766                                      opts->service_name);
767         break;
768     }
769 
770     if (ret) {
771         char *doing = NULL;
772         switch (opts->action) {
773         case INIT_PW:
774         case INIT_KT:
775             doing = _("getting initial credentials");
776             break;
777         case VALIDATE:
778             doing = _("validating credentials");
779             break;
780         case RENEW:
781             doing = _("renewing credentials");
782             break;
783         }
784 
785         /* If reply decryption failed, or if pre-authentication failed and we
786          * were prompted for a password, assume the password was wrong. */
787         if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY ||
788             (pwprompt && ret == KRB5KDC_ERR_PREAUTH_FAILED)) {
789             fprintf(stderr, _("%s: Password incorrect while %s\n"), progname,
790                     doing);
791         } else {
792             com_err(progname, ret, _("while %s"), doing);
793         }
794         goto cleanup;
795     }
796 
797     if (opts->action != INIT_PW && opts->action != INIT_KT) {
798         cprinc = opts->canonicalize ? my_creds.client : k5->me;
799         ret = krb5_cc_new_unique(k5->ctx, "MEMORY", NULL, &mcc);
800         if (!ret)
801             ret = krb5_cc_initialize(k5->ctx, mcc, cprinc);
802         if (ret) {
803             com_err(progname, ret, _("when creating temporary cache"));
804             goto cleanup;
805         }
806         if (opts->verbose)
807             fprintf(stderr, _("Initialized cache\n"));
808 
809         ret = k5_cc_store_primary_cred(k5->ctx, mcc, &my_creds);
810         if (ret) {
811             com_err(progname, ret, _("while storing credentials"));
812             goto cleanup;
813         }
814         ret = krb5_cc_move(k5->ctx, mcc, k5->out_cc);
815         if (ret) {
816             com_err(progname, ret, _("while saving to cache %s"),
817                     opts->k5_out_cache_name ? opts->k5_out_cache_name : "");
818             goto cleanup;
819         }
820         mcc = NULL;
821         if (opts->verbose)
822             fprintf(stderr, _("Stored credentials\n"));
823     }
824     notix = 0;
825     if (k5->switch_to_cache) {
826         ret = krb5_cc_switch(k5->ctx, k5->out_cc);
827         if (ret) {
828             com_err(progname, ret, _("while switching to new ccache"));
829             goto cleanup;
830         }
831     }
832 
833 cleanup:
834 #ifndef _WIN32
835     kinit_kdb_fini();
836 #endif
837     if (mcc != NULL)
838         krb5_cc_destroy(k5->ctx, mcc);
839     if (options)
840         krb5_get_init_creds_opt_free(k5->ctx, options);
841     if (my_creds.client == k5->me)
842         my_creds.client = 0;
843     if (opts->pa_opts) {
844         free(opts->pa_opts);
845         opts->pa_opts = NULL;
846         opts->num_pa_opts = 0;
847     }
848     krb5_free_cred_contents(k5->ctx, &my_creds);
849     if (keytab != NULL)
850         krb5_kt_close(k5->ctx, keytab);
851     return notix ? 0 : 1;
852 }
853 
854 int
main(int argc,char * argv[])855 main(int argc, char *argv[])
856 {
857     struct k_opts opts;
858     struct k5_data k5;
859     int authed_k5 = 0;
860 
861     setlocale(LC_ALL, "");
862     progname = GET_PROGNAME(argv[0]);
863 
864     /* Ensure we can be driven from a pipe */
865     if (!isatty(fileno(stdin)))
866         setvbuf(stdin, 0, _IONBF, 0);
867     if (!isatty(fileno(stdout)))
868         setvbuf(stdout, 0, _IONBF, 0);
869     if (!isatty(fileno(stderr)))
870         setvbuf(stderr, 0, _IONBF, 0);
871 
872     memset(&opts, 0, sizeof(opts));
873     opts.action = INIT_PW;
874 
875     memset(&k5, 0, sizeof(k5));
876 
877     set_com_err_hook(extended_com_err_fn);
878 
879     parse_options(argc, argv, &opts);
880 
881     if (k5_begin(&opts, &k5))
882         authed_k5 = k5_kinit(&opts, &k5);
883 
884     if (authed_k5 && opts.verbose)
885         fprintf(stderr, _("Authenticated to Kerberos v5\n"));
886 
887     k5_end(&k5);
888 
889     if (!authed_k5)
890         exit(1);
891     return 0;
892 }
893