summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <Todd.Miller@sudo.ws>2022-05-27 08:25:12 -0600
committerTodd C. Miller <Todd.Miller@sudo.ws>2022-05-27 08:25:12 -0600
commitbd55de5ec981561fe624069109e43f8907101805 (patch)
tree2858a5364cf2d815876a6baeb0616c4de174d338
parentf2c0a92176dea15368e5a19a46b2355fa47987bc (diff)
parent50693c9ea4ad3d7a2168a3b5020d0f8f630056eb (diff)
downloadsudo-bd55de5ec981561fe624069109e43f8907101805.tar.gz
Merge branch 'main' into apparmor_support
-rw-r--r--docs/sudo_plugin.man.in98
-rw-r--r--docs/sudo_plugin.mdoc.in92
-rw-r--r--docs/sudoers.man.in75
-rw-r--r--docs/sudoers.mdoc.in69
-rw-r--r--etc/sudo-logsrvd.pp4
-rw-r--r--etc/sudo-python.pp4
-rw-r--r--etc/sudo.pp4
-rw-r--r--include/sudo_plugin.h4
-rw-r--r--logsrvd/logsrvd.c2
-rw-r--r--logsrvd/logsrvd_journal.c2
-rw-r--r--logsrvd/logsrvd_relay.c2
-rw-r--r--logsrvd/sendlog.c2
-rw-r--r--plugins/python/regress/testdata/check_multiple_approval_plugin_and_arguments.stdout4
-rw-r--r--plugins/sudoers/def_data.c10
-rw-r--r--plugins/sudoers/def_data.h6
-rw-r--r--plugins/sudoers/def_data.in6
-rw-r--r--plugins/sudoers/defaults.c3
-rw-r--r--plugins/sudoers/log_client.c2
-rw-r--r--plugins/sudoers/policy.c32
-rw-r--r--plugins/sudoers/sudoers.c35
-rw-r--r--plugins/sudoers/sudoers.h2
-rw-r--r--src/exec.c2
-rw-r--r--src/exec_intercept.c2
-rw-r--r--src/exec_ptrace.c149
-rw-r--r--src/exec_ptrace.h187
-rw-r--r--src/parse_args.c5
-rw-r--r--src/sudo.c14
-rw-r--r--src/sudo.h7
-rw-r--r--src/sudo_exec.h6
-rw-r--r--src/sudo_intercept.c6
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, &regs->u) == -1)
+ debug_return_bool(false);
+# else
+ struct iovec iov;
iov.iov_base = &regs->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 = &regs->u.compat;
- iov.iov_len = sizeof(regs->u.compat);
- } else
-#endif
- {
- iov.iov_base = &regs->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, &regs->u) == -1)
+ debug_return_bool(false);
+# else
+ struct iovec iov;
+ iov.iov_base = &regs->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