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