]>
Commit | Line | Data |
---|---|---|
b0d623f7 | 1 | /*- |
39037602 | 2 | * Copyright (c) 1999-2016 Apple Inc. |
b0d623f7 A |
3 | * Copyright (c) 2006-2008 Robert N. M. Watson |
4 | * All rights reserved. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of | |
15 | * its contributors may be used to endorse or promote products derived | |
16 | * from this software without specific prior written permission. | |
17 | * | |
18 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND | |
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
21 | * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR | |
22 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
26 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
27 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
28 | * POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | ||
31 | #include <sys/param.h> | |
32 | #include <sys/fcntl.h> | |
33 | #include <sys/kernel.h> | |
34 | #include <sys/lock.h> | |
35 | #include <sys/namei.h> | |
36 | #include <sys/proc_internal.h> | |
37 | #include <sys/kauth.h> | |
38 | #include <sys/queue.h> | |
39 | #include <sys/systm.h> | |
40 | #include <sys/time.h> | |
41 | #include <sys/ucred.h> | |
42 | #include <sys/uio.h> | |
43 | #include <sys/unistd.h> | |
44 | #include <sys/file_internal.h> | |
45 | #include <sys/vnode_internal.h> | |
46 | #include <sys/user.h> | |
47 | #include <sys/syscall.h> | |
48 | #include <sys/malloc.h> | |
49 | #include <sys/un.h> | |
50 | #include <sys/sysent.h> | |
51 | #include <sys/sysproto.h> | |
52 | #include <sys/vfs_context.h> | |
53 | #include <sys/domain.h> | |
54 | #include <sys/protosw.h> | |
55 | #include <sys/socketvar.h> | |
56 | ||
57 | #include <bsm/audit.h> | |
58 | #include <bsm/audit_internal.h> | |
59 | #include <bsm/audit_kevents.h> | |
60 | ||
61 | #include <security/audit/audit.h> | |
62 | #include <security/audit/audit_bsd.h> | |
63 | #include <security/audit/audit_private.h> | |
64 | ||
65 | #include <mach/host_priv.h> | |
66 | #include <mach/host_special_ports.h> | |
67 | #include <mach/audit_triggers_server.h> | |
68 | ||
69 | #include <kern/host.h> | |
70 | #include <kern/zalloc.h> | |
b0d623f7 A |
71 | #include <kern/sched_prim.h> |
72 | #include <kern/task.h> | |
b0d623f7 A |
73 | |
74 | #include <net/route.h> | |
75 | ||
76 | #include <netinet/in.h> | |
77 | #include <netinet/in_pcb.h> | |
78 | ||
79 | /* | |
80 | * Worker thread that will schedule disk I/O, etc. | |
81 | */ | |
82 | static thread_t audit_thread; | |
83 | ||
84 | /* | |
85 | * audit_ctx and audit_vp are the stored credential and vnode to use for | |
86 | * active audit trail. They are protected by audit_worker_sl, which will be | |
87 | * held across all I/O and all rotation to prevent them from being replaced | |
88 | * (rotated) while in use. The audit_file_rotate_wait flag is set when the | |
89 | * kernel has delivered a trigger to auditd to rotate the trail, and is | |
90 | * cleared when the next rotation takes place. It is also protected by | |
91 | * audit_worker_sl. | |
92 | */ | |
0a7de745 A |
93 | static int audit_file_rotate_wait; |
94 | static struct slck audit_worker_sl; | |
95 | static struct vfs_context audit_ctx; | |
96 | static struct vnode *audit_vp; | |
97 | ||
98 | #define AUDIT_WORKER_SX_INIT() slck_init(&audit_worker_sl, \ | |
99 | "audit_worker_sl") | |
100 | #define AUDIT_WORKER_SX_XLOCK() slck_lock(&audit_worker_sl) | |
101 | #define AUDIT_WORKER_SX_XUNLOCK() slck_unlock(&audit_worker_sl) | |
102 | #define AUDIT_WORKER_SX_ASSERT() slck_assert(&audit_worker_sl, SL_OWNED) | |
103 | #define AUDIT_WORKER_SX_DESTROY() slck_destroy(&audit_worker_sl) | |
b0d623f7 A |
104 | |
105 | /* | |
316670eb | 106 | * The audit_q_draining flag is set when audit is disabled and the audit |
b0d623f7 A |
107 | * worker queue is being drained. |
108 | */ | |
0a7de745 | 109 | static int audit_q_draining; |
b0d623f7 A |
110 | |
111 | /* | |
112 | * The special kernel audit record, audit_drain_kar, is used to mark the end of | |
113 | * the queue when draining it. | |
114 | */ | |
0a7de745 | 115 | static struct kaudit_record audit_drain_kar = { |
b0d623f7 A |
116 | .k_ar = { |
117 | .ar_event = AUE_NULL, | |
118 | }, | |
119 | .k_ar_commit = AR_DRAIN_QUEUE, | |
120 | }; | |
121 | ||
122 | /* | |
123 | * Write an audit record to a file, performed as the last stage after both | |
124 | * preselection and BSM conversion. Both space management and write failures | |
125 | * are handled in this function. | |
126 | * | |
127 | * No attempt is made to deal with possible failure to deliver a trigger to | |
128 | * the audit daemon, since the message is asynchronous anyway. | |
129 | */ | |
130 | static void | |
131 | audit_record_write(struct vnode *vp, struct vfs_context *ctx, void *data, | |
132 | size_t len) | |
133 | { | |
134 | static struct timeval last_lowspace_trigger; | |
135 | static struct timeval last_fail; | |
136 | static int cur_lowspace_trigger; | |
137 | struct vfsstatfs *mnt_stat; | |
138 | int error; | |
139 | static int cur_fail; | |
140 | uint64_t temp; | |
141 | off_t file_size; | |
142 | ||
0a7de745 | 143 | AUDIT_WORKER_SX_ASSERT(); /* audit_file_rotate_wait. */ |
b0d623f7 | 144 | |
0a7de745 | 145 | if (vp == NULL) { |
b0d623f7 | 146 | return; |
0a7de745 | 147 | } |
b0d623f7 | 148 | |
0a7de745 | 149 | if (vnode_getwithref(vp)) { |
b0d623f7 | 150 | return /*(ENOENT)*/; |
0a7de745 | 151 | } |
b0d623f7 A |
152 | |
153 | mnt_stat = &vp->v_mount->mnt_vfsstat; | |
154 | ||
155 | /* | |
156 | * First, gather statistics on the audit log file and file system so | |
157 | * that we know how we're doing on space. Consider failure of these | |
158 | * operations to indicate a future inability to write to the file. | |
159 | */ | |
160 | error = vfs_update_vfsstat(vp->v_mount, ctx, VFS_KERNEL_EVENT); | |
0a7de745 | 161 | if (error) { |
b0d623f7 | 162 | goto fail; |
0a7de745 | 163 | } |
b0d623f7 | 164 | error = vnode_size(vp, &file_size, ctx); |
0a7de745 | 165 | if (error) { |
b0d623f7 | 166 | goto fail; |
0a7de745 | 167 | } |
b0d623f7 A |
168 | audit_fstat.af_currsz = (u_quad_t)file_size; |
169 | ||
170 | /* | |
171 | * We handle four different space-related limits: | |
172 | * | |
173 | * - A fixed (hard) limit on the minimum free blocks we require on | |
174 | * the file system, and results in record loss, a trigger, and | |
175 | * possible fail stop due to violating invariants. | |
176 | * | |
177 | * - An administrative (soft) limit, which when fallen below, results | |
178 | * in the kernel notifying the audit daemon of low space. | |
179 | * | |
180 | * - An audit trail size limit, which when gone above, results in the | |
181 | * kernel notifying the audit daemon that rotation is desired. | |
182 | * | |
183 | * - The total depth of the kernel audit record exceeding free space, | |
184 | * which can lead to possible fail stop (with drain), in order to | |
185 | * prevent violating invariants. Failure here doesn't halt | |
186 | * immediately, but prevents new records from being generated. | |
187 | * | |
188 | * Possibly, the last of these should be handled differently, always | |
189 | * allowing a full queue to be lost, rather than trying to prevent | |
190 | * loss. | |
191 | * | |
192 | * First, handle the hard limit, which generates a trigger and may | |
193 | * fail stop. This is handled in the same manner as ENOSPC from | |
194 | * VOP_WRITE, and results in record loss. | |
195 | */ | |
196 | if (mnt_stat->f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) { | |
197 | error = ENOSPC; | |
198 | goto fail_enospc; | |
199 | } | |
200 | ||
201 | /* | |
202 | * Second, handle falling below the soft limit, if defined; we send | |
203 | * the daemon a trigger and continue processing the record. Triggers | |
204 | * are limited to 1/sec. | |
205 | */ | |
206 | if (audit_qctrl.aq_minfree != 0) { | |
207 | temp = mnt_stat->f_blocks / (100 / audit_qctrl.aq_minfree); | |
6d2010ae A |
208 | if (mnt_stat->f_bfree < temp && |
209 | ppsratecheck(&last_lowspace_trigger, | |
0a7de745 A |
210 | &cur_lowspace_trigger, 1)) { |
211 | (void)audit_send_trigger( | |
212 | AUDIT_TRIGGER_LOW_SPACE); | |
213 | } | |
b0d623f7 A |
214 | } |
215 | ||
216 | /* | |
217 | * If the current file is getting full, generate a rotation trigger | |
218 | * to the daemon. This is only approximate, which is fine as more | |
219 | * records may be generated before the daemon rotates the file. | |
220 | */ | |
221 | if ((audit_fstat.af_filesz != 0) && (audit_file_rotate_wait == 0) && | |
222 | ((u_quad_t)file_size >= audit_fstat.af_filesz)) { | |
223 | AUDIT_WORKER_SX_ASSERT(); | |
224 | ||
225 | audit_file_rotate_wait = 1; | |
226 | (void)audit_send_trigger(AUDIT_TRIGGER_ROTATE_KERNEL); | |
227 | } | |
228 | ||
229 | /* | |
230 | * If the estimated amount of audit data in the audit event queue | |
231 | * (plus records allocated but not yet queued) has reached the amount | |
232 | * of free space on the disk, then we need to go into an audit fail | |
233 | * stop state, in which we do not permit the allocation/committing of | |
234 | * any new audit records. We continue to process records but don't | |
235 | * allow any activities that might generate new records. In the | |
236 | * future, we might want to detect when space is available again and | |
237 | * allow operation to continue, but this behavior is sufficient to | |
238 | * meet fail stop requirements in CAPP. | |
239 | */ | |
240 | if (audit_fail_stop) { | |
241 | if ((unsigned long)((audit_q_len + audit_pre_q_len + 1) * | |
242 | MAX_AUDIT_RECORD_SIZE) / mnt_stat->f_bsize >= | |
243 | (unsigned long)(mnt_stat->f_bfree)) { | |
0a7de745 | 244 | if (ppsratecheck(&last_fail, &cur_fail, 1)) { |
b0d623f7 A |
245 | printf("audit_record_write: free space " |
246 | "below size of audit queue, failing " | |
247 | "stop\n"); | |
0a7de745 | 248 | } |
b0d623f7 A |
249 | audit_in_failure = 1; |
250 | } else if (audit_in_failure) { | |
251 | /* | |
252 | * Note: if we want to handle recovery, this is the | |
253 | * spot to do it: unset audit_in_failure, and issue a | |
254 | * wakeup on the cv. | |
255 | */ | |
256 | } | |
257 | } | |
258 | ||
259 | error = vn_rdwr(UIO_WRITE, vp, data, len, (off_t)0, UIO_SYSSPACE, | |
0a7de745 | 260 | IO_APPEND | IO_UNIT, vfs_context_ucred(ctx), NULL, |
b0d623f7 | 261 | vfs_context_proc(ctx)); |
0a7de745 | 262 | if (error == ENOSPC) { |
b0d623f7 | 263 | goto fail_enospc; |
0a7de745 | 264 | } else if (error) { |
b0d623f7 | 265 | goto fail; |
0a7de745 | 266 | } |
b0d623f7 A |
267 | |
268 | /* | |
269 | * Catch completion of a queue drain here; if we're draining and the | |
270 | * queue is now empty, fail stop. That audit_fail_stop is implicitly | |
271 | * true, since audit_in_failure can only be set of audit_fail_stop is | |
272 | * set. | |
273 | * | |
274 | * Note: if we handle recovery from audit_in_failure, then we need to | |
275 | * make panic here conditional. | |
276 | */ | |
277 | if (audit_in_failure) { | |
278 | if (audit_q_len == 0 && audit_pre_q_len == 0) { | |
279 | (void)VNOP_FSYNC(vp, MNT_WAIT, ctx); | |
280 | panic("Audit store overflow; record queue drained."); | |
281 | } | |
282 | } | |
283 | ||
284 | vnode_put(vp); | |
285 | return; | |
286 | ||
287 | fail_enospc: | |
288 | /* | |
289 | * ENOSPC is considered a special case with respect to failures, as | |
290 | * this can reflect either our preemptive detection of insufficient | |
291 | * space, or ENOSPC returned by the vnode write call. | |
292 | */ | |
293 | if (audit_fail_stop) { | |
294 | (void)VNOP_FSYNC(vp, MNT_WAIT, ctx); | |
295 | panic("Audit log space exhausted and fail-stop set."); | |
296 | } | |
297 | (void)audit_send_trigger(AUDIT_TRIGGER_NO_SPACE); | |
298 | audit_suspended = 1; | |
299 | ||
300 | /* FALLTHROUGH */ | |
301 | fail: | |
302 | /* | |
303 | * We have failed to write to the file, so the current record is | |
304 | * lost, which may require an immediate system halt. | |
305 | */ | |
306 | if (audit_panic_on_write_fail) { | |
307 | (void)VNOP_FSYNC(vp, MNT_WAIT, ctx); | |
308 | panic("audit_worker: write error %d\n", error); | |
0a7de745 | 309 | } else if (ppsratecheck(&last_fail, &cur_fail, 1)) { |
b0d623f7 | 310 | printf("audit_worker: write error %d\n", error); |
0a7de745 | 311 | } |
b0d623f7 A |
312 | vnode_put(vp); |
313 | } | |
314 | ||
315 | /* | |
316 | * Given a kernel audit record, process as required. Kernel audit records | |
317 | * are converted to one, or possibly two, BSM records, depending on whether | |
318 | * there is a user audit record present also. Kernel records need be | |
319 | * converted to BSM before they can be written out. Both types will be | |
320 | * written to disk, and audit pipes. | |
321 | */ | |
322 | static void | |
323 | audit_worker_process_record(struct kaudit_record *ar) | |
324 | { | |
325 | struct au_record *bsm; | |
326 | au_class_t class; | |
327 | au_event_t event; | |
328 | au_id_t auid; | |
329 | int error, sorf; | |
330 | int trail_locked; | |
331 | ||
332 | /* | |
333 | * We hold the audit_worker_sl lock over both writes, if there are | |
334 | * two, so that the two records won't be split across a rotation and | |
335 | * end up in two different trail files. | |
336 | */ | |
337 | if (((ar->k_ar_commit & AR_COMMIT_USER) && | |
338 | (ar->k_ar_commit & AR_PRESELECT_USER_TRAIL)) || | |
339 | (ar->k_ar_commit & AR_PRESELECT_TRAIL)) { | |
340 | AUDIT_WORKER_SX_XLOCK(); | |
341 | trail_locked = 1; | |
0a7de745 | 342 | } else { |
b0d623f7 | 343 | trail_locked = 0; |
0a7de745 | 344 | } |
b0d623f7 A |
345 | |
346 | /* | |
347 | * First, handle the user record, if any: commit to the system trail | |
348 | * and audit pipes as selected. | |
349 | */ | |
350 | if ((ar->k_ar_commit & AR_COMMIT_USER) && | |
351 | (ar->k_ar_commit & AR_PRESELECT_USER_TRAIL)) { | |
352 | AUDIT_WORKER_SX_ASSERT(); | |
353 | audit_record_write(audit_vp, &audit_ctx, ar->k_udata, | |
354 | ar->k_ulen); | |
355 | } | |
356 | ||
357 | if ((ar->k_ar_commit & AR_COMMIT_USER) && | |
0a7de745 | 358 | (ar->k_ar_commit & AR_PRESELECT_USER_PIPE)) { |
b0d623f7 | 359 | audit_pipe_submit_user(ar->k_udata, ar->k_ulen); |
0a7de745 | 360 | } |
b0d623f7 A |
361 | |
362 | if (!(ar->k_ar_commit & AR_COMMIT_KERNEL) || | |
363 | ((ar->k_ar_commit & AR_PRESELECT_PIPE) == 0 && | |
6d2010ae | 364 | (ar->k_ar_commit & AR_PRESELECT_TRAIL) == 0 && |
0a7de745 | 365 | (ar->k_ar_commit & AR_PRESELECT_FILTER) == 0)) { |
b0d623f7 | 366 | goto out; |
0a7de745 | 367 | } |
b0d623f7 A |
368 | |
369 | auid = ar->k_ar.ar_subj_auid; | |
370 | event = ar->k_ar.ar_event; | |
371 | class = au_event_class(event); | |
0a7de745 | 372 | if (ar->k_ar.ar_errno == 0) { |
b0d623f7 | 373 | sorf = AU_PRS_SUCCESS; |
0a7de745 | 374 | } else { |
b0d623f7 | 375 | sorf = AU_PRS_FAILURE; |
0a7de745 | 376 | } |
b0d623f7 A |
377 | |
378 | error = kaudit_to_bsm(ar, &bsm); | |
379 | switch (error) { | |
380 | case BSM_NOAUDIT: | |
381 | goto out; | |
382 | ||
383 | case BSM_FAILURE: | |
384 | printf("audit_worker_process_record: BSM_FAILURE\n"); | |
385 | goto out; | |
386 | ||
387 | case BSM_SUCCESS: | |
388 | break; | |
389 | ||
390 | default: | |
391 | panic("kaudit_to_bsm returned %d", error); | |
392 | } | |
393 | ||
394 | if (ar->k_ar_commit & AR_PRESELECT_TRAIL) { | |
395 | AUDIT_WORKER_SX_ASSERT(); | |
396 | audit_record_write(audit_vp, &audit_ctx, bsm->data, bsm->len); | |
397 | } | |
398 | ||
0a7de745 | 399 | if (ar->k_ar_commit & AR_PRESELECT_PIPE) { |
b0d623f7 A |
400 | audit_pipe_submit(auid, event, class, sorf, |
401 | ar->k_ar_commit & AR_PRESELECT_TRAIL, bsm->data, | |
402 | bsm->len); | |
0a7de745 | 403 | } |
b0d623f7 | 404 | |
6d2010ae | 405 | if (ar->k_ar_commit & AR_PRESELECT_FILTER) { |
6d2010ae A |
406 | /* |
407 | * XXXss - This needs to be generalized so new filters can | |
408 | * be easily plugged in. | |
409 | */ | |
410 | audit_sdev_submit(auid, ar->k_ar.ar_subj_asid, bsm->data, | |
411 | bsm->len); | |
412 | } | |
413 | ||
b0d623f7 A |
414 | kau_free(bsm); |
415 | out: | |
0a7de745 | 416 | if (trail_locked) { |
b0d623f7 | 417 | AUDIT_WORKER_SX_XUNLOCK(); |
0a7de745 | 418 | } |
b0d623f7 A |
419 | } |
420 | ||
421 | /* | |
422 | * The audit_worker thread is responsible for watching the event queue, | |
423 | * dequeueing records, converting them to BSM format, and committing them to | |
424 | * disk. In order to minimize lock thrashing, records are dequeued in sets | |
425 | * to a thread-local work queue. | |
426 | * | |
427 | * Note: this means that the effect bound on the size of the pending record | |
428 | * queue is 2x the length of the global queue. | |
429 | */ | |
39037602 | 430 | __attribute__((noreturn)) |
b0d623f7 A |
431 | static void |
432 | audit_worker(void) | |
433 | { | |
434 | struct kaudit_queue ar_worklist; | |
435 | struct kaudit_record *ar; | |
436 | int lowater_signal; | |
437 | ||
0a7de745 | 438 | if (audit_ctx.vc_thread == NULL) { |
6d2010ae | 439 | audit_ctx.vc_thread = current_thread(); |
0a7de745 | 440 | } |
6d2010ae | 441 | |
b0d623f7 A |
442 | TAILQ_INIT(&ar_worklist); |
443 | mtx_lock(&audit_mtx); | |
444 | while (1) { | |
445 | mtx_assert(&audit_mtx, MA_OWNED); | |
446 | ||
447 | /* | |
448 | * Wait for a record. | |
449 | */ | |
0a7de745 | 450 | while (TAILQ_EMPTY(&audit_q)) { |
6d2010ae A |
451 | cv_wait_continuation(&audit_worker_cv, &audit_mtx, |
452 | (thread_continue_t)audit_worker); | |
0a7de745 | 453 | } |
b0d623f7 A |
454 | |
455 | /* | |
456 | * If there are records in the global audit record queue, | |
457 | * transfer them to a thread-local queue and process them | |
458 | * one by one. If we cross the low watermark threshold, | |
459 | * signal any waiting processes that they may wake up and | |
460 | * continue generating records. | |
461 | */ | |
462 | lowater_signal = 0; | |
463 | while ((ar = TAILQ_FIRST(&audit_q))) { | |
464 | TAILQ_REMOVE(&audit_q, ar, k_q); | |
465 | audit_q_len--; | |
0a7de745 | 466 | if (audit_q_len == audit_qctrl.aq_lowater) { |
b0d623f7 | 467 | lowater_signal++; |
0a7de745 | 468 | } |
b0d623f7 A |
469 | TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q); |
470 | } | |
0a7de745 | 471 | if (lowater_signal) { |
b0d623f7 | 472 | cv_broadcast(&audit_watermark_cv); |
0a7de745 | 473 | } |
b0d623f7 A |
474 | |
475 | mtx_unlock(&audit_mtx); | |
476 | while ((ar = TAILQ_FIRST(&ar_worklist))) { | |
477 | TAILQ_REMOVE(&ar_worklist, ar, k_q); | |
478 | if (ar->k_ar_commit & AR_DRAIN_QUEUE) { | |
316670eb | 479 | audit_q_draining = 0; |
b0d623f7 A |
480 | cv_broadcast(&audit_drain_cv); |
481 | } else { | |
482 | audit_worker_process_record(ar); | |
483 | audit_free(ar); | |
484 | } | |
485 | } | |
486 | mtx_lock(&audit_mtx); | |
487 | } | |
488 | } | |
489 | ||
490 | /* | |
491 | * audit_rotate_vnode() is called by a user or kernel thread to configure or | |
492 | * de-configure auditing on a vnode. The arguments are the replacement | |
493 | * credential (referenced) and vnode (referenced and opened) to substitute | |
494 | * for the current credential and vnode, if any. If either is set to NULL, | |
495 | * both should be NULL, and this is used to indicate that audit is being | |
496 | * disabled. Any previous cred/vnode will be closed and freed. We re-enable | |
497 | * generating rotation requests to auditd. | |
498 | */ | |
499 | void | |
500 | audit_rotate_vnode(kauth_cred_t cred, struct vnode *vp) | |
501 | { | |
502 | kauth_cred_t old_audit_cred; | |
503 | struct vnode *old_audit_vp; | |
b0d623f7 A |
504 | |
505 | KASSERT((cred != NULL && vp != NULL) || (cred == NULL && vp == NULL), | |
506 | ("audit_rotate_vnode: cred %p vp %p", cred, vp)); | |
507 | ||
b0d623f7 | 508 | |
b0d623f7 | 509 | mtx_lock(&audit_mtx); |
316670eb A |
510 | if (audit_enabled && (NULL == vp)) { |
511 | /* Auditing is currently enabled but will be disabled. */ | |
512 | ||
b0d623f7 | 513 | /* |
316670eb A |
514 | * Disable auditing now so nothing more is added while the |
515 | * audit worker thread is draining the audit record queue. | |
b0d623f7 | 516 | */ |
316670eb A |
517 | audit_enabled = 0; |
518 | ||
519 | /* | |
520 | * Drain the auditing queue by inserting a drain record at the | |
521 | * end of the queue and waiting for the audit worker thread | |
522 | * to find this record and signal that it is done before | |
523 | * we close the audit trail. | |
524 | */ | |
525 | audit_q_draining = 1; | |
0a7de745 | 526 | while (audit_q_len >= audit_qctrl.aq_hiwater) { |
b0d623f7 | 527 | cv_wait(&audit_watermark_cv, &audit_mtx); |
0a7de745 | 528 | } |
b0d623f7 A |
529 | TAILQ_INSERT_TAIL(&audit_q, &audit_drain_kar, k_q); |
530 | audit_q_len++; | |
531 | cv_signal(&audit_worker_cv); | |
b0d623f7 | 532 | } |
316670eb A |
533 | |
534 | /* If the audit queue is draining then wait here until it's done. */ | |
0a7de745 | 535 | while (audit_q_draining) { |
316670eb | 536 | cv_wait(&audit_drain_cv, &audit_mtx); |
0a7de745 | 537 | } |
b0d623f7 A |
538 | mtx_unlock(&audit_mtx); |
539 | ||
316670eb A |
540 | |
541 | /* | |
542 | * Rotate the vnode/cred, and clear the rotate flag so that we will | |
543 | * send a rotate trigger if the new file fills. | |
544 | */ | |
545 | AUDIT_WORKER_SX_XLOCK(); | |
546 | old_audit_cred = audit_ctx.vc_ucred; | |
547 | old_audit_vp = audit_vp; | |
548 | audit_ctx.vc_ucred = cred; | |
549 | audit_vp = vp; | |
550 | audit_file_rotate_wait = 0; | |
551 | audit_enabled = (audit_vp != NULL); | |
552 | AUDIT_WORKER_SX_XUNLOCK(); | |
553 | ||
b0d623f7 A |
554 | /* |
555 | * If there was an old vnode/credential, close and free. | |
556 | */ | |
557 | if (old_audit_vp != NULL) { | |
558 | if (vnode_get(old_audit_vp) == 0) { | |
559 | vn_close(old_audit_vp, AUDIT_CLOSE_FLAGS, | |
560 | vfs_context_kernel()); | |
561 | vnode_put(old_audit_vp); | |
0a7de745 | 562 | } else { |
b0d623f7 A |
563 | printf("audit_rotate_vnode: Couldn't close " |
564 | "audit file.\n"); | |
0a7de745 | 565 | } |
b0d623f7 A |
566 | kauth_cred_unref(&old_audit_cred); |
567 | } | |
568 | } | |
569 | ||
570 | void | |
571 | audit_worker_init(void) | |
572 | { | |
b0d623f7 A |
573 | AUDIT_WORKER_SX_INIT(); |
574 | kernel_thread_start((thread_continue_t)audit_worker, NULL, | |
575 | &audit_thread); | |
0a7de745 | 576 | if (audit_thread == THREAD_NULL) { |
b0d623f7 | 577 | panic("audit_worker_init: Couldn't create audit_worker thread"); |
0a7de745 | 578 | } |
b0d623f7 | 579 | } |