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