1 /*- 2 * Copyright (c) 2011, David E. O'Brien. 3 * Copyright (c) 2009-2011, Juniper Networks, Inc. 4 * Copyright (c) 2015, 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 <sys/imgact.h> 33 #include <sys/eventhandler.h> 34 #include <sys/sx.h> 35 #include <sys/vnode.h> 36 37 #include "opt_compat.h" 38 39 #if __FreeBSD_version > 800032 40 #define FILEMON_HAS_LINKAT 41 #endif 42 43 #if __FreeBSD_version < 900044 /* r225617 (2011-09-16) failed to bump 44 __FreeBSD_version. This really should 45 be based on "900045". "900044" is r225469 46 (2011-09-10) so this code is broken for 47 9-CURRENT September 10th-16th. */ 48 #define sys_chdir chdir 49 #define sys_link link 50 #define sys_open open 51 #define sys_rename rename 52 #define sys_stat stat 53 #define sys_symlink symlink 54 #define sys_unlink unlink 55 #ifdef FILEMON_HAS_LINKAT 56 #define sys_linkat linkat 57 #endif 58 #endif /* __FreeBSD_version */ 59 60 static eventhandler_tag filemon_exec_tag; 61 static eventhandler_tag filemon_exit_tag; 62 static eventhandler_tag filemon_fork_tag; 63 64 static void 65 filemon_output(struct filemon *filemon, char *msg, size_t len) 66 { 67 struct uio auio; 68 struct iovec aiov; 69 70 if (filemon->fp == NULL) 71 return; 72 73 aiov.iov_base = msg; 74 aiov.iov_len = len; 75 auio.uio_iov = &aiov; 76 auio.uio_iovcnt = 1; 77 auio.uio_resid = len; 78 auio.uio_segflg = UIO_SYSSPACE; 79 auio.uio_rw = UIO_WRITE; 80 auio.uio_td = curthread; 81 auio.uio_offset = (off_t) -1; 82 83 bwillwrite(); 84 85 fo_write(filemon->fp, &auio, curthread->td_ucred, 0, curthread); 86 } 87 88 static struct filemon * 89 filemon_pid_check(struct proc *p) 90 { 91 struct filemon *filemon; 92 93 filemon_lock_read(); 94 if (TAILQ_EMPTY(&filemons_inuse)) { 95 filemon_unlock_read(); 96 return (NULL); 97 } 98 sx_slock(&proctree_lock); 99 while (p != initproc) { 100 TAILQ_FOREACH(filemon, &filemons_inuse, link) { 101 if (p == filemon->p) { 102 sx_sunlock(&proctree_lock); 103 filemon_filemon_lock(filemon); 104 filemon_unlock_read(); 105 return (filemon); 106 } 107 } 108 p = proc_realparent(p); 109 } 110 sx_sunlock(&proctree_lock); 111 filemon_unlock_read(); 112 return (NULL); 113 } 114 115 static int 116 filemon_wrapper_chdir(struct thread *td, struct chdir_args *uap) 117 { 118 int ret; 119 size_t done; 120 size_t len; 121 struct filemon *filemon; 122 123 if ((ret = sys_chdir(td, uap)) == 0) { 124 if ((filemon = filemon_pid_check(curproc)) != NULL) { 125 copyinstr(uap->path, filemon->fname1, 126 sizeof(filemon->fname1), &done); 127 128 len = snprintf(filemon->msgbufr, 129 sizeof(filemon->msgbufr), "C %d %s\n", 130 curproc->p_pid, filemon->fname1); 131 132 filemon_output(filemon, filemon->msgbufr, len); 133 134 /* Unlock the found filemon structure. */ 135 filemon_filemon_unlock(filemon); 136 } 137 } 138 139 return (ret); 140 } 141 142 static void 143 filemon_event_process_exec(void *arg __unused, struct proc *p, 144 struct image_params *imgp) 145 { 146 struct filemon *filemon; 147 char *fullpath, *freepath; 148 size_t len; 149 150 if ((filemon = filemon_pid_check(p)) != NULL) { 151 fullpath = "<unknown>"; 152 freepath = NULL; 153 154 vn_fullpath(FIRST_THREAD_IN_PROC(p), imgp->vp, &fullpath, 155 &freepath); 156 157 len = snprintf(filemon->msgbufr, 158 sizeof(filemon->msgbufr), "E %d %s\n", 159 p->p_pid, fullpath); 160 161 filemon_output(filemon, filemon->msgbufr, len); 162 163 /* Unlock the found filemon structure. */ 164 filemon_filemon_unlock(filemon); 165 166 free(freepath, M_TEMP); 167 } 168 } 169 170 static int 171 filemon_wrapper_open(struct thread *td, struct open_args *uap) 172 { 173 int ret; 174 size_t done; 175 size_t len; 176 struct filemon *filemon; 177 178 if ((ret = sys_open(td, uap)) == 0) { 179 if ((filemon = filemon_pid_check(curproc)) != NULL) { 180 copyinstr(uap->path, filemon->fname1, 181 sizeof(filemon->fname1), &done); 182 183 if (uap->flags & O_RDWR) { 184 /* 185 * We'll get the W record below, but need 186 * to also output an R to distingish from 187 * O_WRONLY. 188 */ 189 len = snprintf(filemon->msgbufr, 190 sizeof(filemon->msgbufr), "R %d %s\n", 191 curproc->p_pid, filemon->fname1); 192 filemon_output(filemon, filemon->msgbufr, len); 193 } 194 195 196 len = snprintf(filemon->msgbufr, 197 sizeof(filemon->msgbufr), "%c %d %s\n", 198 (uap->flags & O_ACCMODE) ? 'W':'R', 199 curproc->p_pid, filemon->fname1); 200 filemon_output(filemon, filemon->msgbufr, len); 201 202 /* Unlock the found filemon structure. */ 203 filemon_filemon_unlock(filemon); 204 } 205 } 206 207 return (ret); 208 } 209 210 static int 211 filemon_wrapper_openat(struct thread *td, struct openat_args *uap) 212 { 213 int ret; 214 size_t done; 215 size_t len; 216 struct filemon *filemon; 217 218 if ((ret = sys_openat(td, uap)) == 0) { 219 if ((filemon = filemon_pid_check(curproc)) != NULL) { 220 copyinstr(uap->path, filemon->fname1, 221 sizeof(filemon->fname1), &done); 222 223 filemon->fname2[0] = '\0'; 224 if (filemon->fname1[0] != '/' && uap->fd != AT_FDCWD) { 225 /* 226 * rats - we cannot do too much about this. 227 * the trace should show a dir we read 228 * recently.. output an A record as a clue 229 * until we can do better. 230 */ 231 len = snprintf(filemon->msgbufr, 232 sizeof(filemon->msgbufr), "A %d %s\n", 233 curproc->p_pid, filemon->fname1); 234 filemon_output(filemon, filemon->msgbufr, len); 235 } 236 if (uap->flag & O_RDWR) { 237 /* 238 * We'll get the W record below, but need 239 * to also output an R to distingish from 240 * O_WRONLY. 241 */ 242 len = snprintf(filemon->msgbufr, 243 sizeof(filemon->msgbufr), "R %d %s%s\n", 244 curproc->p_pid, filemon->fname2, filemon->fname1); 245 filemon_output(filemon, filemon->msgbufr, len); 246 } 247 248 249 len = snprintf(filemon->msgbufr, 250 sizeof(filemon->msgbufr), "%c %d %s%s\n", 251 (uap->flag & O_ACCMODE) ? 'W':'R', 252 curproc->p_pid, filemon->fname2, filemon->fname1); 253 filemon_output(filemon, filemon->msgbufr, len); 254 255 /* Unlock the found filemon structure. */ 256 filemon_filemon_unlock(filemon); 257 } 258 } 259 260 return (ret); 261 } 262 263 static int 264 filemon_wrapper_rename(struct thread *td, struct rename_args *uap) 265 { 266 int ret; 267 size_t done; 268 size_t len; 269 struct filemon *filemon; 270 271 if ((ret = sys_rename(td, uap)) == 0) { 272 if ((filemon = filemon_pid_check(curproc)) != NULL) { 273 copyinstr(uap->from, filemon->fname1, 274 sizeof(filemon->fname1), &done); 275 copyinstr(uap->to, filemon->fname2, 276 sizeof(filemon->fname2), &done); 277 278 len = snprintf(filemon->msgbufr, 279 sizeof(filemon->msgbufr), "M %d '%s' '%s'\n", 280 curproc->p_pid, filemon->fname1, filemon->fname2); 281 282 filemon_output(filemon, filemon->msgbufr, len); 283 284 /* Unlock the found filemon structure. */ 285 filemon_filemon_unlock(filemon); 286 } 287 } 288 289 return (ret); 290 } 291 292 static int 293 filemon_wrapper_link(struct thread *td, struct link_args *uap) 294 { 295 int ret; 296 size_t done; 297 size_t len; 298 struct filemon *filemon; 299 300 if ((ret = sys_link(td, uap)) == 0) { 301 if ((filemon = filemon_pid_check(curproc)) != NULL) { 302 copyinstr(uap->path, filemon->fname1, 303 sizeof(filemon->fname1), &done); 304 copyinstr(uap->link, filemon->fname2, 305 sizeof(filemon->fname2), &done); 306 307 len = snprintf(filemon->msgbufr, 308 sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", 309 curproc->p_pid, filemon->fname1, filemon->fname2); 310 311 filemon_output(filemon, filemon->msgbufr, len); 312 313 /* Unlock the found filemon structure. */ 314 filemon_filemon_unlock(filemon); 315 } 316 } 317 318 return (ret); 319 } 320 321 static int 322 filemon_wrapper_symlink(struct thread *td, struct symlink_args *uap) 323 { 324 int ret; 325 size_t done; 326 size_t len; 327 struct filemon *filemon; 328 329 if ((ret = sys_symlink(td, uap)) == 0) { 330 if ((filemon = filemon_pid_check(curproc)) != NULL) { 331 copyinstr(uap->path, filemon->fname1, 332 sizeof(filemon->fname1), &done); 333 copyinstr(uap->link, filemon->fname2, 334 sizeof(filemon->fname2), &done); 335 336 len = snprintf(filemon->msgbufr, 337 sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", 338 curproc->p_pid, filemon->fname1, filemon->fname2); 339 340 filemon_output(filemon, filemon->msgbufr, len); 341 342 /* Unlock the found filemon structure. */ 343 filemon_filemon_unlock(filemon); 344 } 345 } 346 347 return (ret); 348 } 349 350 #ifdef FILEMON_HAS_LINKAT 351 static int 352 filemon_wrapper_linkat(struct thread *td, struct linkat_args *uap) 353 { 354 int ret; 355 size_t done; 356 size_t len; 357 struct filemon *filemon; 358 359 if ((ret = sys_linkat(td, uap)) == 0) { 360 if ((filemon = filemon_pid_check(curproc)) != NULL) { 361 copyinstr(uap->path1, filemon->fname1, 362 sizeof(filemon->fname1), &done); 363 copyinstr(uap->path2, filemon->fname2, 364 sizeof(filemon->fname2), &done); 365 366 len = snprintf(filemon->msgbufr, 367 sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", 368 curproc->p_pid, filemon->fname1, filemon->fname2); 369 370 filemon_output(filemon, filemon->msgbufr, len); 371 372 /* Unlock the found filemon structure. */ 373 filemon_filemon_unlock(filemon); 374 } 375 } 376 377 return (ret); 378 } 379 #endif 380 381 static int 382 filemon_wrapper_stat(struct thread *td, struct stat_args *uap) 383 { 384 int ret; 385 size_t done; 386 size_t len; 387 struct filemon *filemon; 388 389 if ((ret = sys_stat(td, uap)) == 0) { 390 if ((filemon = filemon_pid_check(curproc)) != NULL) { 391 copyinstr(uap->path, filemon->fname1, 392 sizeof(filemon->fname1), &done); 393 394 len = snprintf(filemon->msgbufr, 395 sizeof(filemon->msgbufr), "S %d %s\n", 396 curproc->p_pid, filemon->fname1); 397 398 filemon_output(filemon, filemon->msgbufr, len); 399 400 /* Unlock the found filemon structure. */ 401 filemon_filemon_unlock(filemon); 402 } 403 } 404 405 return (ret); 406 } 407 408 #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) 409 static int 410 filemon_wrapper_freebsd32_stat(struct thread *td, 411 struct freebsd32_stat_args *uap) 412 { 413 int ret; 414 size_t done; 415 size_t len; 416 struct filemon *filemon; 417 418 if ((ret = freebsd32_stat(td, uap)) == 0) { 419 if ((filemon = filemon_pid_check(curproc)) != NULL) { 420 copyinstr(uap->path, filemon->fname1, 421 sizeof(filemon->fname1), &done); 422 423 len = snprintf(filemon->msgbufr, 424 sizeof(filemon->msgbufr), "S %d %s\n", 425 curproc->p_pid, filemon->fname1); 426 427 filemon_output(filemon, filemon->msgbufr, len); 428 429 /* Unlock the found filemon structure. */ 430 filemon_filemon_unlock(filemon); 431 } 432 } 433 434 return (ret); 435 } 436 #endif 437 438 static void 439 filemon_event_process_exit(void *arg __unused, struct proc *p) 440 { 441 size_t len; 442 struct filemon *filemon; 443 struct timeval now; 444 445 /* Get timestamp before locking. */ 446 getmicrotime(&now); 447 448 if ((filemon = filemon_pid_check(p)) != NULL) { 449 len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), 450 "X %d %d %d\n", p->p_pid, p->p_xexit, p->p_xsig); 451 452 filemon_output(filemon, filemon->msgbufr, len); 453 454 /* Check if the monitored process is about to exit. */ 455 if (filemon->p == p) { 456 len = snprintf(filemon->msgbufr, 457 sizeof(filemon->msgbufr), 458 "# Stop %ju.%06ju\n# Bye bye\n", 459 (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); 460 461 filemon_output(filemon, filemon->msgbufr, len); 462 filemon->p = NULL; 463 } 464 465 /* Unlock the found filemon structure. */ 466 filemon_filemon_unlock(filemon); 467 } 468 } 469 470 static int 471 filemon_wrapper_unlink(struct thread *td, struct unlink_args *uap) 472 { 473 int ret; 474 size_t done; 475 size_t len; 476 struct filemon *filemon; 477 478 if ((ret = sys_unlink(td, uap)) == 0) { 479 if ((filemon = filemon_pid_check(curproc)) != NULL) { 480 copyinstr(uap->path, filemon->fname1, 481 sizeof(filemon->fname1), &done); 482 483 len = snprintf(filemon->msgbufr, 484 sizeof(filemon->msgbufr), "D %d %s\n", 485 curproc->p_pid, filemon->fname1); 486 487 filemon_output(filemon, filemon->msgbufr, len); 488 489 /* Unlock the found filemon structure. */ 490 filemon_filemon_unlock(filemon); 491 } 492 } 493 494 return (ret); 495 } 496 497 static void 498 filemon_event_process_fork(void *arg __unused, struct proc *p1, 499 struct proc *p2, int flags __unused) 500 { 501 size_t len; 502 struct filemon *filemon; 503 504 if ((filemon = filemon_pid_check(p1)) != NULL) { 505 len = snprintf(filemon->msgbufr, 506 sizeof(filemon->msgbufr), "F %d %d\n", 507 p1->p_pid, p2->p_pid); 508 509 filemon_output(filemon, filemon->msgbufr, len); 510 511 /* Unlock the found filemon structure. */ 512 filemon_filemon_unlock(filemon); 513 } 514 } 515 516 static void 517 filemon_wrapper_install(void) 518 { 519 #if defined(__LP64__) 520 struct sysent *sv_table = elf64_freebsd_sysvec.sv_table; 521 #else 522 struct sysent *sv_table = elf32_freebsd_sysvec.sv_table; 523 #endif 524 525 sv_table[SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir; 526 sv_table[SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open; 527 sv_table[SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat; 528 sv_table[SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename; 529 sv_table[SYS_stat].sy_call = (sy_call_t *) filemon_wrapper_stat; 530 sv_table[SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink; 531 sv_table[SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link; 532 sv_table[SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink; 533 #ifdef FILEMON_HAS_LINKAT 534 sv_table[SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat; 535 #endif 536 537 #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) 538 sv_table = ia32_freebsd_sysvec.sv_table; 539 540 sv_table[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir; 541 sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open; 542 sv_table[FREEBSD32_SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat; 543 sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename; 544 sv_table[FREEBSD32_SYS_freebsd32_stat].sy_call = (sy_call_t *) filemon_wrapper_freebsd32_stat; 545 sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink; 546 sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link; 547 sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink; 548 #ifdef FILEMON_HAS_LINKAT 549 sv_table[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat; 550 #endif 551 #endif /* COMPAT_ARCH32 */ 552 553 filemon_exec_tag = EVENTHANDLER_REGISTER(process_exec, 554 filemon_event_process_exec, NULL, EVENTHANDLER_PRI_LAST); 555 filemon_exit_tag = EVENTHANDLER_REGISTER(process_exit, 556 filemon_event_process_exit, NULL, EVENTHANDLER_PRI_LAST); 557 filemon_fork_tag = EVENTHANDLER_REGISTER(process_fork, 558 filemon_event_process_fork, NULL, EVENTHANDLER_PRI_LAST); 559 } 560 561 static void 562 filemon_wrapper_deinstall(void) 563 { 564 #if defined(__LP64__) 565 struct sysent *sv_table = elf64_freebsd_sysvec.sv_table; 566 #else 567 struct sysent *sv_table = elf32_freebsd_sysvec.sv_table; 568 #endif 569 570 sv_table[SYS_chdir].sy_call = (sy_call_t *)sys_chdir; 571 sv_table[SYS_open].sy_call = (sy_call_t *)sys_open; 572 sv_table[SYS_openat].sy_call = (sy_call_t *)sys_openat; 573 sv_table[SYS_rename].sy_call = (sy_call_t *)sys_rename; 574 sv_table[SYS_stat].sy_call = (sy_call_t *)sys_stat; 575 sv_table[SYS_unlink].sy_call = (sy_call_t *)sys_unlink; 576 sv_table[SYS_link].sy_call = (sy_call_t *)sys_link; 577 sv_table[SYS_symlink].sy_call = (sy_call_t *)sys_symlink; 578 #ifdef FILEMON_HAS_LINKAT 579 sv_table[SYS_linkat].sy_call = (sy_call_t *)sys_linkat; 580 #endif 581 582 #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) 583 sv_table = ia32_freebsd_sysvec.sv_table; 584 585 sv_table[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *)sys_chdir; 586 sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *)sys_open; 587 sv_table[FREEBSD32_SYS_openat].sy_call = (sy_call_t *)sys_openat; 588 sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *)sys_rename; 589 sv_table[FREEBSD32_SYS_freebsd32_stat].sy_call = (sy_call_t *)freebsd32_stat; 590 sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *)sys_unlink; 591 sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *)sys_link; 592 sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *)sys_symlink; 593 #ifdef FILEMON_HAS_LINKAT 594 sv_table[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *)sys_linkat; 595 #endif 596 #endif /* COMPAT_ARCH32 */ 597 598 EVENTHANDLER_DEREGISTER(process_exec, filemon_exec_tag); 599 EVENTHANDLER_DEREGISTER(process_exit, filemon_exit_tag); 600 EVENTHANDLER_DEREGISTER(process_fork, filemon_fork_tag); 601 } 602