audit_syscalls.c revision 168933
1/* 2 * Copyright (c) 1999-2005 Apple Computer, 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 Computer, 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 * $FreeBSD: head/sys/security/audit/audit_syscalls.c 168933 2007-04-21 22:08:48Z rwatson $ 30 */ 31 32#include "opt_mac.h" 33 34#include <sys/param.h> 35#include <sys/mount.h> 36#include <sys/namei.h> 37#include <sys/priv.h> 38#include <sys/proc.h> 39#include <sys/sysproto.h> 40#include <sys/systm.h> 41#include <sys/vnode.h> 42#include <sys/jail.h> 43 44#include <bsm/audit.h> 45#include <bsm/audit_kevents.h> 46 47#include <security/audit/audit.h> 48#include <security/audit/audit_private.h> 49#include <security/mac/mac_framework.h> 50 51#ifdef AUDIT 52 53/* 54 * System call to allow a user space application to submit a BSM audit record 55 * to the kernel for inclusion in the audit log. This function does little 56 * verification on the audit record that is submitted. 57 * 58 * XXXAUDIT: Audit preselection for user records does not currently work, 59 * since we pre-select only based on the AUE_audit event type, not the event 60 * type submitted as part of the user audit data. 61 */ 62/* ARGSUSED */ 63int 64audit(struct thread *td, struct audit_args *uap) 65{ 66 int error; 67 void * rec; 68 struct kaudit_record *ar; 69 70 if (jailed(td->td_ucred)) 71 return (ENOSYS); 72 error = priv_check(td, PRIV_AUDIT_SUBMIT); 73 if (error) 74 return (error); 75 76 if ((uap->length <= 0) || (uap->length > audit_qctrl.aq_bufsz)) 77 return (EINVAL); 78 79 ar = currecord(); 80 81 /* 82 * If there's no current audit record (audit() itself not audited) 83 * commit the user audit record. 84 */ 85 if (ar == NULL) { 86 87 /* 88 * This is not very efficient; we're required to allocate a 89 * complete kernel audit record just so the user record can 90 * tag along. 91 * 92 * XXXAUDIT: Maybe AUE_AUDIT in the system call context and 93 * special pre-select handling? 94 */ 95 td->td_ar = audit_new(AUE_NULL, td); 96 if (td->td_ar == NULL) 97 return (ENOTSUP); 98 ar = td->td_ar; 99 } 100 101 if (uap->length > MAX_AUDIT_RECORD_SIZE) 102 return (EINVAL); 103 104 rec = malloc(uap->length, M_AUDITDATA, M_WAITOK); 105 106 error = copyin(uap->record, rec, uap->length); 107 if (error) 108 goto free_out; 109 110 /* Verify the record. */ 111 if (bsm_rec_verify(rec) == 0) { 112 error = EINVAL; 113 goto free_out; 114 } 115 116#ifdef MAC 117 error = mac_check_system_audit(td->td_ucred, rec, uap->length); 118 if (error) 119 goto free_out; 120#endif 121 122 /* 123 * Attach the user audit record to the kernel audit record. Because 124 * this system call is an auditable event, we will write the user 125 * record along with the record for this audit event. 126 * 127 * XXXAUDIT: KASSERT appropriate starting values of k_udata, k_ulen, 128 * k_ar_commit & AR_COMMIT_USER? 129 */ 130 ar->k_udata = rec; 131 ar->k_ulen = uap->length; 132 ar->k_ar_commit |= AR_COMMIT_USER; 133 134 /* 135 * Currently we assume that all preselection has been performed in 136 * userspace. We unconditionally set these masks so that the records 137 * get committed both to the trail and pipe. In the future we will 138 * want to setup kernel based preselection. 139 */ 140 ar->k_ar_commit |= (AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE); 141 return (0); 142 143free_out: 144 /* 145 * audit_syscall_exit() will free the audit record on the thread even 146 * if we allocated it above. 147 */ 148 free(rec, M_AUDITDATA); 149 return (error); 150} 151 152/* 153 * System call to manipulate auditing. 154 */ 155/* ARGSUSED */ 156int 157auditon(struct thread *td, struct auditon_args *uap) 158{ 159 int error; 160 union auditon_udata udata; 161 struct proc *tp; 162 163 if (jailed(td->td_ucred)) 164 return (ENOSYS); 165 AUDIT_ARG(cmd, uap->cmd); 166 167#ifdef MAC 168 error = mac_check_system_auditon(td->td_ucred, uap->cmd); 169 if (error) 170 return (error); 171#endif 172 173 error = priv_check(td, PRIV_AUDIT_CONTROL); 174 if (error) 175 return (error); 176 177 if ((uap->length <= 0) || (uap->length > sizeof(union auditon_udata))) 178 return (EINVAL); 179 180 memset((void *)&udata, 0, sizeof(udata)); 181 182 /* 183 * Some of the GET commands use the arguments too. 184 */ 185 switch (uap->cmd) { 186 case A_SETPOLICY: 187 case A_SETKMASK: 188 case A_SETQCTRL: 189 case A_SETSTAT: 190 case A_SETUMASK: 191 case A_SETSMASK: 192 case A_SETCOND: 193 case A_SETCLASS: 194 case A_SETPMASK: 195 case A_SETFSIZE: 196 case A_SETKAUDIT: 197 case A_GETCLASS: 198 case A_GETPINFO: 199 case A_GETPINFO_ADDR: 200 case A_SENDTRIGGER: 201 error = copyin(uap->data, (void *)&udata, uap->length); 202 if (error) 203 return (error); 204 AUDIT_ARG(auditon, &udata); 205 break; 206 } 207 208 /* 209 * XXXAUDIT: Locking? 210 */ 211 switch (uap->cmd) { 212 case A_GETPOLICY: 213 if (!audit_fail_stop) 214 udata.au_policy |= AUDIT_CNT; 215 if (audit_panic_on_write_fail) 216 udata.au_policy |= AUDIT_AHLT; 217 if (audit_argv) 218 udata.au_policy |= AUDIT_ARGV; 219 if (audit_arge) 220 udata.au_policy |= AUDIT_ARGE; 221 break; 222 223 case A_SETPOLICY: 224 if (udata.au_policy & ~(AUDIT_CNT|AUDIT_AHLT|AUDIT_ARGV| 225 AUDIT_ARGE)) 226 return (EINVAL); 227 /* 228 * XXX - Need to wake up waiters if the policy relaxes? 229 */ 230 audit_fail_stop = ((udata.au_policy & AUDIT_CNT) == 0); 231 audit_panic_on_write_fail = (udata.au_policy & AUDIT_AHLT); 232 audit_argv = (udata.au_policy & AUDIT_ARGV); 233 audit_arge = (udata.au_policy & AUDIT_ARGE); 234 break; 235 236 case A_GETKMASK: 237 udata.au_mask = audit_nae_mask; 238 break; 239 240 case A_SETKMASK: 241 audit_nae_mask = udata.au_mask; 242 break; 243 244 case A_GETQCTRL: 245 udata.au_qctrl = audit_qctrl; 246 break; 247 248 case A_SETQCTRL: 249 if ((udata.au_qctrl.aq_hiwater > AQ_MAXHIGH) || 250 (udata.au_qctrl.aq_lowater >= udata.au_qctrl.aq_hiwater) || 251 (udata.au_qctrl.aq_bufsz > AQ_MAXBUFSZ) || 252 (udata.au_qctrl.aq_minfree < 0) || 253 (udata.au_qctrl.aq_minfree > 100)) 254 return (EINVAL); 255 256 audit_qctrl = udata.au_qctrl; 257 /* XXX The queue delay value isn't used with the kernel. */ 258 audit_qctrl.aq_delay = -1; 259 break; 260 261 case A_GETCWD: 262 return (ENOSYS); 263 break; 264 265 case A_GETCAR: 266 return (ENOSYS); 267 break; 268 269 case A_GETSTAT: 270 return (ENOSYS); 271 break; 272 273 case A_SETSTAT: 274 return (ENOSYS); 275 break; 276 277 case A_SETUMASK: 278 return (ENOSYS); 279 break; 280 281 case A_SETSMASK: 282 return (ENOSYS); 283 break; 284 285 case A_GETCOND: 286 if (audit_enabled && !audit_suspended) 287 udata.au_cond = AUC_AUDITING; 288 else 289 udata.au_cond = AUC_NOAUDIT; 290 break; 291 292 case A_SETCOND: 293 if (udata.au_cond == AUC_NOAUDIT) 294 audit_suspended = 1; 295 if (udata.au_cond == AUC_AUDITING) 296 audit_suspended = 0; 297 if (udata.au_cond == AUC_DISABLED) { 298 audit_suspended = 1; 299 audit_shutdown(NULL, 0); 300 } 301 break; 302 303 case A_GETCLASS: 304 udata.au_evclass.ec_class = au_event_class( 305 udata.au_evclass.ec_number); 306 break; 307 308 case A_SETCLASS: 309 au_evclassmap_insert(udata.au_evclass.ec_number, 310 udata.au_evclass.ec_class); 311 break; 312 313 case A_GETPINFO: 314 if (udata.au_aupinfo.ap_pid < 1) 315 return (EINVAL); 316 317 if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) 318 return (EINVAL); 319 if (p_cansee(td, tp) != 0) { 320 PROC_UNLOCK(tp); 321 return (EINVAL); 322 } 323 324 if (tp->p_au->ai_termid.at_type == AU_IPv6) { 325 PROC_UNLOCK(tp); 326 return (EINVAL); 327 } 328 udata.au_aupinfo.ap_auid = tp->p_au->ai_auid; 329 udata.au_aupinfo.ap_mask.am_success = 330 tp->p_au->ai_mask.am_success; 331 udata.au_aupinfo.ap_mask.am_failure = 332 tp->p_au->ai_mask.am_failure; 333 udata.au_aupinfo.ap_termid.machine = 334 tp->p_au->ai_termid.at_addr[0]; 335 udata.au_aupinfo.ap_termid.port = 336 (dev_t)tp->p_au->ai_termid.at_port; 337 udata.au_aupinfo.ap_asid = tp->p_au->ai_asid; 338 PROC_UNLOCK(tp); 339 break; 340 341 case A_SETPMASK: 342 if (udata.au_aupinfo.ap_pid < 1) 343 return (EINVAL); 344 345 if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) 346 return (EINVAL); 347 if (p_cansee(td, tp) != 0) { 348 PROC_UNLOCK(tp); 349 return (EINVAL); 350 } 351 352 tp->p_au->ai_mask.am_success = 353 udata.au_aupinfo.ap_mask.am_success; 354 tp->p_au->ai_mask.am_failure = 355 udata.au_aupinfo.ap_mask.am_failure; 356 PROC_UNLOCK(tp); 357 break; 358 359 case A_SETFSIZE: 360 if ((udata.au_fstat.af_filesz != 0) && 361 (udata.au_fstat.af_filesz < MIN_AUDIT_FILE_SIZE)) 362 return (EINVAL); 363 audit_fstat.af_filesz = udata.au_fstat.af_filesz; 364 break; 365 366 case A_GETFSIZE: 367 udata.au_fstat.af_filesz = audit_fstat.af_filesz; 368 udata.au_fstat.af_currsz = audit_fstat.af_currsz; 369 break; 370 371 case A_GETPINFO_ADDR: 372 if (udata.au_aupinfo_addr.ap_pid < 1) 373 return (EINVAL); 374 if ((tp = pfind(udata.au_aupinfo_addr.ap_pid)) == NULL) 375 return (EINVAL); 376 udata.au_aupinfo_addr.ap_auid = tp->p_au->ai_auid; 377 udata.au_aupinfo_addr.ap_mask.am_success = 378 tp->p_au->ai_mask.am_success; 379 udata.au_aupinfo_addr.ap_mask.am_failure = 380 tp->p_au->ai_mask.am_failure; 381 udata.au_aupinfo_addr.ap_termid = tp->p_au->ai_termid; 382 udata.au_aupinfo_addr.ap_asid = tp->p_au->ai_asid; 383 PROC_UNLOCK(tp); 384 break; 385 386 case A_GETKAUDIT: 387 return (ENOSYS); 388 break; 389 390 case A_SETKAUDIT: 391 return (ENOSYS); 392 break; 393 394 case A_SENDTRIGGER: 395 if ((udata.au_trigger < AUDIT_TRIGGER_MIN) || 396 (udata.au_trigger > AUDIT_TRIGGER_MAX)) 397 return (EINVAL); 398 return (send_trigger(udata.au_trigger)); 399 } 400 401 /* 402 * Copy data back to userspace for the GET comands. 403 */ 404 switch (uap->cmd) { 405 case A_GETPOLICY: 406 case A_GETKMASK: 407 case A_GETQCTRL: 408 case A_GETCWD: 409 case A_GETCAR: 410 case A_GETSTAT: 411 case A_GETCOND: 412 case A_GETCLASS: 413 case A_GETPINFO: 414 case A_GETFSIZE: 415 case A_GETPINFO_ADDR: 416 case A_GETKAUDIT: 417 error = copyout((void *)&udata, uap->data, uap->length); 418 if (error) 419 return (error); 420 break; 421 } 422 423 return (0); 424} 425 426/* 427 * System calls to manage the user audit information. 428 */ 429/* ARGSUSED */ 430int 431getauid(struct thread *td, struct getauid_args *uap) 432{ 433 int error; 434 au_id_t id; 435 436 if (jailed(td->td_ucred)) 437 return (ENOSYS); 438 error = priv_check(td, PRIV_AUDIT_GETAUDIT); 439 if (error) 440 return (error); 441 442 /* 443 * XXX: Integer read on static pointer dereference: doesn't need 444 * locking? 445 */ 446 PROC_LOCK(td->td_proc); 447 id = td->td_proc->p_au->ai_auid; 448 PROC_UNLOCK(td->td_proc); 449 return copyout(&id, uap->auid, sizeof(id)); 450} 451 452/* ARGSUSED */ 453int 454setauid(struct thread *td, struct setauid_args *uap) 455{ 456 int error; 457 au_id_t id; 458 459 if (jailed(td->td_ucred)) 460 return (ENOSYS); 461 error = priv_check(td, PRIV_AUDIT_SETAUDIT); 462 if (error) 463 return (error); 464 465 error = copyin(uap->auid, &id, sizeof(id)); 466 if (error) 467 return (error); 468 469 audit_arg_auid(id); 470 471#ifdef MAC 472 error = mac_check_proc_setauid(td->td_ucred, id); 473 if (error) 474 return (error); 475#endif 476 477 /* 478 * XXX: Integer write on static pointer dereference: doesn't need 479 * locking? 480 * 481 * XXXAUDIT: Might need locking to serialize audit events in the same 482 * order as change events? Or maybe that's an under-solveable 483 * problem. 484 * 485 * XXXRW: Test privilege while holding the proc lock? 486 */ 487 PROC_LOCK(td->td_proc); 488 td->td_proc->p_au->ai_auid = id; 489 PROC_UNLOCK(td->td_proc); 490 491 return (0); 492} 493 494/* 495 * System calls to get and set process audit information. 496 */ 497/* ARGSUSED */ 498int 499getaudit(struct thread *td, struct getaudit_args *uap) 500{ 501 struct auditinfo ai; 502 int error; 503 504 if (jailed(td->td_ucred)) 505 return (ENOSYS); 506 error = priv_check(td, PRIV_AUDIT_GETAUDIT); 507 if (error) 508 return (error); 509 510 PROC_LOCK(td->td_proc); 511 if (td->td_proc->p_au->ai_termid.at_type == AU_IPv6) { 512 PROC_UNLOCK(td->td_proc); 513 return (E2BIG); 514 } 515 bzero(&ai, sizeof(ai)); 516 ai.ai_auid = td->td_proc->p_au->ai_auid; 517 ai.ai_mask = td->td_proc->p_au->ai_mask; 518 ai.ai_asid = td->td_proc->p_au->ai_asid; 519 ai.ai_termid.machine = td->td_proc->p_au->ai_termid.at_addr[0]; 520 ai.ai_termid.port = td->td_proc->p_au->ai_termid.at_port; 521 PROC_UNLOCK(td->td_proc); 522 523 return (copyout(&ai, uap->auditinfo, sizeof(ai))); 524} 525 526/* ARGSUSED */ 527int 528setaudit(struct thread *td, struct setaudit_args *uap) 529{ 530 struct auditinfo ai; 531 int error; 532 533 if (jailed(td->td_ucred)) 534 return (ENOSYS); 535 error = priv_check(td, PRIV_AUDIT_SETAUDIT); 536 if (error) 537 return (error); 538 539 error = copyin(uap->auditinfo, &ai, sizeof(ai)); 540 if (error) 541 return (error); 542 543 audit_arg_auditinfo(&ai); 544 545#ifdef MAC 546 error = mac_check_proc_setaudit(td->td_ucred, &ai); 547 if (error) 548 return (error); 549#endif 550 551 /* 552 * XXXRW: Test privilege while holding the proc lock? 553 */ 554 PROC_LOCK(td->td_proc); 555 bzero(td->td_proc->p_au, sizeof(struct auditinfo_addr)); 556 td->td_proc->p_au->ai_auid = ai.ai_auid; 557 td->td_proc->p_au->ai_mask = ai.ai_mask; 558 td->td_proc->p_au->ai_asid = ai.ai_asid; 559 td->td_proc->p_au->ai_termid.at_addr[0] = ai.ai_termid.machine; 560 td->td_proc->p_au->ai_termid.at_port = ai.ai_termid.port; 561 td->td_proc->p_au->ai_termid.at_type = AU_IPv4; 562 PROC_UNLOCK(td->td_proc); 563 564 return (0); 565} 566 567/* ARGSUSED */ 568int 569getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) 570{ 571 struct auditinfo_addr aia; 572 int error; 573 574 if (jailed(td->td_ucred)) 575 return (ENOSYS); 576 error = priv_check(td, PRIV_AUDIT_GETAUDIT); 577 if (error) 578 return (error); 579 if (uap->length < sizeof(aia)) 580 return (EOVERFLOW); 581 PROC_LOCK(td->td_proc); 582 aia = *td->td_proc->p_au; 583 PROC_UNLOCK(td->td_proc); 584 return (copyout(&aia, uap->auditinfo_addr, sizeof(aia))); 585} 586 587/* ARGSUSED */ 588int 589setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) 590{ 591 struct auditinfo_addr aia; 592 int error; 593 594 if (jailed(td->td_ucred)) 595 return (ENOSYS); 596 error = priv_check(td, PRIV_AUDIT_SETAUDIT); 597 if (error) 598 return (error); 599 600#ifdef MAC 601 error = mac_check_proc_setaudit(td->td_ucred, NULL); 602 if (error) 603 return (error); 604#endif 605 error = copyin(uap->auditinfo_addr, &aia, sizeof(aia)); 606 if (error) 607 return (error); 608 PROC_LOCK(td->td_proc); 609 *td->td_proc->p_au = aia; 610 PROC_UNLOCK(td->td_proc); 611 return (error); 612} 613 614/* 615 * Syscall to manage audit files. 616 */ 617/* ARGSUSED */ 618int 619auditctl(struct thread *td, struct auditctl_args *uap) 620{ 621 struct nameidata nd; 622 struct ucred *cred; 623 struct vnode *vp; 624 int error = 0; 625 int flags, vfslocked; 626 627 if (jailed(td->td_ucred)) 628 return (ENOSYS); 629 error = priv_check(td, PRIV_AUDIT_CONTROL); 630 if (error) 631 return (error); 632 633 vp = NULL; 634 cred = NULL; 635 636 /* 637 * If a path is specified, open the replacement vnode, perform 638 * validity checks, and grab another reference to the current 639 * credential. 640 * 641 * On Darwin, a NULL path argument is also used to disable audit. 642 */ 643 if (uap->path == NULL) 644 return (EINVAL); 645 646 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, 647 UIO_USERSPACE, uap->path, td); 648 flags = AUDIT_OPEN_FLAGS; 649 error = vn_open(&nd, &flags, 0, -1); 650 if (error) 651 return (error); 652 vfslocked = NDHASGIANT(&nd); 653 vp = nd.ni_vp; 654#ifdef MAC 655 error = mac_check_system_auditctl(td->td_ucred, vp); 656 VOP_UNLOCK(vp, 0, td); 657 if (error) { 658 vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td); 659 VFS_UNLOCK_GIANT(vfslocked); 660 return (error); 661 } 662#else 663 VOP_UNLOCK(vp, 0, td); 664#endif 665 NDFREE(&nd, NDF_ONLY_PNBUF); 666 if (vp->v_type != VREG) { 667 vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td); 668 VFS_UNLOCK_GIANT(vfslocked); 669 return (EINVAL); 670 } 671 VFS_UNLOCK_GIANT(vfslocked); 672 cred = td->td_ucred; 673 crhold(cred); 674 675 /* 676 * XXXAUDIT: Should audit_suspended actually be cleared by 677 * audit_worker? 678 */ 679 audit_suspended = 0; 680 681 audit_rotate_vnode(cred, vp); 682 683 return (error); 684} 685 686#else /* !AUDIT */ 687 688int 689audit(struct thread *td, struct audit_args *uap) 690{ 691 692 return (ENOSYS); 693} 694 695int 696auditon(struct thread *td, struct auditon_args *uap) 697{ 698 699 return (ENOSYS); 700} 701 702int 703getauid(struct thread *td, struct getauid_args *uap) 704{ 705 706 return (ENOSYS); 707} 708 709int 710setauid(struct thread *td, struct setauid_args *uap) 711{ 712 713 return (ENOSYS); 714} 715 716int 717getaudit(struct thread *td, struct getaudit_args *uap) 718{ 719 720 return (ENOSYS); 721} 722 723int 724setaudit(struct thread *td, struct setaudit_args *uap) 725{ 726 727 return (ENOSYS); 728} 729 730int 731getaudit_addr(struct thread *td, struct getaudit_addr_args *uap) 732{ 733 734 return (ENOSYS); 735} 736 737int 738setaudit_addr(struct thread *td, struct setaudit_addr_args *uap) 739{ 740 741 return (ENOSYS); 742} 743 744int 745auditctl(struct thread *td, struct auditctl_args *uap) 746{ 747 748 return (ENOSYS); 749} 750 751void 752audit_proc_init(struct proc *p) 753{ 754 755} 756 757void 758audit_proc_fork(struct proc *parent, struct proc *child) 759{ 760 761} 762 763void 764audit_proc_free(struct proc *p) 765{ 766 767} 768 769#endif /* AUDIT */ 770