Fix potential double free for rules that include a CHROOT= option.
If a rule with a CHROOT= option matches the user, host and runas, the user_cmnd variable could be freed twice.
This commit is contained in:
2
MANIFEST
2
MANIFEST
@@ -1056,6 +1056,8 @@ plugins/sudoers/regress/testsudoers/test19.sh
|
|||||||
plugins/sudoers/regress/testsudoers/test2.inc
|
plugins/sudoers/regress/testsudoers/test2.inc
|
||||||
plugins/sudoers/regress/testsudoers/test2.out.ok
|
plugins/sudoers/regress/testsudoers/test2.out.ok
|
||||||
plugins/sudoers/regress/testsudoers/test2.sh
|
plugins/sudoers/regress/testsudoers/test2.sh
|
||||||
|
plugins/sudoers/regress/testsudoers/test20.out.ok
|
||||||
|
plugins/sudoers/regress/testsudoers/test20.sh
|
||||||
plugins/sudoers/regress/testsudoers/test3.out.ok
|
plugins/sudoers/regress/testsudoers/test3.out.ok
|
||||||
plugins/sudoers/regress/testsudoers/test3.sh
|
plugins/sudoers/regress/testsudoers/test3.sh
|
||||||
plugins/sudoers/regress/testsudoers/test4.out.ok
|
plugins/sudoers/regress/testsudoers/test4.out.ok
|
||||||
|
@@ -825,12 +825,16 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
/* Rule-specific runchroot, set user_cmnd and user_stat after pivot. */
|
/* Rule-specific runchroot, set user_cmnd and user_stat after pivot. */
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
/* Save old user_cmnd first, set_cmnd_path() will free it. */
|
||||||
saved_user_cmnd = user_cmnd;
|
saved_user_cmnd = user_cmnd;
|
||||||
|
user_cmnd = NULL;
|
||||||
if (user_stat != NULL)
|
if (user_stat != NULL)
|
||||||
saved_user_stat = *user_stat;
|
saved_user_stat = *user_stat;
|
||||||
status = set_cmnd_path(NULL);
|
status = set_cmnd_path(NULL);
|
||||||
if (status != FOUND)
|
if (status != FOUND) {
|
||||||
|
user_cmnd = saved_user_cmnd;
|
||||||
saved_user_cmnd = NULL;
|
saved_user_cmnd = NULL;
|
||||||
|
}
|
||||||
if (info != NULL)
|
if (info != NULL)
|
||||||
info->status = status;
|
info->status = status;
|
||||||
}
|
}
|
||||||
|
@@ -45,6 +45,9 @@ static int fuzz_conversation(int num_msgs, const struct sudo_conv_message msgs[]
|
|||||||
static int fuzz_printf(int msg_type, const char *fmt, ...);
|
static int fuzz_printf(int msg_type, const char *fmt, ...);
|
||||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||||
|
|
||||||
|
/* For set_cmnd_path() */
|
||||||
|
static const char *orig_cmnd;
|
||||||
|
|
||||||
/* Required to link with parser. */
|
/* Required to link with parser. */
|
||||||
struct sudo_user sudo_user;
|
struct sudo_user sudo_user;
|
||||||
struct passwd *list_pw;
|
struct passwd *list_pw;
|
||||||
@@ -104,8 +107,13 @@ init_envtables(void)
|
|||||||
int
|
int
|
||||||
set_cmnd_path(const char *runchroot)
|
set_cmnd_path(const char *runchroot)
|
||||||
{
|
{
|
||||||
/* Cannot return FOUND without also setting user_cmnd to a new value. */
|
/* Reallocate user_cmnd to catch bugs in command_matches(). */
|
||||||
return NOT_FOUND;
|
char *new_cmnd = strdup(orig_cmnd);
|
||||||
|
if (new_cmnd == NULL)
|
||||||
|
return NOT_FOUND_ERROR;
|
||||||
|
free(user_cmnd);
|
||||||
|
user_cmnd = new_cmnd;
|
||||||
|
return FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* STUB */
|
/* STUB */
|
||||||
@@ -277,11 +285,12 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
|||||||
|
|
||||||
/* The minimum needed to perform matching (user_cmnd must be dynamic). */
|
/* The minimum needed to perform matching (user_cmnd must be dynamic). */
|
||||||
user_host = user_shost = user_runhost = user_srunhost = (char *)"localhost";
|
user_host = user_shost = user_runhost = user_srunhost = (char *)"localhost";
|
||||||
user_cmnd = strdup("/usr/bin/id");
|
orig_cmnd = (char *)"/usr/bin/id";
|
||||||
|
user_cmnd = strdup(orig_cmnd);
|
||||||
if (user_cmnd == NULL)
|
if (user_cmnd == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
user_args = (char *)"-u";
|
user_args = (char *)"-u";
|
||||||
user_base = (char *)"id";
|
user_base = sudo_basename(user_cmnd);
|
||||||
|
|
||||||
/* Add a fake network interfaces. */
|
/* Add a fake network interfaces. */
|
||||||
interfaces = get_interfaces();
|
interfaces = get_interfaces();
|
||||||
|
15
plugins/sudoers/regress/testsudoers/test20.out.ok
Normal file
15
plugins/sudoers/regress/testsudoers/test20.out.ok
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
Parses OK
|
||||||
|
|
||||||
|
Entries for user root:
|
||||||
|
|
||||||
|
ALL = CHROOT=/ /bin/ls
|
||||||
|
host matched
|
||||||
|
runas matched
|
||||||
|
cmnd allowed
|
||||||
|
|
||||||
|
ALL = CWD=/ /bin/pwd
|
||||||
|
host matched
|
||||||
|
runas matched
|
||||||
|
cmnd allowed
|
||||||
|
|
||||||
|
Command allowed
|
18
plugins/sudoers/regress/testsudoers/test20.sh
Normal file
18
plugins/sudoers/regress/testsudoers/test20.sh
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Verify CHROOT and CWD support
|
||||||
|
# This will catch an unpatched double-free in set_cmnd_path() under ASAN.
|
||||||
|
#
|
||||||
|
|
||||||
|
: ${TESTSUDOERS=testsudoers}
|
||||||
|
|
||||||
|
exec 2>&1
|
||||||
|
|
||||||
|
# Exercise double free of user_cmnd in set_cmnd_path() under ASAN.
|
||||||
|
# We need more than one rule where the last rule matches and has CHROOT.
|
||||||
|
$TESTSUDOERS root /bin/ls <<'EOF'
|
||||||
|
root ALL = CWD=/ /bin/pwd
|
||||||
|
root ALL = CHROOT=/ /bin/ls
|
||||||
|
EOF
|
||||||
|
|
||||||
|
exit 0
|
@@ -82,6 +82,7 @@ extern int (*trace_print)(const char *msg);
|
|||||||
*/
|
*/
|
||||||
struct sudo_user sudo_user;
|
struct sudo_user sudo_user;
|
||||||
struct passwd *list_pw;
|
struct passwd *list_pw;
|
||||||
|
static const char *orig_cmnd;
|
||||||
static char *runas_group, *runas_user;
|
static char *runas_group, *runas_user;
|
||||||
|
|
||||||
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
|
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
|
||||||
@@ -203,14 +204,18 @@ main(int argc, char *argv[])
|
|||||||
if (!dflag)
|
if (!dflag)
|
||||||
usage();
|
usage();
|
||||||
user_name = argc ? *argv++ : (char *)"root";
|
user_name = argc ? *argv++ : (char *)"root";
|
||||||
user_cmnd = user_base = (char *)"true";
|
orig_cmnd = "true";
|
||||||
argc = 0;
|
argc = 0;
|
||||||
} else {
|
} else {
|
||||||
user_name = *argv++;
|
user_name = *argv++;
|
||||||
user_cmnd = *argv++;
|
orig_cmnd = *argv++;
|
||||||
user_base = sudo_basename(user_cmnd);
|
|
||||||
argc -= 2;
|
argc -= 2;
|
||||||
}
|
}
|
||||||
|
user_cmnd = strdup(orig_cmnd);
|
||||||
|
if (user_cmnd == NULL)
|
||||||
|
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||||
|
user_base = sudo_basename(user_cmnd);
|
||||||
|
|
||||||
if ((sudo_user.pw = sudo_getpwnam(user_name)) == NULL)
|
if ((sudo_user.pw = sudo_getpwnam(user_name)) == NULL)
|
||||||
sudo_fatalx(U_("unknown user %s"), user_name);
|
sudo_fatalx(U_("unknown user %s"), user_name);
|
||||||
|
|
||||||
@@ -521,8 +526,13 @@ unpivot_root(int fds[2])
|
|||||||
int
|
int
|
||||||
set_cmnd_path(const char *runchroot)
|
set_cmnd_path(const char *runchroot)
|
||||||
{
|
{
|
||||||
/* Cannot return FOUND without also setting user_cmnd to a new value. */
|
/* Reallocate user_cmnd to catch bugs in command_matches(). */
|
||||||
return NOT_FOUND;
|
char *new_cmnd = strdup(orig_cmnd);
|
||||||
|
if (new_cmnd == NULL)
|
||||||
|
return NOT_FOUND_ERROR;
|
||||||
|
free(user_cmnd);
|
||||||
|
user_cmnd = new_cmnd;
|
||||||
|
return FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@@ -260,7 +260,9 @@ main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Mock up a fake sudo_user struct. */
|
/* Mock up a fake sudo_user struct. */
|
||||||
user_cmnd = user_base = (char *)"";
|
user_cmnd = user_base = strdup("true");
|
||||||
|
if (user_cmnd == NULL)
|
||||||
|
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||||
if (geteuid() == 0) {
|
if (geteuid() == 0) {
|
||||||
const char *user = getenv("SUDO_USER");
|
const char *user = getenv("SUDO_USER");
|
||||||
if (user != NULL && *user != '\0')
|
if (user != NULL && *user != '\0')
|
||||||
|
Reference in New Issue
Block a user