Apply multiarch rules when loading plugins too.

This commit is contained in:
Todd C. Miller
2022-10-06 12:46:38 -06:00
parent 0b506a2d07
commit 7e20e4b80f
8 changed files with 360 additions and 61 deletions

View File

@@ -253,6 +253,7 @@ lib/util/mksiglist.c
lib/util/mksigname.c
lib/util/mktemp.c
lib/util/mmap_alloc.c
lib/util/multiarch.c
lib/util/nanosleep.c
lib/util/openat.c
lib/util/parseln.c
@@ -280,6 +281,7 @@ lib/util/regress/glob/globtest.c
lib/util/regress/glob/globtest.in
lib/util/regress/harness.in
lib/util/regress/mktemp/mktemp_test.c
lib/util/regress/multiarch/multiarch_test.c
lib/util/regress/open_parent_dir/open_parent_dir_test.c
lib/util/regress/parse_gids/parse_gids_test.c
lib/util/regress/progname/progname_test.c

View File

@@ -262,6 +262,10 @@ sudo_dso_public void sudo_mmap_free_v1(void *ptr);
sudo_dso_public int sudo_mmap_protect_v1(void *ptr);
#define sudo_mmap_protect(_a) sudo_mmap_protect_v1(_a)
/* multiarch.c */
sudo_dso_public char *sudo_stat_multiarch_v1(const char *path, struct stat *sb);
#define sudo_stat_multiarch(_a, _b) sudo_stat_multiarch_v1((_a), (_b))
/* parseln.c */
sudo_dso_public ssize_t sudo_parseln_v1(char **buf, size_t *bufsize, unsigned int *lineno, FILE *fp);
sudo_dso_public ssize_t sudo_parseln_v2(char **buf, size_t *bufsize, unsigned int *lineno, FILE *fp, int flags);

View File

@@ -111,8 +111,8 @@ PVS_IGNORE = 'V707,V011,V002,V536'
PVS_LOG_OPTS = -a 'GA:1,2' -e -t errorfile -d $(PVS_IGNORE)
# Regression tests
TEST_PROGS = conf_test hltq_test parseln_test progname_test \
parse_gids_test getgids getgrouplist_test open_parent_dir_test \
TEST_PROGS = conf_test hltq_test parseln_test progname_test parse_gids_test \
getgids getgrouplist_test multiarch_test open_parent_dir_test \
strsplit_test strtobool_test strtoid_test strtomode_test \
strtonum_test uuid_test @COMPAT_TEST_PROGS@
TEST_LIBS = @LIBS@
@@ -144,11 +144,11 @@ SHELL = @SHELL@
LTOBJS = basename.lo @DIGEST@ event.lo fatal.lo key_val.lo gethostname.lo \
gettime.lo getgrouplist.lo gidlist.lo json.lo lbuf.lo locking.lo \
logfac.lo logpri.lo mkdir_parents.lo mmap_alloc.lo parseln.lo \
progname.lo rcstr.lo regex.lo roundup.lo secure_path.lo setgroups.lo \
strsplit.lo strtobool.lo strtoid.lo strtomode.lo strtonum.lo \
sudo_conf.lo sudo_debug.lo sudo_dso.lo term.lo ttyname_dev.lo \
ttysize.lo uuid.lo @COMMON_OBJS@ @LTLIBOBJS@
logfac.lo logpri.lo mkdir_parents.lo mmap_alloc.lo multiarch.lo \
parseln.lo progname.lo rcstr.lo regex.lo roundup.lo secure_path.lo \
setgroups.lo strsplit.lo strtobool.lo strtoid.lo strtomode.lo \
strtonum.lo sudo_conf.lo sudo_debug.lo sudo_dso.lo term.lo \
ttyname_dev.lo ttysize.lo uuid.lo @COMMON_OBJS@ @LTLIBOBJS@
IOBJS = $(LTOBJS:.lo=.i)
@@ -172,6 +172,8 @@ GLOBTEST_OBJS = globtest.lo glob.lo
GETDELIM_TEST_OBJS = getdelim_test.lo getdelim.lo
MULTIARCH_TEST_OBJS = multiarch_test.lo multiarch.lo
OPEN_PARENT_DIR_TEST_OBJS = open_parent_dir_test.lo mkdir_parents.lo
STRTOBOOL_TEST_OBJS = strtobool_test.lo strtobool.lo
@@ -285,6 +287,9 @@ hltq_test: $(HLTQ_TEST_OBJS) libsudo_util.la
mktemp_test: $(MKTEMP_TEST_OBJS) libsudo_util.la
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(MKTEMP_TEST_OBJS) libsudo_util.la $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(HARDENING_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS)
multiarch_test: $(MULTIARCH_TEST_OBJS) libsudo_util.la
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(MULTIARCH_TEST_OBJS) libsudo_util.la $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(HARDENING_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS)
open_parent_dir_test: $(OPEN_PARENT_DIR_TEST_OBJS) libsudo_util.la
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(OPEN_PARENT_DIR_TEST_OBJS) libsudo_util.la $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(HARDENING_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS)
@@ -460,6 +465,7 @@ check: $(TEST_PROGS) check-fuzzer
./strsig_test || rval=`expr $$rval + $$?`; \
fi; \
./getgrouplist_test || rval=`expr $$rval + $$?`; \
./multiarch_test || rval=`expr $$rval + $$?`; \
./open_parent_dir_test || rval=`expr $$rval + $$?`; \
./strtobool_test || rval=`expr $$rval + $$?`; \
./strtoid_test || rval=`expr $$rval + $$?`; \
@@ -1111,6 +1117,28 @@ mmap_alloc.i: $(srcdir)/mmap_alloc.c $(incdir)/compat/stdbool.h \
$(CC) -E -o $@ $(CPPFLAGS) $<
mmap_alloc.plog: mmap_alloc.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/mmap_alloc.c --i-file $< --output-file $@
multiarch.lo: $(srcdir)/multiarch.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.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)/multiarch.c
multiarch.i: $(srcdir)/multiarch.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(CC) -E -o $@ $(CPPFLAGS) $<
multiarch.plog: multiarch.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/multiarch.c --i-file $< --output-file $@
multiarch_test.lo: $(srcdir)/regress/multiarch/multiarch_test.c \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_plugin.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)/regress/multiarch/multiarch_test.c
multiarch_test.i: $(srcdir)/regress/multiarch/multiarch_test.c \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_util.h $(top_builddir)/config.h
$(CC) -E -o $@ $(CPPFLAGS) $<
multiarch_test.plog: multiarch_test.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/multiarch/multiarch_test.c --i-file $< --output-file $@
nanosleep.lo: $(srcdir)/nanosleep.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
@@ -1527,11 +1555,13 @@ sudo_debug.i: $(srcdir)/sudo_debug.c $(incdir)/compat/stdbool.h \
$(CC) -E -o $@ $(CPPFLAGS) $<
sudo_debug.plog: sudo_debug.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/sudo_debug.c --i-file $< --output-file $@
sudo_dso.lo: $(srcdir)/sudo_dso.c $(incdir)/sudo_compat.h $(incdir)/sudo_dso.h \
$(top_builddir)/config.h
sudo_dso.lo: $(srcdir)/sudo_dso.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_dso.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)/sudo_dso.c
sudo_dso.i: $(srcdir)/sudo_dso.c $(incdir)/sudo_compat.h $(incdir)/sudo_dso.h \
$(top_builddir)/config.h
sudo_dso.i: $(srcdir)/sudo_dso.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_dso.h \
$(incdir)/sudo_util.h $(top_builddir)/config.h
$(CC) -E -o $@ $(CPPFLAGS) $<
sudo_dso.plog: sudo_dso.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/sudo_dso.c --i-file $< --output-file $@

103
lib/util/multiarch.c Normal file
View File

@@ -0,0 +1,103 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2022 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.
*/
/*
* This is an open source non-commercial project. Dear PVS-Studio, please check it.
* PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
*/
#include <config.h>
#ifdef __linux__
# include <sys/utsname.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sudo_compat.h"
#include "sudo_util.h"
# if defined(__linux__)
/*
* On Linux systems that use muti-arch, the actual DSO may be in a
* machine-specific subdirectory. If the specified path contains
* /lib/ or /libexec/, insert a multi-arch directory after it.
* If sb is non-NULL, stat(2) will be called on the new path, filling in sb.
* Returns a dynamically allocated string on success and NULL on failure.
*/
char *
sudo_stat_multiarch_v1(const char *path, struct stat *sb)
{
# if defined(__ILP32__)
const char *libdirs[] = { "/libx32/", "/lib/", "/libexec/", NULL };
# elif defined(__LP64__)
const char *libdirs[] = { "/lib64/", "/lib/", "/libexec/", NULL };
# else
const char *libdirs[] = { "/lib32/", "/lib/", "/libexec/", NULL };
# endif
const char **lp, *lib, *slash;
struct utsname unamebuf;
char *newpath = NULL;
int len;
if (uname(&unamebuf) == -1)
return NULL;
for (lp = libdirs; *lp != NULL; lp++) {
/* Replace lib64, lib32, libx32 with lib in new path. */
const char *newlib = lp == libdirs ? "/lib/" : *lp;
/* Search for lib dir in path, find the trailing slash. */
lib = strstr(path, *lp);
if (lib == NULL)
continue;
slash = lib + strlen(*lp) - 1;
/* Make sure there isn't already a machine-linux-gnu dir. */
len = strcspn(slash + 1, "/-");
if (strncmp(slash + 1 + len, "-linux-gnu/", 11) == 0) {
/* Multiarch already present. */
break;
}
/* Add machine-linux-gnu dir after /lib/ or /libexec/. */
len = asprintf(&newpath, "%.*s%s%s-linux-gnu%s",
(int)(lib - path), path, newlib, unamebuf.machine, slash);
if (len == -1) {
newpath = NULL;
break;
}
/* If sb was set, use stat(2) to make sure newpath exists. */
if (sb == NULL || stat(newpath, sb) == 0)
break;
free(newpath);
newpath = NULL;
}
return newpath;
}
#else
char *
sudo_stat_multiarch_v1(const char *path, struct stat *sb)
{
return NULL;
}
#endif /* __linux__ */

View File

@@ -0,0 +1,178 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2022 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>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SUDO_ERROR_WRAP 0
#include "sudo_compat.h"
#include "sudo_fatal.h"
#include "sudo_util.h"
sudo_dso_public int main(int argc, char *argv[]);
#ifdef __linux__
# include <sys/utsname.h>
# if defined(__ILP32__)
# define ARCH_LIB "libx32"
# elif defined(__LP64__)
# define ARCH_LIB "lib64"
# else
# define ARCH_LIB "lib32"
# endif
struct multiarch_test {
const char *inpath;
char *outpath;
};
static struct multiarch_test *
make_test_data(void)
{
struct multiarch_test *test_data;
struct utsname unamebuf;
int i;
if (uname(&unamebuf) == -1)
return NULL;
test_data = calloc(7, sizeof(*test_data));
if (test_data == NULL)
return NULL;
test_data[0].inpath = "/usr/" ARCH_LIB "/libfoo.so";
i = asprintf(&test_data[0].outpath, "/usr/lib/%s-linux-gnu/libfoo.so",
unamebuf.machine);
if (i == -1) {
test_data[0].outpath = NULL;
goto bad;
}
test_data[1].inpath = "/usr/lib/something.so";
i = asprintf(&test_data[1].outpath, "/usr/lib/%s-linux-gnu/something.so",
unamebuf.machine);
if (i == -1) {
test_data[1].outpath = NULL;
goto bad;
}
test_data[2].inpath = "/usr/libexec/libbar.so";
i = asprintf(&test_data[2].outpath, "/usr/libexec/%s-linux-gnu/libbar.so",
unamebuf.machine);
if (i == -1) {
test_data[2].outpath = NULL;
goto bad;
}
test_data[3].inpath = "/usr/local/lib/sudo/libsudo_util.so";
i = asprintf(&test_data[3].outpath, "/usr/local/lib/%s-linux-gnu/sudo/libsudo_util.so",
unamebuf.machine);
if (i == -1) {
test_data[3].outpath = NULL;
goto bad;
}
test_data[4].inpath = "/opt/sudo/lib/sudoers.so";
i = asprintf(&test_data[4].outpath, "/opt/sudo/lib/%s-linux-gnu/sudoers.so",
unamebuf.machine);
if (i == -1) {
test_data[4].outpath = NULL;
goto bad;
}
i = asprintf(&test_data[5].outpath, "/usr/lib/%s-linux-gnu/something.so",
unamebuf.machine);
if (i == -1) {
test_data[5].outpath = NULL;
goto bad;
}
test_data[5].inpath = test_data[5].outpath;
test_data[5].outpath = NULL;
return test_data;
bad:
for (i = 0; test_data[i].outpath != NULL; i++)
free(test_data[i].outpath);
free(test_data);
return NULL;
}
#endif /* __linux__ */
int
main(int argc, char *argv[])
{
int ch, errors = 0;
#ifdef __linux__
int ntests = 0;
struct multiarch_test *test_data;
#endif
initprogname(argc > 0 ? argv[0] : "multiarch_test");
while ((ch = getopt(argc, argv, "v")) != -1) {
switch (ch) {
case 'v':
/* ignore */
break;
default:
fprintf(stderr, "usage: %s [-v]\n", getprogname());
return EXIT_FAILURE;
}
}
argc -= optind;
argv += optind;
#ifdef __linux__
test_data = make_test_data();
if (test_data == NULL) {
sudo_warnx("%s", "failed to generate test data");
return EXIT_FAILURE;
}
for (ch = 0; test_data[ch].inpath != NULL; ch++) {
char *outpath = sudo_stat_multiarch(test_data[ch].inpath, NULL);
ntests++;
if (outpath == NULL) {
if (test_data[ch].outpath != NULL) {
sudo_warnx("%s: sudo_stat_multiarch failed",
test_data[ch].inpath);
errors++;
}
} else if (strcmp(outpath, test_data[ch].outpath) != 0) {
sudo_warnx("%s: expected %s got %s", test_data[ch].inpath,
test_data[ch].outpath, outpath);
errors++;
free(outpath);
}
}
if (ntests != 0) {
printf("%s: %d tests run, %d errors, %d%% success rate\n",
getprogname(), ntests, errors, (ntests - errors) * 100 / ntests);
}
#endif /* __linux__ */
return errors;
}

View File

@@ -39,6 +39,7 @@
#include "sudo_compat.h"
#include "sudo_dso.h"
#include "sudo_util.h"
/*
* Pointer for statically compiled symbols.
@@ -183,46 +184,26 @@ sudo_dso_strerror_v1(void)
static void *
dlopen_multi_arch(const char *path, int flags)
{
# if defined(__ILP32__)
const char *libdirs[] = { "/libx32/", "/lib/", "/libexec/", NULL };
# elif defined(__LP64__)
const char *libdirs[] = { "/lib64/", "/lib/", "/libexec/", NULL };
# else
const char *libdirs[] = { "/lib32/", "/lib/", "/libexec/", NULL };
# endif
const char **lp, *lib, *slash;
struct utsname unamebuf;
void *ret = NULL;
struct stat sb;
char *newpath;
int len;
/* Only try multi-arch if the original path does not exist. */
if (stat(path, &sb) == -1 && errno == ENOENT && uname(&unamebuf) == 0) {
for (lp = libdirs; *lp != NULL; lp++) {
/* Replace lib64, lib32, libx32 with lib in new path. */
const char *newlib = lp == libdirs ? "/lib/" : *lp;
/* Search for lib dir in path, find the trailing slash. */
lib = strstr(path, *lp);
if (lib == NULL)
continue;
slash = lib + strlen(*lp) - 1;
/* Add machine-linux-gnu dir after /lib/ or /libexec/. */
len = asprintf(&newpath, "%.*s%s%s-linux-gnu%s",
(int)(lib - path), path, newlib, unamebuf.machine, slash);
if (len == -1)
break;
if (stat(newpath, &sb) == 0)
if (stat(path, &sb) == -1 && errno == ENOENT) {
newpath = sudo_stat_multiarch(path, &sb);
if (newpath != NULL) {
ret = dlopen(newpath, flags);
free(newpath);
if (ret != NULL)
break;
}
}
return ret;
}
# else
static void *
dlopen_multi_arch(const char *path, int flags)
{
return NULL;
}
# endif /* __linux__ */
void *
@@ -278,11 +259,10 @@ sudo_dso_load_v1(const char *path, int mode)
ret = dlopen(path, flags);
}
}
#elif defined(__linux__)
# endif /* RTLD_MEMBER */
/* On failure, try again with a muti-arch path where possible. */
if (ret == NULL)
ret = dlopen_multi_arch(path, flags);
#endif /* RTLD_MEMBER */
return ret;
}

View File

@@ -131,6 +131,7 @@ sudo_secure_file_v1
sudo_secure_open_dir_v1
sudo_secure_open_file_v1
sudo_setgroups_v1
sudo_stat_multiarch_v1
sudo_str2logfac_v1
sudo_str2logpri_v1
sudo_strsplit_v1

View File

@@ -43,6 +43,7 @@ sudo_stat_plugin(struct plugin_info *info, char *fullpath,
size_t pathsize, struct stat *sb)
{
int status = -1;
size_t len;
debug_decl(sudo_stat_plugin, SUDO_DEBUG_PLUGIN);
if (info->path[0] == '/') {
@@ -52,8 +53,6 @@ sudo_stat_plugin(struct plugin_info *info, char *fullpath,
}
status = stat(fullpath, sb);
} else {
int len;
#ifdef STATIC_SUDOERS_PLUGIN
/* Check static symbols. */
if (strcmp(info->path, SUDOERS_PLUGIN) == 0) {
@@ -77,20 +76,22 @@ sudo_stat_plugin(struct plugin_info *info, char *fullpath,
len = snprintf(fullpath, pathsize, "%s%s", sudo_conf_plugin_dir_path(),
info->path);
if (len < 0 || (size_t)len >= pathsize) {
if (len >= pathsize) {
errno = ENAMETOOLONG;
goto done;
}
/* Try parent dir for compatibility with old plugindir default. */
if ((status = stat(fullpath, sb)) != 0) {
char *cp = strrchr(fullpath, '/');
if (cp > fullpath + 4 && cp[-5] == '/' && cp[-4] == 's' &&
cp[-3] == 'u' && cp[-2] == 'd' && cp[-1] == 'o') {
int serrno = errno;
strlcpy(cp - 4, info->path, pathsize - (cp - 4 - fullpath));
if ((status = stat(fullpath, sb)) != 0)
errno = serrno;
status = stat(fullpath, sb);
}
if (status == -1) {
char *newpath = sudo_stat_multiarch(fullpath, sb);
if (newpath != NULL) {
len = strlcpy(fullpath, newpath, pathsize);
free(newpath);
if (len >= pathsize) {
errno = ENAMETOOLONG;
goto done;
}
status = 0;
}
}
done: