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#ifndef _SYS_PERSONA_H_
29#define _SYS_PERSONA_H_
30
31#ifdef PRIVATE
32#include <sys/param.h>
33
34#ifdef KERNEL
35__enum_decl(persona_type_t, int, {
36#else /* !KERNEL */
37enum {
38#endif /* KERNEL */
39 PERSONA_INVALID = 0,
40 PERSONA_GUEST = 1,
41 PERSONA_MANAGED = 2,
42 PERSONA_PRIV = 3,
43 PERSONA_SYSTEM = 4,
44 PERSONA_DEFAULT = 5,
45 PERSONA_SYSTEM_PROXY = 6,
46 PERSONA_SYS_EXT = 7,
47 PERSONA_ENTERPRISE = 8,
48
49 PERSONA_TYPE_MAX = PERSONA_ENTERPRISE,
50#ifdef KERNEL
51});
52#else /* !KERNEL */
53};
54#endif /* KERNEL */
55
56#define PERSONA_ID_NONE ((uid_t)-1)
57
58struct kpersona_info {
59 /* v1 fields */
60 uint32_t persona_info_version;
61
62 uid_t persona_id;
63 int persona_type;
64 gid_t persona_gid; /* unused */
65 uint32_t persona_ngroups; /* unused */
66 gid_t persona_groups[NGROUPS]; /* unused */
67 uid_t persona_gmuid; /* unused */
68 char persona_name[MAXLOGNAME + 1];
69
70 /* v2 fields */
71 uid_t persona_uid;
72} __attribute__((packed));
73
74#define PERSONA_INFO_V1 1
75#define PERSONA_INFO_V2 2
76
77// Userspace and the kernel must see the same struct layout. Assert that in
78// either case sizeof() is equal to the same pre-determined value.
79_Static_assert(sizeof(struct kpersona_info) == 348, "sizeof(kpersona_info) == 348");
80
81#define PERSONA_OP_ALLOC 1
82#define PERSONA_OP_PALLOC 2
83#define PERSONA_OP_DEALLOC 3
84#define PERSONA_OP_GET 4
85#define PERSONA_OP_INFO 5
86#define PERSONA_OP_PIDINFO 6
87#define PERSONA_OP_FIND 7
88#define PERSONA_OP_GETPATH 8
89#define PERSONA_OP_FIND_BY_TYPE 9
90
91#define PERSONA_MGMT_ENTITLEMENT "com.apple.private.persona-mgmt"
92
93#ifndef KERNEL
94/*
95 * user space persona interface
96 */
97
98/*
99 * kpersona_alloc: Allocate a new in-kernel persona
100 *
101 * Parameters:
102 * info: Pointer to persona info structure describing the
103 * attributes of the persona to create / allocate.
104 *
105 * id: output: set to the ID of the created persona
106 *
107 * Note:
108 * The 'persona_id' field of the 'info' parameter is ignored.
109 *
110 * Return:
111 * != 0: ERROR
112 * == 0: Success
113 */
114int kpersona_alloc(struct kpersona_info *info, uid_t *id);
115
116/*
117 * kpersona_palloc: Allocate a new in-kernel persona with a directory
118 * pathname
119 *
120 * Parameters:
121 * info: Pointer to persona info structure describing the
122 * attributes of the persona to create / allocate.
123 *
124 * path: Pointer to directory name that stores persona specific
125 * data. Assumes path buffer length = MAXPATHLEN and is a
126 * null-terminated string.
127 *
128 * id: output: set to the ID of the created persona
129 *
130 * Note:
131 * The 'persona_id' field of the 'info' parameter is ignored.
132 *
133 * Return:
134 * != 0: ERROR
135 * == 0: Success
136 */
137int kpersona_palloc(struct kpersona_info *info, uid_t *id, char path[MAXPATHLEN]);
138
139/*
140 * kpersona_dealloc: delete / destroy an in-kernel persona
141 *
142 * Parameters:
143 * id: the ID of the persona to destroy
144 *
145 * Return:
146 * < 0: ERROR
147 * 0: Success
148 */
149int kpersona_dealloc(uid_t id);
150
151/*
152 * kpersona_get: retrieve the persona with which the current thread is running
153 *
154 * To find the proc's persona id use kpersona_pidinfo
155 *
156 * Parameters:
157 * id: output: will be filled with the persona id from the voucher adopted
158 * on the current thread. If that voucher contains no persona information
159 * or there is no such voucher, then it defaults to the proc's persona id.
160 *
161 * Return:
162 * < 0: Thread is not running under any persona
163 * 0: Success (uuid is filled with running persona UUID)
164 */
165int kpersona_get(uid_t *id);
166
167/*
168 * kpersona_get_path: retrieve the given persona's path
169 *
170 * Parameters:
171 * id: ID of the persona
172 *
173 * path: output: filled in with path on success.
174 * Assumes path buffer length = MAXPATHLEN
175 *
176 * Return:
177 * < 0: Error
178 * 0: Success
179 */
180int kpersona_getpath(uid_t id, char path[MAXPATHLEN]);
181
182/*
183 * kpersona_info: gather info about the given persona
184 *
185 * Parameters:
186 * id: ID of the persona to investigate
187 * If set to 0, it uses persona id from the voucher adopted on the current
188 * thread. If that voucher contains no persona information or there is no
189 * such voucher, then it defaults to the proc's persona id.
190 *
191 * info: output: filled in with persona attributes on success.
192 *
193 * Return:
194 * < 0: ERROR
195 * 0: Success
196 */
197int kpersona_info(uid_t id, struct kpersona_info *info);
198
199/*
200 * kpersona_pidinfo: gather persona info about the given PID
201 *
202 * Parameters:
203 * pid: PID of the process whose persona info we're to return
204 *
205 * info: output: filled in with persona attributes on success.
206 *
207 * Return:
208 * < 0: ERROR
209 * 0: Success
210 */
211int kpersona_pidinfo(pid_t pid, struct kpersona_info *info);
212
213/*
214 * kpersona_find: lookup the kernel's UUID of a persona
215 *
216 * Parameters:
217 * name: Local login name of the persona.
218 * Set this to NULL to find personas by 'uid'.
219 *
220 * uid: UID of the persona.
221 * Set this to -1 to find personas by 'name'
222 *
223 * id: output: the ID(s) matching the input parameters
224 * This can be NULL
225 *
226 * idlen: input - size of 'id' buffer (in number of IDs)
227 * output - the total required size of the 'id' buffer
228 * (in number of IDs) - may be larger than input size
229 * Note:
230 * At least one of 'name' or 'uid' must be set.
231 *
232 * Return:
233 * < 0: ERROR
234 * >= 0: Output value of idlen - may be larger than input size
235 */
236int kpersona_find(const char *name, uid_t uid, uid_t *id, size_t *idlen);
237
238/*
239 * kpersona_find_by_type: lookup the persona ids by type
240 *
241 * Parameters:
242 * persona_type: Type of persona id (see enum)
243 *
244 * id: output: the ID(s) matching the input parameters
245 * This can be NULL
246 *
247 * idlen: input - size of 'id' buffer (in number of IDs)
248 * output - the total required size of the 'id' buffer
249 * (in number of IDs) - may be larger than input size
250 * Return:
251 * < 0: ERROR
252 * >= 0: Output value of idlen - may be larger than input size
253 */
254int kpersona_find_by_type(int persona_type, uid_t *id, size_t *idlen);
255#endif /* !KERNEL */
256
257#ifdef KERNEL_PRIVATE
258/* XNU + kext private interface */
259#include <sys/cdefs.h>
260#include <sys/kauth.h>
261#include <libkern/libkern.h>
262#include <os/refcnt.h>
263
264#ifdef PERSONA_DEBUG
265#include <os/log.h>
266#define persona_dbg(fmt, ...) \
267 os_log(OS_LOG_DEFAULT, "[%4d] %s: " fmt "\n", \
268 current_proc() ? proc_getpid(current_proc()) : -1, \
269 __func__, ## __VA_ARGS__)
270#else
271#define persona_dbg(fmt, ...) do { } while (0)
272#endif
273
274/*
275 * Persona
276 */
277#ifdef XNU_KERNEL_PRIVATE
278/* only XNU proper needs to see the persona structure */
279struct persona {
280 os_refcnt_t pna_refcount;
281 int32_t pna_valid;
282
283 uid_t pna_id;
284 persona_type_t pna_type;
285 char pna_login[MAXLOGNAME + 1];
286 char *pna_path;
287 uid_t pna_uid;
288
289 LIST_ENTRY(persona) pna_list;
290
291 /* this could go away if we used a coalition */
292 LIST_HEAD(, proc) pna_members;
293
294 lck_mtx_t pna_lock;
295
296 /*
297 * We can add things here such as PID maps, UID maps, etc.
298 */
299#ifdef PERSONA_DEBUG
300 char pna_desc[128];
301#endif
302};
303
304#define persona_lock(persona) lck_mtx_lock(&(persona)->pna_lock)
305#define persona_unlock(persona) lck_mtx_unlock(&(persona)->pna_lock)
306#define persona_try_lock(persona) lck_mtx_try_lock(&(persona)->pna_lock)
307
308#define persona_lock_assert_held(persona) \
309 LCK_MTX_ASSERT(&(persona)->pna_lock, LCK_MTX_ASSERT_OWNED)
310
311#ifdef PERSONA_DEBUG
312static inline const char *
313persona_desc(struct persona *persona, int locked)
314{
315 if (!persona) {
316 return "<none>";
317 }
318
319 if (persona->pna_desc[0] != 0) {
320 return persona->pna_desc;
321 }
322
323 if (!locked) {
324 persona_lock(persona);
325 }
326 if (persona->pna_desc[0] != 0) {
327 goto out_unlock;
328 }
329
330 char *p = &persona->pna_desc[0];
331 char *end = p + sizeof(persona->pna_desc) - 1;
332
333 *end = 0;
334 p += scnprintf(p, end - p, "%s/%d",
335 persona->pna_login,
336 persona->pna_id);
337
338 if (p <= end) {
339 *p = 0;
340 }
341out_unlock:
342 if (!locked) {
343 persona_unlock(persona);
344 }
345
346 return persona->pna_desc;
347}
348#else /* !PERSONA_DEBUG */
349static inline const char *
350persona_desc(struct persona *persona, int locked)
351{
352 (void)persona;
353 (void)locked;
354 return "<persona>";
355}
356#endif
357
358#else /* !XNU_KERNEL_PRIVATE */
359/* kexts should only see an opaque persona structure */
360struct persona;
361#endif
362
363__BEGIN_DECLS
364
365#ifndef _KAUTH_CRED_T
366#define _KAUTH_CRED_T
367typedef struct ucred *kauth_cred_t;
368#endif /* !_KAUTH_CRED_T */
369
370/* returns the persona ID for the given pesona structure */
371uid_t persona_get_id(struct persona *persona);
372
373/* returns the persona UID for the given pesona structure */
374uid_t persona_get_uid(struct persona *persona);
375
376/* returns the type of the persona (see enum above: PERSONA_GUEST, etc.) */
377int persona_get_type(struct persona *persona);
378
379/* returns a reference that must be released with persona_put() */
380struct persona *persona_lookup(uid_t id);
381
382/*
383 * Search for personas based on name or uid
384 *
385 * Parameters:
386 * name: Local login name of the persona.
387 * Set this to NULL to find personas by 'uid'.
388 *
389 * uid: UID of the persona.
390 * Set this to -1 to find personas by 'name'
391 *
392 * persona: output - array of persona pointers. Each non-NULL value
393 * must* be released with persona_put. This can be NULL.
394 *
395 * plen: input - size of 'persona' buffer (in number of pointers)
396 * output - the total required size of the 'persona' buffer (could be larger than input value)
397 *
398 * Return:
399 * 0: Success
400 * != 0: failure (BSD errno value ESRCH or EINVAL)
401 */
402int persona_find(const char *login, uid_t uid,
403 struct persona **persona, size_t *plen);
404
405/* returns a reference that must be released with persona_put() */
406struct persona *persona_proc_get(pid_t pid);
407
408/* returns the persona id tied to the current thread (also uses adopted voucher) */
409uid_t current_persona_get_id(void);
410
411/* returns a reference to the persona tied to the current thread (also uses adopted voucher) */
412struct persona *current_persona_get(void);
413
414/* get a reference to a persona structure */
415struct persona *persona_get(struct persona *persona);
416
417/* returns a reference to proc's persona that must be released with persona_put() */
418struct persona *proc_persona_get(proc_t p);
419
420/* release a reference to a persona structure */
421void persona_put(struct persona *persona);
422
423/*
424 * Search for personas of a given type, 'persona_type'.
425 *
426 * Parameters:
427 * persona_type: Type of persona (see enum)
428 *
429 * persona: output - array of persona pointers. Each non-NULL value
430 * must* be released with persona_put. This can be NULL.
431 *
432 * plen: input - size of 'persona' buffer (in number of pointers)
433 * output - the total required size of the 'persona' buffer (could be larger than input value)
434 *
435 * Return:
436 * 0: Success
437 * != 0: failure (BSD errno value ESRCH or EINVAL)
438 */
439int persona_find_by_type(persona_type_t persona_type, struct persona **persona,
440 size_t *plen);
441
442boolean_t persona_is_adoption_allowed(struct persona *persona);
443
444#ifdef XNU_KERNEL_PRIVATE
445
446#if CONFIG_PERSONAS
447#include <sys/proc_internal.h>
448
449/*
450 * In-kernel persona API
451 */
452extern const uint32_t g_max_personas;
453
454struct persona *persona_alloc(uid_t id, const char *login,
455 persona_type_t type, char *path, uid_t uid, int *error);
456
457int persona_init_begin(struct persona *persona);
458void persona_init_end(struct persona *persona, int error);
459
460struct persona *persona_lookup_and_invalidate(uid_t id);
461
462static inline int
463proc_has_persona(proc_t p)
464{
465 if (p && p->p_persona) {
466 return 1;
467 }
468 return 0;
469}
470
471static inline uid_t
472persona_id_from_proc(proc_t p)
473{
474 if (p && p->p_persona) {
475 return p->p_persona->pna_id;
476 }
477 return PERSONA_ID_NONE;
478}
479
480/* consumes persona */
481int persona_proc_adopt(proc_t p, struct persona *persona, kauth_cred_derive_t fn);
482
483int persona_proc_drop(proc_t p);
484
485/* returns a reference that must be released with persona_put() */
486struct persona *persona_proc_get(pid_t pid);
487
488int persona_find_all(const char *login, uid_t uid, persona_type_t persona_type,
489 struct persona **persona, size_t *plen);
490
491#else /* !CONFIG_PERSONAS */
492
493static inline int
494proc_has_persona(__unused proc_t p)
495{
496 return 0;
497}
498
499static inline uid_t
500persona_id_from_proc(__unused proc_t p)
501{
502 return PERSONA_ID_NONE;
503}
504
505#endif /* CONFIG_PERSONAS */
506#endif /* XNU_KERNEL_PRIVATE */
507__END_DECLS
508
509#endif /* KERNEL_PRIVATE */
510
511#endif /* PRIVATE */
512#endif /* _SYS_PERSONA_H_ */
513