When user_realm is used by PAM, do likewise in kinit.

When PAM is configured to use a user_realm that is different from the
default realm, do likewise in kinit with bare user names or the default
principal computed from the login name.

Similarly, when using a keytab, if no realm is specified find the most
suitable match in the keytab file.
This commit is contained in:
Viktor Dukhovni
2013-05-16 00:07:56 +00:00
committed by Viktor Dukhovni
parent 4fcad71a3a
commit 3f3bcc2731

View File

@@ -63,6 +63,7 @@ static int switch_cache_flags = 1;
struct getarg_strings etype_str;
int use_keytab = 0;
char *keytab_str = NULL;
static krb5_keytab kt = NULL;
int do_afslog = -1;
int fcache_version;
char *password_file = NULL;
@@ -361,7 +362,6 @@ get_new_tickets(krb5_context context,
const char *renewstr = NULL;
krb5_enctype *enctype = NULL;
krb5_ccache tempccache;
krb5_keytab kt = NULL;
krb5_init_creds_context ctx;
#ifndef NO_NTLM
struct ntlm_buf ntlmkey;
@@ -524,18 +524,9 @@ get_new_tickets(krb5_context context,
}
if (use_keytab || keytab_str) {
if (keytab_str)
ret = krb5_kt_resolve(context, keytab_str, &kt);
else
ret = krb5_kt_default(context, &kt);
if (ret)
krb5_err(context, 1, ret, "resolving keytab");
ret = krb5_init_creds_set_keytab(context, ctx, kt);
if (ret)
krb5_err(context, 1, ret, "krb5_init_creds_set_keytab");
} else if (pk_user_id || ent_user_id || anonymous_flag) {
} else if (!interactive) {
@@ -666,8 +657,6 @@ get_new_tickets(krb5_context context,
krb5_get_init_creds_opt_free(context, opt);
if (kt)
krb5_kt_close(context, kt);
if (enctype)
free(enctype);
@@ -773,16 +762,204 @@ renew_func(void *ptr)
return expire / 2 + 1;
}
static void
parse_name_realm(krb5_context context,
const char *name,
int flags,
const char *realm,
krb5_principal *princ)
{
krb5_error_code ret;
ret = krb5_parse_name_flags_realm(context, name, flags, realm, princ);
if (ret)
krb5_err(context, 1, ret, "krb5_parse_name_flags_realm");
}
static char *
get_default_realm(krb5_context context)
{
char *realm;
krb5_error_code ret;
if ((ret = krb5_get_default_realm(context, &realm)) != 0)
krb5_err(context, 1, ret, "krb5_get_default_realm");
return realm;
}
static void
get_default_principal(krb5_context context, krb5_principal *princ)
{
krb5_error_code ret;
if ((ret = krb5_get_default_principal(context, princ)) != 0)
krb5_err(context, 1, ret, "krb5_get_default_principal");
}
static void
set_princ_realm(krb5_context context,
krb5_principal principal,
const char *realm)
{
krb5_error_code ret;
if ((ret = krb5_principal_set_realm(context, principal, realm)) != 0)
krb5_err(context, 1, ret, "krb5_principal_set_realm");
}
static char *
get_user_realm(krb5_context context)
{
krb5_error_code ret;
char *user_realm = NULL;
/*
* If memory allocation fails, we don't try to use the wrong realm,
* that will trigger misleading error messages complicate support.
*/
krb5_appdefault_string(context, "kinit", NULL, "user_realm", "",
&user_realm);
if (user_realm == NULL) {
ret = krb5_enomem(context);
krb5_err(context, 1, ret, "krb5_appdefault_string");
}
if (*user_realm == 0) {
free(user_realm);
user_realm = NULL;
}
return user_realm;
}
static void
get_princ(krb5_context context, krb5_principal *principal, const char *name)
{
krb5_error_code ret;
krb5_principal tmp;
int parseflags = 0;
char *user_realm;
if (name == NULL) {
krb5_ccache ccache;
/* If credential cache provides a client principal, use that. */
if (krb5_cc_default(context, &ccache) == 0) {
ret = krb5_cc_get_principal(context, ccache, principal);
krb5_cc_close(context, ccache);
if (ret == 0)
return;
}
}
user_realm = get_user_realm(context);
if (name) {
if (canonicalize_flag || enterprise_flag)
parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
parse_name_realm(context, name, parseflags, user_realm, &tmp);
if (user_realm && krb5_principal_get_num_comp(context, tmp) > 1) {
/* Principal is instance qualified, reparse with default realm. */
krb5_free_principal(context, tmp);
parse_name_realm(context, name, parseflags, NULL, principal);
} else {
*principal = tmp;
}
} else {
get_default_principal(context, principal);
if (user_realm)
set_princ_realm(context, *principal, user_realm);
}
if (user_realm)
free(user_realm);
}
static void
get_princ_kt(krb5_context context,
krb5_principal *principal,
char *name)
{
krb5_error_code ret;
krb5_principal tmp;
krb5_ccache ccache;
krb5_kt_cursor cursor;
krb5_keytab_entry entry;
int parseflags = 0;
char *def_realm;
if (name == NULL) {
/*
* If the credential cache exists and specifies a client principal,
* use that.
*/
if (krb5_cc_default(context, &ccache) == 0) {
ret = krb5_cc_get_principal(context, ccache, principal);
krb5_cc_close(context, ccache);
if (ret == 0)
return;
}
}
if (name) {
/* If the principal specifies an explicit realm, just use that. */
parseflags |= KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
parse_name_realm(context, name, parseflags, NULL, &tmp);
if (krb5_principal_get_realm(context, tmp) != NULL) {
*principal = tmp;
return;
}
} else {
/* Otherwise, search keytab for bare name of the default principal. */
get_default_principal(context, &tmp);
set_princ_realm(context, tmp, NULL);
}
def_realm = get_default_realm(context);
ret = krb5_kt_start_seq_get(context, kt, &cursor);
if (ret)
krb5_err(context, 1, ret, "krb5_kt_start_seq_get");
while (ret == 0 &&
krb5_kt_next_entry(context, kt, &entry, &cursor) == 0) {
const char *realm;
if (!krb5_principal_compare_any_realm(context, tmp, entry.principal))
continue;
if (*principal &&
krb5_principal_compare(context, *principal, entry.principal))
continue;
/* The default realm takes precedence */
realm = krb5_principal_get_realm(context, entry.principal);
if (*principal && strcmp(def_realm, realm) == 0) {
krb5_free_principal(context, *principal);
ret = krb5_copy_principal(context, entry.principal, principal);
break;
}
if (!*principal)
ret = krb5_copy_principal(context, entry.principal, principal);
}
if (ret != 0 || (ret = krb5_kt_end_seq_get(context, kt, &cursor)) != 0)
krb5_err(context, 1, ret, "get_princ_kt");
if (!*principal)
parse_name_realm(context, name, parseflags, NULL, principal);
krb5_free_principal(context, tmp);
free(def_realm);
}
int
main(int argc, char **argv)
{
krb5_error_code ret;
krb5_context context;
krb5_ccache ccache;
krb5_principal principal;
krb5_principal principal = NULL;
int optidx = 0;
krb5_deltat ticket_life = 0;
int parseflags = 0;
setprogname(argv[0]);
@@ -810,8 +987,18 @@ main(int argc, char **argv)
argc -= optidx;
argv += optidx;
if (canonicalize_flag || enterprise_flag)
parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
/*
* Open the keytab now, we use the keytab to determine the principal's
* realm when the requested principal has no realm.
*/
if (use_keytab || keytab_str) {
if (keytab_str)
ret = krb5_kt_resolve(context, keytab_str, &kt);
else
ret = krb5_kt_default(context, &kt);
if (ret)
krb5_err(context, 1, ret, "resolving keytab");
}
if (pk_enterprise_flag) {
ret = krb5_pk_enterprise_cert(context, pk_user_id,
@@ -831,17 +1018,10 @@ main(int argc, char **argv)
krb5_err(context, 1, ret, "krb5_make_principal");
krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN);
} else if (use_keytab || keytab_str) {
get_princ_kt(context, &principal, argv[0]);
} else {
if (argv[0]) {
ret = krb5_parse_name_flags(context, argv[0], parseflags,
&principal);
if (ret)
krb5_err(context, 1, ret, "krb5_parse_name");
} else {
ret = krb5_get_default_principal(context, &principal);
if (ret)
krb5_err(context, 1, ret, "krb5_get_default_principal");
}
get_princ(context, &principal, argv[0]);
}
if (fcache_version)
@@ -969,6 +1149,8 @@ main(int argc, char **argv)
ret = 0;
}
krb5_free_principal(context, principal);
if (kt)
krb5_kt_close(context, kt);
krb5_free_context(context);
return ret;
}