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