From owner-p4-projects@FreeBSD.ORG Wed May 5 19:48:28 2010 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 8EDF41065673; Wed, 5 May 2010 19:48:28 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 3AFC41065670 for ; Wed, 5 May 2010 19:48:28 +0000 (UTC) (envelope-from gpf@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [69.147.83.41]) by mx1.freebsd.org (Postfix) with ESMTP id 287FF8FC1B for ; Wed, 5 May 2010 19:48:28 +0000 (UTC) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.3/8.14.3) with ESMTP id o45JmRkV060165 for ; Wed, 5 May 2010 19:48:27 GMT (envelope-from gpf@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.3/8.14.3/Submit) id o45JmR5p060163 for perforce@freebsd.org; Wed, 5 May 2010 19:48:27 GMT (envelope-from gpf@FreeBSD.org) Date: Wed, 5 May 2010 19:48:27 GMT Message-Id: <201005051948.o45JmR5p060163@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to gpf@FreeBSD.org using -f From: Efstratios Karatzas To: Perforce Change Reviews Precedence: bulk Cc: Subject: PERFORCE change 177776 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 05 May 2010 19:48:28 -0000 http://p4web.freebsd.org/@@177776?ac=10 Change 177776 by gpf@gpf_desktop on 2010/05/05 19:47:55 Integration of my older patches. Able to audit file creation, read & write. Fixed a deadlock in my patches, everything seems ok now. procedures serviced: 3/23 Affected files ... .. //depot/projects/soc2010/gpf_audit/freebsd/src/sys/bsm/audit_kevents.h#2 edit .. //depot/projects/soc2010/gpf_audit/freebsd/src/sys/nfsserver/nfs_serv.c#2 edit .. //depot/projects/soc2010/gpf_audit/freebsd/src/sys/nfsserver/nfs_srvkrpc.c#2 edit .. //depot/projects/soc2010/gpf_audit/freebsd/src/sys/security/audit/audit.c#2 edit .. //depot/projects/soc2010/gpf_audit/freebsd/src/sys/security/audit/audit.h#2 edit .. //depot/projects/soc2010/gpf_audit/freebsd/src/sys/security/audit/audit_bsm.c#2 edit .. //depot/projects/soc2010/gpf_audit/freebsd/src/sys/security/audit/audit_private.h#2 edit Differences ... ==== //depot/projects/soc2010/gpf_audit/freebsd/src/sys/bsm/audit_kevents.h#2 (text) ==== @@ -385,6 +385,33 @@ #define AUE_DARWIN_COPYFILE 361 /* Darwin-specific. */ /* + * NFS RPC events + */ +#define AUE_NFS_NULL 2000 +#define AUE_NFS_GETATTR 2001 +#define AUE_NFS_SETATTR 2002 +#define AUE_NFS_LOOKUP 2003 +#define AUE_NFS_ACCESS 2004 +#define AUE_NFS_REALINK 2005 +#define AUE_NFS_READ 2006 +#define AUE_NFS_WRITE 2007 +#define AUE_NFS_CREATE 2008 +#define AUE_NFS_MKDIR 2009 +#define AUE_NFS_SYMLINK 2010 +#define AUE_NFS_MKNODE 2011 +#define AUE_NFS_REMOVE 2012 +#define AUE_NFS_RMDIR 2013 +#define AUE_NFS_RENAME 2014 +#define AUE_NFS_LINK 2015 +#define AUE_NFS_READDIR 2016 +#define AUE_NFS_READDIR_PLUS 2017 +#define AUE_NFS_STATFS 2018 +#define AUE_NFS_FSINFO 2019 +#define AUE_NFS_PATHCONF 2020 +#define AUE_NFS_COMMIT 2021 +#define AUE_NFS_NOOP 2022 + +/* * Audit event identifiers added as part of OpenBSM, generally corresponding * to events in FreeBSD, Darwin, and Linux that were not present in Solaris. * These often duplicate events added to the Solaris set by Darwin, but use ==== //depot/projects/soc2010/gpf_audit/freebsd/src/sys/nfsserver/nfs_serv.c#2 (text+ko) ==== @@ -88,6 +88,8 @@ #include #include +#include + #include #include #include @@ -778,7 +780,7 @@ int v3 = (nfsd->nd_flag & ND_NFSV3), reqlen; struct mbuf *mb, *mreq; struct mbuf *m2; - struct vnode *vp = NULL; + struct vnode *vp = NULL, *vp_alt = NULL; nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; @@ -818,7 +820,9 @@ error = 0; goto nfsmout; } - + + vp_alt = vp; + if (vp->v_type != VREG) { if (v3) error = EINVAL; @@ -1003,6 +1007,21 @@ if (vp) vput(vp); VFS_UNLOCK_GIANT(vfslocked); + + /* XXX AUDIT */ + if (vp_alt != NULL) { + char *fullpath, *freepath; + struct thread *td = curthread; + + freepath = NULL; + vn_fullpath_global(td, vp_alt, &fullpath, &freepath); + + if (freepath != NULL) { + AUDIT_ARG_UPATH1(td, fullpath); + free(freepath, M_TEMP); + } + } + return(error); } @@ -1031,7 +1050,7 @@ int stable = NFSV3WRITE_FILESYNC; int v3 = (nfsd->nd_flag & ND_NFSV3); struct mbuf *mb, *mreq; - struct vnode *vp = NULL; + struct vnode *vp = NULL, *vp_alt = NULL;; nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; @@ -1120,6 +1139,9 @@ error = 0; goto nfsmout; } + + vp_alt = vp; + if (v3) forat_ret = VOP_GETATTR(vp, &forat, cred); if (vp->v_type != VREG) { @@ -1221,6 +1243,36 @@ vput(vp); vn_finished_write(mntp); VFS_UNLOCK_GIANT(vfslocked); + + /* XXX AUDIT */ + + /* + * another way we could go about re-obtaining the vp from the file handle. + * right now, I think i like the vp_alt method better + */ + /* + if (vp == NULL) { + tvfslocked = 0; + error = nfsrv_fhtovp(fhp, 0, &vp, &tvfslocked, + nfsd, slp, nam, &rdonly, TRUE); + if (error) + vp = NULL; + } + */ + + if (vp_alt != NULL) { + char *fullpath, *freepath; + struct thread *td = curthread; + + freepath = NULL; + vn_fullpath_global(td, vp_alt, &fullpath, &freepath); + + if (freepath != NULL) { + AUDIT_ARG_UPATH1(td, fullpath); + free(freepath, M_TEMP); + } + } + return(error); } @@ -1519,6 +1571,38 @@ NDFREE(&nd, NDF_ONLY_PNBUF); vn_finished_write(mp); VFS_UNLOCK_GIANT(vfslocked); + + /* XXX AUDIT */ + if (nd.ni_vp != NULL && nd.ni_dvp != NULL) { + struct thread *td = curthread; + char *fullpath, *freepath; + char path[PATH_MAX]; + + AUDIT_ARG_VNODE1(nd.ni_vp); + + freepath = NULL; + vn_fullpath_global(td, nd.ni_vp, &fullpath, &freepath); + + if (freepath != NULL) { + strlcpy(path, fullpath, sizeof(path)); + free(freepath, M_TEMP); + } + /* if we fail to acquire a path from the new vnode, use the directory vnode instead */ + else { + vn_fullpath_global(td, nd.ni_dvp, &fullpath, &freepath); + if (freepath != NULL) { + snprintf(path, sizeof(path), "%s/%s", fullpath, nd.ni_cnd.cn_pnbuf); + free(freepath, M_TEMP); + } + /* last resort: just save the name of the new file */ + else { + strlcpy(path, nd.ni_cnd.cn_pnbuf, sizeof(path)); + } + } + + AUDIT_ARG_UPATH1(td, path); + } + return (error); } ==== //depot/projects/soc2010/gpf_audit/freebsd/src/sys/nfsserver/nfs_srvkrpc.c#2 (text+ko) ==== @@ -63,6 +63,8 @@ #include #include +#include + #include #include #ifdef INET6 @@ -254,6 +256,7 @@ struct nfsrv_descript nd; struct mbuf *mreq, *mrep; int error; + struct thread *td = curthread; if (rqst->rq_vers == NFS_VER2) { if (rqst->rq_proc > NFSV2PROC_STATFS) { @@ -349,7 +352,9 @@ } nfsrvstats.srvrpccnt[nd.nd_procnum]++; + AUDIT_NFS_ENTER(procnum, td); error = proc(&nd, NULL, &mrep); + AUDIT_NFS_EXIT(error, td); if (nd.nd_cr) crfree(nd.nd_cr); ==== //depot/projects/soc2010/gpf_audit/freebsd/src/sys/security/audit/audit.c#2 (text) ==== @@ -60,6 +60,9 @@ #include #include +/* XXX gpf: for debuging */ +#include + #include #include #include @@ -589,6 +592,234 @@ td->td_pflags &= ~TDP_AUDITREC; } +/* + * Convert an NFS RPC procedure number to an audit event + */ +int +audit_nfs_proc_to_event(unsigned int proc, au_event_t *event) +{ + switch (proc) { + case 0: + /* nfsrv_null */ + *event = AUE_NFS_NULL; + break; + + case 1: + /* nfsrv_getattr */ + *event = AUE_NFS_GETATTR; + break; + + case 2: + /* nfsrv_setattr */ + *event = AUE_NFS_SETATTR; + break; + + case 3: + /* nfsrv_lookup */ + *event = AUE_NFS_LOOKUP; + break; + + case 4: + /* nfsrv3_access */ + *event = AUE_NFS_ACCESS; + break; + + case 5: + /* nfsrv_readlink */ + *event = AUE_NFS_REALINK; + break; + + case 6: + /* nfsrv_read */ + *event = AUE_NFS_READ; + break; + + case 7: + /* nfsrv_write */ + *event = AUE_NFS_WRITE; + break; + + case 8: + /* nfsrv_create */ + *event = AUE_NFS_CREATE; + break; + + case 9: + /* nfsrv_mkdir */ + *event = AUE_NFS_MKDIR; + break; + + case 10: + /* nfsrv_symlink */ + *event = AUE_NFS_SYMLINK; + break; + + case 11: + /* nfsrv_mknod */ + *event = AUE_NFS_MKNODE; + break; + + case 12: + /* nfsrv_remove */ + *event = AUE_NFS_REMOVE; + break; + + case 13: + /* nfsrv_rmdir */ + *event = AUE_NFS_RMDIR; + break; + + case 14: + /* nfsrv_rename */ + *event = AUE_NFS_RENAME; + break; + + case 15: + /* nfsrv_link */ + *event = AUE_NFS_LINK; + break; + + case 16: + /* nfsrv_readdir */ + *event = AUE_NFS_READDIR; + break; + + case 17: + /* nfsrv_readdirplus */ + *event = AUE_NFS_READDIR_PLUS; + break; + + case 18: + /* nfsrv_statfs */ + *event = AUE_NFS_STATFS; + break; + + case 19: + /* nfsrv_fsinfo */ + *event = AUE_NFS_FSINFO; + break; + + case 20: + /* nfsrv_pathconf */ + *event = AUE_NFS_PATHCONF; + break; + + case 21: + /* nfsrv_commit */ + *event = AUE_NFS_COMMIT; + break; + + case 22: + /* nfsrv_noop */ + *event = AUE_NFS_NOOP; + break; + + default: + *event = AUE_NFS_NULL; + return 1; /* unmatched */ + } + + return 0; /* matched */ +} + +/* + * audit_nfs_enter() is called on entry to each rpc request that *will* + * be serviced by the nfs server. It is responsible for deciding + * whether or not to audit the remote procedure call (preselection), + * and if so, allocating a per-thread audit record. + * audit_new() will fill in basic thread/credential properties. + */ +void +audit_nfs_enter(unsigned int proc, struct thread *td) +{ + struct au_mask *aumask; + au_class_t class; + au_event_t event; + au_id_t auid; + + KASSERT(td->td_ar == NULL, ("audit_nfs_enter: td->td_ar != NULL")); + KASSERT((td->td_pflags & TDP_AUDITREC) == 0, + ("audit_nfs_enter: TDP_AUDITREC set")); + + /* XXXgpf: perhaps log a failure to match a rpc to an audit event? */ + audit_nfs_proc_to_event(proc, &event); + + printf("audit_nfs_enter: procedure = %d\n" + "audit_nfs_enter: event = %d\n", proc, event); + + /* + * Check which audit mask to use; either the kernel non-attributable + * event mask or the process audit mask. + */ + auid = td->td_ucred->cr_audit.ai_auid; + if (auid == AU_DEFAUDITID) + aumask = &audit_nae_mask; + else + aumask = &td->td_ucred->cr_audit.ai_mask; + + /* + * Allocate an audit record, if preselection allows it, and store in + * the thread for later use. + */ + class = au_event_class(event); + if (au_preselect(event, class, aumask, AU_PRS_BOTH)) { + printf("audit_nfs_enter: select it!\n\n"); + /* + * If we're out of space and need to suspend unprivileged + * processes, do that here rather than trying to allocate + * another audit record. + * + * Note: we might wish to be able to continue here in the + * future, if the system recovers. That should be possible + * by means of checking the condition in a loop around + * cv_wait(). It might be desirable to reevaluate whether an + * audit record is still required for this event by + * re-calling au_preselect(). + */ + if (audit_in_failure && + priv_check(td, PRIV_AUDIT_FAILSTOP) != 0) { + cv_wait(&audit_fail_cv, &audit_mtx); + panic("audit_failing_stop: thread continued"); + } + td->td_ar = audit_new(event, td); + if (td->td_ar != NULL) + td->td_pflags |= TDP_AUDITREC; + } else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0)) { + td->td_ar = audit_new(event, td); + if (td->td_ar != NULL) + td->td_pflags |= TDP_AUDITREC; + } else + td->td_ar = NULL; +} + +/* + * audit_nfs_exit() is called from the return of every NFS pseudo-sys/call + * that services an RPC by performing the various vnode operations. + * It is responsible for committing the audit record, if any, along with + * return condition. + */ +void +audit_nfs_exit(int error, struct thread *td) +{ + int retval; + + /* + * Commit the audit record as desired; once we pass the record into + * audit_commit(), the memory is owned by the audit subsystem. The + * return value from the system call is stored on the user thread. + * If there was an error, the return value is set to -1, imitating + * the behavior of the cerror routine. + */ + if (error) + retval = -1; + else + retval = td->td_retval[0]; + + audit_commit(td->td_ar, error, retval); + td->td_ar = NULL; + td->td_pflags &= ~TDP_AUDITREC; +} + void audit_cred_copy(struct ucred *src, struct ucred *dest) { ==== //depot/projects/soc2010/gpf_audit/freebsd/src/sys/security/audit/audit.h#2 (text) ==== @@ -59,6 +59,9 @@ void audit_syscall_enter(unsigned short code, struct thread *td); void audit_syscall_exit(int error, struct thread *td); +void audit_nfs_enter(unsigned int proc, struct thread *td); +void audit_nfs_exit(int error, struct thread *td); + /* * The remaining kernel functions are conditionally compiled in as they are * wrapped by a macro, and the macro should be the only place in the source @@ -311,6 +314,17 @@ audit_syscall_exit(error, td); \ } while (0) +#define AUDIT_NFS_ENTER(proc, td) do { \ + if (audit_enabled) { \ + audit_nfs_enter(proc, td); \ + } \ +} while (0) + +#define AUDIT_NFS_EXIT(error, td) do { \ + if (td->td_pflags & TDP_AUDITREC) \ + audit_nfs_exit(error, td); \ +} while (0) + /* * A Macro to wrap the audit_sysclose() function. */ @@ -360,6 +374,9 @@ #define AUDIT_SYSCLOSE(p, fd) +#define AUDIT_NFS_ENTER(proc, td) +#define AUDIT_NFS_EXIT(error, td) + #endif /* AUDIT */ #endif /* !_SECURITY_AUDIT_KERNEL_H_ */ ==== //depot/projects/soc2010/gpf_audit/freebsd/src/sys/security/audit/audit_bsm.c#2 (text) ==== @@ -1581,6 +1581,18 @@ kau_write(rec, tok); break; + case AUE_NFS_CREATE: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(3, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + /* FALLTHROUGH */ + + case AUE_NFS_READ: + case AUE_NFS_WRITE: + UPATH1_VNODE1_TOKENS; + break; + case AUE_WAIT4: PROCESS_PID_TOKENS(1); if (ARG_IS_VALID(kar, ARG_VALUE)) { ==== //depot/projects/soc2010/gpf_audit/freebsd/src/sys/security/audit/audit_private.h#2 (text) ==== @@ -324,6 +324,11 @@ struct kaudit_record *audit_new(int event, struct thread *td); /* + * NFS specific functions + */ +int audit_nfs_proc_to_event(unsigned int proc, au_event_t *event); + +/* * Functions relating to the conversion of internal kernel audit records to * the BSM file format. */