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