import UBI krb5-1.21.3-10.el10_2

This commit is contained in:
AlmaLinux RelEng Bot 2026-05-19 19:33:24 -04:00
parent 297f069b69
commit fa695c3901
6 changed files with 1181 additions and 5 deletions

View File

@ -0,0 +1,257 @@
From 72b42b3b523cea1809616aed81158bf784ac35b9 Mon Sep 17 00:00:00 2001
From: Zoltan Borbely <Zoltan.Borbely@morganstanley.com>
Date: Fri, 31 Jan 2025 18:00:17 +0100
Subject: [PATCH] Improve ulog block resize efficiency
When it is necessary to increase the ulog block size, copy the
existing entries instead of reinitializing the log.
[ghudson@mit.edu: added test case; renamed and split INDEX() to avoid
duplication; added sync_ulog() helper; modified copying loop for
clarity; edited commit message]
ticket: 9161 (new)
(cherry picked from commit 197ddd4196a3049cd17d29d1c8f0af1424472956)
---
src/include/kdb_log.h | 20 +++++++++----
src/kprop/kproplog.c | 2 +-
src/lib/kdb/kdb_log.c | 68 ++++++++++++++++++++++++++++++-------------
src/tests/t_iprop.py | 30 +++++++++++++++++--
4 files changed, 90 insertions(+), 30 deletions(-)
diff --git a/src/include/kdb_log.h b/src/include/kdb_log.h
index 4239575659..2856859f8e 100644
--- a/src/include/kdb_log.h
+++ b/src/include/kdb_log.h
@@ -18,12 +18,6 @@
extern "C" {
#endif
-/*
- * DB macros
- */
-#define INDEX(ulog, i) (kdb_ent_header_t *)(void *) \
- ((char *)(ulog) + sizeof(kdb_hlog_t) + (i) * ulog->kdb_block)
-
/*
* Current DB version #
*/
@@ -106,6 +100,20 @@ typedef struct _kdb_log_context {
int ulogfd;
} kdb_log_context;
+/* Return the address of the i'th record in ulog for the given block size. */
+static inline uint8_t *
+ulog_record_ptr(kdb_hlog_t *ulog, size_t i, size_t bsize)
+{
+ return (uint8_t *)ulog + sizeof(*ulog) + i * bsize;
+}
+
+/* Return the i'th update entry header for ulog. */
+static inline kdb_ent_header_t *
+ulog_index(kdb_hlog_t *ulog, size_t i)
+{
+ return (void *)ulog_record_ptr(ulog, i, ulog->kdb_block);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/src/kprop/kproplog.c b/src/kprop/kproplog.c
index 1f10aa6dc7..45ded429e7 100644
--- a/src/kprop/kproplog.c
+++ b/src/kprop/kproplog.c
@@ -330,7 +330,7 @@ print_update(kdb_hlog_t *ulog, uint32_t entry, uint32_t ulogentries,
for (i = start_sno; i < ulog->kdb_last_sno; i++) {
indx = i % ulogentries;
- indx_log = INDEX(ulog, indx);
+ indx_log = ulog_index(ulog, indx);
/*
* Check for corrupt update entry
diff --git a/src/lib/kdb/kdb_log.c b/src/lib/kdb/kdb_log.c
index c805ebd988..419beab9e4 100644
--- a/src/lib/kdb/kdb_log.c
+++ b/src/lib/kdb/kdb_log.c
@@ -103,13 +103,31 @@ sync_header(kdb_hlog_t *ulog)
}
}
+/* Sync memory to disk for the entire ulog. */
+static void
+sync_ulog(kdb_hlog_t *ulog, uint32_t ulogentries)
+{
+ size_t len;
+
+ if (!pagesize)
+ pagesize = getpagesize();
+
+ len = (sizeof(kdb_hlog_t) + ulogentries * ulog->kdb_block +
+ (pagesize - 1)) & ~(pagesize - 1);
+ if (msync(ulog, len, MS_SYNC)) {
+ /* Couldn't sync to disk, let's panic. */
+ syslog(LOG_ERR, _("could not sync the whole ulog to disk"));
+ abort();
+ }
+}
+
/* Return true if the ulog entry for sno matches sno and timestamp. */
static krb5_boolean
check_sno(kdb_log_context *log_ctx, kdb_sno_t sno,
const kdbe_time_t *timestamp)
{
unsigned int indx = (sno - 1) % log_ctx->ulogentries;
- kdb_ent_header_t *ent = INDEX(log_ctx->ulog, indx);
+ kdb_ent_header_t *ent = ulog_index(log_ctx->ulog, indx);
return ent->kdb_entry_sno == sno && time_equal(&ent->kdb_time, timestamp);
}
@@ -175,17 +193,16 @@ extend_file_to(int fd, unsigned int new_size)
return 0;
}
-/*
- * Resize the array elements. We reinitialize the update log rather than
- * unrolling the the log and copying it over to a temporary log for obvious
- * performance reasons. Replicas will subsequently do a full resync, but the
- * need for resizing should be very small.
- */
+/* Resize the array elements of ulog to be at least as large as recsize. Move
+ * the existing elements into the proper offsets for the new block size. */
static krb5_error_code
resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd,
unsigned int recsize, const kdb_incr_update_t *upd)
{
- unsigned int new_block, new_size;
+ size_t old_block = ulog->kdb_block, new_block, new_size;
+ krb5_error_code retval;
+ uint8_t *old_ent, *new_ent;
+ uint32_t i;
if (ulog == NULL)
return KRB5_LOG_ERROR;
@@ -204,16 +221,25 @@ resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd,
if (new_size > MAXLOGLEN)
return KRB5_LOG_ERROR;
- /* Reinit log with new block size. */
- memset(ulog, 0, sizeof(*ulog));
- ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
- ulog->db_version_num = KDB_VERSION;
- ulog->kdb_state = KDB_STABLE;
- ulog->kdb_block = new_block;
- sync_header(ulog);
-
/* Expand log considering new block size. */
- return extend_file_to(ulogfd, new_size);
+ retval = extend_file_to(ulogfd, new_size);
+ if (retval)
+ return retval;
+
+ /* Copy each record into its new location and zero out the unused areas.
+ * The area is overlapping, so we have to iterate backwards. */
+ for (i = ulogentries; i > 0; i--) {
+ old_ent = ulog_record_ptr(ulog, i - 1, old_block);
+ new_ent = ulog_record_ptr(ulog, i - 1, new_block);
+ memmove(new_ent, old_ent, old_block);
+ memset(new_ent + old_block, 0, new_block - old_block);
+ }
+
+ syslog(LOG_INFO, _("ulog block size has been resized from %lu to %lu"),
+ (unsigned long)old_block, (unsigned long)new_block);
+ ulog->kdb_block = new_block;
+ sync_ulog(ulog, ulogentries);
+ return 0;
}
/* Set the ulog to contain only a dummy entry with the given serial number and
@@ -222,7 +248,7 @@ static void
set_dummy(kdb_log_context *log_ctx, kdb_sno_t sno, const kdbe_time_t *kdb_time)
{
kdb_hlog_t *ulog = log_ctx->ulog;
- kdb_ent_header_t *ent = INDEX(ulog, (sno - 1) % log_ctx->ulogentries);
+ kdb_ent_header_t *ent = ulog_index(ulog, (sno - 1) % log_ctx->ulogentries);
memset(ent, 0, sizeof(*ent));
ent->kdb_umagic = KDB_ULOG_MAGIC;
@@ -305,7 +331,7 @@ store_update(kdb_log_context *log_ctx, kdb_incr_update_t *upd)
ulog->kdb_state = KDB_UNSTABLE;
i = (upd->kdb_entry_sno - 1) % ulogentries;
- indx_log = INDEX(ulog, i);
+ indx_log = ulog_index(ulog, i);
memset(indx_log, 0, ulog->kdb_block);
indx_log->kdb_umagic = KDB_ULOG_MAGIC;
@@ -335,7 +361,7 @@ store_update(kdb_log_context *log_ctx, kdb_incr_update_t *upd)
} else {
/* We are circling; set kdb_first_sno and time to the next update. */
i = upd->kdb_entry_sno % ulogentries;
- indx_log = INDEX(ulog, i);
+ indx_log = ulog_index(ulog, i);
ulog->kdb_first_sno = indx_log->kdb_entry_sno;
ulog->kdb_first_time = indx_log->kdb_time;
}
@@ -593,7 +619,7 @@ ulog_get_entries(krb5_context context, const kdb_last_t *last,
for (; sno < ulog->kdb_last_sno; sno++) {
indx = sno % ulogentries;
- indx_log = INDEX(ulog, indx);
+ indx_log = ulog_index(ulog, indx);
memset(upd, 0, sizeof(kdb_incr_update_t));
xdrmem_create(&xdrs, (char *)indx_log->entry_data,
diff --git a/src/tests/t_iprop.py b/src/tests/t_iprop.py
index b356971dbb..1f1634f31f 100755
--- a/src/tests/t_iprop.py
+++ b/src/tests/t_iprop.py
@@ -86,8 +86,10 @@ def wait_for_prop(kpropd, full_expected, expected_old, expected_new):
# Verify the output of kproplog against the expected number of
# entries, first and last serial number, and a list of principal names
# for the update entrires.
-def check_ulog(num, first, last, entries, env=None):
+def check_ulog(num, first, last, entries, env=None, bsize=2048):
out = realm.run([kproplog], env=env)
+ if 'Entry block size : ' + str(bsize) + '\n' not in out:
+ fail('Expected block size %d' % bsize)
if 'Number of entries : ' + str(num) + '\n' not in out:
fail('Expected %d entries' % num)
if last:
@@ -458,8 +460,32 @@ for realm in multidb_realms(kdc_conf=conf, create_user=False,
wait_for_prop(kpropd2, True, 6, 1)
check_ulog(1, 1, 1, [None], replica2)
- # Stop the kprop daemons so we can test kpropd -t.
+ # Create an update large enough to cause a block resize, and make
+ # sure that it propagates incrementally.
+ mark('block resize')
+ cmd = [kadminl, 'cpw',
+ '-e', 'aes128-sha1,aes256-sha1,aes128-sha2,aes256-sha2',
+ '-randkey', '-keepold', pr2]
+ n = 6
+ for i in range(n):
+ realm.run(cmd)
+ check_ulog(n + 1, 1, n + 1, [None] + n * [pr2], bsize=4096)
+ kpropd1.send_signal(signal.SIGUSR1)
+ wait_for_prop(kpropd1, False, 1, n + 1)
+ check_ulog(n + 1, 1, n + 1, [None] + n * [pr2], replica1, bsize=4096)
+ kpropd2.send_signal(signal.SIGUSR1)
+ wait_for_prop(kpropd2, False, 1, n + 1)
+ check_ulog(n + 1, 1, n + 1, [None] + n * [pr2], replica2, bsize=4096)
+
+ # Reset the ulog again.
+ realm.run([kproplog, '-R'])
+ kpropd1.send_signal(signal.SIGUSR1)
+ wait_for_prop(kpropd1, True, 7, 1)
+ kpropd2.send_signal(signal.SIGUSR1)
+ wait_for_prop(kpropd2, True, 7, 1)
realm.stop_kpropd(kpropd1)
+
+ # Stop the kprop daemons so we can test kpropd -t.
stop_daemon(kpropd2)
stop_daemon(kadmind_proponly)
mark('kpropd -t')
--
2.51.1

View File

@ -0,0 +1,760 @@
From 55dcea9c5f2ffd80e396f0d47dd6e8f6efd4c944 Mon Sep 17 00:00:00 2001
From: Dax Kelson <dakelson@redhat.com>
Date: Tue, 13 May 2025 11:54:41 -0600
Subject: [PATCH] Add xrealmauthz KDC policy module and tests
This module provides fine-grained access control for cross-realm
authentications by checking string attributes on the incoming
cross-realm TGT entry. It supports realm-based and principal-specific
authorization rules.
The module is not installed by the build system or loaded by default,
and is documented only in the module source code.
[ghudson@mit.edu: simplified code and tests; edited commit message]
(cherry picked from commit ae8801b8e12d198f11f9279c747f8fa6d48c593e)
---
src/Makefile.in | 1 +
src/configure.ac | 1 +
src/plugins/kdcpolicy/xrealmauthz/Makefile.in | 18 +
src/plugins/kdcpolicy/xrealmauthz/deps | 14 +
src/plugins/kdcpolicy/xrealmauthz/main.c | 380 ++++++++++++++++++
.../kdcpolicy/xrealmauthz/xrealmauthz.exports | 1 +
src/tests/Makefile.in | 1 +
src/tests/t_xrealmauthz.py | 246 ++++++++++++
8 files changed, 662 insertions(+)
create mode 100644 src/plugins/kdcpolicy/xrealmauthz/Makefile.in
create mode 100644 src/plugins/kdcpolicy/xrealmauthz/deps
create mode 100644 src/plugins/kdcpolicy/xrealmauthz/main.c
create mode 100644 src/plugins/kdcpolicy/xrealmauthz/xrealmauthz.exports
create mode 100644 src/tests/t_xrealmauthz.py
diff --git a/src/Makefile.in b/src/Makefile.in
index ba3bb18eec..946a3b7212 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -25,6 +25,7 @@ SUBDIRS=util include lib \
@lmdb_plugin_dir@ \
plugins/kdb/test \
plugins/kdcpolicy/test \
+ plugins/kdcpolicy/xrealmauthz \
plugins/preauth/otp \
plugins/preauth/pkinit \
plugins/preauth/spake \
diff --git a/src/configure.ac b/src/configure.ac
index 487f393146..a8d2e4cca2 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -1554,6 +1554,7 @@ V5_AC_OUTPUT_MAKEFILE(.
plugins/kdb/db2/libdb2/test
plugins/kdb/test
plugins/kdcpolicy/test
+ plugins/kdcpolicy/xrealmauthz
plugins/preauth/otp
plugins/preauth/spake
plugins/preauth/test
diff --git a/src/plugins/kdcpolicy/xrealmauthz/Makefile.in b/src/plugins/kdcpolicy/xrealmauthz/Makefile.in
new file mode 100644
index 0000000000..78346d6572
--- /dev/null
+++ b/src/plugins/kdcpolicy/xrealmauthz/Makefile.in
@@ -0,0 +1,18 @@
+mydir=plugins$(S)kdcpolicy$(S)xrealmauthz
+BUILDTOP=$(REL)..$(S)..$(S)..
+
+LIBBASE=xrealmauthz
+LIBMAJOR=0
+LIBMINOR=0
+RELDIR=../plugins/kdcpolicy/xrealmauthz
+SHLIB_EXPDEPS=$(KRB5_BASE_DEPLIBS) $(KDB5_DEPLIB)
+SHLIB_EXPLIBS=$(KRB5_BASE_LIBS) $(KDB5_LIB)
+STLIBOBJS=main.o
+
+SRCS=$(srcdir)/main.c
+
+all-unix: all-libs
+install-unix:
+clean-unix:: clean-libs clean-libobjs
+@libnover_frag@
+@libobj_frag@
diff --git a/src/plugins/kdcpolicy/xrealmauthz/deps b/src/plugins/kdcpolicy/xrealmauthz/deps
new file mode 100644
index 0000000000..4ecf533f3f
--- /dev/null
+++ b/src/plugins/kdcpolicy/xrealmauthz/deps
@@ -0,0 +1,14 @@
+#
+# Generated makefile dependencies follow.
+#
+main.so main.po $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/kdcpolicy_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h main.c
diff --git a/src/plugins/kdcpolicy/xrealmauthz/main.c b/src/plugins/kdcpolicy/xrealmauthz/main.c
new file mode 100644
index 0000000000..72f077d434
--- /dev/null
+++ b/src/plugins/kdcpolicy/xrealmauthz/main.c
@@ -0,0 +1,380 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* plugins/kdcpolicy/xrealmauthz/main.c - xrealmauthz module implementation */
+/*
+ * Copyright (C) 2025 by Red Hat, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * The xrealmauthz module restricts authentications from clients in other
+ * realms. It is not installed by the build system or loaded by default. It
+ * can be loaded with the following configuration:
+ *
+ * [plugins]
+ * kdcpolicy = {
+ * module = /path/to/xrealmauthz.so
+ * }
+ *
+ * Once the module is loaded, all authentications from clients in other realms
+ * are rejected unless they are explicitly authorized, unless enforcement is
+ * turned off. Authorization can be achieved in three ways:
+ *
+ * 1. If the xrealmauthz_allowed_realms profile variable in [kdcdefaults] has
+ * one or more values, authentications by clients in those realms are always
+ * permitted by this module, regardless of the authentication path. (The
+ * authentication path must still pass the transited check as configured in
+ * [capaths]). For example, the following configuration:
+ *
+ * [kdcdefaults]
+ * xrealmauthz_allowed_realms = REALM2.COM
+ * xrealmauthz_allowed_realms = REALM3.COM
+ *
+ * would cause this module to permit all authentications from clients in
+ * REALM2.COM or REALM3.COM.
+ *
+ * 2. If the string attribute "xr:@CLIENTREALM" is present in the TGS entry
+ * krbtgt/MYREALM@OREALM (where MYREALM is the realm served by the KDC),
+ * then authentications from clients in CLIENTREALM are permitted via
+ * OREALM. The value of the string attribute is ignored. For example, if
+ * this KDC serves REALM1.COM, the following commands would permit
+ * authentications via REALM2.COM for clients in both REALM2.COM itself and
+ * REALM3.COM:
+ *
+ * kadmin.local setstr krbtgt/REALM1.COM@REALM2.COM xr:@REALM2.COM ""
+ * kadmin.local setstr krbtgt/REALM1.COM@REALM2.COM xr:@REALM3.COM ""
+ *
+ * 3. If the string attribute "xr:PRINC" is present in KRBTGT/MYREALM@OREALM,
+ * authentications from the client principal PRINC are permitted. PRINC
+ * must contain a realm part if its realm differs from OREALM, and must
+ * _not_ contain a realm part if its realm is the same as OREALM. For
+ * example, the following commands would permit authentications via
+ * REALM2.COM for the clients u1@REALM2.COM and u2@REALM3.COM:
+ *
+ * kadmin.local setstr krbtgt/REALM1.COM@REALM2.COM xr:u1 ""
+ * kadmin.local setstr krbtgt/REALM1.COM@REALM2.COM xr:u2@REALM3.COM ""
+ *
+ * Enforcement may be turned off by setting the profile variable
+ * xrealmauthz_enforcing to false in [kdcdefaults]:
+ *
+ * [kdcdefaults]
+ * xrealmauthz_enforcing = false
+ *
+ * If enforcement is turned off, this module will permit all cross-realm
+ * authentications, but will log authentications that would otherwise be denied
+ * with a message containing:
+ *
+ * xrealmauthz module would deny CLIENTPRINC for SERVERPRINC from REALM
+ */
+
+#include "k5-int.h"
+#include <kdb.h>
+#include <krb5/kdcpolicy_plugin.h>
+
+/* Prefix used for cross-realm authorization attributes */
+#define ATTR_PREFIX "xr:"
+
+struct xrealmauthz_data {
+ int enforcing; /* Whether to actually enforce restrictions */
+ krb5_data *allowed_realms;
+ size_t num_allowed_realms;
+};
+
+/* Typedef for pointer to the structure */
+typedef struct xrealmauthz_data *xrealmauthz_moddata;
+
+static void
+free_moddata(xrealmauthz_moddata data)
+{
+ size_t i;
+
+ if (data == NULL)
+ return;
+ for (i = 0; i < data->num_allowed_realms; i++)
+ free(data->allowed_realms[i].data);
+ free(data->allowed_realms);
+ free(data);
+}
+
+static krb5_error_code
+xrealmauthz_init(krb5_context context, krb5_kdcpolicy_moddata *moddata_out)
+{
+ krb5_error_code ret;
+ int enforcing = 1;
+ xrealmauthz_moddata data = NULL;
+ profile_t profile = NULL;
+ char **realmlist = NULL;
+ size_t count, i;
+ const char *section[] = { "kdcdefaults", "xrealmauthz_allowed_realms",
+ NULL };
+
+ *moddata_out = NULL;
+
+ ret = krb5_get_profile(context, &profile);
+ if (ret)
+ goto cleanup;
+
+ /* Check if enforcing mode is disabled in config, default to TRUE */
+ profile_get_boolean(profile, "kdcdefaults", "xrealmauthz_enforcing",
+ NULL, TRUE, &enforcing);
+
+ data = k5alloc(sizeof(*data), &ret);
+ if (data == NULL)
+ goto cleanup;
+
+ /* Get array of allowed realms from config. */
+ ret = profile_get_values(profile, section, &realmlist);
+ if (ret && ret != PROF_NO_RELATION)
+ goto cleanup;
+ ret = 0;
+
+ if (realmlist != NULL) {
+ /* Count and allocate realm entries. */
+ for (count = 0; realmlist[count] != NULL; count++);
+ data->allowed_realms = k5calloc(count, sizeof(krb5_data), &ret);
+ if (data->allowed_realms == NULL)
+ goto cleanup;
+ data->num_allowed_realms = count;
+
+ /* Transfer ownership of the strings from the profile list. */
+ for (i = 0; i < count; i++)
+ data->allowed_realms[i] = string2data(realmlist[i]);
+ free(realmlist);
+ realmlist = NULL;
+ }
+
+ data->enforcing = enforcing;
+
+ com_err("", 0,
+ _("xrealmauthz cross-realm authorization module loaded "
+ "(enforcing mode: %s, pre-approved realms: %d)"),
+ enforcing ? _("enabled") : _("disabled"),
+ (int)data->num_allowed_realms);
+
+ *moddata_out = (krb5_kdcpolicy_moddata)data;
+ data = NULL;
+
+cleanup:
+ free_moddata(data);
+ profile_free_list(realmlist);
+ profile_release(profile);
+ return ret;
+}
+
+static krb5_error_code
+xrealmauthz_fini(krb5_context context, krb5_kdcpolicy_moddata moddata)
+{
+ free_moddata((xrealmauthz_moddata)moddata);
+ return 0;
+}
+
+static krb5_boolean
+is_realm_preapproved(xrealmauthz_moddata data, const krb5_data *client_realm)
+{
+ size_t i;
+
+ for (i = 0; i < data->num_allowed_realms; i++) {
+ if (data_eq(data->allowed_realms[i], *client_realm))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Set *result_out to true if tgt has a string attribute for attr_key with any
+ * value. */
+static krb5_error_code
+check_attr(krb5_context context, krb5_db_entry *tgt, const char *key,
+ krb5_boolean *result_out)
+{
+ krb5_error_code ret;
+ char *value;
+
+ *result_out = FALSE;
+
+ ret = krb5_dbe_get_string(context, tgt, key, &value);
+ if (!ret && value != NULL) {
+ *result_out = TRUE;
+ krb5_dbe_free_string(context, value);
+ }
+
+ return ret;
+}
+
+/* Set *result_out to true if tgt has an ACL attribute for realm
+ * ("xr:@realm"). */
+static krb5_error_code
+check_realm_attr(krb5_context context, krb5_db_entry *tgt,
+ const krb5_data *realm, krb5_boolean *result_out)
+{
+ krb5_error_code ret;
+ char *key;
+
+ if (asprintf(&key, "%s@%.*s", ATTR_PREFIX,
+ (int)realm->length, realm->data) < 0)
+ return ENOMEM;
+ ret = check_attr(context, tgt, key, result_out);
+ free(key);
+ return ret;
+}
+
+/* Set *result_out to true if tgt has an ACL attribute for princ ("xr:princ",
+ * with the realm omitted if princ is in tgt's realm). */
+static krb5_error_code
+check_princ_attr(krb5_context context, krb5_db_entry *tgt,
+ krb5_const_principal princ, krb5_boolean *result_out)
+{
+ krb5_error_code ret;
+ int flags = 0, r;
+ char *princstr, *key;
+
+ /* Omit the realm if princ is in tgt's realm. */
+ if (krb5_realm_compare(context, tgt->princ, princ))
+ flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
+ ret = krb5_unparse_name_flags(context, princ, flags, &princstr);
+ if (ret)
+ return ret;
+
+ r = asprintf(&key, "%s%s", ATTR_PREFIX, princstr);
+ krb5_free_unparsed_name(context, princstr);
+ if (r < 0)
+ return ENOMEM;
+
+ ret = check_attr(context, tgt, key, result_out);
+ free(key);
+ return ret;
+}
+
+/* Check if cross-realm authentication is allowed from client via tgtname. */
+static krb5_error_code
+check_cross_realm_auth(krb5_context context, krb5_const_principal client,
+ krb5_const_principal tgtname,
+ krb5_const_principal server, xrealmauthz_moddata data,
+ const char **status_out)
+{
+ krb5_error_code ret;
+ char *cpstr = NULL, *spstr = NULL;
+ krb5_boolean is_allowed = FALSE;
+ krb5_db_entry *tgt_entry = NULL;
+
+ *status_out = NULL;
+
+ /* Check if the client realm is pre-approved. */
+ if (is_realm_preapproved(data, &client->realm))
+ return 0;
+
+ /* Get TGT principal entry for string attribute checks. */
+ ret = krb5_db_get_principal(context, tgtname, 0, &tgt_entry);
+ if (ret) {
+ *status_out = "XREALMAUTHZ_GET_TGT";
+ goto cleanup;
+ }
+
+ /* Check if client's realm is allowed. */
+ ret = check_realm_attr(context, tgt_entry, &client->realm, &is_allowed);
+ if (ret || is_allowed)
+ goto cleanup;
+
+ /* Check if client is allowed. */
+ ret = check_princ_attr(context, tgt_entry, client, &is_allowed);
+ if (ret || is_allowed)
+ goto cleanup;
+
+ if (data->enforcing) {
+ /* The authentication is denied. KDC logging of the error will include
+ * the client and server principal names. */
+ *status_out = "XREALMAUTHZ";
+ ret = KRB5KDC_ERR_POLICY;
+ k5_setmsg(context, ret, _("xrealmauthz module denied from %.*s"),
+ (int)tgtname->realm.length, tgtname->realm.data);
+ goto cleanup;
+ }
+
+ /* The authentication would be denied if enforcement were turned on.
+ * Generate a log message including the client and server names. */
+ ret = krb5_unparse_name(context, client, &cpstr);
+ if (ret)
+ goto cleanup;
+ ret = krb5_unparse_name(context, server, &spstr);
+ if (ret)
+ goto cleanup;
+ com_err("", 0, _("xrealmauthz module would deny %s for %s from %.*s"),
+ cpstr, spstr, (int)tgtname->realm.length, tgtname->realm.data);
+
+cleanup:
+ krb5_db_free_principal(context, tgt_entry);
+ krb5_free_unparsed_name(context, cpstr);
+ krb5_free_unparsed_name(context, spstr);
+ return ret;
+}
+
+static krb5_error_code
+xrealmauthz_check(krb5_context context, krb5_kdcpolicy_moddata moddata,
+ const krb5_kdc_req *request,
+ const struct _krb5_db_entry_new *server,
+ const krb5_ticket *ticket,
+ const char *const *auth_indicators, const char **status_out,
+ krb5_deltat *lifetime_out, krb5_deltat *renew_lifetime_out)
+{
+ xrealmauthz_moddata data = (xrealmauthz_moddata)moddata;
+
+ *status_out = NULL;
+ *lifetime_out = *renew_lifetime_out = 0;
+
+ /* Only check cross-realm requests. */
+ if (krb5_realm_compare(context, server->princ, ticket->enc_part2->client))
+ return 0;
+
+ /* Don't check if the header ticket isn't a TGT (such as for renewals). */
+ if (ticket->server->length != 2 ||
+ !data_eq_string(ticket->server->data[0], KRB5_TGS_NAME))
+ return 0;
+
+ return check_cross_realm_auth(context, ticket->enc_part2->client,
+ ticket->server, request->server, data,
+ status_out);
+}
+
+krb5_error_code
+kdcpolicy_xrealmauthz_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+krb5_error_code
+kdcpolicy_xrealmauthz_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_kdcpolicy_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+
+ vt = (krb5_kdcpolicy_vtable)vtable;
+ vt->name = "xrealmauthz";
+ vt->init = xrealmauthz_init;
+ vt->fini = xrealmauthz_fini;
+ vt->check_tgs = xrealmauthz_check;
+ return 0;
+}
diff --git a/src/plugins/kdcpolicy/xrealmauthz/xrealmauthz.exports b/src/plugins/kdcpolicy/xrealmauthz/xrealmauthz.exports
new file mode 100644
index 0000000000..a5794afdbd
--- /dev/null
+++ b/src/plugins/kdcpolicy/xrealmauthz/xrealmauthz.exports
@@ -0,0 +1 @@
+kdcpolicy_xrealmauthz_initvt
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index e7cf64e086..098b6a9f24 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -191,6 +191,7 @@ check-pytests: responder s2p s4u2proxy unlockiter s4u2self
$(RUNPYTEST) $(srcdir)/t_u2u.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_kdcoptions.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_replay.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_xrealmauthz.py $(PYTESTFLAGS)
clean:
$(RM) adata conccache etinfo forward gcred hist hooks hrealm
diff --git a/src/tests/t_xrealmauthz.py b/src/tests/t_xrealmauthz.py
new file mode 100644
index 0000000000..3b3921f036
--- /dev/null
+++ b/src/tests/t_xrealmauthz.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+
+from k5test import *
+import os
+
+# Define realm names for testing topology.
+REALM1 = 'REALM1.COM'
+REALM2 = 'REALM2.COM'
+REALM3 = 'REALM3.COM'
+
+# Name the cross-realm TGS for incoming authentications as seen by REALM1.
+cross_tgt_name = 'krbtgt/REALM1.COM@REALM2.COM'
+
+# Define capaths configuration to allow authentication from REALM3 via REALM2.
+capaths_config = {
+ 'capaths': {
+ REALM3: {REALM1: [REALM2]}, # REALM3 -> REALM2 -> REALM1
+ REALM2: {REALM1: '.'} # Direct path from REALM2 to REALM1
+ }
+}
+
+# Restart realm's KDC with xrealmauthz_enforcing set to true, false,
+# or not set at all if enforcing is None. Clear the log and look for
+# the expected startup message.
+def set_enforcing_mode(realm, enforcing):
+ if enforcing is None:
+ kdc_conf = {}
+ else:
+ kdc_conf = {'kdcdefaults': {'xrealmauthz_enforcing': str(enforcing)}}
+ expected_msg = 'enabled' if enforcing else 'disabled'
+
+ realm.stop_kdc()
+ realm_env = realm.special_env('enforce_config', True, kdc_conf=kdc_conf)
+
+ # Clear the KDC log before starting.
+ kdc_log = os.path.join(realm.testdir, 'kdc.log')
+ with open(kdc_log, 'w') as f:
+ f.truncate(0)
+
+ realm.start_kdc(env=realm_env)
+
+ # Check for module initialization message.
+ with open(kdc_log, 'r') as f:
+ log_content = f.read()
+ expected_init_msg = 'loaded (enforcing mode: %s,' % expected_msg
+ if expected_init_msg not in log_content:
+ fail('could not find module init log message')
+
+
+# Return true if a "would deny" message is present in the KDC log file.
+def check_would_deny_log(realm):
+ kdc_log = os.path.join(realm.testdir, 'kdc.log')
+ with open(kdc_log, 'r') as f:
+ log_content = f.read()
+ return 'would deny' in log_content
+
+
+# Clear the KDC log file.
+def clear_kdc_log(realm):
+ kdc_log = os.path.join(realm.testdir, 'kdc.log')
+ with open(kdc_log, 'w') as f:
+ f.truncate(0)
+
+
+# Return a descriptive string for an enforcing mode.
+def enforcing_str(enforcing):
+ if enforcing is None:
+ return 'default mode'
+ elif enforcing:
+ return 'enforcing explicitly enabled'
+ else:
+ return 'enforcing explicitly disabled'
+
+
+# Test unauthorized cross-realm access with the given enforcing mode.
+def test_denied(src_realm, dst_realm, client_princ, service_princ,
+ enforcing=None):
+ src_realm.kinit(client_princ, password('user'))
+ if enforcing is False:
+ clear_kdc_log(dst_realm)
+ src_realm.run([kvno, service_princ])
+ if not check_would_deny_log(dst_realm):
+ fail('Expected "would deny" message in KDC log')
+ else:
+ # Both enforcing=True and enforcing=None should enforce.
+ src_realm.run([kvno, service_princ], expected_code=1,
+ expected_msg='KDC policy rejects request')
+
+
+# Verify that access is allowed when properly authorized.
+def test_allowed(src_realm, client_princ, service_princ):
+ src_realm.kinit(client_princ, password('user'))
+ src_realm.run([kvno, service_princ])
+
+
+# Test realm-based authorization with direct trust.
+def test_direct_realm_authz(r1, r2, enforcing=None):
+ mark('direct realm authorization (%s)' % enforcing_str(enforcing))
+
+ # Verify that access is denied without authorization.
+ test_denied(r2, r1, r2.user_princ, r1.host_princ, enforcing)
+
+ # Add realm authorization and verify that access is allowed.
+ r1.run([kadminl, 'setstr', cross_tgt_name, 'xr:@' + r2.realm, '""'])
+ test_allowed(r2, r2.user_princ, r1.host_princ)
+
+ # Remove authorization and verify denial/logging again.
+ r1.run([kadminl, 'delstr', cross_tgt_name, 'xr:@' + r2.realm])
+ test_denied(r2, r1, r2.user_princ, r1.host_princ, enforcing)
+
+
+# Test principal-specific authorization with direct trust
+def test_direct_principal_authz(r1, r2, enforcing=None):
+ mark('direct princ authorization (%s)' % enforcing_str(enforcing))
+
+ # Create test principals.
+ authorized_princ = 'authz_test@' + r2.realm
+ unauthorized_princ = 'unauth_test@' + r2.realm
+ r2.addprinc(authorized_princ, password('user'))
+ r2.addprinc(unauthorized_princ, password('user'))
+
+ # Add principal authorization and verify that only
+ # authorized_princ has access.
+ r1.run([kadminl, 'setstr', cross_tgt_name, 'xr:authz_test', '""'])
+ test_allowed(r2, authorized_princ, r1.host_princ)
+ test_denied(r2, r1, unauthorized_princ, r1.host_princ, enforcing)
+
+ # Remove authorization and verify that authorized_princ is denied.
+ r1.run([kadminl, 'delstr', cross_tgt_name, 'xr:authz_test'])
+ test_denied(r2, r1, authorized_princ, r1.host_princ, enforcing)
+
+ # Clean up.
+ r2.run([kadminl, 'delprinc', '-force', authorized_princ])
+ r2.run([kadminl, 'delprinc', '-force', unauthorized_princ])
+
+
+# Test realm-based authorization with transitive trust.
+def test_transitive_realm_authz(r1, r2, r3, enforcing=None):
+ mark('transitive realm authorization (%s)' + enforcing_str(enforcing))
+
+ # Verify that access is denied/logged without authorization.
+ test_denied(r3, r1, r3.user_princ, r1.host_princ, enforcing)
+
+ # Add realm authorization and verify that access is allowed.
+ r1.run([kadminl, 'setstr', cross_tgt_name, 'xr:@' + r3.realm, '""'])
+ test_allowed(r3, r3.user_princ, r1.host_princ)
+
+ # Remove authorization and verify denial/logging again.
+ r1.run([kadminl, 'delstr', cross_tgt_name, 'xr:@' + r3.realm])
+ test_denied(r3, r1, r3.user_princ, r1.host_princ, enforcing)
+
+
+# Test principal-specific authorization with transitive trust.
+def test_transitive_principal_authz(r1, r2, r3, enforcing=None):
+ mark('transitive princ authorization (%s)' % enforcing_str(enforcing))
+
+ # Create test principals.
+ authorized_princ = 'authz_test@' + r3.realm
+ unauthorized_princ = 'unauth_test@' + r3.realm
+ r3.addprinc(authorized_princ, password('user'))
+ r3.addprinc(unauthorized_princ, password('user'))
+
+ # Add principal authorization and verify that only
+ # authorized_princ has access.
+ r1.run([kadminl, 'setstr', cross_tgt_name, 'xr:' + authorized_princ, '""'])
+ test_allowed(r3, authorized_princ, r1.host_princ)
+ test_denied(r3, r1, unauthorized_princ, r1.host_princ, enforcing)
+
+ # Remove authorization and verify that authorized_princ is denied.
+ r1.run([kadminl, 'delstr', cross_tgt_name, 'xr:' + authorized_princ])
+ test_denied(r3, r1, authorized_princ, r1.host_princ, enforcing)
+
+ # Clean up.
+ r3.run([kadminl, 'delprinc', '-force', authorized_princ])
+ r3.run([kadminl, 'delprinc', '-force', unauthorized_princ])
+
+
+# Test pre-approved realms configuration.
+def test_allowed_realms(r1, r2, r3, enforcing=None):
+ mark('pre-approved realms (%s)' % enforcing_str(enforcing))
+
+ # Configure a single allowed realm.
+ conf = {'kdcdefaults': {'xrealmauthz_allowed_realms': [REALM2]}}
+ if enforcing is not None:
+ conf['kdcdefaults']['xrealmauthz_enforcing'] = str(enforcing)
+ r1.stop_kdc()
+ realm_env = r1.special_env('allowed_realms', True, kdc_conf=conf)
+ r1.start_kdc(env=realm_env)
+
+ # Verify that REALM2 has full access, but REALM3 still goes
+ # through normal authorization and is denied.
+ test_allowed(r2, r2.user_princ, r1.host_princ)
+ test_denied(r3, r1, r3.user_princ, r1.host_princ, enforcing)
+
+ # Configure multiple allowed realms.
+ conf = {'kdcdefaults': {'xrealmauthz_allowed_realms': [REALM2, REALM3]}}
+ if enforcing is not None:
+ conf['kdcdefaults']['xrealmauthz_enforcing'] = str(enforcing)
+ r1.stop_kdc()
+ realm_env = r1.special_env('multi_allowed', True, kdc_conf=conf)
+ r1.start_kdc(env=realm_env)
+
+ # Verify that both realms have full access.
+ test_allowed(r2, r2.user_princ, r1.host_princ)
+ test_allowed(r3, r3.user_princ, r1.host_princ)
+
+
+# Configure realm1 with the xrealmauthz module enabled.
+plugin_path = os.path.join(buildtop, 'plugins', 'kdcpolicy', 'xrealmauthz',
+ 'xrealmauthz.so')
+realm1_kdc_conf = {'plugins': {'kdcpolicy':
+ {'module': 'xrealmauthz:' + plugin_path}}}
+
+# Set up three realms for all tests.
+# REALM1 <- REALM2 <- REALM3 for transitive tests
+# REALM1 <- REALM2 direct trust is used for direct tests
+mark('creating realms')
+realms = cross_realms(3, xtgts=((1, 0), (2, 1)),
+ args=({'realm': REALM1, 'krb5_conf': capaths_config,
+ 'kdc_conf': realm1_kdc_conf},
+ {'realm': REALM2, 'krb5_conf': capaths_config},
+ {'realm': REALM3, 'krb5_conf': capaths_config}))
+r1, r2, r3 = realms
+
+test_direct_realm_authz(r1, r2)
+test_direct_principal_authz(r1, r2)
+test_transitive_realm_authz(r1, r2, r3)
+test_transitive_principal_authz(r1, r2, r3)
+
+test_allowed_realms(r1, r2, r3)
+test_allowed_realms(r1, r2, r3, enforcing=True)
+test_allowed_realms(r1, r2, r3, enforcing=False)
+
+set_enforcing_mode(r1, True)
+test_direct_realm_authz(r1, r2, enforcing=True)
+test_direct_principal_authz(r1, r2, enforcing=True)
+test_transitive_realm_authz(r1, r2, r3, enforcing=True)
+test_transitive_principal_authz(r1, r2, r3, enforcing=True)
+
+set_enforcing_mode(r1, False)
+test_direct_realm_authz(r1, r2, enforcing=False)
+test_direct_principal_authz(r1, r2, enforcing=False)
+test_transitive_realm_authz(r1, r2, r3, enforcing=False)
+test_transitive_principal_authz(r1, r2, r3, enforcing=False)
+
+success('Cross-realm authorization tests completed successfully')
--
2.51.1

View File

@ -1,4 +1,4 @@
From 38074663f9c4d2f5f561f253bd8f7d29120513cc Mon Sep 17 00:00:00 2001
From dbc1ed5c91cab47875eccfb15f9a2512b7bdaf9c Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Wed, 21 Jan 2026 11:31:39 +0100
Subject: [PATCH] Fix uninitialized pointer dereference in libkrad

View File

@ -0,0 +1,65 @@
From 12b0c21c670f6cdfb94385c19fa2b6425f6a9d9e Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Thu, 29 Jan 2026 19:05:01 +0100
Subject: [PATCH] [downstream] Install xrealmauthz like other plugins
The xrealmauthz kdcpolicy plugin was merged upstream, but it is not
installed my the Makefile. The commit adds additional configuration to
install it in the krb5 modules directory when running "make install".
(cherry picked from commit ee30d1a9193219ce3ba3edc4b84c3db72740af32)
---
src/Makefile.in | 2 +-
src/config/pre.in | 1 +
src/plugins/kdcpolicy/xrealmauthz/Makefile.in | 4 +++-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/Makefile.in b/src/Makefile.in
index 946a3b7212..b95930d97c 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -70,7 +70,7 @@ INSTALLMKDIRS = $(KRB5ROOT) $(KRB5MANROOT) $(KRB5OTHERMKDIRS) \
$(FILE_CATDIR) $(OVERVIEW_CATDIR) \
$(KRB5_LIBDIR) $(KRB5_INCDIR) \
$(KRB5_DB_MODULE_DIR) $(KRB5_PA_MODULE_DIR) \
- $(KRB5_AD_MODULE_DIR) \
+ $(KRB5_AD_MODULE_DIR) $(KRB5_KP_MODULE_DIR) \
$(KRB5_LIBKRB5_MODULE_DIR) $(KRB5_TLS_MODULE_DIR) \
$(localstatedir) $(localstatedir)/krb5kdc \
$(runstatedir) $(runstatedir)/krb5kdc \
diff --git a/src/config/pre.in b/src/config/pre.in
index e9ae71471e..e3f667fed4 100644
--- a/src/config/pre.in
+++ b/src/config/pre.in
@@ -219,6 +219,7 @@ MODULE_DIR = @libdir@/krb5/plugins
KRB5_DB_MODULE_DIR = $(MODULE_DIR)/kdb
KRB5_PA_MODULE_DIR = $(MODULE_DIR)/preauth
KRB5_AD_MODULE_DIR = $(MODULE_DIR)/authdata
+KRB5_KP_MODULE_DIR = $(MODULE_DIR)/kdcpolicy
KRB5_LIBKRB5_MODULE_DIR = $(MODULE_DIR)/libkrb5
KRB5_TLS_MODULE_DIR = $(MODULE_DIR)/tls
KRB5_LOCALEDIR = @localedir@
diff --git a/src/plugins/kdcpolicy/xrealmauthz/Makefile.in b/src/plugins/kdcpolicy/xrealmauthz/Makefile.in
index 78346d6572..b6740210a7 100644
--- a/src/plugins/kdcpolicy/xrealmauthz/Makefile.in
+++ b/src/plugins/kdcpolicy/xrealmauthz/Makefile.in
@@ -1,5 +1,7 @@
mydir=plugins$(S)kdcpolicy$(S)xrealmauthz
BUILDTOP=$(REL)..$(S)..$(S)..
+KRB5_KP_MODULE_DIR = $(MODULE_DIR)/kdcpolicy
+MODULE_INSTALL_DIR = $(KRB5_KP_MODULE_DIR)
LIBBASE=xrealmauthz
LIBMAJOR=0
@@ -12,7 +14,7 @@ STLIBOBJS=main.o
SRCS=$(srcdir)/main.c
all-unix: all-libs
-install-unix:
+install-unix: install-libs
clean-unix:: clean-libs clean-libobjs
@libnover_frag@
@libobj_frag@
--
2.51.1

View File

@ -0,0 +1,65 @@
From 9ec162cb3096bf1c45f7d8cf8bb553c9ea3d910a Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 8 Apr 2026 17:57:59 -0400
Subject: [PATCH] Fix two NegoEx parsing vulnerabilities
In parse_nego_message(), check the result of the second call to
vector_base() before dereferencing it. In parse_message(), check for
a short header_len to prevent an integer underflow when calculating
the remaining message length.
Reported by Cem Onat Karagun.
CVE-2026-40355:
In MIT krb5 release 1.18 and later, if an application calls
gss_accept_sec_context() on a system with a NegoEx mechanism
registered in /etc/gss/mech, an unauthenticated remote attacker can
trigger a null pointer dereference, causing the process to terminate.
CVE-2026-40356:
In MIT krb5 release 1.18 and later, if an application calls
gss_accept_sec_context() on a system with a NegoEx mechanism
registered in /etc/gss/mech, an unauthenticated remote attacker can
trigger a read overrun of up to 52 bytes, possibly causing the process
to terminate. Exfiltration of the bytes read does not appear
possible.
ticket: 9205 (new)
tags: pullup
target_version: 1.22-next
(cherry picked from commit 2e75f0d9362fb979f5fc92829431a590a130929f)
---
src/lib/gssapi/spnego/negoex_util.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/lib/gssapi/spnego/negoex_util.c b/src/lib/gssapi/spnego/negoex_util.c
index edc5462e84..a65238e573 100644
--- a/src/lib/gssapi/spnego/negoex_util.c
+++ b/src/lib/gssapi/spnego/negoex_util.c
@@ -253,6 +253,10 @@ parse_nego_message(OM_uint32 *minor, struct k5input *in,
offset = k5_input_get_uint32_le(in);
count = k5_input_get_uint16_le(in);
p = vector_base(offset, count, EXTENSION_LENGTH, msg_base, msg_len);
+ if (p == NULL) {
+ *minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
for (i = 0; i < count; i++) {
extension_type = load_32_le(p + i * EXTENSION_LENGTH);
if (extension_type & EXTENSION_FLAG_CRITICAL) {
@@ -391,7 +395,8 @@ parse_message(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, struct k5input *in,
msg_len = k5_input_get_uint32_le(in);
conv_id = k5_input_get_bytes(in, GUID_LENGTH);
- if (in->status || msg_len > token_remaining || header_len > msg_len) {
+ if (in->status || msg_len > token_remaining ||
+ header_len < (size_t)(in->ptr - msg_base) || header_len > msg_len) {
*minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
return GSS_S_DEFECTIVE_TOKEN;
}
--
2.53.0

View File

@ -10,7 +10,7 @@
#
# baserelease is what we have standardized across Fedora and what
# rpmdev-bumpspec knows how to handle.
%global baserelease 9
%global baserelease 10
# This should be e.g. beta1 or %%nil
%global pre_release %nil
@ -97,7 +97,11 @@ Patch0035: 0035-Don-t-issue-session-keys-with-deprecated-enctypes.patch
Patch0036: 0036-downstream-Remove-3des-support-cumulative-1.patch
Patch0037: 0037-Add-PKINIT-paChecksum2-from-MS-PKCA-v20230920.patch
Patch0038: 0038-downstream-Do-not-block-HMAC-MD4-5-in-FIPS-mode.patch
Patch0039: 0039-Fix-uninitialized-pointer-dereference-in-libkrad.patch
Patch0039: 0039-Improve-ulog-block-resize-efficiency.patch
Patch0040: 0040-Add-xrealmauthz-KDC-policy-module-and-tests.patch
Patch0041: 0041-Fix-uninitialized-pointer-dereference-in-libkrad.patch
Patch0042: 0042-downstream-Install-xrealmauthz-like-other-plugins.patch
Patch0043: 0043-Fix-two-NegoEx-parsing-vulnerabilities.patch
License: Brian-Gladman-2-Clause AND BSD-2-Clause AND (BSD-2-Clause OR GPL-2.0-or-later) AND BSD-2-Clause-first-lines AND BSD-3-Clause AND BSD-4-Clause AND CMU-Mach-nodoc AND FSFULLRWD AND HPND AND HPND-export2-US AND HPND-export-US AND HPND-export-US-acknowledgement AND HPND-export-US-modify AND ISC AND MIT AND MIT-CMU AND OLDAP-2.8 AND OpenVision
URL: https://web.mit.edu/kerberos/www/
@ -234,6 +238,17 @@ package contains the PKINIT plugin, which allows clients
to obtain initial credentials from a KDC using a private key and a
certificate.
%package xrealmauthz
Summary: Xrealmauthz policy module for Kerberos 5 KDC
Group: System Environment/Libraries
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
%description xrealmauthz
Kerberos is a network authentication system. The krb5-xrealmauthz
package contains the xrealmauthz KDC plugin, which allows to configure
access rules to local realm for client principals from direct or
transitive cross-realms.
%package -n libkadm5
Summary: Kerberos 5 Administrative libraries
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
@ -710,6 +725,12 @@ exit 0
%dir %{_libdir}/krb5/plugins/preauth
%{_libdir}/krb5/plugins/preauth/pkinit.so
%files xrealmauthz
%dir %{_libdir}/krb5
%dir %{_libdir}/krb5/plugins
%dir %{_libdir}/krb5/plugins/kdcpolicy
%{_libdir}/krb5/plugins/kdcpolicy/xrealmauthz.so
%files devel
%docdir %{_mandir}
@ -739,9 +760,17 @@ exit 0
%{_datarootdir}/%{name}-tests/%{_arch}
%changelog
* Thu Feb 19 2026 Julien Rische <jrische@redhat.com> - 1.21.3-9
* Wed Apr 29 2026 Julien Rische <jrische@redhat.com> - 1.21.3-10
- Fix NegoEx parsing vulnerabilities (CVE-2026-40355, CVE-2026-40356)
Resolves: RHEL-171588 RHEL-171597
* Fri Jan 30 2026 Julien Rische <jrische@redhat.com> - 1.21.3-9
- krad: packet ID fetched from uninitialized variable
Resolves: RHEL-150954
Resolves: RHEL-145356
- Create sub-package for xrealmauthz KDC plugin
Resolves: RHEL-145358
- Improving kerberos ulog resize efficiency
Resolves: RHEL-145397
* Mon Apr 28 2025 Julien Rische <jrische@redhat.com> - 1.21.3-8
- Do not block HMAC-MD4/5 in FIPS mode