import UBI krb5-1.21.3-10.el10_2
This commit is contained in:
parent
297f069b69
commit
fa695c3901
257
0039-Improve-ulog-block-resize-efficiency.patch
Normal file
257
0039-Improve-ulog-block-resize-efficiency.patch
Normal 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
|
||||
|
||||
760
0040-Add-xrealmauthz-KDC-policy-module-and-tests.patch
Normal file
760
0040-Add-xrealmauthz-KDC-policy-module-and-tests.patch
Normal 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
|
||||
|
||||
@ -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
|
||||
65
0042-downstream-Install-xrealmauthz-like-other-plugins.patch
Normal file
65
0042-downstream-Install-xrealmauthz-like-other-plugins.patch
Normal 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
|
||||
|
||||
65
0043-Fix-two-NegoEx-parsing-vulnerabilities.patch
Normal file
65
0043-Fix-two-NegoEx-parsing-vulnerabilities.patch
Normal 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
|
||||
|
||||
37
krb5.spec
37
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 <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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user