]>
Commit | Line | Data |
---|---|---|
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 | } |