3 * Use new POSIX spawn attributes to create a new process in a persona
5 * Jeremy C. Andrus <jeremy_andrus@apple.com>
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>
27 #include "persona_test.h"
31 #include <spawn_private.h>
32 #include <sys/persona.h>
33 #include <sys/proc_info.h>
34 #include <sys/spawn_internal.h>
36 #define PERSONA_TEST_NAME "Persona Spawn"
37 #define PERSONA_TEST_VMAJOR 0
38 #define PERSONA_TEST_VMINOR 1
40 static struct test_config
{
42 int wait_for_children
;
46 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
50 * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
53 TAILQ_ENTRY(child
) sibling
;
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;
62 spawn_child(int argc
, char **argv
, struct persona_args
*pa
)
65 uint32_t persona_flags
= 0;
66 posix_spawnattr_t attr
;
67 struct child
*child
= NULL
;
68 extern char **environ
;
73 err_print("Invalid persona args!");
77 if (!(pa
->flags
& PA_HAS_ID
)) {
78 err_print("No persona ID specified!");
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
]);
90 child
= (struct child
*)calloc(1, sizeof(*child
));
92 err_print("No memory left :-(");
96 ret
= posix_spawnattr_init(&attr
);
98 err_print("posix_spawnattr_init");
99 ret
= -ERR_SPAWN_ATTR
;
103 if (pa
->flags
& PA_SHOULD_VERIFY
) {
104 persona_flags
|= POSIX_SPAWN_PERSONA_FLAGS_VERIFY
;
107 if (pa
->flags
& PA_OVERRIDE
) {
108 persona_flags
|= POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE
;
111 ret
= posix_spawnattr_set_persona_np(&attr
, pa
->kinfo
.persona_id
, persona_flags
);
113 err_print("posix_spawnattr_set_persona_np failed!");
114 ret
= -ERR_SPAWN_ATTR
;
118 if (pa
->flags
& PA_HAS_UID
) {
119 ret
= posix_spawnattr_set_persona_uid_np(&attr
, pa
->override_uid
);
121 err_print("posix_spawnattr_set_persona_uid_np failed!");
122 ret
= -ERR_SPAWN_ATTR
;
127 if (pa
->flags
& PA_HAS_GID
) {
128 ret
= posix_spawnattr_set_persona_gid_np(&attr
, pa
->kinfo
.persona_gid
);
130 err_print("posix_spawnattr_set_persona_gid_np failed!");
131 ret
= -ERR_SPAWN_ATTR
;
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
);
140 ret
= -ERR_SPAWN_ATTR
;
145 ret
= posix_spawn(&child
->pid
, argv
[0], NULL
, &attr
, argv
, environ
);
147 err_print("posix_spawn (ret=%d)", ret
);
152 infov("\tspawned child PID: %d", child
->pid
);
154 /* link the processes onto the global children list */
155 pthread_mutex_lock(&g_child_mtx
);
156 TAILQ_INSERT_TAIL(&g_children
, child
, sibling
);
158 pthread_mutex_unlock(&g_child_mtx
);
160 posix_spawnattr_destroy(&attr
);
164 posix_spawnattr_destroy(&attr
);
170 static int child_should_exit
= 0;
173 child_sighandler(int sig
)
176 dbg("PID: %d received sig %d", getpid(), sig
);
177 child_should_exit
= 1;
181 child_main_loop(int argc
, char **argv
)
186 uid_t persona_id
= 0;
187 struct kpersona_info kinfo
;
190 while ((ch
= getopt(argc
, argv
, "vhER")) != -1) {
196 child_should_exit
= 1;
205 err("Invalid child process invocation.");
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
);
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
);
223 err
= kpersona_get(&persona_id
);
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
);
230 kinfo
.persona_info_version
= PERSONA_INFO_V1
;
231 err
= kpersona_info(persona_id
, &kinfo
);
233 dump_kpersona("Child: kpersona_info", &kinfo
);
235 info("Child: ERROR grabbing kpersona_info: %d", errno
);
238 if (child_should_exit
) {
242 infov("Child Sleeping!");
243 while (!child_should_exit
) {
247 infov("Child exiting!");
252 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
256 * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
259 main_sighandler(int sig
)
261 dbg("PID: %d received sig %d", getpid(), sig
);
262 if (sig
== SIGCHLD
) {
268 usage_main(const char *progname
, int verbose
)
270 const char *nm
= basename((char *)progname
);
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");
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");
293 main(int argc
, char **argv
)
298 pthread_mutex_init(&g_child_mtx
, NULL
);
304 g
.wait_for_children
= 1;
306 if (argc
> 1 && strcmp(argv
[1], "child") == 0) {
308 ret
= child_main_loop(argc
, argv
);
312 if (strcmp(argv
[optind
], "spawn") != 0) {
313 printf("child exiting (%s).\n", argv
[optind
]);
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.
326 if (geteuid() != 0) {
327 err("%s must be run as root", argv
[0] ? basename(argv
[0]) : PERSONA_TEST_NAME
);
330 struct persona_args pa
;
331 memset(&pa
, 0, sizeof(pa
));
334 pa
.kinfo
.persona_id
= getuid();
337 * Argument parse for default overrides:
339 while ((ch
= getopt(argc
, argv
, "Vg:G:I:u:vwh")) != -1) {
342 pa
.flags
|= PA_SHOULD_VERIFY
;
345 pa
.kinfo
.persona_gid
= atoi(optarg
);
346 pa
.flags
|= PA_HAS_GID
;
347 pa
.flags
|= PA_OVERRIDE
;
350 ret
= parse_groupspec(&pa
.kinfo
, optarg
);
352 err("Invalid groupspec: \"%s\"", optarg
);
354 pa
.flags
|= PA_HAS_GROUPS
;
355 pa
.flags
|= PA_OVERRIDE
;
358 pa
.kinfo
.persona_id
= atoi(optarg
);
359 if (pa
.kinfo
.persona_id
== 0) {
360 err("Invalid Persona ID: %s", optarg
);
362 pa
.flags
|= PA_HAS_ID
;
365 pa
.override_uid
= atoi(optarg
);
366 pa
.flags
|= PA_HAS_UID
;
367 pa
.flags
|= PA_OVERRIDE
;
373 g
.wait_for_children
= 0;
377 usage_main(argv
[0], 1);
380 printf("Invalid option: '%c'\n", ch
);
381 usage_main(argv
[0], 0);
385 if (pa
.flags
& PA_SHOULD_VERIFY
) {
386 pa
.flags
= ~PA_OVERRIDE
;
389 if (optind
>= argc
) {
390 printf("No program given!\n");
391 usage_main(argv
[0], 0);
395 for (int i
= 0; i
< argc
; i
++) {
396 argv
[i
] = argv
[i
+ optind
];
401 ret
= spawn_child(argc
, argv
, &pa
);
406 pid_t child_pid
= (pid_t
)ret
;
409 sigemptyset(&sigset
);
410 sigaddset(&sigset
, SIGCHLD
);
411 sigprocmask(SIG_UNBLOCK
, &sigset
, NULL
);
412 signal(SIGCHLD
, main_sighandler
);
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
);
421 "Child exited with status: %d", status
);