aboutsummaryrefslogtreecommitdiff
path: root/src/mac/kinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mac/kinit.c')
-rw-r--r--src/mac/kinit.c1137
1 files changed, 1137 insertions, 0 deletions
diff --git a/src/mac/kinit.c b/src/mac/kinit.c
new file mode 100644
index 0000000..aa8785a
--- /dev/null
+++ b/src/mac/kinit.c
@@ -0,0 +1,1137 @@
+/*
+ * 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.
+ */
+
+#if TARGET_HEADER_FRAMEWORK
+ #include <Kerberos/Kerberos.h>
+#else
+ #include <krb5.h>
+ #ifdef KRB5_KRB4_COMPAT
+ #include <kerberosIV/krb.h>
+ #endif
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+#ifdef GETOPT_LONG
+#include <getopt.h>
+#else
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#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 */
+
+#ifdef HAVE_KRB524
+#include "krb524.h"
+#endif
+
+#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 <pwd.h>
+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
+
+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 10*60*60 /* 10 hours */
+
+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;
+};
+
+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
+};
+
+#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, "Usage: %s [-5] [-4] [-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" USAGE_LONG_ADDRESSES "] "
+ USAGE_BREAK
+ "[-v] [-R] "
+ "[-k [-t keytab_file]] "
+ USAGE_BREAK
+ "[-c cachename] "
+ "[-S service_name] [principal]"
+ "\n\n",
+ progname);
+
+#define KRB_AVAIL_STRING(x) ((x)?"available":"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"
+#else
+#define USAGE_OPT_FMT "%s%s\n"
+#endif
+
+#define ULINE(indent, col1, col2) \
+fprintf(stderr, USAGE_OPT_FMT, indent, col1, col2)
+
+ 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)?" and ":"",
+ default_k4?"Kerberos 4":"",
+ (!default_k5 && !default_k4)?"neither":"");
+ ULINE("\t", "-V verbose", OPTTYPE_EITHER);
+ ULINE("\t", "-l lifetime", OPTTYPE_EITHER);
+ ULINE("\t", "-s start time", OPTTYPE_KRB5);
+ ULINE("\t", "-r renewable lifetime", OPTTYPE_KRB5);
+ ULINE("\t", "-f forwardable", OPTTYPE_KRB5);
+ ULINE("\t", "-F not forwardable", OPTTYPE_KRB5);
+ ULINE("\t", "-p proxiable", OPTTYPE_KRB5);
+ ULINE("\t", "-P not proxiable", OPTTYPE_KRB5);
+ ULINE("\t", "-A do not include addresses", OPTTYPE_KRB5);
+ ULINE("\t", "-v validate", OPTTYPE_KRB5);
+ ULINE("\t", "-R renew", OPTTYPE_BOTH);
+ ULINE("\t", "-k use keytab", OPTTYPE_BOTH);
+ ULINE("\t", "-t filename of keytab to use", OPTTYPE_BOTH);
+ ULINE("\t", "-c Kerberos 5 cache name", OPTTYPE_KRB5);
+ /* This options is not yet available: */
+ /* ULINE("\t", "-C Kerberos 4 cache name", OPTTYPE_KRB4); */
+ ULINE("\t", "-S service", OPTTYPE_BOTH);
+ exit(2);
+}
+
+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:fpFP54AVl:s:c:kt:RS:v"))
+ != -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, "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, "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, "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, "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, "Only one -c option allowed\n");
+ errflg++;
+ } else {
+ opts->k5_cache_name = optarg;
+ }
+ 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, "This was not built with Kerberos 4 support\n");
+#endif
+ exit(3);
+ }
+ use_k4 = 1;
+ break;
+ case '5':
+ if (!got_k5)
+ {
+ fprintf(stderr, "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, "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->addresses && opts->no_addresses)
+ {
+ fprintf(stderr, "Only one of -a and -A allowed\n");
+ errflg++;
+ }
+
+ if (argc - optind > 1) {
+ fprintf(stderr, "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, "Specified option that requires Kerberos 5\n");
+ errflg++;
+ }
+ if (!use_k4 &&
+ opts->k4_cache_name)
+ {
+ fprintf(stderr, "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, "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, "while initializing Kerberos 5 library");
+ return 0;
+ }
+ if (opts->k5_cache_name)
+ {
+ code = krb5_cc_resolve(k5->ctx, opts->k5_cache_name, &k5->cc);
+ if (code != 0) {
+ com_err(progname, code, "resolving ccache %s",
+ opts->k5_cache_name);
+ return 0;
+ }
+ }
+ else
+ {
+ if ((code = krb5_cc_default(k5->ctx, &k5->cc))) {
+ com_err(progname, code, "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, "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,
+ "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, "Unable to identify user\n");
+ return 0;
+ }
+ if ((code = krb5_parse_name(k5->ctx, name,
+ &k5->me)))
+ {
+ com_err(progname, code, "when parsing name %s",
+ name);
+ return 0;
+ }
+ }
+ }
+ }
+
+ code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
+ if (code) {
+ com_err(progname, code, "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);
+ 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;
+ }
+ if (k_errno = kname_parse(k4->aname, k4->inst, k4->realm,
+ name))
+ {
+ 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;
+
+ if (!got_k5)
+ return 0;
+
+ krb5_get_init_creds_opt_init(&options);
+ memset(&my_creds, 0, sizeof(my_creds));
+
+ /*
+ 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, "getting local addresses");
+ goto cleanup;
+ }
+ krb5_get_init_creds_opt_set_address_list(&options, addresses);
+ krb5_free_addresses(k5->ctx, 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, "resolving keytab %s",
+ opts->keytab_name);
+ 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 = "getting initial credentials";
+ break;
+ case VALIDATE:
+ doing = "validating credentials";
+ break;
+ case RENEW:
+ doing = "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, "%s: Password incorrect while %s\n", progname,
+ doing);
+ else
+ com_err(progname, code, "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, "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, "while storing credentials");
+ goto cleanup;
+ }
+
+ notix = 0;
+
+ cleanup:
+ if (my_creds.client == k5->me) {
+ my_creds.client = 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, "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, "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, "Maybe your KDC does not support v4. %s",
+ "Try the -5 option next time.\n");
+ return 0;
+ }
+ return 1;
+#ifndef HAVE_KRB524
+ case INIT_KT:
+ fprintf(stderr, "%s: srvtabs are not supported\n", progname);
+ return 0;
+ case RENEW:
+ fprintf(stderr, "%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.
+ */
+
+ /* or do this directly with krb524_convert_creds_kdc */
+ krb524_init_ets(k5->ctx);
+
+ 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,
+ "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,
+ "getting V5 credentials");
+ goto cleanup;
+ }
+ if ((icode = krb524_convert_creds_kdc(k5->ctx,
+ v5creds,
+ &v4creds))) {
+ com_err(progname, icode,
+ "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,
+ "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,
+ "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;
+
+
+ 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));
+
+ 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, "Authenticated to Kerberos v5\n");
+ if (authed_k4 && opts.verbose)
+ fprintf(stderr, "Authenticated to Kerberos v4\n");
+
+ k5_end(&k5);
+ k4_end(&k4);
+
+ if ((got_k5 && !authed_k5) || (got_k4 && !authed_k4))
+ exit(1);
+ return 0;
+}