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/queue.h>
23 #include <sys/sysctl.h>
24 #include <sys/types.h>
26 #include "persona_test.h"
30 #include <spawn_private.h>
31 #include <sys/persona.h>
32 #include <sys/proc_info.h>
33 #include <sys/spawn_internal.h>
35 #define PERSONA_TEST_NAME "Persona Spawn"
36 #define PERSONA_TEST_VMAJOR 0
37 #define PERSONA_TEST_VMINOR 1
39 static struct test_config
{
41 int wait_for_children
;
45 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
49 * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
52 TAILQ_ENTRY(child
) sibling
;
56 static pthread_mutex_t g_child_mtx
;
57 static TAILQ_HEAD(, child
) g_children
= TAILQ_HEAD_INITIALIZER(g_children
);
58 static int g_nchildren
= 0;
60 static pid_t
spawn_child(int argc
, char **argv
, struct persona_args
*pa
)
63 uint32_t persona_flags
= 0;
64 posix_spawnattr_t attr
;
65 struct child
*child
= NULL
;
66 extern char **environ
;
71 err_print("Invalid persona args!");
75 if (!pa
->flags
& PA_HAS_ID
) {
76 err_print("No persona ID specified!");
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
]);
88 child
= (struct child
*)calloc(1, sizeof(*child
));
90 err_print("No memory left :-(");
94 ret
= posix_spawnattr_init(&attr
);
96 err_print("posix_spawnattr_init");
97 ret
= -ERR_SPAWN_ATTR
;
101 if (pa
->flags
& PA_SHOULD_VERIFY
)
102 persona_flags
|= POSIX_SPAWN_PERSONA_FLAGS_VERIFY
;
104 if (pa
->flags
& PA_OVERRIDE
)
105 persona_flags
|= POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE
;
107 ret
= posix_spawnattr_set_persona_np(&attr
, pa
->kinfo
.persona_id
, persona_flags
);
109 err_print("posix_spawnattr_set_persona_np failed!");
110 ret
= -ERR_SPAWN_ATTR
;
114 if (pa
->flags
& PA_HAS_UID
) {
115 ret
= posix_spawnattr_set_persona_uid_np(&attr
, pa
->override_uid
);
117 err_print("posix_spawnattr_set_persona_uid_np failed!");
118 ret
= -ERR_SPAWN_ATTR
;
123 if (pa
->flags
& PA_HAS_GID
) {
124 ret
= posix_spawnattr_set_persona_gid_np(&attr
, pa
->kinfo
.persona_gid
);
126 err_print("posix_spawnattr_set_persona_gid_np failed!");
127 ret
= -ERR_SPAWN_ATTR
;
132 ret
= posix_spawn(&child
->pid
, argv
[0], NULL
, &attr
, argv
, environ
);
134 err_print("posix_spawn (ret=%d)", ret
);
139 infov("\tspawned child PID: %d", child
->pid
);
141 /* link the processes onto the global children list */
142 pthread_mutex_lock(&g_child_mtx
);
143 TAILQ_INSERT_TAIL(&g_children
, child
, sibling
);
145 pthread_mutex_unlock(&g_child_mtx
);
147 posix_spawnattr_destroy(&attr
);
151 posix_spawnattr_destroy(&attr
);
157 static int child_should_exit
= 0;
159 static void child_sighandler(int sig
)
162 dbg("PID: %d received sig %d", getpid(), sig
);
163 child_should_exit
= 1;
166 static int child_main_loop(int argc
, char **argv
)
171 uid_t persona_id
= 0;
172 struct kpersona_info kinfo
;
175 while ((ch
= getopt(argc
, argv
, "vhER")) != -1) {
181 child_should_exit
= 1;
190 err("Invalid child process invocation.");
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
);
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
);
208 err
= kpersona_get(&persona_id
);
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
);
215 kinfo
.persona_info_version
= PERSONA_INFO_V1
;
216 err
= kpersona_info(persona_id
, &kinfo
);
218 dump_kpersona("Child: kpersona_info", &kinfo
);
220 info("Child: ERROR grabbing kpersona_info: %d", errno
);
222 if (child_should_exit
)
225 infov("Child Sleeping!");
226 while (!child_should_exit
)
229 infov("Child exiting!");
234 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
238 * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
240 static void main_sighandler(int sig
)
242 dbg("PID: %d received sig %d", getpid(), sig
);
243 if (sig
== SIGCHLD
) {
248 static void usage_main(const char *progname
, int verbose
)
250 const char *nm
= basename((char *)progname
);
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");
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");
269 int main(int argc
, char **argv
)
274 pthread_mutex_init(&g_child_mtx
, NULL
);
280 g
.wait_for_children
= 1;
282 if (argc
> 1 && strcmp(argv
[1], "child") == 0) {
284 ret
= child_main_loop(argc
, argv
);
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.
297 err("%s must be run as root", argv
[0] ? basename(argv
[0]) : PERSONA_TEST_NAME
);
299 struct persona_args pa
;
300 memset(&pa
, 0, sizeof(pa
));
303 pa
.kinfo
.persona_id
= getuid();
306 * Argument parse for default overrides:
308 while ((ch
= getopt(argc
, argv
, "Vg:I:u:vwh")) != -1) {
311 pa
.flags
|= PA_SHOULD_VERIFY
;
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
;
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
;
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
;
337 g
.wait_for_children
= 0;
341 usage_main(argv
[0], 1);
344 printf("Invalid option: '%c'\n", ch
);
345 usage_main(argv
[0], 0);
349 if (pa
.flags
& PA_SHOULD_VERIFY
)
350 pa
.flags
= ~PA_OVERRIDE
;
352 if (optind
>= argc
) {
353 printf("No program given!\n");
354 usage_main(argv
[0], 0);
358 for (int i
= 0; i
< argc
; i
++) {
359 argv
[i
] = argv
[i
+ optind
];
364 ret
= spawn_child(argc
, argv
, &pa
);
368 pid_t child_pid
= (pid_t
)ret
;
371 sigemptyset(&sigset
);
372 sigaddset(&sigset
, SIGCHLD
);
373 sigprocmask(SIG_UNBLOCK
, &sigset
, NULL
);
374 signal(SIGCHLD
, main_sighandler
);
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
);
383 "Child exited with status: %d", status
);