1 /*
2  * spawn_persona.c
3  * Use new POSIX spawn attributes to create a new process in a persona
4  *
5  * Jeremy C. Andrus <[email protected]>
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/kauth.h>
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 
40 static struct test_config {
41 	int verbose;
42 	int wait_for_children;
43 } g;
44 
45 
46 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
47  *
48  * Child Management
49  *
50  * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
51  */
52 struct child {
53 	TAILQ_ENTRY(child) sibling;
54 	int pid;
55 };
56 
57 static pthread_mutex_t g_child_mtx;
58 static TAILQ_HEAD(, child) g_children = TAILQ_HEAD_INITIALIZER(g_children);
59 static int g_nchildren = 0;
60 
61 static pid_t
spawn_child(int argc,char ** argv,struct persona_args * pa)62 spawn_child(int argc, char **argv, struct persona_args *pa)
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 
77 	if (!(pa->flags & PA_HAS_ID)) {
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 
103 	if (pa->flags & PA_SHOULD_VERIFY) {
104 		persona_flags |= POSIX_SPAWN_PERSONA_FLAGS_VERIFY;
105 	}
106 
107 	if (pa->flags & PA_OVERRIDE) {
108 		persona_flags |= POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE;
109 	}
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 
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 
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 
163 out_err:
164 	posix_spawnattr_destroy(&attr);
165 	free(child);
166 	return (pid_t)ret;
167 }
168 
169 
170 static int child_should_exit = 0;
171 
172 static void
child_sighandler(int sig)173 child_sighandler(int sig)
174 {
175 	(void)sig;
176 	dbg("PID: %d received sig %d", getpid(), sig);
177 	child_should_exit = 1;
178 }
179 
180 static int
child_main_loop(int argc,char ** argv)181 child_main_loop(int argc, char **argv)
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);
232 	if (err == 0) {
233 		dump_kpersona("Child: kpersona_info", &kinfo);
234 	} else {
235 		info("Child: ERROR grabbing kpersona_info: %d", errno);
236 	}
237 
238 	if (child_should_exit) {
239 		return rval;
240 	}
241 
242 	infov("Child Sleeping!");
243 	while (!child_should_exit) {
244 		sleep(1);
245 	}
246 
247 	infov("Child exiting!");
248 	return rval;
249 }
250 
251 
252 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
253  *
254  * Main Entry Point
255  *
256  * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
257  */
258 static void
main_sighandler(int sig)259 main_sighandler(int sig)
260 {
261 	dbg("PID: %d received sig %d", getpid(), sig);
262 	if (sig == SIGCHLD) {
263 		--g_nchildren;
264 	}
265 }
266 
267 static void
usage_main(const char * progname,int verbose)268 usage_main(const char *progname, int verbose)
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");
275 	if (!verbose) {
276 		exit(1);
277 	}
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");
283 	printf("\t%-15s\tGroups to which the persona will belong\n", "-G {groupspec}");
284 	printf("\t%-15s\tgroupspec: G1{,G2,G3...}\n", " ");
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 
292 int
main(int argc,char ** argv)293 main(int argc, char **argv)
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);
309 		if (ret != 1) {
310 			exit(ret);
311 		}
312 		if (strcmp(argv[optind], "spawn") != 0) {
313 			printf("child exiting (%s).\n", argv[optind]);
314 			exit(0);
315 		}
316 		optind++;
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 
326 	if (geteuid() != 0) {
327 		err("%s must be run as root", argv[0] ? basename(argv[0]) : PERSONA_TEST_NAME);
328 	}
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 	 */
339 	while ((ch = getopt(argc, argv, "Vg:G:I:u:vwh")) != -1) {
340 		switch (ch) {
341 		case 'V':
342 			pa.flags |= PA_SHOULD_VERIFY;
343 			break;
344 		case 'g':
345 			pa.kinfo.persona_gid = atoi(optarg);
346 			pa.flags |= PA_HAS_GID;
347 			pa.flags |= PA_OVERRIDE;
348 			break;
349 		case 'G':
350 			ret = parse_groupspec(&pa.kinfo, optarg);
351 			if (ret < 0) {
352 				err("Invalid groupspec: \"%s\"", optarg);
353 			}
354 			pa.flags |= PA_HAS_GROUPS;
355 			pa.flags |= PA_OVERRIDE;
356 			break;
357 		case 'I':
358 			pa.kinfo.persona_id = atoi(optarg);
359 			if (pa.kinfo.persona_id == 0) {
360 				err("Invalid Persona ID: %s", optarg);
361 			}
362 			pa.flags |= PA_HAS_ID;
363 			break;
364 		case 'u':
365 			pa.override_uid = atoi(optarg);
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 
385 	if (pa.flags & PA_SHOULD_VERIFY) {
386 		pa.flags = ~PA_OVERRIDE;
387 	}
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);
402 	if (ret < 0) {
403 		return ret;
404 	}
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);
419 			if (status != 0) {
420 				errc(ERR_CHILD_FAIL,
421 				    "Child exited with status: %d", status);
422 			}
423 		}
424 	}
425 
426 	info("Done.");
427 	return 0;
428 }
429