Commit 515294bb authored by Alan Coopersmith's avatar Alan Coopersmith
Browse files

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's avatarAlan Coopersmith <alan.coopersmith@oracle.com>
parent f80fa6ae
......@@ -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.
......@@ -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
......
......@@ -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 */
......
......@@ -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
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment