]> git.saurik.com Git - apple/xnu.git/blame - bsd/security/audit/audit_session.c
xnu-1504.15.3.tar.gz
[apple/xnu.git] / bsd / security / audit / audit_session.c
CommitLineData
b0d623f7
A
1/*-
2 * Copyright (c) 2008-2009 Apple Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <sys/kernel.h>
31#include <sys/event.h>
32#include <sys/kauth.h>
33#include <sys/queue.h>
34#include <sys/syscall.h>
35#include <sys/sysent.h>
36#include <sys/sysproto.h>
37#include <sys/systm.h>
38#include <sys/ucred.h>
39
40#include <libkern/OSAtomic.h>
41
42#include <bsm/audit.h>
43#include <security/audit/audit.h>
44#include <security/audit/audit_bsd.h>
45#include <security/audit/audit_private.h>
46
47#include <vm/vm_protos.h>
48#include <kern/audit_sessionport.h>
49
50kern_return_t ipc_object_copyin(ipc_space_t, mach_port_name_t,
51 mach_msg_type_name_t, ipc_port_t *);
52void ipc_port_release_send(ipc_port_t);
53
54/*
55 * The default auditinfo_addr entry for ucred.
56 */
57struct auditinfo_addr audit_default_aia = {
58 .ai_auid = AU_DEFAUDITID,
59 .ai_asid = AU_DEFAUDITSID,
60 .ai_termid = { .at_type = AU_IPv4, },
61};
62
63#if CONFIG_AUDIT
64
65/*
66 * Currently the hash table is a fixed size.
67 */
68#define HASH_TABLE_SIZE 97
69#define HASH_ASID(asid) (audit_session_hash(asid) % HASH_TABLE_SIZE)
70
71/*
72 * Audit Session Entry. This is treated as an object with public and private
73 * data. The se_auinfo field is the only information that is public and
74 * needs to be the first entry.
75 */
76struct au_sentry {
77 auditinfo_addr_t se_auinfo; /* Public audit session data. */
78#define se_asid se_auinfo.ai_asid
79#define se_auid se_auinfo.ai_auid
80#define se_mask se_auinfo.ai_mask
81#define se_termid se_auinfo.ai_termid
82#define se_flags se_auinfo.ai_flags
83
84 long se_refcnt; /* Reference count. */
85 long se_procnt; /* Processes in session. */
86 ipc_port_t se_port; /* Session port. */
87 struct klist se_klist; /* Knotes for session */
88 struct mtx se_klist_mtx; /* se_klist mutex */
89 LIST_ENTRY(au_sentry) se_link; /* Hash bucket link list (1) */
90};
91typedef struct au_sentry au_sentry_t;
92
93#define AU_SENTRY_PTR(aia_p) ((au_sentry_t *)(aia_p))
94
95static struct rwlock se_entry_lck; /* (1) lock for se_link above */
96
97LIST_HEAD(au_sentry_head, au_sentry);
98static struct au_sentry_head *au_sentry_bucket = NULL;
99
100/*
101 * Audit Propagation Knote List is a list of kevent knotes that are assosiated
102 * with an any ASID knote. If the any ASID gets modified or deleted these are
103 * modified or deleted as well.
104 */
105struct au_plist {
106 struct knote *pl_knote; /* ptr to per-session knote */
107 LIST_ENTRY(au_plist) pl_link; /* list link (2) */
108};
109typedef struct au_plist au_plist_t;
110
111struct au_plisthead {
112 struct rlck ph_rlck; /* (2) lock for pl_link list */
113 LIST_HEAD(au_plhead, au_plist) ph_head; /* list head */
114};
115typedef struct au_plisthead au_plisthead_t;
116
117#define EV_ANY_ASID EV_FLAG0
118
119MALLOC_DEFINE(M_AU_SESSION, "audit_session", "Audit session data");
120MALLOC_DEFINE(M_AU_EV_PLIST, "audit_ev_plist", "Audit session event plist");
121
122/*
123 * Kevent filters.
124 */
125static int audit_filt_sessionattach(struct knote *kn);
126static void audit_filt_sessiondetach(struct knote *kn);
127static void audit_filt_sessiontouch(struct knote *kn,
128 struct kevent64_s *kev, long type);
129static int audit_filt_session(struct knote *kn, long hint);
130
131static void audit_register_kevents(uint32_t asid, uint32_t auid);
132
133struct filterops audit_session_filtops = {
134 .f_attach = audit_filt_sessionattach,
135 .f_detach = audit_filt_sessiondetach,
136 .f_touch = audit_filt_sessiontouch,
137 .f_event = audit_filt_session,
138};
139
140/*
141 * The klist for consumers that are interested in any session (ASID). This list
142 * is not associated with any data structure but is used for registering
143 * new kevents when sessions are created. This klist is lock by
144 * anyas_klist_mtx.
145 */
146static struct klist anyas_klist;
147struct mtx anyas_klist_mtx;
148
149#define AUDIT_ANYAS_KLIST_LOCK_INIT() mtx_init(&anyas_klist_mtx, \
150 "audit anyas_klist_mtx", NULL, MTX_DEF)
151#define AUDIT_ANYAS_KLIST_LOCK() mtx_lock(&anyas_klist_mtx)
152#define AUDIT_ANYAS_KLIST_UNLOCK() mtx_unlock(&anyas_klist_mtx)
153#define AUDIT_ANYAS_KLIST_LOCK_ASSERT() mtx_assert(&anyas_klist_mtx, MA_OWNED)
154
155#define AUDIT_SENTRY_RWLOCK_INIT() rw_init(&se_entry_lck, \
156 "audit se_entry_lck")
157#define AUDIT_SENTRY_RLOCK() rw_rlock(&se_entry_lck)
158#define AUDIT_SENTRY_WLOCK() rw_wlock(&se_entry_lck)
159#define AUDIT_SENTRY_RWLOCK_ASSERT() rw_assert(&se_entry_lck, RA_LOCKED)
160#define AUDIT_SENTRY_RUNLOCK() rw_runlock(&se_entry_lck)
161#define AUDIT_SENTRY_WUNLOCK() rw_wunlock(&se_entry_lck)
162
163#define AUDIT_SE_KLIST_LOCK_INIT(se, n) mtx_init(&(se)->se_klist_mtx, \
164 n, NULL, MTX_DEF)
165#define AUDIT_SE_KLIST_LOCK(se) mtx_lock(&(se)->se_klist_mtx)
166#define AUDIT_SE_KLIST_UNLOCK(se) mtx_unlock(&(se)->se_klist_mtx)
167#define AUDIT_SE_KLIST_LOCK_DESTROY(se) mtx_destroy(&(se)->se_klist_mtx)
168#define AUDIT_SE_KLIST_LOCK_ASSERT(se) mtx_assert(&(se)->se_klist_mtx, \
169 MA_OWNED)
170
171#define AUDIT_PLIST_LOCK_INIT(pl) rlck_init(&(pl)->ph_rlck, \
172 "audit ph_rlck")
173#define AUDIT_PLIST_LOCK(pl) rlck_lock(&(pl)->ph_rlck)
174#define AUDIT_PLIST_UNLOCK(pl) rlck_unlock(&(pl)->ph_rlck)
175#define AUDIT_PLIST_LOCK_DESTROY(pl) rlck_destroy(&(pl)->ph_rlck)
176
177#if AUDIT_SESSION_DEBUG
178#include <kern/kalloc.h>
179
180struct au_sentry_debug {
181 auditinfo_addr_t se_auinfo;
182 long se_refcnt;
183 long se_procnt;
184};
185typedef struct au_sentry_debug au_sentry_debug_t;
186
187static int audit_sysctl_session_debug(struct sysctl_oid *oidp, void *arg1,
188 int arg2, struct sysctl_req *req);
189
190SYSCTL_PROC(_kern, OID_AUTO, audit_session_debug, CTLFLAG_RD, NULL, 0,
191 audit_sysctl_session_debug, "S,audit_session_debug",
192 "Current session debug info for auditing.");
193
194/*
195 * Copy out the session debug info via the sysctl interface. The userland code
196 * is something like the following:
197 *
198 * error = sysctlbyname("kern.audit_session_debug", buffer_ptr, &buffer_len,
199 * NULL, 0);
200 */
201static int
202audit_sysctl_session_debug(__unused struct sysctl_oid *oidp,
203 __unused void *arg1, __unused int arg2, struct sysctl_req *req)
204{
205 au_sentry_t *se;
206 au_sentry_debug_t *sed_tab, *next_sed;
207 int i, entry_cnt = 0;
208 size_t sz;
209 int err = 0;
210
211 /*
212 * This provides a read-only node.
213 */
214 if (req->newptr != USER_ADDR_NULL)
215 return (EPERM);
216
217 /*
218 * Walk the audit session hash table to determine the size.
219 */
220 AUDIT_SENTRY_RLOCK();
221 for(i = 0; i < HASH_TABLE_SIZE; i++)
222 LIST_FOREACH(se, &au_sentry_bucket[i], se_link)
223 if (se != NULL)
224 entry_cnt++;
225
226 /*
227 * If just querying then return the space required. There is an
228 * obvious race condition here so we just fudge this by 3 in case
229 * the audit session table grows.
230 */
231 if (req->oldptr == USER_ADDR_NULL) {
232 req->oldidx = (entry_cnt + 3) * sizeof(au_sentry_debug_t);
233 AUDIT_SENTRY_RUNLOCK();
234 return (0);
235 }
236
237 /*
238 * Alloc a temporary buffer.
239 */
240 if (req->oldlen < (entry_cnt * sizeof(au_sentry_debug_t))) {
241 AUDIT_SENTRY_RUNLOCK();
242 return (ENOMEM);
243 }
244 /*
245 * We hold the lock over the alloc since we don't want the table to
246 * grow on us. Therefore, use the non-blocking version of kalloc().
247 */
248 sed_tab = (au_sentry_debug_t *)kalloc_noblock(entry_cnt *
249 sizeof(au_sentry_debug_t));
250 if (sed_tab == NULL) {
251 AUDIT_SENTRY_RUNLOCK();
252 return (ENOMEM);
253 }
254 bzero(sed_tab, entry_cnt * sizeof(au_sentry_debug_t));
255
256 /*
257 * Walk the audit session hash table and build the record array.
258 */
259 sz = 0;
260 next_sed = sed_tab;
261 for(i = 0; i < HASH_TABLE_SIZE; i++) {
262 LIST_FOREACH(se, &au_sentry_bucket[i], se_link) {
263 if (se != NULL) {
264 bcopy(se, next_sed, sizeof(next_sed));
265 next_sed++;
266 sz += sizeof(au_sentry_debug_t);
267 }
268 }
269 }
270 AUDIT_SENTRY_RUNLOCK();
271
272 req->oldlen = sz;
273 err = SYSCTL_OUT(req, sed_tab, sz);
274 kfree(sed_tab, entry_cnt * sizeof(au_sentry_debug_t));
275
276 return (err);
277}
278
279#endif /* AUDIT_SESSION_DEBUG */
280
281/*
282 * Hash the audit session ID using a simple 32-bit mix.
283 */
284static inline uint32_t
285audit_session_hash(au_asid_t asid)
286{
287 uint32_t a = (uint32_t) asid;
288
289 a = (a - (a << 6)) ^ (a >> 17);
290 a = (a - (a << 9)) ^ (a << 4);
291 a = (a - (a << 3)) ^ (a << 10);
292 a = a ^ (a >> 15);
293
294 return (a);
295}
296
297/*
298 * Do an hash lookup and find the session entry for a given ASID. Return NULL
299 * if not found.
300 */
301static au_sentry_t *
302audit_session_find(au_asid_t asid)
303{
304 uint32_t hkey;
305 au_sentry_t *found_se;
306
307 AUDIT_SENTRY_RWLOCK_ASSERT();
308
309 hkey = HASH_ASID(asid);
310
311 LIST_FOREACH(found_se, &au_sentry_bucket[hkey], se_link)
312 if (found_se->se_asid == asid)
313 return (found_se);
314 return (NULL);
315}
316
317/*
318 * Call kqueue knote while holding the session entry klist lock.
319 */
320static void
321audit_session_knote(au_sentry_t *se, long hint)
322{
323
324 AUDIT_SE_KLIST_LOCK(se);
325 KNOTE(&se->se_klist, hint);
326 AUDIT_SE_KLIST_UNLOCK(se);
327}
328
329/*
330 * Remove the given audit_session entry from the hash table.
331 */
332static void
333audit_session_remove(au_sentry_t *se)
334{
335 uint32_t hkey;
336 au_sentry_t *found_se, *tmp_se;
337
338 KASSERT(se->se_refcnt == 0, ("audit_session_remove: ref count != 0"));
339
340 hkey = HASH_ASID(se->se_asid);
341
342 AUDIT_SENTRY_WLOCK();
343 LIST_FOREACH_SAFE(found_se, &au_sentry_bucket[hkey], se_link, tmp_se) {
344 if (found_se == se) {
345
346 audit_session_knote(found_se, NOTE_AS_CLOSE);
347
348 LIST_REMOVE(found_se, se_link);
349 AUDIT_SENTRY_WUNLOCK();
350 AUDIT_SE_KLIST_LOCK_DESTROY(found_se);
351 found_se->se_refcnt = 0;
352 free(found_se, M_AU_SESSION);
353
354 return;
355 }
356 }
357 AUDIT_SENTRY_WUNLOCK();
358}
359
360/*
361 * Reference the session by incrementing the sentry ref count.
362 */
363static void
364audit_ref_session(au_sentry_t *se)
365{
366 long old_val;
367
368 old_val = OSAddAtomicLong(1, &se->se_refcnt);
369 KASSERT(old_val < 100000,
370 ("audit_ref_session: Too many references on session."));
371}
372
373/*
374 * Decrement the sentry ref count and remove the session entry if last one.
375 */
376static void
377audit_unref_session(au_sentry_t *se)
378{
379 long old_val;
380
381 old_val = OSAddAtomicLong(-1, &se->se_refcnt);
382 if (old_val == 1)
383 audit_session_remove(se);
384 KASSERT(old_val > 0,
385 ("audit_unref_session: Too few references on session."));
386}
387
388/*
389 * Increment the process count in the session.
390 */
391static void
392audit_inc_procount(au_sentry_t *se)
393{
394 long old_val;
395
396 old_val = OSAddAtomicLong(1, &se->se_procnt);
397 KASSERT(old_val <= PID_MAX,
398 ("audit_inc_procount: proc count > PID_MAX"));
399}
400
401/*
402 * Decrement the process count and add a knote if it is the last process
403 * to exit the session.
404 */
405static void
406audit_dec_procount(au_sentry_t *se)
407{
408 long old_val;
409
410 old_val = OSAddAtomicLong(-1, &se->se_procnt);
411 if (old_val == 1)
412 audit_session_knote(se, NOTE_AS_END);
413 KASSERT(old_val >= 1,
414 ("audit_dec_procount: proc count < 0"));
415}
416
417/*
418 * Update the session entry and check to see if anything was updated.
419 * Returns:
420 * 0 Nothing was updated (We don't care about process preselection masks)
421 * 1 Something was updated.
422 */
423static int
424audit_update_sentry(au_sentry_t *se, auditinfo_addr_t *new_aia)
425{
426 auditinfo_addr_t *aia = &se->se_auinfo;
427 int update;
428
429 KASSERT(new_aia != &audit_default_aia,
430 ("audit_update_sentry: Trying to update the default aia."));
431
432 update = (aia->ai_auid != new_aia->ai_auid ||
433 bcmp(&aia->ai_termid, &new_aia->ai_termid,
434 sizeof(new_aia->ai_termid)) ||
435 aia->ai_flags != new_aia->ai_flags);
436
437 if (update)
438 bcopy(new_aia, aia, sizeof(*aia));
439
440 return (update);
441}
442
443/*
444 * Return the next session ID. The range of kernel generated audit session IDs
445 * is ASSIGNED_ASID_MIN to ASSIGNED_ASID_MAX.
446 */
447static uint32_t
448audit_session_nextid(void)
449{
450 static uint32_t next_asid = ASSIGNED_ASID_MIN;
451
452 AUDIT_SENTRY_RWLOCK_ASSERT();
453
454 if (next_asid > ASSIGNED_ASID_MAX)
455 next_asid = ASSIGNED_ASID_MIN;
456
457 return (next_asid++);
458}
459
460/*
461 * Allocated a new audit_session entry and add it to the hash table. If the
462 * given ASID is set to AU_ASSIGN_ASID then audit_session_new() will pick an
463 * audit session ID. Otherwise, it attempts use the one given. It creates a
464 * reference to the entry that must be unref'ed.
465 */
466static auditinfo_addr_t *
467audit_session_new(auditinfo_addr_t *new_aia, int newprocess)
468{
469 au_asid_t asid;
470 au_sentry_t *se = NULL;
471 auditinfo_addr_t *aia = NULL;
472 char nm[LOCK_MAX_NAME];
473
474 KASSERT(new_aia != NULL, ("audit_session_new: new_aia == NULL"));
475
476 asid = new_aia->ai_asid;
477
478#if 0 /* XXX this assertion is currently broken by securityd/LoginWindow */
479 KASSERT((asid != AU_ASSIGN_ASID && asid <= PID_MAX),
480 ("audit_session_new: illegal ASID value: %d", asid));
481#endif
482
483 /*
484 * Alloc a new session entry now so we don't wait holding the lock.
485 */
486 se = malloc(sizeof(au_sentry_t), M_AU_SESSION, M_WAITOK | M_ZERO);
487
488 snprintf(nm, sizeof(nm), "audit se_klist_mtx %d", asid);
489 AUDIT_SE_KLIST_LOCK_INIT(se, nm);
490
491 /*
492 * Find an unique session ID, if desired.
493 */
494 AUDIT_SENTRY_WLOCK();
495 if (asid == AU_ASSIGN_ASID) {
496 do {
497 asid = (au_asid_t)audit_session_nextid();
498 } while(audit_session_find(asid) != NULL);
499 } else {
500 au_sentry_t *found_se = NULL;
501
502 /*
503 * Check to see if the requested ASID is already in the
504 * hash table. If so, update it with the new auditinfo.
505 */
506 if ((found_se = audit_session_find(asid)) != NULL) {
507 int updated;
508
509 updated = audit_update_sentry(found_se, new_aia);
510 audit_ref_session(found_se);
511
512 AUDIT_SENTRY_WUNLOCK();
513 AUDIT_SE_KLIST_LOCK_DESTROY(se);
514 free(se, M_AU_SESSION);
515
516 if (updated)
517 audit_session_knote(found_se, NOTE_AS_UPDATE);
518
519 /*
520 * If this is a new process joining this session then
521 * we need to update the proc count.
522 */
523 if (newprocess)
524 audit_inc_procount(found_se);
525
526 return (&found_se->se_auinfo);
527 }
528 }
529
530 /*
531 * Start the reference and proc count at 1 to account for the process
532 * that invoked this via setaudit_addr() (or friends).
533 */
534 se->se_refcnt = se->se_procnt = 1;
535
536 /*
537 * Populate the new session entry. Note that process masks are stored
538 * in kauth ucred so just zero them here.
539 */
540 se->se_port = IPC_PORT_NULL;
541 aia = &se->se_auinfo;
542 aia->ai_asid = asid;
543 aia->ai_auid = new_aia->ai_auid;
544 bzero(&new_aia->ai_mask, sizeof(new_aia->ai_mask));
545 bcopy(&new_aia->ai_termid, &aia->ai_termid, sizeof(aia->ai_termid));
546 aia->ai_flags = new_aia->ai_flags;
547
548 /*
549 * Add it to the hash table.
550 */
551 LIST_INSERT_HEAD(&au_sentry_bucket[HASH_ASID(asid)], se, se_link);
552 AUDIT_SENTRY_WUNLOCK();
553
554 /*
555 * Register kevents for consumers wanting events for any ASID
556 * and knote the event.
557 */
558 audit_register_kevents(se->se_asid, se->se_auid);
559 audit_session_knote(se, NOTE_AS_START);
560
561 return (aia);
562}
563
564/*
565 * Lookup an existing session. A copy of the audit session info for a given
566 * ASID is returned in ret_aia. Returns 0 on success.
567 */
568int
569audit_session_lookup(au_asid_t asid, auditinfo_addr_t *ret_aia)
570{
571 au_sentry_t *se = NULL;
572
573 if ((uint32_t)asid > ASSIGNED_ASID_MAX)
574 return (-1);
575 AUDIT_SENTRY_RLOCK();
576 if ((se = audit_session_find(asid)) == NULL) {
577 AUDIT_SENTRY_RUNLOCK();
578 return (1);
579 }
580 if (ret_aia != NULL)
581 bcopy(&se->se_auinfo, ret_aia, sizeof(*ret_aia));
582 AUDIT_SENTRY_RUNLOCK();
583
584 return (0);
585}
586
587/*
588 * Add a reference to the session entry.
589 */
590void
591audit_session_ref(kauth_cred_t cred)
592{
593 auditinfo_addr_t *aia_p;
594
595 KASSERT(IS_VALID_CRED(cred),
596 ("audit_session_ref: Invalid kauth_cred."));
597
598 aia_p = cred->cr_audit.as_aia_p;
599
600 if (IS_VALID_SESSION(aia_p))
601 audit_ref_session(AU_SENTRY_PTR(aia_p));
602}
603
604/*
605 * Remove a reference to the session entry.
606 */
607void
608audit_session_unref(kauth_cred_t cred)
609{
610 auditinfo_addr_t *aia_p;
611
612 KASSERT(IS_VALID_CRED(cred),
613 ("audit_session_unref: Invalid kauth_cred."));
614
615 aia_p = cred->cr_audit.as_aia_p;
616
617 if (IS_VALID_SESSION(aia_p))
618 audit_unref_session(AU_SENTRY_PTR(aia_p));
619}
620
621void
622audit_session_procnew(kauth_cred_t cred)
623{
624 auditinfo_addr_t *aia_p;
625
626 KASSERT(IS_VALID_CRED(cred),
627 ("audit_session_procnew: Invalid kauth_cred."));
628
629 aia_p = cred->cr_audit.as_aia_p;
630
631 if (IS_VALID_SESSION(aia_p))
632 audit_inc_procount(AU_SENTRY_PTR(aia_p));
633}
634
635void
636audit_session_procexit(kauth_cred_t cred)
637{
638 auditinfo_addr_t *aia_p;
639
640 KASSERT(IS_VALID_CRED(cred),
641 ("audit_session_procexit: Invalid kauth_cred."));
642
643 aia_p = cred->cr_audit.as_aia_p;
644
645 if (IS_VALID_SESSION(aia_p))
646 audit_dec_procount(AU_SENTRY_PTR(aia_p));
647}
648
649/*
650 * Init the audit session code.
651 */
652void
653audit_session_init(void)
654{
655 int i;
656
657 KASSERT((ASSIGNED_ASID_MAX - ASSIGNED_ASID_MIN) > PID_MAX,
658 ("audit_session_init: ASSIGNED_ASID_MAX is not large enough."));
659
660 AUDIT_SENTRY_RWLOCK_INIT();
661 AUDIT_ANYAS_KLIST_LOCK_INIT();
662
663 au_sentry_bucket = malloc( sizeof(struct au_sentry) *
664 HASH_TABLE_SIZE, M_AU_SESSION, M_WAITOK | M_ZERO);
665
666 for (i = 0; i < HASH_TABLE_SIZE; i++)
667 LIST_INIT(&au_sentry_bucket[i]);
668}
669
670/*
671 * Allocate a new kevent propagation list (plist).
672 */
673static caddr_t
674audit_new_plist(void)
675{
676 au_plisthead_t *plhead;
677
678 plhead = malloc(sizeof(au_plisthead_t), M_AU_EV_PLIST, M_WAITOK |
679 M_ZERO);
680
681 LIST_INIT(&plhead->ph_head);
682 AUDIT_PLIST_LOCK_INIT(plhead);
683
684 return ((caddr_t) plhead);
685}
686
687/*
688 * Destroy a kevent propagation list (plist). The anyas_klist_mtx mutex must be
689 * held by the caller.
690 */
691static void
692audit_destroy_plist(struct knote *anyas_kn)
693{
694 au_plisthead_t *plhead;
695 au_plist_t *plentry, *ple_tmp;
696 struct kevent64_s kev;
697
698 KASSERT(anyas_kn != NULL, ("audit_destroy_plist: anyas = NULL"));
699 plhead = (au_plisthead_t *)anyas_kn->kn_hook;
700 KASSERT(plhead != NULL, ("audit_destroy_plist: plhead = NULL"));
701
702 /*
703 * Delete everything in the propagation list.
704 */
705 AUDIT_PLIST_LOCK(plhead);
706 LIST_FOREACH_SAFE(plentry, &plhead->ph_head, pl_link, ple_tmp) {
707 struct kqueue *kq = plentry->pl_knote->kn_kq;
708
709 kev.ident = plentry->pl_knote->kn_id;
710 kev.filter = EVFILT_SESSION;
711 kev.flags = EV_DELETE;
712
713 /*
714 * The plist entry gets removed in rm_from_plist() which is
715 * called indirectly by kevent_register().
716 */
717 kevent_register(kq, &kev, NULL);
718 }
719 AUDIT_PLIST_UNLOCK(plhead);
720
721 /*
722 * Remove the head.
723 */
724 AUDIT_PLIST_LOCK_DESTROY(plhead);
725 free(plhead, M_AU_EV_PLIST);
726}
727
728/*
729 * Add a knote pointer entry to the kevent propagation list.
730 */
731static void
732audit_add_to_plist(struct knote *anyas_kn, struct knote *kn)
733{
734 au_plisthead_t *plhead;
735 au_plist_t *plentry;
736
737 KASSERT(anyas_kn != NULL, ("audit_add_to_plist: anyas = NULL"));
738 plhead = (au_plisthead_t *)anyas_kn->kn_hook;
739 KASSERT(plhead != NULL, ("audit_add_to_plist: plhead = NULL"));
740
741 plentry = malloc(sizeof(au_plist_t), M_AU_EV_PLIST, M_WAITOK | M_ZERO);
742
743 plentry->pl_knote = kn;
744 AUDIT_PLIST_LOCK(plhead);
745 LIST_INSERT_HEAD(&plhead->ph_head, plentry, pl_link);
746 AUDIT_PLIST_UNLOCK(plhead);
747}
748
749/*
750 * Remote a knote pointer entry from the kevent propagation list. The lock
751 * on the plist may already be head (by audit_destroy_plist() above) so we use
752 * a recursive lock.
753 */
754static void
755audit_rm_from_plist(struct knote *kn)
756{
757 struct knote *anyas_kn;
758 au_plisthead_t *plhd;
759 au_plist_t *plentry, *ple_tmp;
760
761 KASSERT(kn != NULL, ("audit_rm_from_plist: kn = NULL"));
762 anyas_kn = (struct knote *)kn->kn_hook;
763 KASSERT(anyas_kn != NULL, ("audit_rm_to_plist: anyas = NULL"));
764 plhd = (au_plisthead_t *)anyas_kn->kn_hook;
765
766 AUDIT_PLIST_LOCK(plhd);
767 LIST_FOREACH_SAFE(plentry, &plhd->ph_head, pl_link, ple_tmp) {
768 if (plentry->pl_knote == kn) {
769 LIST_REMOVE(plentry, pl_link);
770 free(plentry, M_AU_EV_PLIST);
771 AUDIT_PLIST_UNLOCK(plhd);
772 return;
773 }
774 }
775 AUDIT_PLIST_UNLOCK(plhd);
776}
777
778/*
779 * The attach filter for EVFILT_SESSION.
780 */
781static int
782audit_filt_sessionattach(struct knote *kn)
783{
784 au_sentry_t *se = NULL;
785
786 /*
787 * Check flags for the events we currently support.
788 */
789 if ((kn->kn_sfflags & (NOTE_AS_START | NOTE_AS_END | NOTE_AS_CLOSE
790 | NOTE_AS_UPDATE | NOTE_AS_ERR)) == 0)
791 return (ENOTSUP);
792
793 /*
794 * If the interest is in any session then add to the any ASID knote
795 * list. Otherwise, add it to the knote list assosiated with the
796 * given session.
797 */
798 if (kn->kn_id == AS_ANY_ASID) {
799
800 kn->kn_flags |= EV_CLEAR;
801 kn->kn_ptr.p_se = NULL;
802
803 /*
804 * Attach a kevent propagation list for any kevents that get
805 * added.
806 */
807 kn->kn_hook = audit_new_plist();
808
809 AUDIT_ANYAS_KLIST_LOCK();
810 KNOTE_ATTACH(&anyas_klist, kn);
811 AUDIT_ANYAS_KLIST_UNLOCK();
812
813 return (0);
814 } else {
815
816 /*
817 * NOTE: The anyas klist lock will be held in this
818 * part of the code when indirectly called from
819 * audit_register_kevents() below.
820 */
821
822 /*
823 * Check to make sure it is a valid ASID.
824 */
825 if (kn->kn_id > ASSIGNED_ASID_MAX)
826 return (EINVAL);
827
828 AUDIT_SENTRY_RLOCK();
829 se = audit_session_find(kn->kn_id);
830 AUDIT_SENTRY_RUNLOCK();
831 if (se == NULL)
832 return (EINVAL);
833
834 AUDIT_SE_KLIST_LOCK(se);
835 kn->kn_flags |= EV_CLEAR;
836 kn->kn_ptr.p_se = se;
837
838 /*
839 * If this attach is the result of an "any ASID" (pseudo)
840 * kevent then attach the any session knote ptr to this knote.
841 * Also, add this knote to the its propagation list.
842 */
843 if (kn->kn_flags & EV_ANY_ASID) {
844 struct knote *anyas_kn =
845 (struct knote *)((uintptr_t)kn->kn_kevent.ext[0]);
846 kn->kn_hook = (caddr_t) anyas_kn;
847 kn->kn_flags &= ~EV_ANY_ASID;
848 audit_add_to_plist(anyas_kn, kn);
849 } else
850 kn->kn_hook = NULL;
851 KNOTE_ATTACH(&se->se_klist, kn);
852 AUDIT_SE_KLIST_UNLOCK(se);
853
854 return (0);
855 }
856}
857
858/*
859 * The detach filter for EVFILT_SESSION.
860 */
861static void
862audit_filt_sessiondetach(struct knote *kn)
863{
864 au_sentry_t *se = NULL;
865
866 if (kn->kn_id == AS_ANY_ASID) {
867
868 AUDIT_ANYAS_KLIST_LOCK();
869 audit_destroy_plist(kn);
870 KNOTE_DETACH(&anyas_klist, kn);
871 AUDIT_ANYAS_KLIST_UNLOCK();
872
873 } else {
874 /*
875 * If this knote was created by any ASID kevent then remove
876 * from kevent propagation list.
877 */
878 if (kn->kn_hook != NULL) {
879 audit_rm_from_plist(kn);
880 kn->kn_hook = NULL;
881 }
882
883 /*
884 * Check to see if already detached.
885 */
886 se = kn->kn_ptr.p_se;
887 if (se != NULL) {
888 AUDIT_SE_KLIST_LOCK(se);
889 kn->kn_ptr.p_se = NULL;
890 KNOTE_DETACH(&se->se_klist, kn);
891 AUDIT_SE_KLIST_UNLOCK(se);
892 }
893 }
894}
895
896/*
897 * The touch filter for EVFILT_SESSION. Check for any ASID kevent updates and
898 * propagate the change.
899 */
900static void
901audit_filt_sessiontouch(struct knote *kn, struct kevent64_s *kev, long type)
902{
903 struct knote *ple_kn;
904 struct kqueue *kq;
905 au_sentry_t *se;
906 au_plisthead_t *plhead;
907 au_plist_t *plentry;
908 struct kevent64_s newkev;
909
910 switch (type) {
911 case EVENT_REGISTER:
912 kn->kn_sfflags = kev->fflags;
913 kn->kn_sdata = kev->data;
914 /*
915 * If an any ASID kevent was updated then we may need to
916 * propagate the update.
917 */
918 if (kev->ident == AS_ANY_ASID && kn->kn_hook != NULL) {
919
920 /*
921 * Propagate the change to each of the session kevents
922 * that were created by this any ASID kevent.
923 */
924 plhead = (au_plisthead_t *)kn->kn_hook;
925 AUDIT_PLIST_LOCK(plhead);
926 LIST_FOREACH(plentry, &plhead->ph_head, pl_link) {
927
928 if ((ple_kn = plentry->pl_knote) == NULL)
929 continue;
930 if ((se = ple_kn->kn_ptr.p_se) == NULL)
931 continue;
932 if ((kq = ple_kn->kn_kq) == NULL)
933 continue;
934
935 newkev.ident = plentry->pl_knote->kn_id;
936 newkev.filter = EVFILT_SESSION;
937 newkev.flags = kev->flags;
938 newkev.fflags = kev->fflags;
939 newkev.data = kev->data;
940 newkev.udata = kev->udata;
941 kevent_register(kq, &newkev, NULL);
942 }
943 AUDIT_PLIST_UNLOCK(plhead);
944 }
945 break;
946
947 case EVENT_PROCESS:
948 *kev = kn->kn_kevent;
949 if (kn->kn_flags & EV_CLEAR) {
950 kn->kn_data = 0;
951 kn->kn_fflags = 0;
952 }
953 break;
954
955 default:
956 KASSERT((type == EVENT_REGISTER || type == EVENT_PROCESS),
957 ("filt_sessiontouch(): invalid type (%ld)", type));
958 break;
959 }
960}
961
962/*
963 * Event filter for EVFILT_SESSION. The AUDIT_SE_KLIST_LOCK should be held
964 * by audit_session_knote().
965 */
966static int
967audit_filt_session(struct knote *kn, long hint)
968{
969 int events = (int)hint;
970 au_sentry_t *se = kn->kn_ptr.p_se;
971
972 if (hint != 0 && se != NULL) {
973
974 if (kn->kn_sfflags & events) {
975 kn->kn_fflags |= events;
976 kn->kn_data = se->se_auid;
977 }
978
979 /*
980 * If this is the last possible event for the knote,
981 * detach the knote from the audit session before the
982 * session goes away.
983 */
984 if (events & NOTE_AS_CLOSE) {
985
986 /*
987 * If created by any ASID kevent then remove from
988 * propagation list.
989 */
990 if (kn->kn_hook != NULL) {
991 audit_rm_from_plist(kn);
992 kn->kn_hook = NULL;
993 }
994 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
995 kn->kn_ptr.p_se = NULL;
996 AUDIT_SE_KLIST_LOCK_ASSERT(se);
997 KNOTE_DETACH(&se->se_klist, kn);
998
999 return (1);
1000 }
1001 }
1002 return (kn->kn_fflags != 0);
1003}
1004
1005/*
1006 * For all the consumers wanting events for all sessions, register new
1007 * kevents associated with the session for the given ASID. The actual
1008 * attachment is done by the EVFILT_SESSION attach filter above.
1009 */
1010static void
1011audit_register_kevents(uint32_t asid, uint32_t auid)
1012{
1013 struct knote *kn;
1014
1015 AUDIT_ANYAS_KLIST_LOCK();
1016 SLIST_FOREACH(kn, &anyas_klist, kn_selnext) {
1017 struct kqueue *kq = kn->kn_kq;
1018 struct kevent64_s kev;
1019 int err;
1020
1021 kev.ident = asid;
1022 kev.filter = EVFILT_SESSION;
1023 kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | EV_ANY_ASID;
1024 kev.fflags = kn->kn_sfflags;
1025 kev.data = auid;
1026 kev.udata = kn->kn_kevent.udata;
1027
1028 /*
1029 * Save the knote ptr for this "any ASID" knote for the attach
1030 * filter.
1031 */
1032 kev.ext[0] = (uint64_t)((uintptr_t)kn);
1033
1034 /*
1035 * XXX kevent_register() may block here alloc'ing a new knote.
1036 * We may want to think about using a lockless linked list or
1037 * at least a sleep rwlock for the anyas_klist.
1038 */
1039 err = kevent_register(kq, &kev, NULL);
1040 if (err)
1041 kn->kn_fflags |= NOTE_AS_ERR;
1042 }
1043 AUDIT_ANYAS_KLIST_UNLOCK();
1044}
1045
1046/*
1047 * Safely update kauth cred of the given process with new the given audit info.
1048 * If the newprocess flag is set then we need to account for this process in
1049 * the proc count.
1050 */
1051int
1052audit_session_setaia(proc_t p, auditinfo_addr_t *aia_p, int newprocess)
1053{
1054 kauth_cred_t my_cred, my_new_cred;
1055 struct au_session as;
1056 struct au_session tmp_as;
1057 auditinfo_addr_t caia;
1058
1059 /*
1060 * If this is going to modify an existing session then do some
1061 * immutable checks.
1062 */
1063 if (audit_session_lookup(aia_p->ai_asid, &caia) == 0) {
1064
1065 /*
1066 * If the current audit ID is not the default then it is
1067 * immutable.
1068 */
1069 if (caia.ai_auid != AU_DEFAUDITID &&
1070 caia.ai_auid != aia_p->ai_auid)
1071 return (EINVAL);
1072
1073 /*
1074 * If the current termid is not the default then it is
1075 * immutable.
1076 */
1077 if ((caia.ai_termid.at_type != AU_IPv4 ||
1078 caia.ai_termid.at_port != 0 ||
1079 caia.ai_termid.at_addr[0] != 0) &&
1080 (caia.ai_termid.at_port != aia_p->ai_termid.at_port ||
1081 caia.ai_termid.at_type != aia_p->ai_termid.at_type ||
1082 bcmp(&caia.ai_termid.at_addr, &aia_p->ai_termid.at_addr,
1083 sizeof (caia.ai_termid.at_addr) )) )
1084 return (EINVAL);
1085
1086 /* The audit flags are immutable. */
1087 if (caia.ai_flags != aia_p->ai_flags)
1088 return (EINVAL);
1089
1090 /* The audit masks are mutable. */
1091 }
1092
1093 my_cred = kauth_cred_proc_ref(p);
1094 bcopy(&aia_p->ai_mask, &as.as_mask, sizeof(as.as_mask));
1095 as.as_aia_p = audit_session_new(aia_p, newprocess);
1096
1097 /*
1098 * We are modifying the audit info in a credential so we need a new
1099 * credential (or take another reference on an existing credential that
1100 * matches our new one). We must do this because the audit info in the
1101 * credential is used as part of our hash key. Get current credential
1102 * in the target process and take a reference while we muck with it.
1103 */
1104 for (;;) {
1105
1106 /*
1107 * Set the credential with new info. If there is no change,
1108 * we get back the same credential we passed in; if there is
1109 * a change, we drop the reference on the credential we
1110 * passed in. The subsequent compare is safe, because it is
1111 * a pointer compare rather than a contents compare.
1112 */
1113 bcopy(&as, &tmp_as, sizeof(tmp_as));
1114 my_new_cred = kauth_cred_setauditinfo(my_cred, &tmp_as);
1115
1116 if (my_cred != my_new_cred) {
1117 proc_lock(p);
1118 /* Need to protect for a race where another thread also
1119 * changed the credential after we took our reference.
1120 * If p_ucred has changed then we should restart this
1121 * again with the new cred.
1122 */
1123 if (p->p_ucred != my_cred) {
1124 proc_unlock(p);
1125 audit_session_unref(my_new_cred);
1126 kauth_cred_unref(&my_new_cred);
1127 /* try again */
1128 my_cred = kauth_cred_proc_ref(p);
1129 continue;
1130 }
1131 p->p_ucred = my_new_cred;
1132 proc_unlock(p);
1133 }
1134 /*
1135 * Drop old proc reference or our extra reference.
1136 */
1137 kauth_cred_unref(&my_cred);
1138 break;
1139 }
1140 audit_session_unref(my_new_cred);
1141
1142 /*
1143 * Propagate the change from the process to the Mach task.
1144 */
1145 set_security_token(p);
1146
1147 return (0);
1148}
1149
1150/*
1151 * audit_session_self (system call)
1152 *
1153 * Description: Obtain a Mach send right for the current session.
1154 *
1155 * Parameters: p Process calling audit_session_self().
1156 *
1157 * Returns: *ret_port Named Mach send right, which may be
1158 * MACH_PORT_NULL in the failure case.
1159 *
1160 * Errno: 0 Success
1161 * EINVAL The calling process' session has not be set.
1162 * ESRCH Bad process, can't get valid cred for process.
1163 * ENOMEM Port allocation failed due to no free memory.
1164 */
1165int
1166audit_session_self(proc_t p, __unused struct audit_session_self_args *uap,
1167 mach_port_name_t *ret_port)
1168{
1169 ipc_port_t sendport = IPC_PORT_NULL;
1170 kauth_cred_t cred = NULL;
1171 auditinfo_addr_t *aia_p;
1172 au_sentry_t *se;
1173 int err = 0;
1174
1175 cred = kauth_cred_proc_ref(p);
1176 if (!IS_VALID_CRED(cred)) {
1177 err = ESRCH;
1178 goto done;
1179 }
1180
1181 aia_p = cred->cr_audit.as_aia_p;
1182 if (!IS_VALID_SESSION(aia_p)) {
1183 err = EINVAL;
1184 goto done;
1185 }
1186
1187 se = AU_SENTRY_PTR(aia_p);
1188
1189 /*
1190 * Processes that join using this mach port will inherit this process'
1191 * pre-selection masks.
1192 */
1193 if (se->se_port == IPC_PORT_NULL)
1194 bcopy(&cred->cr_audit.as_mask, &se->se_mask,
1195 sizeof(se->se_mask));
1196
1197 if ((sendport = audit_session_mksend(aia_p, &se->se_port)) == NULL) {
1198 /* failed to alloc new port */
1199 err = ENOMEM;
1200 goto done;
1201 }
1202
1203 /*
1204 * This reference on the session is unref'ed in
1205 * audit_session_port_destory(). This reference is needed so the
1206 * session doesn't get dropped until the session join is done.
1207 */
1208 audit_ref_session(se);
1209
1210
1211done:
1212 if (cred != NULL)
1213 kauth_cred_unref(&cred);
1214 if (err == 0)
1215 *ret_port = ipc_port_copyout_send(sendport,
1216 get_task_ipcspace(p->task));
1217 else
1218 *ret_port = MACH_PORT_NULL;
1219
1220 return (err);
1221}
1222
1223void
1224audit_session_portaiadestroy(struct auditinfo_addr *port_aia_p)
1225{
1226 au_sentry_t *se;
1227
1228 KASSERT(port_aia_p != NULL,
1229 ("audit_session_infodestroy: port_aia_p = NULL"));
1230
1231 se = AU_SENTRY_PTR(port_aia_p);
1232
1233 /*
1234 * Drop the reference added in audit_session_self().
1235 */
1236 if (se != NULL) {
1237 se->se_port = IPC_PORT_NULL;
1238 audit_unref_session(se);
1239 }
1240
1241}
1242
1243static int
1244audit_session_join_internal(proc_t p, ipc_port_t port, au_asid_t *new_asid)
1245{
1246 auditinfo_addr_t *port_aia_p, *old_aia_p;
1247 kauth_cred_t cred = NULL;
1248 au_asid_t old_asid;
1249 int err = 0;
1250
1251 *new_asid = AU_DEFAUDITSID;
1252
1253 if ((port_aia_p = audit_session_porttoaia(port)) == NULL) {
1254 err = EINVAL;
1255 goto done;
1256 }
1257 *new_asid = port_aia_p->ai_asid;
1258
1259 cred = kauth_cred_proc_ref(p);
1260 if (!IS_VALID_CRED(cred)) {
1261 kauth_cred_unref(&cred);
1262 err = ESRCH;
1263 goto done;
1264 }
1265 old_aia_p = cred->cr_audit.as_aia_p;
1266 old_asid = old_aia_p->ai_asid;
1267
1268 /*
1269 * Add process in if not already in the session.
1270 */
1271 if (*new_asid != old_asid) {
1272 audit_session_setaia(p, port_aia_p, 1);
1273 /*
1274 * If this process was in a valid session before then we
1275 * need to decrement the process count of the session it
1276 * came from.
1277 */
1278 if (IS_VALID_SESSION(old_aia_p))
1279 audit_dec_procount(AU_SENTRY_PTR(old_aia_p));
1280 }
1281 kauth_cred_unref(&cred);
1282
1283done:
1284 if (port != IPC_PORT_NULL)
1285 ipc_port_release_send(port);
1286
1287 return (err);
1288}
1289
1290/*
1291 * audit_session_spawnjoin
1292 *
1293 * Description: posix_spawn() interface to audit_session_join_internal().
1294 *
1295 * Returns: 0 Success
1296 * EINVAL Invalid Mach port name.
1297 * ESRCH Invalid calling process/cred.
1298 */
1299int
1300audit_session_spawnjoin(proc_t p, ipc_port_t port)
1301{
1302 au_asid_t new_asid;
1303
1304 return (audit_session_join_internal(p, port, &new_asid));
1305}
1306
1307/*
1308 * audit_session_join (system call)
1309 *
1310 * Description: Join the session for a given Mach port send right.
1311 *
1312 * Parameters: p Process calling session join.
1313 * uap->port A Mach send right.
1314 *
1315 * Returns: *ret_asid Audit session ID of new session, which may
1316 * be AU_DEFAUDITSID in the failure case.
1317 *
1318 * Errno: 0 Success
1319 * EINVAL Invalid Mach port name.
1320 * ESRCH Invalid calling process/cred.
1321 */
1322int
1323audit_session_join(proc_t p, struct audit_session_join_args *uap,
1324 au_asid_t *ret_asid)
1325{
1326 ipc_port_t port = IPC_PORT_NULL;
1327 mach_port_name_t send = uap->port;
1328 int err = 0;
1329
1330
1331 if (ipc_object_copyin(get_task_ipcspace(p->task), send,
1332 MACH_MSG_TYPE_COPY_SEND, &port) != KERN_SUCCESS) {
1333 *ret_asid = AU_DEFAUDITSID;
1334 err = EINVAL;
1335 } else
1336 err = audit_session_join_internal(p, port, ret_asid);
1337
1338 return (err);
1339}
1340
1341#else
1342
1343int
1344audit_session_self(proc_t p, struct audit_session_self_args *uap,
1345 mach_port_name_t *ret_port)
1346{
1347#pragma unused(p, uap, ret_port)
1348
1349 return (ENOSYS);
1350}
1351
1352int
1353audit_session_join(proc_t p, struct audit_session_join_args *uap,
1354 au_asid_t *ret_asid)
1355{
1356#pragma unused(p, uap, ret_asid)
1357
1358 return (ENOSYS);
1359}
1360
1361#endif /* CONFIG_AUDIT */