]> git.saurik.com Git - apple/xnu.git/blame - tools/tests/personas/persona_mgr.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / tools / tests / personas / persona_mgr.c
CommitLineData
490019cf
A
1/*
2 * persona_mgr.c
3 * Tool to manage personas
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/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 PROG_NAME "Persona Manager"
37#define PROG_VMAJOR 0
38#define PROG_VMINOR 1
39
40enum {
41 PERSONA_OP_CREATE = 1,
42 PERSONA_OP_DESTROY = 2,
43 PERSONA_OP_LOOKUP = 3,
d9a64523
A
44 PERSONA_OP_SUPPORT = 4,
45 PERSONA_OP_MAX = 4,
490019cf
A
46};
47
48static struct mgr_config {
49 int verbose;
50} g;
51
52
0a7de745
A
53static int
54persona_op_create(struct kpersona_info *ki)
490019cf
A
55{
56 int ret;
57 uid_t persona_id = 0;
58
59 info("Creating persona...");
60 ki->persona_info_version = PERSONA_INFO_V1;
61 ret = kpersona_alloc(ki, &persona_id);
62 if (ret == 0) {
63 info("Created persona %d:", persona_id);
64 dump_kpersona(NULL, ki);
65 } else {
66 err("kpersona_alloc return %d (errno:%d)", ret, errno);
67 }
68
69 return ret;
70}
71
0a7de745
A
72static int
73persona_op_destroy(struct kpersona_info *ki)
490019cf
A
74{
75 int ret;
76
77 info("Destroying Persona %d...", ki->persona_id);
78 ki->persona_info_version = PERSONA_INFO_V1;
79 ret = kpersona_dealloc(ki->persona_id);
0a7de745 80 if (ret < 0) {
490019cf 81 err_print("destroy failed!");
0a7de745 82 }
490019cf
A
83
84 return ret;
85}
86
0a7de745
A
87static int
88persona_op_lookup(struct kpersona_info *ki, pid_t pid, uid_t uid)
490019cf
A
89{
90 int ret;
91
d9a64523 92 info("Looking up persona (login:%s, pid:%d, uid:%d)", ki->persona_name, pid, uid);
490019cf
A
93 if (pid > 0) {
94 ki->persona_info_version = PERSONA_INFO_V1;
95 ret = kpersona_pidinfo(pid, ki);
0a7de745 96 if (ret < 0) {
490019cf 97 err_print("pidinfo failed!");
0a7de745 98 } else {
490019cf 99 dump_kpersona("Persona-for-pid:", ki);
0a7de745 100 }
490019cf
A
101 } else {
102 int np = 0;
103 uid_t personas[128];
104 size_t npersonas = ARRAY_SZ(personas);
105 const char *name = NULL;
0a7de745 106 if (ki->persona_name[0] != 0) {
490019cf 107 name = ki->persona_name;
0a7de745 108 }
490019cf
A
109
110 np = kpersona_find(name, uid, personas, &npersonas);
0a7de745 111 if (np < 0) {
490019cf 112 err("kpersona_find returned %d (errno:%d)", np, errno);
0a7de745 113 }
490019cf
A
114 info("Found %zu persona%c", npersonas, npersonas != 1 ? 's' : ' ');
115 np = npersonas;
116 while (np--) {
117 info("\tpersona[%d]=%d...", np, personas[np]);
118 ki->persona_info_version = PERSONA_INFO_V1;
119 ret = kpersona_info(personas[np], ki);
0a7de745 120 if (ret < 0) {
490019cf 121 err("kpersona_info failed (errno:%d) for persona[%d]", errno, personas[np]);
0a7de745 122 }
490019cf
A
123 dump_kpersona(NULL, ki);
124 }
125 }
126
127 return ret;
128}
129
0a7de745
A
130static int
131persona_op_support(void)
d9a64523
A
132{
133 uid_t pna_id = -1;
134 int ret = kpersona_get(&pna_id);
135 if (ret == 0 || errno != ENOSYS) {
136 info("Persona subsystem is supported (id=%d)", pna_id);
137 return 0;
138 }
139
140 info("Persona subsystem is not supported");
141 return ENOSYS;
142}
143
490019cf
A
144
145/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
146 *
147 * Main Entry Point
148 *
149 * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
150 */
0a7de745
A
151static void
152usage_main(const char *progname, const char *msg, int verbose)
490019cf
A
153{
154 const char *nm = basename((char *)progname);
155
0a7de745 156 if (msg) {
490019cf 157 printf("%s\n\n", msg);
0a7de745 158 }
490019cf
A
159
160 printf("%s v%d.%d\n", PROG_NAME, PROG_VMAJOR, PROG_VMINOR);
161 printf("usage: %s [op] [-v] [-i id] [-t type] [-p pid] [-u uid] [-g gid] [-l login] [-G {groupspec}] [-m gmuid]\n", nm);
0a7de745 162 if (!verbose) {
490019cf 163 exit(1);
0a7de745 164 }
490019cf 165
d9a64523 166 printf("\t%-15s\tOne of: create | destroy | lookup | support\n", "[op]");
490019cf
A
167 printf("\t%-15s\tBe verbose\n", "-v");
168
169 printf("\t%-15s\tID of the persona\n", "-i id");
170 printf("\t%-15s\tType of the persona\n", "-t type");
171 printf("\t%-15s\tPID of the process whose persona info to lookup\n", "-p pid");
172 printf("\t%-15s\tUID to use in lookup\n", "-u uid");
173 printf("\t%-15s\tGID of the persona\n", "-g gid");
174 printf("\t%-15s\tLogin name of the persona\n", "-l login");
175 printf("\t%-15s\tGroups to which the persona will belong\n", "-G {groupspec}");
176 printf("\t%-15s\tgroupspec: G1{,G2,G3...}\n", " ");
177 printf("\t%-15s\tUID used for memberd lookup (opt-in to memberd)\n", "-m gmuid");
178
179 printf("\n");
180 exit(1);
181}
182
0a7de745
A
183int
184main(int argc, char **argv)
490019cf
A
185{
186 char ch;
187 int ret;
188
189 const char *op_str = NULL;
d9a64523 190 int persona_op = -1;
490019cf
A
191 struct kpersona_info kinfo;
192 uid_t uid = (uid_t)-1;
193 pid_t pid = (pid_t)-1;
194
195 /*
196 * Defaults
197 */
198 g.verbose = 0;
199
0a7de745 200 if (geteuid() != 0) {
490019cf 201 err("%s must be run as root", argv[0] ? basename(argv[0]) : PROG_NAME);
0a7de745 202 }
490019cf 203
0a7de745 204 if (argc < 2) {
490019cf 205 usage_main(argv[0], "Not enough arguments", 0);
0a7de745 206 }
490019cf
A
207
208 op_str = argv[1];
209
0a7de745 210 if (strcmp(op_str, "create") == 0) {
490019cf 211 persona_op = PERSONA_OP_CREATE;
0a7de745 212 } else if (strcmp(op_str, "destroy") == 0) {
490019cf 213 persona_op = PERSONA_OP_DESTROY;
0a7de745 214 } else if (strcmp(op_str, "lookup") == 0) {
490019cf 215 persona_op = PERSONA_OP_LOOKUP;
0a7de745 216 } else if (strcmp(op_str, "support") == 0) {
d9a64523 217 persona_op = PERSONA_OP_SUPPORT;
0a7de745 218 } else if (strcmp(op_str, "help") == 0 || strcmp(op_str, "-h") == 0) {
490019cf 219 usage_main(argv[0], NULL, 1);
0a7de745 220 }
490019cf 221
0a7de745 222 if (persona_op <= 0 || persona_op > PERSONA_OP_MAX) {
490019cf 223 usage_main(argv[0], "Invalid [op]", 0);
0a7de745 224 }
490019cf
A
225
226 memset(&kinfo, 0, sizeof(kinfo));
227 kinfo.persona_gmuid = KAUTH_UID_NONE;
228
229 /*
230 * Argument parse
231 */
232 optind = 2;
233 while ((ch = getopt(argc, argv, "vi:t:p:u:g:l:G:m:h")) != -1) {
234 switch (ch) {
235 case 'i':
236 ret = atoi(optarg);
d9a64523
A
237 if (ret <= 0) {
238 ret = PERSONA_ID_NONE;
239 }
490019cf
A
240 kinfo.persona_id = (uid_t)ret;
241 break;
242 case 't':
d9a64523
A
243 if (strncmp(optarg, "guest", 6) == 0) {
244 kinfo.persona_type = PERSONA_GUEST;
245 } else if (strncmp(optarg, "managed", 8) == 0) {
246 kinfo.persona_type = PERSONA_MANAGED;
247 } else if (strncmp(optarg, "priv", 4) == 0) { /* shortcut... */
248 kinfo.persona_type = PERSONA_PRIV;
249 } else if (strncmp(optarg, "system", 7) == 0) {
250 kinfo.persona_type = PERSONA_SYSTEM;
251 } else {
252 ret = atoi(optarg);
253 if (ret <= PERSONA_INVALID || ret > PERSONA_TYPE_MAX) {
254 err("Invalid type specification: %s", optarg);
255 }
256 kinfo.persona_type = ret;
257 }
490019cf
A
258 break;
259 case 'p':
260 ret = atoi(optarg);
0a7de745 261 if (ret <= 0) {
490019cf 262 err("Invalid PID: %s", optarg);
0a7de745 263 }
490019cf
A
264 pid = (pid_t)ret;
265 break;
266 case 'u':
267 ret = atoi(optarg);
527f9951
A
268 /* allow invalid / -1 as a wildcard for lookup */
269 if (ret < 0 && persona_op != PERSONA_OP_LOOKUP) {
270 err("Invalid UID:%s (%d)", optarg, ret);
271 }
490019cf
A
272 uid = (uid_t)ret;
273 break;
274 case 'g':
275 kinfo.persona_gid = (gid_t)atoi(optarg);
0a7de745 276 if (kinfo.persona_gid <= 500) {
490019cf 277 err("Invalid GID: %d", kinfo.persona_gid);
0a7de745 278 }
490019cf
A
279 break;
280 case 'l':
281 strncpy(kinfo.persona_name, optarg, MAXLOGNAME);
282 break;
283 case 'G':
284 ret = parse_groupspec(&kinfo, optarg);
0a7de745 285 if (ret < 0) {
490019cf 286 err("Invalid groupspec: \"%s\"", optarg);
0a7de745 287 }
490019cf
A
288 break;
289 case 'm':
290 ret = atoi(optarg);
0a7de745 291 if (ret < 0) {
490019cf 292 err("Invalid group membership ID: %s", optarg);
0a7de745 293 }
490019cf
A
294 kinfo.persona_gmuid = (uid_t)ret;
295 break;
296 case 'v':
297 g.verbose = 1;
298 break;
299 case 'h':
300 case '?':
301 usage_main(argv[0], NULL, 1);
302 case ':':
303 default:
304 printf("Invalid option: '%c'\n", ch);
305 usage_main(argv[0], NULL, 0);
306 }
307 }
308
d9a64523 309 if (uid == (uid_t)-1 && persona_op != PERSONA_OP_LOOKUP) {
490019cf 310 uid = kinfo.persona_id;
d9a64523 311 }
490019cf 312
d9a64523 313 if (kinfo.persona_gmuid != KAUTH_UID_NONE && kinfo.persona_ngroups == 0) {
490019cf
A
314 /*
315 * In order to set the group membership UID, we need to set at
316 * least one group: make it equal to either the GID or UID
317 */
318 kinfo.persona_ngroups = 1;
0a7de745 319 if (kinfo.persona_gid) {
490019cf 320 kinfo.persona_groups[0] = kinfo.persona_gid;
0a7de745 321 } else {
490019cf 322 kinfo.persona_groups[0] = kinfo.persona_id;
0a7de745 323 }
490019cf
A
324 }
325
0a7de745 326 if (g.verbose) {
490019cf 327 dump_kpersona("Input persona:", &kinfo);
0a7de745 328 }
490019cf
A
329
330 switch (persona_op) {
331 case PERSONA_OP_CREATE:
332 ret = persona_op_create(&kinfo);
333 break;
334 case PERSONA_OP_DESTROY:
335 ret = persona_op_destroy(&kinfo);
336 break;
337 case PERSONA_OP_LOOKUP:
338 ret = persona_op_lookup(&kinfo, pid, uid);
339 break;
d9a64523
A
340 case PERSONA_OP_SUPPORT:
341 ret = persona_op_support();
342 break;
490019cf
A
343 default:
344 err("Invalid persona op: %d", persona_op);
345 }
346
347 return ret;
348}