- 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>
225 lines
7.1 KiB
Diff
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
|
|
|