audit_worker.c (162508) | audit_worker.c (162599) |
---|---|
1/* 2 * Copyright (c) 1999-2005 Apple Computer, Inc. 3 * Copyright (c) 2006 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: --- 13 unchanged lines hidden (view full) --- 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 * | 1/* 2 * Copyright (c) 1999-2005 Apple Computer, Inc. 3 * Copyright (c) 2006 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: --- 13 unchanged lines hidden (view full) --- 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 * $FreeBSD: head/sys/security/audit/audit_worker.c 162508 2006-09-21 07:27:02Z rwatson $ | 30 * $FreeBSD: head/sys/security/audit/audit_worker.c 162599 2006-09-24 13:35:58Z rwatson $ |
31 */ 32 33#include <sys/param.h> 34#include <sys/condvar.h> 35#include <sys/conf.h> 36#include <sys/file.h> 37#include <sys/filedesc.h> 38#include <sys/fcntl.h> --- 58 unchanged lines hidden (view full) --- 97static struct ucred *audit_replacement_cred; 98 99/* 100 * Flags related to Kernel->user-space communication. 101 */ 102static int audit_file_rotate_wait; 103 104/* | 31 */ 32 33#include <sys/param.h> 34#include <sys/condvar.h> 35#include <sys/conf.h> 36#include <sys/file.h> 37#include <sys/filedesc.h> 38#include <sys/fcntl.h> --- 58 unchanged lines hidden (view full) --- 97static struct ucred *audit_replacement_cred; 98 99/* 100 * Flags related to Kernel->user-space communication. 101 */ 102static int audit_file_rotate_wait; 103 104/* |
105 * XXXAUDIT: Should adjust comments below to make it clear that we get to 106 * this point only if we believe we have storage, so not having space here is 107 * a violation of invariants derived from administrative procedures. I.e., 108 * someone else has written to the audit partition, leaving less space than 109 * we accounted for. | 105 * Write an audit record to a file, performed as the last stage after both 106 * preselection and BSM conversion. Both space management and write failures 107 * are handled in this function. 108 * 109 * No attempt is made to deal with possible failure to deliver a trigger to 110 * the audit daemon, since the message is asynchronous anyway. |
110 */ | 111 */ |
111static int | 112static void |
112audit_record_write(struct vnode *vp, struct ucred *cred, struct thread *td, 113 void *data, size_t len) 114{ | 113audit_record_write(struct vnode *vp, struct ucred *cred, struct thread *td, 114 void *data, size_t len) 115{ |
115 int ret; 116 long temp; 117 struct vattr vattr; | 116 static struct timeval last_lowspace_trigger; 117 static struct timeval last_fail; 118 static int cur_lowspace_trigger; |
118 struct statfs *mnt_stat; | 119 struct statfs *mnt_stat; |
119 int vfslocked; | 120 int error, vfslocked; 121 static int cur_fail; 122 struct vattr vattr; 123 long temp; |
120 121 if (vp == NULL) | 124 125 if (vp == NULL) |
122 return (0); | 126 return; |
123 124 mnt_stat = &vp->v_mount->mnt_stat; 125 vfslocked = VFS_LOCK_GIANT(vp->v_mount); 126 127 /* 128 * First, gather statistics on the audit log file and file system so | 127 128 mnt_stat = &vp->v_mount->mnt_stat; 129 vfslocked = VFS_LOCK_GIANT(vp->v_mount); 130 131 /* 132 * First, gather statistics on the audit log file and file system so |
129 * that we know how we're doing on space. In both cases, if we're 130 * unable to perform the operation, we drop the record and return. 131 * However, this is arguably an assertion failure. 132 * XXX Need a FreeBSD equivalent. | 133 * that we know how we're doing on space. Consider failure of these 134 * operations to indicate a future inability to write to the file. |
133 */ | 135 */ |
134 ret = VFS_STATFS(vp->v_mount, mnt_stat, td); 135 if (ret) 136 goto out; 137 | 136 error = VFS_STATFS(vp->v_mount, mnt_stat, td); 137 if (error) 138 goto fail; |
138 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); | 139 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); |
139 ret = VOP_GETATTR(vp, &vattr, cred, td); | 140 error = VOP_GETATTR(vp, &vattr, cred, td); |
140 VOP_UNLOCK(vp, 0, td); | 141 VOP_UNLOCK(vp, 0, td); |
141 if (ret) 142 goto out; 143 144 /* update the global stats struct */ | 142 if (error) 143 goto fail; |
145 audit_fstat.af_currsz = vattr.va_size; 146 147 /* | 144 audit_fstat.af_currsz = vattr.va_size; 145 146 /* |
148 * XXX Need to decide what to do if the trigger to the audit daemon 149 * fails. | 147 * We handle four different space-related limits: 148 * 149 * - A fixed (hard) limit on the minimum free blocks we require on 150 * the file system, and results in record loss, a trigger, and 151 * possible fail stop due to violating invariants. 152 * 153 * - An administrative (soft) limit, which when fallen below, results 154 * in the kernel notifying the audit daemon of low space. 155 * 156 * - An audit trail size limit, which when gone above, results in the 157 * kernel notifying the audit daemon that rotation is desired. 158 * 159 * - The total depth of the kernel audit record exceeding free space, 160 * which can lead to possible fail stop (with drain), in order to 161 * prevent violating invariants. Failure here doesn't halt 162 * immediately, but prevents new records from being generated. 163 * 164 * Possibly, the last of these should be handled differently, always 165 * allowing a full queue to be lost, rather than trying to prevent 166 * loss. 167 * 168 * First, handle the hard limit, which generates a trigger and may 169 * fail stop. This is handled in the same manner as ENOSPC from 170 * VOP_WRITE, and results in record loss. |
150 */ | 171 */ |
172 if (mnt_stat->f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) { 173 error = ENOSPC; 174 goto fail_enospc; 175 } |
|
151 152 /* | 176 177 /* |
153 * If we fall below minimum free blocks (hard limit), tell the audit 154 * daemon to force a rotation off of the file system. We also stop 155 * writing, which means this audit record is probably lost. If we 156 * fall below the minimum percent free blocks (soft limit), then 157 * kindly suggest to the audit daemon to do something. | 178 * Second, handle falling below the soft limit, if defined; we send 179 * the daemon a trigger and continue processing the record. Triggers 180 * are limited to 1/sec. |
158 */ | 181 */ |
159 if (mnt_stat->f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) { 160 (void)send_trigger(AUDIT_TRIGGER_NO_SPACE); | 182 if (audit_qctrl.aq_minfree != 0) { |
161 /* | 183 /* |
162 * Hopefully userspace did something about all the previous 163 * triggers that were sent prior to this critical condition. 164 * If fail-stop is set, then we're done; goodnight Gracie. | 184 * XXXAUDIT: Check math and block size calculations here. |
165 */ | 185 */ |
166 if (audit_fail_stop) 167 panic("Audit log space exhausted and fail-stop set."); 168 else { 169 audit_suspended = 1; 170 ret = ENOSPC; 171 goto out; 172 } 173 } else 174 /* 175 * Send a message to the audit daemon that disk space is 176 * getting low. 177 * 178 * XXXAUDIT: Check math and block size calculation here. 179 */ 180 if (audit_qctrl.aq_minfree != 0) { 181 temp = mnt_stat->f_blocks / (100 / 182 audit_qctrl.aq_minfree); 183 if (mnt_stat->f_bfree < temp) | 186 temp = mnt_stat->f_blocks / (100 / audit_qctrl.aq_minfree); 187 if (mnt_stat->f_bfree < temp) { 188 if (ppsratecheck(&last_lowspace_trigger, 189 &cur_lowspace_trigger, 1)) { |
184 (void)send_trigger(AUDIT_TRIGGER_LOW_SPACE); | 190 (void)send_trigger(AUDIT_TRIGGER_LOW_SPACE); |
191 printf("Warning: audit space low\n"); 192 } |
|
185 } | 193 } |
194 } |
|
186 187 /* | 195 196 /* |
188 * Check if the current log file is full; if so, call for a log 189 * rotate. This is not an exact comparison; we may write some records 190 * over the limit. If that's not acceptable, then add a fudge factor 191 * here. | 197 * If the current file is getting full, generate a rotation trigger 198 * to the daemon. This is only approximate, which is fine as more 199 * records may be generated before the daemon rotates the file. |
192 */ | 200 */ |
193 if ((audit_fstat.af_filesz != 0) && 194 (audit_file_rotate_wait == 0) && | 201 if ((audit_fstat.af_filesz != 0) && (audit_file_rotate_wait == 0) && |
195 (vattr.va_size >= audit_fstat.af_filesz)) { 196 audit_file_rotate_wait = 1; 197 (void)send_trigger(AUDIT_TRIGGER_ROTATE_KERNEL); 198 } 199 200 /* 201 * If the estimated amount of audit data in the audit event queue 202 * (plus records allocated but not yet queued) has reached the amount 203 * of free space on the disk, then we need to go into an audit fail 204 * stop state, in which we do not permit the allocation/committing of | 202 (vattr.va_size >= audit_fstat.af_filesz)) { 203 audit_file_rotate_wait = 1; 204 (void)send_trigger(AUDIT_TRIGGER_ROTATE_KERNEL); 205 } 206 207 /* 208 * If the estimated amount of audit data in the audit event queue 209 * (plus records allocated but not yet queued) has reached the amount 210 * of free space on the disk, then we need to go into an audit fail 211 * stop state, in which we do not permit the allocation/committing of |
205 * any new audit records. We continue to process packets but don't | 212 * any new audit records. We continue to process records but don't |
206 * allow any activities that might generate new records. In the 207 * future, we might want to detect when space is available again and 208 * allow operation to continue, but this behavior is sufficient to 209 * meet fail stop requirements in CAPP. 210 */ | 213 * allow any activities that might generate new records. In the 214 * future, we might want to detect when space is available again and 215 * allow operation to continue, but this behavior is sufficient to 216 * meet fail stop requirements in CAPP. 217 */ |
211 if (audit_fail_stop && 212 (unsigned long) 213 ((audit_q_len + audit_pre_q_len + 1) * MAX_AUDIT_RECORD_SIZE) / 214 mnt_stat->f_bsize >= (unsigned long)(mnt_stat->f_bfree)) { 215 printf("audit_record_write: free space below size of audit " 216 "queue, failing stop\n"); 217 audit_in_failure = 1; | 218 if (audit_fail_stop) { 219 if ((unsigned long)((audit_q_len + audit_pre_q_len + 1) * 220 MAX_AUDIT_RECORD_SIZE) / mnt_stat->f_bsize >= 221 (unsigned long)(mnt_stat->f_bfree)) { 222 if (ppsratecheck(&last_fail, &cur_fail, 1)) 223 printf("audit_record_write: free space " 224 "below size of audit queue, failing " 225 "stop\n"); 226 audit_in_failure = 1; 227 } else if (audit_in_failure) { 228 /* 229 * XXXRW: If we want to handle recovery, this is the 230 * spot to do it: unset audit_in_failure, and issue a 231 * wakeup on the cv. 232 */ 233 } |
218 } 219 | 234 } 235 |
220 ret = vn_rdwr(UIO_WRITE, vp, data, len, (off_t)0, UIO_SYSSPACE, | 236 error = vn_rdwr(UIO_WRITE, vp, data, len, (off_t)0, UIO_SYSSPACE, |
221 IO_APPEND|IO_UNIT, cred, NULL, NULL, td); | 237 IO_APPEND|IO_UNIT, cred, NULL, NULL, td); |
238 if (error == ENOSPC) 239 goto fail_enospc; 240 else if (error) 241 goto fail; |
|
222 | 242 |
223out: | |
224 /* | 243 /* |
225 * When we're done processing the current record, we have to check to 226 * see if we're in a failure mode, and if so, whether this was the 227 * last record left to be drained. If we're done draining, then we 228 * fsync the vnode and panic. | 244 * Catch completion of a queue drain here; if we're draining and the 245 * queue is now empty, fail stop. That audit_fail_stop is implicitly 246 * true, since audit_in_failure can only be set of audit_fail_stop is 247 * set. 248 * 249 * XXXRW: If we handle recovery from audit_in_failure, then we need 250 * to make panic here conditional. |
229 */ | 251 */ |
230 if (audit_in_failure && audit_q_len == 0 && audit_pre_q_len == 0) { | 252 if (audit_in_failure) { 253 if (audit_q_len == 0 && audit_pre_q_len == 0) { 254 VOP_LOCK(vp, LK_DRAIN | LK_INTERLOCK, td); 255 (void)VOP_FSYNC(vp, MNT_WAIT, td); 256 VOP_UNLOCK(vp, 0, td); 257 panic("Audit store overflow; record queue drained."); 258 } 259 } 260 261 VFS_UNLOCK_GIANT(vfslocked); 262 return; 263 264fail_enospc: 265 /* 266 * ENOSPC is considered a special case with respect to failures, as 267 * this can reflect either our preemptive detection of insufficient 268 * space, or ENOSPC returned by the vnode write call. 269 */ 270 if (audit_fail_stop) { |
231 VOP_LOCK(vp, LK_DRAIN | LK_INTERLOCK, td); 232 (void)VOP_FSYNC(vp, MNT_WAIT, td); 233 VOP_UNLOCK(vp, 0, td); | 271 VOP_LOCK(vp, LK_DRAIN | LK_INTERLOCK, td); 272 (void)VOP_FSYNC(vp, MNT_WAIT, td); 273 VOP_UNLOCK(vp, 0, td); |
234 panic("Audit store overflow; record queue drained."); | 274 panic("Audit log space exhausted and fail-stop set."); |
235 } | 275 } |
276 (void)send_trigger(AUDIT_TRIGGER_NO_SPACE); 277 audit_suspended = 1; |
|
236 | 278 |
279 /* FALLTHROUGH */ 280fail: 281 /* 282 * We have failed to write to the file, so the current record is 283 * lost, which may require an immediate system halt. 284 */ 285 if (audit_panic_on_write_fail) { 286 VOP_LOCK(vp, LK_DRAIN | LK_INTERLOCK, td); 287 (void)VOP_FSYNC(vp, MNT_WAIT, td); 288 VOP_UNLOCK(vp, 0, td); 289 panic("audit_worker: write error %d\n", error); 290 } else if (ppsratecheck(&last_fail, &cur_fail, 1)) 291 printf("audit_worker: write error %d\n", error); |
|
237 VFS_UNLOCK_GIANT(vfslocked); | 292 VFS_UNLOCK_GIANT(vfslocked); |
238 239 return (ret); | |
240} 241 242/* 243 * If an appropriate signal has been received rotate the audit log based on 244 * the global replacement variables. Signal consumers as needed that the 245 * rotation has taken place. 246 * 247 * XXXRW: The global variables and CVs used to signal the audit_worker to --- 65 unchanged lines hidden (view full) --- 313 */ 314static void 315audit_worker_process_record(struct vnode *audit_vp, struct ucred *audit_cred, 316 struct thread *audit_td, struct kaudit_record *ar) 317{ 318 struct au_record *bsm; 319 au_class_t class; 320 au_event_t event; | 293} 294 295/* 296 * If an appropriate signal has been received rotate the audit log based on 297 * the global replacement variables. Signal consumers as needed that the 298 * rotation has taken place. 299 * 300 * XXXRW: The global variables and CVs used to signal the audit_worker to --- 65 unchanged lines hidden (view full) --- 366 */ 367static void 368audit_worker_process_record(struct vnode *audit_vp, struct ucred *audit_cred, 369 struct thread *audit_td, struct kaudit_record *ar) 370{ 371 struct au_record *bsm; 372 au_class_t class; 373 au_event_t event; |
321 int error, ret; | |
322 au_id_t auid; | 374 au_id_t auid; |
323 int sorf; | 375 int error, sorf; |
324 | 376 |
377 /* 378 * First, handle the user record, if any: commit to the system trail 379 * and audit pipes as selected. 380 */ |
|
325 if ((ar->k_ar_commit & AR_COMMIT_USER) && | 381 if ((ar->k_ar_commit & AR_COMMIT_USER) && |
326 (ar->k_ar_commit & AR_PRESELECT_USER_TRAIL)) { 327 error = audit_record_write(audit_vp, audit_cred, audit_td, | 382 (ar->k_ar_commit & AR_PRESELECT_USER_TRAIL)) 383 audit_record_write(audit_vp, audit_cred, audit_td, |
328 ar->k_udata, ar->k_ulen); | 384 ar->k_udata, ar->k_ulen); |
329 if (error && audit_panic_on_write_fail) 330 panic("audit_worker: write error %d\n", error); 331 else if (error) 332 printf("audit_worker: write error %d\n", error); 333 } | |
334 335 if ((ar->k_ar_commit & AR_COMMIT_USER) && 336 (ar->k_ar_commit & AR_PRESELECT_USER_PIPE)) 337 audit_pipe_submit_user(ar->k_udata, ar->k_ulen); 338 339 if (!(ar->k_ar_commit & AR_COMMIT_KERNEL) || 340 ((ar->k_ar_commit & AR_PRESELECT_PIPE) == 0 && 341 (ar->k_ar_commit & AR_PRESELECT_TRAIL) == 0)) 342 return; 343 344 auid = ar->k_ar.ar_subj_auid; 345 event = ar->k_ar.ar_event; 346 class = au_event_class(event); 347 if (ar->k_ar.ar_errno == 0) 348 sorf = AU_PRS_SUCCESS; 349 else 350 sorf = AU_PRS_FAILURE; 351 | 385 386 if ((ar->k_ar_commit & AR_COMMIT_USER) && 387 (ar->k_ar_commit & AR_PRESELECT_USER_PIPE)) 388 audit_pipe_submit_user(ar->k_udata, ar->k_ulen); 389 390 if (!(ar->k_ar_commit & AR_COMMIT_KERNEL) || 391 ((ar->k_ar_commit & AR_PRESELECT_PIPE) == 0 && 392 (ar->k_ar_commit & AR_PRESELECT_TRAIL) == 0)) 393 return; 394 395 auid = ar->k_ar.ar_subj_auid; 396 event = ar->k_ar.ar_event; 397 class = au_event_class(event); 398 if (ar->k_ar.ar_errno == 0) 399 sorf = AU_PRS_SUCCESS; 400 else 401 sorf = AU_PRS_FAILURE; 402 |
352 ret = kaudit_to_bsm(ar, &bsm); 353 switch (ret) { | 403 error = kaudit_to_bsm(ar, &bsm); 404 switch (error) { |
354 case BSM_NOAUDIT: 355 return; 356 357 case BSM_FAILURE: 358 printf("audit_worker_process_record: BSM_FAILURE\n"); 359 return; 360 361 case BSM_SUCCESS: 362 break; 363 364 default: | 405 case BSM_NOAUDIT: 406 return; 407 408 case BSM_FAILURE: 409 printf("audit_worker_process_record: BSM_FAILURE\n"); 410 return; 411 412 case BSM_SUCCESS: 413 break; 414 415 default: |
365 panic("kaudit_to_bsm returned %d", ret); | 416 panic("kaudit_to_bsm returned %d", error); |
366 } 367 | 417 } 418 |
368 if (ar->k_ar_commit & AR_PRESELECT_TRAIL) { 369 error = audit_record_write(audit_vp, audit_cred, 370 audit_td, bsm->data, bsm->len); 371 if (error && audit_panic_on_write_fail) 372 panic("audit_worker: write error %d\n", 373 error); 374 else if (error) 375 printf("audit_worker: write error %d\n", 376 error); 377 } | 419 if (ar->k_ar_commit & AR_PRESELECT_TRAIL) 420 audit_record_write(audit_vp, audit_cred, audit_td, bsm->data, 421 bsm->len); |
378 379 if (ar->k_ar_commit & AR_PRESELECT_PIPE) 380 audit_pipe_submit(auid, event, class, sorf, 381 ar->k_ar_commit & AR_PRESELECT_TRAIL, bsm->data, 382 bsm->len); | 422 423 if (ar->k_ar_commit & AR_PRESELECT_PIPE) 424 audit_pipe_submit(auid, event, class, sorf, 425 ar->k_ar_commit & AR_PRESELECT_TRAIL, bsm->data, 426 bsm->len); |
427 |
|
383 kau_free(bsm); 384} 385 386/* 387 * The audit_worker thread is responsible for watching the event queue, 388 * dequeueing records, converting them to BSM format, and committing them to 389 * disk. In order to minimize lock thrashing, records are dequeued in sets 390 * to a thread-local work queue. In addition, the audit_work performs the --- 145 unchanged lines hidden --- | 428 kau_free(bsm); 429} 430 431/* 432 * The audit_worker thread is responsible for watching the event queue, 433 * dequeueing records, converting them to BSM format, and committing them to 434 * disk. In order to minimize lock thrashing, records are dequeued in sets 435 * to a thread-local work queue. In addition, the audit_work performs the --- 145 unchanged lines hidden --- |