summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Coopersmith <alan.coopersmith@oracle.com>2023-01-06 12:50:48 -0800
committerAlan Coopersmith <alan.coopersmith@oracle.com>2023-01-12 15:47:43 -0800
commit515294bb8023a45ff916696d0a14308ff4f3a376 (patch)
tree97c28ec16bd3a548f0ef5638e39d267ffa6c506a
parentf80fa6ae47ad4a5beacb287c0030c9913b046643 (diff)
Fix CVE-2022-4883: compression commands depend on $PATH
By default, on all platforms except MinGW, libXpm will detect if a filename ends in .Z or .gz, and will when reading such a file fork off an uncompress or gunzip command to read from via a pipe, and when writing such a file will fork off a compress or gzip command to write to via a pipe. In libXpm 3.5.14 or older these are run via execlp(), relying on $PATH to find the commands. If libXpm is called from a program running with raised privileges, such as via setuid, then a malicious user could set $PATH to include programs of their choosing to be run with those privileges. Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
-rw-r--r--README.md12
-rw-r--r--configure.ac14
-rw-r--r--src/RdFToI.c17
-rw-r--r--src/WrFFrI.c4
4 files changed, 42 insertions, 5 deletions
diff --git a/README.md b/README.md
index 7895350..30fdd8e 100644
--- a/README.md
+++ b/README.md
@@ -31,3 +31,15 @@ if it can't find the file it was asked to open. It relies on the
--enable-open-zfile feature to open the file, and is enabled by default
when --enable-open-zfile is enabled, and can be disabled by passing the
--disable-stat-zfile flag to the configure script.
+
+All of these commands will be executed with whatever userid & privileges the
+function is called with, relying on the caller to ensure the correct euid,
+egid, etc. are set before calling.
+
+To reduce risk, the paths to these commands are now set at configure time to
+the first version found in the PATH used to run configure, and do not depend
+on the PATH environment variable set at runtime.
+
+To specify paths to be used for these commands instead of searching $PATH, pass
+the XPM_PATH_COMPRESS, XPM_PATH_UNCOMPRESS, XPM_PATH_GZIP, and XPM_PATH_GUNZIP
+variables to the configure command.
diff --git a/configure.ac b/configure.ac
index 2f90a68..e6b6509 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,6 +49,14 @@ if test "x$USE_GETTEXT" = "xyes" ; then
fi
AM_CONDITIONAL(USE_GETTEXT, test "x$USE_GETTEXT" = "xyes")
+dnl Helper macro to find absolute path to program and add a #define for it
+AC_DEFUN([XPM_PATH_PROG],[
+AC_PATH_PROG([$1], [$2], [])
+AS_IF([test "x$$1" = "x"],
+ [AC_MSG_ERROR([$2 not found, set $1 or use --disable-stat-zfile])])
+AC_DEFINE_UNQUOTED([$1], ["$$1"], [Path to $2])
+]) dnl End of AC_DEFUN([XPM_PATH_PROG]...
+
# Optional feature: When a filename ending in .Z or .gz is requested,
# open a pipe to a newly forked compress/uncompress/gzip/gunzip command to
# handle it.
@@ -65,6 +73,12 @@ AC_MSG_RESULT([$OPEN_ZFILE])
AM_CONDITIONAL(COMPRESSED_PIXMAPS, test "x$OPEN_ZFILE" = "xyes")
if test x$OPEN_ZFILE = xno ; then
AC_DEFINE(NO_ZPIPE, 1, [Define to 1 to disable decompression via pipes])
+else
+ XPM_PATH_PROG([XPM_PATH_COMPRESS], [compress])
+ XPM_PATH_PROG([XPM_PATH_UNCOMPRESS], [uncompress])
+ XPM_PATH_PROG([XPM_PATH_GZIP], [gzip])
+ XPM_PATH_PROG([XPM_PATH_GUNZIP], [gunzip])
+ AC_CHECK_FUNCS([closefrom close_range], [break])
fi
# Optional feature: When ___.xpm is requested, also look for ___.xpm.Z & .gz
diff --git a/src/RdFToI.c b/src/RdFToI.c
index bd09611..a91d337 100644
--- a/src/RdFToI.c
+++ b/src/RdFToI.c
@@ -43,6 +43,7 @@
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
#else
#ifdef FOR_MSW
#include <fcntl.h>
@@ -161,7 +162,17 @@ xpmPipeThrough(
goto err;
if ( 0 == pid )
{
- execlp(cmd, cmd, arg1, (char *)NULL);
+#ifdef HAVE_CLOSEFROM
+ closefrom(3);
+#elif defined(HAVE_CLOSE_RANGE)
+# ifdef CLOSE_RANGE_UNSHARE
+# define close_range_flags CLOSE_RANGE_UNSHARE
+# else
+# define close_range_flags 0
+#endif
+ close_range(3, ~0U, close_range_flags);
+#endif
+ execl(cmd, cmd, arg1, (char *)NULL);
perror(cmd);
goto err;
}
@@ -235,12 +246,12 @@ OpenReadFile(
if ( ext && !strcmp(ext, ".Z") )
{
mdata->type = XPMPIPE;
- mdata->stream.file = xpmPipeThrough(fd, "uncompress", "-c", "r");
+ mdata->stream.file = xpmPipeThrough(fd, XPM_PATH_UNCOMPRESS, "-c", "r");
}
else if ( ext && !strcmp(ext, ".gz") )
{
mdata->type = XPMPIPE;
- mdata->stream.file = xpmPipeThrough(fd, "gunzip", "-qc", "r");
+ mdata->stream.file = xpmPipeThrough(fd, XPM_PATH_GUNZIP, "-qc", "r");
}
else
#endif /* z-files */
diff --git a/src/WrFFrI.c b/src/WrFFrI.c
index 328c987..d59098f 100644
--- a/src/WrFFrI.c
+++ b/src/WrFFrI.c
@@ -342,10 +342,10 @@ OpenWriteFile(
#ifndef NO_ZPIPE
len = strlen(filename);
if (len > 2 && !strcmp(".Z", filename + (len - 2))) {
- mdata->stream.file = xpmPipeThrough(fd, "compress", NULL, "w");
+ mdata->stream.file = xpmPipeThrough(fd, XPM_PATH_COMPRESS, NULL, "w");
mdata->type = XPMPIPE;
} else if (len > 3 && !strcmp(".gz", filename + (len - 3))) {
- mdata->stream.file = xpmPipeThrough(fd, "gzip", "-q", "w");
+ mdata->stream.file = xpmPipeThrough(fd, XPM_PATH_GZIP, "-q", "w");
mdata->type = XPMPIPE;
} else
#endif