1 /*- 2 * Copyright (c) 2011, David E. O'Brien. 3 * Copyright (c) 2009-2011, Juniper Networks, Inc. 4 * Copyright (c) 2015-2016, EMC Corp. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY JUNIPER NETWORKS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL JUNIPER NETWORKS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include "opt_compat.h" 33 34 #include <sys/param.h> 35 #include <sys/file.h> 36 #include <sys/systm.h> 37 #include <sys/buf.h> 38 #include <sys/capsicum.h> 39 #include <sys/condvar.h> 40 #include <sys/conf.h> 41 #include <sys/fcntl.h> 42 #include <sys/ioccom.h> 43 #include <sys/kernel.h> 44 #include <sys/lock.h> 45 #include <sys/malloc.h> 46 #include <sys/module.h> 47 #include <sys/poll.h> 48 #include <sys/proc.h> 49 #include <sys/sx.h> 50 #include <sys/syscall.h> 51 #include <sys/sysent.h> 52 #include <sys/sysproto.h> 53 #include <sys/uio.h> 54 55 #include "filemon.h" 56 57 #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) 58 #include <compat/freebsd32/freebsd32_syscall.h> 59 #include <compat/freebsd32/freebsd32_proto.h> 60 61 extern struct sysentvec ia32_freebsd_sysvec; 62 #endif 63 64 extern struct sysentvec elf32_freebsd_sysvec; 65 extern struct sysentvec elf64_freebsd_sysvec; 66 67 static d_close_t filemon_close; 68 static d_ioctl_t filemon_ioctl; 69 static d_open_t filemon_open; 70 71 static struct cdevsw filemon_cdevsw = { 72 .d_version = D_VERSION, 73 .d_close = filemon_close, 74 .d_ioctl = filemon_ioctl, 75 .d_open = filemon_open, 76 .d_name = "filemon", 77 }; 78 79 MALLOC_DECLARE(M_FILEMON); 80 MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); 81 82 /* 83 * The filemon->lock protects several things currently: 84 * - fname1/fname2/msgbufr are pre-allocated and used per syscall 85 * for logging and copyins rather than stack variables. 86 * - Serializing the filemon's log output. 87 * - Preventing inheritance or removal of the filemon into proc.p_filemon. 88 */ 89 struct filemon { 90 struct sx lock; /* Lock for this filemon. */ 91 struct file *fp; /* Output file pointer. */ 92 char fname1[MAXPATHLEN]; /* Temporary filename buffer. */ 93 char fname2[MAXPATHLEN]; /* Temporary filename buffer. */ 94 char msgbufr[1024]; /* Output message buffer. */ 95 u_int refcnt; /* Pointer reference count. */ 96 u_int proccnt; /* Process count. */ 97 }; 98 99 static struct cdev *filemon_dev; 100 static void filemon_output(struct filemon *filemon, char *msg, size_t len); 101 102 static __inline struct filemon * 103 filemon_acquire(struct filemon *filemon) 104 { 105 106 if (filemon != NULL) 107 refcount_acquire(&filemon->refcnt); 108 return (filemon); 109 } 110 111 /* 112 * Release a reference and on the last one write the footer and free the 113 * filemon. 114 */ 115 static void 116 filemon_release(struct filemon *filemon) 117 { 118 size_t len; 119 struct timeval now; 120 121 if (refcount_release(&filemon->refcnt) == 0) 122 return; 123 /* 124 * There are valid cases of releasing while locked, such as in 125 * filemon_untrack_processes, but none which are done where there 126 * is not at least 1 reference remaining. 127 */ 128 sx_assert(&filemon->lock, SA_UNLOCKED); 129 130 if (filemon->fp != NULL) { 131 getmicrotime(&now); 132 133 len = snprintf(filemon->msgbufr, 134 sizeof(filemon->msgbufr), 135 "# Stop %ju.%06ju\n# Bye bye\n", 136 (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); 137 138 filemon_output(filemon, filemon->msgbufr, len); 139 fdrop(filemon->fp, curthread); 140 } 141 142 sx_destroy(&filemon->lock); 143 free(filemon, M_FILEMON); 144 } 145 146 /* 147 * Acquire the proc's p_filemon reference and lock the filemon. 148 * The proc's p_filemon may not match this filemon on return. 149 */ 150 static struct filemon * 151 filemon_proc_get(struct proc *p) 152 { 153 struct filemon *filemon; 154 155 PROC_LOCK(p); 156 filemon = filemon_acquire(p->p_filemon); 157 PROC_UNLOCK(p); 158 159 if (filemon == NULL) 160 return (NULL); 161 /* 162 * The p->p_filemon may have changed by now. That case is handled 163 * by the exit and fork hooks and filemon_attach_proc specially. 164 */ 165 sx_xlock(&filemon->lock); 166 return (filemon); 167 } 168 169 /* Remove and release the filemon on the given process. */ 170 static void 171 filemon_proc_drop(struct proc *p) 172 { 173 struct filemon *filemon; 174 175 KASSERT(p->p_filemon != NULL, ("%s: proc %p NULL p_filemon", 176 __func__, p)); 177 sx_assert(&p->p_filemon->lock, SA_XLOCKED); 178 PROC_LOCK(p); 179 filemon = p->p_filemon; 180 p->p_filemon = NULL; 181 --filemon->proccnt; 182 PROC_UNLOCK(p); 183 /* 184 * This should not be the last reference yet. filemon_release() 185 * cannot be called with filemon locked, which the caller expects 186 * will stay locked. 187 */ 188 KASSERT(filemon->refcnt > 1, ("%s: proc %p dropping filemon %p " 189 "with last reference", __func__, p, filemon)); 190 filemon_release(filemon); 191 } 192 193 /* Unlock and release the filemon. */ 194 static __inline void 195 filemon_drop(struct filemon *filemon) 196 { 197 198 sx_xunlock(&filemon->lock); 199 filemon_release(filemon); 200 } 201 202 #include "filemon_wrapper.c" 203 204 static void 205 filemon_comment(struct filemon *filemon) 206 { 207 int len; 208 struct timeval now; 209 210 getmicrotime(&now); 211 212 len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), 213 "# filemon version %d\n# Target pid %d\n# Start %ju.%06ju\nV %d\n", 214 FILEMON_VERSION, curproc->p_pid, (uintmax_t)now.tv_sec, 215 (uintmax_t)now.tv_usec, FILEMON_VERSION); 216 217 filemon_output(filemon, filemon->msgbufr, len); 218 } 219 220 /* 221 * Invalidate the passed filemon in all processes. 222 */ 223 static void 224 filemon_untrack_processes(struct filemon *filemon) 225 { 226 struct proc *p; 227 228 sx_assert(&filemon->lock, SA_XLOCKED); 229 230 /* Avoid allproc loop if there is no need. */ 231 if (filemon->proccnt == 0) 232 return; 233 234 /* 235 * Processes in this list won't go away while here since 236 * filemon_event_process_exit() will lock on filemon->lock 237 * which we hold. 238 */ 239 sx_slock(&allproc_lock); 240 FOREACH_PROC_IN_SYSTEM(p) { 241 /* 242 * No PROC_LOCK is needed to compare here since it is 243 * guaranteed to not change since we have its filemon 244 * locked. Everything that changes this p_filemon will 245 * be locked on it. 246 */ 247 if (p->p_filemon == filemon) 248 filemon_proc_drop(p); 249 } 250 sx_sunlock(&allproc_lock); 251 252 /* 253 * It's possible some references were acquired but will be 254 * dropped shortly as they are restricted from being 255 * inherited. There is at least the reference in cdevpriv remaining. 256 */ 257 KASSERT(filemon->refcnt > 0, ("%s: filemon %p should have " 258 "references still.", __func__, filemon)); 259 KASSERT(filemon->proccnt == 0, ("%s: filemon %p should not have " 260 "attached procs still.", __func__, filemon)); 261 } 262 263 264 /* The devfs file is being closed. Untrace all processes. */ 265 static void 266 filemon_dtr(void *data) 267 { 268 struct filemon *filemon = data; 269 270 if (filemon == NULL) 271 return; 272 273 sx_xlock(&filemon->lock); 274 /* 275 * Detach the filemon. The actual closing of it may not 276 * occur until syscalls in other threads with references complete. 277 * The filemon cannot be inherited after this though. 278 */ 279 filemon_untrack_processes(filemon); 280 filemon_drop(filemon); 281 } 282 283 /* Attach the filemon to the process. */ 284 static int 285 filemon_attach_proc(struct filemon *filemon, struct proc *p) 286 { 287 struct filemon *filemon2; 288 289 sx_assert(&filemon->lock, SA_XLOCKED); 290 PROC_LOCK_ASSERT(p, MA_OWNED); 291 KASSERT((p->p_flag & P_WEXIT) == 0, 292 ("%s: filemon %p attaching to exiting process %p", 293 __func__, filemon, p)); 294 295 if (p->p_filemon == filemon) 296 return (0); 297 /* 298 * Don't allow truncating other process traces. It is 299 * not really intended to trace procs other than curproc 300 * anyhow. 301 */ 302 if (p->p_filemon != NULL && p != curproc) 303 return (EBUSY); 304 /* 305 * Historic behavior of filemon has been to let a child initiate 306 * tracing on itself and cease existing tracing. Bmake 307 * .META + .MAKE relies on this. It is only relevant for attaching to 308 * curproc. 309 */ 310 while (p->p_filemon != NULL) { 311 PROC_UNLOCK(p); 312 sx_xunlock(&filemon->lock); 313 while ((filemon2 = filemon_proc_get(p)) != NULL) { 314 /* It may have changed. */ 315 if (p->p_filemon == filemon2) 316 filemon_proc_drop(p); 317 filemon_drop(filemon2); 318 } 319 sx_xlock(&filemon->lock); 320 PROC_LOCK(p); 321 /* 322 * It may have been attached to, though unlikely. 323 * Try again if needed. 324 */ 325 } 326 327 KASSERT(p->p_filemon == NULL, 328 ("%s: proc %p didn't detach filemon %p", __func__, p, 329 p->p_filemon)); 330 p->p_filemon = filemon_acquire(filemon); 331 ++filemon->proccnt; 332 333 return (0); 334 } 335 336 static int 337 filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, 338 struct thread *td) 339 { 340 int error = 0; 341 struct filemon *filemon; 342 struct proc *p; 343 cap_rights_t rights; 344 345 if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) 346 return (error); 347 348 sx_xlock(&filemon->lock); 349 350 switch (cmd) { 351 /* Set the output file descriptor. */ 352 case FILEMON_SET_FD: 353 if (filemon->fp != NULL) { 354 error = EEXIST; 355 break; 356 } 357 358 error = fget_write(td, *(int *)data, 359 cap_rights_init(&rights, CAP_PWRITE), 360 &filemon->fp); 361 if (error == 0) 362 /* Write the file header. */ 363 filemon_comment(filemon); 364 break; 365 366 /* Set the monitored process ID. */ 367 case FILEMON_SET_PID: 368 /* Invalidate any existing processes already set. */ 369 filemon_untrack_processes(filemon); 370 371 error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT, 372 &p); 373 if (error == 0) { 374 KASSERT(p->p_filemon != filemon, 375 ("%s: proc %p didn't untrack filemon %p", 376 __func__, p, filemon)); 377 error = filemon_attach_proc(filemon, p); 378 PROC_UNLOCK(p); 379 } 380 break; 381 382 default: 383 error = EINVAL; 384 break; 385 } 386 387 sx_xunlock(&filemon->lock); 388 return (error); 389 } 390 391 static int 392 filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, 393 struct thread *td __unused) 394 { 395 int error; 396 struct filemon *filemon; 397 398 filemon = malloc(sizeof(*filemon), M_FILEMON, 399 M_WAITOK | M_ZERO); 400 sx_init(&filemon->lock, "filemon"); 401 refcount_init(&filemon->refcnt, 1); 402 403 error = devfs_set_cdevpriv(filemon, filemon_dtr); 404 if (error != 0) 405 filemon_release(filemon); 406 407 return (error); 408 } 409 410 static int 411 filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused, 412 struct thread *td __unused) 413 { 414 415 return (0); 416 } 417 418 static void 419 filemon_load(void *dummy __unused) 420 { 421 422 /* Install the syscall wrappers. */ 423 filemon_wrapper_install(); 424 425 filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, 426 "filemon"); 427 } 428 429 static int 430 filemon_unload(void) 431 { 432 433 destroy_dev(filemon_dev); 434 filemon_wrapper_deinstall(); 435 436 return (0); 437 } 438 439 static int 440 filemon_modevent(module_t mod __unused, int type, void *data) 441 { 442 int error = 0; 443 444 switch (type) { 445 case MOD_LOAD: 446 filemon_load(data); 447 break; 448 449 case MOD_UNLOAD: 450 error = filemon_unload(); 451 break; 452 453 case MOD_QUIESCE: 454 /* 455 * The wrapper implementation is unsafe for reliable unload. 456 * Require forcing an unload. 457 */ 458 error = EBUSY; 459 break; 460 461 case MOD_SHUTDOWN: 462 break; 463 464 default: 465 error = EOPNOTSUPP; 466 break; 467 468 } 469 470 return (error); 471 } 472 473 DEV_MODULE(filemon, filemon_modevent, NULL); 474 MODULE_VERSION(filemon, 1); 475