158 lines
6.4 KiB
Diff
158 lines
6.4 KiB
Diff
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
|
|
|