]>
Commit | Line | Data |
---|---|---|
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 | ||
40 | enum { | |
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 | ||
48 | static struct mgr_config { | |
49 | int verbose; | |
50 | } g; | |
51 | ||
52 | ||
0a7de745 A |
53 | static int |
54 | persona_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 |
72 | static int |
73 | persona_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 |
87 | static int |
88 | persona_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 |
130 | static int |
131 | persona_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 |
151 | static void |
152 | usage_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 |
183 | int |
184 | main(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 | } |