]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/sys_persona.c
xnu-4570.71.2.tar.gz
[apple/xnu.git] / bsd / kern / sys_persona.c
CommitLineData
490019cf
A
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
40static 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
61static 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
80static 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
138out_error:
139 printf("%s: ERROR:%d\n", __func__, error);
140 if (persona)
141 persona_put(persona);
142 return error;
143}
144
145static int kpersona_dealloc_syscall(user_addr_t idp)
146{
a39ff7e2 147 int error = 0;
490019cf
A
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
a39ff7e2
A
158 /* invalidate the persona (deny subsequent spawn/fork) */
159 persona = persona_lookup_and_invalidate(persona_id);
160
490019cf
A
161 if (!persona)
162 return ESRCH;
163
490019cf
A
164 /* one reference from the _lookup() */
165 persona_put(persona);
166
167 /* one reference from the _alloc() */
a39ff7e2 168 persona_put(persona);
490019cf
A
169
170 return error;
171}
172
173static int kpersona_get_syscall(user_addr_t idp)
174{
175 int error;
176 struct persona *persona = current_persona_get();
177
178 if (!persona)
179 return ESRCH;
180
181 error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
182 persona_put(persona);
183
184 return error;
185}
186
187static int kpersona_info_syscall(user_addr_t idp, user_addr_t infop)
188{
189 int error;
190 uid_t persona_id;
191 struct persona *persona;
192 struct kpersona_info kinfo;
193
194 error = copyin(idp, &persona_id, sizeof(persona_id));
195 if (error)
196 return error;
197
198 /*
199 * TODO: rdar://problem/19981151
200 * Add entitlement check!
201 */
202
203 persona = persona_lookup(persona_id);
204 if (!persona)
205 return ESRCH;
206
207 persona_dbg("FOUND: persona:%p, id:%d, gid:%d, login:\"%s\"",
208 persona, persona->pna_id, persona_get_gid(persona),
209 persona->pna_login);
210
211 memset(&kinfo, 0, sizeof(kinfo));
212 kinfo.persona_info_version = PERSONA_INFO_V1;
213 kinfo.persona_id = persona->pna_id;
214 kinfo.persona_type = persona->pna_type;
215 kinfo.persona_gid = persona_get_gid(persona);
527f9951 216 unsigned ngroups = 0;
490019cf
A
217 persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
218 kinfo.persona_ngroups = ngroups;
219 kinfo.persona_gmuid = persona_get_gmuid(persona);
220
221 /*
222 * NULL termination is assured b/c persona_name is
223 * exactly MAXLOGNAME + 1 bytes (and has been memset to 0)
224 */
225 strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
226
227 persona_put(persona);
228
229 error = kpersona_copyout(&kinfo, infop);
230
231 return error;
232}
233
234static int kpersona_pidinfo_syscall(user_addr_t idp, user_addr_t infop)
235{
236 int error;
237 pid_t pid;
238 struct persona *persona;
239 struct kpersona_info kinfo;
240
241 error = copyin(idp, &pid, sizeof(pid));
242 if (error)
243 return error;
244
245 if (!kauth_cred_issuser(kauth_cred_get())
246 && (pid != current_proc()->p_pid))
247 return EPERM;
248
249 persona = persona_proc_get(pid);
250 if (!persona)
251 return ESRCH;
252
253 memset(&kinfo, 0, sizeof(kinfo));
254 kinfo.persona_info_version = PERSONA_INFO_V1;
255 kinfo.persona_id = persona->pna_id;
256 kinfo.persona_type = persona->pna_type;
257 kinfo.persona_gid = persona_get_gid(persona);
527f9951 258 unsigned ngroups = 0;
490019cf
A
259 persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
260 kinfo.persona_ngroups = ngroups;
261 kinfo.persona_gmuid = persona_get_gmuid(persona);
262
263 strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
264
265 persona_put(persona);
266
267 error = kpersona_copyout(&kinfo, infop);
268
269 return error;
270}
271
272static int kpersona_find_syscall(user_addr_t infop, user_addr_t idp, user_addr_t idlenp)
273{
274 int error;
275 struct kpersona_info kinfo;
276 const char *login;
277 size_t u_idlen, k_idlen = 0;
278 struct persona **persona = NULL;
279
280 error = copyin(idlenp, &u_idlen, sizeof(u_idlen));
281 if (error)
282 return error;
283
284 if (u_idlen > g_max_personas)
285 u_idlen = g_max_personas;
286
287 error = kpersona_copyin(infop, &kinfo);
288 if (error)
289 goto out;
290
291 login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
292
293 if (u_idlen > 0) {
294 MALLOC(persona, struct persona **, sizeof(*persona) * u_idlen,
295 M_TEMP, M_WAITOK|M_ZERO);
296 if (!persona) {
297 error = ENOMEM;
298 goto out;
299 }
300 }
301
302 k_idlen = u_idlen;
303 error = persona_find(login, kinfo.persona_id, persona, &k_idlen);
304 if (error)
305 goto out;
306
307 /* copyout all the IDs of each persona we found */
308 for (size_t i = 0; i < k_idlen; i++) {
309 if (i >= u_idlen)
310 break;
311 error = copyout(&persona[i]->pna_id,
312 idp + (i * sizeof(persona[i]->pna_id)),
313 sizeof(persona[i]->pna_id));
314 if (error)
315 goto out;
316 }
317
318out:
319 if (persona) {
320 for (size_t i = 0; i < u_idlen; i++)
321 persona_put(persona[i]);
322 FREE(persona, M_TEMP);
323 }
324
325 (void)copyout(&k_idlen, idlenp, sizeof(u_idlen));
326
327 return error;
328}
329
330
331/*
332 * Syscall entry point / demux.
333 */
334int persona(__unused proc_t p, struct persona_args *pargs, __unused int32_t *retval)
335{
336 int error;
337 uint32_t op = pargs->operation;
338 /* uint32_t flags = pargs->flags; */
339 user_addr_t infop = pargs->info;
340 user_addr_t idp = pargs->id;
341
342 switch (op) {
343 case PERSONA_OP_ALLOC:
344 error = kpersona_alloc_syscall(infop, idp);
345 break;
346 case PERSONA_OP_DEALLOC:
347 error = kpersona_dealloc_syscall(idp);
348 break;
349 case PERSONA_OP_GET:
350 error = kpersona_get_syscall(idp);
351 break;
352 case PERSONA_OP_INFO:
353 error = kpersona_info_syscall(idp, infop);
354 break;
355 case PERSONA_OP_PIDINFO:
356 error = kpersona_pidinfo_syscall(idp, infop);
357 break;
358 case PERSONA_OP_FIND:
359 error = kpersona_find_syscall(infop, idp, pargs->idlen);
360 break;
361 default:
362 error = ENOSYS;
363 break;
364 }
365
366 return error;
367}