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