Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/regress/intercept/test_ptrace.c
1532 views
1
/*
2
* Copyright (c) 2022 Todd C. Miller <[email protected]>
3
*
4
* Permission to use, copy, modify, and distribute this software for any
5
* purpose with or without fee is hereby granted, provided that the above
6
* copyright notice and this permission notice appear in all copies.
7
*
8
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
*/
16
17
/*
18
* Test program to exercise seccomp(2) and ptrace(2) intercept code.
19
*
20
* Usage: test_ptrace [-d 1-3] [command]
21
*/
22
23
/* Ignore architecture restrictions and define this unilaterally. */
24
#define HAVE_PTRACE_INTERCEPT
25
#include "exec_ptrace.c"
26
27
static sig_atomic_t got_sigchld;
28
static int debug;
29
int sudo_debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
30
31
sudo_dso_public int main(int argc, char *argv[]);
32
33
static void
34
handler(int signo)
35
{
36
if (signo == SIGCHLD)
37
got_sigchld = 1;
38
}
39
40
void
41
intercept_closure_reset(struct intercept_closure *closure)
42
{
43
memset(closure, 0, sizeof(*closure));
44
}
45
46
bool
47
intercept_check_policy(const char *command, int argc, char **argv, int envc,
48
char **envp, const char *runcwd, int *oldcwd, void *v)
49
{
50
struct intercept_closure *closure = v;
51
struct stat sb1, sb2;
52
bool is_denied;
53
debug_decl(intercept_check_policy, SUDO_DEBUG_EXEC);
54
55
/* Fake policy decisions. */
56
is_denied = stat(command, &sb1) == 0 && stat("/usr/bin/who", &sb2) == 0 &&
57
sb1.st_ino == sb2.st_ino && sb1.st_dev == sb2.st_dev;
58
if (is_denied) {
59
sudo_debug_printf(SUDO_DEBUG_DIAG, "denied %s", command);
60
closure->state = POLICY_REJECT;
61
} else {
62
sudo_debug_printf(SUDO_DEBUG_DIAG, "allowed %s", command);
63
closure->state = POLICY_TEST;
64
}
65
*oldcwd = -1;
66
67
debug_return_bool(true);
68
}
69
70
static void
71
init_debug_files(struct sudo_conf_debug_file_list *file_list,
72
struct sudo_debug_file *file)
73
{
74
debug_decl(init_debug_files, SUDO_DEBUG_EXEC);
75
76
TAILQ_INIT(file_list);
77
switch (debug) {
78
case 0:
79
debug_return;
80
case 1:
81
file->debug_flags = (char *)"exec@diag";
82
break;
83
case 2:
84
file->debug_flags = (char *)"exec@info";
85
break;
86
default:
87
file->debug_flags = (char *)"exec@debug";
88
break;
89
}
90
file->debug_file = (char *)"/dev/stderr";
91
TAILQ_INSERT_HEAD(file_list, file, entries);
92
93
debug_return;
94
}
95
96
int
97
sudo_sigaction(int signo, struct sigaction *sa, struct sigaction *osa)
98
{
99
return sigaction(signo, sa, osa);
100
}
101
102
int
103
main(int argc, char *argv[])
104
{
105
struct sudo_conf_debug_file_list debug_files;
106
struct sudo_debug_file debug_file;
107
const char *base, *shell = _PATH_SUDO_BSHELL;
108
struct intercept_closure closure = { 0 };
109
const char *errstr;
110
sigset_t blocked, empty;
111
struct sigaction sa;
112
pid_t child, my_pid, pid, my_pgrp;
113
int ch, status;
114
debug_decl_vars(main, SUDO_DEBUG_MAIN);
115
116
initprogname(argc > 0 ? argv[0] : "test_ptrace");
117
118
if (!have_seccomp_action("trap"))
119
sudo_fatalx("SECCOMP_MODE_FILTER not available in this kernel");
120
121
while ((ch = getopt(argc, argv, "d:")) != -1) {
122
switch (ch) {
123
case 'd':
124
debug = sudo_strtonum(optarg, 1, INT_MAX, &errstr);
125
if (errstr != NULL)
126
sudo_fatalx(U_("%s: %s"), optarg, U_(errstr));
127
break;
128
default:
129
fprintf(stderr, "usage: %s [-d 1-3] [command]\n", getprogname());
130
return EXIT_FAILURE;
131
}
132
}
133
argc -= optind;
134
argv += optind;
135
136
if (argc > 0)
137
shell = argv[0];
138
base = strrchr(shell, '/');
139
base = base ? base + 1 : shell;
140
141
/* Set debug level based on the debug flag. */
142
init_debug_files(&debug_files, &debug_file);
143
sudo_debug_instance = sudo_debug_register(getprogname(),
144
NULL, NULL, &debug_files, -1);
145
if (sudo_debug_instance == SUDO_DEBUG_INSTANCE_ERROR)
146
return EXIT_FAILURE;
147
148
/* Block SIGCHLD and SIGUSR during critical section. */
149
sigemptyset(&empty);
150
sigemptyset(&blocked);
151
sigaddset(&blocked, SIGCHLD);
152
sigaddset(&blocked, SIGUSR1);
153
sigprocmask(SIG_BLOCK, &blocked, NULL);
154
155
/* Signal handler sets a flag for SIGCHLD, nothing for SIGUSR1. */
156
memset(&sa, 0, sizeof(sa));
157
sigemptyset(&sa.sa_mask);
158
sa.sa_flags = SA_RESTART;
159
sa.sa_handler = handler;
160
sigaction(SIGCHLD, &sa, NULL);
161
sigaction(SIGUSR1, &sa, NULL);
162
163
/* Fork a shell. */
164
my_pid = getpid();
165
my_pgrp = getpgrp();
166
child = fork();
167
switch (child) {
168
case -1:
169
sudo_fatal("fork");
170
case 0:
171
/* child */
172
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
173
sudo_fatal("%s", "unable to set no_new_privs bit");
174
if (!set_exec_filter())
175
_exit(EXIT_FAILURE);
176
177
/* Suspend child until tracer seizes control and sends SIGUSR1. */
178
sigsuspend(&empty);
179
execl(shell, base, NULL);
180
sudo_fatal("execl");
181
default:
182
/* Parent attaches to child and allows it to continue. */
183
if (exec_ptrace_seize(child) == -1)
184
return EXIT_FAILURE;
185
break;
186
}
187
188
/* Wait for SIGCHLD. */
189
for (;;) {
190
sigsuspend(&empty);
191
if (!got_sigchld)
192
continue;
193
got_sigchld = 0;
194
195
for (;;) {
196
do {
197
pid = waitpid(-1, &status, __WALL|WNOHANG);
198
} while (pid == -1 && errno == EINTR);
199
if (pid <= 0) {
200
if (pid == -1 && errno != ECHILD)
201
sudo_fatal("waitpid");
202
/* No child to wait for. */
203
break;
204
}
205
206
if (WIFEXITED(status)) {
207
sudo_debug_printf(SUDO_DEBUG_DIAG, "%d: exited %d",
208
(int)pid, WEXITSTATUS(status));
209
if (pid == child)
210
return WEXITSTATUS(status);
211
} else if (WIFSIGNALED(status)) {
212
sudo_debug_printf(SUDO_DEBUG_DIAG, "%d: killed by signal %d",
213
(int)pid, WTERMSIG(status));
214
if (pid == child)
215
return WTERMSIG(status) | 128;
216
} else if (WIFSTOPPED(status)) {
217
if (exec_ptrace_stopped(pid, status, &closure)) {
218
if (pid == child) {
219
sudo_suspend_parent(WSTOPSIG(status), my_pid,
220
my_pgrp, child, NULL, NULL);
221
if (kill(child, SIGCONT) != 0)
222
sudo_warn("kill(%d, SIGCONT)", (int)child);
223
}
224
}
225
} else {
226
sudo_fatalx("%d: unknown status 0x%x", (int)pid, status);
227
}
228
}
229
}
230
}
231
232