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: stable/11/sys/dev/filemon/filemon.c 343885 2019-02-07 23:55:11Z bdrewery $"); 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_FREEBSD32) 58#include <compat/freebsd32/freebsd32_syscall.h> 59#include <compat/freebsd32/freebsd32_proto.h> 60#include <compat/freebsd32/freebsd32_util.h> 61#endif 62 63static d_close_t filemon_close; 64static d_ioctl_t filemon_ioctl; 65static d_open_t filemon_open; 66 67static struct cdevsw filemon_cdevsw = { 68 .d_version = D_VERSION, 69 .d_close = filemon_close, 70 .d_ioctl = filemon_ioctl, 71 .d_open = filemon_open, 72 .d_name = "filemon", 73}; 74 75MALLOC_DECLARE(M_FILEMON); 76MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); 77 78/* 79 * The filemon->lock protects several things currently: 80 * - fname1/fname2/msgbufr are pre-allocated and used per syscall 81 * for logging and copyins rather than stack variables. 82 * - Serializing the filemon's log output. 83 * - Preventing inheritance or removal of the filemon into proc.p_filemon. 84 */ 85struct filemon { 86 struct sx lock; /* Lock for this filemon. */ 87 struct file *fp; /* Output file pointer. */ 88 struct ucred *cred; /* Credential of tracer. */ 89 char fname1[MAXPATHLEN]; /* Temporary filename buffer. */ 90 char fname2[MAXPATHLEN]; /* Temporary filename buffer. */ 91 char msgbufr[2*MAXPATHLEN + 100]; /* Output message buffer. */ 92 int error; /* Log write error, returned on close(2). */ 93 u_int refcnt; /* Pointer reference count. */ 94 u_int proccnt; /* Process count. */ 95}; 96 97static struct cdev *filemon_dev; 98static void filemon_output(struct filemon *filemon, char *msg, size_t len); 99 100static __inline struct filemon * 101filemon_acquire(struct filemon *filemon) 102{ 103 104 if (filemon != NULL) 105 refcount_acquire(&filemon->refcnt); 106 return (filemon); 107} 108 109/* 110 * Release a reference and free on the last one. 111 */ 112static void 113filemon_release(struct filemon *filemon) 114{ 115 116 if (refcount_release(&filemon->refcnt) == 0) 117 return; 118 /* 119 * There are valid cases of releasing while locked, such as in 120 * filemon_untrack_processes, but none which are done where there 121 * is not at least 1 reference remaining. 122 */ 123 sx_assert(&filemon->lock, SA_UNLOCKED); 124 125 if (filemon->cred != NULL) 126 crfree(filemon->cred); 127 sx_destroy(&filemon->lock); 128 free(filemon, M_FILEMON); 129} 130 131/* 132 * Acquire the proc's p_filemon reference and lock the filemon. 133 * The proc's p_filemon may not match this filemon on return. 134 */ 135static struct filemon * 136filemon_proc_get(struct proc *p) 137{ 138 struct filemon *filemon; 139 140 if (p->p_filemon == NULL) 141 return (NULL); 142 PROC_LOCK(p); 143 filemon = filemon_acquire(p->p_filemon); 144 PROC_UNLOCK(p); 145 146 if (filemon == NULL) 147 return (NULL); 148 /* 149 * The p->p_filemon may have changed by now. That case is handled 150 * by the exit and fork hooks and filemon_attach_proc specially. 151 */ 152 sx_xlock(&filemon->lock); 153 return (filemon); 154} 155 156/* Remove and release the filemon on the given process. */ 157static void 158filemon_proc_drop(struct proc *p) 159{ 160 struct filemon *filemon; 161 162 KASSERT(p->p_filemon != NULL, ("%s: proc %p NULL p_filemon", 163 __func__, p)); 164 sx_assert(&p->p_filemon->lock, SA_XLOCKED); 165 PROC_LOCK(p); 166 filemon = p->p_filemon; 167 p->p_filemon = NULL; 168 --filemon->proccnt; 169 PROC_UNLOCK(p); 170 /* 171 * This should not be the last reference yet. filemon_release() 172 * cannot be called with filemon locked, which the caller expects 173 * will stay locked. 174 */ 175 KASSERT(filemon->refcnt > 1, ("%s: proc %p dropping filemon %p " 176 "with last reference", __func__, p, filemon)); 177 filemon_release(filemon); 178} 179 180/* Unlock and release the filemon. */ 181static __inline void 182filemon_drop(struct filemon *filemon) 183{ 184 185 sx_xunlock(&filemon->lock); 186 filemon_release(filemon); 187} 188 189#include "filemon_wrapper.c" 190 191static void 192filemon_write_header(struct filemon *filemon) 193{ 194 int len; 195 struct timeval now; 196 197 getmicrotime(&now); 198 199 len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), 200 "# filemon version %d\n# Target pid %d\n# Start %ju.%06ju\nV %d\n", 201 FILEMON_VERSION, curproc->p_pid, (uintmax_t)now.tv_sec, 202 (uintmax_t)now.tv_usec, FILEMON_VERSION); 203 if (len < sizeof(filemon->msgbufr)) 204 filemon_output(filemon, filemon->msgbufr, len); 205} 206 207/* 208 * Invalidate the passed filemon in all processes. 209 */ 210static void 211filemon_untrack_processes(struct filemon *filemon) 212{ 213 struct proc *p; 214 215 sx_assert(&filemon->lock, SA_XLOCKED); 216 217 /* Avoid allproc loop if there is no need. */ 218 if (filemon->proccnt == 0) 219 return; 220 221 /* 222 * Processes in this list won't go away while here since 223 * filemon_event_process_exit() will lock on filemon->lock 224 * which we hold. 225 */ 226 sx_slock(&allproc_lock); 227 FOREACH_PROC_IN_SYSTEM(p) { 228 /* 229 * No PROC_LOCK is needed to compare here since it is 230 * guaranteed to not change since we have its filemon 231 * locked. Everything that changes this p_filemon will 232 * be locked on it. 233 */ 234 if (p->p_filemon == filemon) 235 filemon_proc_drop(p); 236 } 237 sx_sunlock(&allproc_lock); 238 239 /* 240 * It's possible some references were acquired but will be 241 * dropped shortly as they are restricted from being 242 * inherited. There is at least the reference in cdevpriv remaining. 243 */ 244 KASSERT(filemon->refcnt > 0, ("%s: filemon %p should have " 245 "references still.", __func__, filemon)); 246 KASSERT(filemon->proccnt == 0, ("%s: filemon %p should not have " 247 "attached procs still.", __func__, filemon)); 248} 249 250/* 251 * Close out the log. 252 */ 253static void 254filemon_close_log(struct filemon *filemon) 255{ 256 struct file *fp; 257 struct timeval now; 258 size_t len; 259 260 sx_assert(&filemon->lock, SA_XLOCKED); 261 if (filemon->fp == NULL) 262 return; 263 264 getmicrotime(&now); 265 266 len = snprintf(filemon->msgbufr, 267 sizeof(filemon->msgbufr), 268 "# Stop %ju.%06ju\n# Bye bye\n", 269 (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); 270 271 if (len < sizeof(filemon->msgbufr)) 272 filemon_output(filemon, filemon->msgbufr, len); 273 fp = filemon->fp; 274 filemon->fp = NULL; 275 276 sx_xunlock(&filemon->lock); 277 fdrop(fp, curthread); 278 sx_xlock(&filemon->lock); 279} 280 281/* 282 * The devfs file is being closed. Untrace all processes. It is possible 283 * filemon_close/close(2) was not called. 284 */ 285static void 286filemon_dtr(void *data) 287{ 288 struct filemon *filemon = data; 289 290 if (filemon == NULL) 291 return; 292 293 sx_xlock(&filemon->lock); 294 /* 295 * Detach the filemon. It cannot be inherited after this. 296 */ 297 filemon_untrack_processes(filemon); 298 filemon_close_log(filemon); 299 filemon_drop(filemon); 300} 301 302/* Attach the filemon to the process. */ 303static int 304filemon_attach_proc(struct filemon *filemon, struct proc *p) 305{ 306 struct filemon *filemon2; 307 308 sx_assert(&filemon->lock, SA_XLOCKED); 309 PROC_LOCK_ASSERT(p, MA_OWNED); 310 KASSERT((p->p_flag & P_WEXIT) == 0, 311 ("%s: filemon %p attaching to exiting process %p", 312 __func__, filemon, p)); 313 KASSERT((p->p_flag & P_INEXEC) == 0, 314 ("%s: filemon %p attaching to execing process %p", 315 __func__, filemon, p)); 316 317 if (p->p_filemon == filemon) 318 return (0); 319 /* 320 * Don't allow truncating other process traces. It is 321 * not really intended to trace procs other than curproc 322 * anyhow. 323 */ 324 if (p->p_filemon != NULL && p != curproc) 325 return (EBUSY); 326 /* 327 * Historic behavior of filemon has been to let a child initiate 328 * tracing on itself and cease existing tracing. Bmake 329 * .META + .MAKE relies on this. It is only relevant for attaching to 330 * curproc. 331 */ 332 while (p->p_filemon != NULL) { 333 PROC_UNLOCK(p); 334 sx_xunlock(&filemon->lock); 335 while ((filemon2 = filemon_proc_get(p)) != NULL) { 336 /* It may have changed. */ 337 if (p->p_filemon == filemon2) 338 filemon_proc_drop(p); 339 filemon_drop(filemon2); 340 } 341 sx_xlock(&filemon->lock); 342 PROC_LOCK(p); 343 /* 344 * It may have been attached to, though unlikely. 345 * Try again if needed. 346 */ 347 } 348 349 KASSERT(p->p_filemon == NULL, 350 ("%s: proc %p didn't detach filemon %p", __func__, p, 351 p->p_filemon)); 352 p->p_filemon = filemon_acquire(filemon); 353 ++filemon->proccnt; 354 355 return (0); 356} 357 358static int 359filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, 360 struct thread *td) 361{ 362 int error = 0; 363 struct filemon *filemon; 364 struct proc *p; 365 cap_rights_t rights; 366 367 if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) 368 return (error); 369 370 sx_xlock(&filemon->lock); 371 372 switch (cmd) { 373 /* Set the output file descriptor. */ 374 case FILEMON_SET_FD: 375 if (filemon->fp != NULL) { 376 error = EEXIST; 377 break; 378 } 379 380 error = fget_write(td, *(int *)data, 381 cap_rights_init(&rights, CAP_PWRITE), 382 &filemon->fp); 383 if (error == 0) 384 /* Write the file header. */ 385 filemon_write_header(filemon); 386 break; 387 388 /* Set the monitored process ID. */ 389 case FILEMON_SET_PID: 390 /* Invalidate any existing processes already set. */ 391 filemon_untrack_processes(filemon); 392 393 error = pget(*((pid_t *)data), 394 PGET_CANDEBUG | PGET_NOTWEXIT | PGET_NOTINEXEC, &p); 395 if (error == 0) { 396 KASSERT(p->p_filemon != filemon, 397 ("%s: proc %p didn't untrack filemon %p", 398 __func__, p, filemon)); 399 error = filemon_attach_proc(filemon, p); 400 PROC_UNLOCK(p); 401 } 402 break; 403 404 default: 405 error = EINVAL; 406 break; 407 } 408 409 sx_xunlock(&filemon->lock); 410 return (error); 411} 412 413static int 414filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, 415 struct thread *td) 416{ 417 int error; 418 struct filemon *filemon; 419 420 filemon = malloc(sizeof(*filemon), M_FILEMON, 421 M_WAITOK | M_ZERO); 422 sx_init(&filemon->lock, "filemon"); 423 refcount_init(&filemon->refcnt, 1); 424 filemon->cred = crhold(td->td_ucred); 425 426 error = devfs_set_cdevpriv(filemon, filemon_dtr); 427 if (error != 0) 428 filemon_release(filemon); 429 430 return (error); 431} 432 433/* Called on close of last devfs file handle, before filemon_dtr(). */ 434static int 435filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused, 436 struct thread *td __unused) 437{ 438 struct filemon *filemon; 439 int error; 440 441 if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) 442 return (error); 443 444 sx_xlock(&filemon->lock); 445 filemon_close_log(filemon); 446 error = filemon->error; 447 sx_xunlock(&filemon->lock); 448 /* 449 * Processes are still being traced but won't log anything 450 * now. After this call returns filemon_dtr() is called which 451 * will detach processes. 452 */ 453 454 return (error); 455} 456 457static void 458filemon_load(void *dummy __unused) 459{ 460 461 /* Install the syscall wrappers. */ 462 filemon_wrapper_install(); 463 464 filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, 465 "filemon"); 466} 467 468static int 469filemon_unload(void) 470{ 471 472 destroy_dev(filemon_dev); 473 filemon_wrapper_deinstall(); 474 475 return (0); 476} 477 478static int 479filemon_modevent(module_t mod __unused, int type, void *data) 480{ 481 int error = 0; 482 483 switch (type) { 484 case MOD_LOAD: 485 filemon_load(data); 486 break; 487 488 case MOD_UNLOAD: 489 error = filemon_unload(); 490 break; 491 492 case MOD_QUIESCE: 493 /* 494 * The wrapper implementation is unsafe for reliable unload. 495 * Require forcing an unload. 496 */ 497 error = EBUSY; 498 break; 499 500 case MOD_SHUTDOWN: 501 break; 502 503 default: 504 error = EOPNOTSUPP; 505 break; 506 507 } 508 509 return (error); 510} 511 512DEV_MODULE(filemon, filemon_modevent, NULL); 513MODULE_VERSION(filemon, 1); 514