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/param.h>
33 #include <sys/file.h>
34 #include <sys/systm.h>
35 #include <sys/buf.h>
36 #include <sys/capsicum.h>
37 #include <sys/condvar.h>
38 #include <sys/conf.h>
39 #include <sys/fcntl.h>
40 #include <sys/ioccom.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/malloc.h>
44 #include <sys/module.h>
45 #include <sys/poll.h>
46 #include <sys/proc.h>
47 #include <sys/sx.h>
48 #include <sys/syscall.h>
49 #include <sys/sysent.h>
50 #include <sys/sysproto.h>
51 #include <sys/uio.h>
52
53 #include "filemon.h"
54
55 #if defined(COMPAT_FREEBSD32)
56 #include <compat/freebsd32/freebsd32_syscall.h>
57 #include <compat/freebsd32/freebsd32_proto.h>
58 #include <compat/freebsd32/freebsd32_util.h>
59 #endif
60
61 static d_close_t filemon_close;
62 static d_ioctl_t filemon_ioctl;
63 static d_open_t filemon_open;
64
65 static struct cdevsw filemon_cdevsw = {
66 .d_version = D_VERSION,
67 .d_close = filemon_close,
68 .d_ioctl = filemon_ioctl,
69 .d_open = filemon_open,
70 .d_name = "filemon",
71 };
72
73 MALLOC_DECLARE(M_FILEMON);
74 MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor");
75
76 /*
77 * The filemon->lock protects several things currently:
78 * - fname1/fname2/msgbufr are pre-allocated and used per syscall
79 * for logging and copyins rather than stack variables.
80 * - Serializing the filemon's log output.
81 * - Preventing inheritance or removal of the filemon into proc.p_filemon.
82 */
83 struct filemon {
84 struct sx lock; /* Lock for this filemon. */
85 struct file *fp; /* Output file pointer. */
86 struct ucred *cred; /* Credential of tracer. */
87 char fname1[MAXPATHLEN]; /* Temporary filename buffer. */
88 char fname2[MAXPATHLEN]; /* Temporary filename buffer. */
89 char msgbufr[2*MAXPATHLEN + 100]; /* Output message buffer. */
90 int error; /* Log write error, returned on close(2). */
91 u_int refcnt; /* Pointer reference count. */
92 u_int proccnt; /* Process count. */
93 };
94
95 static struct cdev *filemon_dev;
96 static void filemon_output(struct filemon *filemon, char *msg, size_t len);
97
98 static __inline struct filemon *
filemon_acquire(struct filemon * filemon)99 filemon_acquire(struct filemon *filemon)
100 {
101
102 if (filemon != NULL)
103 refcount_acquire(&filemon->refcnt);
104 return (filemon);
105 }
106
107 /*
108 * Release a reference and free on the last one.
109 */
110 static void
filemon_release(struct filemon * filemon)111 filemon_release(struct filemon *filemon)
112 {
113
114 if (refcount_release(&filemon->refcnt) == 0)
115 return;
116 /*
117 * There are valid cases of releasing while locked, such as in
118 * filemon_untrack_processes, but none which are done where there
119 * is not at least 1 reference remaining.
120 */
121 sx_assert(&filemon->lock, SA_UNLOCKED);
122
123 if (filemon->cred != NULL)
124 crfree(filemon->cred);
125 sx_destroy(&filemon->lock);
126 free(filemon, M_FILEMON);
127 }
128
129 /*
130 * Acquire the proc's p_filemon reference and lock the filemon.
131 * The proc's p_filemon may not match this filemon on return.
132 */
133 static struct filemon *
filemon_proc_get(struct proc * p)134 filemon_proc_get(struct proc *p)
135 {
136 struct filemon *filemon;
137
138 if (p->p_filemon == NULL)
139 return (NULL);
140 PROC_LOCK(p);
141 filemon = filemon_acquire(p->p_filemon);
142 PROC_UNLOCK(p);
143
144 if (filemon == NULL)
145 return (NULL);
146 /*
147 * The p->p_filemon may have changed by now. That case is handled
148 * by the exit and fork hooks and filemon_attach_proc specially.
149 */
150 sx_xlock(&filemon->lock);
151 return (filemon);
152 }
153
154 /* Remove and release the filemon on the given process. */
155 static void
filemon_proc_drop(struct proc * p)156 filemon_proc_drop(struct proc *p)
157 {
158 struct filemon *filemon;
159
160 KASSERT(p->p_filemon != NULL, ("%s: proc %p NULL p_filemon",
161 __func__, p));
162 sx_assert(&p->p_filemon->lock, SA_XLOCKED);
163 PROC_LOCK(p);
164 filemon = p->p_filemon;
165 p->p_filemon = NULL;
166 --filemon->proccnt;
167 PROC_UNLOCK(p);
168 /*
169 * This should not be the last reference yet. filemon_release()
170 * cannot be called with filemon locked, which the caller expects
171 * will stay locked.
172 */
173 KASSERT(filemon->refcnt > 1, ("%s: proc %p dropping filemon %p "
174 "with last reference", __func__, p, filemon));
175 filemon_release(filemon);
176 }
177
178 /* Unlock and release the filemon. */
179 static __inline void
filemon_drop(struct filemon * filemon)180 filemon_drop(struct filemon *filemon)
181 {
182
183 sx_xunlock(&filemon->lock);
184 filemon_release(filemon);
185 }
186
187 #include "filemon_wrapper.c"
188
189 static void
filemon_write_header(struct filemon * filemon)190 filemon_write_header(struct filemon *filemon)
191 {
192 int len;
193 struct timeval now;
194
195 getmicrotime(&now);
196
197 len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr),
198 "# filemon version %d\n# Target pid %d\n# Start %ju.%06ju\nV %d\n",
199 FILEMON_VERSION, curproc->p_pid, (uintmax_t)now.tv_sec,
200 (uintmax_t)now.tv_usec, FILEMON_VERSION);
201 if (len < sizeof(filemon->msgbufr))
202 filemon_output(filemon, filemon->msgbufr, len);
203 }
204
205 /*
206 * Invalidate the passed filemon in all processes.
207 */
208 static void
filemon_untrack_processes(struct filemon * filemon)209 filemon_untrack_processes(struct filemon *filemon)
210 {
211 struct proc *p;
212
213 sx_assert(&filemon->lock, SA_XLOCKED);
214
215 /* Avoid allproc loop if there is no need. */
216 if (filemon->proccnt == 0)
217 return;
218
219 /*
220 * Processes in this list won't go away while here since
221 * filemon_event_process_exit() will lock on filemon->lock
222 * which we hold.
223 */
224 sx_slock(&allproc_lock);
225 FOREACH_PROC_IN_SYSTEM(p) {
226 /*
227 * No PROC_LOCK is needed to compare here since it is
228 * guaranteed to not change since we have its filemon
229 * locked. Everything that changes this p_filemon will
230 * be locked on it.
231 */
232 if (p->p_filemon == filemon)
233 filemon_proc_drop(p);
234 }
235 sx_sunlock(&allproc_lock);
236
237 /*
238 * It's possible some references were acquired but will be
239 * dropped shortly as they are restricted from being
240 * inherited. There is at least the reference in cdevpriv remaining.
241 */
242 KASSERT(filemon->refcnt > 0, ("%s: filemon %p should have "
243 "references still.", __func__, filemon));
244 KASSERT(filemon->proccnt == 0, ("%s: filemon %p should not have "
245 "attached procs still.", __func__, filemon));
246 }
247
248 /*
249 * Close out the log.
250 */
251 static void
filemon_close_log(struct filemon * filemon)252 filemon_close_log(struct filemon *filemon)
253 {
254 struct file *fp;
255 struct timeval now;
256 size_t len;
257
258 sx_assert(&filemon->lock, SA_XLOCKED);
259 if (filemon->fp == NULL)
260 return;
261
262 getmicrotime(&now);
263
264 len = snprintf(filemon->msgbufr,
265 sizeof(filemon->msgbufr),
266 "# Stop %ju.%06ju\n# Bye bye\n",
267 (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec);
268
269 if (len < sizeof(filemon->msgbufr))
270 filemon_output(filemon, filemon->msgbufr, len);
271 fp = filemon->fp;
272 filemon->fp = NULL;
273
274 sx_xunlock(&filemon->lock);
275 fdrop(fp, curthread);
276 sx_xlock(&filemon->lock);
277 }
278
279 /*
280 * The devfs file is being closed. Untrace all processes. It is possible
281 * filemon_close/close(2) was not called.
282 */
283 static void
filemon_dtr(void * data)284 filemon_dtr(void *data)
285 {
286 struct filemon *filemon = data;
287
288 if (filemon == NULL)
289 return;
290
291 sx_xlock(&filemon->lock);
292 /*
293 * Detach the filemon. It cannot be inherited after this.
294 */
295 filemon_untrack_processes(filemon);
296 filemon_close_log(filemon);
297 filemon_drop(filemon);
298 }
299
300 /* Attach the filemon to the process. */
301 static int
filemon_attach_proc(struct filemon * filemon,struct proc * p)302 filemon_attach_proc(struct filemon *filemon, struct proc *p)
303 {
304 struct filemon *filemon2;
305
306 sx_assert(&filemon->lock, SA_XLOCKED);
307 PROC_LOCK_ASSERT(p, MA_OWNED);
308 KASSERT((p->p_flag & P_WEXIT) == 0,
309 ("%s: filemon %p attaching to exiting process %p",
310 __func__, filemon, p));
311 KASSERT((p->p_flag & P_INEXEC) == 0,
312 ("%s: filemon %p attaching to execing process %p",
313 __func__, filemon, p));
314
315 if (p->p_filemon == filemon)
316 return (0);
317 /*
318 * Don't allow truncating other process traces. It is
319 * not really intended to trace procs other than curproc
320 * anyhow.
321 */
322 if (p->p_filemon != NULL && p != curproc)
323 return (EBUSY);
324 /*
325 * Historic behavior of filemon has been to let a child initiate
326 * tracing on itself and cease existing tracing. Bmake
327 * .META + .MAKE relies on this. It is only relevant for attaching to
328 * curproc.
329 */
330 while (p->p_filemon != NULL) {
331 PROC_UNLOCK(p);
332 sx_xunlock(&filemon->lock);
333 while ((filemon2 = filemon_proc_get(p)) != NULL) {
334 /* It may have changed. */
335 if (p->p_filemon == filemon2)
336 filemon_proc_drop(p);
337 filemon_drop(filemon2);
338 }
339 sx_xlock(&filemon->lock);
340 PROC_LOCK(p);
341 /*
342 * It may have been attached to, though unlikely.
343 * Try again if needed.
344 */
345 }
346
347 KASSERT(p->p_filemon == NULL,
348 ("%s: proc %p didn't detach filemon %p", __func__, p,
349 p->p_filemon));
350 p->p_filemon = filemon_acquire(filemon);
351 ++filemon->proccnt;
352
353 return (0);
354 }
355
356 static int
filemon_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int flag __unused,struct thread * td)357 filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused,
358 struct thread *td)
359 {
360 struct filemon *filemon;
361 struct file *fp;
362 struct proc *p;
363 int error;
364
365 if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0)
366 return (error);
367
368 sx_xlock(&filemon->lock);
369
370 switch (cmd) {
371 /* Set the output file descriptor. */
372 case FILEMON_SET_FD:
373 if (filemon->fp != NULL) {
374 error = EEXIST;
375 break;
376 }
377
378 error = fget_write(td, *(int *)data, &cap_pwrite_rights, &fp);
379 if (error == 0) {
380 /*
381 * The filemon handle may be passed to another process,
382 * so the underlying file handle must support this.
383 */
384 if ((fp->f_ops->fo_flags & DFLAG_PASSABLE) == 0) {
385 fdrop(fp, curthread);
386 error = EINVAL;
387 break;
388 }
389 filemon->fp = fp;
390 /* Write the file header. */
391 filemon_write_header(filemon);
392 }
393 break;
394
395 /* Set the monitored process ID. */
396 case FILEMON_SET_PID:
397 /* Invalidate any existing processes already set. */
398 filemon_untrack_processes(filemon);
399
400 error = pget(*((pid_t *)data),
401 PGET_CANDEBUG | PGET_NOTWEXIT | PGET_NOTINEXEC, &p);
402 if (error == 0) {
403 KASSERT(p->p_filemon != filemon,
404 ("%s: proc %p didn't untrack filemon %p",
405 __func__, p, filemon));
406 error = filemon_attach_proc(filemon, p);
407 PROC_UNLOCK(p);
408 }
409 break;
410
411 default:
412 error = EINVAL;
413 break;
414 }
415
416 sx_xunlock(&filemon->lock);
417 return (error);
418 }
419
420 static int
filemon_open(struct cdev * dev,int oflags __unused,int devtype __unused,struct thread * td)421 filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused,
422 struct thread *td)
423 {
424 int error;
425 struct filemon *filemon;
426
427 filemon = malloc(sizeof(*filemon), M_FILEMON,
428 M_WAITOK | M_ZERO);
429 sx_init(&filemon->lock, "filemon");
430 refcount_init(&filemon->refcnt, 1);
431 filemon->cred = crhold(td->td_ucred);
432
433 error = devfs_set_cdevpriv(filemon, filemon_dtr);
434 if (error != 0)
435 filemon_release(filemon);
436
437 return (error);
438 }
439
440 /* Called on close of last devfs file handle, before filemon_dtr(). */
441 static int
filemon_close(struct cdev * dev __unused,int flag __unused,int fmt __unused,struct thread * td __unused)442 filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused,
443 struct thread *td __unused)
444 {
445 struct filemon *filemon;
446 int error;
447
448 if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0)
449 return (error);
450
451 sx_xlock(&filemon->lock);
452 filemon_close_log(filemon);
453 error = filemon->error;
454 sx_xunlock(&filemon->lock);
455 /*
456 * Processes are still being traced but won't log anything
457 * now. After this call returns filemon_dtr() is called which
458 * will detach processes.
459 */
460
461 return (error);
462 }
463
464 static void
filemon_load(void * dummy __unused)465 filemon_load(void *dummy __unused)
466 {
467
468 /* Install the syscall wrappers. */
469 filemon_wrapper_install();
470
471 filemon_dev = make_dev(&filemon_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666,
472 "filemon");
473 }
474
475 static int
filemon_unload(void)476 filemon_unload(void)
477 {
478
479 destroy_dev(filemon_dev);
480 filemon_wrapper_deinstall();
481
482 return (0);
483 }
484
485 static int
filemon_modevent(module_t mod __unused,int type,void * data)486 filemon_modevent(module_t mod __unused, int type, void *data)
487 {
488 int error = 0;
489
490 switch (type) {
491 case MOD_LOAD:
492 filemon_load(data);
493 break;
494
495 case MOD_UNLOAD:
496 error = filemon_unload();
497 break;
498
499 case MOD_QUIESCE:
500 /*
501 * The wrapper implementation is unsafe for reliable unload.
502 * Require forcing an unload.
503 */
504 error = EBUSY;
505 break;
506
507 case MOD_SHUTDOWN:
508 break;
509
510 default:
511 error = EOPNOTSUPP;
512 break;
513
514 }
515
516 return (error);
517 }
518
519 DEV_MODULE(filemon, filemon_modevent, NULL);
520 MODULE_VERSION(filemon, 1);
521