summaryrefslogtreecommitdiff
path: root/lib/util/sudo_dso.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/util/sudo_dso.c')
-rw-r--r--lib/util/sudo_dso.c58
1 files changed, 51 insertions, 7 deletions
diff --git a/lib/util/sudo_dso.c b/lib/util/sudo_dso.c
index 0c8a08ef2..8c680e034 100644
--- a/lib/util/sudo_dso.c
+++ b/lib/util/sudo_dso.c
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
- * Copyright (c) 2010, 2012-2014 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2010, 2012-2014, 2021-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
@@ -23,6 +23,10 @@
#include <config.h>
+#ifdef __linux__
+# include <sys/utsname.h>
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -35,6 +39,7 @@
#include "sudo_compat.h"
#include "sudo_dso.h"
+#include "sudo_util.h"
/*
* Pointer for statically compiled symbols.
@@ -169,15 +174,47 @@ sudo_dso_strerror_v1(void)
# endif
# endif
+# if defined(__linux__)
+/*
+ * On Linux systems that use multi-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.
+ */
+static void *
+dlopen_multi_arch(const char *path, int flags)
+{
+ void *ret = NULL;
+ struct stat sb;
+ char *newpath;
+
+ /* Only try multi-arch if the original path does not exist. */
+ if (stat(path, &sb) == -1 && errno == ENOENT) {
+ newpath = sudo_stat_multiarch(path, &sb);
+ if (newpath != NULL) {
+ ret = dlopen(newpath, flags);
+ free(newpath);
+ }
+ }
+ return ret;
+}
+# else
+static void *
+dlopen_multi_arch(const char *path, int flags)
+{
+ return NULL;
+}
+# endif /* __linux__ */
+
void *
sudo_dso_load_v1(const char *path, int mode)
{
struct sudo_preload_table *pt;
int flags = 0;
void *ret;
-#ifdef RTLD_MEMBER
+# ifdef RTLD_MEMBER
char *cp;
-#endif
+# endif
/* Check prelinked symbols first. */
if (preload_table != NULL) {
@@ -197,7 +234,7 @@ sudo_dso_load_v1(const char *path, int mode)
if (ISSET(mode, SUDO_DSO_LOCAL))
SET(flags, RTLD_LOCAL);
-#ifdef RTLD_MEMBER
+# ifdef RTLD_MEMBER
/* Check for AIX path(module) syntax and add RTLD_MEMBER for a module. */
cp = strrchr(path, '(');
if (cp != NULL) {
@@ -205,9 +242,9 @@ sudo_dso_load_v1(const char *path, int mode)
if (len > 2 && cp[len - 1] == '\0')
SET(flags, RTLD_MEMBER);
}
-#endif /* RTLD_MEMBER */
+# endif /* RTLD_MEMBER */
ret = dlopen(path, flags);
-#ifdef RTLD_MEMBER
+# if defined(RTLD_MEMBER)
/*
* If we try to dlopen() an AIX .a file without an explicit member
* it will fail with ENOEXEC. Try again using the default member.
@@ -217,8 +254,15 @@ sudo_dso_load_v1(const char *path, int mode)
ret = dlopen(cp, flags|RTLD_MEMBER);
free(cp);
}
+ if (ret == NULL) {
+ /* Retry with the original path so we get the correct error. */
+ ret = dlopen(path, flags);
+ }
}
-#endif /* RTLD_MEMBER */
+# endif /* RTLD_MEMBER */
+ /* On failure, try again with a multi-arch path where possible. */
+ if (ret == NULL)
+ ret = dlopen_multi_arch(path, flags);
return ret;
}