ipa/0095-ipa-kdb-support-storing-multiple-KVNO-for-the-same-p.patch
Florence Blanc-Renaud a96d03c543 ipa-4.12.2-19
- Resolves: RHEL-100450 eDNS: multiple issues during encrypted DNS setup
- Resolves: RHEL-89907 Privilege escalation from host to domain admin in FreeIPA
- Resolves: RHEL-99315 Include latest fixes in python3-ipatests package
- Resolves: RHEL-98565 ipa-idrange-fix: 'Env' object has no attribute 'basedn'
- Resolves: RHEL-96920 Nightly test failure (rawhide) in test_trust.py::TestTrust::test_server_option_with_unreachable_ad
- Resolves: RHEL-31907 kdb: support storing and retrieving multiple master keys

Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
2025-06-30 11:07:39 +02:00

225 lines
7.1 KiB
Diff

From cafaed1c2119fb2e25209eefac74bc21ccab3dcb Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Thu, 25 Jul 2024 19:35:33 +0200
Subject: [PATCH] ipa-kdb: support storing multiple KVNO for the same principal
All MIT krb5 keys are encoded with an encryption type identifier and a
KVNO (key version number). The KVNO is referring to the original
credentials string (and its associated salt) which was used to generate
the key using the derivation function of the associated encryption type.
So far, when a set of Kerberos keys was provided to ipa-kdb, only the
newest KVNO ones were saved in a single "krbPrincipalKey" LDAP
attribute. All the older ones were deleted in the process.
This commit allows to keep older keys by splitting them in multiple
"krbPrincipalKey" attribute based on their KVNO.
Fixes: https://pagure.io/freeipa/issue/9370
Signed-off-by: Julien Rische <jrische@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
daemons/ipa-kdb/ipa_kdb_principals.c | 125 +++++++++++++++++++++++----
util/ipa_krb5.c | 7 ++
2 files changed, 115 insertions(+), 17 deletions(-)
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index 114957b884786dd3ca3b01c47f6bb82e8a040beb..19998c2a38b5d8ae80aeedeb003f54241d2c2a9f 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -22,6 +22,7 @@
#include "ipa_kdb.h"
#include "ipa_krb5.h"
+#include <stdlib.h>
#include <unicase.h>
/*
@@ -279,22 +280,64 @@ done:
static int ipadb_ldap_attr_to_key_data(LDAP *lcontext, LDAPMessage *le,
char *attrname,
krb5_key_data **result, int *num,
- krb5_kvno *res_mkvno)
+ krb5_kvno *mkvno)
{
- struct berval **vals;
- int mkvno;
+ struct berval **vals, **p;
+ krb5_key_data *cur_res = NULL, *fin_res = NULL, *tmp_res;
+ int fin_mkvno = 0, cur_mkvno, cur_num, fin_num = 0;
int ret;
vals = ldap_get_values_len(lcontext, le, attrname);
- if (!vals) {
+ if (!vals)
return ENOENT;
+
+ for (p = vals; *p; ++p) {
+ ret = ber_decode_krb5_key_data(*p, &cur_mkvno, &cur_num, &cur_res);
+ if (ret)
+ goto end;
+
+ /* All keys in a principal entry should be encrypted with the same
+ * master key. */
+ if (fin_mkvno == 0) {
+ fin_mkvno = cur_mkvno;
+ } else if (cur_mkvno != fin_mkvno) {
+ ret = EINVAL;
+ goto end;
+ }
+
+ if (!fin_res) {
+ fin_res = cur_res;
+ } else {
+ tmp_res = realloc(fin_res, (fin_num + cur_num) * sizeof(*fin_res));
+ if (!tmp_res) {
+ ret = ENOMEM;
+ goto end;
+ } else {
+ fin_res = tmp_res;
+ }
+
+ memcpy(fin_res + fin_num, cur_res, cur_num * sizeof(*fin_res));
+ free(cur_res);
+ }
+
+ cur_res = NULL;
+ fin_num += cur_num;
}
- ret = ber_decode_krb5_key_data(vals[0], &mkvno, num, result);
- ldap_value_free_len(vals);
- if (ret == 0) {
- *res_mkvno = mkvno;
+ if (mkvno)
+ *mkvno = fin_mkvno;
+ if (num)
+ *num = fin_num;
+ if (result) {
+ *result = fin_res;
+ } else {
+ free(fin_res);
}
+
+end:
+ ldap_value_free_len(vals);
+ if (cur_res && fin_res != cur_res)
+ free(cur_res);
return ret;
}
@@ -2532,15 +2575,26 @@ static krb5_error_code ipadb_get_mkvno_from_tl_data(krb5_tl_data *tl_data,
return 0;
}
+static int desc_key_data(const void *a, const void *b)
+{
+ const krb5_key_data *ka = a;
+ const krb5_key_data *kb = b;
+
+ return ka->key_data_kvno != kb->key_data_kvno
+ ? kb->key_data_kvno - ka->key_data_kvno
+ : kb->key_data_type[0] - ka->key_data_type[0];
+}
+
static krb5_error_code ipadb_get_ldap_mod_key_data(struct ipadb_mods *imods,
krb5_key_data *key_data,
int n_key_data, int mkvno,
int mod_op)
{
krb5_error_code kerr;
- struct berval *bval = NULL;
+ krb5_key_data *kvno_kdata;
+ struct berval **bvals = NULL;
LDAPMod *mod;
- int ret;
+ int i, j, begin, n_kvno;
/* If the key data is empty, remove all keys. */
if (n_key_data == 0 || key_data == NULL) {
@@ -2559,19 +2613,56 @@ static krb5_error_code ipadb_get_ldap_mod_key_data(struct ipadb_mods *imods,
return 0;
}
- ret = ber_encode_krb5_key_data(key_data, n_key_data, mkvno, &bval);
- if (ret != 0) {
- kerr = ret;
+ /* Copy key list. */
+ kvno_kdata = calloc(n_key_data, sizeof(*kvno_kdata));
+ if (!kvno_kdata)
+ return ENOMEM;
+
+ memcpy(kvno_kdata, key_data, n_key_data * sizeof(*kvno_kdata));
+
+ /* Make sure the key list is sorted by KVNO and enctype. */
+ qsort(kvno_kdata, n_key_data, sizeof(*kvno_kdata), desc_key_data);
+
+ /* Count number of distinct KVNOs. */
+ for (i = 1, n_kvno = 1; i < n_key_data; ++i) {
+ if (kvno_kdata[i - 1].key_data_kvno != kvno_kdata[i].key_data_kvno)
+ ++n_kvno;
+ }
+
+ bvals = calloc(n_kvno, sizeof(*bvals));
+ if (!bvals) {
+ kerr = ENOMEM;
goto done;
}
- kerr = ipadb_get_ldap_mod_bvalues(imods, "krbPrincipalKey",
- &bval, 1, mod_op);
+ /* Add a "krbPrincipalKey" attribute for each KVNO. */
+ for (i = 0, j = 0, begin = 0; i < n_key_data; ++i) {
+ if (kvno_kdata[begin].key_data_kvno != kvno_kdata[i].key_data_kvno) {
+ kerr = ber_encode_krb5_key_data(kvno_kdata + begin, i - begin,
+ mkvno, bvals + j);
+ if (kerr)
+ goto done;
+
+ begin = i;
+ ++j;
+ }
+ }
+
+ kerr = ber_encode_krb5_key_data(kvno_kdata + begin, i - begin, mkvno,
+ bvals + j);
+ if (kerr)
+ goto done;
+
+ kerr = ipadb_get_ldap_mod_bvalues(imods, "krbPrincipalKey", bvals, n_kvno,
+ mod_op);
done:
- if (kerr) {
- ber_bvfree(bval);
+ if (kerr && bvals) {
+ for (i = 0; i < n_kvno; ++i)
+ ber_bvfree(bvals[i]);
+ free(bvals);
}
+ free(kvno_kdata);
return kerr;
}
diff --git a/util/ipa_krb5.c b/util/ipa_krb5.c
index 0087e53e689fc4dc5549908b3eadd6d963d94489..fbd4d57de6728cd6d9449f25c7eb0d5e48269dbe 100644
--- a/util/ipa_krb5.c
+++ b/util/ipa_krb5.c
@@ -394,6 +394,13 @@ int ber_encode_krb5_key_data(krb5_key_data *data,
for (i = 0; i < numk; i++) {
+ /* All keys must have the same KVNO, because there is only one attribute
+ * for all of them. */
+ if (data[i].key_data_kvno != data[0].key_data_kvno) {
+ ret = EINVAL;
+ goto done;
+ }
+
ret = ber_printf(be, "{");
if (ret == -1) {
ret = EFAULT;
--
2.50.0