/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* clients/kinit/kinit.c - Initialize a credential cache */ /* * Copyright 1990, 2008 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ #include "autoconf.h" #include #include "k5-platform.h" /* For asprintf and getopt */ #include #include "extern.h" #include #include #include #include #include #include #ifndef _WIN32 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/') + 1 : (x)) #else #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x)) #endif #ifdef HAVE_PWD_H #include static char * get_name_from_os() { struct passwd *pw; pw = getpwuid(getuid()); return (pw != NULL) ? pw->pw_name : NULL; } #else /* HAVE_PWD_H */ #ifdef _WIN32 static char * get_name_from_os() { static char name[1024]; DWORD name_size = sizeof(name); if (GetUserName(name, &name_size)) { name[sizeof(name) - 1] = '\0'; /* Just to be extra safe */ return name; } else { return NULL; } } #else /* _WIN32 */ static char * get_name_from_os() { return NULL; } #endif /* _WIN32 */ #endif /* HAVE_PWD_H */ static char *progname; typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type; struct k_opts { /* In seconds */ krb5_deltat starttime; krb5_deltat lifetime; krb5_deltat rlife; int forwardable; int proxiable; int request_pac; int anonymous; int addresses; int not_forwardable; int not_proxiable; int not_request_pac; int no_addresses; int verbose; char *principal_name; char *service_name; char *keytab_name; char *k5_in_cache_name; char *k5_out_cache_name; char *armor_ccache; action_type action; int use_client_keytab; int num_pa_opts; krb5_gic_opt_pa_data *pa_opts; int canonicalize; int enterprise; }; struct k5_data { krb5_context ctx; krb5_ccache in_cc, out_cc; krb5_principal me; char *name; krb5_boolean switch_to_cache; }; /* * If struct[2] == NULL, then long_getopt acts as if the short flag struct[3] * were specified. If struct[2] != NULL, then struct[3] is stored in * *(struct[2]), the array index which was specified is stored in *index, and * long_getopt() returns 0. */ const char *shopts = "r:fpFPn54aAVl:s:c:kit:T:RS:vX:CEI:"; #define USAGE_BREAK "\n\t" static void usage() { fprintf(stderr, _("Usage: %s [-V] [-l lifetime] [-s start_time] " "[-r renewable_life]\n" "\t[-f | -F] [-p | -P] [-n] [-a | -A] [-C] [-E]\n" "\t[--request-pac | --no-request-pac]\n" "\t[-v] [-R] [-k [-i|-t keytab_file]] [-c cachename]\n" "\t[-S service_name] [-I input_ccache] [-T ticket_armor_cache]\n" "\t[-X [=]] [principal]\n" "\n"), progname); fprintf(stderr, " options:\n"); fprintf(stderr, _("\t-V verbose\n")); fprintf(stderr, _("\t-l lifetime\n")); fprintf(stderr, _("\t-s start time\n")); fprintf(stderr, _("\t-r renewable lifetime\n")); fprintf(stderr, _("\t-f forwardable\n")); fprintf(stderr, _("\t-F not forwardable\n")); fprintf(stderr, _("\t-p proxiable\n")); fprintf(stderr, _("\t-P not proxiable\n")); fprintf(stderr, _("\t-n anonymous\n")); fprintf(stderr, _("\t-a include addresses\n")); fprintf(stderr, _("\t-A do not include addresses\n")); fprintf(stderr, _("\t-v validate\n")); fprintf(stderr, _("\t-R renew\n")); fprintf(stderr, _("\t-C canonicalize\n")); fprintf(stderr, _("\t-E client is enterprise principal name\n")); fprintf(stderr, _("\t-k use keytab\n")); fprintf(stderr, _("\t-i use default client keytab (with -k)\n")); fprintf(stderr, _("\t-t filename of keytab to use\n")); fprintf(stderr, _("\t-c Kerberos 5 cache name\n")); fprintf(stderr, _("\t-S service\n")); fprintf(stderr, _("\t-I input credential cache\n")); fprintf(stderr, _("\t-T armor credential cache\n")); fprintf(stderr, _("\t-X [=]\n")); fprintf(stderr, _("\t--{,no}-request-pac request KDC include/exclude a PAC\n")); exit(2); } static krb5_context errctx; static void extended_com_err_fn(const char *myprog, errcode_t code, const char *fmt, va_list args) { const char *emsg; emsg = krb5_get_error_message(errctx, code); fprintf(stderr, "%s: %s ", myprog, emsg); krb5_free_error_message(errctx, emsg); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); } static int add_preauth_opt(struct k_opts *opts, char *av) { char *sep, *v; krb5_gic_opt_pa_data *p, *x; size_t newsize = (opts->num_pa_opts + 1) * sizeof(*opts->pa_opts); x = realloc(opts->pa_opts, newsize); if (x == NULL) return ENOMEM; opts->pa_opts = x; p = &opts->pa_opts[opts->num_pa_opts]; sep = strchr(av, '='); if (sep) { *sep = '\0'; v = ++sep; p->value = v; } else { p->value = "yes"; } p->attr = av; opts->num_pa_opts++; return 0; } static char * parse_options(int argc, char **argv, struct k_opts *opts) { struct option long_options[] = { { "noforwardable", 0, NULL, 'F' }, { "noproxiable", 0, NULL, 'P' }, { "addresses", 0, NULL, 'a'}, { "forwardable", 0, NULL, 'f' }, { "proxiable", 0, NULL, 'p' }, { "noaddresses", 0, NULL, 'A' }, { "canonicalize", 0, NULL, 'C' }, { "enterprise", 0, NULL, 'E' }, { "request-pac", 0, &opts->request_pac, 1 }, { "no-request-pac", 0, &opts->not_request_pac, 1 }, { NULL, 0, NULL, 0 } }; krb5_error_code ret; int errflg = 0; int i; while ((i = getopt_long(argc, argv, shopts, long_options, 0)) != -1) { switch (i) { case 'V': opts->verbose = 1; break; case 'l': /* Lifetime */ ret = krb5_string_to_deltat(optarg, &opts->lifetime); if (ret || opts->lifetime == 0) { fprintf(stderr, _("Bad lifetime value %s\n"), optarg); errflg++; } break; case 'r': /* Renewable Time */ ret = krb5_string_to_deltat(optarg, &opts->rlife); if (ret || opts->rlife == 0) { fprintf(stderr, _("Bad lifetime value %s\n"), optarg); errflg++; } break; case 'f': opts->forwardable = 1; break; case 'F': opts->not_forwardable = 1; break; case 'p': opts->proxiable = 1; break; case 'P': opts->not_proxiable = 1; break; case 'n': opts->anonymous = 1; break; case 'a': opts->addresses = 1; break; case 'A': opts->no_addresses = 1; break; case 's': ret = krb5_string_to_deltat(optarg, &opts->starttime); if (ret || opts->starttime == 0) { /* Parse as an absolute time; intentionally undocumented * but left for backwards compatibility. */ krb5_timestamp abs_starttime; ret = krb5_string_to_timestamp(optarg, &abs_starttime); if (ret || abs_starttime == 0) { fprintf(stderr, _("Bad start time value %s\n"), optarg); errflg++; } else { opts->starttime = ts_delta(abs_starttime, time(NULL)); } } break; case 'S': opts->service_name = optarg; break; case 'k': opts->action = INIT_KT; break; case 'i': opts->use_client_keytab = 1; break; case 't': if (opts->keytab_name != NULL) { fprintf(stderr, _("Only one -t option allowed.\n")); errflg++; } else { opts->keytab_name = optarg; } break; case 'T': if (opts->armor_ccache != NULL) { fprintf(stderr, _("Only one armor_ccache\n")); errflg++; } else { opts->armor_ccache = optarg; } break; case 'R': opts->action = RENEW; break; case 'v': opts->action = VALIDATE; break; case 'c': if (opts->k5_out_cache_name != NULL) { fprintf(stderr, _("Only one -c option allowed\n")); errflg++; } else { opts->k5_out_cache_name = optarg; } break; case 'I': if (opts->k5_in_cache_name != NULL) { fprintf(stderr, _("Only one -I option allowed\n")); errflg++; } else { opts->k5_in_cache_name = optarg; } break; case 'X': ret = add_preauth_opt(opts, optarg); if (ret) { com_err(progname, ret, _("while adding preauth option")); errflg++; } break; case 'C': opts->canonicalize = 1; break; case 'E': opts->enterprise = 1; break; case '4': fprintf(stderr, _("Kerberos 4 is no longer supported\n")); exit(3); break; case '5': break; case 0: /* If this option set a flag, do nothing else now. */ break; default: errflg++; break; } } if (opts->forwardable && opts->not_forwardable) { fprintf(stderr, _("Only one of -f and -F allowed\n")); errflg++; } if (opts->proxiable && opts->not_proxiable) { fprintf(stderr, _("Only one of -p and -P allowed\n")); errflg++; } if (opts->request_pac && opts->not_request_pac) { fprintf(stderr, _("Only one of --request-pac and --no-request-pac " "allowed\n")); errflg++; } if (opts->addresses && opts->no_addresses) { fprintf(stderr, _("Only one of -a and -A allowed\n")); errflg++; } if (opts->keytab_name != NULL && opts->use_client_keytab == 1) { fprintf(stderr, _("Only one of -t and -i allowed\n")); errflg++; } if ((opts->keytab_name != NULL || opts->use_client_keytab == 1) && opts->action != INIT_KT) { opts->action = INIT_KT; fprintf(stderr, _("keytab specified, forcing -k\n")); } if (argc - optind > 1) { fprintf(stderr, _("Extra arguments (starting with \"%s\").\n"), argv[optind + 1]); errflg++; } if (errflg) usage(); opts->principal_name = (optind == argc - 1) ? argv[optind] : 0; return opts->principal_name; } static int k5_begin(struct k_opts *opts, struct k5_data *k5) { krb5_error_code ret; int success = 0; int flags = opts->enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0; krb5_ccache defcache = NULL; krb5_principal defcache_princ = NULL, princ; krb5_keytab keytab; const char *deftype = NULL; char *defrealm, *name; ret = krb5_init_context(&k5->ctx); if (ret) { com_err(progname, ret, _("while initializing Kerberos 5 library")); return 0; } errctx = k5->ctx; if (opts->k5_out_cache_name) { ret = krb5_cc_resolve(k5->ctx, opts->k5_out_cache_name, &k5->out_cc); if (ret) { com_err(progname, ret, _("resolving ccache %s"), opts->k5_out_cache_name); goto cleanup; } if (opts->verbose) { fprintf(stderr, _("Using specified cache: %s\n"), opts->k5_out_cache_name); } } else { /* Resolve the default ccache and get its type and default principal * (if it is initialized). */ ret = krb5_cc_default(k5->ctx, &defcache); if (ret) { com_err(progname, ret, _("while getting default ccache")); goto cleanup; } deftype = krb5_cc_get_type(k5->ctx, defcache); if (krb5_cc_get_principal(k5->ctx, defcache, &defcache_princ) != 0) defcache_princ = NULL; } /* Choose a client principal name. */ if (opts->principal_name != NULL) { /* Use the specified principal name. */ ret = krb5_parse_name_flags(k5->ctx, opts->principal_name, flags, &k5->me); if (ret) { com_err(progname, ret, _("when parsing name %s"), opts->principal_name); goto cleanup; } } else if (opts->anonymous) { /* Use the anonymous principal for the local realm. */ ret = krb5_get_default_realm(k5->ctx, &defrealm); if (ret) { com_err(progname, ret, _("while getting default realm")); goto cleanup; } ret = krb5_build_principal_ext(k5->ctx, &k5->me, strlen(defrealm), defrealm, strlen(KRB5_WELLKNOWN_NAMESTR), KRB5_WELLKNOWN_NAMESTR, strlen(KRB5_ANONYMOUS_PRINCSTR), KRB5_ANONYMOUS_PRINCSTR, 0); krb5_free_default_realm(k5->ctx, defrealm); if (ret) { com_err(progname, ret, _("while building principal")); goto cleanup; } } else if (opts->action == INIT_KT && opts->use_client_keytab) { /* Use the first entry from the client keytab. */ ret = krb5_kt_client_default(k5->ctx, &keytab); if (ret) { com_err(progname, ret, _("When resolving the default client keytab")); goto cleanup; } ret = k5_kt_get_principal(k5->ctx, keytab, &k5->me); krb5_kt_close(k5->ctx, keytab); if (ret) { com_err(progname, ret, _("When determining client principal name from keytab")); goto cleanup; } } else if (opts->action == INIT_KT) { /* Use the default host/service name. */ ret = krb5_sname_to_principal(k5->ctx, NULL, NULL, KRB5_NT_SRV_HST, &k5->me); if (ret) { com_err(progname, ret, _("when creating default server principal name")); goto cleanup; } } else if (k5->out_cc != NULL) { /* If the output ccache is initialized, use its principal. */ if (krb5_cc_get_principal(k5->ctx, k5->out_cc, &princ) == 0) k5->me = princ; } else if (defcache_princ != NULL) { /* Use the default cache's principal, and use the default cache as the * output cache. */ k5->out_cc = defcache; defcache = NULL; k5->me = defcache_princ; defcache_princ = NULL; } /* If we still haven't chosen, use the local username. */ if (k5->me == NULL) { name = get_name_from_os(); if (name == NULL) { fprintf(stderr, _("Unable to identify user\n")); goto cleanup; } ret = krb5_parse_name_flags(k5->ctx, name, flags, &k5->me); if (ret) { com_err(progname, ret, _("when parsing name %s"), name); goto cleanup; } } if (k5->out_cc == NULL && krb5_cc_support_switch(k5->ctx, deftype)) { /* Use an existing cache for the client principal if we can. */ ret = krb5_cc_cache_match(k5->ctx, k5->me, &k5->out_cc); if (ret && ret != KRB5_CC_NOTFOUND) { com_err(progname, ret, _("while searching for ccache for %s"), opts->principal_name); goto cleanup; } if (!ret) { if (opts->verbose) { fprintf(stderr, _("Using existing cache: %s\n"), krb5_cc_get_name(k5->ctx, k5->out_cc)); } k5->switch_to_cache = 1; } else if (defcache_princ != NULL) { /* Create a new cache to avoid overwriting the initialized default * cache. */ ret = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->out_cc); if (ret) { com_err(progname, ret, _("while generating new ccache")); goto cleanup; } if (opts->verbose) { fprintf(stderr, _("Using new cache: %s\n"), krb5_cc_get_name(k5->ctx, k5->out_cc)); } k5->switch_to_cache = 1; } } /* Use the default cache if we haven't picked one yet. */ if (k5->out_cc == NULL) { k5->out_cc = defcache; defcache = NULL; if (opts->verbose) { fprintf(stderr, _("Using default cache: %s\n"), krb5_cc_get_name(k5->ctx, k5->out_cc)); } } if (opts->k5_in_cache_name) { ret = krb5_cc_resolve(k5->ctx, opts->k5_in_cache_name, &k5->in_cc); if (ret) { com_err(progname, ret, _("resolving ccache %s"), opts->k5_in_cache_name); goto cleanup; } if (opts->verbose) { fprintf(stderr, _("Using specified input cache: %s\n"), opts->k5_in_cache_name); } } ret = krb5_unparse_name(k5->ctx, k5->me, &k5->name); if (ret) { com_err(progname, ret, _("when unparsing name")); goto cleanup; } if (opts->verbose) fprintf(stderr, _("Using principal: %s\n"), k5->name); opts->principal_name = k5->name; success = 1; cleanup: if (defcache != NULL) krb5_cc_close(k5->ctx, defcache); krb5_free_principal(k5->ctx, defcache_princ); return success; } static void k5_end(struct k5_data *k5) { krb5_free_unparsed_name(k5->ctx, k5->name); krb5_free_principal(k5->ctx, k5->me); if (k5->in_cc != NULL) krb5_cc_close(k5->ctx, k5->in_cc); if (k5->out_cc != NULL) krb5_cc_close(k5->ctx, k5->out_cc); krb5_free_context(k5->ctx); errctx = NULL; memset(k5, 0, sizeof(*k5)); } 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[]) { krb5_boolean *pwprompt = data; krb5_prompt_type *ptypes; int i; /* Make a note if we receive a password prompt. */ ptypes = krb5_get_prompt_types(ctx); for (i = 0; i < num_prompts; i++) { if (ptypes != NULL && ptypes[i] == KRB5_PROMPT_TYPE_PASSWORD) *pwprompt = TRUE; } return krb5_prompter_posix(ctx, data, name, banner, num_prompts, prompts); } static int k5_kinit(struct k_opts *opts, struct k5_data *k5) { int notix = 1; krb5_keytab keytab = 0; krb5_creds my_creds; krb5_error_code ret; krb5_get_init_creds_opt *options = NULL; krb5_boolean pwprompt = FALSE; krb5_address **addresses = NULL; int i; memset(&my_creds, 0, sizeof(my_creds)); ret = krb5_get_init_creds_opt_alloc(k5->ctx, &options); if (ret) goto cleanup; if (opts->lifetime) krb5_get_init_creds_opt_set_tkt_life(options, opts->lifetime); if (opts->rlife) krb5_get_init_creds_opt_set_renew_life(options, opts->rlife); if (opts->forwardable) krb5_get_init_creds_opt_set_forwardable(options, 1); if (opts->not_forwardable) krb5_get_init_creds_opt_set_forwardable(options, 0); if (opts->proxiable) krb5_get_init_creds_opt_set_proxiable(options, 1); if (opts->not_proxiable) krb5_get_init_creds_opt_set_proxiable(options, 0); if (opts->canonicalize) krb5_get_init_creds_opt_set_canonicalize(options, 1); if (opts->anonymous) krb5_get_init_creds_opt_set_anonymous(options, 1); if (opts->addresses) { ret = krb5_os_localaddr(k5->ctx, &addresses); if (ret) { com_err(progname, ret, _("getting local addresses")); goto cleanup; } krb5_get_init_creds_opt_set_address_list(options, addresses); } if (opts->no_addresses) krb5_get_init_creds_opt_set_address_list(options, NULL); if (opts->armor_ccache != NULL) { krb5_get_init_creds_opt_set_fast_ccache_name(k5->ctx, options, opts->armor_ccache); } if (opts->request_pac) krb5_get_init_creds_opt_set_pac_request(k5->ctx, options, TRUE); if (opts->not_request_pac) krb5_get_init_creds_opt_set_pac_request(k5->ctx, options, FALSE); if (opts->action == INIT_KT && opts->keytab_name != NULL) { #ifndef _WIN32 if (strncmp(opts->keytab_name, "KDB:", 4) == 0) { ret = kinit_kdb_init(&k5->ctx, k5->me->realm.data); errctx = k5->ctx; if (ret) { com_err(progname, ret, _("while setting up KDB keytab for realm %s"), k5->me->realm.data); goto cleanup; } } #endif ret = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab); if (ret) { com_err(progname, ret, _("resolving keytab %s"), opts->keytab_name); goto cleanup; } if (opts->verbose) fprintf(stderr, _("Using keytab: %s\n"), opts->keytab_name); } else if (opts->action == INIT_KT && opts->use_client_keytab) { ret = krb5_kt_client_default(k5->ctx, &keytab); if (ret) { com_err(progname, ret, _("resolving default client keytab")); goto cleanup; } } for (i = 0; i < opts->num_pa_opts; i++) { ret = krb5_get_init_creds_opt_set_pa(k5->ctx, options, opts->pa_opts[i].attr, opts->pa_opts[i].value); if (ret) { com_err(progname, ret, _("while setting '%s'='%s'"), opts->pa_opts[i].attr, opts->pa_opts[i].value); goto cleanup; } if (opts->verbose) { fprintf(stderr, _("PA Option %s = %s\n"), opts->pa_opts[i].attr, opts->pa_opts[i].value); } } if (k5->in_cc) { ret = krb5_get_init_creds_opt_set_in_ccache(k5->ctx, options, k5->in_cc); if (ret) goto cleanup; } ret = krb5_get_init_creds_opt_set_out_ccache(k5->ctx, options, k5->out_cc); if (ret) goto cleanup; switch (opts->action) { case INIT_PW: ret = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me, 0, kinit_prompter, &pwprompt, opts->starttime, opts->service_name, options); break; case INIT_KT: ret = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me, keytab, opts->starttime, opts->service_name, options); break; case VALIDATE: ret = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->out_cc, opts->service_name); break; case RENEW: ret = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->out_cc, opts->service_name); break; } if (ret) { char *doing = NULL; switch (opts->action) { case INIT_PW: case INIT_KT: doing = _("getting initial credentials"); break; case VALIDATE: doing = _("validating credentials"); break; case RENEW: doing = _("renewing credentials"); break; } /* If reply decryption failed, or if pre-authentication failed and we * were prompted for a password, assume the password was wrong. */ if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY || (pwprompt && ret == KRB5KDC_ERR_PREAUTH_FAILED)) { fprintf(stderr, _("%s: Password incorrect while %s\n"), progname, doing); } else { com_err(progname, ret, _("while %s"), doing); } goto cleanup; } if (opts->action != INIT_PW && opts->action != INIT_KT) { ret = krb5_cc_initialize(k5->ctx, k5->out_cc, opts->canonicalize ? my_creds.client : k5->me); if (ret) { com_err(progname, ret, _("when initializing cache %s"), opts->k5_out_cache_name ? opts->k5_out_cache_name : ""); goto cleanup; } if (opts->verbose) fprintf(stderr, _("Initialized cache\n")); ret = k5_cc_store_primary_cred(k5->ctx, k5->out_cc, &my_creds); if (ret) { com_err(progname, ret, _("while storing credentials")); goto cleanup; } if (opts->verbose) fprintf(stderr, _("Stored credentials\n")); } notix = 0; if (k5->switch_to_cache) { ret = krb5_cc_switch(k5->ctx, k5->out_cc); if (ret) { com_err(progname, ret, _("while switching to new ccache")); goto cleanup; } } cleanup: #ifndef _WIN32 kinit_kdb_fini(); #endif if (options) krb5_get_init_creds_opt_free(k5->ctx, options); if (my_creds.client == k5->me) my_creds.client = 0; if (opts->pa_opts) { free(opts->pa_opts); opts->pa_opts = NULL; opts->num_pa_opts = 0; } krb5_free_cred_contents(k5->ctx, &my_creds); if (keytab != NULL) krb5_kt_close(k5->ctx, keytab); return notix ? 0 : 1; } int main(int argc, char *argv[]) { struct k_opts opts; struct k5_data k5; int authed_k5 = 0; setlocale(LC_ALL, ""); progname = GET_PROGNAME(argv[0]); /* Ensure we can be driven from a pipe */ if (!isatty(fileno(stdin))) setvbuf(stdin, 0, _IONBF, 0); if (!isatty(fileno(stdout))) setvbuf(stdout, 0, _IONBF, 0); if (!isatty(fileno(stderr))) setvbuf(stderr, 0, _IONBF, 0); memset(&opts, 0, sizeof(opts)); opts.action = INIT_PW; memset(&k5, 0, sizeof(k5)); set_com_err_hook(extended_com_err_fn); parse_options(argc, argv, &opts); if (k5_begin(&opts, &k5)) authed_k5 = k5_kinit(&opts, &k5); if (authed_k5 && opts.verbose) fprintf(stderr, _("Authenticated to Kerberos v5\n")); k5_end(&k5); if (!authed_k5) exit(1); return 0; }