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