]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/personas/persona_mgr.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / tools / tests / personas / persona_mgr.c
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
40 enum {
41 PERSONA_OP_CREATE = 1,
42 PERSONA_OP_DESTROY = 2,
43 PERSONA_OP_LOOKUP = 3,
44 PERSONA_OP_SUPPORT = 4,
45 PERSONA_OP_MAX = 4,
46 };
47
48 static struct mgr_config {
49 int verbose;
50 } g;
51
52
53 static int
54 persona_op_create(struct kpersona_info *ki)
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
72 static int
73 persona_op_destroy(struct kpersona_info *ki)
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);
80 if (ret < 0) {
81 err_print("destroy failed!");
82 }
83
84 return ret;
85 }
86
87 static int
88 persona_op_lookup(struct kpersona_info *ki, pid_t pid, uid_t uid)
89 {
90 int ret;
91
92 info("Looking up persona (login:%s, pid:%d, uid:%d)", ki->persona_name, pid, uid);
93 if (pid > 0) {
94 ki->persona_info_version = PERSONA_INFO_V1;
95 ret = kpersona_pidinfo(pid, ki);
96 if (ret < 0) {
97 err_print("pidinfo failed!");
98 } else {
99 dump_kpersona("Persona-for-pid:", ki);
100 }
101 } else {
102 int np = 0;
103 uid_t personas[128];
104 size_t npersonas = ARRAY_SZ(personas);
105 const char *name = NULL;
106 if (ki->persona_name[0] != 0) {
107 name = ki->persona_name;
108 }
109
110 np = kpersona_find(name, uid, personas, &npersonas);
111 if (np < 0) {
112 err("kpersona_find returned %d (errno:%d)", np, errno);
113 }
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);
120 if (ret < 0) {
121 err("kpersona_info failed (errno:%d) for persona[%d]", errno, personas[np]);
122 }
123 dump_kpersona(NULL, ki);
124 }
125 }
126
127 return ret;
128 }
129
130 static int
131 persona_op_support(void)
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
144
145 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
146 *
147 * Main Entry Point
148 *
149 * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
150 */
151 static void
152 usage_main(const char *progname, const char *msg, int verbose)
153 {
154 const char *nm = basename((char *)progname);
155
156 if (msg) {
157 printf("%s\n\n", msg);
158 }
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);
162 if (!verbose) {
163 exit(1);
164 }
165
166 printf("\t%-15s\tOne of: create | destroy | lookup | support\n", "[op]");
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
183 int
184 main(int argc, char **argv)
185 {
186 char ch;
187 int ret;
188
189 const char *op_str = NULL;
190 int persona_op = -1;
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
200 if (geteuid() != 0) {
201 err("%s must be run as root", argv[0] ? basename(argv[0]) : PROG_NAME);
202 }
203
204 if (argc < 2) {
205 usage_main(argv[0], "Not enough arguments", 0);
206 }
207
208 op_str = argv[1];
209
210 if (strcmp(op_str, "create") == 0) {
211 persona_op = PERSONA_OP_CREATE;
212 } else if (strcmp(op_str, "destroy") == 0) {
213 persona_op = PERSONA_OP_DESTROY;
214 } else if (strcmp(op_str, "lookup") == 0) {
215 persona_op = PERSONA_OP_LOOKUP;
216 } else if (strcmp(op_str, "support") == 0) {
217 persona_op = PERSONA_OP_SUPPORT;
218 } else if (strcmp(op_str, "help") == 0 || strcmp(op_str, "-h") == 0) {
219 usage_main(argv[0], NULL, 1);
220 }
221
222 if (persona_op <= 0 || persona_op > PERSONA_OP_MAX) {
223 usage_main(argv[0], "Invalid [op]", 0);
224 }
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);
237 if (ret <= 0) {
238 ret = PERSONA_ID_NONE;
239 }
240 kinfo.persona_id = (uid_t)ret;
241 break;
242 case 't':
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 }
258 break;
259 case 'p':
260 ret = atoi(optarg);
261 if (ret <= 0) {
262 err("Invalid PID: %s", optarg);
263 }
264 pid = (pid_t)ret;
265 break;
266 case 'u':
267 ret = atoi(optarg);
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 }
272 uid = (uid_t)ret;
273 break;
274 case 'g':
275 kinfo.persona_gid = (gid_t)atoi(optarg);
276 if (kinfo.persona_gid <= 500) {
277 err("Invalid GID: %d", kinfo.persona_gid);
278 }
279 break;
280 case 'l':
281 strncpy(kinfo.persona_name, optarg, MAXLOGNAME);
282 break;
283 case 'G':
284 ret = parse_groupspec(&kinfo, optarg);
285 if (ret < 0) {
286 err("Invalid groupspec: \"%s\"", optarg);
287 }
288 break;
289 case 'm':
290 ret = atoi(optarg);
291 if (ret < 0) {
292 err("Invalid group membership ID: %s", optarg);
293 }
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
309 if (uid == (uid_t)-1 && persona_op != PERSONA_OP_LOOKUP) {
310 uid = kinfo.persona_id;
311 }
312
313 if (kinfo.persona_gmuid != KAUTH_UID_NONE && kinfo.persona_ngroups == 0) {
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;
319 if (kinfo.persona_gid) {
320 kinfo.persona_groups[0] = kinfo.persona_gid;
321 } else {
322 kinfo.persona_groups[0] = kinfo.persona_id;
323 }
324 }
325
326 if (g.verbose) {
327 dump_kpersona("Input persona:", &kinfo);
328 }
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;
340 case PERSONA_OP_SUPPORT:
341 ret = persona_op_support();
342 break;
343 default:
344 err("Invalid persona op: %d", persona_op);
345 }
346
347 return ret;
348 }