xref: /xnu-8019.80.24/tools/tests/personas/persona_mgr.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1 /*
2  * persona_mgr.c
3  * Tool to manage personas
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 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
persona_op_create(struct kpersona_info * ki)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
persona_op_destroy(struct kpersona_info * ki)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
persona_op_lookup(struct kpersona_info * ki,pid_t pid,uid_t uid)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
persona_op_support(void)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
usage_main(const char * progname,const char * msg,int verbose)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
main(int argc,char ** argv)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 }
349