diff options
author | Todd C. Miller <Todd.Miller@sudo.ws> | 2022-05-27 08:25:12 -0600 |
---|---|---|
committer | Todd C. Miller <Todd.Miller@sudo.ws> | 2022-05-27 08:25:12 -0600 |
commit | bd55de5ec981561fe624069109e43f8907101805 (patch) | |
tree | 2858a5364cf2d815876a6baeb0616c4de174d338 | |
parent | f2c0a92176dea15368e5a19a46b2355fa47987bc (diff) | |
parent | 50693c9ea4ad3d7a2168a3b5020d0f8f630056eb (diff) | |
download | sudo-bd55de5ec981561fe624069109e43f8907101805.tar.gz |
Merge branch 'main' into apparmor_support
30 files changed, 741 insertions, 95 deletions
diff --git a/docs/sudo_plugin.man.in b/docs/sudo_plugin.man.in index 8fee422f8..4fa699e21 100644 --- a/docs/sudo_plugin.man.in +++ b/docs/sudo_plugin.man.in @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.TH "SUDO_PLUGIN" "5" "February 16, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual" +.TH "SUDO_PLUGIN" "5" "May 26, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .nh .if n .ad l .SH "NAME" @@ -291,6 +291,36 @@ implied_shell=bool If the user does not specify a program on the command line, \fBsudo\fR will pass the plugin the path to the user's shell and set +\fIimplied_shell\fR. +.TP 6n +intercept_ptrace=bool +Indicates whether or not the system supports intercept +mode using +ptrace(2). +This is currently only true for Linux systems that support +seccomp(2) +filtering and the +\(lqtrap\(rq +action. +Other systems will use a dynamic shared object to implement +intercept. +.TP 6n +intercept_setid=bool +Indicates whether or not the system supports running set-user-ID +and set-group-ID binaries in intercept mode. +This is currently only true for Linux systems that support +seccomp(2) +filtering and the +\(lqtrap\(rq +action. +On systems that use a dynamic shared object to implement +intercept, the dynamic linker (ld.so or the equivalent) +will disable preloading of shared objects when executing a +set-user-ID or set-group-ID binary. +This will disable intercept mode for that program and any other +programs that it executes. +The policy plugin may refuse to execute a set-user-ID or set-group-ID +binary in intercept mode to avoid this. .TP 6n login_class=string BSD @@ -1078,6 +1108,21 @@ The specified \fInumber\fR must refer to an open file descriptor. .TP 6n +intercept=bool +If specified, +\fBsudo\fR +will intercept attempts to execute a subsequent command and perform +a policy check via the policy plugin's +\fBcheck_policy\fR() +function to determine whether or not the command is permitted. +This can be used to prevent shell escapes on supported platforms +but it has a number of limitations. +See +\fBPreventing shell escapes\fR +in +sudoers(@mansectform@) +for details. +.TP 6n iolog_compress=bool Set to true if the I/O logging plugins, if any, should compress the log data. @@ -1142,6 +1187,24 @@ on BSD systems. .TP 6n +log_subcmds=bool +If specified, +\fBsudo\fR +will call the audit plugin's +\fBaccept\fR() +function to log when the command runs a subsequent command, if supported +by the system. +If +\fIintercept\fR +is also specified, +\fIlog_subcmds\fR +will be ignored. +See +\fBPreventing shell escapes\fR +in +sudoers(@mansectform@) +for more information. +.TP 6n noexec=bool If set, prevent the command from executing other programs. .TP 6n @@ -1466,6 +1529,16 @@ Force the value specified by the \fIumask\fR option to override any umask set by PAM or login.conf. .TP 6n +use_ptrace=bool +If specified, +\fBsudo\fR +will use +ptrace(2) +to implement intercept mode if supported by the system. +This setting has no effect unless +\fIintercept\fR +is also set. +.TP 6n use_pty=bool Allocate a pseudo-terminal to run the command in, regardless of whether or not I/O logging is in use. @@ -5280,7 +5353,7 @@ The \fIcmnd_chroot\fR and \fIcmnd_cwd\fR -enties were added to the +entries were added to the \fRsettings\fR list. .TP 6n @@ -5293,6 +5366,27 @@ Version 1.18 (sudo 1.9.9) The policy may now set resource limit values in the \fRcommand_info\fR list. +The +\fIintercept\fR +and +\fIlog_subcmds\fR +entries were added to the +\fRcommand_info\fR +list. +.TP 6n +Version 1.19 (sudo 1.9.11) +The +\fIintercept_ptrace\fR +and +\fIintercept_setid\fR +entries were added to the +\fRsettings\fR +list. +The +\fIuser_ptrace\fR +entry was added to the +\fRcommand_info\fR +list. .SH "SEE ALSO" sudo.conf(@mansectform@), sudoers(@mansectform@), diff --git a/docs/sudo_plugin.mdoc.in b/docs/sudo_plugin.mdoc.in index e4432b839..24f2f5976 100644 --- a/docs/sudo_plugin.mdoc.in +++ b/docs/sudo_plugin.mdoc.in @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd February 16, 2022 +.Dd May 26, 2022 .Dt SUDO_PLUGIN @mansectform@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -269,6 +269,34 @@ to print a usage message and exit. If the user does not specify a program on the command line, .Nm sudo will pass the plugin the path to the user's shell and set +.Em implied_shell . +.It intercept_ptrace=bool +Indicates whether or not the system supports intercept +mode using +.Xr ptrace 2 . +This is currently only true for Linux systems that support +.Xr seccomp 2 +filtering and the +.Dq trap +action. +Other systems will use a dynamic shared object to implement +intercept. +.It intercept_setid=bool +Indicates whether or not the system supports running set-user-ID +and set-group-ID binaries in intercept mode. +This is currently only true for Linux systems that support +.Xr seccomp 2 +filtering and the +.Dq trap +action. +On systems that use a dynamic shared object to implement +intercept, the dynamic linker (ld.so or the equivalent) +will disable preloading of shared objects when executing a +set-user-ID or set-group-ID binary. +This will disable intercept mode for that program and any other +programs that it executes. +The policy plugin may refuse to execute a set-user-ID or set-group-ID +binary in intercept mode to avoid this. .It login_class=string .Bx login class to use when setting resource limits and nice value, @@ -965,6 +993,20 @@ system call to execute the command instead of The specified .Em number must refer to an open file descriptor. +.It intercept=bool +If specified, +.Nm sudo +will intercept attempts to execute a subsequent command and perform +a policy check via the policy plugin's +.Fn check_policy +function to determine whether or not the command is permitted. +This can be used to prevent shell escapes on supported platforms +but it has a number of limitations. +See +.Sy Preventing shell escapes +in +.Xr sudoers @mansectform@ +for details. .It iolog_compress=bool Set to true if the I/O logging plugins, if any, should compress the log data. @@ -1017,6 +1059,23 @@ The nice value, if specified, overrides the priority associated with the on .Bx systems. +.It log_subcmds=bool +If specified, +.Nm sudo +will call the audit plugin's +.Fn accept +function to log when the command runs a subsequent command, if supported +by the system. +If +.Em intercept +is also specified, +.Em log_subcmds +will be ignored. +See +.Sy Preventing shell escapes +in +.Xr sudoers @mansectform@ +for more information. .It noexec=bool If set, prevent the command from executing other programs. .It preserve_fds=list @@ -1311,6 +1370,15 @@ option is also set. Force the value specified by the .Em umask option to override any umask set by PAM or login.conf. +.It use_ptrace=bool +If specified, +.Nm sudo +will use +.Xr ptrace 2 +to implement intercept mode if supported by the system. +This setting has no effect unless +.Em intercept +is also set. .It use_pty=bool Allocate a pseudo-terminal to run the command in, regardless of whether or not I/O logging is in use. @@ -4676,7 +4744,7 @@ The .Em cmnd_chroot and .Em cmnd_cwd -enties were added to the +entries were added to the .Li settings list. .It Version 1.17 (sudo 1.9.4) @@ -4687,6 +4755,26 @@ field was added to the audit_plugin and approval_plugin structs. The policy may now set resource limit values in the .Li command_info list. +The +.Em intercept +and +.Em log_subcmds +entries were added to the +.Li command_info +list. +.It Version 1.19 (sudo 1.9.11) +The +.Em intercept_ptrace +and +.Em intercept_setid +entries were added to the +.Li settings +list. +The +.Em user_ptrace +entry was added to the +.Li command_info +list. .El .Sh SEE ALSO .Xr sudo.conf @mansectform@ , diff --git a/docs/sudoers.man.in b/docs/sudoers.man.in index b2a2e573b..ca4b96d3c 100644 --- a/docs/sudoers.man.in +++ b/docs/sudoers.man.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" @@ -3363,18 +3363,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 @@ -4284,6 +4286,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. @@ -6846,7 +6905,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 diff --git a/docs/sudoers.mdoc.in b/docs/sudoers.mdoc.in index 233270076..7809db02f 100644 --- a/docs/sudoers.mdoc.in +++ b/docs/sudoers.mdoc.in @@ -25,7 +25,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 @@ -3186,18 +3186,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 @@ -4059,6 +4061,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. @@ -6343,7 +6396,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 diff --git a/etc/sudo-logsrvd.pp b/etc/sudo-logsrvd.pp index 1e49c65c1..242b21986 100644 --- a/etc/sudo-logsrvd.pp +++ b/etc/sudo-logsrvd.pp @@ -132,6 +132,10 @@ This makes it possible to have all sudo I/O logs on a central server." osrelease=`echo "$pp_rpm_distro" | sed -e 's/^[^0-9]*\([0-9]\{1,2\}\).*/\1/'` case "$pp_rpm_distro" in centos*|rhel*|f[0-9]*) + # CentOS Stream has a single-digit version + if test $osrelease -lt 10; then + osrelease="${osrelease}0" + fi pp_rpm_release="$pp_rpm_release.el${osrelease%%[0-9]}" ;; sles*) diff --git a/etc/sudo-python.pp b/etc/sudo-python.pp index de7734072..9250da4f7 100644 --- a/etc/sudo-python.pp +++ b/etc/sudo-python.pp @@ -88,6 +88,10 @@ osrelease=`echo "$pp_rpm_distro" | sed -e 's/^[^0-9]*\([0-9]\{1,2\}\).*/\1/'` case "$pp_rpm_distro" in centos*|rhel*|f[0-9]*) + # CentOS Stream has a single-digit version + if test $osrelease -lt 10; then + osrelease="${osrelease}0" + fi pp_rpm_release="$pp_rpm_release.el${osrelease%%[0-9]}" ;; sles*) diff --git a/etc/sudo.pp b/etc/sudo.pp index 0c96a75bd..97d82856c 100644 --- a/etc/sudo.pp +++ b/etc/sudo.pp @@ -163,6 +163,10 @@ still allow people to get their work done." osrelease=`echo "$pp_rpm_distro" | sed -e 's/^[^0-9]*\([0-9]\{1,2\}\).*/\1/'` case "$pp_rpm_distro" in centos*|rhel*|f[0-9]*) + # CentOS Stream has a single-digit version + if test $osrelease -lt 10; then + osrelease="${osrelease}0" + fi pp_rpm_release="$pp_rpm_release.el${osrelease%%[0-9]}" ;; sles*) diff --git a/include/sudo_plugin.h b/include/sudo_plugin.h index 873805cdb..032a5fd48 100644 --- a/include/sudo_plugin.h +++ b/include/sudo_plugin.h @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2020 Todd C. Miller <Todd.Miller@sudo.ws> + * Copyright (c) 2009-2022 Todd C. Miller <Todd.Miller@sudo.ws> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,7 +21,7 @@ /* API version major/minor */ #define SUDO_API_VERSION_MAJOR 1 -#define SUDO_API_VERSION_MINOR 18 +#define SUDO_API_VERSION_MINOR 19 #define SUDO_API_MKVERSION(x, y) (((x) << 16) | (y)) #define SUDO_API_VERSION SUDO_API_MKVERSION(SUDO_API_VERSION_MAJOR, SUDO_API_VERSION_MINOR) diff --git a/logsrvd/logsrvd.c b/logsrvd/logsrvd.c index 097a4aa36..b46ad2de7 100644 --- a/logsrvd/logsrvd.c +++ b/logsrvd/logsrvd.c @@ -743,7 +743,7 @@ handle_client_message(uint8_t *buf, size_t len, /* TODO: can we extract type_case without unpacking for relay case? */ msg = client_message__unpack(NULL, len, buf); if (msg == NULL) { - sudo_warnx("unable to unpack %s size %zu", "ClientMessage", len); + sudo_warnx(U_("unable to unpack %s size %zu"), "ClientMessage", len); debug_return_bool(false); } diff --git a/logsrvd/logsrvd_journal.c b/logsrvd/logsrvd_journal.c index eac3563e2..664f1ae9a 100644 --- a/logsrvd/logsrvd_journal.c +++ b/logsrvd/logsrvd_journal.c @@ -280,7 +280,7 @@ journal_seek(struct timespec *target, struct connection_closure *closure) client_message__free_unpacked(msg, NULL); msg = client_message__unpack(NULL, msg_len, buf); if (msg == NULL) { - sudo_warnx("unable to unpack %s size %zu", "ClientMessage", + sudo_warnx(U_("unable to unpack %s size %zu"), "ClientMessage", (size_t)msg_len); closure->errstr = _("invalid journal file, unable to restart"); break; diff --git a/logsrvd/logsrvd_relay.c b/logsrvd/logsrvd_relay.c index 77695d2b2..f23b88f90 100644 --- a/logsrvd/logsrvd_relay.c +++ b/logsrvd/logsrvd_relay.c @@ -644,7 +644,7 @@ handle_server_message(uint8_t *buf, size_t len, struct connection_closure *closu sudo_debug_printf(SUDO_DEBUG_INFO, "%s: unpacking ServerMessage", __func__); msg = server_message__unpack(NULL, len, buf); if (msg == NULL) { - sudo_warnx("unable to unpack %s size %zu", "ServerMessage", len); + sudo_warnx(U_("unable to unpack %s size %zu"), "ServerMessage", len); debug_return_bool(false); } diff --git a/logsrvd/sendlog.c b/logsrvd/sendlog.c index eaed31331..094bd369d 100644 --- a/logsrvd/sendlog.c +++ b/logsrvd/sendlog.c @@ -1214,7 +1214,7 @@ handle_server_message(uint8_t *buf, size_t len, sudo_debug_printf(SUDO_DEBUG_INFO, "%s: unpacking ServerMessage", __func__); msg = server_message__unpack(NULL, len, buf); if (msg == NULL) { - sudo_warnx("unable to unpack %s size %zu", "ServerMessage", len); + sudo_warnx(U_("unable to unpack %s size %zu"), "ServerMessage", len); debug_return_bool(false); } diff --git a/plugins/python/regress/testdata/check_multiple_approval_plugin_and_arguments.stdout b/plugins/python/regress/testdata/check_multiple_approval_plugin_and_arguments.stdout index fa29e46a5..d0c1566cf 100644 --- a/plugins/python/regress/testdata/check_multiple_approval_plugin_and_arguments.stdout +++ b/plugins/python/regress/testdata/check_multiple_approval_plugin_and_arguments.stdout @@ -26,7 +26,7 @@ "INFO1=VALUE1", "info2=value2" ], - "version": "1.18" + "version": "1.19" } (APPROVAL 2) Constructed: { @@ -56,7 +56,7 @@ "INFO1=VALUE1", "info2=value2" ], - "version": "1.18" + "version": "1.19" } (APPROVAL 1) Show version was called with arguments: (0,) Python approval plugin (API 1.0): ApprovalTestPlugin (loaded from 'SRC_DIR/regress/plugin_approval_test.py') diff --git a/plugins/sudoers/def_data.c b/plugins/sudoers/def_data.c index 8849e0942..41de8fc0b 100644 --- a/plugins/sudoers/def_data.c +++ b/plugins/sudoers/def_data.c @@ -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, @@ -658,6 +664,10 @@ struct sudo_defs_types sudo_defs_table[] = { 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, + }, { "apparmor_profile", T_STR, N_("AppArmor profile to use in the new security context: %s"), NULL, diff --git a/plugins/sudoers/def_data.h b/plugins/sudoers/def_data.h index 754b00075..a8deba41b 100644 --- a/plugins/sudoers/def_data.h +++ b/plugins/sudoers/def_data.h @@ -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 154 +#define def_intercept_type (sudo_defs_table[I_INTERCEPT_TYPE].sd_un.tuple) #define I_APPARMOR_PROFILE 153 #define def_apparmor_profile (sudo_defs_table[I_APPARMOR_PROFILE].sd_un.str) @@ -321,5 +323,7 @@ enum def_tuple { tty, kernel, sudo, - json + json, + dso, + trace }; diff --git a/plugins/sudoers/def_data.in b/plugins/sudoers/def_data.in index 4ac670c2c..6372048c9 100644 --- a/plugins/sudoers/def_data.in +++ b/plugins/sudoers/def_data.in @@ -475,6 +475,10 @@ 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 apparmor_profile T_STR - "AppArmor profile to use in the new security context: %s" + "AppArmor profile to use in the new security context: %s"
\ No newline at end of file diff --git a/plugins/sudoers/defaults.c b/plugins/sudoers/defaults.c index 9521b2ec0..d5bd8080d 100644 --- a/plugins/sudoers/defaults.c +++ b/plugins/sudoers/defaults.c @@ -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; diff --git a/plugins/sudoers/log_client.c b/plugins/sudoers/log_client.c index 024f7a5fe..be47a5c07 100644 --- a/plugins/sudoers/log_client.c +++ b/plugins/sudoers/log_client.c @@ -1615,7 +1615,7 @@ handle_server_message(uint8_t *buf, size_t len, sudo_debug_printf(SUDO_DEBUG_INFO, "%s: unpacking ServerMessage", __func__); msg = server_message__unpack(NULL, len, buf); if (msg == NULL) { - sudo_warnx("%s", U_("unable to unpack ServerMessage")); + sudo_warnx(U_("unable to unpack %s size %zu"), "ServerMessage", len); debug_return_bool(false); } diff --git a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c index e346e3c6a..080889eb6 100644 --- a/plugins/sudoers/policy.c +++ b/plugins/sudoers/policy.c @@ -186,8 +186,9 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults) } /* Parse command line settings. */ - sudo_mode = 0; + sudo_user.flags = 0; user_closefrom = -1; + sudo_mode = 0; for (cur = info->settings; *cur != NULL; cur++) { if (MATCHES(*cur, "closefrom=")) { errno = 0; @@ -297,9 +298,15 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults) goto oom; continue; } + if (MATCHES(*cur, "intercept_ptrace=")) { + if (parse_bool(*cur, sizeof("intercept_ptrace") - 1, &sudo_user.flags, + HAVE_INTERCEPT_PTRACE) == -1) + goto bad; + continue; + } if (MATCHES(*cur, "intercept_setid=")) { - if (parse_bool(*cur, sizeof("intercept_setid") - 1, - &sudo_user.flags, CAN_INTERCEPT_SETID) == -1) + if (parse_bool(*cur, sizeof("intercept_setid") - 1, &sudo_user.flags, + CAN_INTERCEPT_SETID) == -1) goto bad; continue; } @@ -567,6 +574,19 @@ sudoers_policy_deserialize_info(void *v, struct defaults_list *defaults) goto bad; } + /* + * Set intercept defaults based on flags set above. + * We pass -1 as the operator to indicate it is set by the front end. + */ + if (ISSET(sudo_user.flags, HAVE_INTERCEPT_PTRACE)) { + if (!append_default("intercept_type", "trace", -1, NULL, defaults)) + goto oom; + } + if (ISSET(sudo_user.flags, CAN_INTERCEPT_SETID)) { + if (!append_default("intercept_allow_setid", NULL, -1, NULL, defaults)) + goto oom; + } + #ifdef NO_ROOT_MAILER eventlog_set_mailuid(user_uid); #endif @@ -615,7 +635,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; @@ -787,6 +807,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; diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index 98a0ee0bb..71e8ba256 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -1633,6 +1633,39 @@ cb_mailsub(const char *file, int line, int column, debug_return_bool(true); } +static bool +cb_intercept_type(const char *file, int line, int column, + const union sudo_defs_val *sd_un, int op) +{ + debug_decl(cb_intercept_type, SUDOERS_DEBUG_PLUGIN); + + if (op != -1) { + /* Set explicitly in sudoers. */ + if (sd_un->tuple == dso) { + /* Reset intercept_allow_setid default value. */ + if (!ISSET(sudo_user.flags, USER_INTERCEPT_SETID)) + def_intercept_allow_setid = false; + } + } + + debug_return_bool(true); +} + +static bool +cb_intercept_allow_setid(const char *file, int line, int column, + const union sudo_defs_val *sd_un, int op) +{ + debug_decl(cb_intercept_allow_setid, SUDOERS_DEBUG_PLUGIN); + + /* Operator will be -1 if set by front-end. */ + if (op != -1) { + /* Set explicitly in sudoers. */ + SET(sudo_user.flags, USER_INTERCEPT_SETID); + } + + debug_return_bool(true); +} + /* * Set parse Defaults callbacks. * We do this here instead in def_data.in so we don't have to @@ -1692,6 +1725,8 @@ set_callbacks(void) sudo_defs_table[I_MAILTO].callback = cb_mailto; sudo_defs_table[I_MAILSUB].callback = cb_mailsub; sudo_defs_table[I_PASSPROMPT_REGEX].callback = cb_passprompt_regex; + sudo_defs_table[I_INTERCEPT_TYPE].callback = cb_intercept_type; + sudo_defs_table[I_INTERCEPT_ALLOW_SETID].callback = cb_intercept_allow_setid; debug_return; } diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 1a57385e9..c506c57c4 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -149,6 +149,8 @@ struct sudo_user { #define RUNAS_USER_SPECIFIED 0x01 #define RUNAS_GROUP_SPECIFIED 0x02 #define CAN_INTERCEPT_SETID 0x04 +#define HAVE_INTERCEPT_PTRACE 0x08 +#define USER_INTERCEPT_SETID 0x10 /* * Return values for sudoers_lookup(), also used as arguments for log_auth() diff --git a/src/exec.c b/src/exec.c index 111ed2d02..db3c8f4ce 100644 --- a/src/exec.c +++ b/src/exec.c @@ -321,7 +321,7 @@ sudo_terminated(struct command_status *cstat) debug_return_bool(false); } -#if SUDO_API_VERSION != SUDO_API_MKVERSION(1, 18) +#if SUDO_API_VERSION != SUDO_API_MKVERSION(1, 19) # error "Update sudo_needs_pty() after changing the plugin API" #endif static bool diff --git a/src/exec_intercept.c b/src/exec_intercept.c index 1d1febf55..cbad78f72 100644 --- a/src/exec_intercept.c +++ b/src/exec_intercept.c @@ -619,7 +619,7 @@ intercept_read(int fd, struct intercept_closure *closure) unpack: req = intercept_request__unpack(NULL, closure->len, closure->buf); if (req == NULL) { - sudo_warnx("unable to unpack %s size %zu", "InterceptRequest", + sudo_warnx(U_("unable to unpack %s size %zu"), "InterceptRequest", (size_t)closure->len); goto done; } diff --git a/src/exec_ptrace.c b/src/exec_ptrace.c index 6661e943f..7d4b829ef 100644 --- a/src/exec_ptrace.c +++ b/src/exec_ptrace.c @@ -51,6 +51,15 @@ # include "exec_intercept.h" # include "exec_ptrace.h" +/* We need to take care when ptracing 32-bit binaries on 64-bit kernels. */ +# ifdef __LP64__ +# define COMPAT_FLAG 0x01 +# else +# define COMPAT_FLAG 0x00 +# endif + +static int seccomp_trap_supported = -1; + /* Register getters and setters. */ # ifdef SECCOMP_AUDIT_ARCH_COMPAT static inline unsigned long @@ -107,9 +116,9 @@ static inline void set_sc_arg1(struct sudo_ptrace_regs *regs, unsigned long addr) { if (regs->compat) { - compat_reg_arg1(regs->u.compat) = addr; + compat_reg_set_arg1(regs->u.compat, addr); } else { - reg_arg1(regs->u.native) = addr; + reg_set_arg1(regs->u.native, addr); } } @@ -127,9 +136,9 @@ static inline void set_sc_arg2(struct sudo_ptrace_regs *regs, unsigned long addr) { if (regs->compat) { - compat_reg_arg2(regs->u.compat) = addr; + compat_reg_set_arg2(regs->u.compat, addr); } else { - reg_arg2(regs->u.native) = addr; + reg_set_arg2(regs->u.native, addr); } } @@ -148,9 +157,9 @@ static inline void set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) { if (regs->compat) { - compat_reg_arg3(regs->u.compat) = addr; + compat_reg_set_arg3(regs->u.compat, addr); } else { - reg_arg3(regs->u.native) = addr; + reg_set_arg3(regs->u.native, addr); } } @@ -168,9 +177,9 @@ static inline void set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) { if (regs->compat) { - compat_reg_arg4(regs->u.compat) = addr; + compat_reg_set_arg4(regs->u.compat, addr); } else { - reg_arg4(regs->u.native) = addr; + reg_set_arg4(regs->u.native, addr); } } # endif /* notyet */ @@ -210,7 +219,7 @@ get_sc_arg1(struct sudo_ptrace_regs *regs) static inline void set_sc_arg1(struct sudo_ptrace_regs *regs, unsigned long addr) { - reg_arg1(regs->u.native) = addr; + reg_set_arg1(regs->u.native, addr); } static inline unsigned long @@ -222,7 +231,7 @@ get_sc_arg2(struct sudo_ptrace_regs *regs) static inline void set_sc_arg2(struct sudo_ptrace_regs *regs, unsigned long addr) { - reg_arg2(regs->u.native) = addr; + reg_set_arg2(regs->u.native, addr); } static inline unsigned long @@ -235,7 +244,7 @@ get_sc_arg3(struct sudo_ptrace_regs *regs) static inline void set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) { - reg_arg3(regs->u.native) = addr; + reg_set_arg3(regs->u.native, addr); } static inline unsigned long @@ -247,7 +256,7 @@ get_sc_arg4(struct sudo_ptrace_regs *regs) static inline void set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) { - reg_arg4(regs->u.native) = addr; + reg_set_arg4(regs->u.native, addr); } # endif /* notyet */ # endif /* SECCOMP_AUDIT_ARCH_COMPAT */ @@ -262,13 +271,19 @@ set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) static bool ptrace_getregs(int pid, struct sudo_ptrace_regs *regs, bool compat) { - struct iovec iov; debug_decl(ptrace_getregs, SUDO_DEBUG_EXEC); +# ifdef __mips__ + /* PTRACE_GETREGSET has bugs with the MIPS o32 ABI at least. */ + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s->u) == -1) + debug_return_bool(false); +# else + struct iovec iov; iov.iov_base = ®s->u; iov.iov_len = sizeof(regs->u); if (ptrace(PTRACE_GETREGSET, pid, (void *)NT_PRSTATUS, &iov) == -1) debug_return_bool(false); +# endif /* __mips__ */ /* Machine-dependent parameters to support compat binaries. */ if (compat) { @@ -289,21 +304,19 @@ ptrace_getregs(int pid, struct sudo_ptrace_regs *regs, bool compat) static bool ptrace_setregs(int pid, struct sudo_ptrace_regs *regs) { - struct iovec iov; debug_decl(ptrace_setregs, SUDO_DEBUG_EXEC); -# ifdef SECCOMP_AUDIT_ARCH_COMPAT - if (regs->compat) { - iov.iov_base = ®s->u.compat; - iov.iov_len = sizeof(regs->u.compat); - } else -#endif - { - iov.iov_base = ®s->u.native; - iov.iov_len = sizeof(regs->u.native); - } +# ifdef __mips__ + /* PTRACE_SETREGSET has bugs with the MIPS o32 ABI at least. */ + if (ptrace(PTRACE_SETREGS, pid, NULL, ®s->u) == -1) + debug_return_bool(false); +# else + struct iovec iov; + iov.iov_base = ®s->u; + iov.iov_len = sizeof(regs->u); if (ptrace(PTRACE_SETREGSET, pid, (void *)NT_PRSTATUS, &iov) == -1) debug_return_bool(false); +# endif /* __mips__ */ debug_return_bool(true); } @@ -631,7 +644,7 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, /* Reserve argv and envp at the start of argbuf so they are aligned. */ if ((argc + 1 + envc + 1) * sizeof(unsigned long) >= bufsize) { - sudo_warnx("%s", U_("insufficient space for argv and envp")); + sudo_warnx("%s", U_("insufficient space for execve arguments")); goto bad; } argv = (char **)argbuf; @@ -642,7 +655,8 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, /* Read argv */ len = ptrace_read_vec(pid, regs, argv_addr, argv, strtab, bufsize); if (len == (size_t)-1) { - sudo_warn(U_("unable to read execve argv for process %d"), (int)pid); + sudo_warn(U_("unable to read execve %s for process %d"), + "argv", (int)pid); goto bad; } strtab += len; @@ -651,7 +665,8 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, /* Read envp */ len = ptrace_read_vec(pid, regs, envp_addr, envp, strtab, bufsize); if (len == (size_t)-1) { - sudo_warn(U_("unable to read execve envp for process %d"), (int)pid); + sudo_warn(U_("unable to read execve %s for process %d"), + "envp", (int)pid); goto bad; } strtab += len; @@ -660,7 +675,8 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out, /* Read the pathname. */ len = ptrace_read_string(pid, path_addr, strtab, bufsize); if (len == (size_t)-1) { - sudo_warn(U_("unable to read execve pathname for process %d"), (int)pid); + sudo_warn(U_("unable to read execve %s for process %d"), + "pathname", (int)pid); goto bad; } pathname = strtab; @@ -736,7 +752,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]; @@ -773,16 +789,27 @@ set_exec_filter(void) struct sock_filter exec_filter[] = { /* Load architecture value (AUDIT_ARCH_*) into the accumulator. */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)), +# ifdef SECCOMP_AUDIT_ARCH_COMPAT2 + /* Match on the compat2 architecture or jump to the compat check. */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_AUDIT_ARCH_COMPAT2, 0, 4), + /* Load syscall number into the accumulator. */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), + /* Jump to trace for compat2 execve(2)/execveat(2), else allow. */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, COMPAT2_execve, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, COMPAT2_execveat, 0, 13), + /* Trace execve(2)/execveat(2) syscalls (w/ compat flag) */ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE | COMPAT_FLAG), +# endif /* SECCOMP_AUDIT_ARCH_COMPAT2 */ # ifdef SECCOMP_AUDIT_ARCH_COMPAT /* Match on the compat architecture or jump to the native arch check. */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_AUDIT_ARCH_COMPAT, 0, 4), /* Load syscall number into the accumulator. */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), - /* Jump to trace for compat execve(2)/execveat(2), else try native. */ + /* Jump to trace for compat execve(2)/execveat(2), else allow. */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, COMPAT_execve, 1, 0), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, COMPAT_execveat, 0, 8), /* Trace execve(2)/execveat(2) syscalls (w/ compat flag) */ - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE | 0x1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE | COMPAT_FLAG), # endif /* SECCOMP_AUDIT_ARCH_COMPAT */ /* Jump to the end unless the architecture matches. */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_AUDIT_ARCH, 0, 6), @@ -1274,6 +1301,29 @@ exec_ptrace_stopped(pid_t pid, int status, void *intercept) debug_return_bool(group_stop); } + +bool +exec_ptrace_intercept_supported(void) +{ +# ifdef __mips__ + /* MIPS doesn't support changing the syscall return value. */ + return false; +# else + if (seccomp_trap_supported == -1) + seccomp_trap_supported = have_seccomp_action("trap"); + + return seccomp_trap_supported == true; +# endif +} + +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 +1345,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; +} diff --git a/src/exec_ptrace.h b/src/exec_ptrace.h index bdc8a45ad..7f26283a8 100644 --- a/src/exec_ptrace.h +++ b/src/exec_ptrace.h @@ -37,6 +37,15 @@ # define __X32_SYSCALL_BIT 0x40000000 #endif +#ifdef __mips__ +# ifndef __NR_O32_Linux +# define __NR_O32_Linux 4000 +# endif +# ifndef __NR_N32_Linux +# define __NR_N32_Linux 6000 +# endif +#endif + /* Align address to a (compat) word boundary. */ #define WORDALIGN(_a, _r) \ (((_a) + ((long)(_r).wordsize - 1L)) & ~((long)(_r).wordsize - 1L)) @@ -141,20 +150,61 @@ # else # error "Unsupported MIPS ABI" # endif -/* Untested/incomplete. +/* * If called via syscall(__NR_###), v0 holds __NR_O32_Linux and the real - * syscall the first arg (a0) and other args are shifted by one. - * We don't currently support this. + * syscall is in the first arg (a0). The actual args are shifted by one. * MIPS does not support setting the syscall return value via ptrace. */ # define sudo_pt_regs struct pt_regs -# define reg_syscall(x) (x).regs[2] /* v0 */ +# define reg_syscall(_r) ({ \ + __u64 _nr; \ + if ((_r).regs[2] == __NR_O32_Linux) \ + _nr = (_r).regs[4]; /* a0 */ \ + else \ + _nr = (_r).regs[2]; /* v0 */ \ + _nr; \ +}) # define reg_retval(x) (x).regs[2] /* v0 */ # define reg_sp(x) (x).regs[29] /* sp */ -# define reg_arg1(x) (x).regs[4] /* a0 */ -# define reg_arg2(x) (x).regs[5] /* a1 */ -# define reg_arg3(x) (x).regs[6] /* a2 */ -# define reg_arg4(x) (x).regs[7] /* a3 */ +# define reg_arg1(x) \ + ((x).regs[2] == __NR_O32_Linux ? (x).regs[5] : (x).regs[4]) +# define reg_set_arg1(_r, _v) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[5] = _v; /* a1 */ \ + else \ + (_r).regs[4] = _v; /* a0 */ \ +} while (0) +# define reg_arg2(x) \ + ((x).regs[2] == __NR_O32_Linux ? (x).regs[6] : (x).regs[5]) +# define reg_set_arg2(_r, _v) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[6] = _v; /* a2 */ \ + else \ + (_r).regs[5] = _v; /* a1 */ \ +} while (0) +# define reg_arg3(x) \ + ((x).regs[2] == __NR_O32_Linux ? (x).regs[7] : (x).regs[6]) +# define reg_set_arg3(_r, _v) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[7] = _v; /* a3 */ \ + else \ + (_r).regs[6] = _v; /* a2 */ \ +} while (0) +/* XXX - reg_arg4 probably wrong for syscall() type calls on 032. */ +# define reg_arg4(x) \ + ((x).regs[2] == __NR_O32_Linux ? (x).regs[8] : (x).regs[7]) +# define reg_set_arg4(_r, _v) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[8] = _v; /* a4 */ \ + else \ + (_r).regs[7] = _v; /* a3 */ \ +} while (0) +# define reg_set_syscall(_r, _nr) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[4] = _nr; /* a0 */ \ + else \ + (_r).regs[2] = _nr; /* v0 */ \ +} while (0) #elif defined(__powerpc__) # if defined(__powerpc64__) # if BYTE_ORDER == LITTLE_ENDIAN @@ -274,6 +324,43 @@ struct arm_pt_regs { # define compat_reg_arg3(x) (x).uregs[2] /* r2 */ # define compat_reg_arg4(x) (x).uregs[3] /* r3 */ # define compat_reg_set_syscall(_r, _nr) reg_set_syscall(_r, _nr) +#elif defined(__mips__) +# if _MIPS_SIM == _MIPS_SIM_ABI64 +/* MIPS o32/n32 binary compatibility on a mips64 system. */ +# if BYTE_ORDER == LITTLE_ENDIAN +# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_MIPSEL +# define SECCOMP_AUDIT_ARCH_COMPAT2 AUDIT_ARCH_MIPSEL64N32 +# else +# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_MIPS +# define SECCOMP_AUDIT_ARCH_COMPAT2 AUDIT_ARCH_MIPS64N32 +# endif +# define COMPAT_execve __NR_O32_Linux + 11 +# define COMPAT_execveat __NR_O32_Linux + 356 +# define COMPAT2_execve __NR_N32_Linux + 57 +# define COMPAT2_execveat __NR_N32_Linux + 320 +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# if BYTE_ORDER == LITTLE_ENDIAN +# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_MIPSEL +# else +# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_MIPS +# endif +# define COMPAT_execve __NR_O32_Linux + 11 +# define COMPAT_execveat __NR_O32_Linux + 356 +# endif /* _MIPS_SIM_ABI64 */ +/* MIPS ABIs use a common ptrace interface. */ +# define compat_sudo_pt_regs struct pt_regs +# define compat_reg_syscall(x) reg_syscall(x) +# define compat_reg_retval(x) reg_retval(x) +# define compat_reg_sp(x) reg_sp(x) +# define compat_reg_arg1(x) reg_arg1(x) +# define compat_reg_set_arg1(_r, _v) reg_set_arg1(_r, _v) +# define compat_reg_arg2(x) reg_arg2(x) +# define compat_reg_set_arg2(_r, _v) reg_set_arg2(_r, _v) +# define compat_reg_arg3(x) reg_arg3(x) +# define compat_reg_set_arg3(_r, _v) reg_set_arg3(_r, _v) +# define compat_reg_arg4(x) reg_arg4(x) +# define compat_reg_set_arg4(_r, _v) reg_set_arg4(_r, _v) +# define compat_reg_set_syscall(_r, _nr) reg_set_syscall(_r, _nr) #elif defined(__powerpc64__) struct ppc_pt_regs { unsigned int gpr[32]; @@ -333,6 +420,90 @@ struct ppc_pt_regs { } while (0) #endif +/* Set the syscall arguments the "normal" way by default. */ +#ifndef reg_set_arg1 +# define reg_set_arg1(_r, _v) do { \ + reg_arg1(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg1 +# define compat_reg_set_arg1(_r, _v) do { \ + compat_reg_arg1(_r) = (_v); \ +} while (0) +#endif +#ifndef reg_set_arg2 +# define reg_set_arg2(_r, _v) do { \ + reg_arg2(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg2 +# define compat_reg_set_arg2(_r, _v) do { \ + compat_reg_arg2(_r) = (_v); \ +} while (0) +#endif +#ifndef reg_set_arg3 +# define reg_set_arg3(_r, _v) do { \ + reg_arg3(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg3 +# define compat_reg_set_arg3(_r, _v) do { \ + compat_reg_arg3(_r) = (_v); \ +} while (0) +#endif +#ifndef reg_set_arg4 +# define reg_set_arg4(_r, _v) do { \ + reg_arg4(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg4 +# define compat_reg_set_arg4(_r, _v) do { \ + compat_reg_arg4(_r) = (_v); \ +} while (0) +#endif + +/* Set the syscall arguments the "normal" way by default. */ +#ifndef reg_set_arg1 +# define reg_set_arg1(_r, _v) do { \ + reg_arg1(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg1 +# define compat_reg_set_arg1(_r, _v) do { \ + compat_reg_arg1(_r) = (_v); \ +} while (0) +#endif +#ifndef reg_set_arg2 +# define reg_set_arg2(_r, _v) do { \ + reg_arg2(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg2 +# define compat_reg_set_arg2(_r, _v) do { \ + compat_reg_arg2(_r) = (_v); \ +} while (0) +#endif +#ifndef reg_set_arg3 +# define reg_set_arg3(_r, _v) do { \ + reg_arg3(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg3 +# define compat_reg_set_arg3(_r, _v) do { \ + compat_reg_arg3(_r) = (_v); \ +} while (0) +#endif +#ifndef reg_set_arg4 +# define reg_set_arg4(_r, _v) do { \ + reg_arg4(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg4 +# define compat_reg_set_arg4(_r, _v) do { \ + compat_reg_arg4(_r) = (_v); \ +} while (0) +#endif + struct sudo_ptrace_regs { union { sudo_pt_regs native; diff --git a/src/parse_args.c b/src/parse_args.c index 5c2a62aed..7a8da9209 100644 --- a/src/parse_args.c +++ b/src/parse_args.c @@ -82,6 +82,7 @@ static struct sudo_settings sudo_settings[] = { { "cmnd_cwd" }, { "askpass" }, { "intercept_setid" }, + { "intercept_ptrace" }, { "apparmor_profile" }, { NULL } }; @@ -586,8 +587,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(); diff --git a/src/sudo.c b/src/sudo.c index fcf2492d4..4f0f8fa35 100644 --- a/src/sudo.c +++ b/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)); @@ -860,17 +860,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; diff --git a/src/sudo.h b/src/sudo.h index 2719eb698..aa018ccd6 100644 --- a/src/sudo.h +++ b/src/sudo.h @@ -103,7 +103,8 @@ #define ARG_CWD 24 #define ARG_ASKPASS 25 #define ARG_INTERCEPT_SETID 26 -#define ARG_APPARMOR_PROFILE 27 +#define ARG_INTERCEPT_PTRACE 27 +#define ARG_APPARMOR_PROFILE 28 /* * Flags for tgetpass() @@ -344,6 +345,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 */ diff --git a/src/sudo_exec.h b/src/sudo_exec.h index c34172e59..f37901b42 100644 --- a/src/sudo_exec.h +++ b/src/sudo_exec.h @@ -92,15 +92,15 @@ union sudo_token_un { /* * Use ptrace-based intercept (using seccomp) on Linux if possible. - * TODO: test other architectures + * On MIPS we can't change the syscall return and only support log_subcmds. */ #if defined(_PATH_SUDO_INTERCEPT) && defined(__linux__) # if defined(HAVE_DECL_SECCOMP_SET_MODE_FILTER) && HAVE_DECL_SECCOMP_SET_MODE_FILTER -# if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) || defined(__powerpc__) || (defined(__riscv) && __riscv_xlen == 64) || defined(__s390__) +# if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) || defined(__mips__) || defined(__powerpc__) || (defined(__riscv) && __riscv_xlen == 64) || defined(__s390__) # 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__ */ diff --git a/src/sudo_intercept.c b/src/sudo_intercept.c index 30a28eee0..9a21f6714 100644 --- a/src/sudo_intercept.c +++ b/src/sudo_intercept.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2021 Todd C. Miller <Todd.Miller@sudo.ws> + * Copyright (c) 2021-2022 Todd C. Miller <Todd.Miller@sudo.ws> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -232,7 +232,7 @@ typedef struct interpose_s { static int my_execve(const char *cmnd, char * const argv[], char * const envp[]) { - return exec_wrapper(cmnd, argv, environ, false); + return exec_wrapper(cmnd, argv, envp, false); } static int @@ -335,7 +335,7 @@ sudo_shl_get_next(const char *symbol, short type) sudo_dso_public int execve(const char *cmnd, char * const argv[], char * const envp[]) { - return exec_wrapper(cmnd, argv, environ, false); + return exec_wrapper(cmnd, argv, envp, false); } sudo_dso_public int |