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