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