summaryrefslogtreecommitdiff
path: root/third_party/heimdal/lib/hdb
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2022-10-31 14:33:09 +1300
committerAndrew Bartlett <abartlet@samba.org>2022-11-02 04:23:34 +0000
commitef28247f3bbbd7cf9daed7a4dba28855496ce38e (patch)
treef97d5b9b41ebbfb7e52269486b3c41d9a0fd9575 /third_party/heimdal/lib/hdb
parentab4c7bda8daccdb99adaf6ec7fddf8b5f84be09a (diff)
downloadsamba-ef28247f3bbbd7cf9daed7a4dba28855496ce38e.tar.gz
third_party/heimdal: import lorikeet-heimdal-202210310104 (commit 0fc20ff4144973047e6aaaeb2fc8708bd75be222)
This commit won't compile on it's own, as we need to fix the build system to cope in the next commit. The purpose of this commit is to update to a new lorikeet-heimdal tree that includes the previous two patches and is rebased on a current Heimdal master snapshot. Signed-off-by: Andrew Bartlett <abartlet@samba.org> Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Diffstat (limited to 'third_party/heimdal/lib/hdb')
-rw-r--r--third_party/heimdal/lib/hdb/Makefile.am4
-rw-r--r--third_party/heimdal/lib/hdb/common.c195
-rw-r--r--third_party/heimdal/lib/hdb/hdb-mdb.c2
-rw-r--r--third_party/heimdal/lib/hdb/hdb.asn12
-rw-r--r--third_party/heimdal/lib/hdb/hdb.opt4
-rw-r--r--third_party/heimdal/lib/hdb/keytab.c5
6 files changed, 190 insertions, 22 deletions
diff --git a/third_party/heimdal/lib/hdb/Makefile.am b/third_party/heimdal/lib/hdb/Makefile.am
index 89ab15d9d3e..4a559953243 100644
--- a/third_party/heimdal/lib/hdb/Makefile.am
+++ b/third_party/heimdal/lib/hdb/Makefile.am
@@ -17,7 +17,9 @@ endif
BUILT_SOURCES = \
$(gen_files_hdb) \
hdb_err.c \
- hdb_err.h
+ hdb_err.h \
+ $(srcdir)/hdb-protos.h \
+ $(srcdir)/hdb-private.h
gen_files_hdb = \
asn1_Event.c \
diff --git a/third_party/heimdal/lib/hdb/common.c b/third_party/heimdal/lib/hdb/common.c
index a92cc1372db..56e582abaa8 100644
--- a/third_party/heimdal/lib/hdb/common.c
+++ b/third_party/heimdal/lib/hdb/common.c
@@ -181,6 +181,7 @@ fetch_entry_or_alias(krb5_context context,
ret = decode_HDB_EntryOrAlias(value.data, value.length, &eoa, NULL);
if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_entry) {
*entry = eoa.u.entry;
+ entry->aliased = 0;
} else if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_alias) {
krb5_data_free(&key);
ret = hdb_principal2key(context, eoa.u.alias.principal, &key);
@@ -192,6 +193,7 @@ fetch_entry_or_alias(krb5_context context,
/* No alias chaining */
ret = hdb_value2entry(context, &value, entry);
krb5_free_principal(context, eoa.u.alias.principal);
+ entry->aliased = 1;
} else if (ret == 0)
ret = ENOTSUP;
if (ret == 0 && enterprise_principal) {
@@ -203,6 +205,7 @@ fetch_entry_or_alias(krb5_context context,
entry->flags.force_canonicalize = 1;
}
+#if 0
/* HDB_F_GET_ANY indicates request originated from KDC (not kadmin) */
if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_alias &&
(flags & (HDB_F_CANON|HDB_F_GET_ANY)) == 0) {
@@ -211,6 +214,7 @@ fetch_entry_or_alias(krb5_context context,
free_HDB_entry(entry);
ret = HDB_ERR_NOENTRY;
}
+#endif
krb5_free_principal(context, enterprise_principal);
krb5_data_free(&value);
@@ -219,11 +223,58 @@ fetch_entry_or_alias(krb5_context context,
return ret;
}
+/*
+ * We have only one type of aliases in our HDB entries, but we really need two:
+ * hard and soft.
+ *
+ * Hard aliases should be treated as if they were distinct principals with the
+ * same keys.
+ *
+ * Soft aliases should be treated as configuration to issue referrals, and they
+ * can only result in referrals to other realms.
+ *
+ * Rather than add a type of aliases, we'll use a convention where the form of
+ * the target of the alias indicates whether the alias is hard or soft.
+ *
+ * TODO We could also use an attribute of the aliased entry.
+ */
+static int
+is_soft_alias_p(krb5_context context,
+ krb5_const_principal principal,
+ unsigned int flags,
+ hdb_entry *h)
+{
+ /* Target is a WELLKNOWN/REFERRALS/TARGET/... -> soft alias */
+ if (krb5_principal_get_num_comp(context, h->principal) >= 3 &&
+ strcmp(krb5_principal_get_comp_string(context, h->principal, 0),
+ KRB5_WELLKNOWN_NAME) == 0 &&
+ strcmp(krb5_principal_get_comp_string(context, h->principal, 1),
+ "REFERRALS") == 0 &&
+ strcmp(krb5_principal_get_comp_string(context, h->principal, 2),
+ "TARGET") == 0)
+ return 1;
+
+ /*
+ * Pre-8.0 we had only soft aliases for a while, and one site used aliases
+ * of referrals-targetNN@TARGET-REALM.
+ */
+ if (krb5_principal_get_num_comp(context, h->principal) == 1 &&
+ strncmp("referrals-target",
+ krb5_principal_get_comp_string(context, h->principal, 0),
+ sizeof("referrals-target") - 1) == 0)
+ return 1;
+
+ /* All other cases are hard aliases */
+ return 0;
+}
+
krb5_error_code
_hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
unsigned flags, krb5_kvno kvno, hdb_entry *entry)
{
krb5_error_code ret;
+ int soft_aliased = 0;
+ int same_realm;
ret = fetch_entry_or_alias(context, db, principal, flags, entry);
if (ret)
@@ -278,7 +329,54 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
}
}
- return 0;
+ if (!entry->aliased)
+ return 0;
+
+ soft_aliased = is_soft_alias_p(context, principal, flags, entry);
+
+ /* Never return HDB_ERR_WRONG_REALM to kadm5 or other non-KDC callers */
+ if ((flags & HDB_F_ADMIN_DATA))
+ return 0;
+
+ same_realm = krb5_realm_compare(context, principal, entry->principal);
+
+ if (entry->aliased && !soft_aliased) {
+ /*
+ * This is a hard alias. We'll make the entry's name be the same as
+ * the alias.
+ *
+ * Except, we allow for disabling this for same-realm aliases, mainly
+ * for our tests.
+ */
+ if (same_realm &&
+ krb5_config_get_bool_default(context, NULL, FALSE, "hdb",
+ "same_realm_aliases_are_soft", NULL))
+ return 0;
+
+ /* EPNs are always soft */
+ if (principal->name.name_type != KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ krb5_free_principal(context, entry->principal);
+ ret = krb5_copy_principal(context, principal, &entry->principal);
+ if (ret) {
+ hdb_free_entry(context, db, entry);
+ return ret;
+ }
+ }
+ return 0;
+ }
+
+ /* Same realm -> not a referral, therefore this is a hard alias */
+ if (same_realm) {
+ if (soft_aliased) {
+ /* Soft alias to the same realm?! No. */
+ hdb_free_entry(context, db, entry);
+ return HDB_ERR_NOENTRY;
+ }
+ return 0;
+ }
+
+ /* Not same realm && not hard alias */
+ return HDB_ERR_WRONG_REALM;
}
static krb5_error_code
@@ -313,6 +411,8 @@ hdb_remove_aliases(krb5_context context, HDB *db, krb5_data *key)
if (code == 0) {
code = db->hdb__del(context, db, akey);
krb5_data_free(&akey);
+ if (code == HDB_ERR_NOENTRY)
+ code = 0;
}
if (code) {
free_HDB_entry(&oldentry);
@@ -348,6 +448,12 @@ hdb_add_aliases(krb5_context context, HDB *db,
if (code == 0) {
code = db->hdb__put(context, db, flags, key, value);
krb5_data_free(&key);
+ if (code == HDB_ERR_EXISTS)
+ /*
+ * Assuming hdb_check_aliases() was called, this must be a
+ * duplicate in the alias list.
+ */
+ code = 0;
}
krb5_data_free(&value);
if (code)
@@ -750,6 +856,10 @@ derive_keys_for_kr(krb5_context context,
* (t - krp->epoch < 0) is better than (krp->epoch < t), making us more
* tolerant of signed 32-bit time_t here near 2038. Of course, we have
* signed 32-bit time_t dragons elsewhere.
+ *
+ * We don't need to check for n == 0 && rotation_period_offset < 0 because
+ * only derive_keys_for_current_kr() calls us with non-zero rotation period
+ * offsets, and it will never call us in that case.
*/
if (t - krp->epoch < 0)
return 0; /* This KR is not relevant yet */
@@ -758,6 +868,37 @@ derive_keys_for_kr(krb5_context context,
set_time = krp->epoch + krp->period * n;
kvno = krp->base_kvno + n;
+ /*
+ * Since this principal is virtual, or has virtual keys, we're going to
+ * derive a "password expiration time" for it in order to help httpkadmind
+ * and other tools figure out when to request keys again.
+ *
+ * The kadm5 representation of principals does not include the set_time of
+ * keys/keysets, so we can't have httpkadmind derive a Cache-Control from
+ * that without adding yet another "TL data". Since adding TL data is a
+ * huge pain, we'll just use the `pw_end' field of `HDB_entry' to
+ * communicate when this principal's keys will change next.
+ */
+ if (h->pw_end[0] == 0) {
+ KerberosTime used = (t - krp->epoch) % krp->period;
+ KerberosTime left = krp->period - used;
+
+ /*
+ * If `h->pw_end[0]' == 0 then this must be the current period of the
+ * current KR we're deriving keys for. See upstairs.
+ *
+ * If there's more than a quarter of this time period left, then we'll
+ * set `h->pw_end[0]' to one quarter before the end of this time
+ * period. Else we'll set it to 1/4 after (we'll be including the next
+ * set of derived keys, so there's no harm in waiting that long to
+ * refetch).
+ */
+ if (left > krp->period >> 2)
+ h->pw_end[0] = set_time + krp->period - (krp->period >> 2);
+ else
+ h->pw_end[0] = set_time + krp->period + (krp->period >> 2);
+ }
+
/*
* Do not waste cycles computing keys not wanted or needed.
@@ -1092,6 +1233,11 @@ derive_keys(krb5_context context,
"because last key rotation period "
"marks deletion", p);
+ /* See `derive_keys_for_kr()' */
+ if (h->pw_end == NULL &&
+ (h->pw_end = calloc(1, sizeof(h->pw_end[0]))) == NULL)
+ ret = krb5_enomem(context);
+
/*
* Derive and set in `h' its current kvno and current keys.
*
@@ -1140,6 +1286,12 @@ derive_keys(krb5_context context,
if (ret == 0 && *h->max_life > kr.val[current_kr].period >> 1)
*h->max_life = kr.val[current_kr].period >> 1;
+ if (ret == 0 && h->pw_end[0] == 0)
+ /* Shouldn't happen */
+ h->pw_end[0] = kr.val[current_kr].epoch +
+ kr.val[current_kr].period *
+ (1 + (t - kr.val[current_kr].epoch) / kr.val[current_kr].period);
+
free_HDB_Ext_KeyRotation(&kr);
free_HDB_Ext_KeySet(&base_keys);
free(p);
@@ -1506,6 +1658,10 @@ fetch_it(krb5_context context,
* of labels.
*/
ret = db->hdb_fetch_kvno(context, db, tmpprinc, flags, kvno, ent);
+ if (ret == 0 && nsprinc && ent->flags.invalid) {
+ free_HDB_entry(ent);
+ ret = HDB_ERR_NOENTRY;
+ }
if (ret != HDB_ERR_NOENTRY || hdots == 0 || hdots < mindots || !tmp ||
!do_search)
break;
@@ -1561,7 +1717,9 @@ fetch_it(krb5_context context,
* If unencrypted keys were requested, derive them. There may not be any
* key derivation to do, but that's decided in derive_keys().
*/
- if (ret == 0) {
+ if (ret == 0 || ret == HDB_ERR_WRONG_REALM) {
+ krb5_error_code save_ret = ret;
+
/* Fix the principal name if namespaced */
ret = fix_princ_name(context, princ, nsprinc, ent);
@@ -1572,6 +1730,8 @@ fetch_it(krb5_context context,
/* Pick the best kvno for this principal at the given time */
if (ret == 0)
ret = pick_kvno(context, db, flags, t, kvno, ent);
+ if (ret == 0)
+ ret = save_ret;
}
out:
@@ -1603,7 +1763,7 @@ out:
* @param kvno Key version number (use zero to mean "current")
* @param h Output HDB entry
*
- * @return Zero on success, an error code otherwise.
+ * @return Zero or HDB_ERR_WRONG_REALM on success, an error code otherwise.
*/
krb5_error_code
hdb_fetch_kvno(krb5_context context,
@@ -1615,24 +1775,25 @@ hdb_fetch_kvno(krb5_context context,
krb5uint32 kvno,
hdb_entry *h)
{
- krb5_error_code ret = HDB_ERR_NOENTRY;
+ krb5_error_code ret;
+ krb5_timestamp now;
+
+ krb5_timeofday(context, &now);
flags |= kvno ? HDB_F_KVNO_SPECIFIED : 0; /* XXX is this needed */
- if (t == 0)
- krb5_timeofday(context, &t);
- ret = fetch_it(context, db, principal, flags, t, etype, kvno, h);
+ ret = fetch_it(context, db, principal, flags, t ? t : now, etype, kvno, h);
+ if (ret == 0 && t == 0 && h->flags.virtual &&
+ h->pw_end && h->pw_end[0] < now) {
+ /*
+ * This shouldn't happen!
+ *
+ * Do not allow h->pw_end[0] to be in the past for virtual principals
+ * outside testing. This is just to prevent the AS/TGS from failing.
+ */
+ h->pw_end[0] = now + 3600;
+ }
if (ret == HDB_ERR_NOENTRY)
krb5_set_error_message(context, ret, "no such entry found in hdb");
-
- /*
- * This check is to support aliases in HDB; the force_canonicalize
- * check is to allow HDB backends to support realm name canon
- * independently of principal aliases (used by Samba).
- */
- if (ret == 0 && !(flags & HDB_F_ADMIN_DATA) &&
- !h->flags.force_canonicalize &&
- !krb5_realm_compare(context, principal, h->principal))
- ret = HDB_ERR_WRONG_REALM;
return ret;
}
diff --git a/third_party/heimdal/lib/hdb/hdb-mdb.c b/third_party/heimdal/lib/hdb/hdb-mdb.c
index 6aa5201eb8a..dd1f27453d5 100644
--- a/third_party/heimdal/lib/hdb/hdb-mdb.c
+++ b/third_party/heimdal/lib/hdb/hdb-mdb.c
@@ -146,7 +146,7 @@ my_mdb_env_create_and_open(krb5_context context,
{
struct keep_it_open *p, *n;
MDB_txn *txn = NULL;
- unsigned int flags = MDB_NOSUBDIR;
+ unsigned int flags = MDB_NOSUBDIR | MDB_NOTLS;
struct stat st;
size_t mapsize = 0;
int max_readers;
diff --git a/third_party/heimdal/lib/hdb/hdb.asn1 b/third_party/heimdal/lib/hdb/hdb.asn1
index 9eb96be73d0..abc75f742cc 100644
--- a/third_party/heimdal/lib/hdb/hdb.asn1
+++ b/third_party/heimdal/lib/hdb/hdb.asn1
@@ -59,6 +59,8 @@ HDBFlags ::= BIT STRING {
force-canonicalize(30), -- force the KDC to return the canonical
-- principal irrespective of the setting
-- of the canonicalize KDC option
+ -- (principals cannot have this flag
+ -- set when stored into the HDB)
do-not-store(31) -- Not to be modified and stored in HDB
}
diff --git a/third_party/heimdal/lib/hdb/hdb.opt b/third_party/heimdal/lib/hdb/hdb.opt
index 626f8c7b07a..a96eb632e52 100644
--- a/third_party/heimdal/lib/hdb/hdb.opt
+++ b/third_party/heimdal/lib/hdb/hdb.opt
@@ -3,3 +3,7 @@
--sequence=HDB-Ext-KeySet
--sequence=Keys
--decorate=HDB_entry:void:context?:::
+# The `aliased` field is for indicating whether a name used in an HDB
+# lookup was an alias. This could be useful to applications when hard
+# aliasing is implemented in an HDB backend, as it should be.
+--decorate=HDB_entry:int:aliased:::
diff --git a/third_party/heimdal/lib/hdb/keytab.c b/third_party/heimdal/lib/hdb/keytab.c
index b1aa0207c97..df16cb782fa 100644
--- a/third_party/heimdal/lib/hdb/keytab.c
+++ b/third_party/heimdal/lib/hdb/keytab.c
@@ -223,10 +223,9 @@ hdb_get_entry(krb5_context context,
HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
0, 0, kvno, &ent);
- if(ret == HDB_ERR_NOENTRY) {
+ if (ret == HDB_ERR_WRONG_REALM || ret == HDB_ERR_NOENTRY)
ret = KRB5_KT_NOTFOUND;
- goto out;
- }else if(ret)
+ if (ret)
goto out;
if(kvno && (krb5_kvno)ent.kvno != kvno) {