2 * Copyright (c) 2008-2009 Apple Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
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>
40 #include <libkern/OSAtomic.h>
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>
47 #include <vm/vm_protos.h>
48 #include <kern/audit_sessionport.h>
50 kern_return_t
ipc_object_copyin(ipc_space_t
, mach_port_name_t
,
51 mach_msg_type_name_t
, ipc_port_t
*);
52 void ipc_port_release_send(ipc_port_t
);
55 * The default auditinfo_addr entry for ucred.
57 struct auditinfo_addr audit_default_aia
= {
58 .ai_auid
= AU_DEFAUDITID
,
59 .ai_asid
= AU_DEFAUDITSID
,
60 .ai_termid
= { .at_type
= AU_IPv4
, },
66 * Currently the hash table is a fixed size.
68 #define HASH_TABLE_SIZE 97
69 #define HASH_ASID(asid) (audit_session_hash(asid) % HASH_TABLE_SIZE)
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.
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
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) */
91 typedef struct au_sentry au_sentry_t
;
93 #define AU_SENTRY_PTR(aia_p) ((au_sentry_t *)(aia_p))
95 static struct rwlock se_entry_lck
; /* (1) lock for se_link above */
97 LIST_HEAD(au_sentry_head
, au_sentry
);
98 static struct au_sentry_head
*au_sentry_bucket
= NULL
;
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.
106 struct knote
*pl_knote
; /* ptr to per-session knote */
107 LIST_ENTRY(au_plist
) pl_link
; /* list link (2) */
109 typedef struct au_plist au_plist_t
;
111 struct au_plisthead
{
112 struct rlck ph_rlck
; /* (2) lock for pl_link list */
113 LIST_HEAD(au_plhead
, au_plist
) ph_head
; /* list head */
115 typedef struct au_plisthead au_plisthead_t
;
117 #define EV_ANY_ASID EV_FLAG0
119 MALLOC_DEFINE(M_AU_SESSION
, "audit_session", "Audit session data");
120 MALLOC_DEFINE(M_AU_EV_PLIST
, "audit_ev_plist", "Audit session event plist");
125 static int audit_filt_sessionattach(struct knote
*kn
);
126 static void audit_filt_sessiondetach(struct knote
*kn
);
127 static void audit_filt_sessiontouch(struct knote
*kn
,
128 struct kevent64_s
*kev
, long type
);
129 static int audit_filt_session(struct knote
*kn
, long hint
);
131 static void audit_register_kevents(uint32_t asid
, uint32_t auid
);
133 struct 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
,
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
146 static struct klist anyas_klist
;
147 struct mtx anyas_klist_mtx
;
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)
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)
163 #define AUDIT_SE_KLIST_LOCK_INIT(se, n) mtx_init(&(se)->se_klist_mtx, \
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, \
171 #define AUDIT_PLIST_LOCK_INIT(pl) rlck_init(&(pl)->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)
177 #if AUDIT_SESSION_DEBUG
178 #include <kern/kalloc.h>
180 struct au_sentry_debug
{
181 auditinfo_addr_t se_auinfo
;
185 typedef struct au_sentry_debug au_sentry_debug_t
;
187 static int audit_sysctl_session_debug(struct sysctl_oid
*oidp
, void *arg1
,
188 int arg2
, struct sysctl_req
*req
);
190 SYSCTL_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.");
195 * Copy out the session debug info via the sysctl interface. The userland code
196 * is something like the following:
198 * error = sysctlbyname("kern.audit_session_debug", buffer_ptr, &buffer_len,
202 audit_sysctl_session_debug(__unused
struct sysctl_oid
*oidp
,
203 __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
206 au_sentry_debug_t
*sed_tab
, *next_sed
;
207 int i
, entry_cnt
= 0;
212 * This provides a read-only node.
214 if (req
->newptr
!= USER_ADDR_NULL
)
218 * Walk the audit session hash table to determine the size.
220 AUDIT_SENTRY_RLOCK();
221 for(i
= 0; i
< HASH_TABLE_SIZE
; i
++)
222 LIST_FOREACH(se
, &au_sentry_bucket
[i
], se_link
)
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.
231 if (req
->oldptr
== USER_ADDR_NULL
) {
232 req
->oldidx
= (entry_cnt
+ 3) * sizeof(au_sentry_debug_t
);
233 AUDIT_SENTRY_RUNLOCK();
238 * Alloc a temporary buffer.
240 if (req
->oldlen
< (entry_cnt
* sizeof(au_sentry_debug_t
))) {
241 AUDIT_SENTRY_RUNLOCK();
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().
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();
254 bzero(sed_tab
, entry_cnt
* sizeof(au_sentry_debug_t
));
257 * Walk the audit session hash table and build the record array.
261 for(i
= 0; i
< HASH_TABLE_SIZE
; i
++) {
262 LIST_FOREACH(se
, &au_sentry_bucket
[i
], se_link
) {
264 bcopy(se
, next_sed
, sizeof(next_sed
));
266 sz
+= sizeof(au_sentry_debug_t
);
270 AUDIT_SENTRY_RUNLOCK();
273 err
= SYSCTL_OUT(req
, sed_tab
, sz
);
274 kfree(sed_tab
, entry_cnt
* sizeof(au_sentry_debug_t
));
279 #endif /* AUDIT_SESSION_DEBUG */
282 * Hash the audit session ID using a simple 32-bit mix.
284 static inline uint32_t
285 audit_session_hash(au_asid_t asid
)
287 uint32_t a
= (uint32_t) asid
;
289 a
= (a
- (a
<< 6)) ^ (a
>> 17);
290 a
= (a
- (a
<< 9)) ^ (a
<< 4);
291 a
= (a
- (a
<< 3)) ^ (a
<< 10);
298 * Do an hash lookup and find the session entry for a given ASID. Return NULL
302 audit_session_find(au_asid_t asid
)
305 au_sentry_t
*found_se
;
307 AUDIT_SENTRY_RWLOCK_ASSERT();
309 hkey
= HASH_ASID(asid
);
311 LIST_FOREACH(found_se
, &au_sentry_bucket
[hkey
], se_link
)
312 if (found_se
->se_asid
== asid
)
318 * Call kqueue knote while holding the session entry klist lock.
321 audit_session_knote(au_sentry_t
*se
, long hint
)
324 AUDIT_SE_KLIST_LOCK(se
);
325 KNOTE(&se
->se_klist
, hint
);
326 AUDIT_SE_KLIST_UNLOCK(se
);
330 * Remove the given audit_session entry from the hash table.
333 audit_session_remove(au_sentry_t
*se
)
336 au_sentry_t
*found_se
, *tmp_se
;
338 KASSERT(se
->se_refcnt
== 0, ("audit_session_remove: ref count != 0"));
340 hkey
= HASH_ASID(se
->se_asid
);
342 AUDIT_SENTRY_WLOCK();
343 LIST_FOREACH_SAFE(found_se
, &au_sentry_bucket
[hkey
], se_link
, tmp_se
) {
344 if (found_se
== se
) {
346 audit_session_knote(found_se
, NOTE_AS_CLOSE
);
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
);
357 AUDIT_SENTRY_WUNLOCK();
361 * Reference the session by incrementing the sentry ref count.
364 audit_ref_session(au_sentry_t
*se
)
368 old_val
= OSAddAtomicLong(1, &se
->se_refcnt
);
369 KASSERT(old_val
< 100000,
370 ("audit_ref_session: Too many references on session."));
374 * Decrement the sentry ref count and remove the session entry if last one.
377 audit_unref_session(au_sentry_t
*se
)
381 old_val
= OSAddAtomicLong(-1, &se
->se_refcnt
);
383 audit_session_remove(se
);
385 ("audit_unref_session: Too few references on session."));
389 * Increment the process count in the session.
392 audit_inc_procount(au_sentry_t
*se
)
396 old_val
= OSAddAtomicLong(1, &se
->se_procnt
);
397 KASSERT(old_val
<= PID_MAX
,
398 ("audit_inc_procount: proc count > PID_MAX"));
402 * Decrement the process count and add a knote if it is the last process
403 * to exit the session.
406 audit_dec_procount(au_sentry_t
*se
)
410 old_val
= OSAddAtomicLong(-1, &se
->se_procnt
);
412 audit_session_knote(se
, NOTE_AS_END
);
413 KASSERT(old_val
>= 1,
414 ("audit_dec_procount: proc count < 0"));
418 * Update the session entry and check to see if anything was updated.
420 * 0 Nothing was updated (We don't care about process preselection masks)
421 * 1 Something was updated.
424 audit_update_sentry(au_sentry_t
*se
, auditinfo_addr_t
*new_aia
)
426 auditinfo_addr_t
*aia
= &se
->se_auinfo
;
429 KASSERT(new_aia
!= &audit_default_aia
,
430 ("audit_update_sentry: Trying to update the default aia."));
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
);
438 bcopy(new_aia
, aia
, sizeof(*aia
));
444 * Return the next session ID. The range of kernel generated audit session IDs
445 * is ASSIGNED_ASID_MIN to ASSIGNED_ASID_MAX.
448 audit_session_nextid(void)
450 static uint32_t next_asid
= ASSIGNED_ASID_MIN
;
452 AUDIT_SENTRY_RWLOCK_ASSERT();
454 if (next_asid
> ASSIGNED_ASID_MAX
)
455 next_asid
= ASSIGNED_ASID_MIN
;
457 return (next_asid
++);
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.
466 static auditinfo_addr_t
*
467 audit_session_new(auditinfo_addr_t
*new_aia
, int newprocess
)
470 au_sentry_t
*se
= NULL
;
471 auditinfo_addr_t
*aia
= NULL
;
472 char nm
[LOCK_MAX_NAME
];
474 KASSERT(new_aia
!= NULL
, ("audit_session_new: new_aia == NULL"));
476 asid
= new_aia
->ai_asid
;
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
));
484 * Alloc a new session entry now so we don't wait holding the lock.
486 se
= malloc(sizeof(au_sentry_t
), M_AU_SESSION
, M_WAITOK
| M_ZERO
);
488 snprintf(nm
, sizeof(nm
), "audit se_klist_mtx %d", asid
);
489 AUDIT_SE_KLIST_LOCK_INIT(se
, nm
);
492 * Find an unique session ID, if desired.
494 AUDIT_SENTRY_WLOCK();
495 if (asid
== AU_ASSIGN_ASID
) {
497 asid
= (au_asid_t
)audit_session_nextid();
498 } while(audit_session_find(asid
) != NULL
);
500 au_sentry_t
*found_se
= NULL
;
503 * Check to see if the requested ASID is already in the
504 * hash table. If so, update it with the new auditinfo.
506 if ((found_se
= audit_session_find(asid
)) != NULL
) {
509 updated
= audit_update_sentry(found_se
, new_aia
);
510 audit_ref_session(found_se
);
512 AUDIT_SENTRY_WUNLOCK();
513 AUDIT_SE_KLIST_LOCK_DESTROY(se
);
514 free(se
, M_AU_SESSION
);
517 audit_session_knote(found_se
, NOTE_AS_UPDATE
);
520 * If this is a new process joining this session then
521 * we need to update the proc count.
524 audit_inc_procount(found_se
);
526 return (&found_se
->se_auinfo
);
531 * Start the reference and proc count at 1 to account for the process
532 * that invoked this via setaudit_addr() (or friends).
534 se
->se_refcnt
= se
->se_procnt
= 1;
537 * Populate the new session entry. Note that process masks are stored
538 * in kauth ucred so just zero them here.
540 se
->se_port
= IPC_PORT_NULL
;
541 aia
= &se
->se_auinfo
;
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
;
549 * Add it to the hash table.
551 LIST_INSERT_HEAD(&au_sentry_bucket
[HASH_ASID(asid
)], se
, se_link
);
552 AUDIT_SENTRY_WUNLOCK();
555 * Register kevents for consumers wanting events for any ASID
556 * and knote the event.
558 audit_register_kevents(se
->se_asid
, se
->se_auid
);
559 audit_session_knote(se
, NOTE_AS_START
);
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.
569 audit_session_lookup(au_asid_t asid
, auditinfo_addr_t
*ret_aia
)
571 au_sentry_t
*se
= NULL
;
573 if ((uint32_t)asid
> ASSIGNED_ASID_MAX
)
575 AUDIT_SENTRY_RLOCK();
576 if ((se
= audit_session_find(asid
)) == NULL
) {
577 AUDIT_SENTRY_RUNLOCK();
581 bcopy(&se
->se_auinfo
, ret_aia
, sizeof(*ret_aia
));
582 AUDIT_SENTRY_RUNLOCK();
588 * Add a reference to the session entry.
591 audit_session_ref(kauth_cred_t cred
)
593 auditinfo_addr_t
*aia_p
;
595 KASSERT(IS_VALID_CRED(cred
),
596 ("audit_session_ref: Invalid kauth_cred."));
598 aia_p
= cred
->cr_audit
.as_aia_p
;
600 if (IS_VALID_SESSION(aia_p
))
601 audit_ref_session(AU_SENTRY_PTR(aia_p
));
605 * Remove a reference to the session entry.
608 audit_session_unref(kauth_cred_t cred
)
610 auditinfo_addr_t
*aia_p
;
612 KASSERT(IS_VALID_CRED(cred
),
613 ("audit_session_unref: Invalid kauth_cred."));
615 aia_p
= cred
->cr_audit
.as_aia_p
;
617 if (IS_VALID_SESSION(aia_p
))
618 audit_unref_session(AU_SENTRY_PTR(aia_p
));
622 audit_session_procnew(kauth_cred_t cred
)
624 auditinfo_addr_t
*aia_p
;
626 KASSERT(IS_VALID_CRED(cred
),
627 ("audit_session_procnew: Invalid kauth_cred."));
629 aia_p
= cred
->cr_audit
.as_aia_p
;
631 if (IS_VALID_SESSION(aia_p
))
632 audit_inc_procount(AU_SENTRY_PTR(aia_p
));
636 audit_session_procexit(kauth_cred_t cred
)
638 auditinfo_addr_t
*aia_p
;
640 KASSERT(IS_VALID_CRED(cred
),
641 ("audit_session_procexit: Invalid kauth_cred."));
643 aia_p
= cred
->cr_audit
.as_aia_p
;
645 if (IS_VALID_SESSION(aia_p
))
646 audit_dec_procount(AU_SENTRY_PTR(aia_p
));
650 * Init the audit session code.
653 audit_session_init(void)
657 KASSERT((ASSIGNED_ASID_MAX
- ASSIGNED_ASID_MIN
) > PID_MAX
,
658 ("audit_session_init: ASSIGNED_ASID_MAX is not large enough."));
660 AUDIT_SENTRY_RWLOCK_INIT();
661 AUDIT_ANYAS_KLIST_LOCK_INIT();
663 au_sentry_bucket
= malloc( sizeof(struct au_sentry
) *
664 HASH_TABLE_SIZE
, M_AU_SESSION
, M_WAITOK
| M_ZERO
);
666 for (i
= 0; i
< HASH_TABLE_SIZE
; i
++)
667 LIST_INIT(&au_sentry_bucket
[i
]);
671 * Allocate a new kevent propagation list (plist).
674 audit_new_plist(void)
676 au_plisthead_t
*plhead
;
678 plhead
= malloc(sizeof(au_plisthead_t
), M_AU_EV_PLIST
, M_WAITOK
|
681 LIST_INIT(&plhead
->ph_head
);
682 AUDIT_PLIST_LOCK_INIT(plhead
);
684 return ((caddr_t
) plhead
);
688 * Destroy a kevent propagation list (plist). The anyas_klist_mtx mutex must be
689 * held by the caller.
692 audit_destroy_plist(struct knote
*anyas_kn
)
694 au_plisthead_t
*plhead
;
695 au_plist_t
*plentry
, *ple_tmp
;
696 struct kevent64_s kev
;
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"));
703 * Delete everything in the propagation list.
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
;
709 kev
.ident
= plentry
->pl_knote
->kn_id
;
710 kev
.filter
= EVFILT_SESSION
;
711 kev
.flags
= EV_DELETE
;
714 * The plist entry gets removed in rm_from_plist() which is
715 * called indirectly by kevent_register().
717 kevent_register(kq
, &kev
, NULL
);
719 AUDIT_PLIST_UNLOCK(plhead
);
724 AUDIT_PLIST_LOCK_DESTROY(plhead
);
725 free(plhead
, M_AU_EV_PLIST
);
729 * Add a knote pointer entry to the kevent propagation list.
732 audit_add_to_plist(struct knote
*anyas_kn
, struct knote
*kn
)
734 au_plisthead_t
*plhead
;
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"));
741 plentry
= malloc(sizeof(au_plist_t
), M_AU_EV_PLIST
, M_WAITOK
| M_ZERO
);
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
);
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
755 audit_rm_from_plist(struct knote
*kn
)
757 struct knote
*anyas_kn
;
758 au_plisthead_t
*plhd
;
759 au_plist_t
*plentry
, *ple_tmp
;
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
;
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
);
775 AUDIT_PLIST_UNLOCK(plhd
);
779 * The attach filter for EVFILT_SESSION.
782 audit_filt_sessionattach(struct knote
*kn
)
784 au_sentry_t
*se
= NULL
;
787 * Check flags for the events we currently support.
789 if ((kn
->kn_sfflags
& (NOTE_AS_START
| NOTE_AS_END
| NOTE_AS_CLOSE
790 | NOTE_AS_UPDATE
| NOTE_AS_ERR
)) == 0)
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
798 if (kn
->kn_id
== AS_ANY_ASID
) {
800 kn
->kn_flags
|= EV_CLEAR
;
801 kn
->kn_ptr
.p_se
= NULL
;
804 * Attach a kevent propagation list for any kevents that get
807 kn
->kn_hook
= audit_new_plist();
809 AUDIT_ANYAS_KLIST_LOCK();
810 KNOTE_ATTACH(&anyas_klist
, kn
);
811 AUDIT_ANYAS_KLIST_UNLOCK();
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.
823 * Check to make sure it is a valid ASID.
825 if (kn
->kn_id
> ASSIGNED_ASID_MAX
)
828 AUDIT_SENTRY_RLOCK();
829 se
= audit_session_find(kn
->kn_id
);
830 AUDIT_SENTRY_RUNLOCK();
834 AUDIT_SE_KLIST_LOCK(se
);
835 kn
->kn_flags
|= EV_CLEAR
;
836 kn
->kn_ptr
.p_se
= se
;
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.
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
);
851 KNOTE_ATTACH(&se
->se_klist
, kn
);
852 AUDIT_SE_KLIST_UNLOCK(se
);
859 * The detach filter for EVFILT_SESSION.
862 audit_filt_sessiondetach(struct knote
*kn
)
864 au_sentry_t
*se
= NULL
;
866 if (kn
->kn_id
== AS_ANY_ASID
) {
868 AUDIT_ANYAS_KLIST_LOCK();
869 audit_destroy_plist(kn
);
870 KNOTE_DETACH(&anyas_klist
, kn
);
871 AUDIT_ANYAS_KLIST_UNLOCK();
875 * If this knote was created by any ASID kevent then remove
876 * from kevent propagation list.
878 if (kn
->kn_hook
!= NULL
) {
879 audit_rm_from_plist(kn
);
884 * Check to see if already detached.
886 se
= kn
->kn_ptr
.p_se
;
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
);
897 * The touch filter for EVFILT_SESSION. Check for any ASID kevent updates and
898 * propagate the change.
901 audit_filt_sessiontouch(struct knote
*kn
, struct kevent64_s
*kev
, long type
)
903 struct knote
*ple_kn
;
906 au_plisthead_t
*plhead
;
908 struct kevent64_s newkev
;
912 kn
->kn_sfflags
= kev
->fflags
;
913 kn
->kn_sdata
= kev
->data
;
915 * If an any ASID kevent was updated then we may need to
916 * propagate the update.
918 if (kev
->ident
== AS_ANY_ASID
&& kn
->kn_hook
!= NULL
) {
921 * Propagate the change to each of the session kevents
922 * that were created by this any ASID kevent.
924 plhead
= (au_plisthead_t
*)kn
->kn_hook
;
925 AUDIT_PLIST_LOCK(plhead
);
926 LIST_FOREACH(plentry
, &plhead
->ph_head
, pl_link
) {
928 if ((ple_kn
= plentry
->pl_knote
) == NULL
)
930 if ((se
= ple_kn
->kn_ptr
.p_se
) == NULL
)
932 if ((kq
= ple_kn
->kn_kq
) == NULL
)
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
);
943 AUDIT_PLIST_UNLOCK(plhead
);
948 *kev
= kn
->kn_kevent
;
949 if (kn
->kn_flags
& EV_CLEAR
) {
956 KASSERT((type
== EVENT_REGISTER
|| type
== EVENT_PROCESS
),
957 ("filt_sessiontouch(): invalid type (%ld)", type
));
963 * Event filter for EVFILT_SESSION. The AUDIT_SE_KLIST_LOCK should be held
964 * by audit_session_knote().
967 audit_filt_session(struct knote
*kn
, long hint
)
969 int events
= (int)hint
;
970 au_sentry_t
*se
= kn
->kn_ptr
.p_se
;
972 if (hint
!= 0 && se
!= NULL
) {
974 if (kn
->kn_sfflags
& events
) {
975 kn
->kn_fflags
|= events
;
976 kn
->kn_data
= se
->se_auid
;
980 * If this is the last possible event for the knote,
981 * detach the knote from the audit session before the
984 if (events
& NOTE_AS_CLOSE
) {
987 * If created by any ASID kevent then remove from
990 if (kn
->kn_hook
!= NULL
) {
991 audit_rm_from_plist(kn
);
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
);
1002 return (kn
->kn_fflags
!= 0);
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.
1011 audit_register_kevents(uint32_t asid
, uint32_t auid
)
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
;
1022 kev
.filter
= EVFILT_SESSION
;
1023 kev
.flags
= kn
->kn_flags
| EV_ADD
| EV_ENABLE
| EV_ANY_ASID
;
1024 kev
.fflags
= kn
->kn_sfflags
;
1026 kev
.udata
= kn
->kn_kevent
.udata
;
1029 * Save the knote ptr for this "any ASID" knote for the attach
1032 kev
.ext
[0] = (uint64_t)((uintptr_t)kn
);
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.
1039 err
= kevent_register(kq
, &kev
, NULL
);
1041 kn
->kn_fflags
|= NOTE_AS_ERR
;
1043 AUDIT_ANYAS_KLIST_UNLOCK();
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
1052 audit_session_setaia(proc_t p
, auditinfo_addr_t
*aia_p
, int newprocess
)
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
;
1060 * If this is going to modify an existing session then do some
1063 if (audit_session_lookup(aia_p
->ai_asid
, &caia
) == 0) {
1066 * If the current audit ID is not the default then it is
1069 if (caia
.ai_auid
!= AU_DEFAUDITID
&&
1070 caia
.ai_auid
!= aia_p
->ai_auid
)
1074 * If the current termid is not the default then it is
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
) )) )
1086 /* The audit flags are immutable. */
1087 if (caia
.ai_flags
!= aia_p
->ai_flags
)
1090 /* The audit masks are mutable. */
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
);
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.
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.
1113 bcopy(&as
, &tmp_as
, sizeof(tmp_as
));
1114 my_new_cred
= kauth_cred_setauditinfo(my_cred
, &tmp_as
);
1116 if (my_cred
!= my_new_cred
) {
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.
1123 if (p
->p_ucred
!= my_cred
) {
1125 audit_session_unref(my_new_cred
);
1126 kauth_cred_unref(&my_new_cred
);
1128 my_cred
= kauth_cred_proc_ref(p
);
1131 p
->p_ucred
= my_new_cred
;
1135 * Drop old proc reference or our extra reference.
1137 kauth_cred_unref(&my_cred
);
1140 audit_session_unref(my_new_cred
);
1143 * Propagate the change from the process to the Mach task.
1145 set_security_token(p
);
1151 * audit_session_self (system call)
1153 * Description: Obtain a Mach send right for the current session.
1155 * Parameters: p Process calling audit_session_self().
1157 * Returns: *ret_port Named Mach send right, which may be
1158 * MACH_PORT_NULL in the failure case.
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.
1166 audit_session_self(proc_t p
, __unused
struct audit_session_self_args
*uap
,
1167 mach_port_name_t
*ret_port
)
1169 ipc_port_t sendport
= IPC_PORT_NULL
;
1170 kauth_cred_t cred
= NULL
;
1171 auditinfo_addr_t
*aia_p
;
1175 cred
= kauth_cred_proc_ref(p
);
1176 if (!IS_VALID_CRED(cred
)) {
1181 aia_p
= cred
->cr_audit
.as_aia_p
;
1182 if (!IS_VALID_SESSION(aia_p
)) {
1187 se
= AU_SENTRY_PTR(aia_p
);
1190 * Processes that join using this mach port will inherit this process'
1191 * pre-selection masks.
1193 if (se
->se_port
== IPC_PORT_NULL
)
1194 bcopy(&cred
->cr_audit
.as_mask
, &se
->se_mask
,
1195 sizeof(se
->se_mask
));
1197 if ((sendport
= audit_session_mksend(aia_p
, &se
->se_port
)) == NULL
) {
1198 /* failed to alloc new port */
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.
1208 audit_ref_session(se
);
1213 kauth_cred_unref(&cred
);
1215 *ret_port
= ipc_port_copyout_send(sendport
,
1216 get_task_ipcspace(p
->task
));
1218 *ret_port
= MACH_PORT_NULL
;
1224 audit_session_portaiadestroy(struct auditinfo_addr
*port_aia_p
)
1228 KASSERT(port_aia_p
!= NULL
,
1229 ("audit_session_infodestroy: port_aia_p = NULL"));
1231 se
= AU_SENTRY_PTR(port_aia_p
);
1234 * Drop the reference added in audit_session_self().
1237 se
->se_port
= IPC_PORT_NULL
;
1238 audit_unref_session(se
);
1244 audit_session_join_internal(proc_t p
, ipc_port_t port
, au_asid_t
*new_asid
)
1246 auditinfo_addr_t
*port_aia_p
, *old_aia_p
;
1247 kauth_cred_t cred
= NULL
;
1251 *new_asid
= AU_DEFAUDITSID
;
1253 if ((port_aia_p
= audit_session_porttoaia(port
)) == NULL
) {
1257 *new_asid
= port_aia_p
->ai_asid
;
1259 cred
= kauth_cred_proc_ref(p
);
1260 if (!IS_VALID_CRED(cred
)) {
1261 kauth_cred_unref(&cred
);
1265 old_aia_p
= cred
->cr_audit
.as_aia_p
;
1266 old_asid
= old_aia_p
->ai_asid
;
1269 * Add process in if not already in the session.
1271 if (*new_asid
!= old_asid
) {
1272 audit_session_setaia(p
, port_aia_p
, 1);
1274 * If this process was in a valid session before then we
1275 * need to decrement the process count of the session it
1278 if (IS_VALID_SESSION(old_aia_p
))
1279 audit_dec_procount(AU_SENTRY_PTR(old_aia_p
));
1281 kauth_cred_unref(&cred
);
1284 if (port
!= IPC_PORT_NULL
)
1285 ipc_port_release_send(port
);
1291 * audit_session_spawnjoin
1293 * Description: posix_spawn() interface to audit_session_join_internal().
1295 * Returns: 0 Success
1296 * EINVAL Invalid Mach port name.
1297 * ESRCH Invalid calling process/cred.
1300 audit_session_spawnjoin(proc_t p
, ipc_port_t port
)
1304 return (audit_session_join_internal(p
, port
, &new_asid
));
1308 * audit_session_join (system call)
1310 * Description: Join the session for a given Mach port send right.
1312 * Parameters: p Process calling session join.
1313 * uap->port A Mach send right.
1315 * Returns: *ret_asid Audit session ID of new session, which may
1316 * be AU_DEFAUDITSID in the failure case.
1319 * EINVAL Invalid Mach port name.
1320 * ESRCH Invalid calling process/cred.
1323 audit_session_join(proc_t p
, struct audit_session_join_args
*uap
,
1324 au_asid_t
*ret_asid
)
1326 ipc_port_t port
= IPC_PORT_NULL
;
1327 mach_port_name_t send
= uap
->port
;
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
;
1336 err
= audit_session_join_internal(p
, port
, ret_asid
);
1344 audit_session_self(proc_t p
, struct audit_session_self_args
*uap
,
1345 mach_port_name_t
*ret_port
)
1347 #pragma unused(p, uap, ret_port)
1353 audit_session_join(proc_t p
, struct audit_session_join_args
*uap
,
1354 au_asid_t
*ret_asid
)
1356 #pragma unused(p, uap, ret_asid)
1361 #endif /* CONFIG_AUDIT */