Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tools/regression/security/proc_to_proc/scenario.c
48266 views
1
/*-
2
* Copyright (c) 2001 Robert N. M. Watson
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/param.h>
28
#include <sys/uio.h>
29
#include <sys/ptrace.h>
30
#include <sys/time.h>
31
#include <sys/resource.h>
32
#include <sys/syscall.h>
33
#include <sys/wait.h>
34
#include <sys/ktrace.h>
35
36
#include <assert.h>
37
#include <errno.h>
38
#include <signal.h>
39
#include <stdio.h>
40
#include <string.h>
41
#include <unistd.h>
42
43
/*
44
* Relevant parts of a process credential.
45
*/
46
struct cred {
47
uid_t cr_euid, cr_ruid, cr_svuid;
48
int cr_issetugid;
49
};
50
51
/*
52
* Description of a scenario.
53
*/
54
struct scenario {
55
struct cred *sc_cred1, *sc_cred2; /* credentials of p1 and p2 */
56
int sc_canptrace_errno; /* desired ptrace failure */
57
int sc_canktrace_errno; /* desired ktrace failure */
58
int sc_cansighup_errno; /* desired SIGHUP failure */
59
int sc_cansigsegv_errno; /* desired SIGSEGV failure */
60
int sc_cansee_errno; /* desired getprio failure */
61
int sc_cansched_errno; /* desired setprio failure */
62
char *sc_name; /* test name */
63
};
64
65
/*
66
* Table of relevant credential combinations.
67
*/
68
static struct cred creds[] = {
69
/* euid ruid svuid issetugid */
70
/* 0 */ { 0, 0, 0, 0 }, /* privileged */
71
/* 1 */ { 0, 0, 0, 1 }, /* privileged + issetugid */
72
/* 2 */ { 1000, 1000, 1000, 0 }, /* unprivileged1 */
73
/* 3 */ { 1000, 1000, 1000, 1 }, /* unprivileged1 + issetugid */
74
/* 4 */ { 1001, 1001, 1001, 0 }, /* unprivileged2 */
75
/* 5 */ { 1001, 1001, 1001, 1 }, /* unprivileged2 + issetugid */
76
/* 6 */ { 1000, 0, 0, 0 }, /* daemon1 */
77
/* 7 */ { 1000, 0, 0, 1 }, /* daemon1 + issetugid */
78
/* 8 */ { 1001, 0, 0, 0 }, /* daemon2 */
79
/* 9 */ { 1001, 0, 0, 1 }, /* daemon2 + issetugid */
80
/* 10 */{ 0, 1000, 1000, 0 }, /* setuid1 */
81
/* 11 */{ 0, 1000, 1000, 1 }, /* setuid1 + issetugid */
82
/* 12 */{ 0, 1001, 1001, 0 }, /* setuid2 */
83
/* 13 */{ 0, 1001, 1001, 1 }, /* setuid2 + issetugid */
84
};
85
86
/*
87
* Table of scenarios.
88
*/
89
static const struct scenario scenarios[] = {
90
/* cred1 cred2 ptrace ktrace, sighup sigsegv see sched name */
91
/* privileged on privileged */
92
{ &creds[0], &creds[0], 0, 0, 0, 0, 0, 0, "0. priv on priv"},
93
{ &creds[0], &creds[1], 0, 0, 0, 0, 0, 0, "1. priv on priv"},
94
{ &creds[1], &creds[0], 0, 0, 0, 0, 0, 0, "2. priv on priv"},
95
{ &creds[1], &creds[1], 0, 0, 0, 0, 0, 0, "3. priv on priv"},
96
/* privileged on unprivileged */
97
{ &creds[0], &creds[2], 0, 0, 0, 0, 0, 0, "4. priv on unpriv1"},
98
{ &creds[0], &creds[3], 0, 0, 0, 0, 0, 0, "5. priv on unpriv1"},
99
{ &creds[1], &creds[2], 0, 0, 0, 0, 0, 0, "6. priv on unpriv1"},
100
{ &creds[1], &creds[3], 0, 0, 0, 0, 0, 0, "7. priv on unpriv1"},
101
/* unprivileged on privileged */
102
{ &creds[2], &creds[0], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "8. unpriv1 on priv"},
103
{ &creds[2], &creds[1], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "9. unpriv1 on priv"},
104
{ &creds[3], &creds[0], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "10. unpriv1 on priv"},
105
{ &creds[3], &creds[1], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "11. unpriv1 on priv"},
106
/* unprivileged on same unprivileged */
107
{ &creds[2], &creds[2], 0, 0, 0, 0, 0, 0, "12. unpriv1 on unpriv1"},
108
{ &creds[2], &creds[3], EPERM, EPERM, 0, EPERM, 0, 0, "13. unpriv1 on unpriv1"},
109
{ &creds[3], &creds[2], 0, 0, 0, 0, 0, 0, "14. unpriv1 on unpriv1"},
110
{ &creds[3], &creds[3], EPERM, EPERM, 0, EPERM, 0, 0, "15. unpriv1 on unpriv1"},
111
/* unprivileged on different unprivileged */
112
{ &creds[2], &creds[4], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "16. unpriv1 on unpriv2"},
113
{ &creds[2], &creds[5], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "17. unpriv1 on unpriv2"},
114
{ &creds[3], &creds[4], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "18. unpriv1 on unpriv2"},
115
{ &creds[3], &creds[5], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "19. unpriv1 on unpriv2"},
116
/* unprivileged on daemon, same */
117
{ &creds[2], &creds[6], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "20. unpriv1 on daemon1"},
118
{ &creds[2], &creds[7], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "21. unpriv1 on daemon1"},
119
{ &creds[3], &creds[6], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "22. unpriv1 on daemon1"},
120
{ &creds[3], &creds[7], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "23. unpriv1 on daemon1"},
121
/* unprivileged on daemon, different */
122
{ &creds[2], &creds[8], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "24. unpriv1 on daemon2"},
123
{ &creds[2], &creds[9], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "25. unpriv1 on daemon2"},
124
{ &creds[3], &creds[8], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "26. unpriv1 on daemon2"},
125
{ &creds[3], &creds[9], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "27. unpriv1 on daemon2"},
126
/* unprivileged on setuid, same */
127
{ &creds[2], &creds[10], EPERM, EPERM, 0, 0, 0, 0, "28. unpriv1 on setuid1"},
128
{ &creds[2], &creds[11], EPERM, EPERM, 0, EPERM, 0, 0, "29. unpriv1 on setuid1"},
129
{ &creds[3], &creds[10], EPERM, EPERM, 0, 0, 0, 0, "30. unpriv1 on setuid1"},
130
{ &creds[3], &creds[11], EPERM, EPERM, 0, EPERM, 0, 0, "31. unpriv1 on setuid1"},
131
/* unprivileged on setuid, different */
132
{ &creds[2], &creds[12], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "32. unpriv1 on setuid2"},
133
{ &creds[2], &creds[13], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "33. unpriv1 on setuid2"},
134
{ &creds[3], &creds[12], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "34. unpriv1 on setuid2"},
135
{ &creds[3], &creds[13], EPERM, EPERM, EPERM, EPERM, 0, EPERM, "35. unpriv1 on setuid2"},
136
};
137
int scenarios_count = sizeof(scenarios) / sizeof(struct scenario);
138
139
/*
140
* Convert an error number to a compact string representation. For now,
141
* implement only the error numbers we are likely to see.
142
*/
143
static char *
144
errno_to_string(int error)
145
{
146
147
switch (error) {
148
case EPERM:
149
return ("EPERM");
150
case EACCES:
151
return ("EACCES");
152
case EINVAL:
153
return ("EINVAL");
154
case ENOSYS:
155
return ("ENOSYS");
156
case ESRCH:
157
return ("ESRCH");
158
case EOPNOTSUPP:
159
return ("EOPNOTSUPP");
160
case 0:
161
return ("0");
162
default:
163
printf("%d\n", error);
164
return ("unknown");
165
}
166
}
167
168
/*
169
* Return a process credential describing the current process.
170
*/
171
static int
172
cred_get(struct cred *cred)
173
{
174
int error;
175
176
error = getresuid(&cred->cr_ruid, &cred->cr_euid, &cred->cr_svuid);
177
if (error)
178
return (error);
179
180
cred->cr_issetugid = issetugid();
181
182
return (0);
183
}
184
185
/*
186
* Userland stub for __setsugid() to take into account possible presence
187
* in C library, kernel, et al.
188
*/
189
int
190
setugid(int flag)
191
{
192
193
#ifdef SETSUGID_SUPPORTED
194
return (__setugid(flag));
195
#else
196
#ifdef SETSUGID_SUPPORTED_BUT_NO_LIBC_STUB
197
return (syscall(374, flag));
198
#else
199
return (ENOSYS);
200
#endif
201
#endif
202
}
203
204
/*
205
* Set the current process's credentials to match the passed credential.
206
*/
207
static int
208
cred_set(struct cred *cred)
209
{
210
int error;
211
212
error = setresuid(cred->cr_ruid, cred->cr_euid, cred->cr_svuid);
213
if (error)
214
return (error);
215
216
error = setugid(cred->cr_issetugid);
217
if (error) {
218
perror("__setugid");
219
return (error);
220
}
221
222
#ifdef CHECK_CRED_SET
223
{
224
uid_t ruid, euid, svuid;
225
error = getresuid(&ruid, &euid, &svuid);
226
if (error) {
227
perror("getresuid");
228
return (-1);
229
}
230
assert(ruid == cred->cr_ruid);
231
assert(euid == cred->cr_euid);
232
assert(svuid == cred->cr_svuid);
233
assert(cred->cr_issetugid == issetugid());
234
}
235
#endif /* !CHECK_CRED_SET */
236
237
return (0);
238
}
239
240
/*
241
* Print the passed process credential to the passed I/O stream.
242
*/
243
static void
244
cred_print(FILE *output, struct cred *cred)
245
{
246
247
fprintf(output, "(e:%d r:%d s:%d P_SUGID:%d)", cred->cr_euid,
248
cred->cr_ruid, cred->cr_svuid, cred->cr_issetugid);
249
}
250
251
#define LOOP_PTRACE 0
252
#define LOOP_KTRACE 1
253
#define LOOP_SIGHUP 2
254
#define LOOP_SIGSEGV 3
255
#define LOOP_SEE 4
256
#define LOOP_SCHED 5
257
#define LOOP_MAX LOOP_SCHED
258
259
/*
260
* Enact a scenario by looping through the four test cases for the scenario,
261
* spawning off pairs of processes with the desired credentials, and
262
* reporting results to stdout.
263
*/
264
static int
265
enact_scenario(int scenario)
266
{
267
pid_t pid1, pid2;
268
char *name, *tracefile;
269
int error, desirederror, loop;
270
271
for (loop = 0; loop < LOOP_MAX+1; loop++) {
272
/*
273
* Spawn the first child, target of the operation.
274
*/
275
pid1 = fork();
276
switch (pid1) {
277
case -1:
278
return (-1);
279
case 0:
280
/* child */
281
error = cred_set(scenarios[scenario].sc_cred2);
282
if (error) {
283
perror("cred_set");
284
return (error);
285
}
286
/* 200 seconds should be plenty of time. */
287
sleep(200);
288
exit(0);
289
default:
290
/* parent */
291
break;
292
}
293
294
/*
295
* XXX
296
* This really isn't ideal -- give proc 1 a chance to set
297
* its credentials, or we may get spurious errors. Really,
298
* some for of IPC should be used to allow the parent to
299
* wait for the first child to be ready before spawning
300
* the second child.
301
*/
302
sleep(1);
303
304
/*
305
* Spawn the second child, source of the operation.
306
*/
307
pid2 = fork();
308
switch (pid2) {
309
case -1:
310
return (-1);
311
312
case 0:
313
/* child */
314
error = cred_set(scenarios[scenario].sc_cred1);
315
if (error) {
316
perror("cred_set");
317
return (error);
318
}
319
320
/*
321
* Initialize errno to zero so as to catch any
322
* generated errors. In each case, perform the
323
* operation. Preserve the error number for later
324
* use so it doesn't get stomped on by any I/O.
325
* Determine the desired error for the given case
326
* by extracting it from the scenario table.
327
* Initialize a function name string for output
328
* prettiness.
329
*/
330
errno = 0;
331
switch (loop) {
332
case LOOP_PTRACE:
333
error = ptrace(PT_ATTACH, pid1, NULL, 0);
334
error = errno;
335
name = "ptrace";
336
desirederror =
337
scenarios[scenario].sc_canptrace_errno;
338
break;
339
case LOOP_KTRACE:
340
tracefile = mktemp("/tmp/testuid_ktrace.XXXXXX");
341
if (tracefile == NULL) {
342
error = errno;
343
perror("mktemp");
344
break;
345
}
346
error = ktrace(tracefile, KTROP_SET,
347
KTRFAC_SYSCALL, pid1);
348
error = errno;
349
name = "ktrace";
350
desirederror =
351
scenarios[scenario].sc_canktrace_errno;
352
unlink(tracefile);
353
break;
354
case LOOP_SIGHUP:
355
error = kill(pid1, SIGHUP);
356
error = errno;
357
name = "sighup";
358
desirederror =
359
scenarios[scenario].sc_cansighup_errno;
360
break;
361
case LOOP_SIGSEGV:
362
error = kill(pid1, SIGSEGV);
363
error = errno;
364
name = "sigsegv";
365
desirederror =
366
scenarios[scenario].sc_cansigsegv_errno;
367
break;
368
case LOOP_SEE:
369
getpriority(PRIO_PROCESS, pid1);
370
error = errno;
371
name = "see";
372
desirederror =
373
scenarios[scenario].sc_cansee_errno;
374
break;
375
case LOOP_SCHED:
376
error = setpriority(PRIO_PROCESS, pid1,
377
0);
378
error = errno;
379
name = "sched";
380
desirederror =
381
scenarios[scenario].sc_cansched_errno;
382
break;
383
default:
384
name = "broken";
385
}
386
387
if (error != desirederror) {
388
fprintf(stdout,
389
"[%s].%s: expected %s, got %s\n ",
390
scenarios[scenario].sc_name, name,
391
errno_to_string(desirederror),
392
errno_to_string(error));
393
cred_print(stdout,
394
scenarios[scenario].sc_cred1);
395
cred_print(stdout,
396
scenarios[scenario].sc_cred2);
397
fprintf(stdout, "\n");
398
}
399
400
exit(0);
401
402
default:
403
/* parent */
404
break;
405
}
406
407
error = waitpid(pid2, NULL, 0);
408
/*
409
* Once pid2 has died, it's safe to kill pid1, if it's still
410
* alive. Mask signal failure in case the test actually
411
* killed pid1 (not unlikely: can occur in both signal and
412
* ptrace cases).
413
*/
414
kill(pid1, SIGKILL);
415
error = waitpid(pid2, NULL, 0);
416
}
417
418
return (0);
419
}
420
421
void
422
enact_scenarios(void)
423
{
424
int i, error;
425
426
for (i = 0; i < scenarios_count; i++) {
427
error = enact_scenario(i);
428
if (error)
429
perror("enact_scenario");
430
}
431
}
432
433