Add seccomp bpf sandboxing to redbean

It's now possible to pass the `-S` or `-SS` flags to sandbox redbean
worker proecsses after they've been forked. The first `-S` flag is
intended to be a permissive builtin policy that limits system calls to
only that which the various parts of redbean serving need. The second
`-SS` flag is intended to be more restrictive, preventing things like
the Lua extensions you download off the web from using the HTTP client
or sockets APIs. In upcoming changes you'll be able to implement your
own Berkeley Packet Filter sandbox programs and load them via Lua.
This commit is contained in:
Justine Tunney 2022-04-18 08:54:42 -07:00
parent 7166679620
commit 5a132f9652
79 changed files with 2271 additions and 651 deletions

View file

@ -45,6 +45,7 @@ FLAGS
-s increase silence [repeatable]
-v increase verbosity [repeatable]
-V increase ssl verbosity [repeatable]
-S increase bpf seccomp sandboxing [repeatable]
-H K:V sets http header globally [repeatable]
-D DIR overlay assets in local directory [repeatable]
-r /X=/Y redirect X to Y [repeatable]
@ -1231,7 +1232,7 @@ UNIX MODULE
Reads from file descriptor.
unix.write(fd:int, data[, offset]) → rc:int, errno:int
unix.write(fd:int, data[, offset]) → rc:int[, errno:int]
Writes to file descriptor.
@ -1242,25 +1243,25 @@ UNIX MODULE
`flags` should have one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`.
The following values may also be OR'd into `flags`:
- `O_CREAT`: Create file if it doesn't exist.
- `O_TRUNC` Automatic truncate(fd,0) if exists.
- `O_CLOEXEC`: Automatic close() upon execve().
- `O_EXCL`: Exclusive access. See below.
- `O_APPEND`: Open file for append only.
- `O_DIRECT` (not supported on Apple and OpenBSD)
- `O_DIRECTORY` (hint on UNIX but required on NT)
- `O_TMPFILE` (for Linux and Windows only)
- `O_NOFOLLOW` (zero on Windows)
- `O_DSYNC` (zero on non-Linux/Apple)
- `O_RSYNC` (zero on non-Linux/Apple)
- `O_PATH` (zero on non-Linux)
- `O_VERIFY` (zero on non-FreeBSD)
- `O_SHLOCK` (zero on non-BSD)
- `O_EXLOCK` (zero on non-BSD)
- `O_RANDOM` (zero on non-Windows)
- `O_SEQUENTIAL` (zero on non-Windows)
- `O_COMPRESSED` (zero on non-Windows)
- `O_INDEXED` (zero on non-Windows)
- `O_CREAT`: create file if it doesn't exist
- `O_TRUNC` automatic ftruncate(fd,0) if exists
- `O_CLOEXEC`: automatic close() upon execve()
- `O_EXCL`: exclusive access (see below)
- `O_APPEND`: open file for append only
- `O_DIRECT` it's complicated (not supported on Apple and OpenBSD)
- `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
- `O_TMPFILE` try to make temp more secure (Linux and Windows only)
- `O_NOFOLLOW` fail if it's a symlink (zero on Windows)
- `O_DSYNC` it's complicated (zero on non-Linux/Apple)
- `O_RSYNC` it's complicated (zero on non-Linux/Apple)
- `O_PATH` it's complicated (zero on non-Linux)
- `O_VERIFY` it's complicated (zero on non-FreeBSD)
- `O_SHLOCK` it's complicated (zero on non-BSD)
- `O_EXLOCK` it's complicated (zero on non-BSD)
- `O_RANDOM` hint random access intent (zero on non-Windows)
- `O_SEQUENTIAL` hint sequential access intent (zero on non-Windows)
- `O_COMPRESSED` ask fs to abstract compression (zero on non-Windows)
- `O_INDEXED` turns on that slow performance (zero on non-Windows)
There are three regular combinations for the above flags:
@ -1277,7 +1278,7 @@ UNIX MODULE
already. If it does exist then `nil` is returned along with
`errno` set to `EEXIST`.
unix.close(fd:int) → rc:int, errno:int
unix.close(fd:int) → rc:int[, errno:int]
Closes file descriptor.
@ -1288,12 +1289,12 @@ UNIX MODULE
will be closed. Any open connections it owns will be reset. This
function never returns.
unix.fork() → childpid|0, errno:int
unix.fork() → childpid|0:int[, errno:int]
Creates a new process mitosis style. This returns twice. The
parent process gets the nonzero pid. The child gets zero.
unix.commandv(prog) → path, errno:int
unix.commandv(prog:str) → path:str[, errno:int]
Performs `$PATH` lookup of executable. We automatically suffix
`.com` and `.exe` automatically for all platforms when path
@ -1302,6 +1303,11 @@ UNIX MODULE
`prog` contains slashes then it's not path searched either and
will be returned if it exists.
unix.realpath(filename:str) → abspath:str[, errno:int]
Returns absolute path of filename, with `.` and `..` components
removed, and symlinks will be resolved.
unix.execve(prog, argv[, envp]) → errno
Exits current process, replacing it with a new instance of the
@ -1315,29 +1321,29 @@ UNIX MODULE
The first element in `argv` should be `prog`. This function is
normally called after forking.
unix.access(path:str, how) → rc:int, errno:int
unix.access(path:str, how) → rc:int[, errno:int]
Checks if effective user of current process has permission to
access file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to
check for read, write, execute, and existence respectively.
unix.mkdir(path:str, mode) → rc:int, errno:int
unix.mkdir(path:str, mode) → rc:int[, errno:int]
Makes directory. `mode` should be octal, e.g. `0755`.
unix.chdir(path:str) → rc:int, errno:int
unix.chdir(path:str) → rc:int[, errno:int]
Changes current directory to `path`.
unix.unlink(path:str) → rc:int, errno:int
unix.unlink(path:str) → rc:int[, errno:int]
Removes file at `path`.
unix.rmdir(path:str) → rc:int, errno:int
unix.rmdir(path:str) → rc:int[, errno:int]
Removes empty directory at `path`.
unix.chroot(path:str) → rc:int, errno:int
unix.chroot(path:str) → rc:int[, errno:int]
Changes root directory. Raises `ENOSYS` on Windows.
@ -1352,44 +1358,95 @@ UNIX MODULE
writing. `flags` can have `O_CLOEXEC`. On error, `reader` and
`writer` will be `nil` and `errno` will be set to non-nil.
unix.rename(oldpath, newpath) → rc:int, errno:int
unix.link(existingpath, newpath) → rc:int, errno:int
unix.symlink(target, linkpath) → rc:int, errno:int
unix.chown(path:str, uid, gid) → rc:int, errno:int
unix.chmod(path:str, mode) → rc:int, errno:int
unix.getcwd(path:str, mode) → rc:int, errno:int
unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int]
Renames file.
unix.link(existingpath:str, newpath:str) → rc:int[, errno:int]
Creates hard link, so your underlying inode has two names.
unix.symlink(target:str, linkpath:str) → rc:int[, errno:int]
Creates soft link, or a symbolic link.
unix.chown(path:str, uid, gid) → rc:int[, errno:int]
Changes user and gorup on file.
unix.chmod(path:str, mode) → rc:int[, errno:int]
unix.getcwd(path:str, mode) → rc:int[, errno:int]
unix.getpid() → pid
unix.getppid() → pid
unix.kill(pid, sig) → rc:int, errno:int
unix.raise(sig) → rc:int, errno:int
unix.kill(pid, sig) → rc:int[, errno:int]
unix.raise(sig) → rc:int[, errno:int]
unix.wait(pid[, options]) → pid, wstatus, nil, errno:int
unix.fcntl(fd:int, cmd[, arg]) → rc:int, errno:int
unix.fcntl(fd:int, cmd[, arg]) → rc:int[, errno:int]
unix.getsid(pid) → sid, errno:int
unix.getpgrp() → pgid, errno:int
unix.getpgid(pid) → pgid, errno:int
unix.setpgid(pid, pgid) → pgid, errno:int
unix.setsid() → sid, errno:int
unix.getuid() → uid, errno:int
unix.setuid(uid:int) → rc:int[, errno:int]
unix.getgid() → gid, errno:int
unix.umask(mask) → rc:int, errno:int
unix.gettime([clock]) → seconds, nanos, errno:int
unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno:int
unix.sync(fd:int)
unix.fsync(fd:int) → rc:int, errno:int
unix.fdatasync(fd:int) → rc:int, errno:int
unix.setgid(gid:int) → rc:int[, errno:int]
unix.umask(mask) → rc:int[, errno:int]
unix.seek(fd:int, offset, whence) → newpos, errno:int
where whence ∈ {SEEK_SET, SEEK_CUR, SEEK_END}
whence defaults to SEEK_SET
unix.truncate(path:str, length) → rc:int, errno:int
unix.truncate(fd:int, length) → rc:int, errno:int
unix.syslog(priority:str, msg:str)
Generates a log message which will be distributed by syslogd.
`priority` is a bitmask containing the facility value and the
level value. If no facility value is ORed into priority, then
the default value set by openlog() is used. it set to NULL, the
program name is used. Level is one of `LOG_EMERG`, `LOG_ALERT`,
`LOG_CRIT`, `LOG_ERR`, `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO`,
`LOG_DEBUG`.
This function currently works on Linux, Windows, and NetBSD. On
WIN32 it uses the ReportEvent() facility.
unix.clock_gettime([clock]) → seconds, nanos, errno:int
Returns nanosecond precision timestamp from the system.
`clock` should be `CLOCK_REALTIME`, `CLOCK_MONOTONIC`, or
`CLOCK_MONOTONIC_RAW` since they work across platforms.
You may also try your luck with `CLOCK_REALTIME_COARSE`,
`CLOCK_MONOTONIC_COARSE`, `CLOCK_PROCESS_CPUTIME_ID`,
`CLOCK_TAI`, `CLOCK_PROF`, `CLOCK_BOOTTIME`,
`CLOCK_REALTIME_ALARM`, and `CLOCK_BOOTTIME_ALARM`,
unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno:int
Sleeps with nanosecond precision.
unix.sync(fd:int)
unix.fsync(fd:int) → rc:int[, errno:int]
unix.fdatasync(fd:int) → rc:int[, errno:int]
These functions are used to make programs slower by asking the
operating system to flush data to the physical medium.
unix.seek(fd:int, offset:int, whence:int) → newpos:int[, errno:int]
Seeks to file position.
`whence` can be one of `SEEK_SET`, `SEEK_CUR`, or `SEEK_END`.
unix.truncate(path:str, length) → rc:int[, errno:int]
unix.ftruncate(fd:int, length) → rc:int[, errno:int]
Reduces or extends underlying physical medium of file.
If file was originally larger, content >length is lost.
unix.socket([family[, type[, protocol]]]) → fd:int[, errno:int]
`SOCK_CLOEXEC` may be or'd into type
`family` defaults to `AF_INET`
`type` defaults to `SOCK_STREAM`
`protocol` defaults to `IPPROTO_TCP`
`family` defaults to `AF_INET` but can be `AF_UNIX`
`type` defaults to `SOCK_STREAM` but can be `SOCK_DGRAM`
`protocol` defaults to `IPPROTO_TCP` but can be `IPPROTO_UDP`
unix.socketpair([family[, type[, protocol]]]) → fd1, fd2, errno:int
@ -1398,11 +1455,11 @@ UNIX MODULE
`type` defaults to `SOCK_STREAM`
`protocol` defaults to `IPPROTO_TCP`
unix.bind(fd:int, ip, port) → rc:int, errno:int
unix.bind(fd:int, ip, port) → rc:int[, errno:int]
unix.connect(fd:int, ip, port) → rc:int, errno:int
unix.connect(fd:int, ip, port) → rc:int[, errno:int]
unix.listen(fd:int[, backlog]) → rc:int, errno:int
unix.listen(fd:int[, backlog]) → rc:int[, errno:int]
unix.getsockname(fd:int) → ip, port, errno:int
@ -1430,7 +1487,7 @@ UNIX MODULE
addresses. The `flags` parameter can have `MSG_OOB`,
`MSG_DONTROUTE`, or `MSG_NOSIGNAL`.
unix.shutdown(fd:int, how:int) → rc:int, errno:int
unix.shutdown(fd:int, how:int) → rc:int[, errno:int]
Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or
`SHUT_RDWR`.
@ -1602,21 +1659,6 @@ UNIX MODULE
- `ENOSYS`: System call not available on this platform. On Windows
this is raised by chroot(), setuid(), setgid().
- `EPERM`: Operation not permitted. Raised by accept(), adjtimex(),
arch_prctl(), bdflush(), capget(), chmod(), chown(), chroot(),
clock_getres(), copy_file_range(), execve(), fcntl(),
get_robust_list(), getdomainname(), getgroups(), gethostname(),
getpriority(), getrlimit(), getsid(), gettimeofday(), kill(),
link(), mbind(), membarrier(), migrate_pages(), mkdir(), mknod(),
mlock(), mmap(), msgctl(), nice(), open(), prctl(), ptrace(),
reboot(), rename(), rmdir(), sched_setaffinity(),
sched_setattr(), sched_setparam(), sched_setscheduler(),
seteuid(), setfsgid(), setfsuid(), setgid(), setpgid(),
setresuid(), setreuid(), setsid(), setuid(), setup(), shmget(),
sigaltstack(), stime(), swapon(), symlink(), syslog(),
timer_create(), timerfd_create(), tkill(), truncate(), u
unlink(), utime(), utimensat(), vhangup(), vm86(), write().
- `ENOENT`: no such file or directory. Raised by access(),
alloc_hugepages(), bind(), chdir(), chmod(), chown(), chroot(),
clock_getres(), execve(), opendir(), inotify_add_watch(), kcmp(),
@ -1692,6 +1734,28 @@ UNIX MODULE
rmdir(), semget(), send(), setpgid(), shmget(), socket(), stat(),
symlink(), truncate(), unlink(), uselib(), utime(), utimensat(),
- `EPERM`: Operation not permitted. Raised by accept(), chmod(),
chown(), chroot(), copy_file_range(), execve(), fallocate(),
fanotify_init(), fcntl(), futex(), get_robust_list(),
getdomainname(), getgroups(), gethostname(), getpriority(),
getrlimit(), getsid(), gettimeofday(), idle(), init_module(),
io_submit(), ioctl_console(), ioctl_ficlonerange(),
ioctl_fideduperange(), ioctl_ns(), ioctl_tty(), ioperm(), iopl(),
ioprio_set(), kcmp(), kexec_load(), keyctl(), kill(), link(),
lookup_dcookie(), madvise(), mbind(), membarrier(),
migrate_pages(), mkdir(), mknod(), mlock(), mmap(), mount(),
move_pages(), msgctl(), nice(), open(), open_by_handle_at(),
pciconfig_read(), perf_event_open(), pidfd_getfd(),
pidfd_send_signal(), pivot_root(), prctl(), process_vm_readv(),
ptrace(), quotactl(), reboot(), rename(), request_key(), rmdir(),
rt_sigqueueinfo(), sched_setaffinity(), sched_setattr(),
sched_setparam(), sched_setscheduler(), semctl(), seteuid(),
setfsgid(), setfsuid(), setgid(), setns(), setpgid(),
setresuid(), setreuid(), setsid(), setuid(), setup(), setxattr(),
shmctl(), shmget(), sigaltstack(), spu_create(), stime(),
swapon(), symlink(), syslog(), truncate(), unlink(), utime(),
utimensat(), write()
- `ENOTBLK`: Block device required. Raised by umount().
- `EBUSY`: Device or resource busy. Raised by bdflush(), dup(),

View file

@ -37,6 +37,7 @@
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/sock/syslog.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/at.h"
@ -46,6 +47,7 @@
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/log.h"
#include "libc/sysv/consts/msg.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/o.h"
@ -104,6 +106,16 @@ static int ReturnRc(lua_State *L, int64_t rc, int olderr) {
return 2;
}
static int ReturnErrno(lua_State *L, int nils, int olderr) {
int i;
for (i = 0; i < nils; ++i) {
lua_pushnil(L);
}
lua_pushinteger(L, errno);
errno = olderr;
return nils + 1;
}
static char **ConvertLuaArrayToStringList(lua_State *L, int i) {
int j, n;
char **p;
@ -133,13 +145,13 @@ static void FreeStringList(char **p) {
////////////////////////////////////////////////////////////////////////////////
// System Calls
// unix.exit([exitcode]) → ⊥
// unix.exit([exitcode:int]) → ⊥
static wontreturn int LuaUnixExit(lua_State *L) {
_Exit(luaL_optinteger(L, 1, 0));
}
// unix.access(path, mode) → rc[, errno]
// mode can be: R_OK, W_OK, X_OK, F_OK
// unix.access(path:str, how:int) → rc:int[, errno:int]
// how can be: R_OK, W_OK, X_OK, F_OK
static int LuaUnixAccess(lua_State *L) {
const char *file;
int rc, mode, olderr;
@ -150,19 +162,31 @@ static int LuaUnixAccess(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.mkdir(path, mode) → rc[, errno]
// unix.mkdir(path:str[, mode:int]) → rc:int[, errno:int]
// mode should be octal
static int LuaUnixMkdir(lua_State *L) {
const char *file;
int rc, mode, olderr;
olderr = errno;
file = luaL_checklstring(L, 1, 0);
mode = luaL_checkinteger(L, 2);
mode = luaL_optinteger(L, 2, 0755);
rc = mkdir(file, mode);
return ReturnRc(L, rc, olderr);
}
// unix.chdir(path) → rc[, errno]
// unix.makedirs(path:str[, mode:int]) → rc:int[, errno:int]
// mode should be octal
static int LuaUnixMakedirs(lua_State *L) {
const char *file;
int rc, mode, olderr;
olderr = errno;
file = luaL_checklstring(L, 1, 0);
mode = luaL_optinteger(L, 2, 0755);
rc = makedirs(file, mode);
return ReturnRc(L, rc, olderr);
}
// unix.chdir(path:str) → rc:int[, errno:int]
static int LuaUnixChdir(lua_State *L) {
int rc, olderr;
const char *file;
@ -172,7 +196,7 @@ static int LuaUnixChdir(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.unlink(path) → rc[, errno]
// unix.unlink(path:str) → rc:int[, errno:int]
static int LuaUnixUnlink(lua_State *L) {
int rc, olderr;
const char *file;
@ -182,7 +206,7 @@ static int LuaUnixUnlink(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.rmdir(path) → rc[, errno]
// unix.rmdir(path:str) → rc:int[, errno:int]
static int LuaUnixRmdir(lua_State *L) {
const char *file;
int rc, olderr;
@ -192,7 +216,7 @@ static int LuaUnixRmdir(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.rename(oldpath, newpath) → rc[, errno]
// unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int]
static int LuaUnixRename(lua_State *L) {
const char *oldpath, *newpath;
int rc, olderr;
@ -203,7 +227,7 @@ static int LuaUnixRename(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.link(existingpath, newpath) → rc[, errno]
// unix.link(existingpath:str, newpath:str) → rc:int[, errno:int]
static int LuaUnixLink(lua_State *L) {
const char *existingpath, *newpath;
int rc, olderr;
@ -214,7 +238,7 @@ static int LuaUnixLink(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.symlink(target, linkpath) → rc[, errno]
// unix.symlink(target:str, linkpath:str) → rc:int[, errno:int]
static int LuaUnixSymlink(lua_State *L) {
const char *target, *linkpath;
int rc, olderr;
@ -225,7 +249,7 @@ static int LuaUnixSymlink(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.chown(path, uid, gid) → rc[, errno]
// unix.chown(path:str, uid:int, gid:int) → rc:int[, errno:int]
static int LuaUnixChown(lua_State *L) {
const char *file;
int rc, uid, gid, olderr;
@ -237,7 +261,7 @@ static int LuaUnixChown(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.chmod(path, mode) → rc[, errno]
// unix.chmod(path:str, mode:int) → rc:int[, errno:int]
static int LuaUnixChmod(lua_State *L) {
const char *file;
int rc, mode, olderr;
@ -248,7 +272,7 @@ static int LuaUnixChmod(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.getcwd(path, mode) → rc[, errno]
// unix.getcwd(path:str, mode:int) → rc:int[, errno:int]
static int LuaUnixGetcwd(lua_State *L) {
char *path;
path = getcwd(0, 0);
@ -258,7 +282,7 @@ static int LuaUnixGetcwd(lua_State *L) {
return 1;
}
// unix.fork() → childpid|0, errno
// unix.fork() → childpid|0:int[, errno:int]
static int LuaUnixFork(lua_State *L) {
int rc, olderr;
olderr = errno;
@ -298,7 +322,7 @@ static int LuaUnixExecve(lua_State *L) {
return 1;
}
// unix.commandv(prog) → path[, errno]
// unix.commandv(prog:str) → path:str[, errno:int]
static int LuaUnixCommandv(lua_State *L) {
const char *prog;
int rc, olderr, pushed;
@ -319,7 +343,29 @@ static int LuaUnixCommandv(lua_State *L) {
return pushed;
}
// unix.chroot(path) → rc[, errno]
// unix.realpath(path:str) → path:str[, errno:int]
static int LuaUnixRealpath(lua_State *L) {
char *resolved;
int rc, olderr;
const char *path;
olderr = errno;
path = luaL_checkstring(L, 1);
if ((resolved = realpath(path, 0))) {
lua_pushstring(L, resolved);
free(resolved);
return 1;
} else {
return ReturnErrno(L, 1, olderr);
}
}
// unix.syslog(priority:str, msg:str)
static int LuaUnixSyslog(lua_State *L) {
syslog(luaL_checkinteger(L, 1), "%s", luaL_checkstring(L, 2));
return 0;
}
// unix.chroot(path:str) → rc:int[, errno:int]
static int LuaUnixChroot(lua_State *L) {
int rc, olderr;
const char *path;
@ -351,27 +397,23 @@ static int LuaUnixGetrlimit(lua_State *L) {
lua_pushinteger(L, rlim.rlim_max);
return 2;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 3;
return ReturnErrno(L, 2, olderr);
}
}
// unix.getpid() → pid
// unix.getpid() → pid:int
static int LuaUnixGetpid(lua_State *L) {
lua_pushinteger(L, getpid());
return 1;
}
// unix.getppid() → pid
// unix.getppid() → pid:int
static int LuaUnixGetppid(lua_State *L) {
lua_pushinteger(L, getppid());
return 1;
}
// unix.kill(pid, sig) → rc[, errno]
// unix.kill(pid, sig) → rc:int[, errno:int]
static int LuaUnixKill(lua_State *L) {
int rc, pid, sig, olderr;
olderr = errno;
@ -381,7 +423,7 @@ static int LuaUnixKill(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.raise(sig) → rc[, errno]
// unix.raise(sig) → rc:int[, errno:int]
static int LuaUnixRaise(lua_State *L) {
int rc, sig, olderr;
olderr = errno;
@ -402,16 +444,11 @@ static int LuaUnixWait(lua_State *L) {
lua_pushinteger(L, wstatus);
return 2;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushnil(L); // for future use
lua_pushinteger(L, errno);
errno = olderr;
return 4;
return ReturnErrno(L, 3, olderr);
}
}
// unix.fcntl(fd, cmd[, arg]) → rc[, errno]
// unix.fcntl(fd, cmd[, arg]) → rc:int[, errno:int]
static int LuaUnixFcntl(lua_State *L) {
intptr_t arg;
int rc, fd, cmd, olderr;
@ -450,15 +487,11 @@ static int LuaUnixPipe(lua_State *L) {
lua_pushinteger(L, pipefd[1]);
return 2;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 3;
return ReturnErrno(L, 2, olderr);
}
}
// unix.getsid(pid) → sid, errno
// unix.getsid(pid) → sid:int[, errno:int]
static int LuaUnixGetsid(lua_State *L) {
int rc, pid, olderr;
olderr = errno;
@ -467,7 +500,7 @@ static int LuaUnixGetsid(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.getpgrp() → pgid, errno
// unix.getpgrp() → pgid:int[, errno:int]
static int LuaUnixGetpgrp(lua_State *L) {
int rc, olderr;
olderr = errno;
@ -475,7 +508,7 @@ static int LuaUnixGetpgrp(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.getpgid(pid) → pgid, errno
// unix.getpgid(pid:int) → pgid:int[, errno:int]
static int LuaUnixGetpgid(lua_State *L) {
int rc, pid, olderr;
olderr = errno;
@ -484,7 +517,7 @@ static int LuaUnixGetpgid(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.umask(mask) → rc[, errno]
// unix.umask(mask:int) → rc:int[, errno:int]
static int LuaUnixUmask(lua_State *L) {
int rc, mask, olderr;
olderr = errno;
@ -493,7 +526,7 @@ static int LuaUnixUmask(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.setpgid(pid, pgid) → pgid, errno
// unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int]
static int LuaUnixSetpgid(lua_State *L) {
int rc, pid, pgid, olderr;
olderr = errno;
@ -503,7 +536,7 @@ static int LuaUnixSetpgid(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.setsid() → sid, errno
// unix.setsid() → sid:int[, errno:int]
static int LuaUnixSetsid(lua_State *L) {
int rc, olderr;
olderr = errno;
@ -511,7 +544,7 @@ static int LuaUnixSetsid(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.getuid() → uid, errno
// unix.getuid() → uid[, errno]
static int LuaUnixGetuid(lua_State *L) {
int rc, olderr;
olderr = errno;
@ -519,7 +552,13 @@ static int LuaUnixGetuid(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.getgid() → gid, errno
// unix.setuid(uid:int) → rc:int[, errno:int]
static int LuaUnixSetuid(lua_State *L) {
int olderr = errno;
return ReturnRc(L, setuid(luaL_checkinteger(L, 1)), olderr);
}
// unix.getgid() → gid:int[, errno:int]
static int LuaUnixGetgid(lua_State *L) {
int rc, olderr;
olderr = errno;
@ -527,7 +566,13 @@ static int LuaUnixGetgid(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.gettime([clock]) → seconds, nanos, errno
// unix.setgid(gid:int) → rc:int[, errno:int]
static int LuaUnixSetgid(lua_State *L) {
int olderr = errno;
return ReturnRc(L, setgid(luaL_checkinteger(L, 1)), olderr);
}
// unix.clock_gettime([clock]) → seconds, nanos, errno
static int LuaUnixGettime(lua_State *L) {
struct timespec ts;
int rc, clock, olderr;
@ -539,11 +584,7 @@ static int LuaUnixGettime(lua_State *L) {
lua_pushinteger(L, ts.tv_nsec);
return 2;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 3;
return ReturnErrno(L, 2, olderr);
}
}
@ -560,21 +601,17 @@ static int LuaUnixNanosleep(lua_State *L) {
lua_pushinteger(L, rem.tv_nsec);
return 2;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 3;
return ReturnErrno(L, 2, olderr);
}
}
// unix.sync(fd)
// unix.sync(fd:int)
static int LuaUnixSync(lua_State *L) {
sync();
return 0;
}
// unix.fsync(fd) → rc[, errno]
// unix.fsync(fd:int) → rc:int[, errno:int]
static int LuaUnixFsync(lua_State *L) {
int rc, fd, olderr;
olderr = errno;
@ -583,7 +620,7 @@ static int LuaUnixFsync(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.fdatasync(fd) → rc[, errno]
// unix.fdatasync(fd:int) → rc:int[, errno:int]
static int LuaUnixFdatasync(lua_State *L) {
int rc, fd, olderr;
olderr = errno;
@ -604,7 +641,7 @@ static int LuaUnixOpen(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.close(fd) → rc[, errno]
// unix.close(fd:int) → rc:int[, errno:int]
static int LuaUnixClose(lua_State *L) {
int rc, fd, olderr;
olderr = errno;
@ -627,8 +664,8 @@ static int LuaUnixSeek(lua_State *L) {
return ReturnRc(L, newpos, olderr);
}
// unix.truncate(path, length) → rc[, errno]
// unix.truncate(fd, length) → rc[, errno]
// unix.truncate(path, length) → rc:int[, errno:int]
// unix.truncate(fd, length) → rc:int[, errno:int]
static int LuaUnixTruncate(lua_State *L) {
int64_t length;
const char *path;
@ -649,7 +686,7 @@ static int LuaUnixTruncate(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.read(fd[, bufsiz, offset]) → data, errno
// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str, errno:int
static int LuaUnixRead(lua_State *L) {
char *buf;
size_t got;
@ -680,7 +717,7 @@ static int LuaUnixRead(lua_State *L) {
return pushed;
}
// unix.write(fd, data[, offset]) → rc[, errno]
// unix.write(fd:int, data:str[, offset:int]) → rc:int[, errno:int]
static int LuaUnixWrite(lua_State *L) {
size_t size;
int fd, olderr;
@ -699,8 +736,8 @@ static int LuaUnixWrite(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.stat(path) → UnixStat*[, errno]
// unix.stat(fd) → UnixStat*[, errno]
// unix.stat(path:str) → UnixStat*[, errno]
// unix.stat(fd:int) → UnixStat*[, errno]
static int LuaUnixStat(lua_State *L) {
const char *path;
int rc, fd, olderr;
@ -731,8 +768,8 @@ static int LuaUnixStat(lua_State *L) {
return 1;
}
// unix.opendir(path) → UnixDir*[, errno]
// unix.opendir(fd) → UnixDir*[, errno]
// unix.opendir(path:str) → UnixDir*[, errno]
// unix.opendir(fd:int) → UnixDir*[, errno]
static int LuaUnixOpendir(lua_State *L) {
DIR *rc;
int fd, olderr;
@ -797,15 +834,11 @@ static int LuaUnixSocketpair(lua_State *L) {
lua_pushinteger(L, sv[1]);
return 2;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 3;
return ReturnErrno(L, 2, olderr);
}
}
// unix.bind(fd, ip, port) → rc[, errno]
// unix.bind(fd, ip, port) → rc:int[, errno:int]
// SOCK_CLOEXEC may be or'd into type
// family defaults to AF_INET
// type defaults to SOCK_STREAM
@ -822,7 +855,7 @@ static int LuaUnixBind(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.connect(fd, ip, port) → rc[, errno]
// unix.connect(fd, ip, port) → rc:int[, errno:int]
// SOCK_CLOEXEC may be or'd into type
// family defaults to AF_INET
// type defaults to SOCK_STREAM
@ -839,7 +872,7 @@ static int LuaUnixConnect(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.listen(fd[, backlog]) → rc[, errno]
// unix.listen(fd[, backlog]) → rc:int[, errno:int]
static int LuaUnixListen(lua_State *L) {
int rc, fd, olderr, backlog;
olderr = errno;
@ -863,11 +896,7 @@ static int LuaUnixGetsockname(lua_State *L) {
lua_pushinteger(L, ntohs(sa.sin_port));
return 2;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 3;
return ReturnErrno(L, 2, olderr);
}
}
@ -885,11 +914,7 @@ static int LuaUnixGetpeername(lua_State *L) {
lua_pushinteger(L, ntohs(sa.sin_port));
return 2;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 3;
return ReturnErrno(L, 2, olderr);
}
}
@ -909,12 +934,7 @@ static int LuaUnixAccept(lua_State *L) {
lua_pushinteger(L, ntohs(sa.sin_port));
return 3;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 4;
return ReturnErrno(L, 3, olderr);
}
}
@ -1016,7 +1036,7 @@ static int LuaUnixSendto(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.shutdown(fd, how) → rc[, errno]
// unix.shutdown(fd, how) → rc:int[, errno:int]
// how can be SHUT_RD, SHUT_WR, or SHUT_RDWR
static int LuaUnixShutdown(lua_State *L) {
int rc, fd, how, olderr;
@ -1149,12 +1169,7 @@ static int LuaUnixSigaction(lua_State *L) {
lua_pushinteger(L, oldsa.sa_mask.__bits[0]);
return 3;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 4;
return ReturnErrno(L, 3, olderr);
}
}
@ -1206,13 +1221,7 @@ static int LuaUnixSetitimer(lua_State *L) {
lua_pushinteger(L, oldit.it_value.tv_usec);
return 4;
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 5;
return ReturnErrno(L, 4, olderr);
}
}
@ -1362,7 +1371,7 @@ static int FreeUnixDir(struct UnixDir *dir) {
return closedir(dir->dir);
}
// UnixDir:close() → rc[, errno]
// UnixDir:close() → rc:int[, errno:int]
// may be called multiple times
// called by the garbage collector too
static int LuaUnixDirClose(lua_State *L) {
@ -1393,13 +1402,7 @@ static int LuaUnixDirRead(lua_State *L) {
} else if (!ent && !errno) {
return 0; // end of listing
} else {
lua_pushnil(L);
lua_pushnil(L);
lua_pushnil(L);
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 5;
return ReturnErrno(L, 4, olderr);
}
}
@ -1414,10 +1417,7 @@ static int LuaUnixDirFd(lua_State *L) {
lua_pushinteger(L, fd);
return 1;
} else {
lua_pushnil(L);
lua_pushinteger(L, errno);
errno = olderr;
return 2;
return ReturnErrno(L, 1, olderr);
}
}
@ -1478,12 +1478,15 @@ static const luaL_Reg kLuaUnix[] = {
{"fork", LuaUnixFork}, // make child process via mitosis
{"execve", LuaUnixExecve}, // replace process with program
{"commandv", LuaUnixCommandv}, // resolve program on $PATH
{"realpath", LuaUnixRealpath}, // abspath without dots/symlinks
{"syslog", LuaUnixSyslog}, // logs to system log
{"kill", LuaUnixKill}, // signal child process
{"raise", LuaUnixRaise}, // signal this process
{"wait", LuaUnixWait}, // wait for child to change status
{"pipe", LuaUnixPipe}, // create two anon fifo fds
{"dup", LuaUnixDup}, // copy fd to lowest empty slot
{"mkdir", LuaUnixMkdir}, // make directory
{"makedirs", LuaUnixMakedirs}, // make directory and parents too
{"rmdir", LuaUnixRmdir}, // remove empty directory
{"opendir", LuaUnixOpendir}, // read directory entry list
{"rename", LuaUnixRename}, // rename file or directory
@ -1494,8 +1497,9 @@ static const luaL_Reg kLuaUnix[] = {
{"fsync", LuaUnixFsync}, // flush open file
{"fdatasync", LuaUnixFdatasync}, // flush open file w/o metadata
{"truncate", LuaUnixTruncate}, // shrink or extend file medium
{"umask", LuaUnixChroot}, // change root directory
{"chroot", LuaUnixGetppid}, // get parent process id
{"ftruncate", LuaUnixTruncate}, // shrink or extend file medium
{"umask", LuaUnixUmask}, // change root directory
{"chroot", LuaUnixChroot}, // get parent process id
{"setrlimit", LuaUnixSetrlimit}, // prevent cpu memory bombs
{"getrlimit", LuaUnixGetrlimit}, // query resource limits
{"getppid", LuaUnixGetppid}, // get parent process id
@ -1506,8 +1510,10 @@ static const luaL_Reg kLuaUnix[] = {
{"setsid", LuaUnixSetsid}, // create a new session id
{"getpid", LuaUnixGetpid}, // get id of this process
{"getuid", LuaUnixGetuid}, // get real user id of process
{"setuid", LuaUnixSetuid}, // set real user id of process
{"getgid", LuaUnixGetgid}, // get real group id of process
{"gettime", LuaUnixGettime}, // get timestamp w/ nano precision
{"setgid", LuaUnixSetgid}, // set real group id of process
{"clock_gettime", LuaUnixGettime}, // get timestamp w/ nano precision
{"nanosleep", LuaUnixNanosleep}, // sleep w/ nano precision
{"socket", LuaUnixSocket}, // create network communication fd
{"socketpair", LuaUnixSocketpair}, // create bidirectional pipe
@ -1685,5 +1691,15 @@ int LuaUnix(lua_State *L) {
LuaSetIntField(L, "ITIMER_PROF", ITIMER_PROF);
LuaSetIntField(L, "ITIMER_VIRTUAL", ITIMER_VIRTUAL);
// syslog() stuff
LuaSetIntField(L, "LOG_EMERG", LOG_EMERG);
LuaSetIntField(L, "LOG_ALERT", LOG_ALERT);
LuaSetIntField(L, "LOG_CRIT", LOG_CRIT);
LuaSetIntField(L, "LOG_ERR", LOG_ERR);
LuaSetIntField(L, "LOG_WARNING", LOG_WARNING);
LuaSetIntField(L, "LOG_NOTICE", LOG_NOTICE);
LuaSetIntField(L, "LOG_INFO", LOG_INFO);
LuaSetIntField(L, "LOG_DEBUG", LOG_DEBUG);
return 1;
}

View file

@ -22,11 +22,11 @@
#include "libc/bits/popcnt.h"
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/issandboxed.h"
#include "libc/calls/math.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/filter.h"
#include "libc/calls/struct/flock.h"
#include "libc/calls/struct/rusage.h"
#include "libc/calls/struct/sigaction.h"
@ -71,6 +71,7 @@
#include "libc/str/str.h"
#include "libc/str/undeflate.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/audit.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/dt.h"
#include "libc/sysv/consts/ex.h"
@ -90,7 +91,6 @@
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/rusage.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/seccomp.h"
#include "libc/sysv/consts/shut.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/so.h"
@ -144,6 +144,7 @@
#include "tool/build/lib/case.h"
#include "tool/build/lib/psk.h"
#include "tool/net/luacheck.h"
#include "tool/net/sandbox.h"
STATIC_STACK_SIZE(0x40000);
STATIC_YOINK("zip_uri_support");
@ -367,7 +368,6 @@ static bool terminated;
static bool uniprocess;
static bool invalidated;
static bool logmessages;
static bool issandboxed;
static bool isinitialized;
static bool checkedmethod;
static bool sslinitialized;
@ -393,6 +393,7 @@ static int zfd;
static int frags;
static int gmtoff;
static int client;
static int sandboxed;
static int changeuid;
static int changegid;
static int isyielding;
@ -1168,10 +1169,10 @@ static void ReportWorkerExit(int pid, int ws) {
static void ReportWorkerResources(int pid, struct rusage *ru) {
char *s, *b = 0;
if (logrusage || LOGGABLE(kLogDebug)) {
AppendResourceReport(&b, ru, "\n");
AppendResourceReport(&b, ru, "\r\n");
if (b) {
if ((s = IndentLines(b, appendz(b).i - 1, 0, 1))) {
LOGF(kLogDebug, "(stat) resource report for pid %d\n%s", pid, s);
LOGF(kLogDebug, "(stat) resource report for pid %d\r\n%s", pid, s);
free(s);
}
free(b);
@ -2217,15 +2218,44 @@ static void *LoadAsset(struct Asset *a, size_t *out_size) {
}
}
static wontreturn void PrintUsage(FILE *f, int rc) {
static const char *GetPagerPath(char path[PATH_MAX]) {
const char *s;
if ((s = commandv("less", path))) return s;
if ((s = commandv("more", path))) return s;
return 0;
}
static wontreturn void PrintUsage(int fd, int rc) {
size_t n;
int pip[2];
const char *p;
struct Asset *a;
if ((a = GetAssetZip("/help.txt", 9)) && (p = LoadAsset(a, &n))) {
fwrite(p, 1, n, f);
free(p);
char *args[2] = {0};
char pathbuf[PATH_MAX];
if (!(a = GetAssetZip("/help.txt", 9)) || !(p = LoadAsset(a, &n))) {
fprintf(stderr, "error: /help.txt is not a zip asset\n");
exit(1);
}
if (isatty(0) && isatty(1) && (args[0] = GetPagerPath(pathbuf))) {
sigaction(SIGPIPE, &(struct sigaction){.sa_handler = SIG_IGN}, 0);
close(0);
pipe(pip);
if (!fork()) {
close(pip[1]);
execv(args[0], args);
_Exit(127);
}
close(0);
WritevAll(pip[1], &(struct iovec){p, n}, 1);
close(pip[1]);
wait(0);
free(p);
exit(0);
} else {
WritevAll(fd, &(struct iovec){p, n}, 1);
free(p);
exit(rc);
}
exit(rc);
}
static void AppendLogo(void) {
@ -3623,7 +3653,7 @@ static void LogMessage(const char *d, const char *s, size_t n) {
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n;
if ((s2 = DecodeLatin1(s, n, &n2))) {
if ((s3 = IndentLines(s2, n2, &n3, 1))) {
INFOF("(stat) %s %,ld byte message\n%.*s", d, n, n3, s3);
INFOF("(stat) %s %,ld byte message\r\n%.*s", d, n, n3, s3);
free(s3);
}
free(s2);
@ -3638,7 +3668,7 @@ static void LogBody(const char *d, const char *s, size_t n) {
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n;
if ((s2 = VisualizeControlCodes(s, n, &n2))) {
if ((s3 = IndentLines(s2, n2, &n3, 1))) {
INFOF("(stat) %s %,ld byte payload\n%.*s", d, n, n3, s3);
INFOF("(stat) %s %,ld byte payload\r\n%.*s", d, n, n3, s3);
free(s3);
}
free(s2);
@ -3772,8 +3802,8 @@ static int LuaFetch(lua_State *L) {
*/
DEBUGF("(ftch) client resolving %s", host);
if ((rc = getaddrinfo(host, port, &hints, &addr)) != EAI_SUCCESS) {
luaL_error(L, "getaddrinfo(%s:%s) error: EAI_%s", host, port,
gai_strerror(rc));
luaL_error(L, "getaddrinfo(%s:%s) error: EAI_%s %s", host, port,
gai_strerror(rc), strerror(errno));
unreachable;
}
@ -5516,11 +5546,6 @@ static const luaL_Reg kLuaFuncs[] = {
{"Underlong", LuaUnderlong}, //
{"VisualizeControlCodes", LuaVisualizeControlCodes}, //
{"Write", LuaWrite}, //
{"bsf", LuaBsf}, //
{"bsr", LuaBsr}, //
{"crc32", LuaCrc32}, //
{"crc32c", LuaCrc32c}, //
{"popcnt", LuaPopcnt}, //
#ifndef UNSECURE
{"Fetch", LuaFetch}, //
{"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, //
@ -6581,13 +6606,90 @@ static void CloseServerFds(void) {
}
static int ExitWorker(void) {
if (IsModeDbg() && !issandboxed) {
if (IsModeDbg() && !sandboxed) {
isexitingworker = true;
return eintr();
}
_Exit(0);
}
static const struct sock_filter kSandboxOnline[] = {
_SECCOMP_MACHINE(AUDIT_ARCH_X86_64), //
_SECCOMP_LOAD_SYSCALL_NR(), //
_SECCOMP_ALLOW_SYSCALL(0x0013), // readv
_SECCOMP_ALLOW_SYSCALL(0x0014), // writev
_SECCOMP_ALLOW_SYSCALL(0x0009), // mmap
_SECCOMP_ALLOW_SYSCALL(0x000b), // munmap
_SECCOMP_ALLOW_SYSCALL(0x0000), // read
_SECCOMP_ALLOW_SYSCALL(0x0001), // write
_SECCOMP_ALLOW_SYSCALL(0x0003), // close
_SECCOMP_ALLOW_SYSCALL(0x0008), // lseek
_SECCOMP_ALLOW_SYSCALL(0x000f), // rt_sigreturn
_SECCOMP_ALLOW_SYSCALL(0x00e7), // exit_group
_SECCOMP_ALLOW_SYSCALL(0x0106), // newfstatat
_SECCOMP_ALLOW_SYSCALL(0x00e4), // clock_gettime
_SECCOMP_ALLOW_SYSCALL(0x003f), // uname
_SECCOMP_ALLOW_SYSCALL(0x0048), // fcntl
_SECCOMP_ALLOW_SYSCALL(0x0029), // socket
_SECCOMP_ALLOW_SYSCALL(0x002a), // connect
_SECCOMP_ALLOW_SYSCALL(0x002c), // sendto
_SECCOMP_ALLOW_SYSCALL(0x002d), // recvfrom
_SECCOMP_ALLOW_SYSCALL(0x0036), // setsockopt
_SECCOMP_LOG_AND_RETURN_ERRNO(1), // EPERM
};
static const struct sock_filter kSandboxOffline[] = {
_SECCOMP_MACHINE(AUDIT_ARCH_X86_64), //
_SECCOMP_LOAD_SYSCALL_NR(), //
_SECCOMP_ALLOW_SYSCALL(0x0013), // readv
_SECCOMP_ALLOW_SYSCALL(0x0014), // writev
_SECCOMP_ALLOW_SYSCALL(0x0000), // read
_SECCOMP_ALLOW_SYSCALL(0x0001), // write
_SECCOMP_ALLOW_SYSCALL(0x0009), // mmap
_SECCOMP_ALLOW_SYSCALL(0x000b), // munmap
_SECCOMP_ALLOW_SYSCALL(0x0003), // close
_SECCOMP_ALLOW_SYSCALL(0x0008), // lseek
_SECCOMP_ALLOW_SYSCALL(0x000f), // rt_sigreturn
_SECCOMP_ALLOW_SYSCALL(0x00e7), // exit_group
_SECCOMP_ALLOW_SYSCALL(0x0106), // newfstatat
_SECCOMP_ALLOW_SYSCALL(0x00e4), // clock_gettime
_SECCOMP_ALLOW_SYSCALL(0x003f), // uname
_SECCOMP_ALLOW_SYSCALL(0x0048), // fcntl
_SECCOMP_LOG_AND_RETURN_ERRNO(1), // EPERM
};
static const struct sock_fprog kSandboxOnlineProg = {
.len = ARRAYLEN(kSandboxOnline),
.filter = kSandboxOnline,
};
static const struct sock_fprog kSandboxOfflineProg = {
.len = ARRAYLEN(kSandboxOffline),
.filter = kSandboxOffline,
};
static int EnableSandbox(void) {
const struct sock_fprog *sandbox;
switch (sandboxed) {
case 0:
return 0;
case 1:
DEBUGF("(stat) applying '%s' sandbox policy", "online");
sandbox = &kSandboxOnlineProg;
break;
default:
DEBUGF("(stat) applying '%s' sandbox policy", "offline");
sandbox = &kSandboxOfflineProg;
break;
}
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != -1 &&
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, sandbox) != -1) {
return 0;
} else {
return -1;
}
}
// returns 0 otherwise -1 if worker needs to unwind stack and exit
static int HandleConnection(size_t i) {
int pid, rc = 0;
@ -6595,6 +6697,7 @@ static int HandleConnection(size_t i) {
if ((client = accept4(servers.p[i].fd, &clientaddr, &clientaddrsize,
SOCK_CLOEXEC)) != -1) {
startconnection = nowl();
VERBOSEF("(srvr) accept %s via %s", DescribeClient(), DescribeServer());
messageshandled = 0;
if (hasonclientconnection && LuaOnClientConnection()) {
close(client);
@ -6607,11 +6710,19 @@ static int HandleConnection(size_t i) {
switch ((pid = fork())) {
case 0:
meltdown = false;
__isworker = true;
connectionclose = false;
if (!IsTiny()) {
if (systrace) __strace = 1;
if (funtrace) ftrace_install();
if (systrace) {
extern unsigned long long __kbirth;
__strace = 1;
__kbirth = rdtsc();
}
if (funtrace) {
ftrace_install();
}
}
CHECK_NE(-1, EnableSandbox());
if (hasonworkerstart) {
CallSimpleHook("OnWorkerStart");
}
@ -6632,7 +6743,6 @@ static int HandleConnection(size_t i) {
if (!pid && !IsWindows()) {
CloseServerFds();
}
VERBOSEF("(srvr) accept %s via %s", DescribeClient(), DescribeServer());
HandleMessages();
DEBUGF("(stat) %s closing after %,ldµs", DescribeClient(),
(long)((nowl() - startconnection) * 1e6L));
@ -6809,7 +6919,7 @@ static void Listen(void) {
INFOF("(srvr) listen http://%hhu.%hhu.%hhu.%hhu:%d", ip >> 24, ip >> 16,
ip >> 8, ip, port);
if (printport && !ports.p[j]) {
printf("%d\n", port);
printf("%d\r\n", port);
fflush(stdout);
}
}
@ -7004,6 +7114,7 @@ static void GetOpts(int argc, char *argv[]) {
bool storeasset = false;
while ((opt = getopt(argc, argv, GETOPTS)) != -1) {
switch (opt) {
CASE('S', ++sandboxed);
CASE('v', ++__log_level);
CASE('s', --__log_level);
CASE('f', funtrace = true);
@ -7014,7 +7125,6 @@ static void GetOpts(int argc, char *argv[]) {
CASE('a', logrusage = true);
CASE('u', uniprocess = true);
CASE('g', loglatency = true);
CASE('S', issandboxed = true);
CASE('m', logmessages = true);
CASE('l', ProgramAddr(optarg));
CASE('H', ProgramHeader(optarg));
@ -7028,7 +7138,7 @@ static void GetOpts(int argc, char *argv[]) {
CASE('c', ProgramCache(ParseInt(optarg)));
CASE('r', ProgramRedirectArg(307, optarg));
CASE('t', ProgramTimeout(ParseInt(optarg)));
CASE('h', PrintUsage(stdout, EXIT_SUCCESS));
CASE('h', PrintUsage(1, EXIT_SUCCESS));
CASE('M', ProgramMaxPayloadSize(ParseInt(optarg)));
#ifndef STATIC
CASE('e', LuaEvalCode(optarg));
@ -7046,7 +7156,7 @@ static void GetOpts(int argc, char *argv[]) {
CASE('K', ProgramFile(optarg, ProgramPrivateKey));
#endif
default:
PrintUsage(stderr, EX_USAGE);
PrintUsage(2, EX_USAGE);
}
}
// if storing asset(s) is requested, don't need to continue
@ -7107,6 +7217,8 @@ void RedBean(int argc, char *argv[]) {
#ifdef STATIC
EventLoop(-1, HEARTBEAT);
#else
GetResolvConf(); // for effect
GetHostsTxt(); // for effect
if (!IsWindows() && isatty(0)) {
ReplEventLoop();
} else {
@ -7127,7 +7239,6 @@ void RedBean(int argc, char *argv[]) {
int main(int argc, char *argv[]) {
if (!IsTiny()) {
setenv("GDB", "", true);
ShowCrashReports();
}
RedBean(argc, argv);

23
tool/net/sandbox.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef COSMOPOLITAN_TOOL_NET_SANDBOX_H_
#define COSMOPOLITAN_TOOL_NET_SANDBOX_H_
#include "libc/calls/struct/bpf.h"
#include "libc/calls/struct/filter.h"
#include "libc/calls/struct/seccomp.h"
// clang-format off
#define _SECCOMP_MACHINE(MAGNUM) \
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)), \
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0), \
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS)
#define _SECCOMP_LOAD_SYSCALL_NR() \
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr))
#define _SECCOMP_ALLOW_SYSCALL(MAGNUM) \
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, MAGNUM, 0, 1), \
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
#define _SECCOMP_LOG_AND_RETURN_ERRNO(MAGNUM) \
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (MAGNUM & SECCOMP_RET_DATA))
#endif /* COSMOPOLITAN_TOOL_NET_SANDBOX_H_ */