]> git.saurik.com Git - apple/xnu.git/blame - bsd/security/audit/audit_session.c
xnu-6153.141.1.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
6d2010ae
A
30#include <stdarg.h>
31
b0d623f7 32#include <sys/kernel.h>
6d2010ae 33#include <sys/fcntl.h>
b0d623f7 34#include <sys/kauth.h>
6d2010ae
A
35#include <sys/conf.h>
36#include <sys/poll.h>
cb323159 37#include <sys/priv.h>
b0d623f7 38#include <sys/queue.h>
6d2010ae 39#include <sys/signalvar.h>
b0d623f7
A
40#include <sys/syscall.h>
41#include <sys/sysent.h>
42#include <sys/sysproto.h>
43#include <sys/systm.h>
44#include <sys/ucred.h>
6d2010ae
A
45#include <sys/user.h>
46
47#include <miscfs/devfs/devfs.h>
b0d623f7
A
48
49#include <libkern/OSAtomic.h>
50
51#include <bsm/audit.h>
6d2010ae
A
52#include <bsm/audit_internal.h>
53#include <bsm/audit_kevents.h>
54
b0d623f7
A
55#include <security/audit/audit.h>
56#include <security/audit/audit_bsd.h>
6d2010ae 57#include <security/audit/audit_ioctl.h>
b0d623f7
A
58#include <security/audit/audit_private.h>
59
60#include <vm/vm_protos.h>
6d2010ae 61#include <mach/mach_port.h>
b0d623f7
A
62#include <kern/audit_sessionport.h>
63
6d2010ae 64#include <libkern/OSDebug.h>
b0d623f7
A
65
66/*
67 * Audit Session Entry. This is treated as an object with public and private
68 * data. The se_auinfo field is the only information that is public and
69 * needs to be the first entry.
70 */
71struct au_sentry {
0a7de745
A
72 auditinfo_addr_t se_auinfo; /* Public audit session data. */
73#define se_asid se_auinfo.ai_asid
74#define se_auid se_auinfo.ai_auid
75#define se_mask se_auinfo.ai_mask
76#define se_termid se_auinfo.ai_termid
77#define se_flags se_auinfo.ai_flags
78
79 long se_refcnt; /* Reference count. */
80 long se_procnt; /* Processes in session. */
81 ipc_port_t se_port; /* Session port. */
82 LIST_ENTRY(au_sentry) se_link; /* Hash bucket link list (1) */
b0d623f7
A
83};
84typedef struct au_sentry au_sentry_t;
85
0a7de745 86#define AU_SENTRY_PTR(aia_p) ((au_sentry_t *)(aia_p))
b0d623f7 87
6d2010ae 88/*
0a7de745 89 * The default au_sentry/auditinfo_addr entry for ucred.
6d2010ae
A
90 */
91
92static au_sentry_t audit_default_se = {
93 .se_auinfo = {
0a7de745
A
94 .ai_auid = AU_DEFAUDITID,
95 .ai_asid = AU_DEFAUDITSID,
96 .ai_termid = { .at_type = AU_IPv4, },
6d2010ae 97 },
0a7de745 98 .se_refcnt = 1,
6d2010ae
A
99 .se_procnt = 1,
100};
101
102struct auditinfo_addr *audit_default_aia_p = &audit_default_se.se_auinfo;
103
cb323159
A
104/* Copied from <ipc/ipc_object.h> */
105#define IPC_KMSG_FLAGS_ALLOW_IMMOVABLE_SEND 0x1
6d2010ae 106kern_return_t ipc_object_copyin(ipc_space_t, mach_port_name_t,
cb323159 107 mach_msg_type_name_t, ipc_port_t *, mach_port_context_t, mach_msg_guard_flags_t *, uint32_t);
6d2010ae
A
108void ipc_port_release_send(ipc_port_t);
109
110#if CONFIG_AUDIT
111
112
113/*
114 * Currently the hash table is a fixed size.
115 */
0a7de745
A
116#define HASH_TABLE_SIZE 97
117#define HASH_ASID(asid) (audit_session_hash(asid) % HASH_TABLE_SIZE)
6d2010ae 118
0a7de745 119static struct rwlock se_entry_lck; /* (1) lock for se_link above */
b0d623f7
A
120
121LIST_HEAD(au_sentry_head, au_sentry);
122static struct au_sentry_head *au_sentry_bucket = NULL;
123
6d2010ae
A
124#define AU_HISTORY_LOGGING 0
125#if AU_HISTORY_LOGGING
126typedef enum au_history_event {
127 AU_HISTORY_EVENT_UNKNOWN = 0,
128 AU_HISTORY_EVENT_REF = 1,
129 AU_HISTORY_EVENT_UNREF = 2,
130 AU_HISTORY_EVENT_BIRTH = 3,
131 AU_HISTORY_EVENT_DEATH = 4,
132 AU_HISTORY_EVENT_FIND = 5
133} au_history_event_t;
134
135#define AU_HISTORY_MAX_STACK_DEPTH 8
136
137struct au_history {
0a7de745
A
138 struct au_sentry *ptr;
139 struct au_sentry se;
140 void *stack[AU_HISTORY_MAX_STACK_DEPTH];
141 unsigned int stack_depth;
142 au_history_event_t event;
6d2010ae
A
143};
144
145static struct au_history *au_history;
0a7de745
A
146static size_t au_history_size = 65536;
147static unsigned int au_history_index;
6d2010ae
A
148
149static inline unsigned int
150au_history_entries(void)
151{
0a7de745 152 if (au_history_index >= au_history_size) {
6d2010ae 153 return au_history_size;
0a7de745 154 } else {
6d2010ae 155 return au_history_index;
0a7de745 156 }
6d2010ae
A
157}
158
159static inline void
160au_history_record(au_sentry_t *se, au_history_event_t event)
161{
162 struct au_history *p;
163 unsigned int i;
164
165 i = OSAddAtomic(1, &au_history_index);
166 p = &au_history[i % au_history_size];
167
168 bzero(p, sizeof(*p));
169 p->event = event;
170 bcopy(se, &p->se, sizeof(p->se));
171 p->stack_depth = OSBacktrace(&p->stack[0], AU_HISTORY_MAX_STACK_DEPTH);
172 p->ptr = se;
173}
174#else
175#define au_history_record(se, event) do {} while (0)
176#endif
177
178MALLOC_DEFINE(M_AU_SESSION, "audit_session", "Audit session data");
179
0a7de745
A
180static void audit_ref_session(au_sentry_t *se);
181static void audit_unref_session(au_sentry_t *se);
6d2010ae 182
0a7de745 183static void audit_session_event(int event, auditinfo_addr_t *aia_p);
6d2010ae
A
184
185/*
186 * Audit session device.
187 */
188
189static MALLOC_DEFINE(M_AUDIT_SDEV, "audit_sdev", "Audit sdevs");
190static MALLOC_DEFINE(M_AUDIT_SDEV_ENTRY, "audit_sdevent",
191 "Audit sdev entries and buffers");
192
193/*
194 * Default audit sdev buffer parameters.
195 */
0a7de745
A
196#define AUDIT_SDEV_QLIMIT_DEFAULT 128
197#define AUDIT_SDEV_QLIMIT_MIN 1
198#define AUDIT_SDEV_QLIMIT_MAX 1024
6d2010ae 199
b0d623f7 200/*
6d2010ae 201 * Entry structure.
b0d623f7 202 */
0a7de745
A
203struct audit_sdev_entry {
204 void *ase_record;
205 u_int ase_record_len;
206 TAILQ_ENTRY(audit_sdev_entry) ase_queue;
b0d623f7 207};
b0d623f7 208
6d2010ae 209/*
0a7de745 210 * Per audit sdev structure.
6d2010ae
A
211 */
212
213struct audit_sdev {
0a7de745 214 int asdev_open;
6d2010ae 215
0a7de745
A
216#define AUDIT_SDEV_ASYNC 0x00000001
217#define AUDIT_SDEV_NBIO 0x00000002
6d2010ae 218
0a7de745
A
219#define AUDIT_SDEV_ALLSESSIONS 0x00010000
220 u_int asdev_flags;
6d2010ae 221
0a7de745
A
222 struct selinfo asdev_selinfo;
223 pid_t asdev_sigio;
6d2010ae 224
0a7de745
A
225 au_id_t asdev_auid;
226 au_asid_t asdev_asid;
6d2010ae
A
227
228 /* Per-sdev mutex for most fields in this struct. */
0a7de745 229 struct mtx asdev_mtx;
6d2010ae
A
230
231 /*
232 * Per-sdev sleep lock serializing user-generated reads and
233 * flushes. uiomove() is called to copy out the current head
234 * record's data whie the record remains in the queue, so we
235 * prevent other threads from removing it using this lock.
236 */
0a7de745 237 struct slck asdev_sx;
6d2010ae
A
238
239 /*
0a7de745 240 * Condition variable to signal when data has been delivered to
6d2010ae
A
241 * a sdev.
242 */
0a7de745 243 struct cv asdev_cv;
6d2010ae
A
244
245 /* Count and bound of records in the queue. */
0a7de745
A
246 u_int asdev_qlen;
247 u_int asdev_qlimit;
6d2010ae
A
248
249 /* The number of bytes of data across all records. */
0a7de745
A
250 u_int asdev_qbyteslen;
251
252 /*
6d2010ae
A
253 * The amount read so far of the first record in the queue.
254 * (The number of bytes available for reading in the queue is
255 * qbyteslen - qoffset.)
256 */
0a7de745 257 u_int asdev_qoffset;
6d2010ae
A
258
259 /*
260 * Per-sdev operation statistics.
261 */
0a7de745
A
262 u_int64_t asdev_inserts; /* Records added. */
263 u_int64_t asdev_reads; /* Records read. */
264 u_int64_t asdev_drops; /* Records dropped. */
6d2010ae
A
265
266 /*
267 * Current pending record list. This is protected by a
268 * combination of asdev_mtx and asdev_sx. Note that both
269 * locks are required to remove a record from the head of the
270 * queue, as an in-progress read may sleep while copying and,
271 * therefore, cannot hold asdev_mtx.
272 */
0a7de745 273 TAILQ_HEAD(, audit_sdev_entry) asdev_queue;
6d2010ae
A
274
275 /* Global sdev list. */
0a7de745 276 TAILQ_ENTRY(audit_sdev) asdev_list;
b0d623f7 277};
b0d623f7 278
0a7de745
A
279#define AUDIT_SDEV_LOCK(asdev) mtx_lock(&(asdev)->asdev_mtx)
280#define AUDIT_SDEV_LOCK_ASSERT(asdev) mtx_assert(&(asdev)->asdev_mtx, \
281 MA_OWNED)
282#define AUDIT_SDEV_LOCK_DESTROY(asdev) mtx_destroy(&(asdev)->asdev_mtx)
283#define AUDIT_SDEV_LOCK_INIT(asdev) mtx_init(&(asdev)->asdev_mtx, \
284 "audit_sdev_mtx", NULL, MTX_DEF)
285#define AUDIT_SDEV_UNLOCK(asdev) mtx_unlock(&(asdev)->asdev_mtx)
286#define AUDIT_SDEV_MTX(asdev) (&(asdev)->asdev_mtx)
287
288#define AUDIT_SDEV_SX_LOCK_DESTROY(asd) slck_destroy(&(asd)->asdev_sx)
289#define AUDIT_SDEV_SX_LOCK_INIT(asd) slck_init(&(asd)->asdev_sx, \
290 "audit_sdev_sx")
291#define AUDIT_SDEV_SX_XLOCK_ASSERT(asd) slck_assert(&(asd)->asdev_sx, \
292 SA_XLOCKED)
293#define AUDIT_SDEV_SX_XLOCK_SIG(asd) slck_lock_sig(&(asd)->asdev_sx)
294#define AUDIT_SDEV_SX_XUNLOCK(asd) slck_unlock(&(asd)->asdev_sx)
b0d623f7 295
6d2010ae
A
296/*
297 * Cloning variables and constants.
298 */
0a7de745
A
299#define AUDIT_SDEV_NAME "auditsessions"
300#define MAX_AUDIT_SDEVS 32
6d2010ae
A
301
302static int audit_sdev_major;
303static void *devnode;
304
305/*
306 * Global list of audit sdevs. The list is protected by a rw lock.
307 * Individaul record queues are protected by per-sdev locks. These
0a7de745 308 * locks synchronize between threads walking the list to deliver to
6d2010ae
A
309 * individual sdevs and adds/removes of sdevs.
310 */
311static TAILQ_HEAD(, audit_sdev) audit_sdev_list;
0a7de745 312static struct rwlock audit_sdev_lock;
6d2010ae 313
0a7de745
A
314#define AUDIT_SDEV_LIST_LOCK_INIT() rw_init(&audit_sdev_lock, \
315 "audit_sdev_list_lock")
316#define AUDIT_SDEV_LIST_RLOCK() rw_rlock(&audit_sdev_lock)
317#define AUDIT_SDEV_LIST_RUNLOCK() rw_runlock(&audit_sdev_lock)
318#define AUDIT_SDEV_LIST_WLOCK() rw_wlock(&audit_sdev_lock)
319#define AUDIT_SDEV_LIST_WLOCK_ASSERT() rw_assert(&audit_sdev_lock, \
320 RA_WLOCKED)
321#define AUDIT_SDEV_LIST_WUNLOCK() rw_wunlock(&audit_sdev_lock)
6d2010ae
A
322
323/*
324 * dev_t doesn't have a pointer for "softc" data so we have to keep track of
325 * it with the following global array (indexed by the minor number).
326 *
327 * XXX We may want to dynamically grow this as need.
328 */
0a7de745 329static struct audit_sdev *audit_sdev_dtab[MAX_AUDIT_SDEVS];
b0d623f7
A
330
331/*
6d2010ae 332 * Special device methods and definition.
b0d623f7 333 */
0a7de745
A
334static open_close_fcn_t audit_sdev_open;
335static open_close_fcn_t audit_sdev_close;
336static read_write_fcn_t audit_sdev_read;
337static ioctl_fcn_t audit_sdev_ioctl;
338static select_fcn_t audit_sdev_poll;
6d2010ae
A
339
340static struct cdevsw audit_sdev_cdevsw = {
341 .d_open = audit_sdev_open,
342 .d_close = audit_sdev_close,
343 .d_read = audit_sdev_read,
344 .d_write = eno_rdwrt,
345 .d_ioctl = audit_sdev_ioctl,
346 .d_stop = eno_stop,
347 .d_reset = eno_reset,
348 .d_ttys = NULL,
349 .d_select = audit_sdev_poll,
350 .d_mmap = eno_mmap,
351 .d_strategy = eno_strat,
352 .d_type = 0
b0d623f7
A
353};
354
355/*
6d2010ae
A
356 * Global statistics on audit sdevs.
357 */
0a7de745
A
358static int audit_sdev_count; /* Current number of sdevs. */
359static u_int64_t audit_sdev_ever; /* Sdevs ever allocated. */
360static u_int64_t audit_sdev_records; /* Total records seen. */
361static u_int64_t audit_sdev_drops; /* Global record drop count. */
6d2010ae
A
362
363static int audit_sdev_init(void);
b0d623f7 364
0a7de745
A
365#define AUDIT_SENTRY_RWLOCK_INIT() rw_init(&se_entry_lck, \
366 "se_entry_lck")
367#define AUDIT_SENTRY_RLOCK() rw_rlock(&se_entry_lck)
368#define AUDIT_SENTRY_WLOCK() rw_wlock(&se_entry_lck)
369#define AUDIT_SENTRY_RWLOCK_ASSERT() rw_assert(&se_entry_lck, RA_LOCKED)
370#define AUDIT_SENTRY_RUNLOCK() rw_runlock(&se_entry_lck)
371#define AUDIT_SENTRY_WUNLOCK() rw_wunlock(&se_entry_lck)
b0d623f7 372
6d2010ae
A
373/* Access control on the auditinfo_addr.ai_flags member. */
374static uint64_t audit_session_superuser_set_sflags_mask;
375static uint64_t audit_session_superuser_clear_sflags_mask;
376static uint64_t audit_session_member_set_sflags_mask;
377static uint64_t audit_session_member_clear_sflags_mask;
0a7de745
A
378SYSCTL_NODE(, OID_AUTO, audit, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "Audit controls");
379SYSCTL_NODE(_audit, OID_AUTO, session, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "Audit sessions");
6d2010ae
A
380SYSCTL_QUAD(_audit_session, OID_AUTO, superuser_set_sflags_mask, CTLFLAG_RW | CTLFLAG_LOCKED,
381 &audit_session_superuser_set_sflags_mask,
382 "Audit session flags settable by superuser");
383SYSCTL_QUAD(_audit_session, OID_AUTO, superuser_clear_sflags_mask, CTLFLAG_RW | CTLFLAG_LOCKED,
384 &audit_session_superuser_clear_sflags_mask,
385 "Audit session flags clearable by superuser");
386SYSCTL_QUAD(_audit_session, OID_AUTO, member_set_sflags_mask, CTLFLAG_RW | CTLFLAG_LOCKED,
387 &audit_session_member_set_sflags_mask,
388 "Audit session flags settable by a session member");
389SYSCTL_QUAD(_audit_session, OID_AUTO, member_clear_sflags_mask, CTLFLAG_RW | CTLFLAG_LOCKED,
390 &audit_session_member_clear_sflags_mask,
391 "Audit session flags clearable by a session member");
392
743345f9
A
393extern int set_security_token_task_internal(proc_t p, void *task);
394
0a7de745
A
395#define AUDIT_SESSION_DEBUG 0
396#if AUDIT_SESSION_DEBUG
6d2010ae 397/*
0a7de745 398 * The following is debugging code that can be used to get a snapshot of the
6d2010ae
A
399 * session state. The audit session information is read out using sysctl:
400 *
401 * error = sysctlbyname("kern.audit_session_debug", buffer_ptr, &buffer_len,
0a7de745 402 * NULL, 0);
6d2010ae 403 */
b0d623f7
A
404#include <kern/kalloc.h>
405
6d2010ae
A
406/*
407 * The per session record structure for the snapshot data.
408 */
b0d623f7 409struct au_sentry_debug {
0a7de745
A
410 auditinfo_addr_t se_auinfo;
411 int64_t se_refcnt; /* refereence count */
412 int64_t se_procnt; /* process count */
413 int64_t se_ptcnt; /* process count from
414 * proc table */
b0d623f7
A
415};
416typedef struct au_sentry_debug au_sentry_debug_t;
417
418static int audit_sysctl_session_debug(struct sysctl_oid *oidp, void *arg1,
419 int arg2, struct sysctl_req *req);
420
6d2010ae
A
421SYSCTL_PROC(_kern, OID_AUTO, audit_session_debug, CTLFLAG_RD | CTLFLAG_LOCKED,
422 NULL, 0, audit_sysctl_session_debug, "S,audit_session_debug",
b0d623f7
A
423 "Current session debug info for auditing.");
424
425/*
6d2010ae
A
426 * Callouts for proc_interate() which is used to reconcile the audit session
427 * proc state information with the proc table. We get everything we need
428 * in the filterfn while the proc_lock() is held so we really don't need the
429 * callout() function.
430 */
0a7de745 431static int
6d2010ae
A
432audit_session_debug_callout(__unused proc_t p, __unused void *arg)
433{
0a7de745 434 return PROC_RETURNED_DONE;
6d2010ae
A
435}
436
437static int
438audit_session_debug_filterfn(proc_t p, void *st)
439{
0a7de745 440 kauth_cred_t cred = p->p_ucred;
6d2010ae
A
441 auditinfo_addr_t *aia_p = cred->cr_audit.as_aia_p;
442 au_sentry_debug_t *sed_tab = (au_sentry_debug_t *) st;
443 au_sentry_debug_t *sdtp;
444 au_sentry_t *se;
445
446 if (IS_VALID_SESSION(aia_p)) {
447 sdtp = &sed_tab[0];
448 do {
449 if (aia_p->ai_asid == sdtp->se_asid) {
450 sdtp->se_ptcnt++;
451
452 /* Do some santy checks. */
453 se = AU_SENTRY_PTR(aia_p);
454 if (se->se_refcnt != sdtp->se_refcnt) {
455 sdtp->se_refcnt =
456 (int64_t)se->se_refcnt;
457 }
458 if (se->se_procnt != sdtp->se_procnt) {
459 sdtp->se_procnt =
460 (int64_t)se->se_procnt;
461 }
462 break;
463 }
464 sdtp++;
465 } while (sdtp->se_asid != 0 && sdtp->se_auid != 0);
466 } else {
467 /* add it to the default sesison */
468 sed_tab->se_ptcnt++;
469 }
470
0a7de745 471 return 0;
6d2010ae
A
472}
473
474/*
475 * Copy out the session debug info via the sysctl interface.
b0d623f7 476 *
b0d623f7
A
477 */
478static int
479audit_sysctl_session_debug(__unused struct sysctl_oid *oidp,
480 __unused void *arg1, __unused int arg2, struct sysctl_req *req)
481{
482 au_sentry_t *se;
483 au_sentry_debug_t *sed_tab, *next_sed;
484 int i, entry_cnt = 0;
485 size_t sz;
486 int err = 0;
487
488 /*
489 * This provides a read-only node.
490 */
0a7de745
A
491 if (req->newptr != USER_ADDR_NULL) {
492 return EPERM;
493 }
b0d623f7
A
494
495 /*
496 * Walk the audit session hash table to determine the size.
497 */
498 AUDIT_SENTRY_RLOCK();
0a7de745 499 for (i = 0; i < HASH_TABLE_SIZE; i++) {
b0d623f7 500 LIST_FOREACH(se, &au_sentry_bucket[i], se_link)
0a7de745
A
501 if (se != NULL) {
502 entry_cnt++;
503 }
504 }
b0d623f7 505
6d2010ae 506 entry_cnt++; /* add one for the default entry */
b0d623f7 507 /*
0a7de745 508 * If just querying then return the space required. There is an
b0d623f7
A
509 * obvious race condition here so we just fudge this by 3 in case
510 * the audit session table grows.
511 */
512 if (req->oldptr == USER_ADDR_NULL) {
513 req->oldidx = (entry_cnt + 3) * sizeof(au_sentry_debug_t);
514 AUDIT_SENTRY_RUNLOCK();
0a7de745 515 return 0;
b0d623f7
A
516 }
517
518 /*
519 * Alloc a temporary buffer.
520 */
521 if (req->oldlen < (entry_cnt * sizeof(au_sentry_debug_t))) {
522 AUDIT_SENTRY_RUNLOCK();
0a7de745 523 return ENOMEM;
b0d623f7
A
524 }
525 /*
526 * We hold the lock over the alloc since we don't want the table to
527 * grow on us. Therefore, use the non-blocking version of kalloc().
528 */
529 sed_tab = (au_sentry_debug_t *)kalloc_noblock(entry_cnt *
530 sizeof(au_sentry_debug_t));
531 if (sed_tab == NULL) {
532 AUDIT_SENTRY_RUNLOCK();
0a7de745 533 return ENOMEM;
b0d623f7
A
534 }
535 bzero(sed_tab, entry_cnt * sizeof(au_sentry_debug_t));
536
537 /*
538 * Walk the audit session hash table and build the record array.
539 */
540 sz = 0;
541 next_sed = sed_tab;
6d2010ae 542 /* add the first entry for processes not tracked in sessions. */
0a7de745 543 bcopy(audit_default_aia_p, &next_sed->se_auinfo, sizeof(au_sentry_t));
6d2010ae
A
544 next_sed->se_refcnt = (int64_t)audit_default_se.se_refcnt;
545 next_sed->se_procnt = (int64_t)audit_default_se.se_procnt;
546 next_sed++;
547 sz += sizeof(au_sentry_debug_t);
0a7de745 548 for (i = 0; i < HASH_TABLE_SIZE; i++) {
b0d623f7
A
549 LIST_FOREACH(se, &au_sentry_bucket[i], se_link) {
550 if (se != NULL) {
6d2010ae
A
551 next_sed->se_auinfo = se->se_auinfo;
552 next_sed->se_refcnt = (int64_t)se->se_refcnt;
553 next_sed->se_procnt = (int64_t)se->se_procnt;
b0d623f7
A
554 next_sed++;
555 sz += sizeof(au_sentry_debug_t);
556 }
557 }
558 }
559 AUDIT_SENTRY_RUNLOCK();
560
6d2010ae 561 /* Reconcile with the process table. */
cb323159 562 proc_iterate(PROC_ALLPROCLIST | PROC_ZOMBPROCLIST,
6d2010ae
A
563 audit_session_debug_callout, NULL,
564 audit_session_debug_filterfn, (void *)&sed_tab[0]);
565
566
b0d623f7
A
567 req->oldlen = sz;
568 err = SYSCTL_OUT(req, sed_tab, sz);
569 kfree(sed_tab, entry_cnt * sizeof(au_sentry_debug_t));
570
0a7de745 571 return err;
b0d623f7
A
572}
573
574#endif /* AUDIT_SESSION_DEBUG */
575
6d2010ae
A
576/*
577 * Create and commit a session audit event. The proc and se arguments needs to
578 * be that of the subject and not necessarily the current process.
579 */
580static void
581audit_session_event(int event, auditinfo_addr_t *aia_p)
582{
583 struct kaudit_record *ar;
584
585 KASSERT(AUE_SESSION_START == event || AUE_SESSION_UPDATE == event ||
586 AUE_SESSION_END == event || AUE_SESSION_CLOSE == event,
587 ("audit_session_event: invalid event: %d", event));
588
0a7de745 589 if (NULL == aia_p) {
6d2010ae 590 return;
0a7de745 591 }
6d2010ae 592
0a7de745 593 /*
6d2010ae 594 * Create a new audit record. The record will contain the subject
0a7de745 595 * ruid, rgid, egid, pid, auid, asid, amask, and term_addr
6d2010ae
A
596 * (implicitly added by audit_new).
597 */
598 ar = audit_new(event, PROC_NULL, /* Not used */ NULL);
0a7de745 599 if (NULL == ar) {
6d2010ae 600 return;
0a7de745 601 }
6d2010ae
A
602
603 /*
604 * Audit session events are always generated because they are used
605 * by some userland consumers so just set the preselect flag.
606 */
607 ar->k_ar_commit |= AR_PRESELECT_FILTER;
608
0a7de745 609 /*
6d2010ae
A
610 * Populate the subject information. Note that the ruid, rgid,
611 * egid, and pid values are incorrect. We only need the auditinfo_addr
612 * information.
613 */
614 ar->k_ar.ar_subj_ruid = 0;
615 ar->k_ar.ar_subj_rgid = 0;
616 ar->k_ar.ar_subj_egid = 0;
617 ar->k_ar.ar_subj_pid = 0;
618 ar->k_ar.ar_subj_auid = aia_p->ai_auid;
619 ar->k_ar.ar_subj_asid = aia_p->ai_asid;
620 bcopy(&aia_p->ai_termid, &ar->k_ar.ar_subj_term_addr,
621 sizeof(struct au_tid_addr));
622
623 /* Add the audit masks to the record. */
624 ar->k_ar.ar_arg_amask.am_success = aia_p->ai_mask.am_success;
625 ar->k_ar.ar_arg_amask.am_failure = aia_p->ai_mask.am_failure;
626 ARG_SET_VALID(ar, ARG_AMASK);
627
628 /* Add the audit session flags to the record. */
0a7de745 629 ar->k_ar.ar_arg_value64 = aia_p->ai_flags;
6d2010ae
A
630 ARG_SET_VALID(ar, ARG_VALUE64);
631
632
633 /* Commit the record to the queue. */
634 audit_commit(ar, 0, 0);
635}
636
b0d623f7
A
637/*
638 * Hash the audit session ID using a simple 32-bit mix.
639 */
0a7de745 640static inline uint32_t
b0d623f7
A
641audit_session_hash(au_asid_t asid)
642{
643 uint32_t a = (uint32_t) asid;
644
0a7de745 645 a = (a - (a << 6)) ^ (a >> 17);
b0d623f7
A
646 a = (a - (a << 9)) ^ (a << 4);
647 a = (a - (a << 3)) ^ (a << 10);
648 a = a ^ (a >> 15);
0a7de745
A
649
650 return a;
b0d623f7
A
651}
652
653/*
654 * Do an hash lookup and find the session entry for a given ASID. Return NULL
0a7de745
A
655 * if not found. If the session is found then audit_session_find takes a
656 * reference.
b0d623f7
A
657 */
658static au_sentry_t *
659audit_session_find(au_asid_t asid)
660{
0a7de745
A
661 uint32_t hkey;
662 au_sentry_t *found_se;
b0d623f7
A
663
664 AUDIT_SENTRY_RWLOCK_ASSERT();
665
666 hkey = HASH_ASID(asid);
667
668 LIST_FOREACH(found_se, &au_sentry_bucket[hkey], se_link)
0a7de745
A
669 if (found_se->se_asid == asid) {
670 au_history_record(found_se, AU_HISTORY_EVENT_FIND);
671 audit_ref_session(found_se);
672 return found_se;
673 }
674 return NULL;
b0d623f7
A
675}
676
b0d623f7
A
677/*
678 * Remove the given audit_session entry from the hash table.
679 */
680static void
681audit_session_remove(au_sentry_t *se)
682{
0a7de745
A
683 uint32_t hkey;
684 au_sentry_t *found_se, *tmp_se;
b0d623f7 685
6d2010ae 686 au_history_record(se, AU_HISTORY_EVENT_DEATH);
0a7de745 687 KASSERT(se->se_refcnt == 0, ("audit_session_remove: ref count != 0"));
6d2010ae 688 KASSERT(se != &audit_default_se,
0a7de745 689 ("audit_session_remove: removing default session"));
b0d623f7
A
690
691 hkey = HASH_ASID(se->se_asid);
692
693 AUDIT_SENTRY_WLOCK();
6d2010ae
A
694 /*
695 * Check and see if someone got a reference before we got the lock.
696 */
697 if (se->se_refcnt != 0) {
698 AUDIT_SENTRY_WUNLOCK();
699 return;
700 }
701
702 audit_session_portdestroy(&se->se_port);
b0d623f7
A
703 LIST_FOREACH_SAFE(found_se, &au_sentry_bucket[hkey], se_link, tmp_se) {
704 if (found_se == se) {
6d2010ae
A
705 /*
706 * Generate an audit event to notify userland of the
707 * session close.
708 */
709 audit_session_event(AUE_SESSION_CLOSE,
710 &found_se->se_auinfo);
b0d623f7
A
711
712 LIST_REMOVE(found_se, se_link);
713 AUDIT_SENTRY_WUNLOCK();
b0d623f7
A
714 free(found_se, M_AU_SESSION);
715
716 return;
717 }
718 }
719 AUDIT_SENTRY_WUNLOCK();
720}
721
722/*
723 * Reference the session by incrementing the sentry ref count.
724 */
725static void
726audit_ref_session(au_sentry_t *se)
727{
728 long old_val;
729
0a7de745 730 if (se == NULL || se == &audit_default_se) {
6d2010ae 731 return;
0a7de745 732 }
6d2010ae
A
733
734 au_history_record(se, AU_HISTORY_EVENT_REF);
735
b0d623f7
A
736 old_val = OSAddAtomicLong(1, &se->se_refcnt);
737 KASSERT(old_val < 100000,
738 ("audit_ref_session: Too many references on session."));
739}
740
741/*
742 * Decrement the sentry ref count and remove the session entry if last one.
743 */
744static void
745audit_unref_session(au_sentry_t *se)
746{
747 long old_val;
748
0a7de745 749 if (se == NULL || se == &audit_default_se) {
6d2010ae 750 return;
0a7de745 751 }
6d2010ae
A
752
753 au_history_record(se, AU_HISTORY_EVENT_UNREF);
754
b0d623f7 755 old_val = OSAddAtomicLong(-1, &se->se_refcnt);
0a7de745 756 if (old_val == 1) {
b0d623f7 757 audit_session_remove(se);
0a7de745 758 }
b0d623f7
A
759 KASSERT(old_val > 0,
760 ("audit_unref_session: Too few references on session."));
761}
762
763/*
764 * Increment the process count in the session.
765 */
766static void
767audit_inc_procount(au_sentry_t *se)
768{
769 long old_val;
770
0a7de745 771 if (se == NULL || se == &audit_default_se) {
6d2010ae 772 return;
0a7de745
A
773 }
774
b0d623f7
A
775 old_val = OSAddAtomicLong(1, &se->se_procnt);
776 KASSERT(old_val <= PID_MAX,
777 ("audit_inc_procount: proc count > PID_MAX"));
778}
779
780/*
781 * Decrement the process count and add a knote if it is the last process
782 * to exit the session.
783 */
784static void
785audit_dec_procount(au_sentry_t *se)
786{
787 long old_val;
788
0a7de745 789 if (se == NULL || se == &audit_default_se) {
6d2010ae 790 return;
0a7de745 791 }
6d2010ae 792
b0d623f7 793 old_val = OSAddAtomicLong(-1, &se->se_procnt);
6d2010ae
A
794 /*
795 * If this was the last process generate an audit event to notify
796 * userland of the session ending.
797 */
0a7de745 798 if (old_val == 1) {
6d2010ae 799 audit_session_event(AUE_SESSION_END, &se->se_auinfo);
0a7de745 800 }
b0d623f7
A
801 KASSERT(old_val >= 1,
802 ("audit_dec_procount: proc count < 0"));
0a7de745 803}
b0d623f7
A
804
805/*
806 * Update the session entry and check to see if anything was updated.
807 * Returns:
0a7de745 808 * 0 Nothing was updated (We don't care about process preselection masks)
b0d623f7
A
809 * 1 Something was updated.
810 */
811static int
812audit_update_sentry(au_sentry_t *se, auditinfo_addr_t *new_aia)
813{
814 auditinfo_addr_t *aia = &se->se_auinfo;
815 int update;
816
0a7de745
A
817 KASSERT(new_aia != audit_default_aia_p,
818 ("audit_update_sentry: Trying to update the default aia."));
b0d623f7
A
819
820 update = (aia->ai_auid != new_aia->ai_auid ||
821 bcmp(&aia->ai_termid, &new_aia->ai_termid,
0a7de745 822 sizeof(new_aia->ai_termid)) ||
b0d623f7
A
823 aia->ai_flags != new_aia->ai_flags);
824
0a7de745 825 if (update) {
b0d623f7 826 bcopy(new_aia, aia, sizeof(*aia));
0a7de745 827 }
b0d623f7 828
0a7de745 829 return update;
b0d623f7
A
830}
831
832/*
833 * Return the next session ID. The range of kernel generated audit session IDs
834 * is ASSIGNED_ASID_MIN to ASSIGNED_ASID_MAX.
835 */
0a7de745 836static uint32_t
b0d623f7
A
837audit_session_nextid(void)
838{
0a7de745 839 static uint32_t next_asid = ASSIGNED_ASID_MIN;
b0d623f7
A
840
841 AUDIT_SENTRY_RWLOCK_ASSERT();
842
0a7de745 843 if (next_asid > ASSIGNED_ASID_MAX) {
b0d623f7 844 next_asid = ASSIGNED_ASID_MIN;
0a7de745 845 }
b0d623f7 846
0a7de745 847 return next_asid++;
b0d623f7
A
848}
849
850/*
851 * Allocated a new audit_session entry and add it to the hash table. If the
852 * given ASID is set to AU_ASSIGN_ASID then audit_session_new() will pick an
853 * audit session ID. Otherwise, it attempts use the one given. It creates a
854 * reference to the entry that must be unref'ed.
855 */
856static auditinfo_addr_t *
6d2010ae 857audit_session_new(auditinfo_addr_t *new_aia_p, auditinfo_addr_t *old_aia_p)
b0d623f7 858{
6d2010ae 859 au_asid_t new_asid;
b0d623f7 860 au_sentry_t *se = NULL;
6d2010ae 861 au_sentry_t *found_se = NULL;
b0d623f7 862 auditinfo_addr_t *aia = NULL;
0a7de745 863
6d2010ae 864 KASSERT(new_aia_p != NULL, ("audit_session_new: new_aia_p == NULL"));
b0d623f7 865
0a7de745 866 new_asid = new_aia_p->ai_asid;
b0d623f7 867
b0d623f7
A
868 /*
869 * Alloc a new session entry now so we don't wait holding the lock.
870 */
871 se = malloc(sizeof(au_sentry_t), M_AU_SESSION, M_WAITOK | M_ZERO);
872
b0d623f7
A
873 /*
874 * Find an unique session ID, if desired.
875 */
876 AUDIT_SENTRY_WLOCK();
6d2010ae 877 if (new_asid == AU_ASSIGN_ASID) {
b0d623f7 878 do {
6d2010ae
A
879 new_asid = (au_asid_t)audit_session_nextid();
880 found_se = audit_session_find(new_asid);
0a7de745
A
881
882 /*
6d2010ae
A
883 * If the session ID is currently active then drop the
884 * reference and try again.
885 */
0a7de745 886 if (found_se != NULL) {
6d2010ae 887 audit_unref_session(found_se);
0a7de745 888 } else {
6d2010ae 889 break;
0a7de745
A
890 }
891 } while (1);
b0d623f7 892 } else {
b0d623f7
A
893 /*
894 * Check to see if the requested ASID is already in the
895 * hash table. If so, update it with the new auditinfo.
0a7de745 896 */
6d2010ae 897 if ((found_se = audit_session_find(new_asid)) != NULL) {
b0d623f7
A
898 int updated;
899
6d2010ae 900 updated = audit_update_sentry(found_se, new_aia_p);
b0d623f7
A
901
902 AUDIT_SENTRY_WUNLOCK();
b0d623f7
A
903 free(se, M_AU_SESSION);
904
6d2010ae 905 /* If a different session then add this process in. */
0a7de745 906 if (new_aia_p != old_aia_p) {
6d2010ae 907 audit_inc_procount(found_se);
0a7de745 908 }
b0d623f7
A
909
910 /*
6d2010ae
A
911 * If the session information was updated then
912 * generate an audit event to notify userland.
b0d623f7 913 */
0a7de745 914 if (updated) {
6d2010ae
A
915 audit_session_event(AUE_SESSION_UPDATE,
916 &found_se->se_auinfo);
0a7de745 917 }
b0d623f7 918
0a7de745 919 return &found_se->se_auinfo;
b0d623f7
A
920 }
921 }
922
923 /*
924 * Start the reference and proc count at 1 to account for the process
925 * that invoked this via setaudit_addr() (or friends).
926 */
927 se->se_refcnt = se->se_procnt = 1;
928
929 /*
930 * Populate the new session entry. Note that process masks are stored
931 * in kauth ucred so just zero them here.
932 */
933 se->se_port = IPC_PORT_NULL;
934 aia = &se->se_auinfo;
6d2010ae
A
935 aia->ai_asid = new_asid;
936 aia->ai_auid = new_aia_p->ai_auid;
937 bzero(&new_aia_p->ai_mask, sizeof(new_aia_p->ai_mask));
938 bcopy(&new_aia_p->ai_termid, &aia->ai_termid, sizeof(aia->ai_termid));
939 aia->ai_flags = new_aia_p->ai_flags;
b0d623f7
A
940
941 /*
942 * Add it to the hash table.
943 */
6d2010ae 944 LIST_INSERT_HEAD(&au_sentry_bucket[HASH_ASID(new_asid)], se, se_link);
b0d623f7
A
945 AUDIT_SENTRY_WUNLOCK();
946
947 /*
6d2010ae 948 * Generate an audit event to notify userland of the new session.
b0d623f7 949 */
6d2010ae
A
950 audit_session_event(AUE_SESSION_START, aia);
951 au_history_record(se, AU_HISTORY_EVENT_BIRTH);
0a7de745 952 return aia;
b0d623f7
A
953}
954
955/*
956 * Lookup an existing session. A copy of the audit session info for a given
957 * ASID is returned in ret_aia. Returns 0 on success.
958 */
959int
960audit_session_lookup(au_asid_t asid, auditinfo_addr_t *ret_aia)
961{
962 au_sentry_t *se = NULL;
963
0a7de745
A
964 if ((uint32_t)asid > ASSIGNED_ASID_MAX) {
965 return -1;
966 }
b0d623f7
A
967 AUDIT_SENTRY_RLOCK();
968 if ((se = audit_session_find(asid)) == NULL) {
969 AUDIT_SENTRY_RUNLOCK();
0a7de745 970 return 1;
b0d623f7 971 }
6d2010ae
A
972 /* We have a reference on the session so it is safe to drop the lock. */
973 AUDIT_SENTRY_RUNLOCK();
0a7de745 974 if (ret_aia != NULL) {
b0d623f7 975 bcopy(&se->se_auinfo, ret_aia, sizeof(*ret_aia));
0a7de745 976 }
6d2010ae 977 audit_unref_session(se);
b0d623f7 978
0a7de745 979 return 0;
b0d623f7
A
980}
981
6d2010ae
A
982void
983audit_session_aiaref(auditinfo_addr_t *aia_p)
984{
6d2010ae
A
985 audit_ref_session(AU_SENTRY_PTR(aia_p));
986}
0a7de745 987
b0d623f7
A
988/*
989 * Add a reference to the session entry.
990 */
991void
992audit_session_ref(kauth_cred_t cred)
993{
994 auditinfo_addr_t *aia_p;
995
996 KASSERT(IS_VALID_CRED(cred),
997 ("audit_session_ref: Invalid kauth_cred."));
998
0a7de745 999 aia_p = cred->cr_audit.as_aia_p;
6d2010ae
A
1000 audit_session_aiaref(aia_p);
1001}
1002
0a7de745
A
1003void
1004audit_session_aiaunref(auditinfo_addr_t *aia_p)
6d2010ae 1005{
6d2010ae 1006 audit_unref_session(AU_SENTRY_PTR(aia_p));
b0d623f7
A
1007}
1008
0a7de745 1009/*
b0d623f7
A
1010 * Remove a reference to the session entry.
1011 */
1012void
1013audit_session_unref(kauth_cred_t cred)
1014{
1015 auditinfo_addr_t *aia_p;
1016
1017 KASSERT(IS_VALID_CRED(cred),
1018 ("audit_session_unref: Invalid kauth_cred."));
1019
0a7de745 1020 aia_p = cred->cr_audit.as_aia_p;
6d2010ae 1021 audit_session_aiaunref(aia_p);
b0d623f7
A
1022}
1023
6d2010ae
A
1024/*
1025 * Increment the per audit session process count. Assumes that the caller has
1026 * a reference on the process' cred.
1027 */
b0d623f7 1028void
6d2010ae 1029audit_session_procnew(proc_t p)
b0d623f7 1030{
6d2010ae 1031 kauth_cred_t cred = p->p_ucred;
b0d623f7 1032 auditinfo_addr_t *aia_p;
0a7de745
A
1033
1034 KASSERT(IS_VALID_CRED(cred),
b0d623f7
A
1035 ("audit_session_procnew: Invalid kauth_cred."));
1036
0a7de745 1037 aia_p = cred->cr_audit.as_aia_p;
b0d623f7 1038
6d2010ae 1039 audit_inc_procount(AU_SENTRY_PTR(aia_p));
b0d623f7
A
1040}
1041
6d2010ae
A
1042/*
1043 * Decrement the per audit session process count. Assumes that the caller has
1044 * a reference on the cred.
1045 */
b0d623f7 1046void
6d2010ae 1047audit_session_procexit(proc_t p)
b0d623f7 1048{
6d2010ae 1049 kauth_cred_t cred = p->p_ucred;
b0d623f7
A
1050 auditinfo_addr_t *aia_p;
1051
0a7de745 1052 KASSERT(IS_VALID_CRED(cred),
b0d623f7
A
1053 ("audit_session_procexit: Invalid kauth_cred."));
1054
0a7de745 1055 aia_p = cred->cr_audit.as_aia_p;
b0d623f7 1056
6d2010ae 1057 audit_dec_procount(AU_SENTRY_PTR(aia_p));
b0d623f7
A
1058}
1059
1060/*
0a7de745 1061 * Init the audit session code.
b0d623f7
A
1062 */
1063void
1064audit_session_init(void)
1065{
1066 int i;
1067
1068 KASSERT((ASSIGNED_ASID_MAX - ASSIGNED_ASID_MIN) > PID_MAX,
1069 ("audit_session_init: ASSIGNED_ASID_MAX is not large enough."));
0a7de745 1070
b0d623f7 1071 AUDIT_SENTRY_RWLOCK_INIT();
b0d623f7
A
1072
1073 au_sentry_bucket = malloc( sizeof(struct au_sentry) *
1074 HASH_TABLE_SIZE, M_AU_SESSION, M_WAITOK | M_ZERO);
1075
0a7de745 1076 for (i = 0; i < HASH_TABLE_SIZE; i++) {
b0d623f7 1077 LIST_INIT(&au_sentry_bucket[i]);
0a7de745 1078 }
b0d623f7 1079
6d2010ae
A
1080 (void)audit_sdev_init();
1081#if AU_HISTORY_LOGGING
1082 au_history = malloc(sizeof(struct au_history) * au_history_size,
0a7de745 1083 M_AU_SESSION, M_WAITOK | M_ZERO);
6d2010ae 1084#endif
b0d623f7
A
1085}
1086
6d2010ae
A
1087static int
1088audit_session_update_check(kauth_cred_t cred, auditinfo_addr_t *old,
1089 auditinfo_addr_t *new)
b0d623f7 1090{
6d2010ae
A
1091 uint64_t n;
1092
1093 /* If the current audit ID is not the default then it is immutable. */
0a7de745
A
1094 if (old->ai_auid != AU_DEFAUDITID && old->ai_auid != new->ai_auid) {
1095 return EINVAL;
1096 }
6d2010ae
A
1097
1098 /* If the current termid is not the default then it is immutable. */
1099 if ((old->ai_termid.at_type != AU_IPv4 ||
0a7de745
A
1100 old->ai_termid.at_port != 0 ||
1101 old->ai_termid.at_addr[0] != 0) &&
6d2010ae 1102 (old->ai_termid.at_port != new->ai_termid.at_port ||
0a7de745
A
1103 old->ai_termid.at_type != new->ai_termid.at_type ||
1104 0 != bcmp(&old->ai_termid.at_addr, &new->ai_termid.at_addr,
1105 sizeof(old->ai_termid.at_addr)))) {
1106 return EINVAL;
1107 }
6d2010ae
A
1108
1109 /* The flags may be set only according to the
1110 * audit_session_*_set_sflags_masks.
b0d623f7 1111 */
6d2010ae
A
1112 n = ~old->ai_flags & new->ai_flags;
1113 if (0 != n &&
1114 !((n == (audit_session_superuser_set_sflags_mask & n) &&
0a7de745
A
1115 kauth_cred_issuser(cred)) ||
1116 (n == (audit_session_member_set_sflags_mask & n) &&
1117 old->ai_asid == new->ai_asid))) {
1118 return EINVAL;
1119 }
6d2010ae
A
1120
1121 /* The flags may be cleared only according to the
1122 * audit_session_*_clear_sflags_masks.
b0d623f7 1123 */
6d2010ae
A
1124 n = ~new->ai_flags & old->ai_flags;
1125 if (0 != n &&
1126 !((n == (audit_session_superuser_clear_sflags_mask & n) &&
0a7de745
A
1127 kauth_cred_issuser(cred)) ||
1128 (n == (audit_session_member_clear_sflags_mask & n) &&
1129 old->ai_asid == new->ai_asid))) {
1130 return EINVAL;
1131 }
6d2010ae
A
1132
1133 /* The audit masks are mutable. */
0a7de745 1134 return 0;
b0d623f7
A
1135}
1136
1137/*
0a7de745 1138 * Safely update kauth cred of the given process with new the given audit info.
b0d623f7 1139 */
6d2010ae
A
1140int
1141audit_session_setaia(proc_t p, auditinfo_addr_t *new_aia_p)
b0d623f7 1142{
6d2010ae
A
1143 kauth_cred_t my_cred, my_new_cred;
1144 struct au_session as;
1145 struct au_session tmp_as;
1146 auditinfo_addr_t caia, *old_aia_p;
1147 int ret;
b0d623f7 1148
6d2010ae
A
1149 /*
1150 * If this is going to modify an existing session then do some
1151 * immutable checks.
1152 */
1153 if (audit_session_lookup(new_aia_p->ai_asid, &caia) == 0) {
1154 my_cred = kauth_cred_proc_ref(p);
1155 ret = audit_session_update_check(my_cred, &caia, new_aia_p);
1156 kauth_cred_unref(&my_cred);
0a7de745
A
1157 if (ret) {
1158 return ret;
1159 }
6d2010ae 1160 }
b0d623f7 1161
6d2010ae
A
1162 my_cred = kauth_cred_proc_ref(p);
1163 bcopy(&new_aia_p->ai_mask, &as.as_mask, sizeof(as.as_mask));
1164 old_aia_p = my_cred->cr_audit.as_aia_p;
1165 /* audit_session_new() adds a reference on the session */
1166 as.as_aia_p = audit_session_new(new_aia_p, old_aia_p);
b0d623f7 1167
6d2010ae 1168 /* If the process left a session then update the process count. */
0a7de745 1169 if (old_aia_p != new_aia_p) {
6d2010ae 1170 audit_dec_procount(AU_SENTRY_PTR(old_aia_p));
0a7de745 1171 }
b0d623f7 1172
b0d623f7
A
1173
1174 /*
6d2010ae
A
1175 * We are modifying the audit info in a credential so we need a new
1176 * credential (or take another reference on an existing credential that
1177 * matches our new one). We must do this because the audit info in the
1178 * credential is used as part of our hash key. Get current credential
1179 * in the target process and take a reference while we muck with it.
b0d623f7 1180 */
6d2010ae 1181 for (;;) {
b0d623f7
A
1182 /*
1183 * Set the credential with new info. If there is no change,
1184 * we get back the same credential we passed in; if there is
1185 * a change, we drop the reference on the credential we
1186 * passed in. The subsequent compare is safe, because it is
1187 * a pointer compare rather than a contents compare.
1188 */
1189 bcopy(&as, &tmp_as, sizeof(tmp_as));
1190 my_new_cred = kauth_cred_setauditinfo(my_cred, &tmp_as);
1191
1192 if (my_cred != my_new_cred) {
4bd07ac2 1193 proc_ucred_lock(p);
b0d623f7
A
1194 /* Need to protect for a race where another thread also
1195 * changed the credential after we took our reference.
1196 * If p_ucred has changed then we should restart this
1197 * again with the new cred.
1198 */
1199 if (p->p_ucred != my_cred) {
4bd07ac2 1200 proc_ucred_unlock(p);
b0d623f7
A
1201 audit_session_unref(my_new_cred);
1202 kauth_cred_unref(&my_new_cred);
1203 /* try again */
1204 my_cred = kauth_cred_proc_ref(p);
1205 continue;
1206 }
1207 p->p_ucred = my_new_cred;
6d2010ae
A
1208 /* update cred on proc */
1209 PROC_UPDATE_CREDS_ONPROC(p);
4bd07ac2 1210 proc_ucred_unlock(p);
b0d623f7
A
1211 }
1212 /*
1213 * Drop old proc reference or our extra reference.
1214 */
1215 kauth_cred_unref(&my_cred);
1216 break;
1217 }
b0d623f7 1218
6d2010ae
A
1219 /* Drop the reference taken by audit_session_new() above. */
1220 audit_unref_session(AU_SENTRY_PTR(as.as_aia_p));
1221
1222 /* Propagate the change from the process to the Mach task. */
b0d623f7
A
1223 set_security_token(p);
1224
0a7de745 1225 return 0;
b0d623f7
A
1226}
1227
1228/*
1229 * audit_session_self (system call)
1230 *
0a7de745 1231 * Description: Obtain a Mach send right for the current session.
b0d623f7
A
1232 *
1233 * Parameters: p Process calling audit_session_self().
0a7de745 1234 *
b0d623f7 1235 * Returns: *ret_port Named Mach send right, which may be
0a7de745 1236 * MACH_PORT_NULL in the failure case.
b0d623f7
A
1237 *
1238 * Errno: 0 Success
0a7de745
A
1239 * EINVAL The calling process' session has not be set.
1240 * ESRCH Bad process, can't get valid cred for process.
1241 * ENOMEM Port allocation failed due to no free memory.
b0d623f7
A
1242 */
1243int
1244audit_session_self(proc_t p, __unused struct audit_session_self_args *uap,
1245 mach_port_name_t *ret_port)
1246{
1247 ipc_port_t sendport = IPC_PORT_NULL;
1248 kauth_cred_t cred = NULL;
1249 auditinfo_addr_t *aia_p;
1250 au_sentry_t *se;
1251 int err = 0;
1252
1253 cred = kauth_cred_proc_ref(p);
1254 if (!IS_VALID_CRED(cred)) {
1255 err = ESRCH;
1256 goto done;
1257 }
1258
1259 aia_p = cred->cr_audit.as_aia_p;
1260 if (!IS_VALID_SESSION(aia_p)) {
6d2010ae 1261 /* Can't join the default session. */
b0d623f7
A
1262 err = EINVAL;
1263 goto done;
1264 }
1265
0a7de745 1266 se = AU_SENTRY_PTR(aia_p);
b0d623f7 1267
0a7de745 1268 /*
b0d623f7
A
1269 * Processes that join using this mach port will inherit this process'
1270 * pre-selection masks.
1271 */
0a7de745 1272 if (se->se_port == IPC_PORT_NULL) {
b0d623f7
A
1273 bcopy(&cred->cr_audit.as_mask, &se->se_mask,
1274 sizeof(se->se_mask));
0a7de745 1275 }
b0d623f7 1276
b0d623f7 1277 /*
6d2010ae
A
1278 * Get a send right to the session's Mach port and insert it in the
1279 * process' mach port namespace.
b0d623f7 1280 */
6d2010ae
A
1281 sendport = audit_session_mksend(aia_p, &se->se_port);
1282 *ret_port = ipc_port_copyout_send(sendport, get_task_ipcspace(p->task));
b0d623f7
A
1283
1284done:
0a7de745
A
1285 if (cred != NULL) {
1286 kauth_cred_unref(&cred);
1287 }
1288 if (err != 0) {
b0d623f7 1289 *ret_port = MACH_PORT_NULL;
0a7de745
A
1290 }
1291 return err;
b0d623f7
A
1292}
1293
6d2010ae
A
1294/*
1295 * audit_session_port (system call)
1296 *
1297 * Description: Obtain a Mach send right for the given session ID.
1298 *
1299 * Parameters: p Process calling audit_session_port().
1300 * uap->asid The target audit session ID. The special
0a7de745
A
1301 * value -1 can be used to target the process's
1302 * own session.
6d2010ae
A
1303 * uap->portnamep User address at which to place port name.
1304 *
1305 * Returns: 0 Success
0a7de745
A
1306 * EINVAL The calling process' session has not be set.
1307 * EINVAL The given session ID could not be found.
1308 * EINVAL The Mach port right could not be copied out.
1309 * ESRCH Bad process, can't get valid cred for process.
1310 * EPERM Only the superuser can reference sessions other
1311 * than the process's own.
1312 * ENOMEM Port allocation failed due to no free memory.
6d2010ae
A
1313 */
1314int
1315audit_session_port(proc_t p, struct audit_session_port_args *uap,
1316 __unused int *retval)
b0d623f7 1317{
6d2010ae
A
1318 ipc_port_t sendport = IPC_PORT_NULL;
1319 mach_port_name_t portname = MACH_PORT_NULL;
1320 kauth_cred_t cred = NULL;
1321 auditinfo_addr_t *aia_p = NULL;
1322 au_sentry_t *se = NULL;
1323 int err = 0;
1324
1325 /* Note: Currently this test will never be true, because
1326 * ASSIGNED_ASID_MAX is effectively (uint32_t)-2.
1327 */
1328 if (uap->asid != -1 && (uint32_t)uap->asid > ASSIGNED_ASID_MAX) {
1329 err = EINVAL;
1330 goto done;
1331 }
1332 cred = kauth_cred_proc_ref(p);
1333 if (!IS_VALID_CRED(cred)) {
1334 err = ESRCH;
1335 goto done;
1336 }
1337 aia_p = cred->cr_audit.as_aia_p;
b0d623f7 1338
6d2010ae
A
1339 /* Find the session corresponding to the requested audit
1340 * session ID. If found, take a reference on it so that
1341 * the session is not dropped until the join is later done.
1342 */
1343 if (uap->asid == (au_asid_t)-1 ||
1344 uap->asid == aia_p->ai_asid) {
6d2010ae
A
1345 if (!IS_VALID_SESSION(aia_p)) {
1346 /* Can't join the default session. */
1347 err = EINVAL;
1348 goto done;
1349 }
1350
1351 /* No privilege is required to obtain a port for our
1352 * own session.
1353 */
1354 se = AU_SENTRY_PTR(aia_p);
1355 audit_ref_session(se);
cb323159
A
1356 } else {
1357 /*
1358 * Only privileged processes may obtain a port for
1359 * any existing session.
6d2010ae 1360 */
cb323159
A
1361 err = priv_check_cred(cred, PRIV_AUDIT_SESSION_PORT, 0);
1362 if (err != 0) {
1363 goto done;
1364 }
6d2010ae
A
1365 AUDIT_SENTRY_RLOCK();
1366 se = audit_session_find(uap->asid);
1367 AUDIT_SENTRY_RUNLOCK();
1368 if (NULL == se) {
1369 err = EINVAL;
1370 goto done;
1371 }
1372 aia_p = &se->se_auinfo;
6d2010ae 1373 }
b0d623f7
A
1374
1375 /*
6d2010ae
A
1376 * Processes that join using this mach port will inherit this process'
1377 * pre-selection masks.
b0d623f7 1378 */
0a7de745 1379 if (se->se_port == IPC_PORT_NULL) {
6d2010ae
A
1380 bcopy(&cred->cr_audit.as_mask, &se->se_mask,
1381 sizeof(se->se_mask));
0a7de745 1382 }
6d2010ae
A
1383
1384 /*
1385 * Use the session reference to create a mach port reference for the
1386 * session (at which point we are free to drop the session reference)
1387 * and then copy out the mach port to the process' mach port namespace.
1388 */
1389 sendport = audit_session_mksend(aia_p, &se->se_port);
1390 portname = ipc_port_copyout_send(sendport, get_task_ipcspace(p->task));
1391 if (!MACH_PORT_VALID(portname)) {
1392 err = EINVAL;
1393 goto done;
b0d623f7 1394 }
6d2010ae
A
1395 err = copyout(&portname, uap->portnamep, sizeof(mach_port_name_t));
1396done:
0a7de745 1397 if (cred != NULL) {
6d2010ae 1398 kauth_cred_unref(&cred);
0a7de745
A
1399 }
1400 if (NULL != se) {
6d2010ae 1401 audit_unref_session(se);
0a7de745
A
1402 }
1403 if (MACH_PORT_VALID(portname) && 0 != err) {
1404 (void)mach_port_deallocate(get_task_ipcspace(p->task),
6d2010ae 1405 portname);
0a7de745 1406 }
b0d623f7 1407
0a7de745 1408 return err;
b0d623f7
A
1409}
1410
1411static int
743345f9 1412audit_session_join_internal(proc_t p, task_t task, ipc_port_t port, au_asid_t *new_asid)
b0d623f7 1413{
6d2010ae
A
1414 auditinfo_addr_t *new_aia_p, *old_aia_p;
1415 kauth_cred_t my_cred = NULL;
b0d623f7
A
1416 au_asid_t old_asid;
1417 int err = 0;
1418
1419 *new_asid = AU_DEFAUDITSID;
1420
6d2010ae 1421 if ((new_aia_p = audit_session_porttoaia(port)) == NULL) {
b0d623f7
A
1422 err = EINVAL;
1423 goto done;
1424 }
b0d623f7 1425
4bd07ac2 1426 proc_ucred_lock(p);
6d2010ae
A
1427 kauth_cred_ref(p->p_ucred);
1428 my_cred = p->p_ucred;
1429 if (!IS_VALID_CRED(my_cred)) {
0a7de745 1430 kauth_cred_unref(&my_cred);
4bd07ac2 1431 proc_ucred_unlock(p);
b0d623f7
A
1432 err = ESRCH;
1433 goto done;
1434 }
6d2010ae 1435 old_aia_p = my_cred->cr_audit.as_aia_p;
b0d623f7 1436 old_asid = old_aia_p->ai_asid;
6d2010ae 1437 *new_asid = new_aia_p->ai_asid;
b0d623f7
A
1438
1439 /*
1440 * Add process in if not already in the session.
1441 */
1442 if (*new_asid != old_asid) {
6d2010ae
A
1443 kauth_cred_t my_new_cred;
1444 struct au_session new_as;
1445
1446 bcopy(&new_aia_p->ai_mask, &new_as.as_mask,
0a7de745 1447 sizeof(new_as.as_mask));
6d2010ae
A
1448 new_as.as_aia_p = new_aia_p;
1449
1450 my_new_cred = kauth_cred_setauditinfo(my_cred, &new_as);
1451 p->p_ucred = my_new_cred;
1452 PROC_UPDATE_CREDS_ONPROC(p);
1453
1454 /* Increment the proc count of new session */
1455 audit_inc_procount(AU_SENTRY_PTR(new_aia_p));
1456
4bd07ac2 1457 proc_ucred_unlock(p);
6d2010ae
A
1458
1459 /* Propagate the change from the process to the Mach task. */
743345f9 1460 set_security_token_task_internal(p, task);
6d2010ae
A
1461
1462 /* Decrement the process count of the former session. */
1463 audit_dec_procount(AU_SENTRY_PTR(old_aia_p));
0a7de745 1464 } else {
4bd07ac2 1465 proc_ucred_unlock(p);
b0d623f7 1466 }
6d2010ae 1467 kauth_cred_unref(&my_cred);
b0d623f7
A
1468
1469done:
0a7de745 1470 if (port != IPC_PORT_NULL) {
b0d623f7 1471 ipc_port_release_send(port);
0a7de745 1472 }
b0d623f7 1473
0a7de745 1474 return err;
b0d623f7
A
1475}
1476
1477/*
1478 * audit_session_spawnjoin
1479 *
1480 * Description: posix_spawn() interface to audit_session_join_internal().
1481 *
1482 * Returns: 0 Success
0a7de745
A
1483 * EINVAL Invalid Mach port name.
1484 * ESRCH Invalid calling process/cred.
b0d623f7
A
1485 */
1486int
743345f9 1487audit_session_spawnjoin(proc_t p, task_t task, ipc_port_t port)
b0d623f7
A
1488{
1489 au_asid_t new_asid;
0a7de745
A
1490
1491 return audit_session_join_internal(p, task, port, &new_asid);
b0d623f7
A
1492}
1493
1494/*
1495 * audit_session_join (system call)
1496 *
1497 * Description: Join the session for a given Mach port send right.
0a7de745 1498 *
b0d623f7 1499 * Parameters: p Process calling session join.
0a7de745 1500 * uap->port A Mach send right.
b0d623f7 1501 *
6d2010ae
A
1502 * Returns: *ret_asid Audit session ID of new session.
1503 * In the failure case the return value will be -1
1504 * and 'errno' will be set to a non-zero value
1505 * described below.
b0d623f7 1506 *
0a7de745
A
1507 * Errno: 0 Success
1508 * EINVAL Invalid Mach port name.
1509 * ESRCH Invalid calling process/cred.
b0d623f7
A
1510 */
1511int
1512audit_session_join(proc_t p, struct audit_session_join_args *uap,
1513 au_asid_t *ret_asid)
1514{
1515 ipc_port_t port = IPC_PORT_NULL;
1516 mach_port_name_t send = uap->port;
1517 int err = 0;
1518
0a7de745 1519
b0d623f7 1520 if (ipc_object_copyin(get_task_ipcspace(p->task), send,
cb323159 1521 MACH_MSG_TYPE_COPY_SEND, &port, 0, NULL, IPC_KMSG_FLAGS_ALLOW_IMMOVABLE_SEND) != KERN_SUCCESS) {
b0d623f7
A
1522 *ret_asid = AU_DEFAUDITSID;
1523 err = EINVAL;
0a7de745 1524 } else {
743345f9 1525 err = audit_session_join_internal(p, p->task, port, ret_asid);
0a7de745 1526 }
b0d623f7 1527
0a7de745 1528 return err;
b0d623f7
A
1529}
1530
6d2010ae
A
1531/*
1532 * Audit session device.
1533 */
1534
1535/*
1536 * Free an audit sdev entry.
1537 */
1538static void
1539audit_sdev_entry_free(struct audit_sdev_entry *ase)
1540{
6d2010ae
A
1541 free(ase->ase_record, M_AUDIT_SDEV_ENTRY);
1542 free(ase, M_AUDIT_SDEV_ENTRY);
1543}
1544
1545/*
1546 * Append individual record to a queue. Allocate queue-local buffer and
1547 * add to the queue. If the queue is full or we can't allocate memory,
1548 * drop the newest record.
1549 */
1550static void
1551audit_sdev_append(struct audit_sdev *asdev, void *record, u_int record_len)
1552{
1553 struct audit_sdev_entry *ase;
1554
1555 AUDIT_SDEV_LOCK_ASSERT(asdev);
1556
1557 if (asdev->asdev_qlen >= asdev->asdev_qlimit) {
1558 asdev->asdev_drops++;
1559 audit_sdev_drops++;
1560 return;
1561 }
1562
0a7de745 1563 ase = malloc(sizeof(*ase), M_AUDIT_SDEV_ENTRY, M_NOWAIT | M_ZERO);
6d2010ae
A
1564 if (NULL == ase) {
1565 asdev->asdev_drops++;
1566 audit_sdev_drops++;
1567 return;
1568 }
1569
1570 ase->ase_record = malloc(record_len, M_AUDIT_SDEV_ENTRY, M_NOWAIT);
1571 if (NULL == ase->ase_record) {
1572 free(ase, M_AUDIT_SDEV_ENTRY);
1573 asdev->asdev_drops++;
1574 audit_sdev_drops++;
1575 return;
1576 }
1577
1578 bcopy(record, ase->ase_record, record_len);
1579 ase->ase_record_len = record_len;
1580
1581 TAILQ_INSERT_TAIL(&asdev->asdev_queue, ase, ase_queue);
1582 asdev->asdev_inserts++;
1583 asdev->asdev_qlen++;
1584 asdev->asdev_qbyteslen += ase->ase_record_len;
1585 selwakeup(&asdev->asdev_selinfo);
0a7de745 1586 if (asdev->asdev_flags & AUDIT_SDEV_ASYNC) {
6d2010ae 1587 pgsigio(asdev->asdev_sigio, SIGIO);
0a7de745 1588 }
6d2010ae
A
1589
1590 cv_broadcast(&asdev->asdev_cv);
1591}
1592
1593/*
1594 * Submit an audit record to be queued in the audit session device.
1595 */
1596void
1597audit_sdev_submit(__unused au_id_t auid, __unused au_asid_t asid, void *record,
1598 u_int record_len)
1599{
1600 struct audit_sdev *asdev;
1601
1602 /*
1603 * Lockless read to avoid lock overhead if sessio devices are not in
1604 * use.
1605 */
0a7de745 1606 if (NULL == TAILQ_FIRST(&audit_sdev_list)) {
6d2010ae 1607 return;
0a7de745 1608 }
6d2010ae
A
1609
1610 AUDIT_SDEV_LIST_RLOCK();
1611 TAILQ_FOREACH(asdev, &audit_sdev_list, asdev_list) {
1612 AUDIT_SDEV_LOCK(asdev);
0a7de745
A
1613
1614 /*
6d2010ae
A
1615 * Only append to the sdev queue if the AUID and ASID match that
1616 * of the process that opened this session device or if the
1617 * ALLSESSIONS flag is set.
1618 */
1619 if ((/* XXXss auid == asdev->asdev_auid && */
0a7de745
A
1620 asid == asdev->asdev_asid) ||
1621 (asdev->asdev_flags & AUDIT_SDEV_ALLSESSIONS) != 0) {
6d2010ae 1622 audit_sdev_append(asdev, record, record_len);
0a7de745 1623 }
6d2010ae
A
1624 AUDIT_SDEV_UNLOCK(asdev);
1625 }
1626 AUDIT_SDEV_LIST_RUNLOCK();
1627
1628 /* Unlocked increment. */
1629 audit_sdev_records++;
1630}
1631
1632/*
1633 * Allocate a new audit sdev. Connects the sdev, on succes, to the global
1634 * list and updates statistics.
1635 */
1636static struct audit_sdev *
1637audit_sdev_alloc(void)
1638{
1639 struct audit_sdev *asdev;
1640
1641 AUDIT_SDEV_LIST_WLOCK_ASSERT();
1642
0a7de745
A
1643 asdev = malloc(sizeof(*asdev), M_AUDIT_SDEV, M_WAITOK | M_ZERO);
1644 if (NULL == asdev) {
1645 return NULL;
1646 }
6d2010ae
A
1647
1648 asdev->asdev_qlimit = AUDIT_SDEV_QLIMIT_DEFAULT;
1649 TAILQ_INIT(&asdev->asdev_queue);
1650 AUDIT_SDEV_LOCK_INIT(asdev);
1651 AUDIT_SDEV_SX_LOCK_INIT(asdev);
1652 cv_init(&asdev->asdev_cv, "audit_sdev_cv");
1653
1654 /*
1655 * Add to global list and update global statistics.
1656 */
1657 TAILQ_INSERT_HEAD(&audit_sdev_list, asdev, asdev_list);
1658 audit_sdev_count++;
1659 audit_sdev_ever++;
1660
0a7de745 1661 return asdev;
6d2010ae
A
1662}
1663
1664/*
1665 * Flush all records currently present in an audit sdev.
1666 */
1667static void
1668audit_sdev_flush(struct audit_sdev *asdev)
1669{
1670 struct audit_sdev_entry *ase;
1671
1672 AUDIT_SDEV_LOCK_ASSERT(asdev);
1673
1674 while ((ase = TAILQ_FIRST(&asdev->asdev_queue)) != NULL) {
1675 TAILQ_REMOVE(&asdev->asdev_queue, ase, ase_queue);
1676 asdev->asdev_qbyteslen -= ase->ase_record_len;
1677 audit_sdev_entry_free(ase);
1678 asdev->asdev_qlen--;
1679 }
1680 asdev->asdev_qoffset = 0;
1681
1682 KASSERT(0 == asdev->asdev_qlen, ("audit_sdev_flush: asdev_qlen"));
1683 KASSERT(0 == asdev->asdev_qbyteslen,
1684 ("audit_sdev_flush: asdev_qbyteslen"));
1685}
1686
1687/*
1688 * Free an audit sdev.
1689 */
1690static void
1691audit_sdev_free(struct audit_sdev *asdev)
1692{
6d2010ae
A
1693 AUDIT_SDEV_LIST_WLOCK_ASSERT();
1694 AUDIT_SDEV_LOCK_ASSERT(asdev);
1695
1696 /* XXXss - preselect hook here */
1697 audit_sdev_flush(asdev);
1698 cv_destroy(&asdev->asdev_cv);
1699 AUDIT_SDEV_SX_LOCK_DESTROY(asdev);
39236c6e 1700 AUDIT_SDEV_UNLOCK(asdev);
6d2010ae
A
1701 AUDIT_SDEV_LOCK_DESTROY(asdev);
1702
1703 TAILQ_REMOVE(&audit_sdev_list, asdev, asdev_list);
1704 free(asdev, M_AUDIT_SDEV);
1705 audit_sdev_count--;
1706}
1707
1708/*
1709 * Get the auditinfo_addr of the proc and check to see if suser. Will return
1710 * non-zero if not suser.
1711 */
1712static int
1713audit_sdev_get_aia(proc_t p, struct auditinfo_addr *aia_p)
1714{
1715 int error;
1716 kauth_cred_t scred;
1717
1718 scred = kauth_cred_proc_ref(p);
1719 error = suser(scred, &p->p_acflag);
1720
0a7de745
A
1721 if (NULL != aia_p) {
1722 bcopy(scred->cr_audit.as_aia_p, aia_p, sizeof(*aia_p));
1723 }
6d2010ae
A
1724 kauth_cred_unref(&scred);
1725
0a7de745 1726 return error;
6d2010ae
A
1727}
1728
1729/*
1730 * Audit session dev open method.
1731 */
1732static int
0a7de745 1733audit_sdev_open(dev_t dev, __unused int flags, __unused int devtype, proc_t p)
6d2010ae
A
1734{
1735 struct audit_sdev *asdev;
1736 struct auditinfo_addr aia;
1737 int u;
1738
1739 u = minor(dev);
0a7de745
A
1740 if (u < 0 || u >= MAX_AUDIT_SDEVS) {
1741 return ENXIO;
1742 }
6d2010ae
A
1743
1744 (void) audit_sdev_get_aia(p, &aia);
1745
1746 AUDIT_SDEV_LIST_WLOCK();
1747 asdev = audit_sdev_dtab[u];
1748 if (NULL == asdev) {
1749 asdev = audit_sdev_alloc();
1750 if (NULL == asdev) {
1751 AUDIT_SDEV_LIST_WUNLOCK();
0a7de745 1752 return ENOMEM;
6d2010ae
A
1753 }
1754 audit_sdev_dtab[u] = asdev;
1755 } else {
1756 KASSERT(asdev->asdev_open, ("audit_sdev_open: Already open"));
1757 AUDIT_SDEV_LIST_WUNLOCK();
0a7de745 1758 return EBUSY;
6d2010ae
A
1759 }
1760 asdev->asdev_open = 1;
1761 asdev->asdev_auid = aia.ai_auid;
1762 asdev->asdev_asid = aia.ai_asid;
0a7de745 1763 asdev->asdev_flags = 0;
6d2010ae
A
1764
1765 AUDIT_SDEV_LIST_WUNLOCK();
1766
0a7de745 1767 return 0;
6d2010ae
A
1768}
1769
1770/*
1771 * Audit session dev close method.
1772 */
1773static int
1774audit_sdev_close(dev_t dev, __unused int flags, __unused int devtype,
1775 __unused proc_t p)
1776{
1777 struct audit_sdev *asdev;
1778 int u;
1779
1780 u = minor(dev);
1781 asdev = audit_sdev_dtab[u];
1782
1783 KASSERT(asdev != NULL, ("audit_sdev_close: asdev == NULL"));
1784 KASSERT(asdev->asdev_open, ("audit_sdev_close: !asdev_open"));
1785
1786 AUDIT_SDEV_LIST_WLOCK();
1787 AUDIT_SDEV_LOCK(asdev);
1788 asdev->asdev_open = 0;
1789 audit_sdev_free(asdev); /* sdev lock unlocked in audit_sdev_free() */
1790 audit_sdev_dtab[u] = NULL;
1791 AUDIT_SDEV_LIST_WUNLOCK();
1792
0a7de745 1793 return 0;
6d2010ae
A
1794}
1795
1796/*
1797 * Audit session dev ioctl method.
1798 */
1799static int
1800audit_sdev_ioctl(dev_t dev, u_long cmd, caddr_t data,
1801 __unused int flag, proc_t p)
1802{
1803 struct audit_sdev *asdev;
1804 int error;
1805
1806 asdev = audit_sdev_dtab[minor(dev)];
1807 KASSERT(asdev != NULL, ("audit_sdev_ioctl: asdev == NULL"));
1808
1809 error = 0;
1810
1811 switch (cmd) {
1812 case FIONBIO:
1813 AUDIT_SDEV_LOCK(asdev);
0a7de745 1814 if (*(int *)data) {
6d2010ae 1815 asdev->asdev_flags |= AUDIT_SDEV_NBIO;
0a7de745 1816 } else {
6d2010ae 1817 asdev->asdev_flags &= ~AUDIT_SDEV_NBIO;
0a7de745 1818 }
6d2010ae
A
1819 AUDIT_SDEV_UNLOCK(asdev);
1820 break;
1821
1822 case FIONREAD:
1823 AUDIT_SDEV_LOCK(asdev);
1824 *(int *)data = asdev->asdev_qbyteslen - asdev->asdev_qoffset;
1825 AUDIT_SDEV_UNLOCK(asdev);
1826 break;
1827
1828 case AUDITSDEV_GET_QLEN:
1829 *(u_int *)data = asdev->asdev_qlen;
1830 break;
1831
1832 case AUDITSDEV_GET_QLIMIT:
1833 *(u_int *)data = asdev->asdev_qlimit;
1834 break;
1835
1836 case AUDITSDEV_SET_QLIMIT:
1837 if (*(u_int *)data >= AUDIT_SDEV_QLIMIT_MIN ||
1838 *(u_int *)data <= AUDIT_SDEV_QLIMIT_MAX) {
1839 asdev->asdev_qlimit = *(u_int *)data;
0a7de745 1840 } else {
6d2010ae 1841 error = EINVAL;
0a7de745 1842 }
6d2010ae
A
1843 break;
1844
1845 case AUDITSDEV_GET_QLIMIT_MIN:
1846 *(u_int *)data = AUDIT_SDEV_QLIMIT_MIN;
1847 break;
1848
1849 case AUDITSDEV_GET_QLIMIT_MAX:
1850 *(u_int *)data = AUDIT_SDEV_QLIMIT_MAX;
1851 break;
1852
1853 case AUDITSDEV_FLUSH:
0a7de745
A
1854 if (AUDIT_SDEV_SX_XLOCK_SIG(asdev) != 0) {
1855 return EINTR;
1856 }
6d2010ae
A
1857 AUDIT_SDEV_LOCK(asdev);
1858 audit_sdev_flush(asdev);
1859 AUDIT_SDEV_UNLOCK(asdev);
1860 AUDIT_SDEV_SX_XUNLOCK(asdev);
1861 break;
1862
1863 case AUDITSDEV_GET_MAXDATA:
1864 *(u_int *)data = MAXAUDITDATA;
1865 break;
1866
1867 /* XXXss these should be 64 bit, maybe. */
1868 case AUDITSDEV_GET_INSERTS:
1869 *(u_int *)data = asdev->asdev_inserts;
1870 break;
1871
1872 case AUDITSDEV_GET_READS:
1873 *(u_int *)data = asdev->asdev_reads;
1874 break;
1875
1876 case AUDITSDEV_GET_DROPS:
1877 *(u_int *)data = asdev->asdev_drops;
1878 break;
1879
1880 case AUDITSDEV_GET_ALLSESSIONS:
1881 error = audit_sdev_get_aia(p, NULL);
0a7de745 1882 if (error) {
6d2010ae 1883 break;
0a7de745 1884 }
6d2010ae
A
1885 *(u_int *)data = (asdev->asdev_flags & AUDIT_SDEV_ALLSESSIONS) ?
1886 1 : 0;
1887 break;
1888
1889 case AUDITSDEV_SET_ALLSESSIONS:
1890 error = audit_sdev_get_aia(p, NULL);
0a7de745 1891 if (error) {
6d2010ae 1892 break;
0a7de745 1893 }
6d2010ae
A
1894
1895 AUDIT_SDEV_LOCK(asdev);
0a7de745 1896 if (*(int *)data) {
6d2010ae 1897 asdev->asdev_flags |= AUDIT_SDEV_ALLSESSIONS;
0a7de745 1898 } else {
6d2010ae 1899 asdev->asdev_flags &= ~AUDIT_SDEV_ALLSESSIONS;
0a7de745 1900 }
6d2010ae
A
1901 AUDIT_SDEV_UNLOCK(asdev);
1902 break;
1903
1904 default:
1905 error = ENOTTY;
1906 }
1907
0a7de745 1908 return error;
6d2010ae
A
1909}
1910
1911/*
0a7de745 1912 * Audit session dev read method.
6d2010ae
A
1913 */
1914static int
1915audit_sdev_read(dev_t dev, struct uio *uio, __unused int flag)
1916{
1917 struct audit_sdev_entry *ase;
1918 struct audit_sdev *asdev;
1919 u_int toread;
1920 int error;
1921
1922 asdev = audit_sdev_dtab[minor(dev)];
1923 KASSERT(NULL != asdev, ("audit_sdev_read: asdev == NULL"));
1924
1925 /*
1926 * We hold a sleep lock over read and flush because we rely on the
1927 * stability of a record in the queue during uiomove.
1928 */
0a7de745
A
1929 if (0 != AUDIT_SDEV_SX_XLOCK_SIG(asdev)) {
1930 return EINTR;
1931 }
6d2010ae
A
1932 AUDIT_SDEV_LOCK(asdev);
1933 while (TAILQ_EMPTY(&asdev->asdev_queue)) {
1934 if (asdev->asdev_flags & AUDIT_SDEV_NBIO) {
1935 AUDIT_SDEV_UNLOCK(asdev);
1936 AUDIT_SDEV_SX_XUNLOCK(asdev);
0a7de745 1937 return EAGAIN;
6d2010ae
A
1938 }
1939 error = cv_wait_sig(&asdev->asdev_cv, AUDIT_SDEV_MTX(asdev));
1940 if (error) {
1941 AUDIT_SDEV_UNLOCK(asdev);
1942 AUDIT_SDEV_SX_XUNLOCK(asdev);
0a7de745 1943 return error;
6d2010ae
A
1944 }
1945 }
1946
1947 /*
1948 * Copy as many remaining bytes from the current record to userspace
1949 * as we can. Keep processing records until we run out of records in
1950 * the queue or until the user buffer runs out of space.
1951 *
1952 * We rely on the sleep lock to maintain ase's stability here.
1953 */
1954 asdev->asdev_reads++;
1955 while ((ase = TAILQ_FIRST(&asdev->asdev_queue)) != NULL &&
1956 uio_resid(uio) > 0) {
1957 AUDIT_SDEV_LOCK_ASSERT(asdev);
1958
1959 KASSERT(ase->ase_record_len > asdev->asdev_qoffset,
1960 ("audit_sdev_read: record_len > qoffset (1)"));
39236c6e 1961 toread = MIN((int)(ase->ase_record_len - asdev->asdev_qoffset),
6d2010ae
A
1962 uio_resid(uio));
1963 AUDIT_SDEV_UNLOCK(asdev);
1964 error = uiomove((char *) ase->ase_record + asdev->asdev_qoffset,
1965 toread, uio);
1966 if (error) {
1967 AUDIT_SDEV_SX_XUNLOCK(asdev);
0a7de745 1968 return error;
6d2010ae
A
1969 }
1970
1971 /*
1972 * If the copy succeeded then update book-keeping, and if no
1973 * bytes remain in the current record then free it.
1974 */
1975 AUDIT_SDEV_LOCK(asdev);
1976 KASSERT(TAILQ_FIRST(&asdev->asdev_queue) == ase,
1977 ("audit_sdev_read: queue out of sync after uiomove"));
1978 asdev->asdev_qoffset += toread;
1979 KASSERT(ase->ase_record_len >= asdev->asdev_qoffset,
0a7de745
A
1980 ("audit_sdev_read: record_len >= qoffset (2)"));
1981 if (asdev->asdev_qoffset == ase->ase_record_len) {
1982 TAILQ_REMOVE(&asdev->asdev_queue, ase, ase_queue);
1983 asdev->asdev_qbyteslen -= ase->ase_record_len;
1984 audit_sdev_entry_free(ase);
1985 asdev->asdev_qlen--;
1986 asdev->asdev_qoffset = 0;
1987 }
6d2010ae
A
1988 }
1989 AUDIT_SDEV_UNLOCK(asdev);
1990 AUDIT_SDEV_SX_XUNLOCK(asdev);
0a7de745 1991 return 0;
6d2010ae
A
1992}
1993
1994/*
1995 * Audit session device poll method.
1996 */
1997static int
1998audit_sdev_poll(dev_t dev, int events, void *wql, struct proc *p)
1999{
2000 struct audit_sdev *asdev;
2001 int revents;
2002
2003 revents = 0;
2004 asdev = audit_sdev_dtab[minor(dev)];
2005 KASSERT(NULL != asdev, ("audit_sdev_poll: asdev == NULL"));
2006
2007 if (events & (POLLIN | POLLRDNORM)) {
2008 AUDIT_SDEV_LOCK(asdev);
0a7de745 2009 if (NULL != TAILQ_FIRST(&asdev->asdev_queue)) {
6d2010ae 2010 revents |= events & (POLLIN | POLLRDNORM);
0a7de745 2011 } else {
6d2010ae 2012 selrecord(p, &asdev->asdev_selinfo, wql);
0a7de745 2013 }
6d2010ae
A
2014 AUDIT_SDEV_UNLOCK(asdev);
2015 }
0a7de745 2016 return revents;
6d2010ae
A
2017}
2018
2019/*
2020 * Audit sdev clone routine. Provides a new minor number or returns -1.
2021 * This called with DEVFS_LOCK held.
2022 */
2023static int
2024audit_sdev_clone(__unused dev_t dev, int action)
2025{
2026 int i;
2027
2028 if (DEVFS_CLONE_ALLOC == action) {
0a7de745
A
2029 for (i = 0; i < MAX_AUDIT_SDEVS; i++) {
2030 if (NULL == audit_sdev_dtab[i]) {
2031 return i;
2032 }
2033 }
6d2010ae 2034
0a7de745 2035 /*
6d2010ae
A
2036 * This really should return -1 here but that seems to
2037 * hang things in devfs. We instead return 0 and let
2038 * audit_sdev_open tell userland the bad news.
2039 */
0a7de745 2040 return 0;
6d2010ae
A
2041 }
2042
0a7de745 2043 return -1;
6d2010ae
A
2044}
2045
2046static int
2047audit_sdev_init(void)
2048{
2049 dev_t dev;
2050
2051 TAILQ_INIT(&audit_sdev_list);
2052 AUDIT_SDEV_LIST_LOCK_INIT();
2053
2054 audit_sdev_major = cdevsw_add(-1, &audit_sdev_cdevsw);
0a7de745
A
2055 if (audit_sdev_major < 0) {
2056 return KERN_FAILURE;
2057 }
6d2010ae
A
2058
2059 dev = makedev(audit_sdev_major, 0);
2060 devnode = devfs_make_node_clone(dev, DEVFS_CHAR, UID_ROOT, GID_WHEEL,
2061 0644, audit_sdev_clone, AUDIT_SDEV_NAME, 0);
2062
0a7de745
A
2063 if (NULL == devnode) {
2064 return KERN_FAILURE;
2065 }
6d2010ae 2066
0a7de745 2067 return KERN_SUCCESS;
6d2010ae
A
2068}
2069
2070/* XXXss
0a7de745
A
2071 * static int
2072 * audit_sdev_shutdown(void)
2073 * {
2074 *
2075 * devfs_remove(devnode);
2076 * (void) cdevsw_remove(audit_sdev_major, &audit_sdev_cdevsw);
2077 *
2078 * return (KERN_SUCCESS);
2079 * }
2080 */
6d2010ae 2081
b0d623f7
A
2082#else
2083
2084int
2085audit_session_self(proc_t p, struct audit_session_self_args *uap,
2086 mach_port_name_t *ret_port)
2087{
2088#pragma unused(p, uap, ret_port)
2089
0a7de745 2090 return ENOSYS;
b0d623f7
A
2091}
2092
2093int
2094audit_session_join(proc_t p, struct audit_session_join_args *uap,
2095 au_asid_t *ret_asid)
2096{
2097#pragma unused(p, uap, ret_asid)
2098
0a7de745 2099 return ENOSYS;
b0d623f7
A
2100}
2101
6d2010ae
A
2102int
2103audit_session_port(proc_t p, struct audit_session_port_args *uap, int *retval)
2104{
2105#pragma unused(p, uap, retval)
2106
0a7de745 2107 return ENOSYS;
6d2010ae
A
2108}
2109
b0d623f7 2110#endif /* CONFIG_AUDIT */