NetBSD/sys/dev/filemon/filemon_wrapper.c
pgoyette dd025ffd3b Be a bit more paranoid about hijacking (and restoring) syscall function
pointers.  Ensure that we have the correct "old" values before setting
new values, for both "intall" and "deinstall".

XXX This is NOT intended to encourage additional cases of hijacking!  If
XXX some other hijack feature ever gets committed, this code should be
XXX moved to (most likely) kern/kern_syscall.c.
2015-11-25 07:34:49 +00:00

458 lines
11 KiB
C

/* $NetBSD: filemon_wrapper.c,v 1.11 2015/11/25 07:34:49 pgoyette Exp $ */
/*
* Copyright (c) 2010, Juniper Networks, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: filemon_wrapper.c,v 1.11 2015/11/25 07:34:49 pgoyette Exp $");
#include <sys/param.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/lwp.h>
#include <sys/proc.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>
#include "filemon.h"
static int filemon_wrapper_chdir(struct lwp *, const struct sys_chdir_args *,
register_t *);
static int filemon_wrapper_execve(struct lwp *, struct sys_execve_args *,
register_t *);
static void filemon_wrapper_sys_exit(struct lwp *, struct sys_exit_args *,
register_t *);
static int filemon_wrapper_fork(struct lwp *, const void *, register_t *);
static int filemon_wrapper_link(struct lwp *, struct sys_link_args *,
register_t *);
static int filemon_wrapper_open(struct lwp *, struct sys_open_args *,
register_t *);
static int filemon_wrapper_openat(struct lwp *, struct sys_openat_args *,
register_t *);
static int filemon_wrapper_rename(struct lwp *, struct sys_rename_args *,
register_t *);
static int filemon_wrapper_symlink(struct lwp *, struct sys_symlink_args *,
register_t *);
static int filemon_wrapper_unlink(struct lwp *, struct sys_unlink_args *,
register_t *);
static int filemon_wrapper_vfork(struct lwp *, const void *, register_t *);
const struct hijack filemon_hijack[] = {
{ SYS_chdir,
{ (sy_call_t *)sys_chdir, (sy_call_t *)filemon_wrapper_chdir } },
{ SYS_execve,
{ (sy_call_t *)sys_execve, (sy_call_t *)filemon_wrapper_execve } },
{ SYS_exit,
{ (sy_call_t *)sys_exit, (sy_call_t *)filemon_wrapper_sys_exit } },
{ SYS_fork,
{ (sy_call_t *)sys_fork, (sy_call_t *)filemon_wrapper_fork } },
{ SYS_link,
{ (sy_call_t *)sys_link, (sy_call_t *)filemon_wrapper_link } },
{ SYS_open,
{ (sy_call_t *)sys_open, (sy_call_t *)filemon_wrapper_open } },
{ SYS_openat,
{ (sy_call_t *)sys_openat, (sy_call_t *)filemon_wrapper_openat } },
{ SYS_rename,
{ (sy_call_t *)sys_rename, (sy_call_t *)filemon_wrapper_rename } },
{ SYS_symlink,
{ (sy_call_t *)sys_symlink, (sy_call_t *)filemon_wrapper_symlink } },
{ SYS_unlink,
{ (sy_call_t *)sys_unlink, (sy_call_t *)filemon_wrapper_unlink } },
{ SYS_vfork,
{ (sy_call_t *)sys_vfork, (sy_call_t *)filemon_wrapper_vfork } },
{ -1, {NULL, NULL } }
};
int
syscall_hijack(struct sysent *sv_table, const struct hijack *hj_pkg,
bool restore)
{
int from, to;
const struct hijack *entry;
if (restore) /* Which entry should currently match? */
from = 1;
else
from = 0;
to = 1 - from; /* Which entry will we replace with? */
KASSERT(kernconfig_is_held());
/* First, make sure that all of the old values match */
for (entry = hj_pkg; entry->hj_index >= 0; entry++)
if (sv_table[entry->hj_index].sy_call != entry->hj_funcs[from])
break;
if (entry->hj_index >= 0)
return EBUSY;
/* Now replace the old values with the new ones */
for (entry = hj_pkg; entry->hj_index >= 0; entry++)
sv_table[entry->hj_index].sy_call = entry->hj_funcs[to];
return 0;
}
static int
filemon_wrapper_chdir(struct lwp * l, const struct sys_chdir_args * uap,
register_t * retval)
{
int error;
size_t done;
struct filemon *filemon;
if ((error = sys_chdir(l, uap, retval)) != 0)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return 0;
error = copyinstr(SCARG(uap, path), filemon->fm_fname1,
sizeof(filemon->fm_fname1), &done);
if (error)
goto out;
filemon_printf(filemon, "C %d %s\n",
curproc->p_pid, filemon->fm_fname1);
out:
rw_exit(&filemon->fm_mtx);
return 0;
}
static int
filemon_wrapper_execve(struct lwp * l, struct sys_execve_args * uap,
register_t * retval)
{
char fname[MAXPATHLEN];
int error, cerror;
size_t done;
struct filemon *filemon;
cerror = copyinstr(SCARG(uap, path), fname, sizeof(fname), &done);
if ((error = sys_execve(l, uap, retval)) != EJUSTRETURN)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return EJUSTRETURN;
if (cerror)
goto out;
filemon_printf(filemon, "E %d %s\n", curproc->p_pid, fname);
out:
rw_exit(&filemon->fm_mtx);
return EJUSTRETURN;
}
static int
filemon_wrapper_fork(struct lwp * l, const void *v, register_t * retval)
{
int error;
struct filemon *filemon;
if ((error = sys_fork(l, v, retval)) != 0)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return 0;
filemon_printf(filemon, "F %d %ld\n", curproc->p_pid, (long) retval[0]);
rw_exit(&filemon->fm_mtx);
return 0;
}
static int
filemon_wrapper_vfork(struct lwp * l, const void *v, register_t * retval)
{
int error;
struct filemon *filemon;
if ((error = sys_vfork(l, v, retval)) != 0)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return 0;
filemon_printf(filemon, "F %d %ld\n", curproc->p_pid, (long) retval[0]);
rw_exit(&filemon->fm_mtx);
return 0;
}
static void
filemon_flags(struct filemon * filemon, int f)
{
if (f & O_RDWR) {
/* we want a separate R record */
filemon_printf(filemon, "R %d %s\n", curproc->p_pid,
filemon->fm_fname1);
}
filemon_printf(filemon, "%c %d %s\n", (f & O_ACCMODE) ? 'W' : 'R',
curproc->p_pid, filemon->fm_fname1);
}
static int
filemon_wrapper_open(struct lwp * l, struct sys_open_args * uap,
register_t * retval)
{
int error;
size_t done;
struct filemon *filemon;
if ((error = sys_open(l, uap, retval)) != 0)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return 0;
error = copyinstr(SCARG(uap, path), filemon->fm_fname1,
sizeof(filemon->fm_fname1), &done);
if (error)
goto out;
filemon_flags(filemon, SCARG(uap, flags));
out:
rw_exit(&filemon->fm_mtx);
return 0;
}
static int
filemon_wrapper_openat(struct lwp * l, struct sys_openat_args * uap,
register_t * retval)
{
int error;
size_t done;
struct filemon *filemon;
if ((error = sys_openat(l, uap, retval)) != 0)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return 0;
error = copyinstr(SCARG(uap, path), filemon->fm_fname1,
sizeof(filemon->fm_fname1), &done);
if (error)
goto out;
if (filemon->fm_fname1[0] != '/' && SCARG(uap, fd) != AT_FDCWD) {
/*
* Rats we cannot just treat like open.
* Output an 'A' record as a clue.
*/
filemon_printf(filemon, "A %d %s\n", curproc->p_pid,
filemon->fm_fname1);
}
filemon_flags(filemon, SCARG(uap, oflags));
out:
rw_exit(&filemon->fm_mtx);
return 0;
}
static int
filemon_wrapper_rename(struct lwp * l, struct sys_rename_args * uap,
register_t * retval)
{
int error;
size_t done;
struct filemon *filemon;
if ((error = sys_rename(l, uap, retval)) != 0)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return 0;
error = copyinstr(SCARG(uap, from), filemon->fm_fname1,
sizeof(filemon->fm_fname1), &done);
if (error)
goto out;
error = copyinstr(SCARG(uap, to), filemon->fm_fname2,
sizeof(filemon->fm_fname2), &done);
if (error)
goto out;
filemon_printf(filemon, "M %d '%s' '%s'\n", curproc->p_pid,
filemon->fm_fname1, filemon->fm_fname2);
out:
rw_exit(&filemon->fm_mtx);
return 0;
}
static int
filemon_wrapper_link(struct lwp * l, struct sys_link_args * uap,
register_t * retval)
{
int error;
size_t done;
struct filemon *filemon;
if ((error = sys_link(l, uap, retval)) != 0)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return 0;
error = copyinstr(SCARG(uap, path), filemon->fm_fname1,
sizeof(filemon->fm_fname1), &done);
if (error)
goto out;
error = copyinstr(SCARG(uap, link), filemon->fm_fname2,
sizeof(filemon->fm_fname2), &done);
if (error)
goto out;
filemon_printf(filemon, "L %d '%s' '%s'\n", curproc->p_pid,
filemon->fm_fname1, filemon->fm_fname2);
out:
rw_exit(&filemon->fm_mtx);
return 0;
}
static int
filemon_wrapper_symlink(struct lwp * l, struct sys_symlink_args * uap,
register_t * retval)
{
int error;
size_t done;
struct filemon *filemon;
if ((error = sys_symlink(l, uap, retval)) != 0)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return 0;
error = copyinstr(SCARG(uap, path), filemon->fm_fname1,
sizeof(filemon->fm_fname1), &done);
if (error)
goto out;
error = copyinstr(SCARG(uap, link), filemon->fm_fname2,
sizeof(filemon->fm_fname2), &done);
if (error)
goto out;
filemon_printf(filemon, "L %d '%s' '%s'\n", curproc->p_pid,
filemon->fm_fname1, filemon->fm_fname2);
out:
rw_exit(&filemon->fm_mtx);
return 0;
}
static void
filemon_wrapper_sys_exit(struct lwp * l, struct sys_exit_args * uap,
register_t * retval)
{
struct filemon *filemon;
filemon = filemon_lookup(curproc);
if (filemon) {
filemon_printf(filemon, "X %d %d\n",
curproc->p_pid, SCARG(uap, rval));
/* Check if the monitored process is about to exit. */
if (filemon->fm_pid == curproc->p_pid)
filemon_printf(filemon, "# Bye bye\n");
rw_exit(&filemon->fm_mtx);
}
sys_exit(l, uap, retval);
/* NOT REACHED */
}
static int
filemon_wrapper_unlink(struct lwp * l, struct sys_unlink_args * uap,
register_t * retval)
{
int error;
size_t done;
struct filemon *filemon;
if ((error = sys_unlink(l, uap, retval)) != 0)
return error;
filemon = filemon_lookup(curproc);
if (filemon == NULL)
return 0;
error = copyinstr(SCARG(uap, path), filemon->fm_fname1,
sizeof(filemon->fm_fname1), &done);
if (error)
goto out;
filemon_printf(filemon, "D %d %s\n",
curproc->p_pid, filemon->fm_fname1);
out:
rw_exit(&filemon->fm_mtx);
return 0;
}
int
filemon_wrapper_install(void)
{
int error;
struct sysent *sv_table = emul_netbsd.e_sysent;
kernconfig_lock();
error = syscall_hijack(sv_table, filemon_hijack, false);
kernconfig_unlock();
return error;
}
int
filemon_wrapper_deinstall(void)
{
int error;
struct sysent *sv_table = emul_netbsd.e_sysent;
kernconfig_lock();
error = syscall_hijack(sv_table, filemon_hijack, true);
kernconfig_unlock();
return error;
}