From a32936288e7c3403662b88cab67e0cd721282343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20L=C3=B3pez?= Date: Tue, 18 Aug 2026 13:34:34 +0200 Subject: [PATCH] - Resolves: RHEL-215573 - Use canonicalized path if user path contains ".." --- 0006-RHEL-215573-dot-dot.patch | 157 +++++++++++++++++++++++++++++++++ sudo.spec | 1 + 2 files changed, 158 insertions(+) create mode 100644 0006-RHEL-215573-dot-dot.patch diff --git a/0006-RHEL-215573-dot-dot.patch b/0006-RHEL-215573-dot-dot.patch new file mode 100644 index 0000000..5b9ed9b --- /dev/null +++ b/0006-RHEL-215573-dot-dot.patch @@ -0,0 +1,157 @@ +From f8163e5ea3ec204727c77cfd06fec788ad9679f4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alejandro=20L=C3=B3pez?= +Date: Tue, 7 Jul 2026 17:00:14 +0200 +Subject: [PATCH] RHEL-189002: dot_dot + +Minimal backport of commits 620ba7a3880d9934b349c28c9a670a8346a37c91 and +8443ab434e20f8003ca4bc11783462401722b987 +--- + include/sudo_util.h | 4 +++ + lib/util/Makefile.in | 20 +++++++++---- + lib/util/dotdot.c | 51 +++++++++++++++++++++++++++++++++ + lib/util/util.exp.in | 1 + + plugins/sudoers/match_command.c | 6 ++-- + 5 files changed, 75 insertions(+), 7 deletions(-) + create mode 100644 lib/util/dotdot.c + +diff --git a/include/sudo_util.h b/include/sudo_util.h +index 2da62d6..fdd81db 100644 +--- a/include/sudo_util.h ++++ b/include/sudo_util.h +@@ -182,6 +182,10 @@ sudo_dso_public int aix_setauthdb_v2(char *user, char *registry); + sudo_dso_public char *sudo_basename_v1(const char *filename); + #define sudo_basename(_a) sudo_basename_v1(_a) + ++/* dotdot.c */ ++sudo_dso_public bool sudo_contains_dot_dot_v1(const char *str); ++#define sudo_contains_dot_dot(_a) sudo_contains_dot_dot_v1(_a) ++ + /* gethostname.c */ + sudo_dso_public char *sudo_gethostname_v1(void); + #define sudo_gethostname() sudo_gethostname_v1() +diff --git a/lib/util/Makefile.in b/lib/util/Makefile.in +index 428a127..695ab8a 100644 +--- a/lib/util/Makefile.in ++++ b/lib/util/Makefile.in +@@ -145,7 +145,7 @@ DEVEL = @DEVEL@ + + SHELL = @SHELL@ + +-LTOBJS = basename.lo @DIGEST@ event.lo fatal.lo key_val.lo gethostname.lo \ ++LTOBJS = basename.lo @DIGEST@ dotdot.lo event.lo fatal.lo key_val.lo gethostname.lo \ + gettime.lo getgrouplist.lo gidlist.lo hexchar.lo json.lo lbuf.lo \ + locking.lo logfac.lo login_max.lo logpri.lo mkdir_parents.lo \ + mmap_alloc.lo multiarch.lo parseln.lo progname.lo rcstr.lo regex.lo \ +@@ -678,6 +678,16 @@ digest_test.i: $(srcdir)/regress/digest/digest_test.c \ + $(CPP) $(CPPFLAGS) $(srcdir)/regress/digest/digest_test.c > $@ + digest_test.plog: digest_test.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/digest/digest_test.c --i-file digest_test.i --output-file $@ ++dotdot.lo: $(srcdir)/dotdot.c $(incdir)/compat/stdbool.h \ ++ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ ++ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(top_builddir)/config.h ++ $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/dotdot.c ++dotdot.i: $(srcdir)/dotdot.c $(incdir)/compat/stdbool.h \ ++ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ ++ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(top_builddir)/config.h ++ $(CPP) $(CPPFLAGS) $(srcdir)/dotdot.c > $@ ++dotdot.plog: dotdot.i ++ rm -f $@; pvs-studio --cfg $(PVS_CFG) --source-file $(srcdir)/dotdot.c --i-file dotdot.i --output-file $@ + dup3.lo: $(srcdir)/dup3.c $(incdir)/sudo_compat.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/dup3.c + dup3.i: $(srcdir)/dup3.c $(incdir)/sudo_compat.h $(top_builddir)/config.h +diff --git a/lib/util/dotdot.c b/lib/util/dotdot.c +new file mode 100644 +index 0000000..66fd2bb +--- /dev/null ++++ b/lib/util/dotdot.c +@@ -0,0 +1,51 @@ ++/* ++ * SPDX-License-Identifier: ISC ++ * ++ * Copyright (c) 2025 Todd C. Miller ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include ++ ++#ifdef HAVE_STDBOOL_H ++# include ++#else ++# include ++#endif /* HAVE_STDBOOL_H */ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++bool ++sudo_contains_dot_dot_v1(const char *str) ++{ ++ const char *cp; ++ debug_decl(sudo_contains_dot_dot, SUDO_DEBUG_UTIL); ++ ++ for (cp = str; *cp != '\0'; cp++) { ++ /* Match ".." */ ++ if (cp[0] != '.' || cp[1] != '.') ++ continue; ++ ++ /* Match "^.." or "/.." then "../" or "..$" */ ++ if ((cp == str || cp[-1] == '/') && (cp[2] == '/' || cp[2] == '\0')) ++ debug_return_bool(true); ++ } ++ ++ debug_return_bool(false); ++} +diff --git a/lib/util/util.exp.in b/lib/util/util.exp.in +index 0363147..98af276 100644 +--- a/lib/util/util.exp.in ++++ b/lib/util/util.exp.in +@@ -17,6 +17,7 @@ sudo_conf_plugins_v1 + sudo_conf_probe_interfaces_v1 + sudo_conf_read_v1 + sudo_conf_sesh_path_v1 ++sudo_contains_dot_dot_v1 + sudo_debug_deregister_v1 + sudo_debug_enter_v1 + sudo_debug_execve2_v1 +diff --git a/plugins/sudoers/match_command.c b/plugins/sudoers/match_command.c +index dc4d244..b2d231e 100644 +--- a/plugins/sudoers/match_command.c ++++ b/plugins/sudoers/match_command.c +@@ -423,7 +423,8 @@ command_matches_fnmatch(struct sudoers_context *ctx, const char *sudoers_cmnd, + * We do not attempt to match a relative path unless there is a + * canonicalized version. + */ +- if (cmnd[0] != '/' || fnmatch(sudoers_cmnd, cmnd, FNM_PATHNAME) != 0) { ++ if (cmnd[0] != '/' || sudo_contains_dot_dot(cmnd) || ++ fnmatch(sudoers_cmnd, cmnd, FNM_PATHNAME) != 0) { + /* No match, retry using the canonicalized path (if possible). */ + if (ctx->user.cmnd_dir == NULL) + debug_return_int(DENY); +@@ -480,7 +481,8 @@ command_matches_regex(struct sudoers_context *ctx, const char *sudoers_cmnd, + * + * Neither sudoers_cmnd nor user_cmnd are relative to runchroot. + */ +- if (cmnd[0] != '/' || regex_matches(sudoers_cmnd, cmnd) != ALLOW) { ++ if (cmnd[0] != '/' || sudo_contains_dot_dot(cmnd) || ++ regex_matches(sudoers_cmnd, cmnd) != ALLOW) { + /* No match, retry using the canonicalized path (if possible). */ + if (ctx->user.cmnd_dir == NULL) + debug_return_int(DENY); +-- +2.54.0 + diff --git a/sudo.spec b/sudo.spec index f149eb6..23767e4 100644 --- a/sudo.spec +++ b/sudo.spec @@ -31,6 +31,7 @@ Patch2: 0002-sudo-conf.patch Patch3: 0003-rebuild_env-Avoid-setting-SHELL-twice-for-sudo-i.patch Patch4: 0004-cve-2026-35535.patch Patch5: 0005-exec_pty-Treat-a-socketpair-for-stdin-stdout-same-as.patch +Patch6: 0006-RHEL-215573-dot-dot.patch %description Sudo (superuser do) allows a system administrator to give certain