Add intercept_type sudoers option to set intercept/log_subcmds mechanism.
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
.nr BA @BAMAN@
|
||||
.nr LC @LCMAN@
|
||||
.nr PS @PSMAN@
|
||||
.TH "SUDOERS" "@mansectform@" "May 4, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
|
||||
.TH "SUDOERS" "@mansectform@" "May 24, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
|
||||
.nh
|
||||
.if n .ad l
|
||||
.SH "NAME"
|
||||
@@ -3307,18 +3307,20 @@ To prevent this from happening,
|
||||
will not permit a set-user-ID or set-group-ID program to be run in
|
||||
intercept mode unless
|
||||
\fIintercept_allow_setid\fR
|
||||
is set.
|
||||
is enable.
|
||||
This flag has no effect unless the
|
||||
\fIintercept\fR
|
||||
flag is enabled or the
|
||||
\fIINTERCEPT\fR
|
||||
tag has been set for the command.
|
||||
This flag is
|
||||
\fIoff\fR
|
||||
by default except on Linux systems that support
|
||||
seccomp(2)
|
||||
filtering, where it defaults to
|
||||
\fIon\fR.
|
||||
\fIon\fR
|
||||
by default when the
|
||||
\fIintercept_type\fR
|
||||
option is set to
|
||||
\fItrace\fR,
|
||||
otherwise it default to
|
||||
\fIoff\fR.
|
||||
.sp
|
||||
This setting is only supported by version 1.9.8 or higher.
|
||||
.TP 18n
|
||||
@@ -4215,6 +4217,63 @@ option is disabled.
|
||||
The default is
|
||||
\fI@editor@\fR.
|
||||
.TP 18n
|
||||
intercept_type
|
||||
The underlying mechanism used by the
|
||||
\fIintercept\fR
|
||||
and
|
||||
\fIlog_subcmds\fR
|
||||
options.
|
||||
It has the following possible values:
|
||||
.PP
|
||||
.RS 18n
|
||||
.PD 0
|
||||
.TP 8n
|
||||
dso
|
||||
Preload a dynamic shared object (shared library) that intercepts the
|
||||
\fBexecl\fR(),
|
||||
\fBexecle\fR(),
|
||||
\fBexeclp\fR(),
|
||||
\fBexecv\fR(),
|
||||
\fBexecve\fR(),
|
||||
\fBexecvp\fR(),
|
||||
and
|
||||
\fBexecvpe\fR()
|
||||
library functions.
|
||||
A value of
|
||||
\fIdso\fR
|
||||
is incompatible with
|
||||
\fBsudo\fR's
|
||||
SELinux RBAC support.
|
||||
.PD
|
||||
.TP 8n
|
||||
trace
|
||||
Use
|
||||
ptrace(2)
|
||||
to intercept the
|
||||
execve(2)
|
||||
system call.
|
||||
This is only supported on Linux systems where
|
||||
seccomp(2)
|
||||
filtering is enabled.
|
||||
If the
|
||||
\fI/proc/sys/kernel/seccomp/actions_avail\fR
|
||||
file is missing or does not contain a
|
||||
\(lqtrap\(rq
|
||||
element, setting
|
||||
\fIintercept_type\fR
|
||||
to
|
||||
\fItrace\fR
|
||||
will have no effect and
|
||||
\fIdso\fR
|
||||
will be used instead.
|
||||
.PP
|
||||
The default is to use
|
||||
\fItrace\fR
|
||||
if it is supported by the system and
|
||||
\fIdso\fR
|
||||
if it is not.
|
||||
.RE
|
||||
.TP 18n
|
||||
iolog_dir
|
||||
The top-level directory to use when constructing the path name for
|
||||
the input/output log directory.
|
||||
@@ -6777,7 +6836,7 @@ by default and interferes with file descriptor inheritance.
|
||||
.sp
|
||||
Linux systems that support
|
||||
seccomp(2)
|
||||
filtering will use a different method involving
|
||||
filtering can use a different method involving
|
||||
ptrace(2)
|
||||
instead of pre-loading a shared library.
|
||||
This method supports both static and dynamic executables as well as
|
||||
|
@@ -24,7 +24,7 @@
|
||||
.nr BA @BAMAN@
|
||||
.nr LC @LCMAN@
|
||||
.nr PS @PSMAN@
|
||||
.Dd May 4, 2022
|
||||
.Dd May 24, 2022
|
||||
.Dt SUDOERS @mansectform@
|
||||
.Os Sudo @PACKAGE_VERSION@
|
||||
.Sh NAME
|
||||
@@ -3123,18 +3123,20 @@ To prevent this from happening,
|
||||
will not permit a set-user-ID or set-group-ID program to be run in
|
||||
intercept mode unless
|
||||
.Em intercept_allow_setid
|
||||
is set.
|
||||
is enable.
|
||||
This flag has no effect unless the
|
||||
.Em intercept
|
||||
flag is enabled or the
|
||||
.Em INTERCEPT
|
||||
tag has been set for the command.
|
||||
This flag is
|
||||
.Em off
|
||||
by default except on Linux systems that support
|
||||
.Xr seccomp 2
|
||||
filtering, where it defaults to
|
||||
.Em on .
|
||||
.Em on
|
||||
by default when the
|
||||
.Em intercept_type
|
||||
option is set to
|
||||
.Em trace ,
|
||||
otherwise it default to
|
||||
.Em off .
|
||||
.Pp
|
||||
This setting is only supported by version 1.9.8 or higher.
|
||||
.It intercept_authenticate
|
||||
@@ -3982,6 +3984,57 @@ list or the
|
||||
option is disabled.
|
||||
The default is
|
||||
.Pa @editor@ .
|
||||
.It intercept_type
|
||||
The underlying mechanism used by the
|
||||
.Em intercept
|
||||
and
|
||||
.Em log_subcmds
|
||||
options.
|
||||
It has the following possible values:
|
||||
.Bl -tag -width 6n
|
||||
.It dso
|
||||
Preload a dynamic shared object (shared library) that intercepts the
|
||||
.Fn execl ,
|
||||
.Fn execle ,
|
||||
.Fn execlp ,
|
||||
.Fn execv ,
|
||||
.Fn execve ,
|
||||
.Fn execvp ,
|
||||
and
|
||||
.Fn execvpe
|
||||
library functions.
|
||||
A value of
|
||||
.Em dso
|
||||
is incompatible with
|
||||
.Nm sudo Ns 's
|
||||
SELinux RBAC support.
|
||||
.It trace
|
||||
Use
|
||||
.Xr ptrace 2
|
||||
to intercept the
|
||||
.Xr execve 2
|
||||
system call.
|
||||
This is only supported on Linux systems where
|
||||
.Xr seccomp 2
|
||||
filtering is enabled.
|
||||
If the
|
||||
.Pa /proc/sys/kernel/seccomp/actions_avail
|
||||
file is missing or does not contain a
|
||||
.Dq trap
|
||||
element, setting
|
||||
.Em intercept_type
|
||||
to
|
||||
.Em trace
|
||||
will have no effect and
|
||||
.Em dso
|
||||
will be used instead.
|
||||
.El
|
||||
.Pp
|
||||
The default is to use
|
||||
.Em trace
|
||||
if it is supported by the system and
|
||||
.Em dso
|
||||
if it is not.
|
||||
.It iolog_dir
|
||||
The top-level directory to use when constructing the path name for
|
||||
the input/output log directory.
|
||||
@@ -6266,7 +6319,7 @@ by default and interferes with file descriptor inheritance.
|
||||
.Pp
|
||||
Linux systems that support
|
||||
.Xr seccomp 2
|
||||
filtering will use a different method involving
|
||||
filtering can use a different method involving
|
||||
.Xr ptrace 2
|
||||
instead of pre-loading a shared library.
|
||||
This method supports both static and dynamic executables as well as
|
||||
|
@@ -44,6 +44,12 @@ static struct def_values def_data_log_format[] = {
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
static struct def_values def_data_intercept_type[] = {
|
||||
{ "dso", dso },
|
||||
{ "trace", trace },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
struct sudo_defs_types sudo_defs_table[] = {
|
||||
{
|
||||
"syslog", T_LOGFAC|T_BOOL,
|
||||
@@ -657,6 +663,10 @@ struct sudo_defs_types sudo_defs_table[] = {
|
||||
"passprompt_regex", T_LIST|T_SPACE|T_BOOL,
|
||||
N_("List of regular expressions to use when matching a password prompt"),
|
||||
NULL,
|
||||
}, {
|
||||
"intercept_type", T_TUPLE,
|
||||
N_("The mechanism used by the intercept and log_subcmds options: %s"),
|
||||
def_data_intercept_type,
|
||||
}, {
|
||||
NULL, 0, NULL
|
||||
}
|
||||
|
@@ -306,6 +306,8 @@
|
||||
#define def_log_passwords (sudo_defs_table[I_LOG_PASSWORDS].sd_un.flag)
|
||||
#define I_PASSPROMPT_REGEX 152
|
||||
#define def_passprompt_regex (sudo_defs_table[I_PASSPROMPT_REGEX].sd_un.list)
|
||||
#define I_INTERCEPT_TYPE 153
|
||||
#define def_intercept_type (sudo_defs_table[I_INTERCEPT_TYPE].sd_un.tuple)
|
||||
|
||||
enum def_tuple {
|
||||
never,
|
||||
@@ -319,5 +321,7 @@ enum def_tuple {
|
||||
tty,
|
||||
kernel,
|
||||
sudo,
|
||||
json
|
||||
json,
|
||||
dso,
|
||||
trace
|
||||
};
|
||||
|
@@ -475,3 +475,7 @@ log_passwords
|
||||
passprompt_regex
|
||||
T_LIST|T_SPACE|T_BOOL
|
||||
"List of regular expressions to use when matching a password prompt"
|
||||
intercept_type
|
||||
T_TUPLE
|
||||
"The mechanism used by the intercept and log_subcmds options: %s"
|
||||
dso trace
|
||||
|
@@ -548,8 +548,7 @@ init_defaults(void)
|
||||
#endif
|
||||
if ((def_rlimit_core = strdup("0,0")) == NULL)
|
||||
goto oom;
|
||||
if (ISSET(sudo_user.flags, CAN_INTERCEPT_SETID))
|
||||
def_intercept_allow_setid = true;
|
||||
def_intercept_type = dso;
|
||||
def_netgroup_tuple = false;
|
||||
def_sudoedit_checkdir = true;
|
||||
def_iolog_mode = S_IRUSR|S_IWUSR;
|
||||
|
@@ -297,10 +297,24 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults)
|
||||
goto oom;
|
||||
continue;
|
||||
}
|
||||
if (MATCHES(*cur, "intercept_ptrace=")) {
|
||||
int val = sudo_strtobool(*cur + sizeof("intercept_ptrace=") - 1);
|
||||
if (val == -1) {
|
||||
INVALID("intercept_ptrace="); /* Not a fatal error. */
|
||||
} else if (!append_default("intercept_type",
|
||||
val ? "trace" : "dso", true, NULL, defaults)) {
|
||||
goto oom;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (MATCHES(*cur, "intercept_setid=")) {
|
||||
if (parse_bool(*cur, sizeof("intercept_setid") - 1,
|
||||
&sudo_user.flags, CAN_INTERCEPT_SETID) == -1)
|
||||
goto bad;
|
||||
int val = sudo_strtobool(*cur + sizeof("intercept_setid=") - 1);
|
||||
if (val == -1) {
|
||||
INVALID("intercept_setid="); /* Not a fatal error. */
|
||||
} else if (!append_default("intercept_allow_setid", NULL, val,
|
||||
NULL, defaults)) {
|
||||
goto oom;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#ifdef HAVE_SELINUX
|
||||
@@ -605,7 +619,7 @@ sudoers_policy_store_result(bool accepted, char *argv[], char *envp[],
|
||||
}
|
||||
|
||||
/* Increase the length of command_info as needed, it is *not* checked. */
|
||||
command_info = calloc(70, sizeof(char *));
|
||||
command_info = calloc(71, sizeof(char *));
|
||||
if (command_info == NULL)
|
||||
goto oom;
|
||||
|
||||
@@ -777,6 +791,10 @@ sudoers_policy_store_result(bool accepted, char *argv[], char *envp[],
|
||||
if ((command_info[info_len++] = strdup("intercept=true")) == NULL)
|
||||
goto oom;
|
||||
}
|
||||
if (def_intercept_type == trace) {
|
||||
if ((command_info[info_len++] = strdup("use_ptrace=true")) == NULL)
|
||||
goto oom;
|
||||
}
|
||||
if (def_noexec) {
|
||||
if ((command_info[info_len++] = strdup("noexec=true")) == NULL)
|
||||
goto oom;
|
||||
|
@@ -145,7 +145,6 @@ struct sudo_user {
|
||||
*/
|
||||
#define RUNAS_USER_SPECIFIED 0x01
|
||||
#define RUNAS_GROUP_SPECIFIED 0x02
|
||||
#define CAN_INTERCEPT_SETID 0x04
|
||||
|
||||
/*
|
||||
* Return values for sudoers_lookup(), also used as arguments for log_auth()
|
||||
|
@@ -51,6 +51,8 @@
|
||||
# include "exec_intercept.h"
|
||||
# include "exec_ptrace.h"
|
||||
|
||||
static int seccomp_trap_supported = -1;
|
||||
|
||||
/* Register getters and setters. */
|
||||
# ifdef SECCOMP_AUDIT_ARCH_COMPAT
|
||||
static inline unsigned long
|
||||
@@ -736,7 +738,7 @@ done:
|
||||
* Check whether seccomp(2) filtering supports ptrace(2) traps.
|
||||
* Only supported by Linux 4.14 and higher.
|
||||
*/
|
||||
bool
|
||||
static bool
|
||||
have_seccomp_action(const char *action)
|
||||
{
|
||||
char line[LINE_MAX];
|
||||
@@ -1274,6 +1276,24 @@ exec_ptrace_stopped(pid_t pid, int status, void *intercept)
|
||||
|
||||
debug_return_bool(group_stop);
|
||||
}
|
||||
|
||||
bool
|
||||
exec_ptrace_intercept_supported(void)
|
||||
{
|
||||
if (seccomp_trap_supported == -1)
|
||||
seccomp_trap_supporetd = have_seccomp_action("trap");
|
||||
|
||||
return seccomp_trap_supported == true;
|
||||
}
|
||||
|
||||
bool
|
||||
exec_ptrace_subcmds_supported(void)
|
||||
{
|
||||
if (seccomp_trap_supported == -1)
|
||||
seccomp_trap_supported = have_seccomp_action("trap");
|
||||
|
||||
return seccomp_trap_supported == true;
|
||||
}
|
||||
#else
|
||||
/* STUB */
|
||||
bool
|
||||
@@ -1295,4 +1315,41 @@ exec_ptrace_seize(pid_t child)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
bool
|
||||
exec_ptrace_intercept_supported(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
bool
|
||||
exec_ptrace_subcmds_supported(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* HAVE_PTRACE_INTERCEPT */
|
||||
|
||||
/*
|
||||
* Adjust flags based on the availability of ptrace support.
|
||||
*/
|
||||
void
|
||||
exec_ptrace_fix_flags(struct command_details *details)
|
||||
{
|
||||
debug_decl(exec_ptrace_fix_flags, SUDO_DEBUG_EXEC);
|
||||
|
||||
if (ISSET(details->flags, CD_USE_PTRACE)) {
|
||||
/* If both CD_INTERCEPT and CD_LOG_SUBCMDS set, CD_INTERCEPT wins. */
|
||||
if (ISSET(details->flags, CD_INTERCEPT)) {
|
||||
if (!exec_ptrace_intercept_supported())
|
||||
CLR(details->flags, CD_USE_PTRACE);
|
||||
} else if (ISSET(details->flags, CD_LOG_SUBCMDS)) {
|
||||
if (!exec_ptrace_subcmds_supported())
|
||||
CLR(details->flags, CD_USE_PTRACE);
|
||||
} else {
|
||||
CLR(details->flags, CD_USE_PTRACE);
|
||||
}
|
||||
}
|
||||
debug_return;
|
||||
}
|
||||
|
@@ -82,6 +82,7 @@ static struct sudo_settings sudo_settings[] = {
|
||||
{ "cmnd_cwd" },
|
||||
{ "askpass" },
|
||||
{ "intercept_setid" },
|
||||
{ "intercept_ptrace" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -585,8 +586,10 @@ parse_args(int argc, char **argv, int *old_optind, int *nargc, char ***nargv,
|
||||
#ifdef ENABLE_SUDO_PLUGIN_API
|
||||
sudo_settings[ARG_PLUGIN_DIR].value = sudo_conf_plugin_dir_path();
|
||||
#endif
|
||||
if (have_seccomp_action("trap"))
|
||||
if (exec_ptrace_intercept_supported())
|
||||
sudo_settings[ARG_INTERCEPT_SETID].value = "true";
|
||||
if (exec_ptrace_subcmds_supported())
|
||||
sudo_settings[ARG_INTERCEPT_PTRACE].value = "true";
|
||||
|
||||
if (mode == MODE_HELP)
|
||||
help();
|
||||
|
14
src/sudo.c
14
src/sudo.c
@@ -650,10 +650,10 @@ bad:
|
||||
static void
|
||||
command_info_to_details(char * const info[], struct command_details *details)
|
||||
{
|
||||
int i;
|
||||
id_t id;
|
||||
char *cp;
|
||||
const char *errstr;
|
||||
char *cp;
|
||||
id_t id;
|
||||
int i;
|
||||
debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM);
|
||||
|
||||
memset(details, 0, sizeof(*details));
|
||||
@@ -857,17 +857,15 @@ command_info_to_details(char * const info[], struct command_details *details)
|
||||
break;
|
||||
}
|
||||
SET_FLAG("umask_override=", CD_OVERRIDE_UMASK)
|
||||
SET_FLAG("use_ptrace=", CD_USE_PTRACE)
|
||||
SET_FLAG("use_pty=", CD_USE_PTY)
|
||||
SET_STRING("utmp_user=", utmp_user)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
|
||||
/* Use ptrace(2) for intercept/log_subcmds if possible. */
|
||||
if (sudo_settings[ARG_INTERCEPT_SETID].value != NULL)
|
||||
SET(details->flags, CD_USE_PTRACE);
|
||||
}
|
||||
/* Only use ptrace(2) for intercept/log_subcmds if supported. */
|
||||
exec_ptrace_fix_flags(details);
|
||||
|
||||
if (!ISSET(details->flags, CD_SET_EUID))
|
||||
details->cred.euid = details->cred.uid;
|
||||
|
@@ -103,6 +103,7 @@
|
||||
#define ARG_CWD 24
|
||||
#define ARG_ASKPASS 25
|
||||
#define ARG_INTERCEPT_SETID 26
|
||||
#define ARG_INTERCEPT_PTRACE 27
|
||||
|
||||
/*
|
||||
* Flags for tgetpass()
|
||||
@@ -338,6 +339,8 @@ int serialize_rlimits(char **info, size_t info_max);
|
||||
bool parse_policy_rlimit(const char *str);
|
||||
|
||||
/* exec_ptrace.c */
|
||||
bool have_seccomp_action(const char *action);
|
||||
void exec_ptrace_fix_flags(struct command_details *details);
|
||||
bool exec_ptrace_intercept_supported(void);
|
||||
bool exec_ptrace_subcmds_supported(void);
|
||||
|
||||
#endif /* SUDO_SUDO_H */
|
||||
|
@@ -92,7 +92,6 @@ union sudo_token_un {
|
||||
|
||||
/*
|
||||
* Use ptrace-based intercept (using seccomp) on Linux if possible.
|
||||
* TODO: test other architectures
|
||||
*/
|
||||
#if defined(_PATH_SUDO_INTERCEPT) && defined(__linux__)
|
||||
# if defined(HAVE_DECL_SECCOMP_SET_MODE_FILTER) && HAVE_DECL_SECCOMP_SET_MODE_FILTER
|
||||
@@ -100,7 +99,7 @@ union sudo_token_un {
|
||||
# ifndef HAVE_PTRACE_INTERCEPT
|
||||
# define HAVE_PTRACE_INTERCEPT 1
|
||||
# endif /* HAVE_PTRACE_INTERCEPT */
|
||||
# endif /* __amd64__ || __i386__ || __aarch64__ || __riscv */
|
||||
# endif /* __amd64__ || __i386__ || __aarch64__ || __riscv || __s390__ */
|
||||
# endif /* HAVE_DECL_SECCOMP_SET_MODE_FILTER */
|
||||
#endif /* _PATH_SUDO_INTERCEPT && __linux__ */
|
||||
|
||||
|
Reference in New Issue
Block a user