commit d7b94742daae85329067b126d0a4bc5b2ea7e4a0
Author: Greg Hudson <ghudson@mit.edu>
Date: Thu Sep 26 05:38:46 2013 -0400
Improve kinit output credential cache selection
If kinit chooses a client principal based on anything other than the
current default ccache's principal name, apply collection rules if
possible. When applying collection rules, if we don't find an
existing cache for the client principal, use the default cache if it
is uninitialized, instead of creating a new one.
ticket: 7689
diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c
index 5ceede8..d9033ec 100644
--- a/src/clients/kinit/kinit.c
+++ b/src/clients/kinit/kinit.c
@@ -466,9 +466,12 @@ k5_begin(opts, k5)
struct k5_data* k5;
{
krb5_error_code code = 0;
+ int success = 0;
int flags = opts->enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
- krb5_ccache defcache;
- const char *deftype;
+ krb5_ccache defcache = NULL;
+ krb5_principal defcache_princ = NULL, princ;
+ const char *deftype = NULL;
+ char *defrealm, *name;
code = krb5_init_context(&k5->ctx);
if (code) {
@@ -477,73 +480,153 @@ k5_begin(opts, k5)
}
errctx = k5->ctx;
- /* Parse specified principal name now if we got one. */
- if (opts->principal_name) {
- if ((code = krb5_parse_name_flags(k5->ctx, opts->principal_name,
- flags, &k5->me))) {
- com_err(progname, code, _("when parsing name %s"),
- opts->principal_name);
- return 0;
- }
- }
-
if (opts->k5_out_cache_name) {
code = krb5_cc_resolve(k5->ctx, opts->k5_out_cache_name, &k5->out_cc);
if (code != 0) {
com_err(progname, code, _("resolving ccache %s"),
opts->k5_out_cache_name);
- return 0;
+ goto cleanup;
}
if (opts->verbose) {
fprintf(stderr, _("Using specified cache: %s\n"),
opts->k5_out_cache_name);
}
} else {
- if ((code = krb5_cc_default(k5->ctx, &defcache))) {
+ /* Resolve the default ccache and get its type and default principal
+ * (if it is initialized). */
+ code = krb5_cc_default(k5->ctx, &defcache);
+ if (code) {
com_err(progname, code, _("while getting default ccache"));
- return 0;
+ goto cleanup;
}
deftype = krb5_cc_get_type(k5->ctx, defcache);
- if (k5->me != NULL && krb5_cc_support_switch(k5->ctx, deftype)) {
- /* Use an existing cache for the specified principal if we can. */
- code = krb5_cc_cache_match(k5->ctx, k5->me, &k5->out_cc);
- if (code != 0 && code != KRB5_CC_NOTFOUND) {
- com_err(progname, code, _("while searching for ccache for %s"),
- opts->principal_name);
- krb5_cc_close(k5->ctx, defcache);
- return 0;
+ 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. */
+ code = krb5_parse_name_flags(k5->ctx, opts->principal_name, flags,
+ &k5->me);
+ if (code) {
+ com_err(progname, code, _("when parsing name %s"),
+ opts->principal_name);
+ goto cleanup;
+ }
+ } else if (opts->anonymous) {
+ /* Use the anonymous principal for the local realm. */
+ code = krb5_get_default_realm(k5->ctx, &defrealm);
+ if (code) {
+ com_err(progname, code, _("while getting default realm"));
+ goto cleanup;
+ }
+ code = 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 (code) {
+ com_err(progname, code, _("while building principal"));
+ goto cleanup;
+ }
+ } else 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"));
+ goto cleanup;
+ }
+ if (k5->me->realm.data[0] == 0) {
+ code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
+ if (code == 0) {
+ com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
+ _("(principal %s)"), k5->name);
+ } else {
+ com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
+ _("for local services"));
}
- if (code == KRB5_CC_NOTFOUND) {
- code = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->out_cc);
- if (code) {
- com_err(progname, code, _("while generating new ccache"));
- krb5_cc_close(k5->ctx, defcache);
- return 0;
- }
- if (opts->verbose) {
- fprintf(stderr, _("Using new cache: %s\n"),
- krb5_cc_get_name(k5->ctx, k5->out_cc));
- }
- } else if (opts->verbose) {
+ 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;
+ }
+ code = krb5_parse_name_flags(k5->ctx, name, flags, &k5->me);
+ if (code) {
+ com_err(progname, code, _("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. */
+ code = krb5_cc_cache_match(k5->ctx, k5->me, &k5->out_cc);
+ if (code != 0 && code != KRB5_CC_NOTFOUND) {
+ com_err(progname, code, _("while searching for ccache for %s"),
+ opts->principal_name);
+ goto cleanup;
+ }
+ if (code == 0) {
+ if (opts->verbose) {
fprintf(stderr, _("Using existing cache: %s\n"),
krb5_cc_get_name(k5->ctx, k5->out_cc));
}
- krb5_cc_close(k5->ctx, defcache);
k5->switch_to_cache = 1;
- } else {
- k5->out_cc = defcache;
+ } else if (defcache_princ != NULL) {
+ /* Create a new cache to avoid overwriting the initialized default
+ * cache. */
+ code = krb5_cc_new_unique(k5->ctx, deftype, NULL, &k5->out_cc);
+ if (code) {
+ com_err(progname, code, _("while generating new ccache"));
+ goto cleanup;
+ }
if (opts->verbose) {
- fprintf(stderr, _("Using default cache: %s\n"),
+ 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) {
code = krb5_cc_resolve(k5->ctx, opts->k5_in_cache_name, &k5->in_cc);
if (code != 0) {
com_err(progname, code, _("resolving ccache %s"),
opts->k5_in_cache_name);
- return 0;
+ goto cleanup;
}
if (opts->verbose) {
fprintf(stderr, _("Using specified input cache: %s\n"),
@@ -551,80 +634,24 @@ k5_begin(opts, k5)
}
}
- if (!k5->me) {
- /* No principal name specified */
- if (opts->anonymous) {
- char *defrealm;
- code = krb5_get_default_realm(k5->ctx, &defrealm);
- if (code) {
- com_err(progname, code, _("while getting default realm"));
- return 0;
- }
- code = 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 (code) {
- com_err(progname, code, _("while building principal"));
- return 0;
- }
- } else {
- 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;
- }
- if (k5->me->realm.data[0] == 0) {
- code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
- if (code == 0) {
- com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
- _("(principal %s)"), k5->name);
- } else {
- com_err(progname, KRB5_ERR_HOST_REALM_UNKNOWN,
- _("for local services"));
- }
- return 0;
- }
- } else {
- /* Get default principal from cache if one exists */
- code = krb5_cc_get_principal(k5->ctx, k5->out_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_flags(k5->ctx, name,
- flags, &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;
+ goto cleanup;
}
if (opts->verbose)
fprintf(stderr, _("Using principal: %s\n"), k5->name);
opts->principal_name = k5->name;
- return 1;
+ success = 1;
+
+cleanup:
+ if (defcache != NULL)
+ krb5_cc_close(k5->ctx, defcache);
+ krb5_free_principal(k5->ctx, defcache_princ);
+ return success;
}
static void