Index: contrib/openbsm/etc/audit_class =================================================================== --- contrib/openbsm/etc/audit_class +++ contrib/openbsm/etc/audit_class @@ -17,6 +17,7 @@ 0x00001000:lo:login_logout 0x00002000:aa:authentication and authorization 0x00004000:ap:application +0x00008000:nfs:network file system server 0x20000000:io:ioctl 0x40000000:ex:exec 0x80000000:ot:miscellaneous Index: contrib/openbsm/etc/audit_event =================================================================== --- contrib/openbsm/etc/audit_event +++ contrib/openbsm/etc/audit_event @@ -615,6 +615,30 @@ 43262:AUE_EXECVEAT:execveat(2):pc,ex 43263:AUE_SHMRENAME:shm_rename(2):ip # +# NFS RPC related events. +# +43266:AUE_NFSRPC_GETATTR:nfsrvd_getattr:nfs +43267:AUE_NFSRPC_SETATTR:nfsrvd_setattr:nfs +43268:AUE_NFSRPC_LOOKUP:nfsrvd_lookup:nfs +43269:AUE_NFSRPC_ACCESS:nfsrvd_access:nfs +43270:AUE_NFSRPC_READLINK:nfsrvd_readlink:nfs +43271:AUE_NFSRPC_READ:nfsrvd_read:nfs +43272:AUE_NFSRPC_WRITE:nfsrvd_write:nfs +43273:AUE_NFSRPC_CREATE:nfsrvd_create:nfs +43274:AUE_NFSRPC_MKDIR:nfsrvd_mkdir:nfs +43275:AUE_NFSRPC_SYMLINK:nfsrvd_symlink:nfs +43276:AUE_NFSRPC_MKNOD:nfsrvd_mknod:nfs +43277:AUE_NFSRPC_REMOVE:nfsrvd_remove:nfs +43278:AUE_NFSRPC_RMDIR:nfsrvd_rmdir:nfs +43279:AUE_NFSRPC_RENAME:nfsrvd_rename:nfs +43280:AUE_NFSRPC_LINK:nfsrvd_link:nfs +43281:AUE_NFSRPC_READDIR:nfsrvd_readdir:nfs +43282:AUE_NFSRPC_READDIRPLUS:nfsrvd_readdirplus:nfs +43283:AUE_NFSRPC_FSSTAT:nfsrvd_statfs:nfs +43284:AUE_NFSRPC_FSINFO:nfsrvd_fsinfo:nfs +43285:AUE_NFSRPC_PATHCONF:nfsrvd_pathconf:nfs +43286:AUE_NFSRPC_COMMIT:nfsrvd_commit:nfs +# # Solaris userspace events. # 6144:AUE_at_create:at-create atjob:ad Index: sys/bsm/audit_kevents.h =================================================================== --- sys/bsm/audit_kevents.h +++ sys/bsm/audit_kevents.h @@ -660,6 +660,31 @@ #define AUE_REALPATHAT 43264 /* FreeBSD-specific. */ #define AUE_CLOSERANGE 43265 /* FreeBSD-specific. */ +/* + * NFS RPC related events. + */ +#define AUE_NFSRPC_GETATTR 43266 +#define AUE_NFSRPC_SETATTR 43267 +#define AUE_NFSRPC_LOOKUP 43268 +#define AUE_NFSRPC_ACCESS 43269 +#define AUE_NFSRPC_READLINK 43270 +#define AUE_NFSRPC_READ 43271 +#define AUE_NFSRPC_WRITE 43272 +#define AUE_NFSRPC_CREATE 43273 +#define AUE_NFSRPC_MKDIR 43274 +#define AUE_NFSRPC_SYMLINK 43275 +#define AUE_NFSRPC_MKNOD 43276 +#define AUE_NFSRPC_REMOVE 43277 +#define AUE_NFSRPC_RMDIR 43278 +#define AUE_NFSRPC_RENAME 43279 +#define AUE_NFSRPC_LINK 43280 +#define AUE_NFSRPC_READDIR 43281 +#define AUE_NFSRPC_READDIRPLUS 43282 +#define AUE_NFSRPC_FSSTAT 43283 +#define AUE_NFSRPC_FSINFO 43284 +#define AUE_NFSRPC_PATHCONF 43285 +#define AUE_NFSRPC_COMMIT 43286 + /* * Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the * normal Solaris BSM identifiers. _O_ refers to it being an old, or compat Index: sys/fs/nfs/nfs.h =================================================================== --- sys/fs/nfs/nfs.h +++ sys/fs/nfs/nfs.h @@ -36,6 +36,9 @@ #ifndef _NFS_NFS_H_ #define _NFS_NFS_H_ + +#include + /* * Tunable constants for nfs */ @@ -309,7 +312,7 @@ struct nfsreferral { u_char *nfr_srvlist; /* List of servers */ int nfr_srvcnt; /* number of servers */ - vnode_t nfr_vp; /* vnode for referral */ + struct vnode * nfr_vp; /* vnode for referral */ uint64_t nfr_dfileno; /* assigned dir inode# */ }; @@ -558,6 +561,12 @@ #define NFSV4ROOT_INO 2 /* It's traditional */ #define NFSV4ROOT_GEN 1 +/* + * This array indicates the audit event number corresponding to NFSv3 and + * NFSv2 RPCs. This table doesn't support NFSv4. + */ +extern u_int16_t nfsrv_auevent[NFS_V3NPROCS]; + /* * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts. * What should be in this set is open to debate, but I believe that since @@ -670,6 +679,7 @@ nfsv4stateid_t nd_savedcurstateid; /* Saved Current StateID */ uint32_t nd_maxreq; /* Max. request (session). */ uint32_t nd_maxresp; /* Max. reply (session). */ + struct kaudit_record *nd_ar; /* Audit record for NFS server */ int nd_bextpg; /* Current ext_pgs page */ int nd_bextpgsiz; /* Bytes left in page */ int nd_maxextsiz; /* Max ext_pgs mbuf size */ @@ -721,6 +731,7 @@ #define ND_EXTLS 0x8000000000 #define ND_EXTLSCERT 0x10000000000 #define ND_EXTLSCERTUSER 0x20000000000 +#define ND_AUDITREC 0x40000000000 /* * ND_GSS should be the "or" of all GSS type authentications. Index: sys/fs/nfs/nfsdport.h =================================================================== --- sys/fs/nfs/nfsdport.h +++ sys/fs/nfs/nfsdport.h @@ -28,6 +28,9 @@ * $FreeBSD$ */ +#ifndef _NFS_NFSDPORT_H_ +#define _NFS_NFSDPORT_H_ + /* * These macros handle nfsvattr fields. They look a bit silly here, but * are quite different for the Darwin port. @@ -116,3 +119,4 @@ printf(__VA_ARGS__); \ } while (0) +#endif /* _NFS_NFSDPORT_H_ */ Index: sys/fs/nfsserver/nfs_nfsdkrpc.c =================================================================== --- sys/fs/nfsserver/nfs_nfsdkrpc.c +++ sys/fs/nfsserver/nfs_nfsdkrpc.c @@ -46,6 +46,7 @@ #include +#include #include NFSDLOCKMUTEX; @@ -382,7 +383,10 @@ if (cacherep == RC_DOIT) { if ((nd->nd_flag & ND_NFSV41) != 0) nd->nd_xprt = xprt; + AUDIT_NFSRPC_ENTER(nd, curthread); + AUDIT_NFSARG_NETSOCKADDR(nd, nd->nd_nam); nfsrvd_dorpc(nd, isdgram, tagstr, taglen, minorvers); + AUDIT_NFSRPC_EXIT(nd, curthread); if ((nd->nd_flag & ND_NFSV41) != 0) { if (nd->nd_repstat != NFSERR_REPLYFROMCACHE && (nd->nd_flag & ND_SAVEREPLY) != 0) { Index: sys/fs/nfsserver/nfs_nfsdport.c =================================================================== --- sys/fs/nfsserver/nfs_nfsdport.c +++ sys/fs/nfsserver/nfs_nfsdport.c @@ -46,6 +46,7 @@ */ #include +#include #include #include #include @@ -1897,6 +1898,7 @@ nfsrv_postopattr(nd, getret, &at); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); if (nd->nd_flag & ND_NFSV2) { NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); off = fxdr_unsigned(u_quad_t, *tl++); @@ -2151,6 +2153,7 @@ nfsrv_postopattr(nd, getret, &at); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED); off = fxdr_hyper(tl); toff = off; Index: sys/fs/nfsserver/nfs_nfsdserv.c =================================================================== --- sys/fs/nfsserver/nfs_nfsdserv.c +++ sys/fs/nfsserver/nfs_nfsdserv.c @@ -51,6 +51,7 @@ #include #include #include +#include /* Global vars */ extern u_int32_t newnfs_false, newnfs_true; @@ -125,8 +126,10 @@ nfsrv_postopattr(nd, 1, &nva); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); nfsmode = fxdr_unsigned(u_int32_t, *tl); + AUDIT_NFSARG_MODE(nd, nfsmode); if ((nd->nd_flag & ND_NFSV4) && (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP | NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE | @@ -235,6 +238,7 @@ if (nd->nd_repstat) goto out; + AUDIT_NFSARG_VNODE1(nd, vp); if (nd->nd_flag & ND_NFSV4) { error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL); if (error) { @@ -362,6 +366,7 @@ nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); #ifdef NFS4_ACL_EXTATTR_NAME aclp = acl_alloc(M_WAITOK); aclp->acl_cnt = 0; @@ -594,6 +599,8 @@ nfsvno_relpathbuf(&named); goto out; } + AUDIT_NFSARG_UPATH1_VP(nd, p, named.ni_rootdir, dp, + named.ni_cnd.cn_pnbuf); if (!nd->nd_repstat) { nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp); } else { @@ -615,6 +622,7 @@ vrele(named.ni_startdir); nfsvno_relpathbuf(&named); vp = named.ni_vp; + AUDIT_NFSARG_VNODE1(nd, vp); if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) && vp->v_type != VDIR && vp->v_type != VLNK) /* @@ -672,6 +680,7 @@ nfsrv_postopattr(nd, getret, &nva); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); if (vnode_vtype(vp) != VLNK) { if (nd->nd_flag & ND_NFSV2) nd->nd_repstat = ENXIO; @@ -723,6 +732,7 @@ nfsrv_postopattr(nd, getret, &nva); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); if (nd->nd_flag & ND_NFSV2) { NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); off = (off_t)fxdr_unsigned(u_int32_t, *tl++); @@ -910,6 +920,7 @@ nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); gotproxystateid = 0; if (nd->nd_flag & ND_NFSV2) { NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED); @@ -1101,6 +1112,8 @@ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); if (error) goto nfsmout; + AUDIT_NFSARG_UPATH1_VP(nd, p, named.ni_rootdir, dp, + named.ni_cnd.cn_pnbuf); if (!nd->nd_repstat) { NFSVNO_ATTRINIT(&nva); if (nd->nd_flag & ND_NFSV2) { @@ -1111,6 +1124,7 @@ NFSVNO_SETATTRVAL(&nva, type, vtyp); NFSVNO_SETATTRVAL(&nva, mode, nfstov_mode(sp->sa_mode)); + AUDIT_NFSARG_MODE(nd, nva.na_mode); switch (nva.na_type) { case VREG: tsize = fxdr_unsigned(int32_t, sp->sa_size); @@ -1201,6 +1215,7 @@ &exclusive_flag, cverf, rdev, exp); if (!nd->nd_repstat) { + AUDIT_NFSARG_VNODE1(nd, named.ni_vp); nd->nd_repstat = nfsvno_getfh(vp, &fh, p); if (!nd->nd_repstat) nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, @@ -1312,6 +1327,8 @@ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); if (error) goto nfsmout; + AUDIT_NFSARG_UPATH1_VP(nd, p, named.ni_rootdir, dp, + named.ni_cnd.cn_pnbuf); if (!nd->nd_repstat) { if (nd->nd_flag & ND_NFSV3) { NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); @@ -1327,6 +1344,7 @@ major = fxdr_unsigned(u_int32_t, *tl++); minor = fxdr_unsigned(u_int32_t, *tl); nva.na_rdev = NFSMAKEDEV(major, minor); + AUDIT_NFSARG_DEV(nd, nva.na_rdev); } } @@ -1361,6 +1379,7 @@ else nva.na_mode = 0400; } + AUDIT_NFSARG_MODE(nd, nva.na_mode); if (vtyp == VDIR) named.ni_cnd.cn_flags |= WILLBEDIR; @@ -1408,6 +1427,7 @@ if (!nd->nd_repstat) { vp = named.ni_vp; nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp); + AUDIT_NFSARG_VNODE1(nd, vp); nd->nd_repstat = nfsvno_getfh(vp, fhp, p); if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat) nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, @@ -1486,6 +1506,8 @@ nfsvno_relpathbuf(&named); goto out; } + AUDIT_NFSARG_UPATH1_VP(nd, p, named.ni_rootdir, dp, + named.ni_cnd.cn_pnbuf); if (!nd->nd_repstat) { nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp); } else { @@ -1502,6 +1524,7 @@ } } if (!nd->nd_repstat) { + AUDIT_NFSARG_VNODE1(nd, named.ni_vp); if (nd->nd_flag & ND_NFSV4) { if (vnode_vtype(named.ni_vp) == VDIR) nd->nd_repstat = nfsvno_rmdirsub(&named, 1, @@ -1579,6 +1602,8 @@ nfsvno_relpathbuf(&fromnd); goto out; } + AUDIT_NFSARG_UPATH1_VP(nd, p, fromnd.ni_rootdir, dp, + fromnd.ni_cnd.cn_pnbuf); /* * Unlock dp in this code section, so it is unlocked before * tdp gets locked. This avoids a potential LOR if tdp is the @@ -1642,6 +1667,8 @@ nfsvno_relpathbuf(&tond); goto out; } + AUDIT_NFSARG_UPATH2_VP(nd, p, tond.ni_rootdir, tdp, + tond.ni_cnd.cn_pnbuf); } if (nd->nd_repstat) { if (nd->nd_flag & ND_NFSV3) { @@ -1676,6 +1703,7 @@ nfsvno_relpathbuf(&tond); goto out; } + AUDIT_NFSARG_VNODE1(nd, fromnd.ni_vp); if (vnode_vtype(fromnd.ni_vp) == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp); @@ -1733,6 +1761,7 @@ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); NFSVOPUNLOCK(vp); if (vnode_vtype(vp) == VDIR) { if (nd->nd_flag & ND_NFSV4) @@ -1770,6 +1799,8 @@ nfsvno_relpathbuf(&named); goto out; } + AUDIT_NFSARG_UPATH1_VP(nd, p, named.ni_rootdir, dp, + named.ni_cnd.cn_pnbuf); if (!nd->nd_repstat) { nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes, p, &dirp); @@ -1839,8 +1870,11 @@ LOCKPARENT | SAVESTART | NOCACHE); nfsvno_setpathbuf(&named, &bufp, &hashp); error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); - if (!error && !nd->nd_repstat) + if (!error && !nd->nd_repstat) { + AUDIT_NFSARG_UPATH1_VP(nd, p, named.ni_rootdir, dp, + named.ni_cnd.cn_pnbuf); error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen); + } if (error) { vrele(dp); nfsvno_relpathbuf(&named); @@ -1868,6 +1902,7 @@ nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp, pathcp, pathlen); + AUDIT_NFSARG_VNODE1(nd, named.ni_vp); } else if (dirp != NULL) { dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL); vrele(dirp); @@ -1959,6 +1994,8 @@ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); if (error) goto nfsmout; + AUDIT_NFSARG_UPATH1_VP(nd, p, named.ni_rootdir, dp, + named.ni_cnd.cn_pnbuf); if (!nd->nd_repstat) { NFSVNO_ATTRINIT(&nva); if (nd->nd_flag & ND_NFSV3) { @@ -1970,6 +2007,7 @@ nva.na_mode = nfstov_mode(*tl++); } } + AUDIT_NFSARG_MODE(nd, nva.na_mode); if (!nd->nd_repstat) { nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp); } else { @@ -2000,6 +2038,7 @@ nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp); + AUDIT_NFSARG_VNODE1(nd, named.ni_vp); if (nd->nd_flag & ND_NFSV3) { if (!nd->nd_repstat) { (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1); @@ -2080,11 +2119,11 @@ u_int64_t off; struct thread *p = curthread; - if (nd->nd_repstat) { + if (nd->nd_repstat) { nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft); goto out; } - + AUDIT_NFSARG_VNODE1(nd, vp); /* Return NFSERR_ISDIR in NFSv4 when commit on a directory. */ if (vp->v_type != VREG) { if (nd->nd_flag & ND_NFSV3) @@ -2144,6 +2183,7 @@ nfsrv_postopattr(nd, getret, &at); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK); nd->nd_repstat = nfsvno_statfs(vp, sf); getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL); @@ -2202,6 +2242,7 @@ nfsrv_postopattr(nd, getret, &at); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL); nfsvno_getfs(&fs, isdgram); vput(vp); @@ -2242,13 +2283,14 @@ nfsrv_postopattr(nd, getret, &at); goto out; } + AUDIT_NFSARG_VNODE1(nd, vp); nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax, nd->nd_cred, p); if (!nd->nd_repstat) nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax, nd->nd_cred, p); if (!nd->nd_repstat) - nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED, + nd->nd_repstat = nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED, &chownres, nd->nd_cred, p); if (!nd->nd_repstat) nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, ¬runc, Index: sys/fs/nfsserver/nfs_nfsdsocket.c =================================================================== --- sys/fs/nfsserver/nfs_nfsdsocket.c +++ sys/fs/nfsserver/nfs_nfsdsocket.c @@ -460,6 +460,31 @@ NFSV4OP_COMMIT, }; +u_int16_t nfsrv_auevent[NFS_V3NPROCS] = { + AUE_NULL, + AUE_NFSRPC_GETATTR, + AUE_NFSRPC_SETATTR, + AUE_NFSRPC_LOOKUP, + AUE_NFSRPC_ACCESS, + AUE_NFSRPC_READLINK, + AUE_NFSRPC_READ, + AUE_NFSRPC_WRITE, + AUE_NFSRPC_CREATE, + AUE_NFSRPC_MKDIR, + AUE_NFSRPC_SYMLINK, + AUE_NFSRPC_MKNOD, + AUE_NFSRPC_REMOVE, + AUE_NFSRPC_RMDIR, + AUE_NFSRPC_RENAME, + AUE_NFSRPC_LINK, + AUE_NFSRPC_READDIR, + AUE_NFSRPC_READDIRPLUS, + AUE_NFSRPC_FSSTAT, + AUE_NFSRPC_FSINFO, + AUE_NFSRPC_PATHCONF, + AUE_NFSRPC_COMMIT, +}; + static struct mtx nfsrvd_statmtx; MTX_SYSINIT(nfsst, &nfsrvd_statmtx, "NFSstat", MTX_DEF); Index: sys/kern/vfs_cache.c =================================================================== --- sys/kern/vfs_cache.c +++ sys/kern/vfs_cache.c @@ -457,6 +457,11 @@ char *buf, char **retbuf, size_t *buflen); static int vn_fullpath_dir(struct thread *td, struct vnode *vp, struct vnode *rdir, char *buf, char **retbuf, size_t *len, bool slash_prefixed, size_t addend); +static int vn_fullpath_any_locked(struct thread *td, struct vnode *vp, + struct vnode *rdir, char *buf, char **retbuf, size_t *buflen); +static int vn_fullpath_dir_locked(struct thread *td, struct vnode *vp, + struct vnode *rdir, char *buf, char **retbuf, size_t *len, + bool slash_prefixed, size_t addend); static MALLOC_DEFINE(M_VFSCACHE, "vfscache", "VFS name cache entries"); @@ -2386,13 +2391,23 @@ { char *buf; size_t buflen; - int error; + int error, lktype; if (__predict_false(vn == NULL)) return (EINVAL); + lktype = VOP_ISLOCKED(vn); buflen = MAXPATHLEN; buf = malloc(buflen, M_TEMP, M_WAITOK); - error = vn_fullpath_any(td, vn, rootvnode, buf, retbuf, &buflen); + /* + * Should I check != LK_EXCLUSIVE && != LK_SHARED, because of + * LK_EXCLOTHER. Same doubt for everywhere I added VOP_ISLOCKED. + */ + if (lktype) + error = vn_fullpath_any_locked(td, vn, rootvnode, buf, retbuf, + &buflen); + else + error = vn_fullpath_any(td, vn, rootvnode, buf, retbuf, + &buflen); if (!error) *freebuf = buf; else @@ -2462,6 +2477,72 @@ return (0); } +/* + * The function works same as vn_vptocnp but for locked *vp. + */ +static int +vn_vptocnp_locked(struct vnode **vp, struct ucred *cred, char *buf, size_t *buflen) +{ + struct vnode *dvp; + struct namecache *ncp; + struct mtx *vlp; + int error; + + vlp = VP2VNODELOCK(*vp); + mtx_lock(vlp); + TAILQ_FOREACH(ncp, &((*vp)->v_cache_dst), nc_dst) { + if ((ncp->nc_flag & NCF_ISDOTDOT) == 0) + break; + } + if (ncp != NULL) { + if (*buflen < ncp->nc_nlen) { + mtx_unlock(vlp); + vunref(*vp); + counter_u64_add(numfullpathfail4, 1); + error = ENOMEM; + SDT_PROBE3(vfs, namecache, fullpath, return, error, + vp, NULL); + return (error); + } + *buflen -= ncp->nc_nlen; + memcpy(buf + *buflen, ncp->nc_name, ncp->nc_nlen); + SDT_PROBE3(vfs, namecache, fullpath, hit, ncp->nc_dvp, + ncp->nc_name, vp); + dvp = *vp; + *vp = ncp->nc_dvp; + vref(*vp); + mtx_unlock(vlp); + vrele(dvp); + return (0); + } + SDT_PROBE1(vfs, namecache, fullpath, miss, vp); + + mtx_unlock(vlp); + KASSERT(VOP_ISLOCKED(*vp) != 0, + ("vn_vptocnp_locked: vnode not locked")); + error = VOP_VPTOCNP(*vp, &dvp, cred, buf, buflen); + vunref(*vp); + if (error) { + counter_u64_add(numfullpathfail2, 1); + SDT_PROBE3(vfs, namecache, fullpath, return, error, vp, NULL); + return (error); + } + + *vp = dvp; + if (VN_IS_DOOMED(dvp)) { + /* forced unmount */ + vrele(dvp); + error = ENOENT; + SDT_PROBE3(vfs, namecache, fullpath, return, error, vp, NULL); + return (error); + } + /* + * *vp has its use count incremented still. + */ + + return (0); +} + /* * Resolve a directory to a pathname. * @@ -2573,6 +2654,139 @@ return (0); } +/* + * This function works same as vn_fullpath_dir but for locked vnode *vp. + */ +static int +vn_fullpath_dir_locked(struct thread *td, struct vnode *vp, struct vnode *rdir, + char *buf, char **retbuf, size_t *len, bool slash_prefixed, size_t addend) +{ +#ifdef KDTRACE_HOOKS + struct vnode *startvp = vp; +#endif + struct vnode *vp1; + size_t buflen; + int error; + bool islocked = true; + + VNPASS(vp->v_type == VDIR || VN_IS_DOOMED(vp), vp); + VNPASS(vp->v_usecount > 0, vp); + KASSERT(VOP_ISLOCKED(vp) != 0, + ("vn_fullpath_dir_locked: vp not locked")); + buflen = *len; + + if (!slash_prefixed) { + MPASS(*len >= 2); + buflen--; + buf[buflen] = '\0'; + } + + error = 0; + + SDT_PROBE1(vfs, namecache, fullpath, entry, vp); + counter_u64_add(numfullpathcalls, 1); + while (vp != rdir && vp != rootvnode) { + /* + * The vp vnode must be already fully constructed, + * since it is either found in namecache or obtained + * from VOP_VPTOCNP(). We may test for VV_ROOT safely + * without obtaining the vnode lock. + */ + if ((vp->v_vflag & VV_ROOT) != 0) { + if (!islocked) + vn_lock(vp, LK_RETRY | LK_SHARED); + + /* + * With the vnode locked, check for races with + * unmount, forced or not. Note that we + * already verified that vp is not equal to + * the root vnode, which means that + * mnt_vnodecovered can be NULL only for the + * case of unmount. + */ + if (VN_IS_DOOMED(vp) || + (vp1 = vp->v_mount->mnt_vnodecovered) == NULL || + vp1->v_mountedhere != vp->v_mount) { + if (!islocked) + vput(vp); + else + vunref(vp); + error = ENOENT; + SDT_PROBE3(vfs, namecache, fullpath, return, + error, vp, NULL); + break; + } + + vref(vp1); + if (!islocked) + vput(vp); + else + vunref(vp); + vp = vp1; + islocked = false; + continue; + } + islocked = (VOP_ISLOCKED(vp)) ? true : false; + if (vp->v_type != VDIR) { + if (!islocked) + vrele(vp); + else + vunref(vp); + counter_u64_add(numfullpathfail1, 1); + error = ENOTDIR; + SDT_PROBE3(vfs, namecache, fullpath, return, + error, vp, NULL); + break; + } + if (!islocked) + error = vn_vptocnp(&vp, td->td_ucred, buf, &buflen); + else + error = vn_vptocnp_locked(&vp, td->td_ucred, buf, + &buflen); + islocked = (VOP_ISLOCKED(vp)) ? true : false; + if (error) + break; + if (buflen == 0) { + if (!islocked) + vrele(vp); + else + vunref(vp); + error = ENOMEM; + SDT_PROBE3(vfs, namecache, fullpath, return, error, + startvp, NULL); + break; + } + buf[--buflen] = '/'; + slash_prefixed = true; + } + if (error) + return (error); + if (!slash_prefixed) { + if (buflen == 0) { + if (!islocked) + vrele(vp); + else + vunref(vp); + counter_u64_add(numfullpathfail4, 1); + SDT_PROBE3(vfs, namecache, fullpath, return, ENOMEM, + startvp, NULL); + return (ENOMEM); + } + buf[--buflen] = '/'; + } + counter_u64_add(numfullpathfound, 1); + if (!islocked) + vrele(vp); + else + vunref(vp); + + *retbuf = buf + buflen; + SDT_PROBE3(vfs, namecache, fullpath, return, 0, startvp, *retbuf); + *len -= buflen; + *len += addend; + return (0); +} + /* * Resolve an arbitrary vnode to a pathname. * @@ -2616,6 +2830,49 @@ orig_buflen - *buflen)); } +/* + * This function works same as vn_fullpath_any but for locked vnode. + */ +static int +vn_fullpath_any_locked(struct thread *td, struct vnode *vp, struct vnode *rdir, + char *buf, char **retbuf, size_t *buflen) +{ + size_t orig_buflen; + bool slash_prefixed; + int error, lktype; + + if (*buflen < 2) + return (EINVAL); + + orig_buflen = *buflen; + vref(vp); + slash_prefixed = false; + if (vp->v_type != VDIR) { + *buflen -= 1; + buf[*buflen] = '\0'; + error = vn_vptocnp_locked(&vp, td->td_ucred, buf, buflen); + if (error) + return (error); + lktype = VOP_ISLOCKED(vp); + if (*buflen == 0) { + if (lktype) + vunref(vp); + else + vrele(vp); + return (ENOMEM); + } + *buflen -= 1; + buf[*buflen] = '/'; + slash_prefixed = true; + if (lktype == 0) + return (vn_fullpath_dir(td, vp, rdir, buf, retbuf, + buflen, slash_prefixed, orig_buflen - *buflen)); + } + + return (vn_fullpath_dir_locked(td, vp, rdir, buf, retbuf, buflen, + slash_prefixed, orig_buflen - *buflen)); +} + /* * Resolve an arbitrary vnode to a pathname (taking care of hardlinks). * Index: sys/security/audit/audit.h =================================================================== --- sys/security/audit/audit.h +++ sys/security/audit/audit.h @@ -49,11 +49,29 @@ #error "no user-serviceable parts inside" #endif +/* + * These macro defintions are required for NFS part and including their + * NFS header files cause conflict with other structure declarations. + */ +#ifndef NFSMUTEX_T +#define NFSMUTEX_T struct mtx +#endif /* NFSMUTEX_T */ +#ifndef NFSOCKADDR_T +#define NFSSOCKADDR_T struct sockaddr * +#endif /* NFSOCKADDR_T */ + #include +#include + #include +#include #include +#include +#include +#include + /* * Audit subsystem condition flags. The audit_trail_enabled flag is set and * removed automatically as a result of configuring log files, and can be @@ -67,6 +85,10 @@ * * XXXRW: Move trail flags to audit_private.h, as they no longer need to be * visible outside the audit code...? + * + * XXX: For NFS audit, we check audit_sycalls_enabled flag to decide whether + * we should audit NFS RPCs or not. Audit NFS RPCs only if we are auditing the + * syscalls. We can probably rename the flag to audit_enabled? */ extern u_int audit_dtrace_enabled; extern int audit_trail_enabled; @@ -76,6 +98,8 @@ void audit_syscall_enter(unsigned short code, struct thread *td); void audit_syscall_exit(int error, struct thread *td); +void audit_nfsrpc_enter(struct nfsrv_descript *nd, struct thread *td); +void audit_nfsrpc_exit(struct nfsrv_descript *nd, 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 @@ -150,6 +174,19 @@ void audit_thread_alloc(struct thread *td); void audit_thread_free(struct thread *td); +void audit_nfsarg_dev(struct kaudit_record *ar, int dev); +void audit_nfsarg_mode(struct kaudit_record *ar, mode_t mode); +void audit_nfsarg_netsockaddr(struct kaudit_record *ar, + struct sockaddr *sa); +void audit_nfsarg_socket(struct kaudit_record *ar, int sodomain, + int sotype, int soprotocol); +void audit_nfsarg_text(struct kaudit_record *ar, const char *text); +void audit_nfsarg_upath1_vp(struct kaudit_record *ar, struct thread *td, + struct vnode *rdir, struct vnode *cdir, char *upath); +void audit_nfsarg_upath2_vp(struct kaudit_record *ar, struct thread *td, + struct vnode *rdir, struct vnode *cdir, char *upath); +void audit_nfsarg_value(struct kaudit_record *ar, long value); +void audit_nfsarg_vnode1(struct kaudit_record *ar, struct vnode *vp); /* * Define macros to wrap the audit_arg_* calls by checking the global * audit_syscalls_enabled flag before performing the actual call. @@ -410,6 +447,74 @@ audit_syscall_exit(error, td); \ } while (0) +/* + * Macros for wrapping audit_nfsarg_* calls. It checks the global + * audit_syscalls_enabled flag before performing the actual call. + */ +#define AUDITING_NFS(nd) (__predict_false((nd)->nd_flag & ND_AUDITREC)) + +#define AUDIT_NFSARG_DEV(nd, dev) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsarg_dev((nd)->nd_ar, (dev)); \ +} while (0) + +#define AUDIT_NFSARG_MODE(nd, mode) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsarg_mode((nd)->nd_ar, (mode)); \ +} while (0) + +#define AUDIT_NFSARG_NETSOCKADDR(nd, sa) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsarg_netsockaddr((nd)->nd_ar, (sa)); \ +} while (0) + +#define AUDIT_NFSARG_SOCKET(nd, sodomain, sotype, soprotocol) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsarg_socket((nd)->nd_ar, (sodomain), (sotype), \ + (soprotocol)); \ +} while (0) + +#define AUDIT_NFSARG_TEXT(nd, text) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsarg_text((nd)->nd_ar, (text)); \ +} while (0) + +#define AUDIT_NFSARG_UPATH1_VP(nd, td, rdir, cdir, upath) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsarg_upath1_vp((nd)->nd_ar, (td), (rdir), \ + (cdir), (upath)); \ +} while (0) + +#define AUDIT_NFSARG_UPATH2_VP(nd, td, rdir, cdir, upath) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsarg_upath2_vp((nd)->nd_ar, (td), (rdir), \ + (cdir), (upath)); \ +} while (0) + +#define AUDIT_NFSARG_VALUE(nd, value) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsarg_value((nd)->nd_ar, (value)); \ +} while (0) + +#define AUDIT_NFSARG_VNODE1(nd, vp) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsarg_vnode1((nd)->nd_ar, (vp)); \ +} while (0) + +#define AUDIT_NFSRPC_ENTER(nd, td) ({ \ + bool _audit_entered = false; \ + if (__predict_false(audit_syscalls_enabled)) { \ + audit_nfsrpc_enter((nd), (td)); \ + _audit_entered = true; \ + } \ + _audit_entered; \ +}) + +#define AUDIT_NFSRPC_EXIT(nd, td) do { \ + if (AUDITING_NFS(nd)) \ + audit_nfsrpc_exit((nd), (td)); \ +} while (0) + /* * A Macro to wrap the audit_sysclose() function. */ @@ -473,6 +578,19 @@ #define AUDIT_SYSCALL_ENTER(code, td) 0 #define AUDIT_SYSCALL_EXIT(error, td) +#define AUDIT_NFSARG_DEV(nd, dev) +#define AUDIT_NFSARG_MODE(nd, mode) +#define AUDIT_NFSARG_NETSOCKADDR(nd, sa) +#define AUDIT_NFSARG_SOCKET(nd, sodomain, sotype, soprotocol) +#define AUDIT_NFSARG_TEXT(nd, text) +#define AUDIT_NFSARG_UPATH1_VP(nd, td, rdir, cdir, upath) +#define AUDIT_NFSARG_UPATH2_VP(nd, td, rdir, cdir, upath) +#define AUDIT_NFSARG_VALUE(nd, value) +#define AUDIT_NFSARG_VNODE1(nd, vp) + +#define AUDIT_NFSRPC_ENTER(nd, td) 0 +#define AUDIT_NFSRPC_EXIT(nd, td) + #define AUDIT_SYSCLOSE(p, fd) #endif /* AUDIT */ Index: sys/security/audit/audit.c =================================================================== --- sys/security/audit/audit.c +++ sys/security/audit/audit.c @@ -84,6 +84,7 @@ FEATURE(audit, "BSM audit support"); static uma_zone_t audit_record_zone; +static uma_zone_t audit_nfsrecord_zone; static MALLOC_DEFINE(M_AUDITCRED, "audit_cred", "Audit cred storage"); MALLOC_DEFINE(M_AUDITDATA, "audit_data", "Audit data storage"); MALLOC_DEFINE(M_AUDITPATH, "audit_path", "Audit path storage"); @@ -274,6 +275,7 @@ bzero(ar, sizeof(*ar)); ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC; nanotime(&ar->k_ar.ar_starttime); + ar->kaudit_record_type = AUDIT_SYSCALL_RECORD; /* * Export the subject credential. @@ -326,6 +328,44 @@ free(ar->k_ar.ar_arg_groups.gidset, M_AUDITGIDSET); } +/* + * Construct an audit record for the passed nfs server + * request description. + */ +static int +audit_nfsrecord_ctor(void *mem, int size, void *arg, int flags) +{ + struct kaudit_record *ar; + struct nfsrv_descript *nd; + struct ucred *cred; + + KASSERT(sizeof(*ar) == size, ("audit_nfsrecord_ctor: wrong size")); + + nd = arg; + ar = mem; + bzero(ar, sizeof(*ar)); + ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC; + nanotime(&ar->k_ar.ar_starttime); + ar->kaudit_record_type = AUDIT_NFSRPC_RECORD; + + /* + * Export the subject credential. + */ + cred = nd->nd_cred; + cru2x(cred, &ar->k_ar.ar_subj_cred); + ar->k_ar.ar_subj_ruid = cred->cr_ruid; + ar->k_ar.ar_subj_rgid = cred->cr_rgid; + ar->k_ar.ar_subj_egid = cred->cr_groups[0]; + ar->k_ar.ar_subj_auid = cred->cr_audit.ai_auid; + ar->k_ar.ar_subj_asid = cred->cr_audit.ai_asid; + ar->k_ar.ar_subj_pid = 0; + ar->k_ar.ar_subj_amask = cred->cr_audit.ai_mask; + ar->k_ar.ar_subj_term_addr = cred->cr_audit.ai_termid; + ar->k_ar.ar_jailname[0] = '\0'; + + return (0); +} + /* * Initialize the Audit subsystem: configuration state, work queue, * synchronization primitives, worker thread, and trigger device node. Also @@ -370,6 +410,10 @@ sizeof(struct kaudit_record), audit_record_ctor, audit_record_dtor, NULL, NULL, UMA_ALIGN_PTR, 0); + audit_nfsrecord_zone = uma_zcreate("audit_nfsrecord", + sizeof(struct kaudit_record), audit_nfsrecord_ctor, + audit_record_dtor, NULL, NULL, UMA_ALIGN_PTR, 0); + /* First initialisation of audit_syscalls_enabled. */ audit_syscalls_enabled_update(); @@ -437,11 +481,44 @@ return (ar); } +struct kaudit_record * +audit_nfs_new(int event, struct nfsrv_descript *nd) +{ + struct kaudit_record *ar; + + /* This below comment statement (copied from audit_new) becomes untrue in case NFS audit + * records are created. Would this create any problem?? + * Note: the number of outstanding uncommitted audit records is + * limited to the number of concurrent threads servicing system calls + * in the kernel. + */ + + ar = uma_zalloc_arg(audit_nfsrecord_zone, nd, M_WAITOK); + ar->k_ar.ar_event = event; + + mtx_lock(&audit_mtx); + audit_pre_q_len++; + mtx_unlock(&audit_mtx); + + return (ar); +} + void audit_free(struct kaudit_record *ar) { - uma_zfree(audit_record_zone, ar); + switch (ar->kaudit_record_type) { + case AUDIT_SYSCALL_RECORD: + uma_zfree(audit_record_zone, ar); + break; + + case AUDIT_NFSRPC_RECORD: + uma_zfree(audit_nfsrecord_zone, ar); + break; + + default: + panic("audit_free: invalid case"); + } } void @@ -731,6 +808,93 @@ td->td_pflags &= ~TDP_AUDITREC; } +/* + * audit_nfsrpc_enter is called before NFS server is about to do a RPC. + * This function is very similiar to audit_syscall_enter. + */ +void +audit_nfsrpc_enter(struct nfsrv_descript *nd, struct thread *td) +{ + struct au_mask *aumask; + au_class_t class; + au_event_t event; + au_id_t auid; + int record_needed; + + KASSERT(nd->nd_ar == NULL, ("audit_nfsrpc_enter: nd->nd_ar != NULL")); + KASSERT((nd->nd_flag & ND_AUDITREC) == 0, + ("audit_nfsrpc_enter: ND_AUDITREC set")); + + /* Currently, NFSv4 is not supported. */ + if (!(nd->nd_flag & ND_NFSV4)) + event = nfsrv_auevent[nd->nd_procnum]; + else + event = AUE_NULL; + /* NFS Procedure NULL do nothing. So, no need to audit this event. */ + if (event == AUE_NULL) + return; + + memcpy(&(nd->nd_cred->cr_audit), &(td->td_ucred->cr_audit), + sizeof(struct auditinfo_addr)); + + /* + * The auid for NFS Audit events is AU_DEFAUDITID. The kernel + * non-attributable event mask is used as audit mask as all NFS Audit + * events are triggered from within the kernel. + */ + auid = nd->nd_cred->cr_audit.ai_auid; + + KASSERT(auid == AU_DEFAUDITID, + ("audit_nfsrpc_enter: NFS auid != AU_DEFAUDITID")); + + aumask = &audit_nae_mask; + class = au_event_class(event); + if (au_preselect(event, class, aumask, AU_PRS_BOTH)) { + /* + * If we're out of space and need to suspend unprivileged + * processes, do that here rather than trying to allocate + * another audit record. + */ + if (audit_in_failure && + priv_check(td, PRIV_AUDIT_FAILSTOP) != 0) { + cv_wait(&audit_fail_cv, &audit_mtx); + panic("audit_failing_stop: thread continued"); + } + record_needed = 1; + } else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0)) { + record_needed = 1; + } else { + record_needed = 0; + } + if (record_needed) { + nd->nd_ar = audit_nfs_new(event, nd); + if (nd->nd_ar != NULL) + nd->nd_flag |= ND_AUDITREC; + } else + nd->nd_ar = NULL; +} + +/* + * audit_nfsrpc_exit is called each time after NFS server has completed a + * RPC call. This function is very similiar to audit_syscall_exit. + */ +void +audit_nfsrpc_exit(struct nfsrv_descript *nd, __unused struct thread *td) +{ + int retval; + int error; + + error = nd->nd_repstat; + if (error) + retval = -1; + else + retval = *(nd->nd_errp); + + audit_commit(nd->nd_ar, error, retval); + nd->nd_ar = NULL; + nd->nd_flag &= ~ND_AUDITREC; +} + void audit_cred_copy(struct ucred *src, struct ucred *dest) { Index: sys/security/audit/audit_arg.c =================================================================== --- sys/security/audit/audit_arg.c +++ sys/security/audit/audit_arg.c @@ -1019,3 +1019,157 @@ VOP_UNLOCK(vp); fdrop(fp, td); } + +/* NFS RPC related audit args */ +void +audit_nfsarg_dev(struct kaudit_record *ar, int dev) +{ + + if (ar == NULL) + return; + + ar->k_ar.ar_arg_dev = dev; + ARG_SET_VALID(ar, ARG_DEV); +} + +void +audit_nfsarg_mode(struct kaudit_record *ar, mode_t mode) +{ + + if (ar == NULL) + return; + + ar->k_ar.ar_arg_mode = mode; + ARG_SET_VALID(ar, ARG_MODE); +} + +void +audit_nfsarg_netsockaddr(struct kaudit_record *ar, struct sockaddr *sa) +{ + + KASSERT(sa != NULL, ("audit_nfsarg_sockaddr: sa == NULL")); + + if (ar == NULL) + return; + + bcopy(sa, &ar->k_ar.ar_arg_sockaddr, sa->sa_len); + switch (sa->sa_family) { +//#ifdef INET + case AF_INET: + ARG_SET_VALID(ar, ARG_SADDRINET); + break; +//#endif +//#ifdef INET6 + case AF_INET6: + ARG_SET_VALID(ar, ARG_SADDRINET6); + break; +//#endif + default: + printf/*panic*/("audit_nfsarg_netsockaddr: invalid sa_family"); + } +} + +void +audit_nfsarg_socket(struct kaudit_record *ar, int sodomain, int sotype, + int soprotocol) +{ + + if (ar == NULL) + return; + + ar->k_ar.ar_arg_sockinfo.so_domain = sodomain; + ar->k_ar.ar_arg_sockinfo.so_type = sotype; + ar->k_ar.ar_arg_sockinfo.so_protocol = soprotocol; + ARG_SET_VALID(ar, ARG_SOCKINFO); +} + +void +audit_nfsarg_text(struct kaudit_record *ar, const char *text) +{ + + KASSERT(text != NULL, ("audit_arg_text: text == NULL")); + if (ar == NULL) + return; + + /* Invalidate the text string */ + ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_TEXT); + + if (ar->k_ar.ar_arg_text == NULL) + ar->k_ar.ar_arg_text = malloc(MAXPATHLEN, M_AUDITTEXT, + M_WAITOK); + + strncpy(ar->k_ar.ar_arg_text, text, MAXPATHLEN); + ARG_SET_VALID(ar, ARG_TEXT); +} + +void +audit_nfsarg_upath1_vp(struct kaudit_record *ar, struct thread *td, + struct vnode *rdir, struct vnode *cdir, char *upath) +{ + + if (ar == NULL) + return; + + audit_arg_upath_vp(td, rdir, cdir, upath, &ar->k_ar.ar_arg_upath1); + + ARG_SET_VALID(ar, ARG_UPATH1); +} + +void +audit_nfsarg_upath2_vp(struct kaudit_record *ar, struct thread *td, + struct vnode *rdir, struct vnode *cdir, char *upath) +{ + + if (ar == NULL) + return; + + audit_arg_upath_vp(td, rdir, cdir, upath, &ar->k_ar.ar_arg_upath2); + + ARG_SET_VALID(ar, ARG_UPATH2); +} + +void +audit_nfsarg_value(struct kaudit_record *ar, long value) +{ + + if(ar == NULL) + return; + + ar->k_ar.ar_arg_value = value; + ARG_SET_VALID(ar,ARG_VALUE); +} + +void +audit_nfsarg_vnode1(struct kaudit_record *ar, struct vnode *vp) +{ + int error, lktype = 0; + + /* Page fault panic occur if vnode *vp is NULL. */ + KASSERT(vp != NULL, ("audit_nfsarg_vnode1: vp == NULL")); + + if (ar == NULL) + return; + + lktype = VOP_ISLOCKED(vp); + /* + * As well as returning 0 for unlocked, VOP_ISLOCKED can return + * LK_EXCLOTHER for another thread holding a lock on it. + * + * XXX: audit_arg_vnode uses td_ucread. do we need nd_cr for NFS? + */ + ARG_CLEAR_VALID(ar, ARG_VNODE1); + /* Hold the vnode lock for VOP_GETTR call. */ + if (lktype != LK_EXCLUSIVE && lktype != LK_SHARED) { + vref(vp); + if (vn_lock(vp, LK_SHARED | LK_NOWAIT)) { + vrele(vp); + return; + } + } + error = audit_arg_vnode(vp, &ar->k_ar.ar_arg_vnode1); + if (lktype != LK_EXCLUSIVE && lktype != LK_SHARED) { + vput(vp); + } + if (error == 0) + ARG_SET_VALID(ar, ARG_VNODE1); +} Index: sys/security/audit/audit_bsm.c =================================================================== --- sys/security/audit/audit_bsm.c +++ sys/security/audit/audit_bsm.c @@ -1783,6 +1783,93 @@ case AUE_THR_EXIT: break; + /* TODO XXX: Should I also log NFS file handle? The sycalls events generally log + * FD VNODE and UPATH tokens. Following that analogy the NFS RPC event can + * can log filehandle. */ + case AUE_NFSRPC_GETATTR: + case AUE_NFSRPC_SETATTR: + if (ARG_IS_VALID(kar, ARG_VNODE1)) { + tok = au_to_attr32(&ar->ar_arg_vnode1); + kau_write(rec, tok); + } + break; + + case AUE_NFSRPC_LOOKUP: + UPATH1_VNODE1_TOKENS; + break; + + case AUE_NFSRPC_ACCESS: + if (ARG_IS_VALID(kar, ARG_VNODE1)) { + tok = au_to_attr32(&ar->ar_arg_vnode1); + kau_write(rec, tok); + } + /* XXX: argument # in this case? */ + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(3, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + break; + + case AUE_NFSRPC_READLINK: + case AUE_NFSRPC_READ: + case AUE_NFSRPC_WRITE: + if (ARG_IS_VALID(kar, ARG_VNODE1)) { + tok = au_to_attr32(&ar->ar_arg_vnode1); + kau_write(rec, tok); + } + break; + + case AUE_NFSRPC_CREATE: + case AUE_NFSRPC_MKDIR: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(3, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_NFSRPC_SYMLINK: + UPATH1_VNODE1_TOKENS; + break; + + case AUE_NFSRPC_MKNOD: + if (ARG_IS_VALID(kar, ARG_MODE)) { + tok = au_to_arg32(2, "mode", ar->ar_arg_mode); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_DEV)) { + tok = au_to_arg32(3, "dev", ar->ar_arg_dev); + kau_write(rec, tok); + } + UPATH1_VNODE1_TOKENS; + break; + + case AUE_NFSRPC_REMOVE: + case AUE_NFSRPC_RMDIR: + UPATH1_VNODE1_TOKENS; + break; + + case AUE_NFSRPC_RENAME: + UPATH1_VNODE1_TOKENS; + UPATH2_TOKENS; + break; + + case AUE_NFSRPC_LINK: + UPATH1_VNODE1_TOKENS; + break; + + case AUE_NFSRPC_READDIR: + case AUE_NFSRPC_READDIRPLUS: + case AUE_NFSRPC_FSSTAT: + case AUE_NFSRPC_FSINFO: + case AUE_NFSRPC_PATHCONF: + case AUE_NFSRPC_COMMIT: + if (ARG_IS_VALID(kar, ARG_VNODE1)) { + tok = au_to_attr32(&ar->ar_arg_vnode1); + kau_write(rec, tok); + } + break; + case AUE_NULL: default: printf("BSM conversion requested for unknown event %d\n", @@ -1797,6 +1884,16 @@ kau_free(rec); return (BSM_NOAUDIT); } + /* + * Write common tokens for NFS RPCs. + */ + if (kar->kaudit_record_type == AUDIT_NFSRPC_RECORD) { + if (ARG_IS_VALID(kar, ARG_SADDRINET)) { + tok = au_to_sock_inet((struct sockaddr_in *) + &ar->ar_arg_sockaddr); + kau_write(rec, tok); + } + } if (jail_tok != NULL) kau_write(rec, jail_tok); Index: sys/security/audit/audit_bsm_db.c =================================================================== --- sys/security/audit/audit_bsm_db.c +++ sys/security/audit/audit_bsm_db.c @@ -189,6 +189,13 @@ if (sysent[i].sy_auevent != AUE_NULL) au_evclassmap_insert(sysent[i].sy_auevent, 0); } + + /* + * Set up the initial event to class mapping for NFS RPC calls. + */ + for (i = 0; i < NFS_V3NPROCS; i++) { + au_evclassmap_insert(nfsrv_auevent[i], 0); + } } /* Index: sys/security/audit/audit_private.h =================================================================== --- sys/security/audit/audit_private.h +++ sys/security/audit/audit_private.h @@ -82,6 +82,10 @@ #define BSM_FAILURE 1 #define BSM_NOAUDIT 2 +/* Audit record type to differentiate between syscall and NFS record. */ +#define AUDIT_SYSCALL_RECORD 0 +#define AUDIT_NFSRPC_RECORD 1 + /* * Defines for the kernel audit record k_ar_commit field. Flags are set to * indicate what sort of record it is, and which preselection mechanism @@ -330,6 +334,7 @@ u_int k_ulen; /* User data length. */ struct uthread *k_uthread; /* Audited thread. */ void *k_dtaudit_state; + int kaudit_record_type; TAILQ_ENTRY(kaudit_record) k_q; }; TAILQ_HEAD(kaudit_queue, kaudit_record); @@ -342,6 +347,7 @@ void audit_commit(struct kaudit_record *ar, int error, int retval); struct kaudit_record *audit_new(int event, struct thread *td); +struct kaudit_record *audit_nfs_new(int event, struct nfsrv_descript *nd); /* * Function to update the audit_syscalls_enabled flag, whose value is affected