Expand dns_canonicalize_hostname=fallback support

This commit is contained in:
Robbie Harwood 2020-08-07 19:03:02 -04:00
parent 2091f29399
commit c59e4a1c67
7 changed files with 2114 additions and 1 deletions

View File

@ -0,0 +1,327 @@
From 158ce9222351216507da39d7bb4233469603e647 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Tue, 21 Jul 2020 00:40:06 +0200
Subject: [PATCH] Cache S4U2Proxy requests by second ticket
krb5_get_credentials() does not know the client principal for an
S4U2Proxy request until the end, because it is in the encrypted part
of the evidence ticket. However, we can check the cache by second
ticket, since all S4U2Proxy requests in a cache will generally be made
with the same evidence ticket.
In the ccache types, allow mcreds->client and mcreds->server to be
NULL (as Heimdal does) to ignore them for the purpose of matching. In
krb5int_construct_matching_creds(), set mcreds->client to NULL for
S4U2Proxy requests. Add a cache check to
k5_get_proxy_cred_from_kdc(), and remove the cache check from
krb5_get_credentials_for_proxy() and the krb5 mech's
get_credentials().
In get_proxy_cred_from_kdc(), fix a bug where cross-realm S4U2Proxy
would cache the evidence ticket used in the final request, rather than
the original evidence ticket.
[ghudson@mit.edu: debugged cache check and cross-realm caching;
switched from new flag to null matching cred principals; wrote commit
message]
ticket: 8931 (new)
(cherry picked from commit 148b317e1eb5df28dad96679cb4b8a07c62d4786)
---
src/lib/gssapi/krb5/init_sec_context.c | 61 ++++++++++++--------------
src/lib/krb5/ccache/cc_retr.c | 13 +++---
src/lib/krb5/ccache/ccapi/stdcc_util.c | 30 ++++++-------
src/lib/krb5/ccache/ccfns.c | 3 +-
src/lib/krb5/krb/get_creds.c | 5 +++
src/lib/krb5/krb/s4u_creds.c | 58 ++++++++++++------------
src/tests/s4u2proxy.c | 3 ++
7 files changed, 88 insertions(+), 85 deletions(-)
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
index 3f77157c9..823656037 100644
--- a/src/lib/gssapi/krb5/init_sec_context.c
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -165,44 +165,37 @@ static krb5_error_code get_credentials(context, cred, server, now,
goto cleanup;
}
- /*
- * For IAKERB or constrained delegation, only check the cache in this step.
- * For IAKERB we will ask the server to make any necessary TGS requests;
- * for constrained delegation we will adjust in_creds and make an S4U2Proxy
- * request below if the cache lookup fails.
- */
- if (cred->impersonator != NULL || cred->iakerb_mech)
+ /* Try constrained delegation if we have proxy credentials. */
+ if (cred->impersonator != NULL) {
+ /* If we are trying to get a ticket to ourselves, we should use the
+ * the evidence ticket directly from cache. */
+ if (krb5_principal_compare(context, cred->impersonator,
+ server->princ)) {
+ flags |= KRB5_GC_CACHED;
+ } else {
+ memset(&mcreds, 0, sizeof(mcreds));
+ mcreds.magic = KV5M_CREDS;
+ mcreds.server = cred->impersonator;
+ mcreds.client = cred->name->princ;
+ code = krb5_cc_retrieve_cred(context, cred->ccache,
+ KRB5_TC_MATCH_AUTHDATA, &mcreds,
+ &evidence_creds);
+ if (code)
+ goto cleanup;
+
+ in_creds.client = cred->impersonator;
+ in_creds.second_ticket = evidence_creds.ticket;
+ flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
+ }
+ }
+
+ /* For IAKERB, only check the cache in this step. We will ask the server
+ * to make any necessary TGS requests. */
+ if (cred->iakerb_mech)
flags |= KRB5_GC_CACHED;
code = krb5_get_credentials(context, flags, cred->ccache,
&in_creds, &result_creds);
-
- /*
- * Try constrained delegation if we have proxy credentials, unless
- * we are trying to get a ticket to ourselves (in which case we could
- * just use the evidence ticket directly from cache).
- */
- if (code == KRB5_CC_NOTFOUND && cred->impersonator != NULL &&
- !cred->iakerb_mech &&
- !krb5_principal_compare(context, cred->impersonator, server->princ)) {
-
- memset(&mcreds, 0, sizeof(mcreds));
- mcreds.magic = KV5M_CREDS;
- mcreds.server = cred->impersonator;
- mcreds.client = cred->name->princ;
- code = krb5_cc_retrieve_cred(context, cred->ccache,
- KRB5_TC_MATCH_AUTHDATA, &mcreds,
- &evidence_creds);
- if (code)
- goto cleanup;
-
- in_creds.client = cred->impersonator;
- in_creds.second_ticket = evidence_creds.ticket;
- flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
- code = krb5_get_credentials(context, flags, cred->ccache,
- &in_creds, &result_creds);
- }
-
if (code)
goto cleanup;
diff --git a/src/lib/krb5/ccache/cc_retr.c b/src/lib/krb5/ccache/cc_retr.c
index 2c50c9cce..4328b7d6f 100644
--- a/src/lib/krb5/ccache/cc_retr.c
+++ b/src/lib/krb5/ccache/cc_retr.c
@@ -58,15 +58,14 @@ static krb5_boolean
princs_match(krb5_context context, krb5_flags whichfields,
const krb5_creds *mcreds, const krb5_creds *creds)
{
- krb5_principal_data princ;
-
- if (!krb5_principal_compare(context, mcreds->client, creds->client))
+ if (mcreds->client != NULL &&
+ !krb5_principal_compare(context, mcreds->client, creds->client))
return FALSE;
+ if (mcreds->server == NULL)
+ return TRUE;
if (whichfields & KRB5_TC_MATCH_SRV_NAMEONLY) {
- /* Ignore the server realm. */
- princ = *mcreds->server;
- princ.realm = creds->server->realm;
- return krb5_principal_compare(context, &princ, creds->server);
+ return krb5_principal_compare_any_realm(context, mcreds->server,
+ creds->server);
} else {
return krb5_principal_compare(context, mcreds->server, creds->server);
}
diff --git a/src/lib/krb5/ccache/ccapi/stdcc_util.c b/src/lib/krb5/ccache/ccapi/stdcc_util.c
index 1f2a3865c..58aa81165 100644
--- a/src/lib/krb5/ccache/ccapi/stdcc_util.c
+++ b/src/lib/krb5/ccache/ccapi/stdcc_util.c
@@ -945,8 +945,13 @@ standard_fields_match(context, mcreds, creds)
krb5_context context;
const krb5_creds *mcreds, *creds;
{
- return (krb5_principal_compare(context, mcreds->client,creds->client) &&
- krb5_principal_compare(context, mcreds->server,creds->server));
+ if (mcreds->client != NULL &&
+ !krb5_principal_compare(context, mcreds->client, creds->client))
+ return FALSE;
+ if (mcreds->server != NULL &&
+ !krb5_principal_compare(context, mcreds->server,creds->server))
+ return FALSE;
+ return TRUE;
}
/* only match the server name portion, not the server realm portion */
@@ -956,19 +961,14 @@ srvname_match(context, mcreds, creds)
krb5_context context;
const krb5_creds *mcreds, *creds;
{
- krb5_boolean retval;
- krb5_principal_data p1, p2;
-
- retval = krb5_principal_compare(context, mcreds->client,creds->client);
- if (retval != TRUE)
- return retval;
- /*
- * Hack to ignore the server realm for the purposes of the compare.
- */
- p1 = *mcreds->server;
- p2 = *creds->server;
- p1.realm = p2.realm;
- return krb5_principal_compare(context, &p1, &p2);
+ if (mcreds->client != NULL &&
+ !krb5_principal_compare(context, mcreds->client, creds->client))
+ return FALSE;
+ if (mcreds->server != NULL &&
+ !krb5_principal_compare_any_realm(context, mcreds->server,
+ creds->server))
+ return FALSE;
+ return TRUE;
}
diff --git a/src/lib/krb5/ccache/ccfns.c b/src/lib/krb5/ccache/ccfns.c
index 62a6983d8..59982b752 100644
--- a/src/lib/krb5/ccache/ccfns.c
+++ b/src/lib/krb5/ccache/ccfns.c
@@ -96,7 +96,8 @@ krb5_cc_retrieve_cred(krb5_context context, krb5_ccache cache,
TRACE_CC_RETRIEVE(context, cache, mcreds, ret);
if (ret != KRB5_CC_NOTFOUND)
return ret;
- if (!krb5_is_referral_realm(&mcreds->server->realm))
+ if (mcreds->client == NULL || mcreds->server == NULL ||
+ !krb5_is_referral_realm(&mcreds->server->realm))
return ret;
/*
diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
index dc0aef667..b3f01be9b 100644
--- a/src/lib/krb5/krb/get_creds.c
+++ b/src/lib/krb5/krb/get_creds.c
@@ -102,6 +102,11 @@ krb5int_construct_matching_creds(krb5_context context, krb5_flags options,
return KRB5_NO_2ND_TKT;
}
+ /* For S4U2Proxy requests we don't know the impersonated client in this
+ * API, but matching against the second ticket is good enough. */
+ if (options & KRB5_GC_CONSTRAINED_DELEGATION)
+ mcreds->client = NULL;
+
return 0;
}
diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
index 07c7a98be..d44f939f8 100644
--- a/src/lib/krb5/krb/s4u_creds.c
+++ b/src/lib/krb5/krb/s4u_creds.c
@@ -1122,6 +1122,13 @@ get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
code = KRB5KRB_AP_WRONG_PRINC;
goto cleanup;
}
+
+ /* Put the original evidence ticket in the output creds. */
+ krb5_free_data_contents(context, &tkt->second_ticket);
+ code = krb5int_copy_data_contents(context, &in_creds->second_ticket,
+ &tkt->second_ticket);
+ if (code)
+ goto cleanup;
}
/* Note the authdata we asked for in the output creds. */
@@ -1148,11 +1155,33 @@ k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
{
krb5_error_code code;
krb5_const_principal canonprinc;
- krb5_creds copy, *creds;
+ krb5_creds mcreds, copy, *creds, *ncreds;
+ krb5_flags fields;
struct canonprinc iter = { in_creds->server, .no_hostrealm = TRUE };
*out_creds = NULL;
+ code = krb5int_construct_matching_creds(context, options, in_creds,
+ &mcreds, &fields);
+ if (code != 0)
+ return code;
+
+ ncreds = calloc(1, sizeof(*ncreds));
+ if (ncreds == NULL)
+ return ENOMEM;
+ ncreds->magic = KV5M_CRED;
+
+ code = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, ncreds);
+ if (code) {
+ free(ncreds);
+ } else {
+ *out_creds = ncreds;
+ }
+
+ if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE) ||
+ options & KRB5_GC_CACHED)
+ return code;
+
copy = *in_creds;
while ((code = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
canonprinc != NULL) {
@@ -1198,9 +1227,6 @@ krb5_get_credentials_for_proxy(krb5_context context,
krb5_creds **out_creds)
{
krb5_error_code code;
- krb5_creds mcreds;
- krb5_creds *ncreds = NULL;
- krb5_flags fields;
krb5_data *evidence_tkt_data = NULL;
krb5_creds s4u_creds;
@@ -1222,30 +1248,6 @@ krb5_get_credentials_for_proxy(krb5_context context,
goto cleanup;
}
- code = krb5int_construct_matching_creds(context, options, in_creds,
- &mcreds, &fields);
- if (code != 0)
- goto cleanup;
-
- ncreds = calloc(1, sizeof(*ncreds));
- if (ncreds == NULL) {
- code = ENOMEM;
- goto cleanup;
- }
- ncreds->magic = KV5M_CRED;
-
- code = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, ncreds);
- if (code != 0) {
- free(ncreds);
- ncreds = in_creds;
- } else {
- *out_creds = ncreds;
- }
-
- if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE)
- || options & KRB5_GC_CACHED)
- goto cleanup;
-
code = encode_krb5_ticket(evidence_tkt, &evidence_tkt_data);
if (code != 0)
goto cleanup;
diff --git a/src/tests/s4u2proxy.c b/src/tests/s4u2proxy.c
index 4adf6aca5..3786bad2c 100644
--- a/src/tests/s4u2proxy.c
+++ b/src/tests/s4u2proxy.c
@@ -124,6 +124,9 @@ main(int argc, char **argv)
KRB5_GC_CANONICALIZE, defcc,
&mcred, ev_ticket, &new_cred));
+ assert(data_eq(new_cred->second_ticket, ev_cred.ticket));
+ assert(new_cred->second_ticket.length != 0);
+
/* Store the new cred in the default ccache. */
check(krb5_cc_store_cred(context, defcc, new_cred));

View File

@ -0,0 +1,195 @@
From 71070a0f0fa6424cfb37aef7c4ec43e99380a6aa Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 30 Jul 2020 12:14:27 -0400
Subject: [PATCH] Don't create hostbased principals in new KDBs
Unix-like platforms do not provide a simple method to find the
fully-qualified local hostname as the machine is expected to appear to
other hosts. Canonicalizing the gethostname() result with
getaddrinfo() usually works, but potentially uses DNS. Now that
dns_canonicalize_hostname=true is no longer the default, KDB creation
would generally create the wrong host-based principals.
kadmin/hostname is unnecessary because the client software can also
use kadmin/admin, and kiprop/hostname is one of several principals
that must be created for incremental propagation.
ticket: 8935 (new)
(cherry picked from commit ac2b693d0ec464e0bcda4953acd79f201169f396)
---
src/kadmin/dbutil/kadm5_create.c | 52 ++-----------------
.../kdb/ldap/ldap_util/kdb5_ldap_realm.c | 35 +------------
src/tests/dejagnu/krb-standalone/kadmin.exp | 7 +--
src/tests/t_iprop.py | 1 +
src/tests/t_kadmin_acl.py | 1 +
5 files changed, 12 insertions(+), 84 deletions(-)
diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c
index 4f254a387..42b45aa2d 100644
--- a/src/kadmin/dbutil/kadm5_create.c
+++ b/src/kadmin/dbutil/kadm5_create.c
@@ -139,60 +139,18 @@ int kadm5_create_magic_princs(kadm5_config_params *params,
static int add_admin_princs(void *handle, krb5_context context, char *realm)
{
krb5_error_code ret = 0;
- char *service_name = 0, *kiprop_name = 0, *canonhost = 0;
- char localname[MAXHOSTNAMELEN];
-
- if (gethostname(localname, MAXHOSTNAMELEN)) {
- ret = errno;
- perror("gethostname");
- goto clean_and_exit;
- }
- ret = krb5_expand_hostname(context, localname, &canonhost);
- if (ret) {
- com_err(progname, ret, _("while canonicalizing local hostname"));
- goto clean_and_exit;
- }
- if (asprintf(&service_name, "kadmin/%s", canonhost) < 0) {
- ret = ENOMEM;
- fprintf(stderr, _("Out of memory\n"));
- goto clean_and_exit;
- }
- if (asprintf(&kiprop_name, "kiprop/%s", canonhost) < 0) {
- ret = ENOMEM;
- fprintf(stderr, _("Out of memory\n"));
- goto clean_and_exit;
- }
-
- if ((ret = add_admin_princ(handle, context,
- service_name, realm,
- KRB5_KDB_DISALLOW_TGT_BASED |
- KRB5_KDB_LOCKDOWN_KEYS,
- ADMIN_LIFETIME)))
- goto clean_and_exit;
if ((ret = add_admin_princ(handle, context,
KADM5_ADMIN_SERVICE, realm,
KRB5_KDB_DISALLOW_TGT_BASED |
KRB5_KDB_LOCKDOWN_KEYS,
ADMIN_LIFETIME)))
- goto clean_and_exit;
+ return ret;
- if ((ret = add_admin_princ(handle, context,
- KADM5_CHANGEPW_SERVICE, realm,
- KRB5_KDB_DISALLOW_TGT_BASED |
- KRB5_KDB_PWCHANGE_SERVICE |
- KRB5_KDB_LOCKDOWN_KEYS,
- CHANGEPW_LIFETIME)))
- goto clean_and_exit;
-
- ret = add_admin_princ(handle, context, kiprop_name, realm, 0, 0);
-
-clean_and_exit:
- krb5_free_string(context, canonhost);
- free(service_name);
- free(kiprop_name);
-
- return ret;
+ return add_admin_princ(handle, context, KADM5_CHANGEPW_SERVICE, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED |
+ KRB5_KDB_PWCHANGE_SERVICE | KRB5_KDB_LOCKDOWN_KEYS,
+ CHANGEPW_LIFETIME);
}
/*
diff --git a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
index c21d19981..ae1afd4a9 100644
--- a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
+++ b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
@@ -307,29 +307,6 @@ create_fixed_special(krb5_context context, struct realm_info *rinfo,
}
-/* Create a special principal using one specified component and the
- * canonicalized local hostname. */
-static krb5_error_code
-create_hostbased_special(krb5_context context, struct realm_info *rinfo,
- krb5_keyblock *mkey, const char *comp1)
-{
- krb5_error_code ret;
- krb5_principal princ = NULL;
-
- ret = krb5_sname_to_principal(context, NULL, comp1, KRB5_NT_SRV_HST,
- &princ);
- if (ret)
- goto cleanup;
- ret = krb5_set_principal_realm(context, princ, global_params.realm);
- if (ret)
- goto cleanup;
- ret = kdb_ldap_create_principal(context, princ, TGT_KEY, rinfo, mkey);
-
-cleanup:
- krb5_free_principal(context, princ);
- return ret;
-}
-
/* Create all special principals for the realm. */
static krb5_error_code
create_special_princs(krb5_context context, krb5_principal master_princ,
@@ -360,20 +337,10 @@ create_special_princs(krb5_context context, krb5_principal master_princ,
if (ret)
return ret;
- /* Create kadmin/admin and kadmin/<hostname>. */
+ /* Create kadmin/admin. */
rblock.max_life = ADMIN_LIFETIME;
rblock.flags = KRB5_KDB_DISALLOW_TGT_BASED;
ret = create_fixed_special(context, &rblock, mkey, "kadmin", "admin");
- if (ret)
- return ret;
- ret = create_hostbased_special(context, &rblock, mkey, "kadmin");
- if (ret)
- return ret;
-
- /* Create kiprop/<hostname>. */
- rblock.max_life = global_params.max_life;
- rblock.flags = 0;
- ret = create_hostbased_special(context, &rblock, mkey, "kiprop");
if (ret)
return ret;
diff --git a/src/tests/dejagnu/krb-standalone/kadmin.exp b/src/tests/dejagnu/krb-standalone/kadmin.exp
index 36a345258..fa50a61fb 100644
--- a/src/tests/dejagnu/krb-standalone/kadmin.exp
+++ b/src/tests/dejagnu/krb-standalone/kadmin.exp
@@ -1098,10 +1098,11 @@ proc kadmin_test { } {
return
}
- # test fallback to kadmin/admin
- if {![kadmin_delete_locked_down kadmin/$hostname] \
+ # test fallback to kadmin/hostname
+ if {![kadmin_add_rnd kadmin/$hostname] \
+ || ![kadmin_delete_locked_down kadmin/admin] \
|| ![kadmin_list] \
- || ![kadmin_add_rnd kadmin/$hostname -allow_tgs_req] \
+ || ![kadmin_add_rnd kadmin/admin -allow_tgs_req] \
|| ![kadmin_list]} {
return
}
diff --git a/src/tests/t_iprop.py b/src/tests/t_iprop.py
index 371f3a22b..3bb0fd2e9 100755
--- a/src/tests/t_iprop.py
+++ b/src/tests/t_iprop.py
@@ -188,6 +188,7 @@ for realm in multidb_realms(kdc_conf=conf, create_user=False,
# Create the principal used to authenticate kpropd to kadmind.
kiprop_princ = 'kiprop/' + hostname
+ realm.addprinc(kiprop_princ)
realm.extract_keytab(kiprop_princ, realm.keytab)
# Create the initial replica databases.
diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
index 16faf0a9d..31a7fb871 100755
--- a/src/tests/t_kadmin_acl.py
+++ b/src/tests/t_kadmin_acl.py
@@ -331,6 +331,7 @@ realm.run([kadmin, '-c', realm.ccache, 'cpw', '-randkey', '-e', 'aes256-cts',
# Test authentication to kadmin/hostname.
mark('authentication to kadmin/hostname')
kadmin_hostname = 'kadmin/' + hostname
+realm.addprinc(kadmin_hostname)
realm.run([kadminl, 'delprinc', 'kadmin/admin'])
msgs = ('Getting initial credentials for user/admin@KRBTEST.COM',
'Setting initial creds service to kadmin/admin',

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
From 7986adf30dffdd16fec43f261a2fa1384e0b8b90 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sat, 13 Jun 2020 21:55:54 -0400
Subject: [PATCH] Prevent deletion of K/M
In libkadm5srv, do not allow deletion of the master key principal, as
it is very difficult to recover a KDB after doing so.
ticket: 8913
(cherry picked from commit 94b936a1bf0a8c67809597c5ea5400d8994d5dd8)
---
src/lib/kadm5/srv/svr_principal.c | 4 ++++
src/tests/t_kadmin_acl.py | 6 ++++++
2 files changed, 10 insertions(+)
diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
index 53ecbe1bc..c2412df31 100644
--- a/src/lib/kadm5/srv/svr_principal.c
+++ b/src/lib/kadm5/srv/svr_principal.c
@@ -537,6 +537,10 @@ kadm5_delete_principal(void *server_handle, krb5_principal principal)
if (principal == NULL)
return EINVAL;
+ /* Deleting K/M is mostly unrecoverable, so don't allow it. */
+ if (krb5_principal_compare(handle->context, principal, master_princ))
+ return KADM5_PROTECT_PRINCIPAL;
+
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return(ret);
ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles,
diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
index 86eb59729..8946e8cc4 100755
--- a/src/tests/t_kadmin_acl.py
+++ b/src/tests/t_kadmin_acl.py
@@ -328,4 +328,10 @@ realm.run([kadmin, '-c', realm.ccache, 'cpw', '-randkey', 'none'],
realm.run([kadmin, '-c', realm.ccache, 'cpw', '-randkey', '-e', 'aes256-cts',
'none'], expected_code=1, expected_msg=msg)
+# Test operations disallowed at the libkadm5 layer.
+realm.run([kadminl, 'delprinc', 'K/M'],
+ expected_code=1, expected_msg='Cannot change protected principal')
+realm.run([kadminl, 'cpw', '-pw', 'pw', 'kadmin/history'],
+ expected_code=1, expected_msg='Cannot change protected principal')
+
success('kadmin ACL enforcement')

View File

@ -0,0 +1,188 @@
From 0cffa5904341460353fa7f99b5aa514fc27a10eb Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 23 Jul 2020 01:52:43 -0400
Subject: [PATCH] Refactor cache checking in TGS client code
(cherry picked from commit 8f2f0a2e8f65c4b39883129967301e3a8986218b)
---
src/lib/krb5/krb/get_creds.c | 86 +++++++++++++++++++++---------------
src/lib/krb5/krb/int-proto.h | 6 +--
src/lib/krb5/krb/s4u_creds.c | 21 +--------
3 files changed, 55 insertions(+), 58 deletions(-)
diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
index b3f01be9b..32401bcb1 100644
--- a/src/lib/krb5/krb/get_creds.c
+++ b/src/lib/krb5/krb/get_creds.c
@@ -48,10 +48,10 @@
* and options. The fields of *mcreds will be aliased to the fields
* of in_creds, so the contents of *mcreds should not be freed.
*/
-krb5_error_code
-krb5int_construct_matching_creds(krb5_context context, krb5_flags options,
- krb5_creds *in_creds, krb5_creds *mcreds,
- krb5_flags *fields)
+static krb5_error_code
+construct_matching_creds(krb5_context context, krb5_flags options,
+ krb5_creds *in_creds, krb5_creds *mcreds,
+ krb5_flags *fields)
{
if (!in_creds || !in_creds->server || !in_creds->client)
return EINVAL;
@@ -110,6 +110,50 @@ krb5int_construct_matching_creds(krb5_context context, krb5_flags options,
return 0;
}
+/* Simple wrapper around krb5_cc_retrieve_cred which allocates the result
+ * container. */
+static krb5_error_code
+cache_get(krb5_context context, krb5_ccache ccache, krb5_flags flags,
+ krb5_creds *in_creds, krb5_creds **out_creds)
+{
+ krb5_error_code code;
+ krb5_creds *creds;
+
+ *out_creds = NULL;
+
+ creds = malloc(sizeof(*creds));
+ if (creds == NULL)
+ return ENOMEM;
+
+ code = krb5_cc_retrieve_cred(context, ccache, flags, in_creds, creds);
+ if (code != 0) {
+ free(creds);
+ return code;
+ }
+
+ *out_creds = creds;
+ return 0;
+}
+
+krb5_error_code
+k5_get_cached_cred(krb5_context context, krb5_flags options,
+ krb5_ccache ccache, krb5_creds *in_creds,
+ krb5_creds **creds_out)
+{
+ krb5_error_code code;
+ krb5_creds mcreds;
+ krb5_flags fields;
+
+ *creds_out = NULL;
+
+ code = construct_matching_creds(context, options, in_creds,
+ &mcreds, &fields);
+ if (code)
+ return code;
+
+ return cache_get(context, ccache, fields, &mcreds, creds_out);
+}
+
/*
* krb5_tkt_creds_step() is implemented using a tail call style. Every
* begin_*, step_*, or *_request function is responsible for returning an
@@ -235,31 +279,6 @@ cleanup:
return code;
}
-/* Simple wrapper around krb5_cc_retrieve_cred which allocates the result
- * container. */
-static krb5_error_code
-cache_get(krb5_context context, krb5_ccache ccache, krb5_flags flags,
- krb5_creds *in_creds, krb5_creds **out_creds)
-{
- krb5_error_code code;
- krb5_creds *creds;
-
- *out_creds = NULL;
-
- creds = malloc(sizeof(*creds));
- if (creds == NULL)
- return ENOMEM;
-
- code = krb5_cc_retrieve_cred(context, ccache, flags, in_creds, creds);
- if (code != 0) {
- free(creds);
- return code;
- }
-
- *out_creds = creds;
- return 0;
-}
-
/*
* Set up the request given by ctx->tgs_in_creds, using ctx->cur_tgt. KDC
* options for the requests are determined by ctx->cur_tgt->ticket_flags and
@@ -1023,18 +1042,13 @@ static krb5_error_code
check_cache(krb5_context context, krb5_tkt_creds_context ctx)
{
krb5_error_code code;
- krb5_creds mcreds;
- krb5_flags fields;
krb5_creds req_in_creds;
/* Check the cache for the originally requested server principal. */
req_in_creds = *ctx->in_creds;
req_in_creds.server = ctx->req_server;
- code = krb5int_construct_matching_creds(context, ctx->req_options,
- &req_in_creds, &mcreds, &fields);
- if (code)
- return code;
- code = cache_get(context, ctx->ccache, fields, &mcreds, &ctx->reply_creds);
+ code = k5_get_cached_cred(context, ctx->req_options, ctx->ccache,
+ &req_in_creds, &ctx->reply_creds);
if (code == 0) {
ctx->state = STATE_COMPLETE;
return 0;
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index fe61bebf5..5211044dc 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -79,9 +79,9 @@ clpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
krb5_plugin_vtable vtable);
krb5_error_code
-krb5int_construct_matching_creds(krb5_context context, krb5_flags options,
- krb5_creds *in_creds, krb5_creds *mcreds,
- krb5_flags *fields);
+k5_get_cached_cred(krb5_context context, krb5_flags options,
+ krb5_ccache ccache, krb5_creds *in_creds,
+ krb5_creds **creds_out);
#define IS_TGS_PRINC(p) ((p)->length == 2 && \
data_eq_string((p)->data[0], KRB5_TGS_NAME))
diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
index d44f939f8..eadb37cd0 100644
--- a/src/lib/krb5/krb/s4u_creds.c
+++ b/src/lib/krb5/krb/s4u_creds.c
@@ -1155,29 +1155,12 @@ k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
{
krb5_error_code code;
krb5_const_principal canonprinc;
- krb5_creds mcreds, copy, *creds, *ncreds;
- krb5_flags fields;
+ krb5_creds copy, *creds;
struct canonprinc iter = { in_creds->server, .no_hostrealm = TRUE };
*out_creds = NULL;
- code = krb5int_construct_matching_creds(context, options, in_creds,
- &mcreds, &fields);
- if (code != 0)
- return code;
-
- ncreds = calloc(1, sizeof(*ncreds));
- if (ncreds == NULL)
- return ENOMEM;
- ncreds->magic = KV5M_CRED;
-
- code = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, ncreds);
- if (code) {
- free(ncreds);
- } else {
- *out_creds = ncreds;
- }
-
+ code = k5_get_cached_cred(context, options, ccache, in_creds, out_creds);
if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE) ||
options & KRB5_GC_CACHED)
return code;

View File

@ -0,0 +1,159 @@
From fae915ea7f2734cfd9ef3d5952f25638e675bb7c Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Mon, 27 Jul 2020 01:19:01 -0400
Subject: [PATCH] Try kadmin/admin first in libkadm5clnt
The MIT krb5 kadmin protocol originally used kadmin/admin as the
service principal. Commits 493f0da5fbf92b0ac2f10e887706d1964d8a15e8
and 5cfaec38a8e8f1c4b76228ba0a252987af797ca4 changed it to use
kadmin/hostname preferentially, with kadmin/admin as a fallback, for
interoperability with the Solaris SEAM administrative protocol.
Change the preference order so that kadmin/admin is tried first, with
kadmin/hostname as a fallback.
ticket: 8934 (new)
(cherry picked from commit 1d282badfbd6098e3db9d50d22d565c2ec3c8c47)
---
doc/admin/admin_commands/kadmin_local.rst | 14 ++++++------
doc/admin/database.rst | 10 ++++-----
src/lib/kadm5/clnt/client_init.c | 26 +++++++++--------------
src/tests/t_kadmin_acl.py | 14 ++++++++++++
4 files changed, 36 insertions(+), 28 deletions(-)
diff --git a/doc/admin/admin_commands/kadmin_local.rst b/doc/admin/admin_commands/kadmin_local.rst
index fafa61365..33cf3a9cb 100644
--- a/doc/admin/admin_commands/kadmin_local.rst
+++ b/doc/admin/admin_commands/kadmin_local.rst
@@ -44,9 +44,9 @@ Kerberos principals, password policies, and service key tables
(keytabs).
The remote kadmin client uses Kerberos to authenticate to kadmind
-using the service principal ``kadmin/ADMINHOST`` (where *ADMINHOST* is
-the fully-qualified hostname of the admin server) or ``kadmin/admin``.
-If the credentials cache contains a ticket for one of these
+using the service principal ``kadmin/admin`` or ``kadmin/ADMINHOST``
+(where *ADMINHOST* is the fully-qualified hostname of the admin
+server). If the credentials cache contains a ticket for one of these
principals, and the **-c** credentials_cache option is specified, that
ticket is used to authenticate to kadmind. Otherwise, the **-p** and
**-k** options are used to specify the client Kerberos principal name
@@ -100,10 +100,10 @@ OPTIONS
fully anonymous operation.
**-c** *credentials_cache*
- Use *credentials_cache* as the credentials cache. The
- cache should contain a service ticket for the ``kadmin/ADMINHOST``
- (where *ADMINHOST* is the fully-qualified hostname of the admin
- server) or ``kadmin/admin`` service; it can be acquired with the
+ Use *credentials_cache* as the credentials cache. The cache
+ should contain a service ticket for the ``kadmin/admin`` or
+ ``kadmin/ADMINHOST`` (where *ADMINHOST* is the fully-qualified
+ hostname of the admin server) service; it can be acquired with the
:ref:`kinit(1)` program. If this option is not specified, kadmin
requests a new service ticket from the KDC, and stores it in its
own temporary ccache.
diff --git a/doc/admin/database.rst b/doc/admin/database.rst
index e62cef7a7..ca19a362a 100644
--- a/doc/admin/database.rst
+++ b/doc/admin/database.rst
@@ -26,8 +26,8 @@ local filesystem (or through LDAP). kadmin.local is necessary to set
up enough of the database to be able to use the remote version.
kadmin can authenticate to the admin server using the service
-principal ``kadmin/HOST`` (where *HOST* is the hostname of the admin
-server) or ``kadmin/admin``. If the credentials cache contains a
+principal ``kadmin/admin`` or ``kadmin/HOST`` (where *HOST* is the
+hostname of the admin server). If the credentials cache contains a
ticket for either service principal and the **-c** ccache option is
specified, that ticket is used to authenticate to KADM5. Otherwise,
the **-p** and **-k** options are used to specify the client Kerberos
@@ -811,9 +811,9 @@ Both master and replica sides must have a principal named
``kiprop/hostname`` (where *hostname* is the lowercase,
fully-qualified, canonical name for the host) registered in the
Kerberos database, and have keys for that principal stored in the
-default keytab file (|keytab|). In release 1.13, the
-``kiprop/hostname`` principal is created automatically for the master
-KDC, but it must still be created for replica KDCs.
+default keytab file (|keytab|). The ``kiprop/hostname`` principal may
+have been created automatically for the master KDC, but it must always
+be created for replica KDCs.
On the master KDC side, the ``kiprop/hostname`` principal must be
listed in the kadmind ACL file :ref:`kadm5.acl(5)`, and given the
diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c
index aa08918e2..8d43ab97a 100644
--- a/src/lib/kadm5/clnt/client_init.c
+++ b/src/lib/kadm5/clnt/client_init.c
@@ -372,22 +372,10 @@ get_init_creds(kadm5_server_handle_t handle, krb5_principal client,
{
kadm5_ret_t code;
krb5_ccache ccache = NULL;
- char svcname[BUFSIZ];
+ char *svcname, svcbuf[BUFSIZ];
*server_out = NULL;
- /* NULL svcname means use host-based. */
- if (svcname_in == NULL) {
- code = kadm5_get_admin_service_name(handle->context,
- handle->params.realm,
- svcname, sizeof(svcname));
- if (code)
- goto error;
- } else {
- strncpy(svcname, svcname_in, sizeof(svcname));
- svcname[sizeof(svcname)-1] = '\0';
- }
-
/*
* Acquire a service ticket for svcname@realm for client, using password
* pass (which could be NULL), and create a ccache to store them in. If
@@ -423,13 +411,19 @@ get_init_creds(kadm5_server_handle_t handle, krb5_principal client,
}
handle->lhandle->cache_name = handle->cache_name;
+ svcname = (svcname_in != NULL) ? svcname_in : KADM5_ADMIN_SERVICE;
code = gic_iter(handle, init_type, ccache, client, pass, svcname, realm,
server_out);
if ((code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
|| code == KRB5_CC_NOTFOUND) && svcname_in == NULL) {
- /* Retry with old host-independent service principal. */
- code = gic_iter(handle, init_type, ccache, client, pass,
- KADM5_ADMIN_SERVICE, realm, server_out);
+ /* Retry with host-based service principal. */
+ code = kadm5_get_admin_service_name(handle->context,
+ handle->params.realm,
+ svcbuf, sizeof(svcbuf));
+ if (code)
+ goto error;
+ code = gic_iter(handle, init_type, ccache, client, pass, svcbuf, realm,
+ server_out);
}
/* Improved error messages */
if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD;
diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
index 8946e8cc4..16faf0a9d 100755
--- a/src/tests/t_kadmin_acl.py
+++ b/src/tests/t_kadmin_acl.py
@@ -328,6 +328,20 @@ realm.run([kadmin, '-c', realm.ccache, 'cpw', '-randkey', 'none'],
realm.run([kadmin, '-c', realm.ccache, 'cpw', '-randkey', '-e', 'aes256-cts',
'none'], expected_code=1, expected_msg=msg)
+# Test authentication to kadmin/hostname.
+mark('authentication to kadmin/hostname')
+kadmin_hostname = 'kadmin/' + hostname
+realm.run([kadminl, 'delprinc', 'kadmin/admin'])
+msgs = ('Getting initial credentials for user/admin@KRBTEST.COM',
+ 'Setting initial creds service to kadmin/admin',
+ '/Server not found in Kerberos database',
+ 'Getting initial credentials for user/admin@KRBTEST.COM',
+ 'Setting initial creds service to ' + kadmin_hostname,
+ 'Decrypted AS reply')
+realm.run([kadmin, '-p', 'user/admin', 'listprincs'], expected_code=1,
+ expected_msg="Operation requires ``list'' privilege",
+ input=password('user/admin'), expected_trace=msgs)
+
# Test operations disallowed at the libkadm5 layer.
realm.run([kadminl, 'delprinc', 'K/M'],
expected_code=1, expected_msg='Cannot change protected principal')

View File

@ -18,7 +18,7 @@ Summary: The Kerberos network authentication system
Name: krb5
Version: 1.18.2
# for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces)
Release: 18%{?dist}
Release: 19%{?dist}
# rharwood has trust path to signing key and verifies on check-in
Source0: https://web.mit.edu/kerberos/dist/krb5/1.18/krb5-%{version}%{prerelease}.tar.gz
@ -71,6 +71,12 @@ Patch32: Use-two-queues-for-concurrent-t_otp.py-daemons.patch
Patch33: Allow-gss_unwrap_iov-of-unpadded-RC4-tokens.patch
Patch34: Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch
Patch35: Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch
Patch36: Prevent-deletion-of-K-M.patch
Patch37: Try-kadmin-admin-first-in-libkadm5clnt.patch
Patch38: Don-t-create-hostbased-principals-in-new-KDBs.patch
Patch39: Expand-dns_canonicalize_host-fallback-support.patch
Patch40: Cache-S4U2Proxy-requests-by-second-ticket.patch
Patch41: Refactor-cache-checking-in-TGS-client-code.patch
License: MIT
URL: https://web.mit.edu/kerberos/www/
@ -632,6 +638,9 @@ exit 0
%{_libdir}/libkadm5srv_mit.so.*
%changelog
* Fri Aug 07 2020 Robbie Harwood <rharwood@redhat.com> - 1.18.2-19
- Expand dns_canonicalize_hostname=fallback support
* Tue Aug 04 2020 Robbie Harwood <rharwood@redhat.com> - 1.18.2-18
- Fix leak in KERB_AP_OPTIONS_CBT server support