]> git.saurik.com Git - apple/xnu.git/blame - tools/tests/personas/persona_spawn.c
xnu-3248.40.184.tar.gz
[apple/xnu.git] / tools / tests / personas / persona_spawn.c
CommitLineData
490019cf
A
1/*
2 * spawn_persona.c
3 * Use new POSIX spawn attributes to create a new process in a persona
4 *
5 * Jeremy C. Andrus <jeremy_andrus@apple.com>
6 *
7 */
8#include <ctype.h>
9#include <err.h>
10#include <errno.h>
11#include <inttypes.h>
12#include <libgen.h>
13#include <pthread.h>
14#include <signal.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h>
19#include <mach/mach.h>
20#include <mach/task.h>
21#include <mach/vm_param.h>
22#include <sys/queue.h>
23#include <sys/sysctl.h>
24#include <sys/types.h>
25
26#include "persona_test.h"
27
28/* internal */
29#include <libproc.h>
30#include <spawn_private.h>
31#include <sys/persona.h>
32#include <sys/proc_info.h>
33#include <sys/spawn_internal.h>
34
35#define PERSONA_TEST_NAME "Persona Spawn"
36#define PERSONA_TEST_VMAJOR 0
37#define PERSONA_TEST_VMINOR 1
38
39static struct test_config {
40 int verbose;
41 int wait_for_children;
42} g;
43
44
45/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
46 *
47 * Child Management
48 *
49 * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
50 */
51struct child {
52 TAILQ_ENTRY(child) sibling;
53 int pid;
54};
55
56static pthread_mutex_t g_child_mtx;
57static TAILQ_HEAD(, child) g_children = TAILQ_HEAD_INITIALIZER(g_children);
58static int g_nchildren = 0;
59
60static pid_t spawn_child(int argc, char **argv, struct persona_args *pa)
61{
62 int ret;
63 uint32_t persona_flags = 0;
64 posix_spawnattr_t attr;
65 struct child *child = NULL;
66 extern char **environ;
67
68 (void)argc;
69
70 if (!pa) {
71 err_print("Invalid persona args!");
72 return -ERR_SYSTEM;
73 }
74
75 if (!pa->flags & PA_HAS_ID) {
76 err_print("No persona ID specified!");
77 return -ERR_SYSTEM;
78 }
79
80 if (g.verbose) {
81 dump_persona_args("Spawning new child with args: ", pa);
82 infov("\t prog: \"%s\"", argv[0]);
83 for (int i = 1; i < argc; i++) {
84 infov("\t arg[%d]: %s", i, argv[i]);
85 }
86 }
87
88 child = (struct child *)calloc(1, sizeof(*child));
89 if (!child) {
90 err_print("No memory left :-(");
91 return -ERR_SYSTEM;
92 }
93
94 ret = posix_spawnattr_init(&attr);
95 if (ret != 0) {
96 err_print("posix_spawnattr_init");
97 ret = -ERR_SPAWN_ATTR;
98 goto out_err;
99 }
100
101 if (pa->flags & PA_SHOULD_VERIFY)
102 persona_flags |= POSIX_SPAWN_PERSONA_FLAGS_VERIFY;
103
104 if (pa->flags & PA_OVERRIDE)
105 persona_flags |= POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE;
106
107 ret = posix_spawnattr_set_persona_np(&attr, pa->kinfo.persona_id, persona_flags);
108 if (ret != 0) {
109 err_print("posix_spawnattr_set_persona_np failed!");
110 ret = -ERR_SPAWN_ATTR;
111 goto out_err;
112 }
113
114 if (pa->flags & PA_HAS_UID) {
115 ret = posix_spawnattr_set_persona_uid_np(&attr, pa->override_uid);
116 if (ret != 0) {
117 err_print("posix_spawnattr_set_persona_uid_np failed!");
118 ret = -ERR_SPAWN_ATTR;
119 goto out_err;
120 }
121 }
122
123 if (pa->flags & PA_HAS_GID) {
124 ret = posix_spawnattr_set_persona_gid_np(&attr, pa->kinfo.persona_gid);
125 if (ret != 0) {
126 err_print("posix_spawnattr_set_persona_gid_np failed!");
127 ret = -ERR_SPAWN_ATTR;
128 goto out_err;
129 }
130 }
131
132 ret = posix_spawn(&child->pid, argv[0], NULL, &attr, argv, environ);
133 if (ret != 0) {
134 err_print("posix_spawn (ret=%d)", ret);
135 ret = -ERR_SPAWN;
136 goto out_err;
137 }
138
139 infov("\tspawned child PID: %d", child->pid);
140
141 /* link the processes onto the global children list */
142 pthread_mutex_lock(&g_child_mtx);
143 TAILQ_INSERT_TAIL(&g_children, child, sibling);
144 ++g_nchildren;
145 pthread_mutex_unlock(&g_child_mtx);
146
147 posix_spawnattr_destroy(&attr);
148 return child->pid;
149
150out_err:
151 posix_spawnattr_destroy(&attr);
152 free(child);
153 return (pid_t)ret;
154}
155
156
157static int child_should_exit = 0;
158
159static void child_sighandler(int sig)
160{
161 (void)sig;
162 dbg("PID: %d received sig %d", getpid(), sig);
163 child_should_exit = 1;
164}
165
166static int child_main_loop(int argc, char **argv)
167{
168 char ch;
169 sigset_t sigset;
170 int err = 0;
171 uid_t persona_id = 0;
172 struct kpersona_info kinfo;
173 int rval = 0;
174
175 while ((ch = getopt(argc, argv, "vhER")) != -1) {
176 switch (ch) {
177 case 'v':
178 g.verbose = 1;
179 break;
180 case 'E':
181 child_should_exit = 1;
182 break;
183 case 'R':
184 rval = 1;
185 break;
186 case 'h':
187 case '?':
188 case ':':
189 default:
190 err("Invalid child process invocation.");
191 }
192 }
193
194 sigemptyset(&sigset);
195 sigaddset(&sigset, SIGINT);
196 sigaddset(&sigset, SIGHUP);
197 sigaddset(&sigset, SIGTERM);
198 sigaddset(&sigset, SIGABRT);
199 sigaddset(&sigset, SIGCHLD);
200 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
201
202 signal(SIGINT, child_sighandler);
203 signal(SIGHUP, child_sighandler);
204 signal(SIGTERM, child_sighandler);
205 signal(SIGABRT, child_sighandler);
206 signal(SIGCHLD, child_sighandler);
207
208 err = kpersona_get(&persona_id);
209
210 info("Child: PID:%d", getpid());
211 info("Child: UID:%d, GID:%d", getuid(), getgid());
212 info("Child: login:%s", getlogin());
213 info("Child: Persona: %d (err:%d)", persona_id, err);
214
215 kinfo.persona_info_version = PERSONA_INFO_V1;
216 err = kpersona_info(persona_id, &kinfo);
217 if (err == 0)
218 dump_kpersona("Child: kpersona_info", &kinfo);
219 else
220 info("Child: ERROR grabbing kpersona_info: %d", errno);
221
222 if (child_should_exit)
223 return rval;
224
225 infov("Child Sleeping!");
226 while (!child_should_exit)
227 sleep(1);
228
229 infov("Child exiting!");
230 return rval;
231}
232
233
234/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
235 *
236 * Main Entry Point
237 *
238 * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
239 */
240static void main_sighandler(int sig)
241{
242 dbg("PID: %d received sig %d", getpid(), sig);
243 if (sig == SIGCHLD) {
244 --g_nchildren;
245 }
246}
247
248static void usage_main(const char *progname, int verbose)
249{
250 const char *nm = basename((char *)progname);
251
252 printf("%s v%d.%d\n", PERSONA_TEST_NAME, PERSONA_TEST_VMAJOR, PERSONA_TEST_VMINOR);
253 printf("usage: %s [-I id] [-V] [-u uid] [-g gid] [-vw] progname [args...]\n", nm);
254 printf(" Spawn a new process into a new or existing persona.\n");
255 if (!verbose)
256 exit(1);
257
258 printf("\t%-10s\tID of the persona\n", "-I id");
259 printf("\t%-10s\tVerify persona parameters against existing persona (given by -I)\n", "-V");
260 printf("\t%-10s\tOverride/verify the user ID of the new process\n", "-u uid");
261 printf("\t%-10s\tOverride/verify the group ID of the new process\n", "-g gid");
262 printf("\t%-10s\tBe verbose\n", "-v");
263 printf("\t%-10s\tDo not wait for the child process\n", "-w");
264 printf("\n");
265
266 exit(1);
267}
268
269int main(int argc, char **argv)
270{
271 char ch;
272 int ret;
273
274 pthread_mutex_init(&g_child_mtx, NULL);
275
276 /*
277 * Defaults
278 */
279 g.verbose = 0;
280 g.wait_for_children = 1;
281
282 if (argc > 1 && strcmp(argv[1], "child") == 0) {
283 optind = 2;
284 ret = child_main_loop(argc, argv);
285 if (ret != 1)
286 exit(0);
287
288 /*
289 * If we get here, then the child wants us to continue running
290 * to potentially spawn yet another child process. This is
291 * helpful when testing inherited personas and verifying
292 * persona restrictions.
293 */
294 }
295
296 if (geteuid() != 0)
297 err("%s must be run as root", argv[0] ? basename(argv[0]) : PERSONA_TEST_NAME);
298
299 struct persona_args pa;
300 memset(&pa, 0, sizeof(pa));
301
302 pa.flags = PA_NONE;
303 pa.kinfo.persona_id = getuid();
304
305 /*
306 * Argument parse for default overrides:
307 */
308 while ((ch = getopt(argc, argv, "Vg:I:u:vwh")) != -1) {
309 switch (ch) {
310 case 'V':
311 pa.flags |= PA_SHOULD_VERIFY;
312 break;
313 case 'g':
314 pa.kinfo.persona_gid = atoi(optarg);
315 if (pa.kinfo.persona_gid <= 500)
316 err("Invalid GID: %d", pa.kinfo.persona_gid);
317 pa.flags |= PA_HAS_GID;
318 pa.flags |= PA_OVERRIDE;
319 break;
320 case 'I':
321 pa.kinfo.persona_id = atoi(optarg);
322 if (pa.kinfo.persona_id == 0)
323 err("Invalid Persona ID: %s", optarg);
324 pa.flags |= PA_HAS_ID;
325 break;
326 case 'u':
327 pa.override_uid = atoi(optarg);
328 if (pa.override_uid <= 500)
329 err("Invalid UID: %d", pa.override_uid);
330 pa.flags |= PA_HAS_UID;
331 pa.flags |= PA_OVERRIDE;
332 break;
333 case 'v':
334 g.verbose = 1;
335 break;
336 case 'w':
337 g.wait_for_children = 0;
338 break;
339 case 'h':
340 case '?':
341 usage_main(argv[0], 1);
342 case ':':
343 default:
344 printf("Invalid option: '%c'\n", ch);
345 usage_main(argv[0], 0);
346 }
347 }
348
349 if (pa.flags & PA_SHOULD_VERIFY)
350 pa.flags = ~PA_OVERRIDE;
351
352 if (optind >= argc) {
353 printf("No program given!\n");
354 usage_main(argv[0], 0);
355 }
356
357 argc -= optind;
358 for (int i = 0; i < argc; i++) {
359 argv[i] = argv[i + optind];
360 }
361
362 argv[argc] = NULL;
363
364 ret = spawn_child(argc, argv, &pa);
365 if (ret < 0)
366 return ret;
367
368 pid_t child_pid = (pid_t)ret;
369 int status = 0;
370 sigset_t sigset;
371 sigemptyset(&sigset);
372 sigaddset(&sigset, SIGCHLD);
373 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
374 signal(SIGCHLD, main_sighandler);
375
376 if (g.wait_for_children) {
377 infov("Waiting for child...");
378 waitpid(child_pid, &status, 0);
379 if (WIFEXITED(status)) {
380 status = WEXITSTATUS(status);
381 if (status != 0)
382 errc(ERR_CHILD_FAIL,
383 "Child exited with status: %d", status);
384 }
385 }
386
387 info("Done.");
388 return 0;
389}