1eb9aea5aSDavid E. O'Brien /*- 2eb9aea5aSDavid E. O'Brien * Copyright (c) 2011, David E. O'Brien. 3eb9aea5aSDavid E. O'Brien * Copyright (c) 2009-2011, Juniper Networks, Inc. 4*8183f2e3SBryan Drewery * Copyright (c) 2015, EMC Corp. 5eb9aea5aSDavid E. O'Brien * All rights reserved. 6eb9aea5aSDavid E. O'Brien * 7eb9aea5aSDavid E. O'Brien * Redistribution and use in source and binary forms, with or without 8eb9aea5aSDavid E. O'Brien * modification, are permitted provided that the following conditions 9eb9aea5aSDavid E. O'Brien * are met: 10eb9aea5aSDavid E. O'Brien * 1. Redistributions of source code must retain the above copyright 11eb9aea5aSDavid E. O'Brien * notice, this list of conditions and the following disclaimer. 12eb9aea5aSDavid E. O'Brien * 2. Redistributions in binary form must reproduce the above copyright 13eb9aea5aSDavid E. O'Brien * notice, this list of conditions and the following disclaimer in the 14eb9aea5aSDavid E. O'Brien * documentation and/or other materials provided with the distribution. 15eb9aea5aSDavid E. O'Brien * 16eb9aea5aSDavid E. O'Brien * THIS SOFTWARE IS PROVIDED BY JUNIPER NETWORKS AND CONTRIBUTORS ``AS IS'' AND 17eb9aea5aSDavid E. O'Brien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18eb9aea5aSDavid E. O'Brien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19eb9aea5aSDavid E. O'Brien * ARE DISCLAIMED. IN NO EVENT SHALL JUNIPER NETWORKS OR CONTRIBUTORS BE LIABLE 20eb9aea5aSDavid E. O'Brien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21eb9aea5aSDavid E. O'Brien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22eb9aea5aSDavid E. O'Brien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23eb9aea5aSDavid E. O'Brien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24eb9aea5aSDavid E. O'Brien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25eb9aea5aSDavid E. O'Brien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26eb9aea5aSDavid E. O'Brien * SUCH DAMAGE. 27eb9aea5aSDavid E. O'Brien */ 28eb9aea5aSDavid E. O'Brien 29af13de0fSJohn Baldwin #include <sys/cdefs.h> 30eb9aea5aSDavid E. O'Brien __FBSDID("$FreeBSD$"); 31eb9aea5aSDavid E. O'Brien 32f9d4b392SDavid E. O'Brien #include "opt_compat.h" 33f9d4b392SDavid E. O'Brien 34af13de0fSJohn Baldwin #include <sys/param.h> 35eb9aea5aSDavid E. O'Brien #include <sys/file.h> 36eb9aea5aSDavid E. O'Brien #include <sys/systm.h> 37eb9aea5aSDavid E. O'Brien #include <sys/buf.h> 38eb9aea5aSDavid E. O'Brien #include <sys/condvar.h> 39eb9aea5aSDavid E. O'Brien #include <sys/conf.h> 40eb9aea5aSDavid E. O'Brien #include <sys/fcntl.h> 41eb9aea5aSDavid E. O'Brien #include <sys/ioccom.h> 42eb9aea5aSDavid E. O'Brien #include <sys/kernel.h> 43*8183f2e3SBryan Drewery #include <sys/lock.h> 44eb9aea5aSDavid E. O'Brien #include <sys/malloc.h> 45eb9aea5aSDavid E. O'Brien #include <sys/module.h> 46eb9aea5aSDavid E. O'Brien #include <sys/mutex.h> 47eb9aea5aSDavid E. O'Brien #include <sys/poll.h> 48eb9aea5aSDavid E. O'Brien #include <sys/proc.h> 49eb9aea5aSDavid E. O'Brien #include <sys/queue.h> 50*8183f2e3SBryan Drewery #include <sys/sx.h> 51eb9aea5aSDavid E. O'Brien #include <sys/syscall.h> 52eb9aea5aSDavid E. O'Brien #include <sys/sysent.h> 53eb9aea5aSDavid E. O'Brien #include <sys/sysproto.h> 54eb9aea5aSDavid E. O'Brien #include <sys/uio.h> 55eb9aea5aSDavid E. O'Brien 56eb9aea5aSDavid E. O'Brien #if __FreeBSD_version >= 900041 574a144410SRobert Watson #include <sys/capsicum.h> 58eb9aea5aSDavid E. O'Brien #endif 59eb9aea5aSDavid E. O'Brien 60eb9aea5aSDavid E. O'Brien #include "filemon.h" 61eb9aea5aSDavid E. O'Brien 62eb9aea5aSDavid E. O'Brien #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) 63eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_syscall.h> 64eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_proto.h> 65eb9aea5aSDavid E. O'Brien 66eb9aea5aSDavid E. O'Brien extern struct sysentvec ia32_freebsd_sysvec; 67eb9aea5aSDavid E. O'Brien #endif 68eb9aea5aSDavid E. O'Brien 69eb9aea5aSDavid E. O'Brien extern struct sysentvec elf32_freebsd_sysvec; 70eb9aea5aSDavid E. O'Brien extern struct sysentvec elf64_freebsd_sysvec; 71eb9aea5aSDavid E. O'Brien 72eb9aea5aSDavid E. O'Brien static d_close_t filemon_close; 73eb9aea5aSDavid E. O'Brien static d_ioctl_t filemon_ioctl; 74eb9aea5aSDavid E. O'Brien static d_open_t filemon_open; 75eb9aea5aSDavid E. O'Brien static int filemon_unload(void); 76eb9aea5aSDavid E. O'Brien static void filemon_load(void *); 77eb9aea5aSDavid E. O'Brien 78eb9aea5aSDavid E. O'Brien static struct cdevsw filemon_cdevsw = { 79eb9aea5aSDavid E. O'Brien .d_version = D_VERSION, 80eb9aea5aSDavid E. O'Brien .d_close = filemon_close, 81eb9aea5aSDavid E. O'Brien .d_ioctl = filemon_ioctl, 82eb9aea5aSDavid E. O'Brien .d_open = filemon_open, 83eb9aea5aSDavid E. O'Brien .d_name = "filemon", 84eb9aea5aSDavid E. O'Brien }; 85eb9aea5aSDavid E. O'Brien 86eb9aea5aSDavid E. O'Brien MALLOC_DECLARE(M_FILEMON); 87eb9aea5aSDavid E. O'Brien MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); 88eb9aea5aSDavid E. O'Brien 89eb9aea5aSDavid E. O'Brien struct filemon { 90eb9aea5aSDavid E. O'Brien TAILQ_ENTRY(filemon) link; /* Link into the in-use list. */ 91*8183f2e3SBryan Drewery struct sx lock; /* Lock mutex for this filemon. */ 92eb9aea5aSDavid E. O'Brien struct file *fp; /* Output file pointer. */ 93eb9aea5aSDavid E. O'Brien pid_t pid; /* The process ID being monitored. */ 94eb9aea5aSDavid E. O'Brien char fname1[MAXPATHLEN]; /* Temporary filename buffer. */ 95eb9aea5aSDavid E. O'Brien char fname2[MAXPATHLEN]; /* Temporary filename buffer. */ 96eb9aea5aSDavid E. O'Brien char msgbufr[1024]; /* Output message buffer. */ 97eb9aea5aSDavid E. O'Brien }; 98eb9aea5aSDavid E. O'Brien 99eb9aea5aSDavid E. O'Brien static TAILQ_HEAD(, filemon) filemons_inuse = TAILQ_HEAD_INITIALIZER(filemons_inuse); 100eb9aea5aSDavid E. O'Brien static TAILQ_HEAD(, filemon) filemons_free = TAILQ_HEAD_INITIALIZER(filemons_free); 101*8183f2e3SBryan Drewery static struct sx access_lock; 102eb9aea5aSDavid E. O'Brien 103eb9aea5aSDavid E. O'Brien static struct cdev *filemon_dev; 104eb9aea5aSDavid E. O'Brien 105eb9aea5aSDavid E. O'Brien #include "filemon_lock.c" 106eb9aea5aSDavid E. O'Brien #include "filemon_wrapper.c" 107eb9aea5aSDavid E. O'Brien 108eb9aea5aSDavid E. O'Brien static void 109eb9aea5aSDavid E. O'Brien filemon_dtr(void *data) 110eb9aea5aSDavid E. O'Brien { 111eb9aea5aSDavid E. O'Brien struct filemon *filemon = data; 112eb9aea5aSDavid E. O'Brien 113eb9aea5aSDavid E. O'Brien if (filemon != NULL) { 114eb9aea5aSDavid E. O'Brien struct file *fp = filemon->fp; 115eb9aea5aSDavid E. O'Brien 116eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 117eb9aea5aSDavid E. O'Brien filemon_lock_write(); 118eb9aea5aSDavid E. O'Brien 119eb9aea5aSDavid E. O'Brien /* Remove from the in-use list. */ 120eb9aea5aSDavid E. O'Brien TAILQ_REMOVE(&filemons_inuse, filemon, link); 121eb9aea5aSDavid E. O'Brien 122eb9aea5aSDavid E. O'Brien filemon->fp = NULL; 123eb9aea5aSDavid E. O'Brien filemon->pid = -1; 124eb9aea5aSDavid E. O'Brien 125eb9aea5aSDavid E. O'Brien /* Add to the free list. */ 126eb9aea5aSDavid E. O'Brien TAILQ_INSERT_TAIL(&filemons_free, filemon, link); 127eb9aea5aSDavid E. O'Brien 128eb9aea5aSDavid E. O'Brien /* Give up write access. */ 129eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 130eb9aea5aSDavid E. O'Brien 131eb9aea5aSDavid E. O'Brien if (fp != NULL) 132eb9aea5aSDavid E. O'Brien fdrop(fp, curthread); 133eb9aea5aSDavid E. O'Brien } 134eb9aea5aSDavid E. O'Brien } 135eb9aea5aSDavid E. O'Brien 136eb9aea5aSDavid E. O'Brien static int 137eb9aea5aSDavid E. O'Brien filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, 138eb9aea5aSDavid E. O'Brien struct thread *td) 139eb9aea5aSDavid E. O'Brien { 140eb9aea5aSDavid E. O'Brien int error = 0; 141eb9aea5aSDavid E. O'Brien struct filemon *filemon; 142872ce247SHiroki Sato struct proc *p; 1437008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041 1447008be5bSPawel Jakub Dawidek cap_rights_t rights; 1457008be5bSPawel Jakub Dawidek #endif 146eb9aea5aSDavid E. O'Brien 147eb9aea5aSDavid E. O'Brien devfs_get_cdevpriv((void **) &filemon); 148eb9aea5aSDavid E. O'Brien 149eb9aea5aSDavid E. O'Brien switch (cmd) { 150eb9aea5aSDavid E. O'Brien /* Set the output file descriptor. */ 151eb9aea5aSDavid E. O'Brien case FILEMON_SET_FD: 1527008be5bSPawel Jakub Dawidek error = fget_write(td, *(int *)data, 1537008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041 1547008be5bSPawel Jakub Dawidek cap_rights_init(&rights, CAP_PWRITE), 1557008be5bSPawel Jakub Dawidek #endif 1567008be5bSPawel Jakub Dawidek &filemon->fp); 1577008be5bSPawel Jakub Dawidek if (error == 0) 158eb9aea5aSDavid E. O'Brien /* Write the file header. */ 159eb9aea5aSDavid E. O'Brien filemon_comment(filemon); 160eb9aea5aSDavid E. O'Brien break; 161eb9aea5aSDavid E. O'Brien 162eb9aea5aSDavid E. O'Brien /* Set the monitored process ID. */ 163eb9aea5aSDavid E. O'Brien case FILEMON_SET_PID: 16489cac24eSHiroki Sato error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT, 16589cac24eSHiroki Sato &p); 16689cac24eSHiroki Sato if (error == 0) { 167872ce247SHiroki Sato filemon->pid = p->p_pid; 168872ce247SHiroki Sato PROC_UNLOCK(p); 16989cac24eSHiroki Sato } 170eb9aea5aSDavid E. O'Brien break; 171eb9aea5aSDavid E. O'Brien 172eb9aea5aSDavid E. O'Brien default: 173eb9aea5aSDavid E. O'Brien error = EINVAL; 174eb9aea5aSDavid E. O'Brien break; 175eb9aea5aSDavid E. O'Brien } 176eb9aea5aSDavid E. O'Brien 177eb9aea5aSDavid E. O'Brien return (error); 178eb9aea5aSDavid E. O'Brien } 179eb9aea5aSDavid E. O'Brien 180eb9aea5aSDavid E. O'Brien static int 181eb9aea5aSDavid E. O'Brien filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, 182eb9aea5aSDavid E. O'Brien struct thread *td __unused) 183eb9aea5aSDavid E. O'Brien { 184eb9aea5aSDavid E. O'Brien struct filemon *filemon; 185eb9aea5aSDavid E. O'Brien 186eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 187eb9aea5aSDavid E. O'Brien filemon_lock_write(); 188eb9aea5aSDavid E. O'Brien 189eb9aea5aSDavid E. O'Brien if ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) 190eb9aea5aSDavid E. O'Brien TAILQ_REMOVE(&filemons_free, filemon, link); 191eb9aea5aSDavid E. O'Brien 192eb9aea5aSDavid E. O'Brien /* Give up write access. */ 193eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 194eb9aea5aSDavid E. O'Brien 195eb9aea5aSDavid E. O'Brien if (filemon == NULL) { 196eb9aea5aSDavid E. O'Brien filemon = malloc(sizeof(struct filemon), M_FILEMON, 197eb9aea5aSDavid E. O'Brien M_WAITOK | M_ZERO); 198eb9aea5aSDavid E. O'Brien 199eb9aea5aSDavid E. O'Brien filemon->fp = NULL; 200eb9aea5aSDavid E. O'Brien 201*8183f2e3SBryan Drewery sx_init(&filemon->lock, "filemon"); 202eb9aea5aSDavid E. O'Brien } 203eb9aea5aSDavid E. O'Brien 204eb9aea5aSDavid E. O'Brien filemon->pid = curproc->p_pid; 205eb9aea5aSDavid E. O'Brien 206eb9aea5aSDavid E. O'Brien devfs_set_cdevpriv(filemon, filemon_dtr); 207eb9aea5aSDavid E. O'Brien 208eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 209eb9aea5aSDavid E. O'Brien filemon_lock_write(); 210eb9aea5aSDavid E. O'Brien 211eb9aea5aSDavid E. O'Brien /* Add to the in-use list. */ 212eb9aea5aSDavid E. O'Brien TAILQ_INSERT_TAIL(&filemons_inuse, filemon, link); 213eb9aea5aSDavid E. O'Brien 214eb9aea5aSDavid E. O'Brien /* Give up write access. */ 215eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 216eb9aea5aSDavid E. O'Brien 217eb9aea5aSDavid E. O'Brien return (0); 218eb9aea5aSDavid E. O'Brien } 219eb9aea5aSDavid E. O'Brien 220eb9aea5aSDavid E. O'Brien static int 221eb9aea5aSDavid E. O'Brien filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused, 222eb9aea5aSDavid E. O'Brien struct thread *td __unused) 223eb9aea5aSDavid E. O'Brien { 224eb9aea5aSDavid E. O'Brien 225eb9aea5aSDavid E. O'Brien return (0); 226eb9aea5aSDavid E. O'Brien } 227eb9aea5aSDavid E. O'Brien 228eb9aea5aSDavid E. O'Brien static void 229eb9aea5aSDavid E. O'Brien filemon_load(void *dummy __unused) 230eb9aea5aSDavid E. O'Brien { 231*8183f2e3SBryan Drewery sx_init(&access_lock, "filemons_inuse"); 232eb9aea5aSDavid E. O'Brien 233eb9aea5aSDavid E. O'Brien /* Install the syscall wrappers. */ 234eb9aea5aSDavid E. O'Brien filemon_wrapper_install(); 235eb9aea5aSDavid E. O'Brien 236eb9aea5aSDavid E. O'Brien filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, 237eb9aea5aSDavid E. O'Brien "filemon"); 238eb9aea5aSDavid E. O'Brien } 239eb9aea5aSDavid E. O'Brien 240eb9aea5aSDavid E. O'Brien static int 241eb9aea5aSDavid E. O'Brien filemon_unload(void) 242eb9aea5aSDavid E. O'Brien { 243eb9aea5aSDavid E. O'Brien struct filemon *filemon; 244eb9aea5aSDavid E. O'Brien int error = 0; 245eb9aea5aSDavid E. O'Brien 246eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 247eb9aea5aSDavid E. O'Brien filemon_lock_write(); 248eb9aea5aSDavid E. O'Brien 249eb9aea5aSDavid E. O'Brien if (TAILQ_FIRST(&filemons_inuse) != NULL) 250eb9aea5aSDavid E. O'Brien error = EBUSY; 251eb9aea5aSDavid E. O'Brien else { 252eb9aea5aSDavid E. O'Brien destroy_dev(filemon_dev); 253eb9aea5aSDavid E. O'Brien 254eb9aea5aSDavid E. O'Brien /* Deinstall the syscall wrappers. */ 255eb9aea5aSDavid E. O'Brien filemon_wrapper_deinstall(); 256eb9aea5aSDavid E. O'Brien } 257eb9aea5aSDavid E. O'Brien 258eb9aea5aSDavid E. O'Brien /* Give up write access. */ 259eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 260eb9aea5aSDavid E. O'Brien 261eb9aea5aSDavid E. O'Brien if (error == 0) { 262eb9aea5aSDavid E. O'Brien /* free() filemon structs free list. */ 263eb9aea5aSDavid E. O'Brien filemon_lock_write(); 264eb9aea5aSDavid E. O'Brien while ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) { 265eb9aea5aSDavid E. O'Brien TAILQ_REMOVE(&filemons_free, filemon, link); 266*8183f2e3SBryan Drewery sx_destroy(&filemon->lock); 267eb9aea5aSDavid E. O'Brien free(filemon, M_FILEMON); 268eb9aea5aSDavid E. O'Brien } 269eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 270eb9aea5aSDavid E. O'Brien 271*8183f2e3SBryan Drewery sx_destroy(&access_lock); 272eb9aea5aSDavid E. O'Brien } 273eb9aea5aSDavid E. O'Brien 274eb9aea5aSDavid E. O'Brien return (error); 275eb9aea5aSDavid E. O'Brien } 276eb9aea5aSDavid E. O'Brien 277eb9aea5aSDavid E. O'Brien static int 278eb9aea5aSDavid E. O'Brien filemon_modevent(module_t mod __unused, int type, void *data) 279eb9aea5aSDavid E. O'Brien { 280eb9aea5aSDavid E. O'Brien int error = 0; 281eb9aea5aSDavid E. O'Brien 282eb9aea5aSDavid E. O'Brien switch (type) { 283eb9aea5aSDavid E. O'Brien case MOD_LOAD: 284eb9aea5aSDavid E. O'Brien filemon_load(data); 285eb9aea5aSDavid E. O'Brien break; 286eb9aea5aSDavid E. O'Brien 287eb9aea5aSDavid E. O'Brien case MOD_UNLOAD: 288eb9aea5aSDavid E. O'Brien error = filemon_unload(); 289eb9aea5aSDavid E. O'Brien break; 290eb9aea5aSDavid E. O'Brien 291eb9aea5aSDavid E. O'Brien case MOD_SHUTDOWN: 292eb9aea5aSDavid E. O'Brien break; 293eb9aea5aSDavid E. O'Brien 294eb9aea5aSDavid E. O'Brien default: 295eb9aea5aSDavid E. O'Brien error = EOPNOTSUPP; 296eb9aea5aSDavid E. O'Brien break; 297eb9aea5aSDavid E. O'Brien 298eb9aea5aSDavid E. O'Brien } 299eb9aea5aSDavid E. O'Brien 300eb9aea5aSDavid E. O'Brien return (error); 301eb9aea5aSDavid E. O'Brien } 302eb9aea5aSDavid E. O'Brien 303eb9aea5aSDavid E. O'Brien DEV_MODULE(filemon, filemon_modevent, NULL); 304eb9aea5aSDavid E. O'Brien MODULE_VERSION(filemon, 1); 305