/* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * clients/kinit/kinit.c * * Copyright 1990 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. * * * Initialize a credentials cache. */ #include #include #include #include #include #ifdef KRB5_KRB4_COMPAT #include #define HAVE_KRB524 #else #undef HAVE_KRB524 #endif #include #include #include #include #include #include #include #ifdef GETOPT_LONG #include #else #ifdef HAVE_UNISTD_H #include #ifdef sun /* SunOS4 unistd didn't declare these; okay to make unconditional? */ extern int optind; extern char *optarg; #endif /* sun */ #else extern int optind; extern char *optarg; extern int getopt(); #endif /* HAVE_UNISTD_H */ #endif /* GETOPT_LONG */ #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; if ((pw = getpwuid((int) getuid()))) return pw->pw_name; return 0; } #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 0; } } #else /* _WIN32 */ static char * get_name_from_os() { return 0; } #endif /* _WIN32 */ #endif /* HAVE_PWD_H */ static char* progname_v5 = 0; #ifdef KRB5_KRB4_COMPAT static char* progname_v4 = 0; static char* progname_v524 = 0; #endif #include static int got_k5 = 0; static int got_k4 = 0; static int default_k5 = 1; #if defined(KRB5_KRB4_COMPAT) && defined(KINIT_DEFAULT_BOTH) static int default_k4 = 1; #else static int default_k4 = 0; #endif static int authed_k5 = 0; static int authed_k4 = 0; #define KRB4_BACKUP_DEFAULT_LIFE_SECS 24*60*60 /* 1 day */ #define ROOT_UNAME "root" 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 addresses; int not_forwardable; int not_proxiable; int no_addresses; int verbose; char* principal_name; char* service_name; char* keytab_name; char* k5_cache_name; char* k4_cache_name; action_type action; int num_pa_opts; krb5_gic_opt_pa_data *pa_opts; }; int forwardable_flag = 0; int renewable_flag = 0; int proxiable_flag = 0; int no_address_flag = 0; profile_options_boolean config_option[] = { { "forwardable", &forwardable_flag, 0 }, { "renewable", &renewable_flag, 0 }, { "proxiable", &proxiable_flag, 0 }, { "no_addresses", &no_address_flag, 0 }, { NULL, NULL, 0 } }; char *renew_timeval=NULL; char *life_timeval=NULL; int lifetime_specified; int renewtime_specified; profile_option_strings config_times[] = { { "max_life", &life_timeval, 0 }, { "max_renewable_life", &renew_timeval, 0 }, { NULL, NULL, 0 } }; struct k5_data { krb5_context ctx; krb5_ccache cc; krb5_principal me; char* name; }; struct k4_data { krb5_deltat lifetime; #ifdef KRB5_KRB4_COMPAT char aname[ANAME_SZ + 1]; char inst[INST_SZ + 1]; char realm[REALM_SZ + 1]; char name[ANAME_SZ + 1 + INST_SZ + 1 + REALM_SZ + 1]; #endif }; char *realmdef[] = { "realms", NULL, "kinit", NULL }; char *appdef[] = { "appdefaults", "kinit", NULL }; #define krb_realm (*(realmdef + 1)) #define lifetime_specified config_times[0].found #define renewtime_specified config_times[1].found /* * Try no preauthentication first; then try the encrypted timestamp */ krb5_preauthtype * preauth = NULL; krb5_preauthtype preauth_list[2] = { 0, -1 }; static void _kwarnd_add_warning(char *, char *, time_t); static void _kwarnd_del_warning(char *, char *); #ifdef GETOPT_LONG /* if struct[2] == NULL, then long_getopt acts as if the short flag struct[3] was 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. */ 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' }, { NULL, 0, NULL, 0 } }; #define GETOPT(argc, argv, str) getopt_long(argc, argv, str, long_options, 0) #else #define GETOPT(argc, argv, str) getopt(argc, argv, str) #endif static void usage(progname) char *progname; { #define USAGE_BREAK "\n\t" #ifdef GETOPT_LONG #define USAGE_LONG_FORWARDABLE " | --forwardable | --noforwardable" #define USAGE_LONG_PROXIABLE " | --proxiable | --noproxiable" #define USAGE_LONG_ADDRESSES " | --addresses | --noaddresses" #define USAGE_BREAK_LONG USAGE_BREAK #else #define USAGE_LONG_FORWARDABLE "" #define USAGE_LONG_PROXIABLE "" #define USAGE_LONG_ADDRESSES "" #define USAGE_BREAK_LONG "" #endif fprintf(stderr, "%s : %s [-V] " "[-l lifetime] [-s start_time] " USAGE_BREAK "[-r renewable_life] " "[-f | -F" USAGE_LONG_FORWARDABLE "] " USAGE_BREAK_LONG "[-p | -P" USAGE_LONG_PROXIABLE "] " USAGE_BREAK_LONG "[-a | -A" USAGE_LONG_ADDRESSES "] " USAGE_BREAK "[-v] [-R] " "[-k [-t keytab_file]] " "[-c cachename] " USAGE_BREAK "[-S service_name]" "[-X [=]] [principal]" "\n\n", gettext("Usage"), progname); #define KRB_AVAIL_STRING(x) ((x)?gettext("available"):gettext("not available")) #define OPTTYPE_KRB5 "5" #define OPTTYPE_KRB4 "4" #define OPTTYPE_EITHER "Either 4 or 5" #ifdef HAVE_KRB524 #define OPTTYPE_BOTH "5, or both 5 and 4" #else #define OPTTYPE_BOTH "5" #endif #ifdef KRB5_KRB4_COMPAT #define USAGE_OPT_FMT "%s%-50s%s\n" #define ULINE(indent, col1, col2) \ fprintf(stderr, USAGE_OPT_FMT, indent, col1, col2) #else #define USAGE_OPT_FMT "%s%s\n" #define ULINE(indent, col1, col2) \ fprintf(stderr, USAGE_OPT_FMT, indent, col1) #endif ULINE(" ", "options:", "valid with Kerberos:"); fprintf(stderr, "\t-5 Kerberos 5 (%s)\n", KRB_AVAIL_STRING(got_k5)); fprintf(stderr, "\t-4 Kerberos 4 (%s)\n", KRB_AVAIL_STRING(got_k4)); fprintf(stderr, "\t (Default behavior is to try %s%s%s%s)\n", default_k5?"Kerberos 5":"", (default_k5 && default_k4)?gettext(" and "):"", default_k4?"Kerberos 4":"", (!default_k5 && !default_k4)?gettext("neither"):""); ULINE("\t", gettext("-V verbose"), OPTTYPE_EITHER); ULINE("\t", gettext("-l lifetime"), OPTTYPE_EITHER); ULINE("\t", gettext("-s start time"), OPTTYPE_KRB5); ULINE("\t", gettext("-r renewable lifetime"), OPTTYPE_KRB5); ULINE("\t", gettext("-f forwardable"), OPTTYPE_KRB5); ULINE("\t", gettext("-F not forwardable"), OPTTYPE_KRB5); ULINE("\t", gettext("-p proxiable"), OPTTYPE_KRB5); ULINE("\t", gettext("-P not proxiable"), OPTTYPE_KRB5); ULINE("\t", gettext("-A do not include addresses"), OPTTYPE_KRB5); ULINE("\t", gettext("-a include addresses"), OPTTYPE_KRB5); ULINE("\t", gettext("-v validate"), OPTTYPE_KRB5); ULINE("\t", gettext("-R renew"), OPTTYPE_BOTH); ULINE("\t", gettext("-k use keytab"), OPTTYPE_BOTH); ULINE("\t", gettext("-t filename of keytab to use"), OPTTYPE_BOTH); ULINE("\t", gettext("-c Kerberos 5 cache name"), OPTTYPE_KRB5); /* This options is not yet available: */ /* ULINE("\t", "-C Kerberos 4 cache name", OPTTYPE_KRB4); */ ULINE("\t", gettext("-S service"), OPTTYPE_BOTH); ULINE("\t", gettext("-X [=]"), OPTTYPE_KRB5); 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; if (opts->num_pa_opts == 0) { opts->pa_opts = malloc(sizeof(krb5_gic_opt_pa_data)); if (opts->pa_opts == NULL) return ENOMEM; } else { size_t newsize = (opts->num_pa_opts + 1) * sizeof(krb5_gic_opt_pa_data); 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(argc, argv, opts, progname) int argc; char **argv; struct k_opts* opts; char *progname; { krb5_error_code code; int errflg = 0; int use_k4 = 0; int use_k5 = 0; int i; while ((i = GETOPT(argc, argv, "r:fpFP54aAVl:s:c:kt:RS:vX:")) != -1) { switch (i) { case 'V': opts->verbose = 1; break; case 'l': /* Lifetime */ code = krb5_string_to_deltat(optarg, &opts->lifetime); if (code != 0 || opts->lifetime == 0) { fprintf(stderr, gettext("Bad lifetime value %s\n"), optarg); errflg++; } break; case 'r': /* Renewable Time */ code = krb5_string_to_deltat(optarg, &opts->rlife); if (code != 0 || opts->rlife == 0) { fprintf(stderr, gettext("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 'a': /* Note: This is supported only with GETOPT_LONG */ opts->addresses = 1; break; case 'A': opts->no_addresses = 1; break; case 's': code = krb5_string_to_deltat(optarg, &opts->starttime); if (code != 0 || opts->starttime == 0) { krb5_timestamp abs_starttime; code = krb5_string_to_timestamp(optarg, &abs_starttime); if (code != 0 || abs_starttime == 0) { fprintf(stderr, gettext("Bad start time value %s\n"), optarg); errflg++; } else { opts->starttime = abs_starttime - time(0); } } break; case 'S': opts->service_name = optarg; break; case 'k': opts->action = INIT_KT; break; case 't': if (opts->keytab_name) { fprintf(stderr, gettext("Only one -t option allowed.\n")); errflg++; } else { opts->keytab_name = optarg; } break; case 'R': opts->action = RENEW; break; case 'v': opts->action = VALIDATE; break; case 'c': if (opts->k5_cache_name) { fprintf(stderr, gettext("Only one -c option allowed\n")); errflg++; } else { opts->k5_cache_name = optarg; } break; case 'X': code = add_preauth_opt(opts, optarg); if (code) { com_err(progname, code, "while adding preauth option"); errflg++; } break; #if 0 /* A little more work is needed before we can enable this option. */ case 'C': if (opts->k4_cache_name) { fprintf(stderr, "Only one -C option allowed\n"); errflg++; } else { opts->k4_cache_name = optarg; } break; #endif case '4': if (!got_k4) { #ifdef KRB5_KRB4_COMPAT fprintf(stderr, "Kerberos 4 support could not be loaded\n"); #else fprintf(stderr, gettext("This was not built with Kerberos 4 support\n")); #endif exit(3); } use_k4 = 1; break; case '5': if (!got_k5) { fprintf(stderr, gettext("Kerberos 5 support could not be loaded\n")); exit(3); } use_k5 = 1; break; default: errflg++; break; } } if (opts->forwardable && opts->not_forwardable) { fprintf(stderr, gettext("Only one of -f and -F allowed\n")); errflg++; } if (opts->proxiable && opts->not_proxiable) { fprintf(stderr, gettext("Only one of -p and -P allowed\n")); errflg++; } if (opts->addresses && opts->no_addresses) { fprintf(stderr, gettext("Only one of -a and -A allowed\n")); errflg++; } if (argc - optind > 1) { fprintf(stderr, gettext("Extra arguments (starting with \"%s\").\n"), argv[optind+1]); errflg++; } /* At this point, if errorless, we know we only have one option selection */ if (!use_k5 && !use_k4) { use_k5 = default_k5; use_k4 = default_k4; } /* Now, we encode the OPTTYPE stuff here... */ if (!use_k5 && (opts->starttime || opts->rlife || opts->forwardable || opts->proxiable || opts->addresses || opts->not_forwardable || opts->not_proxiable || opts->no_addresses || (opts->action == VALIDATE) || opts->k5_cache_name)) { fprintf(stderr, gettext("Specified option that requires Kerberos 5\n")); errflg++; } if (!use_k4 && opts->k4_cache_name) { fprintf(stderr, gettext("Specified option that require Kerberos 4\n")); errflg++; } if ( #ifdef HAVE_KRB524 !use_k5 #else use_k4 #endif && (opts->service_name || opts->keytab_name || (opts->action == INIT_KT) || (opts->action == RENEW)) ) { fprintf(stderr, gettext("Specified option that requires Kerberos 5\n")); errflg++; } if (errflg) { usage(progname); } got_k5 = got_k5 && use_k5; got_k4 = got_k4 && use_k4; opts->principal_name = (optind == argc-1) ? argv[optind] : 0; return opts->principal_name; } static int k5_begin(opts, k5, k4) struct k_opts* opts; struct k5_data* k5; struct k4_data* k4; { char* progname = progname_v5; krb5_error_code code = 0; if (!got_k5) return 0; code = krb5_init_context(&k5->ctx); if (code) { com_err(progname, code, gettext("while initializing Kerberos 5 library")); return 0; } errctx = k5->ctx; if (opts->k5_cache_name) { code = krb5_cc_resolve(k5->ctx, opts->k5_cache_name, &k5->cc); if (code != 0) { com_err(progname, code, gettext("resolving ccache %s"), opts->k5_cache_name); return 0; } } else { if ((code = krb5_cc_default(k5->ctx, &k5->cc))) { com_err(progname, code, gettext("while getting default ccache")); return 0; } } if (opts->principal_name) { /* Use specified name */ if ((code = krb5_parse_name(k5->ctx, opts->principal_name, &k5->me))) { com_err(progname, code, gettext("when parsing name %s"), opts->principal_name); return 0; } } else { /* No principal name specified */ if (opts->action == INIT_KT) { /* Use the default host/service name */ code = krb5_sname_to_principal(k5->ctx, NULL, NULL, KRB5_NT_SRV_HST, &k5->me); if (code) { com_err(progname, code, gettext( "when creating default server principal name")); return 0; } } else { /* Get default principal from cache if one exists */ code = krb5_cc_get_principal(k5->ctx, k5->cc, &k5->me); if (code) { char *name = get_name_from_os(); if (!name) { fprintf(stderr, gettext("Unable to identify user\n")); return 0; } /* use strcmp to ensure only "root" is matched */ if (strcmp(name, ROOT_UNAME) == 0) { if (code = krb5_sname_to_principal(k5->ctx, NULL, ROOT_UNAME, KRB5_NT_SRV_HST, &k5->me)) { com_err(progname, code, gettext( "when creating default server principal name")); return 0; } } else if ((code = krb5_parse_name(k5->ctx, name, &k5->me))) { com_err(progname, code, gettext("when parsing name %s"), name); return 0; } } } } code = krb5_unparse_name(k5->ctx, k5->me, &k5->name); if (code) { com_err(progname, code, gettext("when unparsing name")); return 0; } opts->principal_name = k5->name; #ifdef KRB5_KRB4_COMPAT if (got_k4) { /* Translate to a Kerberos 4 principal */ code = krb5_524_conv_principal(k5->ctx, k5->me, k4->aname, k4->inst, k4->realm); if (code) { k4->aname[0] = 0; k4->inst[0] = 0; k4->realm[0] = 0; } } #endif return 1; } static void k5_end(k5) struct k5_data* k5; { if (k5->name) krb5_free_unparsed_name(k5->ctx, k5->name); if (k5->me) krb5_free_principal(k5->ctx, k5->me); if (k5->cc) krb5_cc_close(k5->ctx, k5->cc); if (k5->ctx) krb5_free_context(k5->ctx); errctx = NULL; memset(k5, 0, sizeof(*k5)); } static int k4_begin(opts, k4) struct k_opts* opts; struct k4_data* k4; { #ifdef KRB5_KRB4_COMPAT char* progname = progname_v4; int k_errno = 0; #endif if (!got_k4) return 0; #ifdef KRB5_KRB4_COMPAT if (k4->aname[0]) goto skip; if (opts->principal_name) { /* Use specified name */ k_errno = kname_parse(k4->aname, k4->inst, k4->realm, opts->principal_name); if (k_errno) { fprintf(stderr, "%s: %s\n", progname, krb_get_err_text(k_errno)); return 0; } } else { /* No principal name specified */ if (opts->action == INIT_KT) { /* Use the default host/service name */ /* XXX - need to add this functionality */ fprintf(stderr, "%s: Kerberos 4 srvtab support is not " "implemented\n", progname); return 0; } else { /* Get default principal from cache if one exists */ k_errno = krb_get_tf_fullname(tkt_string(), k4->aname, k4->inst, k4->realm); if (k_errno) { char *name = get_name_from_os(); if (!name) { fprintf(stderr, "Unable to identify user\n"); return 0; } k_errno = kname_parse(k4->aname, k4->inst, k4->realm, name); if (k_errno) { fprintf(stderr, "%s: %s\n", progname, krb_get_err_text(k_errno)); return 0; } } } } if (!k4->realm[0]) krb_get_lrealm(k4->realm, 1); if (k4->inst[0]) sprintf(k4->name, "%s.%s@%s", k4->aname, k4->inst, k4->realm); else sprintf(k4->name, "%s@%s", k4->aname, k4->realm); opts->principal_name = k4->name; skip: if (k4->aname[0] && !k_isname(k4->aname)) { fprintf(stderr, "%s: bad Kerberos 4 name format\n", progname); return 0; } if (k4->inst[0] && !k_isinst(k4->inst)) { fprintf(stderr, "%s: bad Kerberos 4 instance format\n", progname); return 0; } if (k4->realm[0] && !k_isrealm(k4->realm)) { fprintf(stderr, "%s: bad Kerberos 4 realm format\n", progname); return 0; } #endif /* KRB5_KRB4_COMPAT */ return 1; } static void k4_end(k4) struct k4_data* k4; { memset(k4, 0, sizeof(*k4)); } #ifdef KRB5_KRB4_COMPAT static char stash_password[1024]; static int got_password = 0; #endif /* KRB5_KRB4_COMPAT */ 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[] ) { int i; krb5_prompt_type *types; krb5_error_code rc = krb5_prompter_posix(ctx, data, name, banner, num_prompts, prompts); if (!rc && (types = krb5_get_prompt_types(ctx))) for (i = 0; i < num_prompts; i++) if ((types[i] == KRB5_PROMPT_TYPE_PASSWORD) || (types[i] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN)) { #ifdef KRB5_KRB4_COMPAT strncpy(stash_password, prompts[i].reply->data, sizeof(stash_password)); got_password = 1; #endif } return rc; } static int k5_kinit(opts, k5) struct k_opts* opts; struct k5_data* k5; { char* progname = progname_v5; int notix = 1; krb5_keytab keytab = 0; krb5_creds my_creds; krb5_error_code code = 0; krb5_get_init_creds_opt *options = NULL; int i; krb5_timestamp now; krb5_deltat lifetime = 0, rlife = 0, krb5_max_duration; if (!got_k5) return 0; code = krb5_get_init_creds_opt_alloc(k5->ctx, &options); if (code) goto cleanup; memset(&my_creds, 0, sizeof(my_creds)); /* * Solaris Kerberos: added support for max_life and max_renewable_life * which should be removed in the next minor release. See PSARC 2003/545 * for more info. * * Also, check krb5.conf for proxiable/forwardable/renewable/no_address * parameter values. */ /* If either tkt life or renew life weren't set earlier take common steps to * get the krb5.conf parameter values. */ if ((code = krb5_timeofday(k5->ctx, &now))) { com_err(progname, code, gettext("while getting time of day")); exit(1); } krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60; if (opts->lifetime == 0 || opts->rlife == 0) { krb_realm = krb5_princ_realm(k5->ctx, k5->me)->data; /* realm params take precedence */ profile_get_options_string(k5->ctx->profile, realmdef, config_times); profile_get_options_string(k5->ctx->profile, appdef, config_times); /* if the input opts doesn't have lifetime set and the krb5.conf * parameter has been set, use that. */ if (opts->lifetime == 0 && life_timeval != NULL) { code = krb5_string_to_deltat(life_timeval, &lifetime); if (code != 0 || lifetime == 0 || lifetime > krb5_max_duration) { fprintf(stderr, gettext("Bad max_life " "value in Kerberos config file %s\n"), life_timeval); exit(1); } opts->lifetime = lifetime; } if (opts->rlife == 0 && renew_timeval != NULL) { code = krb5_string_to_deltat(renew_timeval, &rlife); if (code != 0 || rlife == 0 || rlife > krb5_max_duration) { fprintf(stderr, gettext("Bad max_renewable_life " "value in Kerberos config file %s\n"), renew_timeval); exit(1); } opts->rlife = rlife; } } /* * If lifetime is not set on the cmdline or in the krb5.conf * file, default to max. */ if (opts->lifetime == 0) opts->lifetime = krb5_max_duration; profile_get_options_boolean(k5->ctx->profile, realmdef, config_option); profile_get_options_boolean(k5->ctx->profile, appdef, config_option); /* cmdline opts take precedence over krb5.conf file values */ if (!opts->not_proxiable && proxiable_flag) { krb5_get_init_creds_opt_set_proxiable(options, 1); } if (!opts->not_forwardable && forwardable_flag) { krb5_get_init_creds_opt_set_forwardable(options, 1); } if (renewable_flag) { /* * If this flag is set in krb5.conf, but rlife is 0, then * set it to the max (and let the KDC sort it out). */ opts->rlife = opts->rlife ? opts->rlife : krb5_max_duration; } if (no_address_flag) { /* cmdline opts will overwrite this below if needbe */ krb5_get_init_creds_opt_set_address_list(options, NULL); } /* From this point on, we can goto cleanup because my_creds is initialized. */ 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->addresses) { krb5_address **addresses = NULL; code = krb5_os_localaddr(k5->ctx, &addresses); if (code != 0) { com_err(progname, code, gettext("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->action == INIT_KT) && opts->keytab_name) { code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab); if (code != 0) { com_err(progname, code, gettext("resolving keytab %s"), opts->keytab_name); goto cleanup; } } for (i = 0; i < opts->num_pa_opts; i++) { code = krb5_get_init_creds_opt_set_pa(k5->ctx, options, opts->pa_opts[i].attr, opts->pa_opts[i].value); if (code != 0) { com_err(progname, code, "while setting '%s'='%s'", opts->pa_opts[i].attr, opts->pa_opts[i].value); goto cleanup; } } switch (opts->action) { case INIT_PW: code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me, 0, kinit_prompter, 0, opts->starttime, opts->service_name, options); break; case INIT_KT: code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me, keytab, opts->starttime, opts->service_name, options); break; case VALIDATE: code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->cc, opts->service_name); break; case RENEW: code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->cc, opts->service_name); break; } if (code) { char *doing = 0; switch (opts->action) { case INIT_PW: case INIT_KT: doing = gettext("getting initial credentials"); break; case VALIDATE: doing = gettext("validating credentials"); break; case RENEW: doing = gettext("renewing credentials"); break; } /* If got code == KRB5_AP_ERR_V4_REPLY && got_k4, we should let the user know that maybe he/she wants -4. */ if (code == KRB5KRB_AP_ERR_V4_REPLY && got_k4) com_err(progname, code, "while %s\n" "The KDC doesn't support v5. " "You may want the -4 option in the future", doing); else if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) fprintf(stderr, gettext("%s: Password incorrect while %s\n"), progname, doing); else com_err(progname, code, gettext("while %s"), doing); goto cleanup; } if (!opts->lifetime) { /* We need to figure out what lifetime to use for Kerberos 4. */ opts->lifetime = my_creds.times.endtime - my_creds.times.authtime; } code = krb5_cc_initialize(k5->ctx, k5->cc, k5->me); if (code) { com_err(progname, code, gettext("when initializing cache %s"), opts->k5_cache_name?opts->k5_cache_name:""); goto cleanup; } code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds); if (code) { com_err(progname, code, gettext("while storing credentials")); goto cleanup; } if (opts->action == RENEW) { _kwarnd_del_warning(progname, opts->principal_name); _kwarnd_add_warning(progname, opts->principal_name, my_creds.times.endtime); } else if ((opts->action == INIT_KT) || (opts->action == INIT_PW)) { _kwarnd_add_warning(progname, opts->principal_name, my_creds.times.endtime); } notix = 0; cleanup: 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) krb5_kt_close(k5->ctx, keytab); return notix?0:1; } static int k4_kinit(opts, k4, ctx) struct k_opts* opts; struct k4_data* k4; krb5_context ctx; { #ifdef KRB5_KRB4_COMPAT char* progname = progname_v4; int k_errno = 0; #endif if (!got_k4) return 0; if (opts->starttime) return 0; #ifdef KRB5_KRB4_COMPAT if (!k4->lifetime) k4->lifetime = opts->lifetime; if (!k4->lifetime) k4->lifetime = KRB4_BACKUP_DEFAULT_LIFE_SECS; k4->lifetime = krb_time_to_life(0, k4->lifetime); switch (opts->action) { case INIT_PW: if (!got_password) { unsigned int pwsize = sizeof(stash_password); krb5_error_code code; char prompt[1024]; sprintf(prompt, gettext("Password for %s: "), opts->principal_name); stash_password[0] = 0; /* Note: krb5_read_password does not actually look at the context, so we're ok even if we don't have a context. If we cannot dynamically load krb5, we can substitute any decent read password function instead of the krb5 one. */ code = krb5_read_password(ctx, prompt, 0, stash_password, &pwsize); if (code || pwsize == 0) { fprintf(stderr, gettext("Error while reading password for '%s'\n"), opts->principal_name); memset(stash_password, 0, sizeof(stash_password)); return 0; } got_password = 1; } k_errno = krb_get_pw_in_tkt(k4->aname, k4->inst, k4->realm, "krbtgt", k4->realm, k4->lifetime, stash_password); if (k_errno) { fprintf(stderr, "%s: %s\n", progname, krb_get_err_text(k_errno)); if (authed_k5) fprintf(stderr, gettext("Maybe your KDC does not support v4. " "Try the -5 option next time.\n")); return 0; } return 1; #ifndef HAVE_KRB524 case INIT_KT: fprintf(stderr, gettext("%s: srvtabs are not supported\n"), progname); return 0; case RENEW: fprintf(stderr, gettext("%s: renewal of krb4 tickets is not supported\n"), progname); return 0; #else /* These cases are handled by the 524 code - this prevents the compiler warnings of not using all the enumerated types. */ case INIT_KT: case RENEW: case VALIDATE: return 0; #endif } #endif return 0; } static char* getvprogname(v, progname) char *v, *progname; { unsigned int len = strlen(progname) + 2 + strlen(v) + 2; char *ret = malloc(len); if (ret) sprintf(ret, "%s(v%s)", progname, v); else ret = progname; return ret; } #ifdef HAVE_KRB524 /* Convert krb5 tickets to krb4. */ static int try_convert524(k5) struct k5_data* k5; { char * progname = progname_v524; krb5_error_code code = 0; int icode = 0; krb5_principal kpcserver = 0; krb5_creds *v5creds = 0; krb5_creds increds; CREDENTIALS v4creds; if (!got_k4 || !got_k5) return 0; memset((char *) &increds, 0, sizeof(increds)); /* From this point on, we can goto cleanup because increds is initialized. */ if ((code = krb5_build_principal(k5->ctx, &kpcserver, krb5_princ_realm(k5->ctx, k5->me)->length, krb5_princ_realm(k5->ctx, k5->me)->data, "krbtgt", krb5_princ_realm(k5->ctx, k5->me)->data, NULL))) { com_err(progname, code, gettext( "while creating service principal name")); goto cleanup; } increds.client = k5->me; increds.server = kpcserver; /* Prevent duplicate free calls. */ kpcserver = 0; increds.times.endtime = 0; increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; if ((code = krb5_get_credentials(k5->ctx, 0, k5->cc, &increds, &v5creds))) { com_err(progname, code, gettext("getting V5 credentials")); goto cleanup; } if ((icode = krb524_convert_creds_kdc(k5->ctx, v5creds, &v4creds))) { com_err(progname, icode, gettext("converting to V4 credentials")); goto cleanup; } /* this is stolen from the v4 kinit */ /* initialize ticket cache */ if ((icode = in_tkt(v4creds.pname, v4creds.pinst) != KSUCCESS)) { com_err(progname, icode, gettext( "trying to create the V4 ticket file")); goto cleanup; } /* stash ticket, session key, etc. for future use */ if ((icode = krb_save_credentials(v4creds.service, v4creds.instance, v4creds.realm, v4creds.session, v4creds.lifetime, v4creds.kvno, &(v4creds.ticket_st), v4creds.issue_date))) { com_err(progname, icode, gettext( "trying to save the V4 ticket")); goto cleanup; } cleanup: memset(&v4creds, 0, sizeof(v4creds)); if (v5creds) krb5_free_creds(k5->ctx, v5creds); increds.client = 0; krb5_free_cred_contents(k5->ctx, &increds); if (kpcserver) krb5_free_principal(k5->ctx, kpcserver); return !(code || icode); } #endif /* HAVE_KRB524 */ int main(argc, argv) int argc; char **argv; { struct k_opts opts; struct k5_data k5; struct k4_data k4; char *progname; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); progname = GET_PROGNAME(argv[0]); progname_v5 = getvprogname("5", progname); #ifdef KRB5_KRB4_COMPAT progname_v4 = getvprogname("4", progname); progname_v524 = getvprogname("524", progname); #endif /* 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); /* This is where we would put in code to dynamically load Kerberos libraries. Currenlty, we just get them implicitly. */ got_k5 = 1; #ifdef KRB5_KRB4_COMPAT got_k4 = 1; #endif memset(&opts, 0, sizeof(opts)); opts.action = INIT_PW; memset(&k5, 0, sizeof(k5)); memset(&k4, 0, sizeof(k4)); set_com_err_hook (extended_com_err_fn); parse_options(argc, argv, &opts, progname); got_k5 = k5_begin(&opts, &k5, &k4); got_k4 = k4_begin(&opts, &k4); authed_k5 = k5_kinit(&opts, &k5); #ifdef HAVE_KRB524 if (authed_k5) authed_k4 = try_convert524(&k5); #endif if (!authed_k4) authed_k4 = k4_kinit(&opts, &k4, k5.ctx); #ifdef KRB5_KRB4_COMPAT memset(stash_password, 0, sizeof(stash_password)); #endif if (authed_k5 && opts.verbose) fprintf(stderr, gettext("Authenticated to Kerberos v5\n")); if (authed_k4 && opts.verbose) fprintf(stderr, gettext("Authenticated to Kerberos v4\n")); k5_end(&k5); k4_end(&k4); if ((got_k5 && !authed_k5) || (got_k4 && !authed_k4) || (!got_k5 && !got_k4)) exit(1); return 0; } static void _kwarnd_add_warning(char *progname, char *me, time_t endtime) { if (kwarn_add_warning(me, endtime) != 0) fprintf(stderr, gettext( "%s: no ktkt_warnd warning possible\n"), progname); return; } static void _kwarnd_del_warning(char *progname, char *me) { if (kwarn_del_warning(me) != 0) fprintf(stderr, gettext( "%s: unable to delete ktkt_warnd message for %s\n"), progname, me); return; }