123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902 |
- /* $OpenBSD: kern_tame.c,v 1.18 2015/07/29 17:55:27 deraadt Exp $ */
- /*
- * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
- * Copyright (c) 2015 Theo de Raadt <deraadt@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <sys/param.h>
- #include <sys/types.h>
- #include <sys/mount.h>
- #include <sys/proc.h>
- #include <sys/fcntl.h>
- #include <sys/file.h>
- #include <sys/filedesc.h>
- #include <sys/vnode.h>
- #include <sys/mbuf.h>
- #include <sys/sysctl.h>
- #include <sys/ktrace.h>
- #include <sys/ioctl.h>
- #include <sys/termios.h>
- #include <sys/mtio.h>
- #include <net/bpf.h>
- #include <net/route.h>
- #include <net/if.h>
- #include <netinet/in.h>
- #include <netinet/tcp.h>
- #include <sys/tame.h>
- #include <sys/signal.h>
- #include <sys/signalvar.h>
- #include <sys/syscall.h>
- #include <sys/syscallargs.h>
- #include <sys/systm.h>
- int canonpath(const char *input, char *buf, size_t bufsize);
- const u_int tame_syscalls[SYS_MAXSYSCALL] = {
- [SYS_exit] = 0xffffffff,
- [SYS_kbind] = 0xffffffff,
- [SYS_getuid] = _TM_SELF,
- [SYS_geteuid] = _TM_SELF,
- [SYS_getresuid] = _TM_SELF,
- [SYS_getgid] = _TM_SELF,
- [SYS_getegid] = _TM_SELF,
- [SYS_getresgid] = _TM_SELF,
- [SYS_getgroups] = _TM_SELF,
- [SYS_getlogin] = _TM_SELF,
- [SYS_getpgrp] = _TM_SELF,
- [SYS_getpgid] = _TM_SELF,
- [SYS_getppid] = _TM_SELF,
- [SYS_getsid] = _TM_SELF,
- [SYS_getthrid] = _TM_SELF,
- [SYS_getrlimit] = _TM_SELF,
- [SYS_gettimeofday] = _TM_SELF,
- [SYS_getdtablecount] = _TM_SELF,
- [SYS_issetugid] = _TM_SELF,
- [SYS_clock_getres] = _TM_SELF,
- [SYS_clock_gettime] = _TM_SELF,
- [SYS_getpid] = _TM_SELF,
- [SYS_umask] = _TM_SELF,
- [SYS___sysctl] = _TM_SELF, /* read-only; narrow subset */
- [SYS_adjtime] = _TM_SELF, /* read-only */
- [SYS_chdir] = _TM_RPATH,
- [SYS_fchdir] = _TM_SELF, /* careful of directory fd inside jails */
- /* needed by threaded programs */
- [SYS_sched_yield] = _TM_SELF,
- [SYS___thrsleep] = _TM_SELF,
- [SYS___thrwakeup] = _TM_SELF,
- [SYS___threxit] = _TM_SELF,
- [SYS___thrsigdivert] = _TM_SELF,
- [SYS_sendsyslog] = _TM_SELF,
- [SYS_nanosleep] = _TM_SELF,
- [SYS_sigprocmask] = _TM_SELF,
- [SYS_sigaction] = _TM_SELF,
- [SYS_sigreturn] = _TM_SELF,
- [SYS_getitimer] = _TM_SELF,
- [SYS_setitimer] = _TM_SELF,
- [SYS_tame] = _TM_SELF,
- [SYS_wait4] = _TM_SELF,
- [SYS_poll] = _TM_RW,
- [SYS_kevent] = _TM_RW,
- [SYS_kqueue] = _TM_RW,
- [SYS_select] = _TM_RW,
- [SYS_close] = _TM_RW,
- [SYS_dup] = _TM_RW,
- [SYS_dup2] = _TM_RW,
- [SYS_dup3] = _TM_RW,
- [SYS_closefrom] = _TM_RW,
- [SYS_shutdown] = _TM_RW,
- [SYS_read] = _TM_RW,
- [SYS_readv] = _TM_RW,
- [SYS_pread] = _TM_RW,
- [SYS_preadv] = _TM_RW,
- [SYS_write] = _TM_RW,
- [SYS_writev] = _TM_RW,
- [SYS_pwrite] = _TM_RW,
- [SYS_pwritev] = _TM_RW,
- [SYS_ftruncate] = _TM_RW,
- [SYS_lseek] = _TM_RW,
- [SYS_utimes] = _TM_RW,
- [SYS_futimes] = _TM_RW,
- [SYS_utimensat] = _TM_RW,
- [SYS_futimens] = _TM_RW,
- [SYS_fcntl] = _TM_RW,
- [SYS_fsync] = _TM_RW,
- [SYS_pipe] = _TM_RW,
- [SYS_pipe2] = _TM_RW,
- [SYS_socketpair] = _TM_RW,
- [SYS_getdents] = _TM_RW,
- [SYS_sendto] = _TM_RW | _TM_DNS_ACTIVE | _TM_YP_ACTIVE,
- [SYS_sendmsg] = _TM_RW,
- [SYS_recvmsg] = _TM_RW,
- [SYS_recvfrom] = _TM_RW | _TM_DNS_ACTIVE | _TM_YP_ACTIVE,
- [SYS_fork] = _TM_PROC,
- [SYS_vfork] = _TM_PROC,
- [SYS_kill] = _TM_PROC,
- [SYS_setgroups] = _TM_PROC,
- [SYS_setresgid] = _TM_PROC,
- [SYS_setresuid] = _TM_PROC,
- [SYS_ioctl] = _TM_IOCTL, /* very limited subset */
- [SYS_getentropy] = _TM_MALLOC,
- [SYS_madvise] = _TM_MALLOC,
- [SYS_minherit] = _TM_MALLOC,
- [SYS_mmap] = _TM_MALLOC,
- [SYS_mprotect] = _TM_MALLOC,
- [SYS_mquery] = _TM_MALLOC,
- [SYS_munmap] = _TM_MALLOC,
- [SYS___getcwd] = _TM_RPATH | _TM_WPATH,
- [SYS_open] = _TM_SELF,
- [SYS_openat] = _TM_RPATH | _TM_WPATH,
- [SYS_stat] = _TM_SELF,
- [SYS_fstatat] = _TM_RPATH | _TM_WPATH,
- [SYS_access] = _TM_SELF,
- [SYS_faccessat] = _TM_RPATH | _TM_WPATH,
- [SYS_readlink] = _TM_SELF,
- [SYS_readlinkat] = _TM_RPATH | _TM_WPATH,
- [SYS_lstat] = _TM_RPATH | _TM_WPATH | _TM_TMPPATH | _TM_DNSPATH,
- [SYS_chmod] = _TM_RPATH | _TM_WPATH | _TM_TMPPATH,
- [SYS_fchmod] = _TM_RPATH | _TM_WPATH,
- [SYS_fchmodat] = _TM_RPATH | _TM_WPATH,
- [SYS_chflags] = _TM_RPATH | _TM_WPATH | _TM_TMPPATH,
- [SYS_chflagsat] = _TM_RPATH | _TM_WPATH,
- [SYS_chown] = _TM_RPATH | _TM_WPATH | _TM_TMPPATH,
- [SYS_fchown] = _TM_RPATH | _TM_WPATH,
- [SYS_fchownat] = _TM_RPATH | _TM_WPATH,
- [SYS_rename] = _TM_CPATH,
- [SYS_rmdir] = _TM_CPATH,
- [SYS_renameat] = _TM_CPATH,
- [SYS_link] = _TM_CPATH,
- [SYS_linkat] = _TM_CPATH,
- [SYS_symlink] = _TM_CPATH,
- [SYS_unlink] = _TM_CPATH | _TM_TMPPATH,
- [SYS_unlinkat] = _TM_CPATH,
- [SYS_mkdir] = _TM_CPATH,
- [SYS_mkdirat] = _TM_CPATH,
- [SYS_fstat] = _TM_RW | _TM_RPATH | _TM_WPATH | _TM_TMPPATH, /* rare */
- [SYS_socket] = _TM_INET | _TM_UNIX | _TM_DNS_ACTIVE | _TM_YP_ACTIVE,
- [SYS_listen] = _TM_INET | _TM_UNIX,
- [SYS_bind] = _TM_INET | _TM_UNIX,
- [SYS_connect] = _TM_INET | _TM_UNIX | _TM_DNS_ACTIVE | _TM_YP_ACTIVE,
- [SYS_accept4] = _TM_INET | _TM_UNIX,
- [SYS_accept] = _TM_INET | _TM_UNIX,
- [SYS_getpeername] = _TM_INET | _TM_UNIX,
- [SYS_getsockname] = _TM_INET | _TM_UNIX,
- [SYS_setsockopt] = _TM_INET | _TM_UNIX, /* small subset */
- [SYS_getsockopt] = _TM_INET | _TM_UNIX,
- [SYS_flock] = _TM_GETPW,
- };
- int
- sys_tame(struct proc *p, void *v, register_t *retval)
- {
- #if 1
- return (ENOSYS);
- #else
- struct sys_tame_args /* {
- syscallarg(int) flags;
- } */ *uap = v;
- int flags = SCARG(uap, flags);
- flags &= _TM_USERSET;
- if ((p->p_p->ps_flags & PS_TAMED) == 0) {
- p->p_p->ps_flags |= PS_TAMED;
- p->p_p->ps_tame = flags;
- return (0);
- }
- /* May not set new bits */
- if (((flags | p->p_p->ps_tame) & _TM_USERSET) !=
- (p->p_p->ps_tame & _TM_USERSET))
- return (EPERM);
- /* More tame bits being cleared. Force re-learning of _ACTIVE things */
- p->p_p->ps_tame &= flags;
- p->p_p->ps_tame &= _TM_USERSET;
- return (0);
- #endif
- }
- int
- tame_check(struct proc *p, int code)
- {
- p->p_tamenote = p->p_tameafter = 0; /* XX optimise? */
- p->p_tame_syscall = code;
- if (code < 0 || code > SYS_MAXSYSCALL - 1)
- return (0);
- if (p->p_p->ps_tame == 0)
- return (code == SYS_exit || code == SYS_kbind);
- return (p->p_p->ps_tame & tame_syscalls[code]);
- }
- int
- tame_fail(struct proc *p, int error, int code)
- {
- printf("tame: pid %d %s syscall %d\n", p->p_pid, p->p_comm,
- p->p_tame_syscall);
- if (p->p_p->ps_tame & _TM_ABORT) { /* Core dump requested */
- struct sigaction sa;
- memset(&sa, 0, sizeof sa);
- sa.sa_handler = SIG_DFL;
- setsigvec(p, SIGABRT, &sa);
- psignal(p, SIGABRT);
- } else
- psignal(p, SIGKILL);
- p->p_p->ps_tame = 0; /* Disable all TAME_ flags */
- return (error);
- }
- /*
- * Need to make it more obvious that one cannot get through here
- * without the right flags set
- */
- int
- tame_namei(struct proc *p, char *origpath)
- {
- char path[PATH_MAX];
- if (p->p_tamenote == TMN_COREDUMP)
- return (0); /* Allow a coredump */
- if (canonpath(origpath, path, sizeof(path)) != 0)
- return (tame_fail(p, EPERM, TAME_RPATH));
- /* Detect what looks like a mkstemp(3) family operation */
- if ((p->p_p->ps_tame & _TM_TMPPATH) &&
- (p->p_tame_syscall == SYS_open) &&
- (p->p_tamenote & (TMN_CREAT | TMN_IMODIFY)) == TMN_CREAT &&
- strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) {
- return (0);
- }
- /* Allow unlinking of a mkstemp(3) file...
- * Good opportunity for strict checks here.
- */
- if ((p->p_p->ps_tame & _TM_TMPPATH) &&
- (p->p_tame_syscall == SYS_unlink) &&
- strncmp(path, "/tmp/", sizeof("/tmp") - 1) == 0) {
- return (0);
- }
- /* open, mkdir, or other path creation operation */
- if ((p->p_tamenote & (TMN_CREAT | TMN_IMODIFY)) == TMN_CREAT &&
- ((p->p_p->ps_tame & _TM_CPATH) == 0))
- return (tame_fail(p, EPERM, TAME_CPATH));
- /* inode change operation, issued against a path */
- if ((p->p_tamenote & (TMN_CREAT | TMN_IMODIFY)) == TMN_IMODIFY &&
- ((p->p_p->ps_tame & _TM_CPATH) == 0)) {
- // XXX should _TM_CPATH be a seperate check?
- return (tame_fail(p, EPERM, TAME_CPATH));
- }
- if ((p->p_tamenote & TMN_WRITE) &&
- (p->p_p->ps_tame & _TM_WPATH) == 0)
- return (tame_fail(p, EPERM, TAME_WPATH));
- if (p->p_p->ps_tame & _TM_RPATH)
- return (0);
- if (p->p_p->ps_tame & _TM_WPATH)
- return (0);
- /* All remaining cases are RPATH */
- switch (p->p_tame_syscall) {
- case SYS_access:
- /* tzset() needs this. */
- if (strcmp(path, "/etc/localtime") == 0)
- return (0);
- break;
- case SYS_open:
- /* getpw* and friends need a few files */
- if (p->p_p->ps_tame & _TM_GETPW) {
- if (strcmp(path, "/etc/spwd.db") == 0)
- return (0);
- if (strcmp(path, "/etc/pwd.db") == 0)
- return (0);
- if (strcmp(path, "/etc/group") == 0)
- return (0);
- }
- /* DNS needs /etc/{resolv.conf,hosts,services}. */
- if (p->p_p->ps_tame & _TM_DNSPATH) {
- if (strcmp(path, "/etc/resolv.conf") == 0) {
- p->p_tamenote |= TMN_DNSRESOLV;
- p->p_tameafter = 1;
- return (0);
- }
- if (strcmp(path, "/etc/hosts") == 0)
- return (0);
- if (strcmp(path, "/etc/services") == 0)
- return (0);
- }
- if (p->p_p->ps_tame & _TM_GETPW) {
- if (strcmp(path, "/var/run/ypbind.lock") == 0) {
- p->p_tamenote |= TMN_YPLOCK;
- p->p_tameafter = 1;
- return (0);
- }
- if (strncmp(path, "/var/yp/binding/",
- sizeof("/var/yp/binding/") - 1) == 0)
- return (0);
- }
- /* tzset() needs these. */
- if (strncmp(path, "/usr/share/zoneinfo/",
- sizeof("/usr/share/zoneinfo/") - 1) == 0)
- return (0);
- if (strcmp(path, "/etc/localtime") == 0)
- return (0);
- /* /usr/share/nls/../libc.cat returns EPERM, for strerror(3). */
- if (strncmp(path, "/usr/share/nls/",
- sizeof("/usr/share/nls/") - 1) == 0 &&
- strcmp(path + strlen(path) - 9, "/libc.cat") == 0)
- return (EPERM);
- break;
- case SYS_readlink:
- /* Allow /etc/malloc.conf for malloc(3). */
- if (strcmp(path, "/etc/malloc.conf") == 0)
- return (0);
- break;
- case SYS_stat:
- /* DNS needs /etc/resolv.conf. */
- if (p->p_p->ps_tame & _TM_DNSPATH) {
- if (strcmp(path, "/etc/resolv.conf") == 0) {
- p->p_tamenote |= TMN_DNSRESOLV;
- p->p_tameafter = 1;
- return (0);
- }
- }
- break;
- }
- return (tame_fail(p, EPERM, TAME_RPATH));
- }
- void
- tame_aftersyscall(struct proc *p, int code, int error)
- {
- if ((p->p_tamenote & TMN_YPLOCK) && error == 0)
- atomic_setbits_int(&p->p_p->ps_tame, _TM_YP_ACTIVE | TAME_INET);
- if ((p->p_tamenote & TMN_DNSRESOLV) && error == 0)
- atomic_setbits_int(&p->p_p->ps_tame, _TM_DNS_ACTIVE);
- }
- /*
- * By default, only the advisory cmsg's can be received from the kernel,
- * such as TIMESTAMP ntpd.
- *
- * If TAME_CMSG is set SCM_RIGHTS is also allowed through for a carefully
- * selected set of descriptors (specifically to exclude directories).
- *
- * This results in a kill upon recv, if some other process on the system
- * send a SCM_RIGHTS to an open socket of some sort. That will discourage
- * leaving such sockets lying around...
- */
- int
- tame_cmsg_recv(struct proc *p, void *v, int controllen)
- {
- struct mbuf *control = v;
- struct msghdr tmp;
- struct cmsghdr *cmsg;
- int *fdp, fd;
- struct file *fp;
- int nfds, i;
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- /* Scan the cmsg */
- memset(&tmp, 0, sizeof(tmp));
- tmp.msg_control = mtod(control, struct cmsghdr *);
- tmp.msg_controllen = controllen;
- cmsg = CMSG_FIRSTHDR(&tmp);
- while (cmsg != NULL) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS)
- break;
- cmsg = CMSG_NXTHDR(&tmp, cmsg);
- }
- /* No SCM_RIGHTS found -> OK */
- if (cmsg == NULL)
- return (0);
- if ((p->p_p->ps_tame & _TM_CMSG) == 0)
- return tame_fail(p, EPERM, TAME_CMSG);
- /* In OpenBSD, a CMSG only contains one SCM_RIGHTS. Check it. */
- fdp = (int *)CMSG_DATA(cmsg);
- nfds = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg))) /
- sizeof(struct file *);
- for (i = 0; i < nfds; i++) {
- struct vnode *vp;
- fd = *fdp++;
- fp = fd_getfile(p->p_fd, fd);
- if (fp == NULL)
- return tame_fail(p, EBADF, TAME_CMSG);
- /* Only allow passing of sockets, pipes, and pure files */
- printf("f_type %d\n", fp->f_type);
- switch (fp->f_type) {
- case DTYPE_SOCKET:
- case DTYPE_PIPE:
- continue;
- case DTYPE_VNODE:
- vp = (struct vnode *)fp->f_data;
- printf("v_type %d\n", vp->v_type);
- if (vp->v_type == VREG)
- continue;
- break;
- default:
- break;
- }
- printf("bad fd type\n");
- return tame_fail(p, EPERM, TAME_CMSG);
- }
- return (0);
- }
- /*
- * When tamed, default prevents sending of a cmsg.
- * If CMSG flag is set,
- */
- int
- tame_cmsg_send(struct proc *p, void *v, int controllen)
- {
- struct mbuf *control = v;
- struct msghdr tmp;
- struct cmsghdr *cmsg;
- int *fdp, fd;
- struct file *fp;
- int nfds, i;
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if ((p->p_p->ps_tame & _TM_CMSG) == 0)
- return tame_fail(p, EPERM, TAME_CMSG);
- /* Scan the cmsg */
- memset(&tmp, 0, sizeof(tmp));
- tmp.msg_control = mtod(control, struct cmsghdr *);
- tmp.msg_controllen = controllen;
- cmsg = CMSG_FIRSTHDR(&tmp);
- while (cmsg != NULL) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS)
- break;
- cmsg = CMSG_NXTHDR(&tmp, cmsg);
- }
- /* Contains no SCM_RIGHTS, so OK */
- if (cmsg == NULL)
- return (0);
- /* In OpenBSD, a CMSG only contains one SCM_RIGHTS. Check it. */
- fdp = (int *)CMSG_DATA(cmsg);
- nfds = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg))) /
- sizeof(struct file *);
- for (i = 0; i < nfds; i++) {
- struct vnode *vp;
- fd = *fdp++;
- fp = fd_getfile(p->p_fd, fd);
- if (fp == NULL)
- return tame_fail(p, EBADF, TAME_CMSG);
- /* Only allow passing of sockets, pipes, and pure files */
- printf("f_type %d\n", fp->f_type);
- switch (fp->f_type) {
- case DTYPE_SOCKET:
- case DTYPE_PIPE:
- continue;
- case DTYPE_VNODE:
- vp = (struct vnode *)fp->f_data;
- printf("v_type %d\n", vp->v_type);
- if (vp->v_type == VREG)
- continue;
- break;
- default:
- break;
- }
- /* Not allowed to send a bad fd type */
- return tame_fail(p, EPERM, TAME_CMSG);
- }
- return (0);
- }
- int
- tame_sysctl_check(struct proc *p, int namelen, int *name, void *new)
- {
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if (new)
- return (EFAULT);
- /* getifaddrs() */
- if ((p->p_p->ps_tame & _TM_INET) &&
- namelen == 6 &&
- name[0] == CTL_NET && name[1] == PF_ROUTE &&
- name[2] == 0 && name[3] == 0 &&
- name[4] == NET_RT_IFLIST && name[5] == 0)
- return (0);
- /* used by arp(8). Exposes MAC addresses known on local nets */
- /* XXX Put into a special catagory. */
- if ((p->p_p->ps_tame & _TM_INET) &&
- namelen == 7 &&
- name[0] == CTL_NET && name[1] == PF_ROUTE &&
- name[2] == 0 && name[3] == AF_INET &&
- name[4] == NET_RT_FLAGS && name[5] == RTF_LLINFO)
- return (0);
- /* used by ntpd(8) to read sensors. */
- /* XXX Put into a special catagory. */
- if (namelen >= 3 &&
- name[0] == CTL_HW && name[1] == HW_SENSORS)
- return (0);
- /* getdomainname(), gethostname(), getpagesize(), uname() */
- if (namelen == 2 &&
- name[0] == CTL_KERN && name[1] == KERN_DOMAINNAME)
- return (0);
- if (namelen == 2 &&
- name[0] == CTL_KERN && name[1] == KERN_HOSTNAME)
- return (0);
- if (namelen == 2 &&
- name[0] == CTL_KERN && name[1] == KERN_OSTYPE)
- return (0);
- if (namelen == 2 &&
- name[0] == CTL_KERN && name[1] == KERN_OSRELEASE)
- return (0);
- if (namelen == 2 &&
- name[0] == CTL_KERN && name[1] == KERN_OSVERSION)
- return (0);
- if (namelen == 2 &&
- name[0] == CTL_KERN && name[1] == KERN_VERSION)
- return (0);
- if (namelen == 2 &&
- name[0] == CTL_HW && name[1] == HW_MACHINE)
- return (0);
- if (namelen == 2 &&
- name[0] == CTL_HW && name[1] == HW_PAGESIZE)
- return (0);
- printf("tame: pid %d %s sysctl %d: %d %d %d %d %d %d\n",
- p->p_pid, p->p_comm, namelen, name[0], name[1],
- name[2], name[3], name[4], name[5]);
- return (EFAULT);
- }
- int
- tame_adjtime_check(struct proc *p, const void *v)
- {
- const struct timeval *delta = v;
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if (delta)
- return (EFAULT);
- return (0);
- }
- int
- tame_connect_check(struct proc *p)
- {
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if ((p->p_p->ps_tame & _TM_DNS_ACTIVE))
- return (0); /* A port check happens inside sys_connect() */
- if ((p->p_p->ps_tame & (_TM_INET | _TM_UNIX)))
- return (0);
- return (EPERM);
- }
- int
- tame_recvfrom_check(struct proc *p, void *v)
- {
- struct sockaddr *from = v;
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if ((p->p_p->ps_tame & _TM_DNS_ACTIVE) && from == NULL)
- return (0);
- if (p->p_p->ps_tame & _TM_INET)
- return (0);
- if (p->p_p->ps_tame & _TM_UNIX)
- return (0);
- if (from == NULL)
- return (0); /* behaves just like write */
- return (EPERM);
- }
- int
- tame_sendto_check(struct proc *p, const void *v)
- {
- const struct sockaddr *to = v;
-
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if ((p->p_p->ps_tame & _TM_DNS_ACTIVE) && to == NULL)
- return (0);
- if ((p->p_p->ps_tame & _TM_INET))
- return (0);
- if ((p->p_p->ps_tame & _TM_UNIX))
- return (0);
- if (to == NULL)
- return (0); /* behaves just like write */
- return (EPERM);
- }
- int
- tame_socket_check(struct proc *p, int domain)
- {
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if ((p->p_p->ps_tame & (_TM_INET | _TM_UNIX)))
- return (0);
- if ((p->p_p->ps_tame & _TM_DNS_ACTIVE) && domain == AF_INET)
- return (0);
- return (EPERM);
- }
- int
- tame_bind_check(struct proc *p, const void *v)
- {
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if ((p->p_p->ps_tame & _TM_INET))
- return (0);
- return (EPERM);
- }
- int
- tame_ioctl_check(struct proc *p, long com, void *v)
- {
- struct file *fp = v;
- struct vnode *vp = NULL;
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if (fp == NULL)
- return (EBADF);
- vp = (struct vnode *)fp->f_data;
- switch (com) {
- /*
- * This is a set of "get" info ioctls at the top layer. Hopefully
- * a safe list, since they are used a lot.
- */
- case FIOCLEX:
- case FIONCLEX:
- case FIONREAD:
- case FIONBIO:
- case FIOGETOWN:
- return (0);
- case FIOASYNC:
- case FIOSETOWN:
- return (EPERM);
- /* tty subsystem */
- case TIOCGWINSZ: /* various programs */
- case TIOCSTI: /* ksh? csh? */
- if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
- return (0);
- break;
- default:
- break;
- }
- if ((p->p_p->ps_tame & _TM_IOCTL) == 0)
- return (EPERM);
- /*
- * Further sets of ioctl become available, but are checked a
- * bit more carefully against the vnode.
- */
- switch (com) {
- case BIOCGSTATS: /* bpf: tcpdump privsep on ^C */
- if (fp->f_type == DTYPE_VNODE &&
- fp->f_ops->fo_ioctl == vn_ioctl)
- return (0);
- break;
- case TIOCSETAF: /* tcsetattr TCSAFLUSH, script */
- if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY))
- return (0);
- break;
- case MTIOCGET:
- case MTIOCTOP:
- /* for pax(1) and such, checking tapes... */
- if (fp->f_type == DTYPE_VNODE &&
- (vp->v_type == VCHR || vp->v_type == VBLK))
- return (0);
- break;
- case SIOCGIFGROUP:
- if ((p->p_p->ps_tame & _TM_INET) &&
- fp->f_type == DTYPE_SOCKET)
- return (0);
- break;
- default:
- printf("ioctl %lx\n", com);
- break;
- }
- return (EPERM);
- }
- int
- tame_setsockopt_check(struct proc *p, int level, int optname)
- {
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- switch (level) {
- case SOL_SOCKET:
- switch (optname) {
- case SO_RTABLE:
- return (EPERM);
- }
- return (0);
- case IPPROTO_TCP:
- switch (optname) {
- case TCP_NODELAY:
- case TCP_MD5SIG:
- case TCP_SACK_ENABLE:
- case TCP_MAXSEG:
- case TCP_NOPUSH:
- return (0);
- }
- case IPPROTO_IP:
- switch (optname) {
- case IP_TOS:
- case IP_TTL:
- case IP_MINTTL:
- case IP_PORTRANGE:
- case IP_RECVDSTADDR:
- return (0);
- }
- case IPPROTO_ICMP:
- break;
- case IPPROTO_IPV6:
- case IPPROTO_ICMPV6:
- break;
- }
- return (EPERM);
- }
- int
- tame_dns_check(struct proc *p, in_port_t port)
- {
- if ((p->p_p->ps_flags & PS_TAMED) == 0)
- return (0);
- if ((p->p_p->ps_tame & _TM_INET))
- return (0);
- if ((p->p_p->ps_tame & _TM_DNS_ACTIVE) && port == htons(53))
- return (0); /* Allow a DNS connect outbound */
- return (EPERM);
- }
- int
- canonpath(const char *input, char *buf, size_t bufsize)
- {
- char *p, *q, *s, *end;
- /* can't canon relative paths, don't bother */
- if (input[0] != '/') {
- if (strlcpy(buf, input, bufsize) >= bufsize)
- return (EINVAL);
- return (0);
- }
- /* easiest to work with strings always ending in '/' */
- if (snprintf(buf, bufsize, "%s/", input) >= bufsize)
- return (EINVAL);
- /* after this we will only be shortening the string. */
- p = buf;
- q = p;
- while (*p) {
- if (p[0] == '/' && p[1] == '/') {
- p += 1;
- } else if (p[0] == '/' && p[1] == '.' &&
- p[2] == '/') {
- p += 2;
- } else {
- *q++ = *p++;
- }
- }
- *q = 0;
- end = buf + strlen(buf);
- s = buf;
- p = s;
- while (1) {
- /* find "/../" (where's strstr when you need it?) */
- while (p < end) {
- if (p[0] == '/' && strncmp(p + 1, "../", 3) == 0)
- break;
- p++;
- }
- if (p == end)
- break;
- if (p == s) {
- memmove(s, p + 3, end - p - 3 + 1);
- end -= 3;
- } else {
- /* s starts with '/', so we know there's one
- * somewhere before p. */
- q = p - 1;
- while (*q != '/')
- q--;
- memmove(q, p + 3, end - p - 3 + 1);
- end -= p + 3 - q;
- p = q;
- }
- }
- if (end > s + 1)
- *(end - 1) = 0; /* remove trailing '/' */
- return 0;
- }
|