summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorNatanael Copa <ncopa@alpinelinux.org>2013-03-08 16:42:03 +0100
committerNatanael Copa <ncopa@alpinelinux.org>2013-03-08 16:51:15 +0100
commit3e2d86693f6e8920c97fe296491e7741e31f922c (patch)
tree1c755422fd27c7f192243e8eb7f7811193efa1f6 /server
parente0cabd6295204fe8a6b54edfc9141302943fdbfb (diff)
downloadprivsep-3e2d86693f6e8920c97fe296491e7741e31f922c.tar.bz2
privsep-3e2d86693f6e8920c97fe296491e7741e31f922c.tar.xz
implement one lua state per connectionHEADmaster
This allows you load various modules into a server lua state and give a performance boost when calling the privileged functions
Diffstat (limited to 'server')
-rw-r--r--server/Makefile25
-rw-r--r--server/conn.c139
-rw-r--r--server/conn.h21
-rw-r--r--server/handler.lua64
-rw-r--r--server/list.h112
-rw-r--r--server/lua-privsep.c96
-rw-r--r--server/lua-privsep.h11
-rwxr-xr-xserver/privsep-serverbin0 -> 27672 bytes
-rw-r--r--server/privsep-server.c35
9 files changed, 503 insertions, 0 deletions
diff --git a/server/Makefile b/server/Makefile
new file mode 100644
index 0000000..0f14b48
--- /dev/null
+++ b/server/Makefile
@@ -0,0 +1,25 @@
+
+COMPILE_PROG = $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $($@-objs) $($@-libs)
+COMPILE_LUALIB = $(CC) $(CFLAGS) $(LDFLAGS) -shared -o $@ $($@-objs) $($@-libs)
+
+CFLAGS ?= -g
+
+PKGCONF ?= pkg-config
+
+LUA_PKG ?= lua
+LUA_CFLAGS := $(shell $(PKGCONF) --cflags $(LUA_PKG))
+LUA_LIBS := $(shell $(PKGCONF) --libs $(LUA_PKG))
+
+LIBEV_LIBS := -lev
+
+privsep-server-objs = privsep-server.o lua-privsep.o conn.o
+privsep-server-libs = $(LUA_LIBS) $(LIBEV_LIBS)
+
+all: privsep-server
+
+privsep-server: $(privsep-server-objs)
+ $(COMPILE_PROG)
+
+clean:
+ rm -f $(privsep-server-objs) privsep-server
+
diff --git a/server/conn.c b/server/conn.c
new file mode 100644
index 0000000..f167cdd
--- /dev/null
+++ b/server/conn.c
@@ -0,0 +1,139 @@
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <ev.h>
+
+#include "list.h"
+#include "conn.h"
+#include "lua-privsep.h"
+
+
+#ifndef DEBUG
+#define log_debug(x) printf("%s\n", x)
+#define log_perror(x) perror(x)
+#endif
+
+static struct ev_io accept_io;
+
+static void conn_free(struct ev_loop *loop, struct conn *rm)
+{
+ int fd = rm->io.fd;
+
+ ev_io_stop(loop, &rm->io);
+ ev_timer_stop(loop, &rm->timeout);
+ close(fd);
+ close_lua(rm);
+ free(rm);
+ log_debug("Connection closed");
+}
+
+static void conn_recv_cb (struct ev_loop *loop, struct ev_io *w,
+ int revents)
+{
+ struct conn *conn = container_of(w, struct conn, io);
+ int len, i;
+ char *args;
+
+ len = recv(conn->io.fd, conn->msg, sizeof(conn->msg) - conn->num_read,
+ MSG_DONTWAIT);
+ if (len < 0 && errno == EAGAIN)
+ return;
+ if (len <= 0)
+ goto err;
+
+ conn->num_read += len;
+ if (conn->num_read >= sizeof(conn->msg))
+ goto err;
+
+ call_lua(conn, conn->msg, conn->num_read);
+ conn->num_read = 0;
+ return;
+
+err:
+ conn_free(loop, conn);
+}
+
+static void conn_timeout_cb (struct ev_loop *loop, struct ev_timer *t,
+ int revents)
+{
+ log_debug("Connection timed out");
+ conn_free(loop, container_of(t, struct conn, timeout));
+}
+
+static void conn_accept_cb(struct ev_loop *loop, struct ev_io *w,
+ int revents)
+{
+ struct conn *conn;
+ struct sockaddr_storage from;
+ socklen_t fromlen = sizeof(from);
+ int fd;
+
+ fd = accept(w->fd, (struct sockaddr *) &from, &fromlen);
+ if (fd < 0) {
+ log_perror("accept");
+ return;
+ }
+ log_debug("New connection");
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ conn = calloc(1, sizeof(struct conn));
+
+ ev_io_init(&conn->io, conn_recv_cb, fd, EV_READ);
+ ev_io_start(loop, &conn->io);
+ ev_timer_init(&conn->timeout, conn_timeout_cb, 10.0, 0.);
+ ev_timer_start(loop, &conn->timeout);
+
+ init_lua(conn);
+}
+
+
+int conn_init(struct ev_loop *loop, const char *socket_path)
+{
+ struct sockaddr_un sun;
+ char *p;
+ int fd;
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strncpy(sun.sun_path, socket_path, sizeof(sun.sun_path));
+
+ /* create the dir */
+ p = strrchr(sun.sun_path, '/');
+ if (p) {
+ *p = '\0';
+ mkdir(sun.sun_path, 0755);
+ *p = '/';
+ }
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ log_perror("socket");
+ return -1;
+ }
+
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ unlink(socket_path);
+ if (bind(fd, (struct sockaddr *) &sun, sizeof(sun)) < 0)
+ goto perr_close;
+
+ if (listen(fd, 5) < 0)
+ goto perr_close;
+
+ ev_io_init(&accept_io, conn_accept_cb, fd, EV_READ);
+ ev_io_start(loop, &accept_io);
+ return 0;
+
+perr_close:
+ log_perror(socket_path);
+ close(fd);
+ return -1;
+
+}
diff --git a/server/conn.h b/server/conn.h
new file mode 100644
index 0000000..f698054
--- /dev/null
+++ b/server/conn.h
@@ -0,0 +1,21 @@
+#ifndef CONN_H
+#define CONN_H
+
+#include <ev.h>
+#include <lua.h>
+
+#ifndef MSG_MAX_SIZE
+#define MSG_MAX_SIZE 16386
+#endif
+
+struct conn {
+ struct ev_io io;
+ struct ev_timer timeout;
+ size_t num_read;
+ lua_State *L;
+ char msg[MSG_MAX_SIZE];
+};
+
+int conn_init(struct ev_loop *loop, const char *socket_path);
+
+#endif
diff --git a/server/handler.lua b/server/handler.lua
new file mode 100644
index 0000000..41a02fb
--- /dev/null
+++ b/server/handler.lua
@@ -0,0 +1,64 @@
+
+--print("DEBUG: got message:", msg)
+
+--ipcmsg = require("json")
+ipcmsg = require("cmsgpack")
+ipcmsg.encode = ipcmsg.pack
+ipcmsg.decode = ipcmsg.unpack
+
+ml = require("ml")
+
+modules_path = "../modules/"
+
+mods = {}
+
+local function ret_error(errmsg)
+ io.stderr:write("ERROR: "..tostring(errmsg).."\n")
+ return ipcmsg.encode{ status = false, errmsg = errmsg}
+end
+
+local function ret_success(...)
+ return ipcmsg.encode{ status = true, errmsg = "success",
+ result = {...} }
+end
+
+reqhandler = {}
+function reqhandler.load(req)
+ -- make sure we dont have any path elements in modname so we cannot pass
+ -- modnames like '../myevilmod'
+ local file = modules_path..string.gsub(req.modname, ".*/", "")..".lua"
+ mods[req.modidx] = dofile(file)
+ if mods[req.modidx] == nil then
+ return ret_error(file)
+ end
+ return true
+end
+
+function reqhandler.call(req)
+ if req.modidx == nil or req.func == nil then
+ return ret_error("failed to call function "..tostring(req.func))
+ end
+ local m = mods[req.modidx]
+ -- check that the func we want exists
+ if type(m[req.func]) ~= "function" then
+ ret_error(func..": not a function in '".. mfile .."'")
+ end
+
+ -- TODO: check permissions here
+-- print("DEBUG: Calling mods["..req.modidx.."]."..req.func)
+
+ -- execute the func and pack the return values into a table
+ return m[req.func](unpack(req.args))
+end
+
+function handler(msg)
+ local req = ipcmsg.decode(msg)
+-- print("DEBUG: req="..ml.tstring(req))
+ if type(req.type) ~= "string" then
+ return ret_error("request type missing")
+ end
+ if type(reqhandler[req.type]) == "function" then
+ return ret_success(reqhandler[req.type](req))
+ end
+ return ret_error("no handler for request type "..req.type)
+end
diff --git a/server/list.h b/server/list.h
new file mode 100644
index 0000000..1fca5e7
--- /dev/null
+++ b/server/list.h
@@ -0,0 +1,112 @@
+/* list.h - Single and double linked list macros
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 or later as
+ * published by the Free Software Foundation.
+ *
+ * See http://www.gnu.org/ for details.
+ *
+ * This is more or less based on the code in the linux kernel. There are
+ * minor differences and this is only a subset of the kernel version.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#ifndef offsetof
+#ifdef __compiler_offsetof
+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_INITIALIZER(l) { .next = &l, .prev = &l }
+
+static inline void list_init(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+static inline int list_hashed(const struct list_head *n)
+{
+ return n->next != n && n->next != NULL;
+}
+
+static inline int list_empty(const struct list_head *n)
+{
+ return !list_hashed(n);
+}
+
+#define list_next(ptr, type, member) \
+ (list_hashed(ptr) ? container_of((ptr)->next,type,member) : NULL)
+
+#define list_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif
diff --git a/server/lua-privsep.c b/server/lua-privsep.c
new file mode 100644
index 0000000..65dc39e
--- /dev/null
+++ b/server/lua-privsep.c
@@ -0,0 +1,96 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "conn.h"
+#include "lua-privsep.h"
+
+#ifndef PRIVSEP_PATH
+#define PRIVSEP_PATH "./"
+#endif
+
+static int traceback (lua_State *L) {
+ if (!lua_isstring(L, 1)) /* 'message' not a string? */
+ return 1; /* keep it intact */
+ fprintf(stderr, "traceback\n");
+ lua_getfield(L, LUA_GLOBALSINDEX, "debug");
+ if (!lua_istable(L, -1)) {
+ fprintf(stderr, "traceback: debug\n");
+ lua_pop(L, 1);
+ return 1;
+ }
+
+ lua_getfield(L, -1, "traceback");
+ if (!lua_isfunction(L, -1)) {
+ fprintf(stderr, "traceback: traceback\n");
+ lua_pop(L, 2);
+ return 1;
+ }
+
+ lua_pushvalue(L, 1); /* pass error message */
+ lua_pushinteger(L, 2); /* skip this function and traceback */
+ lua_call(L, 2, 1); /* call debug.traceback */
+ return 1;
+}
+
+static void lerror(lua_State *L, const char *fmt, ...)
+{
+ va_list argp;
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
+ lua_close(L);
+ exit(EXIT_FAILURE);
+}
+
+int call_lua(struct conn *c, const char *msg, size_t msglen)
+{
+ const char *retbuf;
+ size_t retsize;
+ lua_State *L = c->L;
+
+ lua_getglobal(L, "handler");
+ lua_pushlstring(L, msg, msglen);
+
+
+ if (lua_pcall(L, 1, 1, 0))
+ return luaL_error(L, lua_tostring(L, -1));
+
+ if (!lua_isstring(L, -1))
+ lerror(L, "handler function must return string\n");
+
+ retbuf = lua_tolstring(L, -1, &retsize);
+ return write(c->io.fd, retbuf, retsize);
+}
+
+int init_lua(struct conn *c)
+{
+ const char *luamain = PRIVSEP_PATH "handler.lua";
+ int traceback_index;
+ const char *retbuf;
+ size_t retsize;
+
+ c->L = luaL_newstate();
+ if (c == NULL)
+ return -1;
+ luaL_openlibs(c->L);
+
+ if (luaL_loadfile(c->L, luamain) || lua_pcall(c->L, 0, 0, 0)) {
+ lerror(c->L, "%s: %s", luamain, lua_tostring(c->L, -1));
+ lua_close(c->L);
+ c->L = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+void close_lua(struct conn *c)
+{
+ lua_close(c->L);
+ c->L = NULL;
+}
diff --git a/server/lua-privsep.h b/server/lua-privsep.h
new file mode 100644
index 0000000..2d27a11
--- /dev/null
+++ b/server/lua-privsep.h
@@ -0,0 +1,11 @@
+#ifndef LUA_PRIVSEP_H
+#define LUA_PRIVSEP_H
+
+#include "conn.h"
+#include <lua.h>
+
+int call_lua(struct conn *c, const char *msg, size_t msglen);
+int init_lua(struct conn *c);
+void close_lua(struct conn *c);
+
+#endif
diff --git a/server/privsep-server b/server/privsep-server
new file mode 100755
index 0000000..5be1ad1
--- /dev/null
+++ b/server/privsep-server
Binary files differ
diff --git a/server/privsep-server.c b/server/privsep-server.c
new file mode 100644
index 0000000..76d3f4c
--- /dev/null
+++ b/server/privsep-server.c
@@ -0,0 +1,35 @@
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <ev.h>
+
+#ifndef DEFAULT_SOCKET_PATH
+#define DEFAULT_SOCKET_PATH "/var/run/privsep/root.sock"
+#endif
+
+static void sigint_cb(struct ev_loop *loop, ev_signal *w, int revents)
+{
+ ev_break(loop, EVBREAK_ALL);
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ const char *socket_path = DEFAULT_SOCKET_PATH;
+ static struct ev_loop *loop;
+ static struct ev_signal signal_watcher;
+
+ loop = ev_default_loop(0);
+
+ if (conn_init(loop, socket_path) < 0)
+ return 1;
+
+ ev_signal_init(&signal_watcher, sigint_cb, SIGINT);
+ ev_signal_start(loop, &signal_watcher);
+
+ ev_run(loop, 0);
+ printf("%s\n", "Shutting down.");
+ ev_loop_destroy(loop);
+ return 0;
+}
+