Add intercept_type sudoers option to set intercept/log_subcmds mechanism.

This commit is contained in:
Todd C. Miller
2022-05-24 13:39:28 -06:00
parent b203753013
commit f053f174bc
13 changed files with 243 additions and 37 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@" "May 4, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .TH "SUDOERS" "@mansectform@" "May 24, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.nh .nh
.if n .ad l .if n .ad l
.SH "NAME" .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 will not permit a set-user-ID or set-group-ID program to be run in
intercept mode unless intercept mode unless
\fIintercept_allow_setid\fR \fIintercept_allow_setid\fR
is set. is enable.
This flag has no effect unless the This flag has no effect unless the
\fIintercept\fR \fIintercept\fR
flag is enabled or the flag is enabled or the
\fIINTERCEPT\fR \fIINTERCEPT\fR
tag has been set for the command. tag has been set for the command.
This flag is This flag is
\fIoff\fR \fIon\fR
by default except on Linux systems that support by default when the
seccomp(2) \fIintercept_type\fR
filtering, where it defaults to option is set to
\fIon\fR. \fItrace\fR,
otherwise it default to
\fIoff\fR.
.sp .sp
This setting is only supported by version 1.9.8 or higher. This setting is only supported by version 1.9.8 or higher.
.TP 18n .TP 18n
@@ -4215,6 +4217,63 @@ option is disabled.
The default is The default is
\fI@editor@\fR. \fI@editor@\fR.
.TP 18n .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 iolog_dir
The top-level directory to use when constructing the path name for The top-level directory to use when constructing the path name for
the input/output log directory. the input/output log directory.
@@ -6777,7 +6836,7 @@ by default and interferes with file descriptor inheritance.
.sp .sp
Linux systems that support Linux systems that support
seccomp(2) seccomp(2)
filtering will use a different method involving filtering can use a different method involving
ptrace(2) ptrace(2)
instead of pre-loading a shared library. instead of pre-loading a shared library.
This method supports both static and dynamic executables as well as This method supports both static and dynamic executables as well as

View File

@@ -24,7 +24,7 @@
.nr BA @BAMAN@ .nr BA @BAMAN@
.nr LC @LCMAN@ .nr LC @LCMAN@
.nr PS @PSMAN@ .nr PS @PSMAN@
.Dd May 4, 2022 .Dd May 24, 2022
.Dt SUDOERS @mansectform@ .Dt SUDOERS @mansectform@
.Os Sudo @PACKAGE_VERSION@ .Os Sudo @PACKAGE_VERSION@
.Sh NAME .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 will not permit a set-user-ID or set-group-ID program to be run in
intercept mode unless intercept mode unless
.Em intercept_allow_setid .Em intercept_allow_setid
is set. is enable.
This flag has no effect unless the This flag has no effect unless the
.Em intercept .Em intercept
flag is enabled or the flag is enabled or the
.Em INTERCEPT .Em INTERCEPT
tag has been set for the command. tag has been set for the command.
This flag is This flag is
.Em off .Em on
by default except on Linux systems that support by default when the
.Xr seccomp 2 .Em intercept_type
filtering, where it defaults to option is set to
.Em on . .Em trace ,
otherwise it default to
.Em off .
.Pp .Pp
This setting is only supported by version 1.9.8 or higher. This setting is only supported by version 1.9.8 or higher.
.It intercept_authenticate .It intercept_authenticate
@@ -3982,6 +3984,57 @@ list or the
option is disabled. option is disabled.
The default is The default is
.Pa @editor@ . .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 .It iolog_dir
The top-level directory to use when constructing the path name for The top-level directory to use when constructing the path name for
the input/output log directory. the input/output log directory.
@@ -6266,7 +6319,7 @@ by default and interferes with file descriptor inheritance.
.Pp .Pp
Linux systems that support Linux systems that support
.Xr seccomp 2 .Xr seccomp 2
filtering will use a different method involving filtering can use a different method involving
.Xr ptrace 2 .Xr ptrace 2
instead of pre-loading a shared library. instead of pre-loading a shared library.
This method supports both static and dynamic executables as well as This method supports both static and dynamic executables as well as

View File

@@ -44,6 +44,12 @@ static struct def_values def_data_log_format[] = {
{ NULL, 0 }, { NULL, 0 },
}; };
static struct def_values def_data_intercept_type[] = {
{ "dso", dso },
{ "trace", trace },
{ NULL, 0 },
};
struct sudo_defs_types sudo_defs_table[] = { struct sudo_defs_types sudo_defs_table[] = {
{ {
"syslog", T_LOGFAC|T_BOOL, "syslog", T_LOGFAC|T_BOOL,
@@ -657,6 +663,10 @@ struct sudo_defs_types sudo_defs_table[] = {
"passprompt_regex", T_LIST|T_SPACE|T_BOOL, "passprompt_regex", T_LIST|T_SPACE|T_BOOL,
N_("List of regular expressions to use when matching a password prompt"), N_("List of regular expressions to use when matching a password prompt"),
NULL, NULL,
}, {
"intercept_type", T_TUPLE,
N_("The mechanism used by the intercept and log_subcmds options: %s"),
def_data_intercept_type,
}, { }, {
NULL, 0, NULL NULL, 0, NULL
} }

View File

@@ -306,6 +306,8 @@
#define def_log_passwords (sudo_defs_table[I_LOG_PASSWORDS].sd_un.flag) #define def_log_passwords (sudo_defs_table[I_LOG_PASSWORDS].sd_un.flag)
#define I_PASSPROMPT_REGEX 152 #define I_PASSPROMPT_REGEX 152
#define def_passprompt_regex (sudo_defs_table[I_PASSPROMPT_REGEX].sd_un.list) #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 { enum def_tuple {
never, never,
@@ -319,5 +321,7 @@ enum def_tuple {
tty, tty,
kernel, kernel,
sudo, sudo,
json json,
dso,
trace
}; };

View File

@@ -475,3 +475,7 @@ log_passwords
passprompt_regex passprompt_regex
T_LIST|T_SPACE|T_BOOL T_LIST|T_SPACE|T_BOOL
"List of regular expressions to use when matching a password prompt" "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

View File

@@ -548,8 +548,7 @@ init_defaults(void)
#endif #endif
if ((def_rlimit_core = strdup("0,0")) == NULL) if ((def_rlimit_core = strdup("0,0")) == NULL)
goto oom; goto oom;
if (ISSET(sudo_user.flags, CAN_INTERCEPT_SETID)) def_intercept_type = dso;
def_intercept_allow_setid = true;
def_netgroup_tuple = false; def_netgroup_tuple = false;
def_sudoedit_checkdir = true; def_sudoedit_checkdir = true;
def_iolog_mode = S_IRUSR|S_IWUSR; def_iolog_mode = S_IRUSR|S_IWUSR;

View File

@@ -297,10 +297,24 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults)
goto oom; goto oom;
continue; 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 (MATCHES(*cur, "intercept_setid=")) {
if (parse_bool(*cur, sizeof("intercept_setid") - 1, int val = sudo_strtobool(*cur + sizeof("intercept_setid=") - 1);
&sudo_user.flags, CAN_INTERCEPT_SETID) == -1) if (val == -1) {
goto bad; INVALID("intercept_setid="); /* Not a fatal error. */
} else if (!append_default("intercept_allow_setid", NULL, val,
NULL, defaults)) {
goto oom;
}
continue; continue;
} }
#ifdef HAVE_SELINUX #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. */ /* 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) if (command_info == NULL)
goto oom; 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) if ((command_info[info_len++] = strdup("intercept=true")) == NULL)
goto oom; goto oom;
} }
if (def_intercept_type == trace) {
if ((command_info[info_len++] = strdup("use_ptrace=true")) == NULL)
goto oom;
}
if (def_noexec) { if (def_noexec) {
if ((command_info[info_len++] = strdup("noexec=true")) == NULL) if ((command_info[info_len++] = strdup("noexec=true")) == NULL)
goto oom; goto oom;

View File

@@ -145,7 +145,6 @@ struct sudo_user {
*/ */
#define RUNAS_USER_SPECIFIED 0x01 #define RUNAS_USER_SPECIFIED 0x01
#define RUNAS_GROUP_SPECIFIED 0x02 #define RUNAS_GROUP_SPECIFIED 0x02
#define CAN_INTERCEPT_SETID 0x04
/* /*
* Return values for sudoers_lookup(), also used as arguments for log_auth() * Return values for sudoers_lookup(), also used as arguments for log_auth()

View File

@@ -51,6 +51,8 @@
# include "exec_intercept.h" # include "exec_intercept.h"
# include "exec_ptrace.h" # include "exec_ptrace.h"
static int seccomp_trap_supported = -1;
/* Register getters and setters. */ /* Register getters and setters. */
# ifdef SECCOMP_AUDIT_ARCH_COMPAT # ifdef SECCOMP_AUDIT_ARCH_COMPAT
static inline unsigned long static inline unsigned long
@@ -736,7 +738,7 @@ done:
* Check whether seccomp(2) filtering supports ptrace(2) traps. * Check whether seccomp(2) filtering supports ptrace(2) traps.
* Only supported by Linux 4.14 and higher. * Only supported by Linux 4.14 and higher.
*/ */
bool static bool
have_seccomp_action(const char *action) have_seccomp_action(const char *action)
{ {
char line[LINE_MAX]; char line[LINE_MAX];
@@ -1274,6 +1276,24 @@ exec_ptrace_stopped(pid_t pid, int status, void *intercept)
debug_return_bool(group_stop); 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 #else
/* STUB */ /* STUB */
bool bool
@@ -1295,4 +1315,41 @@ exec_ptrace_seize(pid_t child)
{ {
return true; return true;
} }
/* STUB */
bool
exec_ptrace_intercept_supported(void)
{
return false;
}
/* STUB */
bool
exec_ptrace_subcmds_supported(void)
{
return false;
}
#endif /* HAVE_PTRACE_INTERCEPT */ #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;
}

View File

@@ -82,6 +82,7 @@ static struct sudo_settings sudo_settings[] = {
{ "cmnd_cwd" }, { "cmnd_cwd" },
{ "askpass" }, { "askpass" },
{ "intercept_setid" }, { "intercept_setid" },
{ "intercept_ptrace" },
{ NULL } { NULL }
}; };
@@ -585,8 +586,10 @@ parse_args(int argc, char **argv, int *old_optind, int *nargc, char ***nargv,
#ifdef ENABLE_SUDO_PLUGIN_API #ifdef ENABLE_SUDO_PLUGIN_API
sudo_settings[ARG_PLUGIN_DIR].value = sudo_conf_plugin_dir_path(); sudo_settings[ARG_PLUGIN_DIR].value = sudo_conf_plugin_dir_path();
#endif #endif
if (have_seccomp_action("trap")) if (exec_ptrace_intercept_supported())
sudo_settings[ARG_INTERCEPT_SETID].value = "true"; sudo_settings[ARG_INTERCEPT_SETID].value = "true";
if (exec_ptrace_subcmds_supported())
sudo_settings[ARG_INTERCEPT_PTRACE].value = "true";
if (mode == MODE_HELP) if (mode == MODE_HELP)
help(); help();

View File

@@ -650,10 +650,10 @@ bad:
static void static void
command_info_to_details(char * const info[], struct command_details *details) command_info_to_details(char * const info[], struct command_details *details)
{ {
int i;
id_t id;
char *cp;
const char *errstr; const char *errstr;
char *cp;
id_t id;
int i;
debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM); debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM);
memset(details, 0, sizeof(*details)); memset(details, 0, sizeof(*details));
@@ -857,17 +857,15 @@ command_info_to_details(char * const info[], struct command_details *details)
break; break;
} }
SET_FLAG("umask_override=", CD_OVERRIDE_UMASK) SET_FLAG("umask_override=", CD_OVERRIDE_UMASK)
SET_FLAG("use_ptrace=", CD_USE_PTRACE)
SET_FLAG("use_pty=", CD_USE_PTY) SET_FLAG("use_pty=", CD_USE_PTY)
SET_STRING("utmp_user=", utmp_user) SET_STRING("utmp_user=", utmp_user)
break; break;
} }
} }
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) { /* Only use ptrace(2) for intercept/log_subcmds if supported. */
/* Use ptrace(2) for intercept/log_subcmds if possible. */ exec_ptrace_fix_flags(details);
if (sudo_settings[ARG_INTERCEPT_SETID].value != NULL)
SET(details->flags, CD_USE_PTRACE);
}
if (!ISSET(details->flags, CD_SET_EUID)) if (!ISSET(details->flags, CD_SET_EUID))
details->cred.euid = details->cred.uid; details->cred.euid = details->cred.uid;

View File

@@ -103,6 +103,7 @@
#define ARG_CWD 24 #define ARG_CWD 24
#define ARG_ASKPASS 25 #define ARG_ASKPASS 25
#define ARG_INTERCEPT_SETID 26 #define ARG_INTERCEPT_SETID 26
#define ARG_INTERCEPT_PTRACE 27
/* /*
* Flags for tgetpass() * Flags for tgetpass()
@@ -338,6 +339,8 @@ int serialize_rlimits(char **info, size_t info_max);
bool parse_policy_rlimit(const char *str); bool parse_policy_rlimit(const char *str);
/* exec_ptrace.c */ /* 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 */ #endif /* SUDO_SUDO_H */

View File

@@ -92,7 +92,6 @@ union sudo_token_un {
/* /*
* Use ptrace-based intercept (using seccomp) on Linux if possible. * Use ptrace-based intercept (using seccomp) on Linux if possible.
* TODO: test other architectures
*/ */
#if defined(_PATH_SUDO_INTERCEPT) && defined(__linux__) #if defined(_PATH_SUDO_INTERCEPT) && defined(__linux__)
# if defined(HAVE_DECL_SECCOMP_SET_MODE_FILTER) && HAVE_DECL_SECCOMP_SET_MODE_FILTER # 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 # ifndef HAVE_PTRACE_INTERCEPT
# define HAVE_PTRACE_INTERCEPT 1 # define HAVE_PTRACE_INTERCEPT 1
# endif /* HAVE_PTRACE_INTERCEPT */ # endif /* HAVE_PTRACE_INTERCEPT */
# endif /* __amd64__ || __i386__ || __aarch64__ || __riscv */ # endif /* __amd64__ || __i386__ || __aarch64__ || __riscv || __s390__ */
# endif /* HAVE_DECL_SECCOMP_SET_MODE_FILTER */ # endif /* HAVE_DECL_SECCOMP_SET_MODE_FILTER */
#endif /* _PATH_SUDO_INTERCEPT && __linux__ */ #endif /* _PATH_SUDO_INTERCEPT && __linux__ */