sudo_dso_load: add AIX fallback path from shlib.so to shlib.a(shlib.so).

If the .so file is missing but the .a file exists, try to dlopen()
the AIX .a file using the .so name as the member.  We need to avoid
breaking existing configurations if the type of AIX shared library
changes when sudo is upgraded.
This commit is contained in:
Todd C. Miller
2022-12-26 07:43:55 -07:00
parent 206700c3f0
commit 97fb4eae72
2 changed files with 79 additions and 27 deletions

View File

@@ -124,7 +124,7 @@ sudo_dso_findsym_v1(void *vhandle, const char *symbol)
} }
/* /*
* Note that the behavior of of SUDO_DSO_NEXT and SUDO_DSO_SELF * Note that the behavior of of SUDO_DSO_NEXT and SUDO_DSO_SELF
* differs from most implementations when called from * differs from most implementations when called from
* a shared library. * a shared library.
*/ */
@@ -176,7 +176,7 @@ sudo_dso_strerror_v1(void)
# endif # endif
# if defined(__linux__) # if defined(__linux__)
/* /*
* On Linux systems that use multi-arch, the actual DSO may be * On Linux systems that use multi-arch, the actual DSO may be
* in a machine-specific subdirectory. If the specified path * in a machine-specific subdirectory. If the specified path
* contains /lib/ or /libexec/, insert a multi-arch directory * contains /lib/ or /libexec/, insert a multi-arch directory
@@ -215,6 +215,7 @@ sudo_dso_load_v1(const char *path, int mode)
void *ret; void *ret;
# ifdef RTLD_MEMBER # ifdef RTLD_MEMBER
char *cp; char *cp;
size_t pathlen;
# endif # endif
/* Check prelinked symbols first. */ /* Check prelinked symbols first. */
@@ -236,28 +237,58 @@ sudo_dso_load_v1(const char *path, int mode)
SET(flags, RTLD_LOCAL); SET(flags, RTLD_LOCAL);
# ifdef RTLD_MEMBER # ifdef RTLD_MEMBER
/* Check for AIX path(module) syntax and add RTLD_MEMBER for a module. */ /* Check for AIX shlib.a(member) syntax and dlopen() with RTLD_MEMBER. */
cp = strrchr(path, '('); pathlen = strlen(path);
if (cp != NULL) { if (pathlen > 2 && path[pathlen - 1] == ')') {
size_t len = strlen(cp); cp = strrchr(path, '(');
if (len > 2 && cp[len - 1] == ')') if (cp != NULL && cp > path + 2 && cp[-2] == '.' && cp[-1] == 'a') {
/* Only for archive files (e.g. sudoers.a). */
SET(flags, RTLD_MEMBER); SET(flags, RTLD_MEMBER);
}
} }
# endif /* RTLD_MEMBER */ # endif /* RTLD_MEMBER */
ret = dlopen(path, flags); ret = dlopen(path, flags);
# if defined(RTLD_MEMBER) # if defined(RTLD_MEMBER)
/* /* Special fallback handling for AIX shared objects. */
* If we try to dlopen() an AIX .a file without an explicit member if (ret == NULL && !ISSET(flags, RTLD_MEMBER)) {
* it will fail with ENOEXEC. Try again using the default member. switch (errno) {
*/ case ENOEXEC:
if (ret == NULL && !ISSET(flags, RTLD_MEMBER) && errno == ENOEXEC) { /*
if (asprintf(&cp, "%s(%s)", path, SUDO_DSO_MEMBER) != -1) { * If we try to dlopen() an AIX .a file without an explicit member
ret = dlopen(cp, flags|RTLD_MEMBER); * it will fail with ENOEXEC. Try again using the default member.
free(cp); */
} if (pathlen > 2 && strcmp(&path[pathlen - 2], ".a") == 0) {
if (ret == NULL) { int len = asprintf(&cp, "%s(%s)", path, SUDO_DSO_MEMBER);
/* Retry with the original path so we get the correct error. */ if (len != -1) {
ret = dlopen(path, flags); ret = dlopen(cp, flags|RTLD_MEMBER);
free(cp);
}
if (ret == NULL) {
/* Retry with the original path to get the correct error. */
ret = dlopen(path, flags);
}
}
break;
case ENOENT:
/*
* If the .so file is missing but the .a file exists, try to
* dlopen() the AIX .a file using the .so name as the member.
* This is for compatibility with versions of sudo that use
* SVR4-style shared libs, not AIX-style shared libs.
*/
if (pathlen > 3 && strcmp(&path[pathlen - 3], ".so") == 0) {
int len = asprintf(&cp, "%.*s.a(%s)", (int)(pathlen - 3),
path, sudo_basename(path));
if (len != -1) {
ret = dlopen(cp, flags|RTLD_MEMBER);
free(cp);
}
if (ret == NULL) {
/* Retry with the original path to get the correct error. */
ret = dlopen(path, flags);
}
}
break;
} }
} }
# endif /* RTLD_MEMBER */ # endif /* RTLD_MEMBER */

View File

@@ -81,14 +81,35 @@ sudo_stat_plugin(struct plugin_info *info, char *fullpath,
} }
#ifdef _AIX #ifdef _AIX
if (status == -1 && errno == ENOENT) { if (status == -1 && errno == ENOENT) {
/* Check for AIX path(module) syntax. */ len = strlen(fullpath);
char *cp = strrchr(fullpath, '('); if (len > 2 && fullpath[len - 1] == ')') {
if (cp != NULL) { /* Check for AIX shlib.a(member) dlopen(3) syntax. */
/* Only for archive files (e.g. sudoers.a). */ char *cp = strrchr(fullpath, '(');
if (cp > fullpath + 2 && cp[-2] == '.' && cp[-1] == 'a') { if (cp != NULL) {
*cp = '\0'; /* Only for archive files (e.g. sudoers.a). */
status = stat(fullpath, sb); if (cp > fullpath + 2 && cp[-2] == '.' && cp[-1] == 'a') {
*cp = '('; *cp = '\0';
status = stat(fullpath, sb);
*cp = '(';
}
}
} else if (len > 3 && strcmp(&fullpath[len - 3], ".so") == 0) {
/*
* Check for AIX-style shlib.a if shlib.so does not exist for
* compatibility with sudo versions that use SVR4-style shlibs.
*/
fullpath[len - 2] = 'a';
fullpath[len - 1] = '\0';
len--;
status = stat(fullpath, sb);
if (status == 0) {
/* Use info->path as the member of the .a file. */
int n = snprintf(fullpath + len, pathsize - len, "(%s)",
sudo_basename(info->path));
if (n < 0 || (size_t)n >= pathsize - len) {
errno = ENAMETOOLONG;
goto done;
}
} }
} }
} }