xref: /freebsd/sys/dev/filemon/filemon_wrapper.c (revision e453e498cbb88570a3ff7b3679de65c88707da95)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2011, David E. O'Brien.
5  * Copyright (c) 2009-2011, Juniper Networks, Inc.
6  * Copyright (c) 2015-2016, EMC Corp.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY JUNIPER NETWORKS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL JUNIPER NETWORKS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 #include <sys/eventhandler.h>
33 #include <sys/filedesc.h>
34 #include <sys/imgact.h>
35 #include <sys/priv.h>
36 #include <sys/stdarg.h>
37 #include <sys/sx.h>
38 #include <sys/sysent.h>
39 #include <sys/vnode.h>
40 
41 static void filemon_output_event(struct filemon *filemon, const char *fmt, ...)
42     __printflike(2, 3);
43 
44 static eventhandler_tag filemon_exec_tag;
45 static eventhandler_tag filemon_exit_tag;
46 static eventhandler_tag filemon_fork_tag;
47 
48 static void
filemon_output(struct filemon * filemon,char * msg,size_t len)49 filemon_output(struct filemon *filemon, char *msg, size_t len)
50 {
51 	struct uio auio;
52 	struct iovec aiov;
53 	int error;
54 
55 	if (filemon->fp == NULL)
56 		return;
57 
58 	aiov.iov_base = msg;
59 	aiov.iov_len = len;
60 	auio.uio_iov = &aiov;
61 	auio.uio_iovcnt = 1;
62 	auio.uio_resid = len;
63 	auio.uio_segflg = UIO_SYSSPACE;
64 	auio.uio_rw = UIO_WRITE;
65 	auio.uio_td = curthread;
66 	auio.uio_offset = (off_t) -1;
67 
68 	if (filemon->fp->f_type == DTYPE_VNODE)
69 		bwillwrite();
70 
71 	error = fo_write(filemon->fp, &auio, filemon->cred, 0, curthread);
72 	if (error != 0 && filemon->error == 0)
73 		filemon->error = error;
74 }
75 
76 static void
filemon_output_event(struct filemon * filemon,const char * fmt,...)77 filemon_output_event(struct filemon *filemon, const char *fmt, ...)
78 {
79 	va_list ap;
80 	size_t len;
81 
82 	va_start(ap, fmt);
83 	len = vsnprintf(filemon->msgbufr, sizeof(filemon->msgbufr), fmt, ap);
84 	va_end(ap);
85 	/* The event is truncated but still worth logging. */
86 	if (len >= sizeof(filemon->msgbufr))
87 		len = sizeof(filemon->msgbufr) - 1;
88 	filemon_output(filemon, filemon->msgbufr, len);
89 }
90 
91 static int
filemon_wrapper_chdir(struct thread * td,struct chdir_args * uap)92 filemon_wrapper_chdir(struct thread *td, struct chdir_args *uap)
93 {
94 	int error, ret;
95 	struct filemon *filemon;
96 
97 	if ((ret = sys_chdir(td, uap)) == 0) {
98 		if ((filemon = filemon_proc_get(curproc)) != NULL) {
99 			if ((error = copyinstr(uap->path, filemon->fname1,
100 			    sizeof(filemon->fname1), NULL)) != 0) {
101 				filemon->error = error;
102 				goto copyfail;
103 			}
104 
105 			filemon_output_event(filemon, "C %d %s\n",
106 			    curproc->p_pid, filemon->fname1);
107 copyfail:
108 			filemon_drop(filemon);
109 		}
110 	}
111 
112 	return (ret);
113 }
114 
115 static void
filemon_event_process_exec(void * arg __unused,struct proc * p,struct image_params * imgp)116 filemon_event_process_exec(void *arg __unused, struct proc *p,
117     struct image_params *imgp)
118 {
119 	struct filemon *filemon;
120 
121 	if ((filemon = filemon_proc_get(p)) != NULL) {
122 		filemon_output_event(filemon, "E %d %s\n",
123 		    p->p_pid,
124 		    imgp->execpath != NULL ? imgp->execpath : "<unknown>");
125 
126 		/* If the credentials changed then cease tracing. */
127 		if (imgp->newcred != NULL &&
128 		    imgp->credential_setid &&
129 		    priv_check_cred(filemon->cred, PRIV_DEBUG_DIFFCRED) != 0) {
130 			/*
131 			 * It may have changed to NULL already, but
132 			 * will not be re-attached by anything else.
133 			 */
134 			if (p->p_filemon != NULL) {
135 				KASSERT(p->p_filemon == filemon,
136 				    ("%s: proc %p didn't have expected"
137 				    " filemon %p", __func__, p, filemon));
138 				filemon_proc_drop(p);
139 			}
140 		}
141 
142 
143 		filemon_drop(filemon);
144 	}
145 }
146 
147 static void
_filemon_wrapper_openat(struct thread * td,const char * upath,int flags,int fd)148 _filemon_wrapper_openat(struct thread *td, const char *upath, int flags,
149     int fd)
150 {
151 	int error;
152 	struct file *fp;
153 	struct filemon *filemon;
154 	char *atpath, *freepath;
155 	cap_rights_t rights;
156 
157 	if ((filemon = filemon_proc_get(curproc)) != NULL) {
158 		atpath = "";
159 		freepath = NULL;
160 		fp = NULL;
161 
162 		if ((error = copyinstr(upath, filemon->fname1,
163 		    sizeof(filemon->fname1), NULL)) != 0) {
164 			filemon->error = error;
165 			goto copyfail;
166 		}
167 
168 		if (filemon->fname1[0] != '/' && fd != AT_FDCWD) {
169 			/*
170 			 * rats - we cannot do too much about this.
171 			 * the trace should show a dir we read
172 			 * recently.. output an A record as a clue
173 			 * until we can do better.
174 			 * XXX: This may be able to come out with
175 			 * the namecache lookup now.
176 			 */
177 			filemon_output_event(filemon, "A %d %s\n",
178 			    curproc->p_pid, filemon->fname1);
179 			/*
180 			 * Try to resolve the path from the vnode using the
181 			 * namecache.  It may be inaccurate, but better
182 			 * than nothing.
183 			 */
184 			if (getvnode(td, fd,
185 			    cap_rights_init_one(&rights, CAP_LOOKUP), &fp) == 0) {
186 				vn_fullpath(fp->f_vnode, &atpath, &freepath);
187 			}
188 		}
189 		if (flags & O_RDWR) {
190 			/*
191 			 * We'll get the W record below, but need
192 			 * to also output an R to distinguish from
193 			 * O_WRONLY.
194 			 */
195 			filemon_output_event(filemon, "R %d %s%s%s\n",
196 			    curproc->p_pid, atpath,
197 			    atpath[0] != '\0' ? "/" : "", filemon->fname1);
198 		}
199 
200 		filemon_output_event(filemon, "%c %d %s%s%s\n",
201 		    (flags & O_ACCMODE) ? 'W':'R',
202 		    curproc->p_pid, atpath,
203 		    atpath[0] != '\0' ? "/" : "", filemon->fname1);
204 copyfail:
205 		filemon_drop(filemon);
206 		if (fp != NULL)
207 			fdrop(fp, td);
208 		free(freepath, M_TEMP);
209 	}
210 }
211 
212 static int
filemon_wrapper_open(struct thread * td,struct open_args * uap)213 filemon_wrapper_open(struct thread *td, struct open_args *uap)
214 {
215 	int ret;
216 
217 	if ((ret = sys_open(td, uap)) == 0)
218 		_filemon_wrapper_openat(td, uap->path, uap->flags, AT_FDCWD);
219 
220 	return (ret);
221 }
222 
223 static int
filemon_wrapper_openat(struct thread * td,struct openat_args * uap)224 filemon_wrapper_openat(struct thread *td, struct openat_args *uap)
225 {
226 	int ret;
227 
228 	if ((ret = sys_openat(td, uap)) == 0)
229 		_filemon_wrapper_openat(td, uap->path, uap->flag, uap->fd);
230 
231 	return (ret);
232 }
233 
234 static int
filemon_wrapper_rename(struct thread * td,struct rename_args * uap)235 filemon_wrapper_rename(struct thread *td, struct rename_args *uap)
236 {
237 	int error, ret;
238 	struct filemon *filemon;
239 
240 	if ((ret = sys_rename(td, uap)) == 0) {
241 		if ((filemon = filemon_proc_get(curproc)) != NULL) {
242 			if (((error = copyinstr(uap->from, filemon->fname1,
243 			     sizeof(filemon->fname1), NULL)) != 0) ||
244 			    ((error = copyinstr(uap->to, filemon->fname2,
245 			     sizeof(filemon->fname2), NULL)) != 0)) {
246 				filemon->error = error;
247 				goto copyfail;
248 			}
249 
250 			filemon_output_event(filemon, "M %d '%s' '%s'\n",
251 			    curproc->p_pid, filemon->fname1, filemon->fname2);
252 copyfail:
253 			filemon_drop(filemon);
254 		}
255 	}
256 
257 	return (ret);
258 }
259 
260 static void
_filemon_wrapper_link(struct thread * td,const char * upath1,const char * upath2)261 _filemon_wrapper_link(struct thread *td, const char *upath1,
262     const char *upath2)
263 {
264 	struct filemon *filemon;
265 	int error;
266 
267 	if ((filemon = filemon_proc_get(curproc)) != NULL) {
268 		if (((error = copyinstr(upath1, filemon->fname1,
269 		     sizeof(filemon->fname1), NULL)) != 0) ||
270 		    ((error = copyinstr(upath2, filemon->fname2,
271 		     sizeof(filemon->fname2), NULL)) != 0)) {
272 			filemon->error = error;
273 			goto copyfail;
274 		}
275 
276 		filemon_output_event(filemon, "L %d '%s' '%s'\n",
277 		    curproc->p_pid, filemon->fname1, filemon->fname2);
278 copyfail:
279 		filemon_drop(filemon);
280 	}
281 }
282 
283 static int
filemon_wrapper_link(struct thread * td,struct link_args * uap)284 filemon_wrapper_link(struct thread *td, struct link_args *uap)
285 {
286 	int ret;
287 
288 	if ((ret = sys_link(td, uap)) == 0)
289 		_filemon_wrapper_link(td, uap->path, uap->link);
290 
291 	return (ret);
292 }
293 
294 static int
filemon_wrapper_symlink(struct thread * td,struct symlink_args * uap)295 filemon_wrapper_symlink(struct thread *td, struct symlink_args *uap)
296 {
297 	int ret;
298 
299 	if ((ret = sys_symlink(td, uap)) == 0)
300 		_filemon_wrapper_link(td, uap->path, uap->link);
301 
302 	return (ret);
303 }
304 
305 static int
filemon_wrapper_linkat(struct thread * td,struct linkat_args * uap)306 filemon_wrapper_linkat(struct thread *td, struct linkat_args *uap)
307 {
308 	int ret;
309 
310 	if ((ret = sys_linkat(td, uap)) == 0)
311 		_filemon_wrapper_link(td, uap->path1, uap->path2);
312 
313 	return (ret);
314 }
315 
316 static void
filemon_event_process_exit(void * arg __unused,struct proc * p)317 filemon_event_process_exit(void *arg __unused, struct proc *p)
318 {
319 	struct filemon *filemon;
320 
321 	if ((filemon = filemon_proc_get(p)) != NULL) {
322 		filemon_output_event(filemon, "X %d %d %d\n",
323 		    p->p_pid, p->p_xexit, p->p_xsig);
324 
325 		/*
326 		 * filemon_untrack_processes() may have dropped this p_filemon
327 		 * already while in filemon_proc_get() before acquiring the
328 		 * filemon lock.
329 		 */
330 		KASSERT(p->p_filemon == NULL || p->p_filemon == filemon,
331 		    ("%s: p %p was attached while exiting, expected "
332 		    "filemon %p or NULL", __func__, p, filemon));
333 		if (p->p_filemon == filemon)
334 			filemon_proc_drop(p);
335 
336 		filemon_drop(filemon);
337 	}
338 }
339 
340 static int
filemon_wrapper_unlink(struct thread * td,struct unlink_args * uap)341 filemon_wrapper_unlink(struct thread *td, struct unlink_args *uap)
342 {
343 	int error, ret;
344 	struct filemon *filemon;
345 
346 	if ((ret = sys_unlink(td, uap)) == 0) {
347 		if ((filemon = filemon_proc_get(curproc)) != NULL) {
348 			if ((error = copyinstr(uap->path, filemon->fname1,
349 			    sizeof(filemon->fname1), NULL)) != 0) {
350 				filemon->error = error;
351 				goto copyfail;
352 			}
353 
354 			filemon_output_event(filemon, "D %d %s\n",
355 			    curproc->p_pid, filemon->fname1);
356 copyfail:
357 			filemon_drop(filemon);
358 		}
359 	}
360 
361 	return (ret);
362 }
363 
364 static void
filemon_event_process_fork(void * arg __unused,struct proc * p1,struct proc * p2,int flags __unused)365 filemon_event_process_fork(void *arg __unused, struct proc *p1,
366     struct proc *p2, int flags __unused)
367 {
368 	struct filemon *filemon;
369 
370 	if ((filemon = filemon_proc_get(p1)) != NULL) {
371 		filemon_output_event(filemon, "F %d %d\n",
372 		    p1->p_pid, p2->p_pid);
373 
374 		/*
375 		 * filemon_untrack_processes() or
376 		 * filemon_ioctl(FILEMON_SET_PID) may have changed the parent's
377 		 * p_filemon while in filemon_proc_get() before acquiring the
378 		 * filemon lock.  Only inherit if the parent is still traced by
379 		 * this filemon.
380 		 */
381 		if (p1->p_filemon == filemon) {
382 			PROC_LOCK(p2);
383 			/*
384 			 * It may have been attached to already by a new
385 			 * filemon.
386 			 */
387 			if (p2->p_filemon == NULL) {
388 				p2->p_filemon = filemon_acquire(filemon);
389 				++filemon->proccnt;
390 			}
391 			PROC_UNLOCK(p2);
392 		}
393 
394 		filemon_drop(filemon);
395 	}
396 }
397 
398 static void
filemon_wrapper_install(void)399 filemon_wrapper_install(void)
400 {
401 
402 	sysent[SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir;
403 	sysent[SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open;
404 	sysent[SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat;
405 	sysent[SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename;
406 	sysent[SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink;
407 	sysent[SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link;
408 	sysent[SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink;
409 	sysent[SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat;
410 
411 #if defined(COMPAT_FREEBSD32)
412 	freebsd32_sysent[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *) filemon_wrapper_chdir;
413 	freebsd32_sysent[FREEBSD32_SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open;
414 	freebsd32_sysent[FREEBSD32_SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat;
415 	freebsd32_sysent[FREEBSD32_SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename;
416 	freebsd32_sysent[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink;
417 	freebsd32_sysent[FREEBSD32_SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link;
418 	freebsd32_sysent[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink;
419 	freebsd32_sysent[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *) filemon_wrapper_linkat;
420 #endif	/* COMPAT_FREEBSD32 */
421 
422 	filemon_exec_tag = EVENTHANDLER_REGISTER(process_exec,
423 	    filemon_event_process_exec, NULL, EVENTHANDLER_PRI_LAST);
424 	filemon_exit_tag = EVENTHANDLER_REGISTER(process_exit,
425 	    filemon_event_process_exit, NULL, EVENTHANDLER_PRI_LAST);
426 	filemon_fork_tag = EVENTHANDLER_REGISTER(process_fork,
427 	    filemon_event_process_fork, NULL, EVENTHANDLER_PRI_LAST);
428 }
429 
430 static void
filemon_wrapper_deinstall(void)431 filemon_wrapper_deinstall(void)
432 {
433 
434 	sysent[SYS_chdir].sy_call = (sy_call_t *)sys_chdir;
435 	sysent[SYS_open].sy_call = (sy_call_t *)sys_open;
436 	sysent[SYS_openat].sy_call = (sy_call_t *)sys_openat;
437 	sysent[SYS_rename].sy_call = (sy_call_t *)sys_rename;
438 	sysent[SYS_unlink].sy_call = (sy_call_t *)sys_unlink;
439 	sysent[SYS_link].sy_call = (sy_call_t *)sys_link;
440 	sysent[SYS_symlink].sy_call = (sy_call_t *)sys_symlink;
441 	sysent[SYS_linkat].sy_call = (sy_call_t *)sys_linkat;
442 
443 #if defined(COMPAT_FREEBSD32)
444 	freebsd32_sysent[FREEBSD32_SYS_chdir].sy_call = (sy_call_t *)sys_chdir;
445 	freebsd32_sysent[FREEBSD32_SYS_open].sy_call = (sy_call_t *)sys_open;
446 	freebsd32_sysent[FREEBSD32_SYS_openat].sy_call = (sy_call_t *)sys_openat;
447 	freebsd32_sysent[FREEBSD32_SYS_rename].sy_call = (sy_call_t *)sys_rename;
448 	freebsd32_sysent[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *)sys_unlink;
449 	freebsd32_sysent[FREEBSD32_SYS_link].sy_call = (sy_call_t *)sys_link;
450 	freebsd32_sysent[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *)sys_symlink;
451 	freebsd32_sysent[FREEBSD32_SYS_linkat].sy_call = (sy_call_t *)sys_linkat;
452 #endif	/* COMPAT_FREEBSD32 */
453 
454 	EVENTHANDLER_DEREGISTER(process_exec, filemon_exec_tag);
455 	EVENTHANDLER_DEREGISTER(process_exit, filemon_exit_tag);
456 	EVENTHANDLER_DEREGISTER(process_fork, filemon_fork_tag);
457 }
458