Use multilib rules to look for a 64-bit group plugin on failure.

If sudo_dso_load() fails on a 64-bit system, try to load a 64-bit
native version of the file using system-dependent multilib rules.
If we don't support multilib on the platform, check for a version
of the file that ends in "64" before the .so suffix.
This commit is contained in:
Todd C. Miller
2022-08-11 19:34:50 -06:00
parent 68b26056f9
commit 158facf6d5
3 changed files with 174 additions and 23 deletions

View File

@@ -25,7 +25,7 @@
.nr BA @BAMAN@ .nr BA @BAMAN@
.nr LC @LCMAN@ .nr LC @LCMAN@
.nr PS @PSMAN@ .nr PS @PSMAN@
.TH "SUDOERS" "@mansectform@" "July 29, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .TH "SUDOERS" "@mansectform@" "August 11, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.nh .nh
.if n .ad l .if n .ad l
.SH "NAME" .SH "NAME"
@@ -3410,7 +3410,7 @@ has completed but before the new command has had a chance to run.
In the case of a path name or argument mismatch, the command will be sent a In the case of a path name or argument mismatch, the command will be sent a
\fRSIGKILL\fR \fRSIGKILL\fR
signal and terminated. signal and terminated.
This can help prevent a time of check vs. time of use issue with This can help prevent a time of check versus time of use issue with
intercept mode where the intercept mode where the
execve(2) execve(2)
arguments could be altered after the arguments could be altered after the
@@ -4979,6 +4979,34 @@ These arguments (if any) will be passed to the plugin's initialization function.
If arguments are present, the string must be enclosed in double quotes If arguments are present, the string must be enclosed in double quotes
(\&""). (\&"").
.sp .sp
On 64-bit systems, if the plugin is present but cannot be loaded,
\fBsudoers\fR
will look for a 64-bit version and, if it exists, load that as a fallback.
The exact rules for this vary by system.
On Solaris, if the plugin is stored in a directory ending in
\(lqlib\(rq,
\fBsudoers\fR
will create a fallback path by appending
\(lq/64\(rq
to the directory name;
\fI/usr/lib/sudo_plugin.so\fR
becomes
\fI/usr/lib/64/sudo_plugin.so\fR.
On Linux, a directory ending in
\(lqlib\(rq
will be transformed to
\(lqlib64\(rq
as the fallback path;
\fI/usr/lib/sudo_plugin.so\fR
becomes
\fI/usr/lib64/sudo_plugin.so\fR.
On all other systems, the fallback path is generated by adding a
\(lq64\(rq
before the file extension;
\fIsudo_plugin.so\fR
becomes
\fIsudo_plugin64.so\fR.
.sp
For more information see For more information see
\fIGROUP PROVIDER PLUGINS\fR. \fIGROUP PROVIDER PLUGINS\fR.
.TP 14n .TP 14n

View File

@@ -25,7 +25,7 @@
.nr BA @BAMAN@ .nr BA @BAMAN@
.nr LC @LCMAN@ .nr LC @LCMAN@
.nr PS @PSMAN@ .nr PS @PSMAN@
.Dd July 29, 2022 .Dd August 11, 2022
.Dt SUDOERS @mansectform@ .Dt SUDOERS @mansectform@
.Os Sudo @PACKAGE_VERSION@ .Os Sudo @PACKAGE_VERSION@
.Sh NAME .Sh NAME
@@ -3231,7 +3231,7 @@ has completed but before the new command has had a chance to run.
In the case of a path name or argument mismatch, the command will be sent a In the case of a path name or argument mismatch, the command will be sent a
.Dv SIGKILL .Dv SIGKILL
signal and terminated. signal and terminated.
This can help prevent a time of check vs. time of use issue with This can help prevent a time of check versus time of use issue with
intercept mode where the intercept mode where the
.Xr execve 2 .Xr execve 2
arguments could be altered after the arguments could be altered after the
@@ -4690,6 +4690,34 @@ These arguments (if any) will be passed to the plugin's initialization function.
If arguments are present, the string must be enclosed in double quotes If arguments are present, the string must be enclosed in double quotes
.Pq \&"" . .Pq \&"" .
.Pp .Pp
On 64-bit systems, if the plugin is present but cannot be loaded,
.Nm
will look for a 64-bit version and, if it exists, load that as a fallback.
The exact rules for this vary by system.
On Solaris, if the plugin is stored in a directory ending in
.Dq lib ,
.Nm
will create a fallback path by appending
.Dq /64
to the directory name;
.Pa /usr/lib/sudo_plugin.so
becomes
.Pa /usr/lib/64/sudo_plugin.so .
On Linux, a directory ending in
.Dq lib
will be transformed to
.Dq lib64
as the fallback path;
.Pa /usr/lib/sudo_plugin.so
becomes
.Pa /usr/lib64/sudo_plugin.so .
On all other systems, the fallback path is generated by adding a
.Dq 64
before the file extension;
.Pa sudo_plugin.so
becomes
.Pa sudo_plugin64.so .
.Pp
For more information see For more information see
.Sx "GROUP PROVIDER PLUGINS" . .Sx "GROUP PROVIDER PLUGINS" .
.It lecture .It lecture

View File

@@ -40,6 +40,90 @@ static void *group_handle;
static struct sudoers_group_plugin *group_plugin; static struct sudoers_group_plugin *group_plugin;
const char *path_plugin_dir = _PATH_SUDO_PLUGIN_DIR; const char *path_plugin_dir = _PATH_SUDO_PLUGIN_DIR;
/*
* Check for a fallback path when the original group plugin is not loadable.
* Returns true on success, rewriting path and filling in sb, else false.
*/
static bool
group_plugin_fallback(char *path, size_t pathsize, struct stat *sb)
{
#if defined(__LP64__)
char newpath[PATH_MAX];
bool ret = false;
int len;
debug_decl(group_plugin_fallback, SUDOERS_DEBUG_UTIL);
# if defined(__sun__) || defined(__linux__)
/*
* Solaris uses /lib/64 and /usr/lib/64 for 64-bit libraries.
* Linux may use /lib64 and /usr/lib64 for 64-bit libraries.
* If dirname(path) ends in /lib, try /lib/64 (Solaris) or /lib64 (Linux).
*/
# if defined(__sun__)
const char *lib64 = "lib/64";
# else
const char *lib64 = "lib64";
# endif
const char *base, *slash;
int dirlen;
slash = strrchr(path, '/');
if (slash == NULL) {
goto done;
}
base = slash + 1;
/* Collapse consecutive slashes. */
while (slash > path && slash[-1] == '/') {
slash--;
}
/* If directory ends in /lib/, try again with /lib/64/ or /lib64/. */
dirlen = slash - path;
if (dirlen < 4 || strncmp(slash - 4, "/lib", 4) != 0) {
goto done;
}
dirlen -= 4;
len = snprintf(newpath, sizeof(newpath), "%.*s/%s/%s", dirlen, path, lib64,
base);
# else /* !__sun__ && !__linux__ */
/*
* Multilib not supported, check for a path of the form libfoo64.so.
*/
const char *dot;
int plen;
dot = strrchr(path, '.');
if (dot == NULL) {
goto done;
}
plen = dot - path;
/* If basename(path) doesn't match libfoo64.so, try adding the 64. */
if (plen >= 2 && strncmp(dot - 2, "64", 2) == 0) {
goto done;
}
len = snprintf(newpath, sizeof(newpath), "%.*s64%s", plen, path, dot);
# endif /* __sun__ || __linux__ */
if (len < 0 || len >= ssizeof(newpath)) {
errno = ENAMETOOLONG;
goto done;
}
if (stat(newpath, sb) == -1) {
goto done;
}
if (strlcpy(path, newpath, pathsize) >= pathsize) {
errno = ENAMETOOLONG;
goto done;
}
ret = true;
done:
debug_return_bool(ret);
#else
return false;
#endif /* __LP64__ */
}
/* /*
* Load the specified plugin and run its init function. * Load the specified plugin and run its init function.
* Returns -1 if unable to open the plugin, else it returns * Returns -1 if unable to open the plugin, else it returns
@@ -52,6 +136,7 @@ group_plugin_load(const char *plugin_info)
char *args, path[PATH_MAX]; char *args, path[PATH_MAX];
char **argv = NULL; char **argv = NULL;
int len, rc = -1; int len, rc = -1;
bool retry = true;
debug_decl(group_plugin_load, SUDOERS_DEBUG_UTIL); debug_decl(group_plugin_load, SUDOERS_DEBUG_UTIL);
/* /*
@@ -72,31 +157,41 @@ group_plugin_load(const char *plugin_info)
(*plugin_info != '/') ? path_plugin_dir : "", plugin_info); (*plugin_info != '/') ? path_plugin_dir : "", plugin_info);
goto done; goto done;
} }
/* Check owner and mode of plugin path. */
if (stat(path, &sb) != 0) { if (stat(path, &sb) != 0) {
sudo_warn("%s", path); sudo_warn("%s", path);
goto done; goto done;
} }
if (!sudo_conf_developer_mode()) {
if (sb.st_uid != ROOT_UID) { for (;;) {
sudo_warnx(U_("%s must be owned by uid %d"), path, ROOT_UID); if (!sudo_conf_developer_mode()) {
goto done; /* Check owner and mode of plugin path. */
} if (sb.st_uid != ROOT_UID) {
if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) { sudo_warnx(U_("%s must be owned by uid %d"), path, ROOT_UID);
sudo_warnx(U_("%s must only be writable by owner"), path); goto done;
goto done; }
} if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
sudo_warnx(U_("%s must only be writable by owner"), path);
goto done;
}
}
group_handle = sudo_dso_load(path, SUDO_DSO_LAZY|SUDO_DSO_GLOBAL);
if (group_handle != NULL) {
break;
}
if (!retry || !group_plugin_fallback(path, sizeof(path), &sb)) {
const char *errstr = sudo_dso_strerror();
sudo_warnx(U_("unable to load %s: %s"), path,
errstr ? errstr : "unknown error");
goto done;
}
/* Retry once with the fallback path. */
retry = false;
} }
/* Open plugin and map in symbol. */ /* Map in symbol from group plugin. */
group_handle = sudo_dso_load(path, SUDO_DSO_LAZY|SUDO_DSO_GLOBAL);
if (!group_handle) {
const char *errstr = sudo_dso_strerror();
sudo_warnx(U_("unable to load %s: %s"), path,
errstr ? errstr : "unknown error");
goto done;
}
group_plugin = sudo_dso_findsym(group_handle, "group_plugin"); group_plugin = sudo_dso_findsym(group_handle, "group_plugin");
if (group_plugin == NULL) { if (group_plugin == NULL) {
sudo_warnx(U_("unable to find symbol \"group_plugin\" in %s"), path); sudo_warnx(U_("unable to find symbol \"group_plugin\" in %s"), path);