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