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