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