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