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. 48183f2e3SBryan 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> 438183f2e3SBryan 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/poll.h> 47eb9aea5aSDavid E. O'Brien #include <sys/proc.h> 48eb9aea5aSDavid E. O'Brien #include <sys/queue.h> 498183f2e3SBryan Drewery #include <sys/sx.h> 50eb9aea5aSDavid E. O'Brien #include <sys/syscall.h> 51eb9aea5aSDavid E. O'Brien #include <sys/sysent.h> 52eb9aea5aSDavid E. O'Brien #include <sys/sysproto.h> 53eb9aea5aSDavid E. O'Brien #include <sys/uio.h> 54eb9aea5aSDavid E. O'Brien 55eb9aea5aSDavid E. O'Brien #if __FreeBSD_version >= 900041 564a144410SRobert Watson #include <sys/capsicum.h> 57eb9aea5aSDavid E. O'Brien #endif 58eb9aea5aSDavid E. O'Brien 59eb9aea5aSDavid E. O'Brien #include "filemon.h" 60eb9aea5aSDavid E. O'Brien 61eb9aea5aSDavid E. O'Brien #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) 62eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_syscall.h> 63eb9aea5aSDavid E. O'Brien #include <compat/freebsd32/freebsd32_proto.h> 64eb9aea5aSDavid E. O'Brien 65eb9aea5aSDavid E. O'Brien extern struct sysentvec ia32_freebsd_sysvec; 66eb9aea5aSDavid E. O'Brien #endif 67eb9aea5aSDavid E. O'Brien 68eb9aea5aSDavid E. O'Brien extern struct sysentvec elf32_freebsd_sysvec; 69eb9aea5aSDavid E. O'Brien extern struct sysentvec elf64_freebsd_sysvec; 70eb9aea5aSDavid E. O'Brien 71eb9aea5aSDavid E. O'Brien static d_close_t filemon_close; 72eb9aea5aSDavid E. O'Brien static d_ioctl_t filemon_ioctl; 73eb9aea5aSDavid E. O'Brien static d_open_t filemon_open; 74eb9aea5aSDavid E. O'Brien static int filemon_unload(void); 75eb9aea5aSDavid E. O'Brien static void filemon_load(void *); 76eb9aea5aSDavid E. O'Brien 77eb9aea5aSDavid E. O'Brien static struct cdevsw filemon_cdevsw = { 78eb9aea5aSDavid E. O'Brien .d_version = D_VERSION, 79eb9aea5aSDavid E. O'Brien .d_close = filemon_close, 80eb9aea5aSDavid E. O'Brien .d_ioctl = filemon_ioctl, 81eb9aea5aSDavid E. O'Brien .d_open = filemon_open, 82eb9aea5aSDavid E. O'Brien .d_name = "filemon", 83eb9aea5aSDavid E. O'Brien }; 84eb9aea5aSDavid E. O'Brien 85eb9aea5aSDavid E. O'Brien MALLOC_DECLARE(M_FILEMON); 86eb9aea5aSDavid E. O'Brien MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); 87eb9aea5aSDavid E. O'Brien 88eb9aea5aSDavid E. O'Brien struct filemon { 89eb9aea5aSDavid E. O'Brien TAILQ_ENTRY(filemon) link; /* Link into the in-use list. */ 908183f2e3SBryan Drewery struct sx lock; /* Lock mutex for this filemon. */ 91eb9aea5aSDavid E. O'Brien struct file *fp; /* Output file pointer. */ 92eb9aea5aSDavid E. O'Brien pid_t pid; /* The process ID being monitored. */ 93eb9aea5aSDavid E. O'Brien char fname1[MAXPATHLEN]; /* Temporary filename buffer. */ 94eb9aea5aSDavid E. O'Brien char fname2[MAXPATHLEN]; /* Temporary filename buffer. */ 95eb9aea5aSDavid E. O'Brien char msgbufr[1024]; /* Output message buffer. */ 96eb9aea5aSDavid E. O'Brien }; 97eb9aea5aSDavid E. O'Brien 98eb9aea5aSDavid E. O'Brien static TAILQ_HEAD(, filemon) filemons_inuse = TAILQ_HEAD_INITIALIZER(filemons_inuse); 99eb9aea5aSDavid E. O'Brien static TAILQ_HEAD(, filemon) filemons_free = TAILQ_HEAD_INITIALIZER(filemons_free); 1008183f2e3SBryan Drewery static struct sx access_lock; 101eb9aea5aSDavid E. O'Brien 102eb9aea5aSDavid E. O'Brien static struct cdev *filemon_dev; 103eb9aea5aSDavid E. O'Brien 104eb9aea5aSDavid E. O'Brien #include "filemon_lock.c" 105eb9aea5aSDavid E. O'Brien #include "filemon_wrapper.c" 106eb9aea5aSDavid E. O'Brien 107eb9aea5aSDavid E. O'Brien static void 108eb9aea5aSDavid E. O'Brien filemon_dtr(void *data) 109eb9aea5aSDavid E. O'Brien { 110eb9aea5aSDavid E. O'Brien struct filemon *filemon = data; 111eb9aea5aSDavid E. O'Brien 112eb9aea5aSDavid E. O'Brien if (filemon != NULL) { 113eb9aea5aSDavid E. O'Brien struct file *fp = filemon->fp; 114eb9aea5aSDavid E. O'Brien 115eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 116eb9aea5aSDavid E. O'Brien filemon_lock_write(); 117eb9aea5aSDavid E. O'Brien 118eb9aea5aSDavid E. O'Brien /* Remove from the in-use list. */ 119eb9aea5aSDavid E. O'Brien TAILQ_REMOVE(&filemons_inuse, filemon, link); 120eb9aea5aSDavid E. O'Brien 121eb9aea5aSDavid E. O'Brien filemon->fp = NULL; 122eb9aea5aSDavid E. O'Brien filemon->pid = -1; 123eb9aea5aSDavid E. O'Brien 124eb9aea5aSDavid E. O'Brien /* Add to the free list. */ 125eb9aea5aSDavid E. O'Brien TAILQ_INSERT_TAIL(&filemons_free, filemon, link); 126eb9aea5aSDavid E. O'Brien 127eb9aea5aSDavid E. O'Brien /* Give up write access. */ 128eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 129eb9aea5aSDavid E. O'Brien 130eb9aea5aSDavid E. O'Brien if (fp != NULL) 131eb9aea5aSDavid E. O'Brien fdrop(fp, curthread); 132eb9aea5aSDavid E. O'Brien } 133eb9aea5aSDavid E. O'Brien } 134eb9aea5aSDavid E. O'Brien 135eb9aea5aSDavid E. O'Brien static int 136eb9aea5aSDavid E. O'Brien filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, 137eb9aea5aSDavid E. O'Brien struct thread *td) 138eb9aea5aSDavid E. O'Brien { 139eb9aea5aSDavid E. O'Brien int error = 0; 140eb9aea5aSDavid E. O'Brien struct filemon *filemon; 141872ce247SHiroki Sato struct proc *p; 1427008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041 1437008be5bSPawel Jakub Dawidek cap_rights_t rights; 1447008be5bSPawel Jakub Dawidek #endif 145eb9aea5aSDavid E. O'Brien 146*e8c87a09SBryan Drewery if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) 147*e8c87a09SBryan Drewery return (error); 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: 152fac4a7acSBryan Drewery if (filemon->fp != NULL) 153fac4a7acSBryan Drewery fdrop(filemon->fp, td); 154fac4a7acSBryan Drewery 1557008be5bSPawel Jakub Dawidek error = fget_write(td, *(int *)data, 1567008be5bSPawel Jakub Dawidek #if __FreeBSD_version >= 900041 1577008be5bSPawel Jakub Dawidek cap_rights_init(&rights, CAP_PWRITE), 1587008be5bSPawel Jakub Dawidek #endif 1597008be5bSPawel Jakub Dawidek &filemon->fp); 1607008be5bSPawel Jakub Dawidek if (error == 0) 161eb9aea5aSDavid E. O'Brien /* Write the file header. */ 162eb9aea5aSDavid E. O'Brien filemon_comment(filemon); 163eb9aea5aSDavid E. O'Brien break; 164eb9aea5aSDavid E. O'Brien 165eb9aea5aSDavid E. O'Brien /* Set the monitored process ID. */ 166eb9aea5aSDavid E. O'Brien case FILEMON_SET_PID: 16789cac24eSHiroki Sato error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT, 16889cac24eSHiroki Sato &p); 16989cac24eSHiroki Sato if (error == 0) { 170872ce247SHiroki Sato filemon->pid = p->p_pid; 171872ce247SHiroki Sato PROC_UNLOCK(p); 17289cac24eSHiroki Sato } 173eb9aea5aSDavid E. O'Brien break; 174eb9aea5aSDavid E. O'Brien 175eb9aea5aSDavid E. O'Brien default: 176eb9aea5aSDavid E. O'Brien error = EINVAL; 177eb9aea5aSDavid E. O'Brien break; 178eb9aea5aSDavid E. O'Brien } 179eb9aea5aSDavid E. O'Brien 180eb9aea5aSDavid E. O'Brien return (error); 181eb9aea5aSDavid E. O'Brien } 182eb9aea5aSDavid E. O'Brien 183eb9aea5aSDavid E. O'Brien static int 184eb9aea5aSDavid E. O'Brien filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, 185eb9aea5aSDavid E. O'Brien struct thread *td __unused) 186eb9aea5aSDavid E. O'Brien { 187eb9aea5aSDavid E. O'Brien struct filemon *filemon; 188eb9aea5aSDavid E. O'Brien 189eb9aea5aSDavid E. O'Brien /* Get exclusive write access. */ 190eb9aea5aSDavid E. O'Brien filemon_lock_write(); 191eb9aea5aSDavid E. O'Brien 192eb9aea5aSDavid E. O'Brien if ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) 193eb9aea5aSDavid E. O'Brien TAILQ_REMOVE(&filemons_free, filemon, link); 194eb9aea5aSDavid E. O'Brien 195eb9aea5aSDavid E. O'Brien /* Give up write access. */ 196eb9aea5aSDavid E. O'Brien filemon_unlock_write(); 197eb9aea5aSDavid E. O'Brien 198eb9aea5aSDavid E. O'Brien if (filemon == NULL) { 199eb9aea5aSDavid E. O'Brien filemon = malloc(sizeof(struct filemon), M_FILEMON, 200eb9aea5aSDavid E. O'Brien M_WAITOK | M_ZERO); 2018183f2e3SBryan 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 { 2318183f2e3SBryan 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); 2668183f2e3SBryan 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 2718183f2e3SBryan 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