Check the policy for ptrace-based intercept mode.
This commit is contained in:
3
MANIFEST
3
MANIFEST
@@ -1183,10 +1183,11 @@ src/env_hooks.c
|
|||||||
src/exec.c
|
src/exec.c
|
||||||
src/exec_common.c
|
src/exec_common.c
|
||||||
src/exec_intercept.c
|
src/exec_intercept.c
|
||||||
src/exec_ptrace.c
|
src/exec_intercept.h
|
||||||
src/exec_monitor.c
|
src/exec_monitor.c
|
||||||
src/exec_nopty.c
|
src/exec_nopty.c
|
||||||
src/exec_preload.c
|
src/exec_preload.c
|
||||||
|
src/exec_ptrace.c
|
||||||
src/exec_pty.c
|
src/exec_pty.c
|
||||||
src/get_pty.c
|
src/get_pty.c
|
||||||
src/hooks.c
|
src/hooks.c
|
||||||
|
@@ -525,7 +525,8 @@ exec_intercept.o: $(srcdir)/exec_intercept.c $(incdir)/compat/stdbool.h \
|
|||||||
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
|
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
|
||||||
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
||||||
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
|
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
|
||||||
$(incdir)/sudo_rand.h $(incdir)/sudo_util.h $(srcdir)/sudo.h \
|
$(incdir)/sudo_rand.h $(incdir)/sudo_util.h \
|
||||||
|
$(srcdir)/exec_intercept.h $(srcdir)/sudo.h \
|
||||||
$(srcdir)/sudo_exec.h $(srcdir)/sudo_plugin_int.h \
|
$(srcdir)/sudo_exec.h $(srcdir)/sudo_plugin_int.h \
|
||||||
$(top_builddir)/config.h $(top_builddir)/pathnames.h
|
$(top_builddir)/config.h $(top_builddir)/pathnames.h
|
||||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/exec_intercept.c
|
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/exec_intercept.c
|
||||||
@@ -535,7 +536,8 @@ exec_intercept.i: $(srcdir)/exec_intercept.c $(incdir)/compat/stdbool.h \
|
|||||||
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
|
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
|
||||||
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
||||||
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
|
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
|
||||||
$(incdir)/sudo_rand.h $(incdir)/sudo_util.h $(srcdir)/sudo.h \
|
$(incdir)/sudo_rand.h $(incdir)/sudo_util.h \
|
||||||
|
$(srcdir)/exec_intercept.h $(srcdir)/sudo.h \
|
||||||
$(srcdir)/sudo_exec.h $(srcdir)/sudo_plugin_int.h \
|
$(srcdir)/sudo_exec.h $(srcdir)/sudo_plugin_int.h \
|
||||||
$(top_builddir)/config.h $(top_builddir)/pathnames.h
|
$(top_builddir)/config.h $(top_builddir)/pathnames.h
|
||||||
$(CC) -E -o $@ $(CPPFLAGS) $<
|
$(CC) -E -o $@ $(CPPFLAGS) $<
|
||||||
@@ -612,16 +614,18 @@ exec_ptrace.o: $(srcdir)/exec_ptrace.c $(incdir)/compat/stdbool.h \
|
|||||||
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
|
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
|
||||||
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
||||||
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
|
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
|
||||||
$(incdir)/sudo_util.h $(srcdir)/sudo.h $(srcdir)/sudo_exec.h \
|
$(incdir)/sudo_util.h $(srcdir)/exec_intercept.h \
|
||||||
$(top_builddir)/config.h $(top_builddir)/pathnames.h
|
$(srcdir)/sudo.h $(srcdir)/sudo_exec.h $(top_builddir)/config.h \
|
||||||
|
$(top_builddir)/pathnames.h
|
||||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/exec_ptrace.c
|
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/exec_ptrace.c
|
||||||
exec_ptrace.i: $(srcdir)/exec_ptrace.c $(incdir)/compat/stdbool.h \
|
exec_ptrace.i: $(srcdir)/exec_ptrace.c $(incdir)/compat/stdbool.h \
|
||||||
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
|
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
|
||||||
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
|
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
|
||||||
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
||||||
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
|
$(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
|
||||||
$(incdir)/sudo_util.h $(srcdir)/sudo.h $(srcdir)/sudo_exec.h \
|
$(incdir)/sudo_util.h $(srcdir)/exec_intercept.h \
|
||||||
$(top_builddir)/config.h $(top_builddir)/pathnames.h
|
$(srcdir)/sudo.h $(srcdir)/sudo_exec.h $(top_builddir)/config.h \
|
||||||
|
$(top_builddir)/pathnames.h
|
||||||
$(CC) -E -o $@ $(CPPFLAGS) $<
|
$(CC) -E -o $@ $(CPPFLAGS) $<
|
||||||
exec_ptrace.plog: exec_ptrace.i
|
exec_ptrace.plog: exec_ptrace.i
|
||||||
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/exec_ptrace.c --i-file $< --output-file $@
|
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/exec_ptrace.c --i-file $< --output-file $@
|
||||||
|
@@ -43,48 +43,16 @@
|
|||||||
#include "sudo_plugin_int.h"
|
#include "sudo_plugin_int.h"
|
||||||
#include "sudo_rand.h"
|
#include "sudo_rand.h"
|
||||||
#include "intercept.pb-c.h"
|
#include "intercept.pb-c.h"
|
||||||
|
#include "exec_intercept.h"
|
||||||
|
|
||||||
#ifdef _PATH_SUDO_INTERCEPT
|
#ifdef _PATH_SUDO_INTERCEPT
|
||||||
|
|
||||||
/* TCSASOFT is a BSD extension that ignores control flags and speed. */
|
|
||||||
# ifndef TCSASOFT
|
|
||||||
# define TCSASOFT 0
|
|
||||||
# endif
|
|
||||||
|
|
||||||
enum intercept_state {
|
|
||||||
RECV_HELLO_INITIAL,
|
|
||||||
RECV_HELLO,
|
|
||||||
RECV_SECRET,
|
|
||||||
RECV_POLICY_CHECK,
|
|
||||||
RECV_CONNECTION,
|
|
||||||
POLICY_ACCEPT,
|
|
||||||
POLICY_REJECT,
|
|
||||||
POLICY_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Closure for intercept_cb() */
|
|
||||||
struct intercept_closure {
|
|
||||||
union sudo_token_un token;
|
|
||||||
struct command_details *details;
|
|
||||||
struct sudo_event ev;
|
|
||||||
const char *errstr;
|
|
||||||
char *command; /* dynamically allocated */
|
|
||||||
char **run_argv; /* owned by plugin */
|
|
||||||
char **run_envp; /* dynamically allocated */
|
|
||||||
uint8_t *buf; /* dynamically allocated */
|
|
||||||
uint32_t len;
|
|
||||||
uint32_t off;
|
|
||||||
int listen_sock;
|
|
||||||
enum intercept_state state;
|
|
||||||
};
|
|
||||||
|
|
||||||
static union sudo_token_un intercept_token;
|
static union sudo_token_un intercept_token;
|
||||||
static in_port_t intercept_listen_port;
|
static in_port_t intercept_listen_port;
|
||||||
static struct intercept_closure *accept_closure;
|
static struct intercept_closure *accept_closure;
|
||||||
static void intercept_accept_cb(int fd, int what, void *v);
|
static void intercept_accept_cb(int fd, int what, void *v);
|
||||||
static void intercept_cb(int fd, int what, void *v);
|
static void intercept_cb(int fd, int what, void *v);
|
||||||
|
|
||||||
bool
|
void *
|
||||||
intercept_setup(int fd, struct sudo_event_base *evbase,
|
intercept_setup(int fd, struct sudo_event_base *evbase,
|
||||||
struct command_details *details)
|
struct command_details *details)
|
||||||
{
|
{
|
||||||
@@ -113,26 +81,26 @@ intercept_setup(int fd, struct sudo_event_base *evbase,
|
|||||||
*/
|
*/
|
||||||
closure->state = sudo_token_isset(intercept_token) ?
|
closure->state = sudo_token_isset(intercept_token) ?
|
||||||
RECV_SECRET : RECV_HELLO_INITIAL;
|
RECV_SECRET : RECV_HELLO_INITIAL;
|
||||||
|
|
||||||
|
rc = sudo_ev_set(&closure->ev, fd, SUDO_EV_READ|SUDO_EV_PERSIST,
|
||||||
|
intercept_cb, closure);
|
||||||
|
if (rc == -1 || sudo_ev_add(evbase, &closure->ev, NULL, false) == -1) {
|
||||||
|
sudo_warn("%s", U_("unable to add event to queue"));
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = sudo_ev_set(&closure->ev, fd, SUDO_EV_READ|SUDO_EV_PERSIST,
|
debug_return_ptr(closure);
|
||||||
intercept_cb, closure);
|
|
||||||
if (rc == -1 || sudo_ev_add(evbase, &closure->ev, NULL, false) == -1) {
|
|
||||||
sudo_warn("%s", U_("unable to add event to queue"));
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_return_bool(true);
|
|
||||||
|
|
||||||
bad:
|
bad:
|
||||||
free(closure);
|
free(closure);
|
||||||
debug_return_bool(false);
|
debug_return_ptr(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset intercept_closure so it can be re-used.
|
* Reset intercept_closure so it can be re-used.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
intercept_closure_reset(struct intercept_closure *closure)
|
intercept_closure_reset(struct intercept_closure *closure)
|
||||||
{
|
{
|
||||||
size_t n;
|
size_t n;
|
||||||
@@ -324,19 +292,165 @@ bad:
|
|||||||
debug_return_ptr(NULL);
|
debug_return_ptr(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
/*
|
||||||
intercept_check_policy(PolicyCheckRequest *req,
|
* Perform a policy check for the given command.
|
||||||
struct intercept_closure *closure)
|
* While argv must be NULL-terminated, envp need not be.
|
||||||
|
* The status of the policy check is stored in closure->state.
|
||||||
|
* Return false on error, else true.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
intercept_check_policy(const char *command, int argc, char **argv, int envc,
|
||||||
|
char **envp, const char *runcwd, void *v)
|
||||||
{
|
{
|
||||||
|
struct intercept_closure *closure = v;
|
||||||
char **command_info = NULL;
|
char **command_info = NULL;
|
||||||
char **command_info_copy = NULL;
|
char **command_info_copy = NULL;
|
||||||
char **user_env_out = NULL;
|
char **user_env_out = NULL;
|
||||||
char **argv = NULL, **run_argv = NULL;
|
char **run_argv = NULL;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
int rc;
|
int i, rc;
|
||||||
size_t n;
|
|
||||||
debug_decl(intercept_check_policy, SUDO_DEBUG_EXEC);
|
debug_decl(intercept_check_policy, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
|
if (ISSET(closure->details->flags, CD_INTERCEPT)) {
|
||||||
|
/* We don't currently have a good way to validate the environment. */
|
||||||
|
sudo_debug_set_active_instance(policy_plugin.debug_instance);
|
||||||
|
rc = policy_plugin.u.policy->check_policy(argc, argv, NULL,
|
||||||
|
&command_info, &run_argv, &user_env_out, &closure->errstr);
|
||||||
|
sudo_debug_set_active_instance(sudo_debug_instance);
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||||
|
"check_policy returns %d", rc);
|
||||||
|
|
||||||
|
switch (rc) {
|
||||||
|
case 1:
|
||||||
|
/* Rebuild command_info[] with runcwd and extract command. */
|
||||||
|
command_info_copy = update_command_info(command_info, NULL,
|
||||||
|
runcwd ? runcwd : "unknown", &closure->command);
|
||||||
|
if (command_info_copy == NULL) {
|
||||||
|
closure->errstr = N_("unable to allocate memory");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
command_info = command_info_copy;
|
||||||
|
closure->state = POLICY_ACCEPT;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
if (closure->errstr == NULL)
|
||||||
|
closure->errstr = N_("command rejected by policy");
|
||||||
|
audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN,
|
||||||
|
closure->errstr, command_info);
|
||||||
|
closure->state = POLICY_REJECT;
|
||||||
|
ret = true;
|
||||||
|
goto done;
|
||||||
|
default:
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* No actual policy check, just logging child processes. */
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||||
|
"not checking policy, audit only");
|
||||||
|
closure->command = strdup(command);
|
||||||
|
if (closure->command == NULL) {
|
||||||
|
closure->errstr = N_("unable to allocate memory");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rebuild command_info[] with new command and runcwd. */
|
||||||
|
command_info = update_command_info(closure->details->info,
|
||||||
|
command, runcwd ? runcwd : "unknown", NULL);
|
||||||
|
if (command_info == NULL) {
|
||||||
|
closure->errstr = N_("unable to allocate memory");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
closure->state = POLICY_ACCEPT;
|
||||||
|
run_argv = argv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sudo_debug_needed(SUDO_DEBUG_INFO)) {
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||||
|
"run_command: %s", closure->command);
|
||||||
|
for (i = 0; command_info[i] != NULL; i++) {
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||||
|
"command_info[%d]: %s", i, command_info[i]);
|
||||||
|
}
|
||||||
|
for (i = 0; run_argv[i] != NULL; i++) {
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||||
|
"run_argv[%d]: %s", i, run_argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a copy of run_argv, it may share contents of argv. */
|
||||||
|
for (i = 0; run_argv[i] != NULL; i++)
|
||||||
|
continue;
|
||||||
|
closure->run_argv = reallocarray(NULL, i + 1, sizeof(char *));
|
||||||
|
if (closure->run_argv == NULL) {
|
||||||
|
closure->errstr = N_("unable to allocate memory");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
for (i = 0; run_argv[i] != NULL; i++) {
|
||||||
|
closure->run_argv[i] = strdup(run_argv[i]);
|
||||||
|
if (closure->run_argv[i] == NULL) {
|
||||||
|
closure->errstr = N_("unable to allocate memory");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closure->run_argv[i] = NULL;
|
||||||
|
|
||||||
|
/* Make a copy of envp, which may not be NULL-terminated. */
|
||||||
|
closure->run_envp = reallocarray(NULL, envc + 1, sizeof(char *));
|
||||||
|
if (closure->run_envp == NULL) {
|
||||||
|
closure->errstr = N_("unable to allocate memory");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
for (i = 0; i < envc; i++) {
|
||||||
|
closure->run_envp[i] = strdup(envp[i]);
|
||||||
|
if (closure->run_envp[i] == NULL) {
|
||||||
|
closure->errstr = N_("unable to allocate memory");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closure->run_envp[i] = NULL;
|
||||||
|
|
||||||
|
if (ISSET(closure->details->flags, CD_INTERCEPT)) {
|
||||||
|
audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN, command_info,
|
||||||
|
closure->run_argv, closure->run_envp);
|
||||||
|
|
||||||
|
/* Call approval plugins and audit the result. */
|
||||||
|
if (!approval_check(command_info, closure->run_argv, closure->run_envp))
|
||||||
|
debug_return_int(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Audit the event again for the sudo front-end. */
|
||||||
|
audit_accept("sudo", SUDO_FRONT_END, command_info, closure->run_argv,
|
||||||
|
closure->run_envp);
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (!ret) {
|
||||||
|
if (closure->errstr == NULL)
|
||||||
|
closure->errstr = N_("policy plugin error");
|
||||||
|
audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN, closure->errstr,
|
||||||
|
command_info ? command_info : closure->details->info);
|
||||||
|
closure->state = POLICY_ERROR;
|
||||||
|
}
|
||||||
|
if (command_info_copy != NULL) {
|
||||||
|
for (i = 0; command_info_copy[i] != NULL; i++) {
|
||||||
|
free(command_info_copy[i]);
|
||||||
|
}
|
||||||
|
free(command_info_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_return_bool(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
intercept_check_policy_req(PolicyCheckRequest *req,
|
||||||
|
struct intercept_closure *closure)
|
||||||
|
{
|
||||||
|
char **argv = NULL;
|
||||||
|
bool ret = false;
|
||||||
|
size_t n;
|
||||||
|
debug_decl(intercept_check_policy_req, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
if (req->command == NULL || req->n_argv == 0 || req->n_envp == 0) {
|
if (req->command == NULL || req->n_argv == 0 || req->n_envp == 0) {
|
||||||
closure->errstr = N_("invalid PolicyCheckRequest");
|
closure->errstr = N_("invalid PolicyCheckRequest");
|
||||||
goto done;
|
goto done;
|
||||||
@@ -363,133 +477,10 @@ intercept_check_policy(PolicyCheckRequest *req,
|
|||||||
}
|
}
|
||||||
argv[n] = NULL;
|
argv[n] = NULL;
|
||||||
|
|
||||||
if (ISSET(closure->details->flags, CD_INTERCEPT)) {
|
ret = intercept_check_policy(req->command, req->n_argv, argv, req->n_envp,
|
||||||
/* We don't currently have a good way to validate the environment. */
|
req->envp, req->cwd, closure);
|
||||||
sudo_debug_set_active_instance(policy_plugin.debug_instance);
|
|
||||||
rc = policy_plugin.u.policy->check_policy(n, argv, NULL,
|
|
||||||
&command_info, &run_argv, &user_env_out, &closure->errstr);
|
|
||||||
sudo_debug_set_active_instance(sudo_debug_instance);
|
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
||||||
"check_policy returns %d", rc);
|
|
||||||
|
|
||||||
switch (rc) {
|
|
||||||
case 1:
|
|
||||||
/* Rebuild command_info[] with runcwd and extract command. */
|
|
||||||
command_info_copy = update_command_info(command_info, NULL,
|
|
||||||
req->cwd ? req->cwd : "unknown", &closure->command);
|
|
||||||
if (command_info_copy == NULL) {
|
|
||||||
closure->errstr = N_("unable to allocate memory");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
command_info = command_info_copy;
|
|
||||||
closure->state = POLICY_ACCEPT;
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
if (closure->errstr == NULL)
|
|
||||||
closure->errstr = N_("command rejected by policy");
|
|
||||||
audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN,
|
|
||||||
closure->errstr, command_info);
|
|
||||||
closure->state = POLICY_REJECT;
|
|
||||||
ret = true;
|
|
||||||
goto done;
|
|
||||||
default:
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* No actual policy check, just logging child processes. */
|
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
||||||
"not checking policy, audit only");
|
|
||||||
closure->command = strdup(req->command);
|
|
||||||
if (closure->command == NULL) {
|
|
||||||
closure->errstr = N_("unable to allocate memory");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rebuild command_info[] with new command and runcwd. */
|
|
||||||
command_info = update_command_info(closure->details->info,
|
|
||||||
req->command, req->cwd ? req->cwd : "unknown", NULL);
|
|
||||||
if (command_info == NULL) {
|
|
||||||
closure->errstr = N_("unable to allocate memory");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
closure->state = POLICY_ACCEPT;
|
|
||||||
run_argv = argv;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sudo_debug_needed(SUDO_DEBUG_INFO)) {
|
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
||||||
"run_command: %s", closure->command);
|
|
||||||
for (n = 0; command_info[n] != NULL; n++) {
|
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
||||||
"command_info[%zu]: %s", n, command_info[n]);
|
|
||||||
}
|
|
||||||
for (n = 0; run_argv[n] != NULL; n++) {
|
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
||||||
"run_argv[%zu]: %s", n, run_argv[n]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* run_argv strings may be part of PolicyCheckReq, make a copy. */
|
|
||||||
for (n = 0; run_argv[n] != NULL; n++)
|
|
||||||
continue;
|
|
||||||
closure->run_argv = reallocarray(NULL, n + 1, sizeof(char *));
|
|
||||||
if (closure->run_argv == NULL) {
|
|
||||||
closure->errstr = N_("unable to allocate memory");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
for (n = 0; run_argv[n] != NULL; n++) {
|
|
||||||
closure->run_argv[n] = strdup(run_argv[n]);
|
|
||||||
if (closure->run_argv[n] == NULL) {
|
|
||||||
closure->errstr = N_("unable to allocate memory");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closure->run_argv[n] = NULL;
|
|
||||||
|
|
||||||
/* envp strings are part of PolicyCheckReq, make a copy. */
|
|
||||||
closure->run_envp = reallocarray(NULL, req->n_envp + 1, sizeof(char *));
|
|
||||||
if (closure->run_envp == NULL) {
|
|
||||||
closure->errstr = N_("unable to allocate memory");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
for (n = 0; n < req->n_envp; n++) {
|
|
||||||
closure->run_envp[n] = strdup(req->envp[n]);
|
|
||||||
if (closure->run_envp[n] == NULL) {
|
|
||||||
closure->errstr = N_("unable to allocate memory");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closure->run_envp[n] = NULL;
|
|
||||||
|
|
||||||
if (ISSET(closure->details->flags, CD_INTERCEPT)) {
|
|
||||||
audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN, command_info,
|
|
||||||
closure->run_argv, closure->run_envp);
|
|
||||||
|
|
||||||
/* Call approval plugins and audit the result. */
|
|
||||||
if (!approval_check(command_info, closure->run_argv, closure->run_envp))
|
|
||||||
debug_return_int(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Audit the event again for the sudo front-end. */
|
|
||||||
audit_accept("sudo", SUDO_FRONT_END, command_info, closure->run_argv,
|
|
||||||
closure->run_envp);
|
|
||||||
|
|
||||||
ret = true;
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (!ret) {
|
|
||||||
if (closure->errstr == NULL)
|
|
||||||
closure->errstr = N_("policy plugin error");
|
|
||||||
audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN, closure->errstr,
|
|
||||||
command_info ? command_info : closure->details->info);
|
|
||||||
closure->state = POLICY_ERROR;
|
|
||||||
}
|
|
||||||
if (command_info_copy != NULL) {
|
|
||||||
for (n = 0; command_info_copy[n] != NULL; n++) {
|
|
||||||
free(command_info_copy[n]);
|
|
||||||
}
|
|
||||||
free(command_info_copy);
|
|
||||||
}
|
|
||||||
free(argv);
|
free(argv);
|
||||||
|
|
||||||
debug_return_bool(ret);
|
debug_return_bool(ret);
|
||||||
@@ -644,7 +635,7 @@ unpack:
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = intercept_check_policy(req->u.policy_check_req, closure);
|
ret = intercept_check_policy_req(req->u.policy_check_req, closure);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
@@ -918,8 +909,9 @@ intercept_cb(int fd, int what, void *v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Accept a new connection from the client and fill in a client closure.
|
* Accept a new connection from the client register a new event for it.
|
||||||
* Registers a new event for the connection.
|
* Returns an opaque pointer to the intercept closure, which is
|
||||||
|
* also passed to the event callback.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
intercept_accept_cb(int fd, int what, void *v)
|
intercept_accept_cb(int fd, int what, void *v)
|
||||||
@@ -949,7 +941,10 @@ intercept_accept_cb(int fd, int what, void *v)
|
|||||||
if (flags != -1)
|
if (flags != -1)
|
||||||
(void)fcntl(client_sock, F_SETFL, flags | O_NONBLOCK);
|
(void)fcntl(client_sock, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
if (!intercept_setup(client_sock, evbase, closure->details)) {
|
/*
|
||||||
|
* Create a new intercept closure and register an event for client_sock.
|
||||||
|
*/
|
||||||
|
if (intercept_setup(client_sock, evbase, closure->details) == NULL) {
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -961,7 +956,7 @@ bad:
|
|||||||
debug_return;
|
debug_return;
|
||||||
}
|
}
|
||||||
#else /* _PATH_SUDO_INTERCEPT */
|
#else /* _PATH_SUDO_INTERCEPT */
|
||||||
bool
|
void *
|
||||||
intercept_setup(int fd, struct sudo_event_base *evbase,
|
intercept_setup(int fd, struct sudo_event_base *evbase,
|
||||||
struct command_details *details)
|
struct command_details *details)
|
||||||
{
|
{
|
||||||
@@ -969,7 +964,7 @@ intercept_setup(int fd, struct sudo_event_base *evbase,
|
|||||||
|
|
||||||
/* Intercept support not compiled in. */
|
/* Intercept support not compiled in. */
|
||||||
|
|
||||||
debug_return_bool(false);
|
debug_return_ptr(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
52
src/exec_intercept.h
Normal file
52
src/exec_intercept.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: ISC
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SUDO_INTERCEPT_H
|
||||||
|
#define SUDO_INTERCEPT_H
|
||||||
|
|
||||||
|
enum intercept_state {
|
||||||
|
RECV_HELLO_INITIAL,
|
||||||
|
RECV_HELLO,
|
||||||
|
RECV_SECRET,
|
||||||
|
RECV_POLICY_CHECK,
|
||||||
|
RECV_CONNECTION,
|
||||||
|
POLICY_ACCEPT,
|
||||||
|
POLICY_REJECT,
|
||||||
|
POLICY_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Closure for intercept_cb() */
|
||||||
|
struct intercept_closure {
|
||||||
|
union sudo_token_un token;
|
||||||
|
struct command_details *details;
|
||||||
|
struct sudo_event ev;
|
||||||
|
const char *errstr;
|
||||||
|
char *command; /* dynamically allocated */
|
||||||
|
char **run_argv; /* owned by plugin */
|
||||||
|
char **run_envp; /* dynamically allocated */
|
||||||
|
uint8_t *buf; /* dynamically allocated */
|
||||||
|
uint32_t len;
|
||||||
|
uint32_t off;
|
||||||
|
int listen_sock;
|
||||||
|
enum intercept_state state;
|
||||||
|
};
|
||||||
|
|
||||||
|
void intercept_closure_reset(struct intercept_closure *closure);
|
||||||
|
bool intercept_check_policy(const char *command, int argc, char **argv, int envc, char **envp, const char *runcwd, void *closure);
|
||||||
|
|
||||||
|
#endif /* SUDO_INTERCEPT_H */
|
@@ -61,6 +61,7 @@ struct exec_closure_nopty {
|
|||||||
struct sudo_event *sigcont_event;
|
struct sudo_event *sigcont_event;
|
||||||
struct sudo_event *siginfo_event;
|
struct sudo_event *siginfo_event;
|
||||||
struct command_status *cstat;
|
struct command_status *cstat;
|
||||||
|
void *intercept;
|
||||||
pid_t cmnd_pid;
|
pid_t cmnd_pid;
|
||||||
pid_t ppgrp;
|
pid_t ppgrp;
|
||||||
};
|
};
|
||||||
@@ -368,16 +369,18 @@ exec_nopty(struct command_details *details, struct command_status *cstat)
|
|||||||
if (pipe2(errpipe, O_CLOEXEC) != 0)
|
if (pipe2(errpipe, O_CLOEXEC) != 0)
|
||||||
sudo_fatal("%s", U_("unable to create pipe"));
|
sudo_fatal("%s", U_("unable to create pipe"));
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocate a socketpair for communicating with sudo_intercept.so.
|
|
||||||
* This must be inherited across exec, hence no FD_CLOEXEC.
|
|
||||||
* Check if the kernel supports the seccomp(2) filter "trap" action.
|
|
||||||
*/
|
|
||||||
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
|
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
|
||||||
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
|
if (have_seccomp_action("trap")) {
|
||||||
sudo_fatal("%s", U_("unable to create sockets"));
|
/* Kernel supports the seccomp(2) filter "trap" action. */
|
||||||
if (have_seccomp_action("trap"))
|
|
||||||
SET(details->flags, CD_USE_PTRACE);
|
SET(details->flags, CD_USE_PTRACE);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Allocate a socketpair for communicating with sudo_intercept.so.
|
||||||
|
* This must be inherited across exec, hence no FD_CLOEXEC.
|
||||||
|
*/
|
||||||
|
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
|
||||||
|
sudo_fatal("%s", U_("unable to create sockets"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -446,8 +449,9 @@ exec_nopty(struct command_details *details, struct command_status *cstat)
|
|||||||
fill_exec_closure_nopty(&ec, cstat, details, errpipe[0]);
|
fill_exec_closure_nopty(&ec, cstat, details, errpipe[0]);
|
||||||
|
|
||||||
/* Create event and closure for intercept mode. */
|
/* Create event and closure for intercept mode. */
|
||||||
if (intercept_sv[0] != -1) {
|
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
|
||||||
if (!intercept_setup(intercept_sv[0], ec.evbase, details))
|
ec.intercept = intercept_setup(intercept_sv[0], ec.evbase, details);
|
||||||
|
if (ec.intercept == NULL)
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,7 +539,7 @@ handle_sigchld_nopty(struct exec_closure_nopty *ec)
|
|||||||
|
|
||||||
if (ISSET(ec->details->flags, CD_USE_PTRACE)) {
|
if (ISSET(ec->details->flags, CD_USE_PTRACE)) {
|
||||||
/* Did exec_ptrace_handled() suppress the signal? */
|
/* Did exec_ptrace_handled() suppress the signal? */
|
||||||
if (exec_ptrace_handled(pid, status))
|
if (exec_ptrace_handled(pid, status, ec->intercept))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,12 +29,14 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "sudo.h"
|
#include "sudo.h"
|
||||||
#include "sudo_exec.h"
|
#include "sudo_exec.h"
|
||||||
|
#include "exec_intercept.h"
|
||||||
|
|
||||||
#ifdef HAVE_PTRACE_INTERCEPT
|
#ifdef HAVE_PTRACE_INTERCEPT
|
||||||
# include <elf.h>
|
# include <elf.h>
|
||||||
@@ -57,82 +59,82 @@
|
|||||||
*/
|
*/
|
||||||
#if defined(__amd64__)
|
#if defined(__amd64__)
|
||||||
# define user_pt_regs user_regs_struct
|
# define user_pt_regs user_regs_struct
|
||||||
# define reg_syscall(x) (x).orig_rax
|
# define reg_syscall(x) (x)->orig_rax
|
||||||
# define reg_retval(x) (x).rax
|
# define reg_retval(x) (x)->rax
|
||||||
# define reg_arg1(x) (x).rdi
|
# define reg_arg1(x) (x)->rdi
|
||||||
# define reg_arg2(x) (x).rsi
|
# define reg_arg2(x) (x)->rsi
|
||||||
# define reg_arg3(x) (x).rdx
|
# define reg_arg3(x) (x)->rdx
|
||||||
# define reg_arg4(x) (x).r10
|
# define reg_arg4(x) (x)->r10
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
# define reg_syscall(x) (x).regs[8] /* w8 */
|
# define reg_syscall(x) (x)->regs[8] /* w8 */
|
||||||
# define reg_retval(x) (x).regs[0] /* x0 */
|
# define reg_retval(x) (x)->regs[0] /* x0 */
|
||||||
# define reg_arg1(x) (x).regs[0] /* x0 */
|
# define reg_arg1(x) (x)->regs[0] /* x0 */
|
||||||
# define reg_arg2(x) (x).regs[1] /* x1 */
|
# define reg_arg2(x) (x)->regs[1] /* x1 */
|
||||||
# define reg_arg3(x) (x).regs[2] /* x2 */
|
# define reg_arg3(x) (x)->regs[2] /* x2 */
|
||||||
# define reg_arg4(x) (x).regs[3] /* x3 */
|
# define reg_arg4(x) (x)->regs[3] /* x3 */
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__)
|
||||||
/* Note: assumes arm EABI, not OABI */
|
/* Note: assumes arm EABI, not OABI */
|
||||||
/* Untested */
|
/* Untested */
|
||||||
# define user_pt_regs pt_regs
|
# define user_pt_regs pt_regs
|
||||||
# define reg_syscall(x) (x).ARM_r7
|
# define reg_syscall(x) (x)->ARM_r7
|
||||||
# define reg_retval(x) (x).ARM_r0
|
# define reg_retval(x) (x)->ARM_r0
|
||||||
# define reg_arg1(x) (x).ARM_r0
|
# define reg_arg1(x) (x)->ARM_r0
|
||||||
# define reg_arg2(x) (x).ARM_r1
|
# define reg_arg2(x) (x)->ARM_r1
|
||||||
# define reg_arg3(x) (x).ARM_r2
|
# define reg_arg3(x) (x)->ARM_r2
|
||||||
# define reg_arg4(x) (x).ARM_r3
|
# define reg_arg4(x) (x)->ARM_r3
|
||||||
#elif defined (__hppa__)
|
#elif defined (__hppa__)
|
||||||
/* Untested */
|
/* Untested */
|
||||||
# define user_pt_regs user_regs_struct
|
# define user_pt_regs user_regs_struct
|
||||||
# define reg_syscall(x) (x).gr[20] /* r20 */
|
# define reg_syscall(x) (x)->gr[20] /* r20 */
|
||||||
# define reg_retval(x) (x).gr[28] /* r28 */
|
# define reg_retval(x) (x)->gr[28] /* r28 */
|
||||||
# define reg_arg1(x) (x).gr[26] /* r26 */
|
# define reg_arg1(x) (x)->gr[26] /* r26 */
|
||||||
# define reg_arg2(x) (x).gr[25] /* r25 */
|
# define reg_arg2(x) (x)->gr[25] /* r25 */
|
||||||
# define reg_arg3(x) (x).gr[24] /* r24 */
|
# define reg_arg3(x) (x)->gr[24] /* r24 */
|
||||||
# define reg_arg4(x) (x).gr[23] /* r23 */
|
# define reg_arg4(x) (x)->gr[23] /* r23 */
|
||||||
#elif defined(__i386__)
|
#elif defined(__i386__)
|
||||||
# define user_pt_regs user_regs_struct
|
# define user_pt_regs user_regs_struct
|
||||||
# define reg_syscall(x) (x).orig_eax
|
# define reg_syscall(x) (x)->orig_eax
|
||||||
# define reg_retval(x) (x).eax
|
# define reg_retval(x) (x)->eax
|
||||||
# define reg_arg1(x) (x).ebx
|
# define reg_arg1(x) (x)->ebx
|
||||||
# define reg_arg2(x) (x).ecx
|
# define reg_arg2(x) (x)->ecx
|
||||||
# define reg_arg3(x) (x).edx
|
# define reg_arg3(x) (x)->edx
|
||||||
# define reg_arg4(x) (x).esi
|
# define reg_arg4(x) (x)->esi
|
||||||
#elif defined(__powerpc64__)
|
#elif defined(__powerpc64__)
|
||||||
/* Untested */
|
/* Untested */
|
||||||
# define user_pt_regs pt_regs
|
# define user_pt_regs pt_regs
|
||||||
# define reg_syscall(x) (x).gpr[0] /* r0 */
|
# define reg_syscall(x) (x)->gpr[0] /* r0 */
|
||||||
# define reg_retval(x) (x).gpr[3] /* r3 */
|
# define reg_retval(x) (x)->gpr[3] /* r3 */
|
||||||
# define reg_arg1(x) (x).gpr[3] /* r3 */
|
# define reg_arg1(x) (x)->gpr[3] /* r3 */
|
||||||
# define reg_arg2(x) (x).gpr[4] /* r4 */
|
# define reg_arg2(x) (x)->gpr[4] /* r4 */
|
||||||
# define reg_arg3(x) (x).gpr[5] /* r5 */
|
# define reg_arg3(x) (x)->gpr[5] /* r5 */
|
||||||
# define reg_arg4(x) (x).gpr[6] /* r6 */
|
# define reg_arg4(x) (x)->gpr[6] /* r6 */
|
||||||
#elif defined(__powerpc__)
|
#elif defined(__powerpc__)
|
||||||
/* Untested */
|
/* Untested */
|
||||||
# define user_pt_regs pt_regs
|
# define user_pt_regs pt_regs
|
||||||
# define reg_syscall(x) (x).gpr[0] /* r0 */
|
# define reg_syscall(x) (x)->gpr[0] /* r0 */
|
||||||
# define reg_retval(x) (x).gpr[3] /* r3 */
|
# define reg_retval(x) (x)->gpr[3] /* r3 */
|
||||||
# define reg_arg1(x) (x).gpr[3] /* r3 */
|
# define reg_arg1(x) (x)->gpr[3] /* r3 */
|
||||||
# define reg_arg2(x) (x).gpr[4] /* r4 */
|
# define reg_arg2(x) (x)->gpr[4] /* r4 */
|
||||||
# define reg_arg3(x) (x).gpr[5] /* r5 */
|
# define reg_arg3(x) (x)->gpr[5] /* r5 */
|
||||||
# define reg_arg4(x) (x).gpr[6] /* r6 */
|
# define reg_arg4(x) (x)->gpr[6] /* r6 */
|
||||||
#elif defined(__riscv) && __riscv_xlen == 64
|
#elif defined(__riscv) && __riscv_xlen == 64
|
||||||
/* Untested */
|
/* Untested */
|
||||||
# define user_pt_regs user_regs_struct
|
# define user_pt_regs user_regs_struct
|
||||||
# define reg_syscall(x) (x).a7
|
# define reg_syscall(x) (x)->a7
|
||||||
# define reg_retval(x) (x).a0
|
# define reg_retval(x) (x)->a0
|
||||||
# define reg_arg1(x) (x).a0
|
# define reg_arg1(x) (x)->a0
|
||||||
# define reg_arg2(x) (x).a1
|
# define reg_arg2(x) (x)->a1
|
||||||
# define reg_arg3(x) (x).a2
|
# define reg_arg3(x) (x)->a2
|
||||||
# define reg_arg4(x) (x).a3
|
# define reg_arg4(x) (x)->a3
|
||||||
#elif defined(__s390__)
|
#elif defined(__s390__)
|
||||||
/* Untested */
|
/* Untested */
|
||||||
# define user_pt_regs s390_regs
|
# define user_pt_regs s390_regs
|
||||||
# define reg_syscall(x) (x).gprs[1] /* r1 */
|
# define reg_syscall(x) (x)->gprs[1] /* r1 */
|
||||||
# define reg_retval(x) (x).gprs[2] /* r2 */
|
# define reg_retval(x) (x)->gprs[2] /* r2 */
|
||||||
# define reg_arg1(x) (x).gprs[2] /* r2 */
|
# define reg_arg1(x) (x)->gprs[2] /* r2 */
|
||||||
# define reg_arg2(x) (x).gprs[3] /* r3 */
|
# define reg_arg2(x) (x)->gprs[3] /* r3 */
|
||||||
# define reg_arg3(x) (x).gprs[4] /* r4 */
|
# define reg_arg3(x) (x)->gprs[4] /* r4 */
|
||||||
# define reg_arg4(x) (x).gprs[5] /* r6 */
|
# define reg_arg4(x) (x)->gprs[5] /* r6 */
|
||||||
#else
|
#else
|
||||||
# error "Do not know how to find your architecture's registers"
|
# error "Do not know how to find your architecture's registers"
|
||||||
#endif
|
#endif
|
||||||
@@ -154,7 +156,7 @@ ptrace_read_string(pid_t pid, long addr, char *buf, size_t bufsize)
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
word = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL);
|
word = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL);
|
||||||
if (word == -1) {
|
if (word == -1) {
|
||||||
sudo_warn("ptrace(PTRACE_PEEKTEXT, %d, %ld, NULL)", pid, addr);
|
sudo_warn("ptrace(PTRACE_PEEKTEXT, %d, 0x%lx, NULL)", pid, addr);
|
||||||
debug_return_ssize_t(-1);
|
debug_return_ssize_t(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +195,7 @@ ptrace_read_vec(pid_t pid, long addr, char **vec, char *buf, size_t bufsize)
|
|||||||
long word = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL);
|
long word = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL);
|
||||||
switch (word) {
|
switch (word) {
|
||||||
case -1:
|
case -1:
|
||||||
sudo_warn("ptrace(PTRACE_PEEKTEXT, %d, %ld, NULL)", pid, addr);
|
sudo_warn("ptrace(PTRACE_PEEKTEXT, %d, 0x%lx, NULL)", pid, addr);
|
||||||
goto bad;
|
goto bad;
|
||||||
case 0:
|
case 0:
|
||||||
vec[len] = NULL;
|
vec[len] = NULL;
|
||||||
@@ -230,7 +232,7 @@ ptrace_get_vec_len(pid_t pid, long addr)
|
|||||||
long word = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL);
|
long word = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL);
|
||||||
switch (word) {
|
switch (word) {
|
||||||
case -1:
|
case -1:
|
||||||
sudo_warn("ptrace(PTRACE_PEEKTEXT, %d, %ld, NULL)", pid, addr);
|
sudo_warn("ptrace(PTRACE_PEEKTEXT, %d, 0x%lx, NULL)", pid, addr);
|
||||||
debug_return_int(-1);
|
debug_return_int(-1);
|
||||||
case 0:
|
case 0:
|
||||||
debug_return_int(len);
|
debug_return_int(len);
|
||||||
@@ -242,20 +244,43 @@ ptrace_get_vec_len(pid_t pid, long addr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use /proc/PID/cwd to determine the current working directory.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
getcwd_by_pid(pid_t pid, char *buf, size_t bufsize)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
debug_decl(getcwd_by_pid, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
|
len = snprintf(path, sizeof(path), "/proc/%d/cwd", (int)pid);
|
||||||
|
if (len < sizeof(path)) {
|
||||||
|
len = readlink(path, buf, bufsize);
|
||||||
|
if (len != (size_t)-1) {
|
||||||
|
/* Check for truncation. */
|
||||||
|
if (len >= bufsize)
|
||||||
|
buf[bufsize - 1] = '\0';
|
||||||
|
debug_return_bool(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_return_bool(false);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read the filename, argv and envp of the execve(2) system call.
|
* Read the filename, argv and envp of the execve(2) system call.
|
||||||
* Returns a dynamically allocated buffer the parent is responsible for.
|
* Returns a dynamically allocated buffer the parent is responsible for.
|
||||||
*/
|
*/
|
||||||
static char *
|
static char *
|
||||||
get_execve_args(pid_t pid, char **pathname_out, char ***argv_out, char ***envp_out)
|
get_execve_info(pid_t pid, struct user_pt_regs *regs, char **pathname_out,
|
||||||
|
int *argc_out, char ***argv_out, int *envc_out, char ***envp_out)
|
||||||
{
|
{
|
||||||
char *argbuf, *strtab, *pathname, **argv, **envp;
|
char *argbuf, *strtab, *pathname, **argv, **envp;
|
||||||
long path_addr, argv_addr, envp_addr, syscallno;
|
long path_addr, argv_addr, envp_addr, syscallno;
|
||||||
struct user_pt_regs regs;
|
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
int argc, envc;
|
int argc, envc;
|
||||||
size_t bufsize, len;
|
size_t bufsize, len;
|
||||||
debug_decl(get_execve_args, SUDO_DEBUG_EXEC);
|
debug_decl(get_execve_info, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
bufsize = sysconf(_SC_ARG_MAX) + PATH_MAX;
|
bufsize = sysconf(_SC_ARG_MAX) + PATH_MAX;
|
||||||
argbuf = malloc(bufsize);
|
argbuf = malloc(bufsize);
|
||||||
@@ -263,8 +288,8 @@ get_execve_args(pid_t pid, char **pathname_out, char ***argv_out, char ***envp_o
|
|||||||
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||||
|
|
||||||
/* XXX - for amd64 and i386 use PTRACE_GETREGS/PTRACE_SETREGS instead. */
|
/* XXX - for amd64 and i386 use PTRACE_GETREGS/PTRACE_SETREGS instead. */
|
||||||
iov.iov_base = ®s;
|
iov.iov_base = regs;
|
||||||
iov.iov_len = sizeof(regs);
|
iov.iov_len = sizeof(*regs);
|
||||||
if (ptrace(PTRACE_GETREGSET, pid, (long)NT_PRSTATUS, &iov) == -1) {
|
if (ptrace(PTRACE_GETREGSET, pid, (long)NT_PRSTATUS, &iov) == -1) {
|
||||||
sudo_warn(U_("unable to get registers for process %d"), (int)pid);
|
sudo_warn(U_("unable to get registers for process %d"), (int)pid);
|
||||||
goto bad;
|
goto bad;
|
||||||
@@ -282,24 +307,6 @@ get_execve_args(pid_t pid, char **pathname_out, char ***argv_out, char ***envp_o
|
|||||||
argv_addr = reg_arg2(regs);
|
argv_addr = reg_arg2(regs);
|
||||||
envp_addr = reg_arg3(regs);
|
envp_addr = reg_arg3(regs);
|
||||||
|
|
||||||
#ifdef notyet
|
|
||||||
/* Cause the syscall to fail by changing its number to -1. */
|
|
||||||
reg_syscall(regs) |= 0xffffffff;
|
|
||||||
if (ptrace(PTRACE_SETREGSET, pid, (long)NT_PRSTATUS, &iov) == -1) {
|
|
||||||
sudo_warn("unable to set registers");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allow the syscall to complete and change return value to EACCES. */
|
|
||||||
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
|
||||||
waitpid(pid, NULL, 0);
|
|
||||||
reg_retval(regs) = -EACCES;
|
|
||||||
if (ptrace(PTRACE_SETREGSET, pid, (long)NT_PRSTATUS, &iov) == -1) {
|
|
||||||
sudo_warn("unable to set registers");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Count argv and envp */
|
/* Count argv and envp */
|
||||||
argc = ptrace_get_vec_len(pid, argv_addr);
|
argc = ptrace_get_vec_len(pid, argv_addr);
|
||||||
envc = ptrace_get_vec_len(pid, envp_addr);
|
envc = ptrace_get_vec_len(pid, envp_addr);
|
||||||
@@ -347,7 +354,9 @@ get_execve_args(pid_t pid, char **pathname_out, char ***argv_out, char ***envp_o
|
|||||||
sudo_debug_execve(SUDO_DEBUG_INFO, pathname, argv, envp);
|
sudo_debug_execve(SUDO_DEBUG_INFO, pathname, argv, envp);
|
||||||
|
|
||||||
*pathname_out = pathname;
|
*pathname_out = pathname;
|
||||||
|
*argc_out = argc;
|
||||||
*argv_out = argv;
|
*argv_out = argv;
|
||||||
|
*envc_out = envc;
|
||||||
*envp_out = envp;
|
*envp_out = envp;
|
||||||
|
|
||||||
debug_return_ptr(argbuf);
|
debug_return_ptr(argbuf);
|
||||||
@@ -356,6 +365,61 @@ bad:
|
|||||||
debug_return_ptr(NULL);
|
debug_return_ptr(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cause the current syscall to fail and set the error value to ecode.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ptrace_fail_syscall(pid_t pid, struct user_pt_regs *regs, int ecode)
|
||||||
|
{
|
||||||
|
struct iovec iov;
|
||||||
|
sigset_t chldmask;
|
||||||
|
bool ret = false;
|
||||||
|
int status;
|
||||||
|
debug_decl(ptrace_fail_syscall, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
|
iov.iov_base = regs;
|
||||||
|
iov.iov_len = sizeof(*regs);
|
||||||
|
|
||||||
|
/* Cause the syscall to fail by changing its number to -1. */
|
||||||
|
reg_syscall(regs) |= 0xffffffff;
|
||||||
|
if (ptrace(PTRACE_SETREGSET, pid, (long)NT_PRSTATUS, &iov) == -1) {
|
||||||
|
sudo_warn(U_("unable to set registers for process %d"), (int)pid);
|
||||||
|
debug_return_bool(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block SIGCHLD for the critical section (waitpid). */
|
||||||
|
sigemptyset(&chldmask);
|
||||||
|
sigaddset(&chldmask, SIGCHLD);
|
||||||
|
sigprocmask(SIG_BLOCK, &chldmask, NULL);
|
||||||
|
|
||||||
|
/* Allow the syscall to continue and change return value to ecode. */
|
||||||
|
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
|
||||||
|
for (;;) {
|
||||||
|
if (waitpid(pid, &status, WUNTRACED) != -1)
|
||||||
|
break;
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
sudo_warn(U_("%s: %s"), __func__, "waitpid");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (!WIFSTOPPED(status)) {
|
||||||
|
sudo_warnx(U_("process %d exited unexpectedly"), (int)pid);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
reg_retval(regs) = -ecode;
|
||||||
|
if (ptrace(PTRACE_SETREGSET, pid, (long)NT_PRSTATUS, &iov) == -1) {
|
||||||
|
sudo_warn(U_("unable to set registers for process %d"), (int)pid);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
done:
|
||||||
|
sigprocmask(SIG_UNBLOCK, &chldmask, NULL);
|
||||||
|
|
||||||
|
debug_return_bool(ret);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check whether seccomp(2) filtering supports ptrace(2) traps.
|
* Check whether seccomp(2) filtering supports ptrace(2) traps.
|
||||||
* Only supported by Linux 4.14 and higher.
|
* Only supported by Linux 4.14 and higher.
|
||||||
@@ -458,13 +522,83 @@ exec_ptrace_seize(pid_t child)
|
|||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Intercept execve(2) and perform a policy check.
|
||||||
|
* Reads current registers and execve(2) arguments.
|
||||||
|
* If the command is not allowed by policy, fail with EACCES.
|
||||||
|
* If the command is allowed, update argv if needed before continuing.
|
||||||
|
* Returns false on error, else true.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure)
|
||||||
|
{
|
||||||
|
char *pathname, **argv, **envp, *buf;
|
||||||
|
struct user_pt_regs regs;
|
||||||
|
char cwd[PATH_MAX];
|
||||||
|
int argc, envc;
|
||||||
|
debug_decl(ptrace_intercept_execve, SUDO_DEBUG_UTIL);
|
||||||
|
|
||||||
|
/* Get the current working directory and execve info. */
|
||||||
|
if (!getcwd_by_pid(pid, cwd, sizeof(cwd)))
|
||||||
|
(void)strlcpy(cwd, "unknown", sizeof(cwd));
|
||||||
|
buf = get_execve_info(pid, ®s, &pathname, &argc, &argv, &envc, &envp);
|
||||||
|
if (buf == NULL) {
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
|
||||||
|
"%s: %d: unable to get execve info", __func__, (int)pid);
|
||||||
|
|
||||||
|
/* Unrecoverable error, kill the process if it still exists. */
|
||||||
|
if (errno != ESRCH)
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
debug_return_bool(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform a policy check. */
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d: checking policy for %s",
|
||||||
|
__func__, (int)pid, pathname);
|
||||||
|
|
||||||
|
argv[0] = pathname;
|
||||||
|
if (!intercept_check_policy(pathname, argc, argv, envc, envp, cwd,
|
||||||
|
closure)) {
|
||||||
|
sudo_warnx("%s", U_(closure->errstr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closure->state == POLICY_ACCEPT) {
|
||||||
|
/*
|
||||||
|
* Update argv if the policy modified it.
|
||||||
|
* We don't currently ever modify envp.
|
||||||
|
*/
|
||||||
|
bool match = strcmp(pathname, closure->command) == 0;
|
||||||
|
if (match) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; closure->run_argv[i] != NULL && argv[i] != NULL; i++) {
|
||||||
|
if (strcmp(closure->run_argv[i], argv[i]) != 0) {
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!match) {
|
||||||
|
/* Need to replace argv with run_argv. */
|
||||||
|
/* XXX */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* If denied, fake the syscall and set return to EACCES */
|
||||||
|
ptrace_fail_syscall(pid, ®s, EACCES);
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept_closure_reset(closure);
|
||||||
|
|
||||||
|
debug_return_bool(true);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle a process stopped due to ptrace.
|
* Handle a process stopped due to ptrace.
|
||||||
* Returns true if the signal was suppressed and false if it was delivered.
|
* Returns true if the signal was suppressed and false if it was delivered.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
exec_ptrace_handled(pid_t pid, int status)
|
exec_ptrace_handled(pid_t pid, int status, void *intercept)
|
||||||
{
|
{
|
||||||
|
struct intercept_closure *closure = intercept;
|
||||||
const int stopsig = WSTOPSIG(status);
|
const int stopsig = WSTOPSIG(status);
|
||||||
const int sigtrap = status >> 8;
|
const int sigtrap = status >> 8;
|
||||||
long signo = 0;
|
long signo = 0;
|
||||||
@@ -472,22 +606,8 @@ exec_ptrace_handled(pid_t pid, int status)
|
|||||||
debug_decl(exec_ptrace_handled, SUDO_DEBUG_EXEC);
|
debug_decl(exec_ptrace_handled, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
if (sigtrap == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
|
if (sigtrap == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
|
||||||
char *pathname, **argv, **envp, *buf;
|
if (!ptrace_intercept_execve(pid, closure))
|
||||||
|
debug_return_bool(true);
|
||||||
/* Trapped child exec. */
|
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d called exec",
|
|
||||||
__func__, (int)pid);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the exec arguments and perform a policy check either over
|
|
||||||
* the socketpair (pty case) or via a direct function call (no pty).
|
|
||||||
* XXX
|
|
||||||
*/
|
|
||||||
buf = get_execve_args(pid, &pathname, &argv, &envp);
|
|
||||||
if (buf == NULL) {
|
|
||||||
sudo_debug_printf(SUDO_DEBUG_ERROR,
|
|
||||||
"%s: %d: unable to get exec args", __func__, (int)pid);
|
|
||||||
}
|
|
||||||
} else if (sigtrap == (SIGTRAP | (PTRACE_EVENT_CLONE << 8)) ||
|
} else if (sigtrap == (SIGTRAP | (PTRACE_EVENT_CLONE << 8)) ||
|
||||||
sigtrap == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)) ||
|
sigtrap == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)) ||
|
||||||
sigtrap == (SIGTRAP | (PTRACE_EVENT_FORK << 8))) {
|
sigtrap == (SIGTRAP | (PTRACE_EVENT_FORK << 8))) {
|
||||||
@@ -568,7 +688,7 @@ have_seccomp_action(const char *action)
|
|||||||
|
|
||||||
/* STUB */
|
/* STUB */
|
||||||
bool
|
bool
|
||||||
exec_ptrace_handled(pid_t pid, int status)
|
exec_ptrace_handled(pid_t pid, int status, void *intercept)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -77,6 +77,7 @@ struct exec_closure_pty {
|
|||||||
struct sudo_event *sigchld_event;
|
struct sudo_event *sigchld_event;
|
||||||
struct sudo_event *sigwinch_event;
|
struct sudo_event *sigwinch_event;
|
||||||
struct command_status *cstat;
|
struct command_status *cstat;
|
||||||
|
void *intercept;
|
||||||
struct monitor_message_list monitor_messages;
|
struct monitor_message_list monitor_messages;
|
||||||
pid_t monitor_pid;
|
pid_t monitor_pid;
|
||||||
pid_t cmnd_pid;
|
pid_t cmnd_pid;
|
||||||
@@ -1089,7 +1090,7 @@ handle_sigchld_pty(struct exec_closure_pty *ec)
|
|||||||
} else if (WIFSTOPPED(status)) {
|
} else if (WIFSTOPPED(status)) {
|
||||||
if (pid != ec->monitor_pid) {
|
if (pid != ec->monitor_pid) {
|
||||||
if (ISSET(ec->details->flags, CD_USE_PTRACE))
|
if (ISSET(ec->details->flags, CD_USE_PTRACE))
|
||||||
exec_ptrace_handled(pid, status);
|
exec_ptrace_handled(pid, status, ec->intercept);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1415,16 +1416,18 @@ exec_pty(struct command_details *details, struct command_status *cstat)
|
|||||||
fcntl(sv[1], F_SETFD, FD_CLOEXEC) == -1)
|
fcntl(sv[1], F_SETFD, FD_CLOEXEC) == -1)
|
||||||
sudo_fatal("%s", U_("unable to create sockets"));
|
sudo_fatal("%s", U_("unable to create sockets"));
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocate a socketpair for communicating with sudo_intercept.so.
|
|
||||||
* This must be inherited across exec, hence no FD_CLOEXEC.
|
|
||||||
* Check if the kernel supports the seccomp(2) filter "trap" action.
|
|
||||||
*/
|
|
||||||
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
|
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
|
||||||
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
|
if (have_seccomp_action("trap")) {
|
||||||
sudo_fatal("%s", U_("unable to create sockets"));
|
/* Kernel supports the seccomp(2) filter "trap" action. */
|
||||||
if (have_seccomp_action("trap"))
|
|
||||||
SET(details->flags, CD_USE_PTRACE);
|
SET(details->flags, CD_USE_PTRACE);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Allocate a socketpair for communicating with sudo_intercept.so.
|
||||||
|
* This must be inherited across exec, hence no FD_CLOEXEC.
|
||||||
|
*/
|
||||||
|
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
|
||||||
|
sudo_fatal("%s", U_("unable to create sockets"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1669,8 +1672,9 @@ exec_pty(struct command_details *details, struct command_status *cstat)
|
|||||||
fill_exec_closure_pty(&ec, cstat, details, ppgrp, sv[0]);
|
fill_exec_closure_pty(&ec, cstat, details, ppgrp, sv[0]);
|
||||||
|
|
||||||
/* Create event and closure for intercept mode. */
|
/* Create event and closure for intercept mode. */
|
||||||
if (intercept_sv[0] != -1) {
|
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
|
||||||
if (!intercept_setup(intercept_sv[0], ec.evbase, details))
|
ec.intercept = intercept_setup(intercept_sv[0], ec.evbase, details);
|
||||||
|
if (ec.intercept == NULL)
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -121,7 +121,7 @@ char **disable_execute(char *envp[], const char *dso);
|
|||||||
char **enable_monitor(char *envp[], const char *dso);
|
char **enable_monitor(char *envp[], const char *dso);
|
||||||
|
|
||||||
/* exec_intercept.c */
|
/* exec_intercept.c */
|
||||||
bool intercept_setup(int fd, struct sudo_event_base *evbase, struct command_details *details);
|
void *intercept_setup(int fd, struct sudo_event_base *evbase, struct command_details *details);
|
||||||
void intercept_cleanup(void);
|
void intercept_cleanup(void);
|
||||||
|
|
||||||
/* exec_nopty.c */
|
/* exec_nopty.c */
|
||||||
@@ -145,7 +145,7 @@ bool utmp_logout(const char *line, int status);
|
|||||||
char **sudo_preload_dso(char *envp[], const char *dso_file, int intercept_fd);
|
char **sudo_preload_dso(char *envp[], const char *dso_file, int intercept_fd);
|
||||||
|
|
||||||
/* exec_ptrace.c */
|
/* exec_ptrace.c */
|
||||||
bool exec_ptrace_handled(pid_t pid, int status);
|
bool exec_ptrace_handled(pid_t pid, int status, void *intercept);
|
||||||
bool exec_ptrace_seize(pid_t child);
|
bool exec_ptrace_seize(pid_t child);
|
||||||
bool have_seccomp_action(const char *action);
|
bool have_seccomp_action(const char *action);
|
||||||
bool set_exec_filter(void);
|
bool set_exec_filter(void);
|
||||||
|
Reference in New Issue
Block a user