]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sys_persona.c
xnu-4570.41.2.tar.gz
[apple/xnu.git] / bsd / kern / sys_persona.c
1 /*
2 * Copyright (c) 2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 #include <sys/param.h>
29 #include <sys/kernel.h>
30 #include <sys/kernel_types.h>
31 #include <sys/sysproto.h>
32
33 #include <sys/kauth.h>
34 #include <sys/malloc.h>
35 #include <sys/persona.h>
36 #include <sys/proc.h>
37
38 #include <libkern/libkern.h>
39
40 static int kpersona_copyin(user_addr_t infop, struct kpersona_info *kinfo)
41 {
42 uint32_t info_v = 0;
43 int error;
44
45 error = copyin(infop, &info_v, sizeof(info_v));
46 if (error)
47 return error;
48
49 /* only support a single version of the struct for now */
50 if (info_v != PERSONA_INFO_V1)
51 return EINVAL;
52
53 error = copyin(infop, kinfo, sizeof(*kinfo));
54
55 /* enforce NULL termination on strings */
56 kinfo->persona_name[MAXLOGNAME] = 0;
57
58 return error;
59 }
60
61 static int kpersona_copyout(struct kpersona_info *kinfo, user_addr_t infop)
62 {
63 uint32_t info_v;
64 int error;
65
66 error = copyin(infop, &info_v, sizeof(info_v));
67 if (error)
68 return error;
69
70 /* only support a single version of the struct for now */
71 /* TODO: in the future compare info_v to kinfo->persona_info_version */
72 if (info_v != PERSONA_INFO_V1)
73 return EINVAL;
74
75 error = copyout(kinfo, infop, sizeof(*kinfo));
76 return error;
77 }
78
79
80 static int kpersona_alloc_syscall(user_addr_t infop, user_addr_t idp)
81 {
82 int error;
83 struct kpersona_info kinfo;
84 struct persona *persona;
85 uid_t id = PERSONA_ID_NONE;
86 const char *login;
87
88 /*
89 * TODO: rdar://problem/19981151
90 * Add entitlement check!
91 */
92 if (!kauth_cred_issuser(kauth_cred_get()))
93 return EPERM;
94
95 error = kpersona_copyin(infop, &kinfo);
96 if (error)
97 return error;
98
99 login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
100 if (kinfo.persona_id != PERSONA_ID_NONE && kinfo.persona_id != (uid_t)0)
101 id = kinfo.persona_id;
102
103 error = 0;
104 persona = persona_alloc(id, login, kinfo.persona_type, &error);
105 if (!persona)
106 return error;
107
108 if (kinfo.persona_gid) {
109 error = persona_set_gid(persona, kinfo.persona_gid);
110 if (error)
111 goto out_error;
112 }
113
114 if (kinfo.persona_ngroups > 0) {
115 /* force gmuid 0 to *opt-out* of memberd */
116 if (kinfo.persona_gmuid == 0)
117 kinfo.persona_gmuid = KAUTH_UID_NONE;
118
119 error = persona_set_groups(persona, kinfo.persona_groups,
120 kinfo.persona_ngroups,
121 kinfo.persona_gmuid);
122 if (error)
123 goto out_error;
124 }
125
126 error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
127 if (error)
128 goto out_error;
129 error = kpersona_copyout(&kinfo, infop);
130
131 /*
132 * On success, we have a persona structure in the global list with a
133 * single reference count on it. The corresponding _dealloc() call
134 * will release this reference.
135 */
136 return error;
137
138 out_error:
139 printf("%s: ERROR:%d\n", __func__, error);
140 if (persona)
141 persona_put(persona);
142 return error;
143 }
144
145 static int kpersona_dealloc_syscall(user_addr_t idp)
146 {
147 int error;
148 uid_t persona_id;
149 struct persona *persona;
150
151 if (!kauth_cred_issuser(kauth_cred_get()))
152 return EPERM;
153
154 error = copyin(idp, &persona_id, sizeof(persona_id));
155 if (error)
156 return error;
157
158 persona = persona_lookup(persona_id);
159 if (!persona)
160 return ESRCH;
161
162 /* invalidate the persona (deny subsequent spawn/fork) */
163 error = persona_invalidate(persona);
164
165 /* one reference from the _lookup() */
166 persona_put(persona);
167
168 /* one reference from the _alloc() */
169 if (!error)
170 persona_put(persona);
171
172 return error;
173 }
174
175 static int kpersona_get_syscall(user_addr_t idp)
176 {
177 int error;
178 struct persona *persona = current_persona_get();
179
180 if (!persona)
181 return ESRCH;
182
183 error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
184 persona_put(persona);
185
186 return error;
187 }
188
189 static int kpersona_info_syscall(user_addr_t idp, user_addr_t infop)
190 {
191 int error;
192 uid_t persona_id;
193 struct persona *persona;
194 struct kpersona_info kinfo;
195
196 error = copyin(idp, &persona_id, sizeof(persona_id));
197 if (error)
198 return error;
199
200 /*
201 * TODO: rdar://problem/19981151
202 * Add entitlement check!
203 */
204
205 persona = persona_lookup(persona_id);
206 if (!persona)
207 return ESRCH;
208
209 persona_dbg("FOUND: persona:%p, id:%d, gid:%d, login:\"%s\"",
210 persona, persona->pna_id, persona_get_gid(persona),
211 persona->pna_login);
212
213 memset(&kinfo, 0, sizeof(kinfo));
214 kinfo.persona_info_version = PERSONA_INFO_V1;
215 kinfo.persona_id = persona->pna_id;
216 kinfo.persona_type = persona->pna_type;
217 kinfo.persona_gid = persona_get_gid(persona);
218 unsigned ngroups = 0;
219 persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
220 kinfo.persona_ngroups = ngroups;
221 kinfo.persona_gmuid = persona_get_gmuid(persona);
222
223 /*
224 * NULL termination is assured b/c persona_name is
225 * exactly MAXLOGNAME + 1 bytes (and has been memset to 0)
226 */
227 strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
228
229 persona_put(persona);
230
231 error = kpersona_copyout(&kinfo, infop);
232
233 return error;
234 }
235
236 static int kpersona_pidinfo_syscall(user_addr_t idp, user_addr_t infop)
237 {
238 int error;
239 pid_t pid;
240 struct persona *persona;
241 struct kpersona_info kinfo;
242
243 error = copyin(idp, &pid, sizeof(pid));
244 if (error)
245 return error;
246
247 if (!kauth_cred_issuser(kauth_cred_get())
248 && (pid != current_proc()->p_pid))
249 return EPERM;
250
251 persona = persona_proc_get(pid);
252 if (!persona)
253 return ESRCH;
254
255 memset(&kinfo, 0, sizeof(kinfo));
256 kinfo.persona_info_version = PERSONA_INFO_V1;
257 kinfo.persona_id = persona->pna_id;
258 kinfo.persona_type = persona->pna_type;
259 kinfo.persona_gid = persona_get_gid(persona);
260 unsigned ngroups = 0;
261 persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
262 kinfo.persona_ngroups = ngroups;
263 kinfo.persona_gmuid = persona_get_gmuid(persona);
264
265 strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
266
267 persona_put(persona);
268
269 error = kpersona_copyout(&kinfo, infop);
270
271 return error;
272 }
273
274 static int kpersona_find_syscall(user_addr_t infop, user_addr_t idp, user_addr_t idlenp)
275 {
276 int error;
277 struct kpersona_info kinfo;
278 const char *login;
279 size_t u_idlen, k_idlen = 0;
280 struct persona **persona = NULL;
281
282 error = copyin(idlenp, &u_idlen, sizeof(u_idlen));
283 if (error)
284 return error;
285
286 if (u_idlen > g_max_personas)
287 u_idlen = g_max_personas;
288
289 error = kpersona_copyin(infop, &kinfo);
290 if (error)
291 goto out;
292
293 login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
294
295 if (u_idlen > 0) {
296 MALLOC(persona, struct persona **, sizeof(*persona) * u_idlen,
297 M_TEMP, M_WAITOK|M_ZERO);
298 if (!persona) {
299 error = ENOMEM;
300 goto out;
301 }
302 }
303
304 k_idlen = u_idlen;
305 error = persona_find(login, kinfo.persona_id, persona, &k_idlen);
306 if (error)
307 goto out;
308
309 /* copyout all the IDs of each persona we found */
310 for (size_t i = 0; i < k_idlen; i++) {
311 if (i >= u_idlen)
312 break;
313 error = copyout(&persona[i]->pna_id,
314 idp + (i * sizeof(persona[i]->pna_id)),
315 sizeof(persona[i]->pna_id));
316 if (error)
317 goto out;
318 }
319
320 out:
321 if (persona) {
322 for (size_t i = 0; i < u_idlen; i++)
323 persona_put(persona[i]);
324 FREE(persona, M_TEMP);
325 }
326
327 (void)copyout(&k_idlen, idlenp, sizeof(u_idlen));
328
329 return error;
330 }
331
332
333 /*
334 * Syscall entry point / demux.
335 */
336 int persona(__unused proc_t p, struct persona_args *pargs, __unused int32_t *retval)
337 {
338 int error;
339 uint32_t op = pargs->operation;
340 /* uint32_t flags = pargs->flags; */
341 user_addr_t infop = pargs->info;
342 user_addr_t idp = pargs->id;
343
344 switch (op) {
345 case PERSONA_OP_ALLOC:
346 error = kpersona_alloc_syscall(infop, idp);
347 break;
348 case PERSONA_OP_DEALLOC:
349 error = kpersona_dealloc_syscall(idp);
350 break;
351 case PERSONA_OP_GET:
352 error = kpersona_get_syscall(idp);
353 break;
354 case PERSONA_OP_INFO:
355 error = kpersona_info_syscall(idp, infop);
356 break;
357 case PERSONA_OP_PIDINFO:
358 error = kpersona_pidinfo_syscall(idp, infop);
359 break;
360 case PERSONA_OP_FIND:
361 error = kpersona_find_syscall(infop, idp, pargs->idlen);
362 break;
363 default:
364 error = ENOSYS;
365 break;
366 }
367
368 return error;
369 }