xref: /freebsd/sys/dev/filemon/filemon_wrapper.c (revision d9f0ce31900a48d1a2bfc1c8c86f79d1e831451a)
1 /*-
2  * Copyright (c) 2011, David E. O'Brien.
3  * Copyright (c) 2009-2011, Juniper Networks, Inc.
4  * Copyright (c) 2015-2016, 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/eventhandler.h>
33 #include <sys/filedesc.h>
34 #include <sys/imgact.h>
35 #include <sys/sx.h>
36 #include <sys/vnode.h>
37 
38 #include "opt_compat.h"
39 
40 static eventhandler_tag filemon_exec_tag;
41 static eventhandler_tag filemon_exit_tag;
42 static eventhandler_tag filemon_fork_tag;
43 
44 static void
45 filemon_output(struct filemon *filemon, char *msg, size_t len)
46 {
47 	struct uio auio;
48 	struct iovec aiov;
49 	int error;
50 
51 	if (filemon->fp == NULL)
52 		return;
53 
54 	aiov.iov_base = msg;
55 	aiov.iov_len = len;
56 	auio.uio_iov = &aiov;
57 	auio.uio_iovcnt = 1;
58 	auio.uio_resid = len;
59 	auio.uio_segflg = UIO_SYSSPACE;
60 	auio.uio_rw = UIO_WRITE;
61 	auio.uio_td = curthread;
62 	auio.uio_offset = (off_t) -1;
63 
64 	if (filemon->fp->f_type == DTYPE_VNODE)
65 		bwillwrite();
66 
67 	error = fo_write(filemon->fp, &auio, curthread->td_ucred, 0, curthread);
68 	if (error != 0)
69 		filemon->error = error;
70 }
71 
72 static int
73 filemon_wrapper_chdir(struct thread *td, struct chdir_args *uap)
74 {
75 	int error, ret;
76 	size_t len;
77 	struct filemon *filemon;
78 
79 	if ((ret = sys_chdir(td, uap)) == 0) {
80 		if ((filemon = filemon_proc_get(curproc)) != NULL) {
81 			if ((error = copyinstr(uap->path, filemon->fname1,
82 			    sizeof(filemon->fname1), NULL)) != 0) {
83 				filemon->error = error;
84 				goto copyfail;
85 			}
86 
87 			len = snprintf(filemon->msgbufr,
88 			    sizeof(filemon->msgbufr), "C %d %s\n",
89 			    curproc->p_pid, filemon->fname1);
90 
91 			filemon_output(filemon, filemon->msgbufr, len);
92 copyfail:
93 			filemon_drop(filemon);
94 		}
95 	}
96 
97 	return (ret);
98 }
99 
100 static void
101 filemon_event_process_exec(void *arg __unused, struct proc *p,
102     struct image_params *imgp)
103 {
104 	struct filemon *filemon;
105 	char *fullpath, *freepath;
106 	size_t len;
107 
108 	if ((filemon = filemon_proc_get(p)) != NULL) {
109 		fullpath = "<unknown>";
110 		freepath = NULL;
111 
112 		vn_fullpath(curthread, imgp->vp, &fullpath, &freepath);
113 
114 		len = snprintf(filemon->msgbufr,
115 		    sizeof(filemon->msgbufr), "E %d %s\n",
116 		    p->p_pid, fullpath);
117 
118 		filemon_output(filemon, filemon->msgbufr, len);
119 
120 		filemon_drop(filemon);
121 
122 		free(freepath, M_TEMP);
123 	}
124 }
125 
126 static void
127 _filemon_wrapper_openat(struct thread *td, char *upath, int flags, int fd)
128 {
129 	int error;
130 	size_t len;
131 	struct file *fp;
132 	struct filemon *filemon;
133 	char *atpath, *freepath;
134 	cap_rights_t rights;
135 
136 	if ((filemon = filemon_proc_get(curproc)) != NULL) {
137 		atpath = "";
138 		freepath = NULL;
139 		fp = NULL;
140 
141 		if ((error = copyinstr(upath, filemon->fname1,
142 		    sizeof(filemon->fname1), NULL)) != 0) {
143 			filemon->error = error;
144 			goto copyfail;
145 		}
146 
147 		if (filemon->fname1[0] != '/' && fd != AT_FDCWD) {
148 			/*
149 			 * rats - we cannot do too much about this.
150 			 * the trace should show a dir we read
151 			 * recently.. output an A record as a clue
152 			 * until we can do better.
153 			 * XXX: This may be able to come out with
154 			 * the namecache lookup now.
155 			 */
156 			len = snprintf(filemon->msgbufr,
157 			    sizeof(filemon->msgbufr), "A %d %s\n",
158 			    curproc->p_pid, filemon->fname1);
159 			filemon_output(filemon, filemon->msgbufr, len);
160 			/*
161 			 * Try to resolve the path from the vnode using the
162 			 * namecache.  It may be inaccurate, but better
163 			 * than nothing.
164 			 */
165 			if (getvnode(td, fd,
166 			    cap_rights_init(&rights, CAP_LOOKUP), &fp) == 0) {
167 				vn_fullpath(td, fp->f_vnode, &atpath,
168 				    &freepath);
169 			}
170 		}
171 		if (flags & O_RDWR) {
172 			/*
173 			 * We'll get the W record below, but need
174 			 * to also output an R to distinguish from
175 			 * O_WRONLY.
176 			 */
177 			len = snprintf(filemon->msgbufr,
178 			    sizeof(filemon->msgbufr), "R %d %s%s%s\n",
179 			    curproc->p_pid, atpath,
180 			    atpath[0] != '\0' ? "/" : "", filemon->fname1);
181 			filemon_output(filemon, filemon->msgbufr, len);
182 		}
183 
184 		len = snprintf(filemon->msgbufr,
185 		    sizeof(filemon->msgbufr), "%c %d %s%s%s\n",
186 		    (flags & O_ACCMODE) ? 'W':'R',
187 		    curproc->p_pid, atpath,
188 		    atpath[0] != '\0' ? "/" : "", filemon->fname1);
189 		filemon_output(filemon, filemon->msgbufr, len);
190 copyfail:
191 		filemon_drop(filemon);
192 		if (fp != NULL)
193 			fdrop(fp, td);
194 		free(freepath, M_TEMP);
195 	}
196 }
197 
198 static int
199 filemon_wrapper_open(struct thread *td, struct open_args *uap)
200 {
201 	int ret;
202 
203 	if ((ret = sys_open(td, uap)) == 0)
204 		_filemon_wrapper_openat(td, uap->path, uap->flags, AT_FDCWD);
205 
206 	return (ret);
207 }
208 
209 static int
210 filemon_wrapper_openat(struct thread *td, struct openat_args *uap)
211 {
212 	int ret;
213 
214 	if ((ret = sys_openat(td, uap)) == 0)
215 		_filemon_wrapper_openat(td, uap->path, uap->flag, uap->fd);
216 
217 	return (ret);
218 }
219 
220 static int
221 filemon_wrapper_rename(struct thread *td, struct rename_args *uap)
222 {
223 	int error, ret;
224 	size_t len;
225 	struct filemon *filemon;
226 
227 	if ((ret = sys_rename(td, uap)) == 0) {
228 		if ((filemon = filemon_proc_get(curproc)) != NULL) {
229 			if (((error = copyinstr(uap->from, filemon->fname1,
230 			     sizeof(filemon->fname1), NULL)) != 0) ||
231 			    ((error = copyinstr(uap->to, filemon->fname2,
232 			     sizeof(filemon->fname2), NULL)) != 0)) {
233 				filemon->error = error;
234 				goto copyfail;
235 			}
236 
237 			len = snprintf(filemon->msgbufr,
238 			    sizeof(filemon->msgbufr), "M %d '%s' '%s'\n",
239 			    curproc->p_pid, filemon->fname1, filemon->fname2);
240 
241 			filemon_output(filemon, filemon->msgbufr, len);
242 copyfail:
243 			filemon_drop(filemon);
244 		}
245 	}
246 
247 	return (ret);
248 }
249 
250 static void
251 _filemon_wrapper_link(struct thread *td, char *upath1, char *upath2)
252 {
253 	struct filemon *filemon;
254 	size_t len;
255 	int error;
256 
257 	if ((filemon = filemon_proc_get(curproc)) != NULL) {
258 		if (((error = copyinstr(upath1, filemon->fname1,
259 		     sizeof(filemon->fname1), NULL)) != 0) ||
260 		    ((error = copyinstr(upath2, filemon->fname2,
261 		     sizeof(filemon->fname2), NULL)) != 0)) {
262 			filemon->error = error;
263 			goto copyfail;
264 		}
265 
266 		len = snprintf(filemon->msgbufr,
267 		    sizeof(filemon->msgbufr), "L %d '%s' '%s'\n",
268 		    curproc->p_pid, filemon->fname1, filemon->fname2);
269 
270 		filemon_output(filemon, filemon->msgbufr, len);
271 copyfail:
272 		filemon_drop(filemon);
273 	}
274 }
275 
276 static int
277 filemon_wrapper_link(struct thread *td, struct link_args *uap)
278 {
279 	int ret;
280 
281 	if ((ret = sys_link(td, uap)) == 0)
282 		_filemon_wrapper_link(td, uap->path, uap->link);
283 
284 	return (ret);
285 }
286 
287 static int
288 filemon_wrapper_symlink(struct thread *td, struct symlink_args *uap)
289 {
290 	int ret;
291 
292 	if ((ret = sys_symlink(td, uap)) == 0)
293 		_filemon_wrapper_link(td, uap->path, uap->link);
294 
295 	return (ret);
296 }
297 
298 static int
299 filemon_wrapper_linkat(struct thread *td, struct linkat_args *uap)
300 {
301 	int ret;
302 
303 	if ((ret = sys_linkat(td, uap)) == 0)
304 		_filemon_wrapper_link(td, uap->path1, uap->path2);
305 
306 	return (ret);
307 }
308 
309 static void
310 filemon_event_process_exit(void *arg __unused, struct proc *p)
311 {
312 	size_t len;
313 	struct filemon *filemon;
314 
315 	if ((filemon = filemon_proc_get(p)) != NULL) {
316 		len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr),
317 		    "X %d %d %d\n", p->p_pid, p->p_xexit, p->p_xsig);
318 
319 		filemon_output(filemon, filemon->msgbufr, len);
320 
321 		/*
322 		 * filemon_untrack_processes() may have dropped this p_filemon
323 		 * already while in filemon_proc_get() before acquiring the
324 		 * filemon lock.
325 		 */
326 		KASSERT(p->p_filemon == NULL || p->p_filemon == filemon,
327 		    ("%s: p %p was attached while exiting, expected "
328 		    "filemon %p or NULL", __func__, p, filemon));
329 		if (p->p_filemon == filemon)
330 			filemon_proc_drop(p);
331 
332 		filemon_drop(filemon);
333 	}
334 }
335 
336 static int
337 filemon_wrapper_unlink(struct thread *td, struct unlink_args *uap)
338 {
339 	int error, ret;
340 	size_t len;
341 	struct filemon *filemon;
342 
343 	if ((ret = sys_unlink(td, uap)) == 0) {
344 		if ((filemon = filemon_proc_get(curproc)) != NULL) {
345 			if ((error = copyinstr(uap->path, filemon->fname1,
346 			    sizeof(filemon->fname1), NULL)) != 0) {
347 				filemon->error = error;
348 				goto copyfail;
349 			}
350 
351 			len = snprintf(filemon->msgbufr,
352 			    sizeof(filemon->msgbufr), "D %d %s\n",
353 			    curproc->p_pid, filemon->fname1);
354 
355 			filemon_output(filemon, filemon->msgbufr, len);
356 copyfail:
357 			filemon_drop(filemon);
358 		}
359 	}
360 
361 	return (ret);
362 }
363 
364 static void
365 filemon_event_process_fork(void *arg __unused, struct proc *p1,
366     struct proc *p2, int flags __unused)
367 {
368 	size_t len;
369 	struct filemon *filemon;
370 
371 	if ((filemon = filemon_proc_get(p1)) != NULL) {
372 		len = snprintf(filemon->msgbufr,
373 		    sizeof(filemon->msgbufr), "F %d %d\n",
374 		    p1->p_pid, p2->p_pid);
375 
376 		filemon_output(filemon, filemon->msgbufr, len);
377 
378 		/*
379 		 * filemon_untrack_processes() or
380 		 * filemon_ioctl(FILEMON_SET_PID) may have changed the parent's
381 		 * p_filemon while in filemon_proc_get() before acquiring the
382 		 * filemon lock.  Only inherit if the parent is still traced by
383 		 * this filemon.
384 		 */
385 		if (p1->p_filemon == filemon) {
386 			PROC_LOCK(p2);
387 			/*
388 			 * It may have been attached to already by a new
389 			 * filemon.
390 			 */
391 			if (p2->p_filemon == NULL) {
392 				p2->p_filemon = filemon_acquire(filemon);
393 				++filemon->proccnt;
394 			}
395 			PROC_UNLOCK(p2);
396 		}
397 
398 		filemon_drop(filemon);
399 	}
400 }
401 
402 static void
403 filemon_wrapper_install(void)
404 {
405 #if defined(__LP64__)
406 	struct sysent *sv_table = elf64_freebsd_sysvec.sv_table;
407 #else
408 	struct sysent *sv_table = elf32_freebsd_sysvec.sv_table;
409 #endif
410 
411 	sv_table[SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir;
412 	sv_table[SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open;
413 	sv_table[SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat;
414 	sv_table[SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename;
415 	sv_table[SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink;
416 	sv_table[SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link;
417 	sv_table[SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink;
418 	sv_table[SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat;
419 
420 #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
421 	sv_table = ia32_freebsd_sysvec.sv_table;
422 
423 	sv_table[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir;
424 	sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open;
425 	sv_table[FREEBSD32_SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat;
426 	sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename;
427 	sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink;
428 	sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link;
429 	sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink;
430 	sv_table[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat;
431 #endif	/* COMPAT_ARCH32 */
432 
433 	filemon_exec_tag = EVENTHANDLER_REGISTER(process_exec,
434 	    filemon_event_process_exec, NULL, EVENTHANDLER_PRI_LAST);
435 	filemon_exit_tag = EVENTHANDLER_REGISTER(process_exit,
436 	    filemon_event_process_exit, NULL, EVENTHANDLER_PRI_LAST);
437 	filemon_fork_tag = EVENTHANDLER_REGISTER(process_fork,
438 	    filemon_event_process_fork, NULL, EVENTHANDLER_PRI_LAST);
439 }
440 
441 static void
442 filemon_wrapper_deinstall(void)
443 {
444 #if defined(__LP64__)
445 	struct sysent *sv_table = elf64_freebsd_sysvec.sv_table;
446 #else
447 	struct sysent *sv_table = elf32_freebsd_sysvec.sv_table;
448 #endif
449 
450 	sv_table[SYS_chdir].sy_call = (sy_call_t *)sys_chdir;
451 	sv_table[SYS_open].sy_call = (sy_call_t *)sys_open;
452 	sv_table[SYS_openat].sy_call = (sy_call_t *)sys_openat;
453 	sv_table[SYS_rename].sy_call = (sy_call_t *)sys_rename;
454 	sv_table[SYS_unlink].sy_call = (sy_call_t *)sys_unlink;
455 	sv_table[SYS_link].sy_call = (sy_call_t *)sys_link;
456 	sv_table[SYS_symlink].sy_call = (sy_call_t *)sys_symlink;
457 	sv_table[SYS_linkat].sy_call = (sy_call_t *)sys_linkat;
458 
459 #if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32)
460 	sv_table = ia32_freebsd_sysvec.sv_table;
461 
462 	sv_table[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *)sys_chdir;
463 	sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *)sys_open;
464 	sv_table[FREEBSD32_SYS_openat].sy_call = (sy_call_t *)sys_openat;
465 	sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *)sys_rename;
466 	sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *)sys_unlink;
467 	sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *)sys_link;
468 	sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *)sys_symlink;
469 	sv_table[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *)sys_linkat;
470 #endif	/* COMPAT_ARCH32 */
471 
472 	EVENTHANDLER_DEREGISTER(process_exec, filemon_exec_tag);
473 	EVENTHANDLER_DEREGISTER(process_exit, filemon_exit_tag);
474 	EVENTHANDLER_DEREGISTER(process_fork, filemon_fork_tag);
475 }
476