From fa695c390115bd094253742d89ad3cbac170503a Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Tue, 19 May 2026 19:33:24 -0400 Subject: [PATCH] import UBI krb5-1.21.3-10.el10_2 --- ...Improve-ulog-block-resize-efficiency.patch | 257 ++++++ ...almauthz-KDC-policy-module-and-tests.patch | 760 ++++++++++++++++++ ...lized-pointer-dereference-in-libkrad.patch | 2 +- ...stall-xrealmauthz-like-other-plugins.patch | 65 ++ ...x-two-NegoEx-parsing-vulnerabilities.patch | 65 ++ krb5.spec | 37 +- 6 files changed, 1181 insertions(+), 5 deletions(-) create mode 100644 0039-Improve-ulog-block-resize-efficiency.patch create mode 100644 0040-Add-xrealmauthz-KDC-policy-module-and-tests.patch rename 0039-Fix-uninitialized-pointer-dereference-in-libkrad.patch => 0041-Fix-uninitialized-pointer-dereference-in-libkrad.patch (96%) create mode 100644 0042-downstream-Install-xrealmauthz-like-other-plugins.patch create mode 100644 0043-Fix-two-NegoEx-parsing-vulnerabilities.patch diff --git a/0039-Improve-ulog-block-resize-efficiency.patch b/0039-Improve-ulog-block-resize-efficiency.patch new file mode 100644 index 0000000..06ae08d --- /dev/null +++ b/0039-Improve-ulog-block-resize-efficiency.patch @@ -0,0 +1,257 @@ +From 72b42b3b523cea1809616aed81158bf784ac35b9 Mon Sep 17 00:00:00 2001 +From: Zoltan Borbely +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 + diff --git a/0040-Add-xrealmauthz-KDC-policy-module-and-tests.patch b/0040-Add-xrealmauthz-KDC-policy-module-and-tests.patch new file mode 100644 index 0000000..02cff2c --- /dev/null +++ b/0040-Add-xrealmauthz-KDC-policy-module-and-tests.patch @@ -0,0 +1,760 @@ +From 55dcea9c5f2ffd80e396f0d47dd6e8f6efd4c944 Mon Sep 17 00:00:00 2001 +From: Dax Kelson +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 ++#include ++ ++/* 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 + diff --git a/0039-Fix-uninitialized-pointer-dereference-in-libkrad.patch b/0041-Fix-uninitialized-pointer-dereference-in-libkrad.patch similarity index 96% rename from 0039-Fix-uninitialized-pointer-dereference-in-libkrad.patch rename to 0041-Fix-uninitialized-pointer-dereference-in-libkrad.patch index 4f51028..83b1a72 100644 --- a/0039-Fix-uninitialized-pointer-dereference-in-libkrad.patch +++ b/0041-Fix-uninitialized-pointer-dereference-in-libkrad.patch @@ -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 Date: Wed, 21 Jan 2026 11:31:39 +0100 Subject: [PATCH] Fix uninitialized pointer dereference in libkrad diff --git a/0042-downstream-Install-xrealmauthz-like-other-plugins.patch b/0042-downstream-Install-xrealmauthz-like-other-plugins.patch new file mode 100644 index 0000000..14e3f67 --- /dev/null +++ b/0042-downstream-Install-xrealmauthz-like-other-plugins.patch @@ -0,0 +1,65 @@ +From 12b0c21c670f6cdfb94385c19fa2b6425f6a9d9e Mon Sep 17 00:00:00 2001 +From: Julien Rische +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 + diff --git a/0043-Fix-two-NegoEx-parsing-vulnerabilities.patch b/0043-Fix-two-NegoEx-parsing-vulnerabilities.patch new file mode 100644 index 0000000..2f200f7 --- /dev/null +++ b/0043-Fix-two-NegoEx-parsing-vulnerabilities.patch @@ -0,0 +1,65 @@ +From 9ec162cb3096bf1c45f7d8cf8bb553c9ea3d910a Mon Sep 17 00:00:00 2001 +From: Greg Hudson +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 + diff --git a/krb5.spec b/krb5.spec index 64178e2..5512312 100644 --- a/krb5.spec +++ b/krb5.spec @@ -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 - 1.21.3-9 +* Wed Apr 29 2026 Julien Rische - 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 - 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 - 1.21.3-8 - Do not block HMAC-MD4/5 in FIPS mode