mooa

Lua + lubev + sandboxing
git clone https://code.literati.org/mooa.git
Log | Files | Refs | README | LICENSE

commit e7538da6786fb85cc2bee624688013793ca0de01
parent 7df84f224e8f9f6a59c5ec1e7594a6ccb839243f
Author: Sean Lynch <seanl@literati.org>
Date:   Tue, 24 Jun 2014 23:25:02 -0700

Fully working main.lua

Diffstat:
MMakefile | 4++--
Mdns.c | 3+++
Mmain.lua | 7+++++--
Mmooa.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msocket.c | 1+
Msuidjail.c | 113+++++++++++++++++++++++--------------------------------------------------------
Mtask.c | 58+++++++++++++++++++++++++++-------------------------------
Mutils.c | 5+++--
8 files changed, 150 insertions(+), 124 deletions(-)

diff --git a/Makefile b/Makefile @@ -4,7 +4,7 @@ CFLAGS += -std=c99 -g -O0 \ -DVERSION=\"$(shell git describe)\" LINT=splint LUA_CFLAGS=$(pkg-config --cflags lua) -LUA_LIBS=$(shell pkg-config --libs lua) -lev +LIBS=$(shell pkg-config --libs lua) -lev -ludns OBJECTS=mooa.o dns.o http.o socket.o task.o utils.o http_parser.o LDLIBS = -lseccomp LDFLAGS += -pie -Wl,--as-needed,-z,relro,-z,now @@ -26,7 +26,7 @@ all: mooa suidjail suidjail: suidjail.c mooa: $(OBJECTS) - ${CC} -o $@ ${LIBS} $^ -ludns + ${CC} -o $@ ${LIBS} ${LDLIBS} $^ clean: rm -f mooa suidjail $(OBJECTS) diff --git a/dns.c b/dns.c @@ -1,5 +1,6 @@ #include <arpa/inet.h> #include <stdlib.h> +#include <assert.h> #include <lua.h> #include <lualib.h> @@ -83,6 +84,8 @@ static mooa_dns_state_t *mooa_dns_get_state(lua_State *L) { static void mooa_dns_context_destroy(mooa_dns_context_t *context, struct ev_loop *loop) { + assert(loop); + assert(context); ev_timer_stop(loop, &context->timer); dns_free(context->ctx); free(context); diff --git a/main.lua b/main.lua @@ -23,19 +23,22 @@ end function test_connect() parser = http.response_parser() s = socket.socket() + print("Socket:") utils.dump(s) - s:connect("66.181.143.21", 80) + s:connect("72.14.185.21", 80) s:write("GET / HTTP/1.0\nHost: www.literati.org\n\n") + callbacks = get_http_callbacks() repeat data = s:read(1024) - callbacks = get_http_callbacks() parser:execute(data, callbacks) until data == "" + print("Headers:") utils.dump(callbacks.get_headers()) end function test_dns() result = dns.gethostbyname("setsuna.literati.org") + print("dns result:") utils.dump(result) end diff --git a/mooa.c b/mooa.c @@ -1,10 +1,19 @@ +#define _GNU_SOURCE + #include <stdlib.h> #include <stdio.h> +#include <getopt.h> +#include <err.h> +#include <string.h> +#include <unistd.h> +#include <stdbool.h> #include <lua.h> #include <lualib.h> #include <lauxlib.h> +#include <seccomp.h> + #include "dns.h" #include "http.h" #include "socket.h" @@ -17,6 +26,10 @@ typedef struct { } mooa_alloc_state_t; +#define check(X) do { int rc = X; \ + if (rc < 0) errx(1, "%s:%d: \"%s\":\n%s", __FILE__, __LINE__, #X, \ + strerror((int)-rc)); } while (false) + static /*@only@*/ /*@null@*/ void *mooa_alloc(void *ud, /*@only@*/ void *ptr, size_t osize, size_t nsize) { mooa_alloc_state_t *state = (mooa_alloc_state_t *)ud; @@ -27,11 +40,14 @@ static /*@only@*/ /*@null@*/ void *mooa_alloc(void *ud, /*@only@*/ void *ptr, state->used -= osize; return NULL; } else { - if (state->used + (nsize - osize) > state->limit) - return NULL; - ptr = realloc(ptr, nsize); - if (ptr != NULL) - state->used += (nsize - osize); + if (state->used + (nsize - osize) > state->limit) return NULL; + if (ptr) { + ptr = realloc(ptr, nsize); + } else { + ptr = calloc(nsize, 1); + } + + if (ptr) state->used += (nsize - osize); return ptr; } } @@ -74,6 +90,28 @@ static void mooa_state_makesafe(lua_State *L) { lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); } +static int get_syscall_nr(const char *name) { + int result = seccomp_syscall_resolve_name(name); + if (result == __NR_SCMP_ERROR) { + errx(EXIT_FAILURE, "non-existent syscall: %s", name); + } + return result; +} + +static void install_seccomp_filter(const char *syscalls[]) { + scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_TRACE(1)); + if (!ctx) { + errx(EXIT_FAILURE, "Failed to init seccomp"); + } + + for (int i = 0; ; i++) { + const char *syscall = syscalls[i]; + if (!syscall) break; + check(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, get_syscall_nr(syscall), 0)); + } + + check(seccomp_load(ctx)); +} static int mooa_panic(lua_State *L) { const char *message = lua_tostring(L, -1); @@ -87,7 +125,7 @@ static int mooa_panic(lua_State *L) { } -static lua_State *mooa_state_new() { +static lua_State *mooa_state_new(void) { lua_State *L; mooa_alloc_state_t *state = (mooa_alloc_state_t *)calloc( @@ -117,8 +155,38 @@ static lua_State *mooa_state_new() { } +static void drop_root_privs(void) { + check(setegid(65534)); + check(seteuid(65534)); +} + + +static void mooa_chroot(const char *new_root) { + check(seteuid(0)); + check(setegid(0)); + check(chroot(new_root)); + check(chdir("/")); + drop_root_privs(); +} + + int main(void) { lua_State *L; + const char *whitelist[] = { + "brk", + "epoll_wait", + "fstat", + "gettid", + "mmap", + "rt_sigaction", + "rt_sigprocmask", + "tgkill", + "write", + NULL + }; + + // Drop root privs temporarily + //drop_root_privs(); L = mooa_state_new(); @@ -128,6 +196,9 @@ int main(void) { return 1; } + /* mooa_chroot("./chroot"); */ + /* install_seccomp_filter(whitelist); */ + mooa_task_spawn(L); /* Start the scheduler */ diff --git a/socket.c b/socket.c @@ -4,6 +4,7 @@ #include <string.h> #include <sys/socket.h> #include <unistd.h> +#include <err.h> #include <lua.h> #include <lualib.h> diff --git a/suidjail.c b/suidjail.c @@ -85,14 +85,6 @@ static void copy_pipe_to(int in_fd, int out_fd) { } while (n != 0); } -static int get_syscall_nr(const char *name) { - int result = seccomp_syscall_resolve_name(name); - if (result == __NR_SCMP_ERROR) { - errx(EXIT_FAILURE, "non-existent syscall: %s", name); - } - return result; -} - __attribute__((noreturn)) static void usage(FILE *out) { fprintf(out, "usage: %s [options] [command ...]\n", program_invocation_short_name); fputs("Options:\n" @@ -100,9 +92,7 @@ __attribute__((noreturn)) static void usage(FILE *out) { " -v, --version display version\n" " -u, --user=USER the user to run the program as\n" " -n, --hostname=NAME the hostname to set the container to\n" - " -m, --memory-limit=LIMIT the memory limit of the container\n" - " -s, --syscalls=LIST comma-separated whitelist of syscalls\n" - " --syscalls-file=PATH whitelist file containing one syscall name per line\n", + " -m, --memory-limit=LIMIT the memory limit of the container\n", out); exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); @@ -220,22 +210,15 @@ static void trace_child(pid_t pid) { check(ptrace(PTRACE_CONT, pid, NULL, NULL)); } -static void trace_trap(pid_t pid) { - siginfo_t si; - check(ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)); - if (si.si_signo != SIGTRAP || (si.si_code & SIGTRAP) != SIGTRAP) { - errx(EXIT_FAILURE, "Expected SIGTRAP, got signal %d and code %d", - si.si_signo, si.si_code); - } - - int event = si.si_code >> 8; +static void trace_trap(pid_t pid, int status) { long syscall; - unsigned long msg; + int event = status >> 16; + + switch (event) { case PTRACE_EVENT_CLONE: case PTRACE_EVENT_FORK: case PTRACE_EVENT_VFORK: - check(ptrace(PTRACE_GETEVENTMSG, pid, NULL, &msg)); check(ptrace(PTRACE_CONT, pid, NULL, NULL)); break; case PTRACE_EVENT_EXEC: @@ -261,18 +244,12 @@ int main(int argc, char **argv) { const char *memory_limit = "128M"; const char *username = "nobody"; const char *hostname = "mooa"; - char *devices = NULL; - char *syscalls = NULL; - const char *syscalls_file = NULL; - int syscalls_from_file[500]; // upper bound on the number of syscalls static const struct option opts[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, { "hostname", required_argument, 0, 'n' }, { "memory-limit", required_argument, 0, 'm' }, - { "syscalls", required_argument, 0, 's' }, - { "syscalls-file", required_argument, 0, 0x100 }, { 0, 0, 0, 0 } }; @@ -296,15 +273,6 @@ int main(int argc, char **argv) { case 'm': memory_limit = optarg; break; - case 'd': - devices = optarg; - break; - case 's': - syscalls = optarg; - break; - case 0x100: - syscalls_file = optarg; - break; default: usage(stderr); } @@ -314,21 +282,6 @@ int main(int argc, char **argv) { usage(stderr); } - if (syscalls_file) { - char name[30]; // longest syscall name - FILE *file = fopen(syscalls_file, "r"); - if (!file) err(EXIT_FAILURE, "failed to open syscalls file: %s", - syscalls_file); - size_t i = 0; - while (fgets(name, sizeof name / sizeof name[0], file)) { - char *pos; - if ((pos = strchr(name, '\n'))) *pos = '\0'; - syscalls_from_file[i++] = get_syscall_nr(name); - } - syscalls_from_file[i] = -1; - fclose(file); - } - int epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd < 0) { err(1, "epoll"); @@ -453,7 +406,7 @@ int main(int argc, char **argv) { for (i = 0; i < n; ++i) { struct epoll_event *evt = &events[i]; - siginfo_t s; + int status; if (evt->events & EPOLLERR || evt->events & EPOLLHUP) { warnx("Closing fd %d", evt->data.fd); @@ -473,36 +426,34 @@ int main(int argc, char **argv) { /* Because signals can be coalesced, we can't expect to receive an event for each one. So let's call waitid in a loop. */ - while (1) { - s.si_pid = 0; - check(waitid(P_ALL, 0, &s, WEXITED|WSTOPPED|WNOHANG)); - if (s.si_pid == 0) { - break; - } - - switch (s.si_code) { - case CLD_EXITED: - case CLD_KILLED: - case CLD_DUMPED: - if (s.si_pid == pid) { - errx(s.si_status, "Child exited with status %d", - s.si_status); + while (waitpid((pid_t)si.ssi_pid, &status, WNOHANG) == + (pid_t)si.ssi_pid) { + if (WIFEXITED(status)) { + if ((pid_t)si.ssi_pid == pid) { + errx(WEXITSTATUS(status), "Child %d exited with status %d", + si.ssi_pid, WEXITSTATUS(status)); + } + } else if (WIFSIGNALED(status)) { + if ((pid_t)si.ssi_pid == pid) { + errx(EXIT_FAILURE, "Child %d terminated by signal %d", + si.ssi_pid, WTERMSIG(status)); } - break; - case CLD_TRAPPED: - if (s.si_status == SIGSTOP) { - trace_child(s.si_pid); - } else { - trace_trap(s.si_pid); + } else if (WIFSTOPPED(status)) { + switch (WSTOPSIG(status)) { + case SIGSTOP: + trace_child((pid_t)si.ssi_pid); + break; + case SIGTRAP: + trace_trap((pid_t)si.ssi_pid, status); + break; + default: + warnx("Delivering signal %d to child %d", WSTOPSIG(status), + si.ssi_pid); + ptrace(PTRACE_CONT, si.ssi_pid, NULL, WSTOPSIG(status)); } - break; - case CLD_STOPPED: - errx(EXIT_FAILURE, "Child %d stopped with status %d", - s.si_pid, s.si_status); - default: - errx(s.si_status, - "Got SIGCHLD with code %d and status %d\n", - s.si_code, s.si_status); + } else { + errx(EXIT_FAILURE, "Got unexpected status %d for child %d", + status, si.ssi_pid); } } } else { diff --git a/task.c b/task.c @@ -11,6 +11,7 @@ /* libev */ #include <ev.h> +#include "task.h" #include "utils.h" @@ -29,15 +30,9 @@ typedef struct mooa_task_state { } mooa_task_state_t; -static int mooa_task_state_finalize(lua_State *L) { - mooa_task_state_t *state = lua_touserdata(L, 1); - return 0; -} - - void mooa_task_init_state(lua_State *L) { luaL_Reg state_funcs[] = { - {"__gc", mooa_task_state_finalize}, + // {"__gc", mooa_task_state_finalize}, {NULL, NULL} }; @@ -55,7 +50,7 @@ void mooa_task_init_state(lua_State *L) { } -mooa_task_state_t *mooa_task_get_state(lua_State *L) { +static mooa_task_state_t *mooa_task_get_state(lua_State *L) { mooa_task_state_t *state; assert(L != NULL); lua_getfield(L, LUA_REGISTRYINDEX, "mooa_task_state"); @@ -67,7 +62,7 @@ mooa_task_state_t *mooa_task_get_state(lua_State *L) { /* Get the task corresponding to the Lua thread. */ -mooa_task_t *mooa_task_get(lua_State *L) { +static mooa_task_t *mooa_task_get(lua_State *L) { mooa_task_t *task; lua_rawgetp(L, LUA_REGISTRYINDEX, L); task = lua_touserdata(L, -1); @@ -89,14 +84,15 @@ struct ev_loop *mooa_task_get_loop(lua_State *L) { static void mooa_task_schedule(mooa_task_t *task, int nargs) { mooa_task_state_t *state = mooa_task_get_state(task->coro); + warnx("Scheduling task %p", task); task->nargs = nargs; task->next = NULL; - if (state->last_runnable_task != NULL) { - state->last_runnable_task->next = task; + if (state->runnable_tasks == NULL) { + state->runnable_tasks = task; state->last_runnable_task = task; } else { - assert(state->runnable_tasks == NULL); - state->runnable_tasks = task; + assert(state->last_runnable_task != NULL); + state->last_runnable_task->next = task; state->last_runnable_task = task; } } @@ -132,33 +128,34 @@ static void mooa_task_delete(mooa_task_t *task) { } -static bool mooa_task_step(mooa_task_t *task) { +static void mooa_task_step(mooa_task_t *task) { assert(task->coro != NULL); int result = lua_resume(task->coro, NULL, task->nargs); switch (result) { case LUA_OK: /* Task completed. */ - return false; + mooa_task_delete(task); + break; case LUA_YIELD: lua_settop(task->coro, 0); - return true; + break; case LUA_ERRRUN: /* TODO: do something with the error */ fprintf(stderr, "Runtime error\n"); lua_error(task->coro); - return false; + break; case LUA_ERRMEM: fprintf(stderr, "Memory error\n"); lua_error(task->coro); - return false; + break; case LUA_ERRERR: fprintf(stderr, "Error running message handler\n"); lua_error(task->coro); - return false; + break; case LUA_ERRGCMM: fprintf(stderr, "Error running GC metamethod\n"); lua_error(task->coro); - return false; + break; default: fprintf(stderr, "Unknown error\n"); abort(); @@ -172,7 +169,6 @@ static void mooa_task_timer_cb(struct ev_loop *loop, ev_timer *timer, } - static void mooa_task_io_cb(struct ev_loop *loop, ev_io *io, int revents) { ev_io_stop(loop, io); mooa_task_schedule((mooa_task_t *)io->data, 0); @@ -205,20 +201,20 @@ static mooa_task_t *mooa_task_new(lua_State *L) { void mooa_task_loop(lua_State *L) { mooa_task_state_t *state = mooa_task_get_state(L); struct ev_loop *loop = mooa_task_get_loop(L); - mooa_task_t *task, **next; + mooa_task_t *task, *last; bool running; do { - running = ev_run(loop, state->runnable_tasks ? EVRUN_NOWAIT : 0); - next = &state->runnable_tasks; - while ((task = *next) != NULL) { - if (mooa_task_step(task)) { - next = &task->next; - } else { - *next = task->next; - mooa_task_delete(task); - } + last = state->last_runnable_task; + if (last != NULL) { + do { + task = state->runnable_tasks; + state->runnable_tasks = task->next; + mooa_task_step(task); + } while (task != last); } + + running = ev_run(loop, state->runnable_tasks ? EVRUN_NOWAIT : 0); } while (running || state->runnable_tasks); } diff --git a/utils.c b/utils.c @@ -1,4 +1,5 @@ #include <stdio.h> +#include <err.h> #include <lua.h> #include <lualib.h> @@ -25,10 +26,10 @@ void mooa_print_string(lua_State *L, int index) { c = str[i]; if (c >= 0x20 && c <= 0x7e) { /* Printable */ - buf[i] = c; + buf[j] = c; j++; } else { - j += snprintf(buf, sizeof(buf) - j, "0x%x", c) - 1; + j += snprintf(&buf[j], sizeof(buf) - j, "\\x%02x", c); } }