f1cd1b73db607cd6636dbe497f81b84bef317df5
[platform/upstream/krb5.git] / src / clients / kinit / kinit.c
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
48 char * get_name_from_os()
49 {
50     struct passwd *pw;
51     if ((pw = getpwuid((int) getuid())))
52         return pw->pw_name;
53     return 0;
54 }
55 #else /* HAVE_PWD_H */
56 #ifdef _WIN32
57 static
58 char * get_name_from_os()
59 {
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 */
64         return name;
65     } else {
66         return 0;
67     }
68 }
69 #else /* _WIN32 */
70 static
71 char * get_name_from_os()
72 {
73     return 0;
74 }
75 #endif /* _WIN32 */
76 #endif /* HAVE_PWD_H */
77
78 static char *progname;
79
80 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
81
82 struct k_opts
83 {
84     /* in seconds */
85     krb5_deltat starttime;
86     krb5_deltat lifetime;
87     krb5_deltat rlife;
88
89     int forwardable;
90     int proxiable;
91     int request_pac;
92     int anonymous;
93     int addresses;
94
95     int not_forwardable;
96     int not_proxiable;
97     int not_request_pac;
98     int no_addresses;
99
100     int verbose;
101
102     char* principal_name;
103     char* service_name;
104     char* keytab_name;
105     char* k5_in_cache_name;
106     char* k5_out_cache_name;
107     char *armor_ccache;
108
109     action_type action;
110     int use_client_keytab;
111
112     int num_pa_opts;
113     krb5_gic_opt_pa_data *pa_opts;
114
115     int canonicalize;
116     int enterprise;
117 };
118
119 struct k5_data
120 {
121     krb5_context ctx;
122     krb5_ccache in_cc, out_cc;
123     krb5_principal me;
124     char* name;
125     krb5_boolean switch_to_cache;
126 };
127
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. */
132
133 const char *shopts = "r:fpFPn54aAVl:s:c:kit:T:RS:vX:CEI:";
134
135 static void
136 usage()
137 {
138 #define USAGE_BREAK "\n\t"
139
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
147
148     fprintf(stderr, "Usage: %s [-V] "
149             "[-l lifetime] [-s start_time] "
150             USAGE_BREAK
151             "[-r renewable_life] "
152             "[-f | -F" USAGE_LONG_FORWARDABLE "] "
153             USAGE_BREAK_LONG
154             "[-p | -P" USAGE_LONG_PROXIABLE "] "
155             USAGE_BREAK_LONG
156             "-n "
157             "[-a | -A" USAGE_LONG_ADDRESSES "] "
158             USAGE_BREAK_LONG
159             "[" USAGE_LONG_REQUESTPAC "] "
160             USAGE_BREAK_LONG
161             "[-C" USAGE_LONG_CANONICALIZE "] "
162             USAGE_BREAK
163             "[-E" USAGE_LONG_ENTERPRISE "] "
164             USAGE_BREAK
165             "[-v] [-R] "
166             "[-k [-i|-t keytab_file]] "
167             "[-c cachename] "
168             USAGE_BREAK
169             "[-S service_name] [-T ticket_armor_cache]"
170             USAGE_BREAK
171             "[-X <attribute>[=<value>]] [principal]"
172             "\n\n",
173             progname);
174
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"));
198     exit(2);
199 }
200
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)
204 {
205     const char *emsg;
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");
211 }
212
213 static int
214 add_preauth_opt(struct k_opts *opts, char *av)
215 {
216     char *sep, *v;
217     krb5_gic_opt_pa_data *p, *x;
218
219     if (opts->num_pa_opts == 0) {
220         opts->pa_opts = malloc(sizeof(krb5_gic_opt_pa_data));
221         if (opts->pa_opts == NULL)
222             return ENOMEM;
223     } else {
224         size_t newsize = (opts->num_pa_opts + 1) * sizeof(krb5_gic_opt_pa_data);
225         x = realloc(opts->pa_opts, newsize);
226         if (x == NULL)
227             return ENOMEM;
228         opts->pa_opts = x;
229     }
230     p = &opts->pa_opts[opts->num_pa_opts];
231     sep = strchr(av, '=');
232     if (sep) {
233         *sep = '\0';
234         v = ++sep;
235         p->value = v;
236     } else {
237         p->value = "yes";
238     }
239     p->attr = av;
240     opts->num_pa_opts++;
241     return 0;
242 }
243
244 static char *
245 parse_options(argc, argv, opts)
246     int argc;
247     char **argv;
248     struct k_opts* opts;
249 {
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 },
261         { NULL, 0, NULL, 0 }
262     };
263     krb5_error_code code;
264     int errflg = 0;
265     int i;
266
267     while ((i = getopt_long(argc, argv, shopts, long_options, 0)) != -1) {
268         switch (i) {
269         case 'V':
270             opts->verbose = 1;
271             break;
272         case 'l':
273             /* Lifetime */
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);
277                 errflg++;
278             }
279             break;
280         case 'r':
281             /* Renewable Time */
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);
285                 errflg++;
286             }
287             break;
288         case 'f':
289             opts->forwardable = 1;
290             break;
291         case 'F':
292             opts->not_forwardable = 1;
293             break;
294         case 'p':
295             opts->proxiable = 1;
296             break;
297         case 'P':
298             opts->not_proxiable = 1;
299             break;
300         case 'n':
301             opts->anonymous = 1;
302             break;
303         case 'a':
304             opts->addresses = 1;
305             break;
306         case 'A':
307             opts->no_addresses = 1;
308             break;
309         case 's':
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;
315
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);
319                     errflg++;
320                 } else {
321                     opts->starttime = abs_starttime - time(0);
322                 }
323             }
324             break;
325         case 'S':
326             opts->service_name = optarg;
327             break;
328         case 'k':
329             opts->action = INIT_KT;
330             break;
331         case 'i':
332             opts->use_client_keytab = 1;
333             break;
334         case 't':
335             if (opts->keytab_name)
336             {
337                 fprintf(stderr, _("Only one -t option allowed.\n"));
338                 errflg++;
339             } else {
340                 opts->keytab_name = optarg;
341             }
342             break;
343         case 'T':
344             if (opts->armor_ccache) {
345                 fprintf(stderr, _("Only one armor_ccache\n"));
346                 errflg++;
347             } else opts->armor_ccache = optarg;
348             break;
349         case 'R':
350             opts->action = RENEW;
351             break;
352         case 'v':
353             opts->action = VALIDATE;
354             break;
355         case 'c':
356             if (opts->k5_out_cache_name)
357             {
358                 fprintf(stderr, _("Only one -c option allowed\n"));
359                 errflg++;
360             } else {
361                 opts->k5_out_cache_name = optarg;
362             }
363             break;
364         case 'I':
365             if (opts->k5_in_cache_name) {
366                 fprintf(stderr, _("Only one -I option allowed\n"));
367                 errflg++;
368             } else {
369                 opts->k5_in_cache_name = optarg;
370             }
371             break;
372         case 'X':
373             code = add_preauth_opt(opts, optarg);
374             if (code)
375             {
376                 com_err(progname, code, _("while adding preauth option"));
377                 errflg++;
378             }
379             break;
380         case 'C':
381             opts->canonicalize = 1;
382             break;
383         case 'E':
384             opts->enterprise = 1;
385             break;
386         case '4':
387             fprintf(stderr, _("Kerberos 4 is no longer supported\n"));
388             exit(3);
389             break;
390         case '5':
391             break;
392         case 0:
393             /* If this option set a flag, do nothing else now. */
394             break;
395         default:
396             errflg++;
397             break;
398         }
399     }
400
401     if (opts->forwardable && opts->not_forwardable)
402     {
403         fprintf(stderr, _("Only one of -f and -F allowed\n"));
404         errflg++;
405     }
406     if (opts->proxiable && opts->not_proxiable)
407     {
408         fprintf(stderr, _("Only one of -p and -P allowed\n"));
409         errflg++;
410     }
411     if (opts->request_pac && opts->not_request_pac)
412     {
413         fprintf(stderr, _("Only one of --request-pac and --no-request-pac "
414                           "allowed\n"));
415         errflg++;
416     }
417     if (opts->addresses && opts->no_addresses)
418     {
419         fprintf(stderr, _("Only one of -a and -A allowed\n"));
420         errflg++;
421     }
422     if (opts->keytab_name != NULL && opts->use_client_keytab == 1)
423     {
424         fprintf(stderr, _("Only one of -t and -i allowed\n"));
425         errflg++;
426     }
427     if ((opts->keytab_name != NULL || opts->use_client_keytab == 1) &&
428         opts->action != INIT_KT)
429     {
430         opts->action = INIT_KT;
431         fprintf(stderr, _("keytab specified, forcing -k\n"));
432     }
433
434     if (argc - optind > 1) {
435         fprintf(stderr, _("Extra arguments (starting with \"%s\").\n"),
436                 argv[optind+1]);
437         errflg++;
438     }
439
440     if (errflg) {
441         usage();
442     }
443
444     opts->principal_name = (optind == argc-1) ? argv[optind] : 0;
445     return opts->principal_name;
446 }
447
448 static int
449 k5_begin(opts, k5)
450     struct k_opts* opts;
451     struct k5_data* k5;
452 {
453     krb5_error_code code = 0;
454     int success = 0;
455     int flags = opts->enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
456     krb5_ccache defcache = NULL;
457     krb5_principal defcache_princ = NULL, princ;
458     krb5_keytab keytab;
459     const char *deftype = NULL;
460     char *defrealm, *name;
461
462     code = krb5_init_context(&k5->ctx);
463     if (code) {
464         com_err(progname, code, _("while initializing Kerberos 5 library"));
465         return 0;
466     }
467     errctx = k5->ctx;
468
469     if (opts->k5_out_cache_name) {
470         code = krb5_cc_resolve(k5->ctx, opts->k5_out_cache_name, &k5->out_cc);
471         if (code != 0) {
472             com_err(progname, code, _("resolving ccache %s"),
473                     opts->k5_out_cache_name);
474             goto cleanup;
475         }
476         if (opts->verbose) {
477             fprintf(stderr, _("Using specified cache: %s\n"),
478                     opts->k5_out_cache_name);
479         }
480     } else {
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);
484         if (code) {
485             com_err(progname, code, _("while getting default ccache"));
486             goto cleanup;
487         }
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;
491     }
492
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,
497                                      &k5->me);
498         if (code) {
499             com_err(progname, code, _("when parsing name %s"),
500                     opts->principal_name);
501             goto cleanup;
502         }
503     } else if (opts->anonymous) {
504         /* Use the anonymous principal for the local realm. */
505         code = krb5_get_default_realm(k5->ctx, &defrealm);
506         if (code) {
507             com_err(progname, code, _("while getting default realm"));
508             goto cleanup;
509         }
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,
516                                         0);
517         krb5_free_default_realm(k5->ctx, defrealm);
518         if (code) {
519             com_err(progname, code, _("while building principal"));
520             goto cleanup;
521         }
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);
525         if (code) {
526             com_err(progname, code,
527                     _("When resolving the default client keytab"));
528             goto cleanup;
529         }
530         code = k5_kt_get_principal(k5->ctx, keytab, &k5->me);
531         krb5_kt_close(k5->ctx, keytab);
532         if (code) {
533             com_err(progname, code,
534                     _("When determining client principal name from keytab"));
535             goto cleanup;
536         }
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,
540                                        &k5->me);
541         if (code) {
542             com_err(progname, code,
543                     _("when creating default server principal name"));
544             goto cleanup;
545         }
546         if (k5->me->realm.data[0] == 0) {
547             code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
548             if (code == 0) {
549                 com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
550                         _("(principal %s)"), k5->name);
551             } else {
552                 com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
553                         _("for local services"));
554             }
555             goto cleanup;
556         }
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)
560             k5->me = princ;
561     } else if (defcache_princ != NULL) {
562         /* Use the default cache's principal, and use the default cache as the
563          * output cache. */
564         k5->out_cc = defcache;
565         defcache = NULL;
566         k5->me = defcache_princ;
567         defcache_princ = NULL;
568     }
569
570     /* If we still haven't chosen, use the local username. */
571     if (k5->me == NULL) {
572         name = get_name_from_os();
573         if (name == NULL) {
574             fprintf(stderr, _("Unable to identify user\n"));
575             goto cleanup;
576         }
577         code = krb5_parse_name_flags(k5->ctx, name, flags, &k5->me);
578         if (code) {
579             com_err(progname, code, _("when parsing name %s"),
580                     name);
581             goto cleanup;
582         }
583     }
584
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);
591             goto cleanup;
592         }
593         if (code == 0) {
594             if (opts->verbose) {
595                 fprintf(stderr, _("Using existing cache: %s\n"),
596                         krb5_cc_get_name(k5->ctx, k5->out_cc));
597             }
598             k5->switch_to_cache = 1;
599         } else if (defcache_princ != NULL) {
600             /* Create a new cache to avoid overwriting the initialized default
601              * cache. */
602             code = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->out_cc);
603             if (code) {
604                 com_err(progname, code, _("while generating new ccache"));
605                 goto cleanup;
606             }
607             if (opts->verbose) {
608                 fprintf(stderr, _("Using new cache: %s\n"),
609                         krb5_cc_get_name(k5->ctx, k5->out_cc));
610             }
611             k5->switch_to_cache = 1;
612         }
613     }
614
615     /* Use the default cache if we haven't picked one yet. */
616     if (k5->out_cc == NULL) {
617         k5->out_cc = defcache;
618         defcache = NULL;
619         if (opts->verbose) {
620             fprintf(stderr, _("Using default cache: %s\n"),
621                     krb5_cc_get_name(k5->ctx, k5->out_cc));
622         }
623     }
624
625     if (opts->k5_in_cache_name) {
626         code = krb5_cc_resolve(k5->ctx, opts->k5_in_cache_name, &k5->in_cc);
627         if (code != 0) {
628             com_err(progname, code, _("resolving ccache %s"),
629                     opts->k5_in_cache_name);
630             goto cleanup;
631         }
632         if (opts->verbose) {
633             fprintf(stderr, _("Using specified input cache: %s\n"),
634                     opts->k5_in_cache_name);
635         }
636     }
637
638
639     code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
640     if (code) {
641         com_err(progname, code, _("when unparsing name"));
642         goto cleanup;
643     }
644     if (opts->verbose)
645         fprintf(stderr, _("Using principal: %s\n"), k5->name);
646
647     opts->principal_name = k5->name;
648
649     success = 1;
650
651 cleanup:
652     if (defcache != NULL)
653         krb5_cc_close(k5->ctx, defcache);
654     krb5_free_principal(k5->ctx, defcache_princ);
655     return success;
656 }
657
658 static void
659 k5_end(k5)
660     struct k5_data* k5;
661 {
662     if (k5->name)
663         krb5_free_unparsed_name(k5->ctx, k5->name);
664     if (k5->me)
665         krb5_free_principal(k5->ctx, k5->me);
666     if (k5->in_cc)
667         krb5_cc_close(k5->ctx, k5->in_cc);
668     if (k5->out_cc)
669         krb5_cc_close(k5->ctx, k5->out_cc);
670     if (k5->ctx)
671         krb5_free_context(k5->ctx);
672     errctx = NULL;
673     memset(k5, 0, sizeof(*k5));
674 }
675
676 static krb5_error_code
677 KRB5_CALLCONV
678 kinit_prompter(
679     krb5_context ctx,
680     void *data,
681     const char *name,
682     const char *banner,
683     int num_prompts,
684     krb5_prompt prompts[]
685 )
686 {
687     krb5_boolean *pwprompt = data;
688     krb5_prompt_type *ptypes;
689     int i;
690
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)
695             *pwprompt = TRUE;
696     }
697
698     return krb5_prompter_posix(ctx, data, name, banner, num_prompts, prompts);
699 }
700
701 static int
702 k5_kinit(opts, k5)
703     struct k_opts* opts;
704     struct k5_data* k5;
705 {
706     int notix = 1;
707     krb5_keytab keytab = 0;
708     krb5_creds my_creds;
709     krb5_error_code code = 0;
710     krb5_get_init_creds_opt *options = NULL;
711     krb5_boolean pwprompt = FALSE;
712     int i;
713
714     memset(&my_creds, 0, sizeof(my_creds));
715
716     code = krb5_get_init_creds_opt_alloc(k5->ctx, &options);
717     if (code)
718         goto cleanup;
719
720     /*
721       From this point on, we can goto cleanup because my_creds is
722       initialized.
723     */
724
725     if (opts->lifetime)
726         krb5_get_init_creds_opt_set_tkt_life(options, opts->lifetime);
727     if (opts->rlife)
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);
733     if (opts->proxiable)
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);
739     if (opts->anonymous)
740         krb5_get_init_creds_opt_set_anonymous(options, 1);
741     if (opts->addresses)
742     {
743         krb5_address **addresses = NULL;
744         code = krb5_os_localaddr(k5->ctx, &addresses);
745         if (code != 0) {
746             com_err(progname, code, _("getting local addresses"));
747             goto cleanup;
748         }
749         krb5_get_init_creds_opt_set_address_list(options, addresses);
750     }
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);
759
760
761     if ((opts->action == INIT_KT) && opts->keytab_name)
762     {
763 #ifndef _WIN32
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);
767             if (code != 0) {
768                 com_err(progname, code,
769                         _("while setting up KDB keytab for realm %s"),
770                         krb5_princ_realm(k5->ctx, k5->me)->data);
771                 goto cleanup;
772             }
773         }
774 #endif
775
776         code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab);
777         if (code != 0) {
778             com_err(progname, code, _("resolving keytab %s"),
779                     opts->keytab_name);
780             goto cleanup;
781         }
782         if (opts->verbose)
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);
786         if (code != 0) {
787             com_err(progname, code, _("resolving default client keytab"));
788             goto cleanup;
789         }
790     }
791
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);
796         if (code != 0) {
797             com_err(progname, code, _("while setting '%s'='%s'"),
798                     opts->pa_opts[i].attr, opts->pa_opts[i].value);
799             goto cleanup;
800         }
801         if (opts->verbose) {
802             fprintf(stderr, _("PA Option %s = %s\n"), opts->pa_opts[i].attr,
803                     opts->pa_opts[i].value);
804         }
805     }
806     if (k5->in_cc) {
807         code = krb5_get_init_creds_opt_set_in_ccache(k5->ctx, options,
808                                                      k5->in_cc);
809         if (code)
810             goto cleanup;
811     }
812     code = krb5_get_init_creds_opt_set_out_ccache(k5->ctx, options,
813                                                   k5->out_cc);
814     if (code)
815         goto cleanup;
816
817     switch (opts->action) {
818     case INIT_PW:
819         code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me,
820                                             0, kinit_prompter, &pwprompt,
821                                             opts->starttime,
822                                             opts->service_name,
823                                             options);
824         break;
825     case INIT_KT:
826         code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me,
827                                           keytab,
828                                           opts->starttime,
829                                           opts->service_name,
830                                           options);
831         break;
832     case VALIDATE:
833         code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->out_cc,
834                                         opts->service_name);
835         break;
836     case RENEW:
837         code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->out_cc,
838                                       opts->service_name);
839         break;
840     }
841
842     if (code) {
843         char *doing = 0;
844         switch (opts->action) {
845         case INIT_PW:
846         case INIT_KT:
847             doing = _("getting initial credentials");
848             break;
849         case VALIDATE:
850             doing = _("validating credentials");
851             break;
852         case RENEW:
853             doing = _("renewing credentials");
854             break;
855         }
856
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,
862                     doing);
863         } else {
864             com_err(progname, code, _("while %s"), doing);
865         }
866         goto cleanup;
867     }
868
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);
872         if (code) {
873             com_err(progname, code, _("when initializing cache %s"),
874                     opts->k5_out_cache_name?opts->k5_out_cache_name:"");
875             goto cleanup;
876         }
877         if (opts->verbose)
878             fprintf(stderr, _("Initialized cache\n"));
879
880         code = krb5_cc_store_cred(k5->ctx, k5->out_cc, &my_creds);
881         if (code) {
882             com_err(progname, code, _("while storing credentials"));
883             goto cleanup;
884         }
885         if (opts->verbose)
886             fprintf(stderr, _("Stored credentials\n"));
887     }
888     notix = 0;
889
890     if (k5->switch_to_cache) {
891         code = krb5_cc_switch(k5->ctx, k5->out_cc);
892         if (code) {
893             com_err(progname, code, _("while switching to new ccache"));
894             goto cleanup;
895         }
896     }
897
898 cleanup:
899 #ifndef _WIN32
900     kinit_kdb_fini();
901 #endif
902     if (options)
903         krb5_get_init_creds_opt_free(k5->ctx, options);
904     if (my_creds.client == k5->me) {
905         my_creds.client = 0;
906     }
907     if (opts->pa_opts) {
908         free(opts->pa_opts);
909         opts->pa_opts = NULL;
910         opts->num_pa_opts = 0;
911     }
912     krb5_free_cred_contents(k5->ctx, &my_creds);
913     if (keytab)
914         krb5_kt_close(k5->ctx, keytab);
915     return notix?0:1;
916 }
917
918 int
919 main(argc, argv)
920     int argc;
921     char **argv;
922 {
923     struct k_opts opts;
924     struct k5_data k5;
925     int authed_k5 = 0;
926
927     setlocale(LC_ALL, "");
928     progname = GET_PROGNAME(argv[0]);
929
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);
937
938     memset(&opts, 0, sizeof(opts));
939     opts.action = INIT_PW;
940
941     memset(&k5, 0, sizeof(k5));
942
943     set_com_err_hook (extended_com_err_fn);
944
945     parse_options(argc, argv, &opts);
946
947     if (k5_begin(&opts, &k5))
948         authed_k5 = k5_kinit(&opts, &k5);
949
950     if (authed_k5 && opts.verbose)
951         fprintf(stderr, _("Authenticated to Kerberos v5\n"));
952
953     k5_end(&k5);
954
955     if (!authed_k5)
956         exit(1);
957     return 0;
958 }