1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2011, David E. O'Brien. 5 * Copyright (c) 2009-2011, Juniper Networks, Inc. 6 * Copyright (c) 2015-2016, EMC Corp. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY JUNIPER NETWORKS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL JUNIPER NETWORKS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/eventhandler.h> 35 #include <sys/filedesc.h> 36 #include <sys/imgact.h> 37 #include <sys/priv.h> 38 #include <sys/sx.h> 39 #include <sys/sysent.h> 40 #include <sys/vnode.h> 41 42 #include <machine/stdarg.h> 43 44 static void filemon_output_event(struct filemon *filemon, const char *fmt, ...) 45 __printflike(2, 3); 46 47 static eventhandler_tag filemon_exec_tag; 48 static eventhandler_tag filemon_exit_tag; 49 static eventhandler_tag filemon_fork_tag; 50 51 static void 52 filemon_output(struct filemon *filemon, char *msg, size_t len) 53 { 54 struct uio auio; 55 struct iovec aiov; 56 int error; 57 58 if (filemon->fp == NULL) 59 return; 60 61 aiov.iov_base = msg; 62 aiov.iov_len = len; 63 auio.uio_iov = &aiov; 64 auio.uio_iovcnt = 1; 65 auio.uio_resid = len; 66 auio.uio_segflg = UIO_SYSSPACE; 67 auio.uio_rw = UIO_WRITE; 68 auio.uio_td = curthread; 69 auio.uio_offset = (off_t) -1; 70 71 if (filemon->fp->f_type == DTYPE_VNODE) 72 bwillwrite(); 73 74 error = fo_write(filemon->fp, &auio, filemon->cred, 0, curthread); 75 if (error != 0 && filemon->error == 0) 76 filemon->error = error; 77 } 78 79 static void 80 filemon_output_event(struct filemon *filemon, const char *fmt, ...) 81 { 82 va_list ap; 83 size_t len; 84 85 va_start(ap, fmt); 86 len = vsnprintf(filemon->msgbufr, sizeof(filemon->msgbufr), fmt, ap); 87 va_end(ap); 88 /* The event is truncated but still worth logging. */ 89 if (len >= sizeof(filemon->msgbufr)) 90 len = sizeof(filemon->msgbufr) - 1; 91 filemon_output(filemon, filemon->msgbufr, len); 92 } 93 94 static int 95 filemon_wrapper_chdir(struct thread *td, struct chdir_args *uap) 96 { 97 int error, ret; 98 struct filemon *filemon; 99 100 if ((ret = sys_chdir(td, uap)) == 0) { 101 if ((filemon = filemon_proc_get(curproc)) != NULL) { 102 if ((error = copyinstr(uap->path, filemon->fname1, 103 sizeof(filemon->fname1), NULL)) != 0) { 104 filemon->error = error; 105 goto copyfail; 106 } 107 108 filemon_output_event(filemon, "C %d %s\n", 109 curproc->p_pid, filemon->fname1); 110 copyfail: 111 filemon_drop(filemon); 112 } 113 } 114 115 return (ret); 116 } 117 118 static void 119 filemon_event_process_exec(void *arg __unused, struct proc *p, 120 struct image_params *imgp) 121 { 122 struct filemon *filemon; 123 124 if ((filemon = filemon_proc_get(p)) != NULL) { 125 filemon_output_event(filemon, "E %d %s\n", 126 p->p_pid, 127 imgp->execpath != NULL ? imgp->execpath : "<unknown>"); 128 129 /* If the credentials changed then cease tracing. */ 130 if (imgp->newcred != NULL && 131 imgp->credential_setid && 132 priv_check_cred(filemon->cred, PRIV_DEBUG_DIFFCRED) != 0) { 133 /* 134 * It may have changed to NULL already, but 135 * will not be re-attached by anything else. 136 */ 137 if (p->p_filemon != NULL) { 138 KASSERT(p->p_filemon == filemon, 139 ("%s: proc %p didn't have expected" 140 " filemon %p", __func__, p, filemon)); 141 filemon_proc_drop(p); 142 } 143 } 144 145 146 filemon_drop(filemon); 147 } 148 } 149 150 static void 151 _filemon_wrapper_openat(struct thread *td, const char *upath, int flags, 152 int fd) 153 { 154 int error; 155 struct file *fp; 156 struct filemon *filemon; 157 char *atpath, *freepath; 158 cap_rights_t rights; 159 160 if ((filemon = filemon_proc_get(curproc)) != NULL) { 161 atpath = ""; 162 freepath = NULL; 163 fp = NULL; 164 165 if ((error = copyinstr(upath, filemon->fname1, 166 sizeof(filemon->fname1), NULL)) != 0) { 167 filemon->error = error; 168 goto copyfail; 169 } 170 171 if (filemon->fname1[0] != '/' && fd != AT_FDCWD) { 172 /* 173 * rats - we cannot do too much about this. 174 * the trace should show a dir we read 175 * recently.. output an A record as a clue 176 * until we can do better. 177 * XXX: This may be able to come out with 178 * the namecache lookup now. 179 */ 180 filemon_output_event(filemon, "A %d %s\n", 181 curproc->p_pid, filemon->fname1); 182 /* 183 * Try to resolve the path from the vnode using the 184 * namecache. It may be inaccurate, but better 185 * than nothing. 186 */ 187 if (getvnode(td, fd, 188 cap_rights_init_one(&rights, CAP_LOOKUP), &fp) == 0) { 189 vn_fullpath(fp->f_vnode, &atpath, &freepath); 190 } 191 } 192 if (flags & O_RDWR) { 193 /* 194 * We'll get the W record below, but need 195 * to also output an R to distinguish from 196 * O_WRONLY. 197 */ 198 filemon_output_event(filemon, "R %d %s%s%s\n", 199 curproc->p_pid, atpath, 200 atpath[0] != '\0' ? "/" : "", filemon->fname1); 201 } 202 203 filemon_output_event(filemon, "%c %d %s%s%s\n", 204 (flags & O_ACCMODE) ? 'W':'R', 205 curproc->p_pid, atpath, 206 atpath[0] != '\0' ? "/" : "", filemon->fname1); 207 copyfail: 208 filemon_drop(filemon); 209 if (fp != NULL) 210 fdrop(fp, td); 211 free(freepath, M_TEMP); 212 } 213 } 214 215 static int 216 filemon_wrapper_open(struct thread *td, struct open_args *uap) 217 { 218 int ret; 219 220 if ((ret = sys_open(td, uap)) == 0) 221 _filemon_wrapper_openat(td, uap->path, uap->flags, AT_FDCWD); 222 223 return (ret); 224 } 225 226 static int 227 filemon_wrapper_openat(struct thread *td, struct openat_args *uap) 228 { 229 int ret; 230 231 if ((ret = sys_openat(td, uap)) == 0) 232 _filemon_wrapper_openat(td, uap->path, uap->flag, uap->fd); 233 234 return (ret); 235 } 236 237 static int 238 filemon_wrapper_rename(struct thread *td, struct rename_args *uap) 239 { 240 int error, ret; 241 struct filemon *filemon; 242 243 if ((ret = sys_rename(td, uap)) == 0) { 244 if ((filemon = filemon_proc_get(curproc)) != NULL) { 245 if (((error = copyinstr(uap->from, filemon->fname1, 246 sizeof(filemon->fname1), NULL)) != 0) || 247 ((error = copyinstr(uap->to, filemon->fname2, 248 sizeof(filemon->fname2), NULL)) != 0)) { 249 filemon->error = error; 250 goto copyfail; 251 } 252 253 filemon_output_event(filemon, "M %d '%s' '%s'\n", 254 curproc->p_pid, filemon->fname1, filemon->fname2); 255 copyfail: 256 filemon_drop(filemon); 257 } 258 } 259 260 return (ret); 261 } 262 263 static void 264 _filemon_wrapper_link(struct thread *td, const char *upath1, 265 const char *upath2) 266 { 267 struct filemon *filemon; 268 int error; 269 270 if ((filemon = filemon_proc_get(curproc)) != NULL) { 271 if (((error = copyinstr(upath1, filemon->fname1, 272 sizeof(filemon->fname1), NULL)) != 0) || 273 ((error = copyinstr(upath2, filemon->fname2, 274 sizeof(filemon->fname2), NULL)) != 0)) { 275 filemon->error = error; 276 goto copyfail; 277 } 278 279 filemon_output_event(filemon, "L %d '%s' '%s'\n", 280 curproc->p_pid, filemon->fname1, filemon->fname2); 281 copyfail: 282 filemon_drop(filemon); 283 } 284 } 285 286 static int 287 filemon_wrapper_link(struct thread *td, struct link_args *uap) 288 { 289 int ret; 290 291 if ((ret = sys_link(td, uap)) == 0) 292 _filemon_wrapper_link(td, uap->path, uap->link); 293 294 return (ret); 295 } 296 297 static int 298 filemon_wrapper_symlink(struct thread *td, struct symlink_args *uap) 299 { 300 int ret; 301 302 if ((ret = sys_symlink(td, uap)) == 0) 303 _filemon_wrapper_link(td, uap->path, uap->link); 304 305 return (ret); 306 } 307 308 static int 309 filemon_wrapper_linkat(struct thread *td, struct linkat_args *uap) 310 { 311 int ret; 312 313 if ((ret = sys_linkat(td, uap)) == 0) 314 _filemon_wrapper_link(td, uap->path1, uap->path2); 315 316 return (ret); 317 } 318 319 static void 320 filemon_event_process_exit(void *arg __unused, struct proc *p) 321 { 322 struct filemon *filemon; 323 324 if ((filemon = filemon_proc_get(p)) != NULL) { 325 filemon_output_event(filemon, "X %d %d %d\n", 326 p->p_pid, p->p_xexit, p->p_xsig); 327 328 /* 329 * filemon_untrack_processes() may have dropped this p_filemon 330 * already while in filemon_proc_get() before acquiring the 331 * filemon lock. 332 */ 333 KASSERT(p->p_filemon == NULL || p->p_filemon == filemon, 334 ("%s: p %p was attached while exiting, expected " 335 "filemon %p or NULL", __func__, p, filemon)); 336 if (p->p_filemon == filemon) 337 filemon_proc_drop(p); 338 339 filemon_drop(filemon); 340 } 341 } 342 343 static int 344 filemon_wrapper_unlink(struct thread *td, struct unlink_args *uap) 345 { 346 int error, ret; 347 struct filemon *filemon; 348 349 if ((ret = sys_unlink(td, uap)) == 0) { 350 if ((filemon = filemon_proc_get(curproc)) != NULL) { 351 if ((error = copyinstr(uap->path, filemon->fname1, 352 sizeof(filemon->fname1), NULL)) != 0) { 353 filemon->error = error; 354 goto copyfail; 355 } 356 357 filemon_output_event(filemon, "D %d %s\n", 358 curproc->p_pid, filemon->fname1); 359 copyfail: 360 filemon_drop(filemon); 361 } 362 } 363 364 return (ret); 365 } 366 367 static void 368 filemon_event_process_fork(void *arg __unused, struct proc *p1, 369 struct proc *p2, int flags __unused) 370 { 371 struct filemon *filemon; 372 373 if ((filemon = filemon_proc_get(p1)) != NULL) { 374 filemon_output_event(filemon, "F %d %d\n", 375 p1->p_pid, p2->p_pid); 376 377 /* 378 * filemon_untrack_processes() or 379 * filemon_ioctl(FILEMON_SET_PID) may have changed the parent's 380 * p_filemon while in filemon_proc_get() before acquiring the 381 * filemon lock. Only inherit if the parent is still traced by 382 * this filemon. 383 */ 384 if (p1->p_filemon == filemon) { 385 PROC_LOCK(p2); 386 /* 387 * It may have been attached to already by a new 388 * filemon. 389 */ 390 if (p2->p_filemon == NULL) { 391 p2->p_filemon = filemon_acquire(filemon); 392 ++filemon->proccnt; 393 } 394 PROC_UNLOCK(p2); 395 } 396 397 filemon_drop(filemon); 398 } 399 } 400 401 static void 402 filemon_wrapper_install(void) 403 { 404 405 sysent[SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir; 406 sysent[SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open; 407 sysent[SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat; 408 sysent[SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename; 409 sysent[SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink; 410 sysent[SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link; 411 sysent[SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink; 412 sysent[SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat; 413 414 #if defined(COMPAT_FREEBSD32) 415 freebsd32_sysent[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir; 416 freebsd32_sysent[FREEBSD32_SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open; 417 freebsd32_sysent[FREEBSD32_SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat; 418 freebsd32_sysent[FREEBSD32_SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename; 419 freebsd32_sysent[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink; 420 freebsd32_sysent[FREEBSD32_SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link; 421 freebsd32_sysent[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink; 422 freebsd32_sysent[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat; 423 #endif /* COMPAT_FREEBSD32 */ 424 425 filemon_exec_tag = EVENTHANDLER_REGISTER(process_exec, 426 filemon_event_process_exec, NULL, EVENTHANDLER_PRI_LAST); 427 filemon_exit_tag = EVENTHANDLER_REGISTER(process_exit, 428 filemon_event_process_exit, NULL, EVENTHANDLER_PRI_LAST); 429 filemon_fork_tag = EVENTHANDLER_REGISTER(process_fork, 430 filemon_event_process_fork, NULL, EVENTHANDLER_PRI_LAST); 431 } 432 433 static void 434 filemon_wrapper_deinstall(void) 435 { 436 437 sysent[SYS_chdir].sy_call = (sy_call_t *)sys_chdir; 438 sysent[SYS_open].sy_call = (sy_call_t *)sys_open; 439 sysent[SYS_openat].sy_call = (sy_call_t *)sys_openat; 440 sysent[SYS_rename].sy_call = (sy_call_t *)sys_rename; 441 sysent[SYS_unlink].sy_call = (sy_call_t *)sys_unlink; 442 sysent[SYS_link].sy_call = (sy_call_t *)sys_link; 443 sysent[SYS_symlink].sy_call = (sy_call_t *)sys_symlink; 444 sysent[SYS_linkat].sy_call = (sy_call_t *)sys_linkat; 445 446 #if defined(COMPAT_FREEBSD32) 447 freebsd32_sysent[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *)sys_chdir; 448 freebsd32_sysent[FREEBSD32_SYS_open].sy_call = (sy_call_t *)sys_open; 449 freebsd32_sysent[FREEBSD32_SYS_openat].sy_call = (sy_call_t *)sys_openat; 450 freebsd32_sysent[FREEBSD32_SYS_rename].sy_call = (sy_call_t *)sys_rename; 451 freebsd32_sysent[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *)sys_unlink; 452 freebsd32_sysent[FREEBSD32_SYS_link].sy_call = (sy_call_t *)sys_link; 453 freebsd32_sysent[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *)sys_symlink; 454 freebsd32_sysent[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *)sys_linkat; 455 #endif /* COMPAT_FREEBSD32 */ 456 457 EVENTHANDLER_DEREGISTER(process_exec, filemon_exec_tag); 458 EVENTHANDLER_DEREGISTER(process_exit, filemon_exit_tag); 459 EVENTHANDLER_DEREGISTER(process_fork, filemon_fork_tag); 460 } 461