- Resolves: RHEL-215573 - Use canonicalized path if user path contains ".."

This commit is contained in:
Alejandro López 2026-08-18 13:34:34 +02:00
parent 44d155ed75
commit a32936288e
2 changed files with 158 additions and 0 deletions

View File

@ -0,0 +1,157 @@
From f8163e5ea3ec204727c77cfd06fec788ad9679f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20L=C3=B3pez?= <allopez@redhat.com>
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 <Todd.Miller@sudo.ws>
+ *
+ * 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 <config.h>
+
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include <compat/stdbool.h>
+#endif /* HAVE_STDBOOL_H */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sudo_compat.h>
+#include <sudo_debug.h>
+#include <sudo_util.h>
+
+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

View File

@ -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