diff options
author | Natanael Copa <ncopa@alpinelinux.org> | 2013-03-08 16:42:03 +0100 |
---|---|---|
committer | Natanael Copa <ncopa@alpinelinux.org> | 2013-03-08 16:51:15 +0100 |
commit | 3e2d86693f6e8920c97fe296491e7741e31f922c (patch) | |
tree | 1c755422fd27c7f192243e8eb7f7811193efa1f6 | |
parent | e0cabd6295204fe8a6b54edfc9141302943fdbfb (diff) | |
download | privsep-master.tar.bz2 privsep-master.tar.xz |
This allows you load various modules into a server lua state and give
a performance boost when calling the privileged functions
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | bin/conn.h | 8 | ||||
-rw-r--r-- | bin/lua-privsep.h | 6 | ||||
-rw-r--r-- | client/Makefile | 23 | ||||
-rwxr-xr-x | client/client.so | bin | 0 -> 13897 bytes | |||
-rw-r--r-- | client/list.h (renamed from bin/list.h) | 0 | ||||
-rw-r--r-- | client/lua-client.c | 112 | ||||
l--------- | client/modules | 1 | ||||
-rw-r--r-- | client/privsep.lua | 57 | ||||
-rw-r--r-- | client/test.lua | 20 | ||||
-rw-r--r-- | example.lua | 12 | ||||
-rw-r--r-- | privileged-main.lua | 59 | ||||
-rw-r--r-- | privsep.lua | 40 | ||||
-rw-r--r-- | server/Makefile (renamed from bin/Makefile) | 1 | ||||
-rw-r--r-- | server/conn.c (renamed from bin/conn.c) | 20 | ||||
-rw-r--r-- | server/conn.h | 21 | ||||
-rw-r--r-- | server/handler.lua | 64 | ||||
-rw-r--r-- | server/list.h | 112 | ||||
-rw-r--r-- | server/lua-privsep.c (renamed from bin/lua-privsep.c) | 64 | ||||
-rw-r--r-- | server/lua-privsep.h | 11 | ||||
-rwxr-xr-x | server/privsep-server | bin | 0 -> 27672 bytes | |||
-rw-r--r-- | server/privsep-server.c (renamed from bin/privsep-server.c) | 0 |
22 files changed, 483 insertions, 155 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..61f6f65 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +SUBDIRS=server client + + +all: + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir || exit 1; \ + done diff --git a/bin/conn.h b/bin/conn.h deleted file mode 100644 index f5b33a9..0000000 --- a/bin/conn.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CONN_H -#define CONN_H - -#include <ev.h> - -int conn_init(struct ev_loop *loop, const char *socket_path); - -#endif diff --git a/bin/lua-privsep.h b/bin/lua-privsep.h deleted file mode 100644 index 9b7e219..0000000 --- a/bin/lua-privsep.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef LUA_PRIVSEP_H -#define LUA_PRIVSEP_H - -int call_lua(int fd, const char *msg, size_t msglen); - -#endif diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..b318964 --- /dev/null +++ b/client/Makefile @@ -0,0 +1,23 @@ + +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)) + +client.so-objs = lua-client.o +client.so-libs = $(LUA_LIBS) + +all: client.so + +client.so: $(client.so-objs) + $(COMPILE_LUALIB) + +clean: + rm -f client.so + diff --git a/client/client.so b/client/client.so Binary files differnew file mode 100755 index 0000000..a6567bd --- /dev/null +++ b/client/client.so diff --git a/bin/list.h b/client/list.h index 1fca5e7..1fca5e7 100644 --- a/bin/list.h +++ b/client/list.h diff --git a/client/lua-client.c b/client/lua-client.c new file mode 100644 index 0000000..ca4365a --- /dev/null +++ b/client/lua-client.c @@ -0,0 +1,112 @@ + +#include <sys/socket.h> +#include <sys/un.h> + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#define LIBNAME "client" + +#ifndef VERSION +#define VERSION "(unknown)" +#endif + +#ifndef DEFAULT_SOCKET_PATH +#define DEFAULT_SOCKET_PATH "/var/run/privsep/root.sock" +#endif + +#define BUFSIZE 32768 + +static int pusherror(lua_State *L, const char *info) +{ + lua_pushnil(L); + if (info == NULL) + lua_pushstring(L, strerror(errno)); + else + lua_pushfstring(L, "%s: %s", info, strerror(errno)); + lua_pushinteger(L, errno); + return 3; +} + +static int Pconnect(lua_State *L) +{ + const char *socket_path = luaL_optstring(L, 1, DEFAULT_SOCKET_PATH); + struct sockaddr_un sun; + int fd, ret; + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + strncpy(sun.sun_path, socket_path, sizeof(sun.sun_path)); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return pusherror(L, "socket"); + + if (connect(fd, (struct sockaddr *) &sun, sizeof(sun)) < 0) { + ret = pusherror(L, socket_path); + goto close_err; + } + + lua_pushinteger(L, fd); + return 1; + +close_err: + close(fd); + return ret; +} + +static int push_err_or_int(lua_State *L, ssize_t n) +{ + if (n < 0) + return pusherror(L, NULL); + lua_pushinteger(L, n); + return 1; +} + +static int Pwrite(lua_State *L) +{ + int fd = luaL_checkinteger(L, 1); + size_t len = 0; + const char *msg = luaL_checklstring(L, 2, &len); + return push_err_or_int(L, write(fd, msg, len)); +} + +static int Precv(lua_State *L) +{ + int fd = luaL_checkinteger(L, 1); + static char buf[BUFSIZE]; + ssize_t n = recv(fd, buf, sizeof(buf), 0); + if (n < 0) + return pusherror(L, NULL); + lua_pushlstring(L, buf, n); + return 1; +} + +static int Pclose(lua_State *L) +{ + int fd = luaL_checkinteger(L, 1); + return push_err_or_int(L, close(fd)); +} + +static const luaL_reg reg_client_methods[] = { + {"connect", Pconnect}, + {"write", Pwrite}, + {"recv", Precv}, + {"close", Pclose}, + {NULL, NULL}, +}; + + +LUALIB_API int luaopen_client(lua_State *L) +{ + luaL_register(L, LIBNAME, reg_client_methods); + lua_pushliteral(L, "version"); + lua_pushliteral(L, VERSION); + lua_settable(L, -3); + return 1; +} diff --git a/client/modules b/client/modules new file mode 120000 index 0000000..464b823 --- /dev/null +++ b/client/modules @@ -0,0 +1 @@ +../modules
\ No newline at end of file diff --git a/client/privsep.lua b/client/privsep.lua new file mode 100644 index 0000000..432f472 --- /dev/null +++ b/client/privsep.lua @@ -0,0 +1,57 @@ + +ipcmsg = require("cmsgpack") +ipcmsg.encode = ipcmsg.pack +ipcmsg.decode = ipcmsg.unpack + +client = require("client") + +local ml = require("ml") + +local modules_path = "./modules" + +local privsep = {} + +local function call(conn, req) + local n = client.write(conn.fd, ipcmsg.encode(req)) + retmsg, errmsg = client.recv(conn.fd) + if retmsg then + local data = ipcmsg.decode(retmsg) + return unpack(data.result or {}) + end + return nil, errmsg +end + +local function wrap(conn, modname) + local mod = dofile(modules_path.."/"..modname..".lua") + local f = {} + local i = #conn.mods + 1 + local ret = conn:call{ type = "load", modidx = i, + modname = modname } + if ret == nil then + return nil + end + for k,v in pairs(mod) do + f[k] = function(...) + return conn:call{ type = "call", modidx = i, + func = k, args = {...}} + end + end + conn[i] = f + return f +end + +function privsep.connect(token) + return { + ["fd"] = client.connect(), + ["call"] = call, + ["wrap"] = wrap, + ["close"] = function(self) + return client.close(self.fd) + end, + ["mods"] = {}, + } +end + + +return privsep + diff --git a/client/test.lua b/client/test.lua new file mode 100644 index 0000000..38e7429 --- /dev/null +++ b/client/test.lua @@ -0,0 +1,20 @@ + +ps = require("privsep") + +c = ps.connect() + +mymod = c:wrap("mymod") + +if mymod == nil then + print("mymod is nil") +end + +pos = require("posix") + +print(mymod.myfunc("arg1", true)) +for i = 1, 1000 do + mymod.myfunc("foo", false) +end + +c:close() + diff --git a/example.lua b/example.lua deleted file mode 100644 index db9973a..0000000 --- a/example.lua +++ /dev/null @@ -1,12 +0,0 @@ - - -mymod = require("privsep").wrap("mymod", "dummy session token") -posix = require("posix") - -print(mymod.myfunc("foo", false)) - -print("posix.getpid:", posix.getpid().pid) -print("mymod.getpid:", mymod.getpid().pid) - - - diff --git a/privileged-main.lua b/privileged-main.lua deleted file mode 100644 index 6aa9681..0000000 --- a/privileged-main.lua +++ /dev/null @@ -1,59 +0,0 @@ -msg = ... - ---print("DEBUG: got message:", msg) - -ipcmsg = require("cmsgpack") -ipcmsg.encode = ipcmsg.pack -ipcmsg.decode = ipcmsg.unpack - - -function ret_error(errmsg) - io.stderr:write("ERROR: "..tostring(errmsg).."\n") - return ipcmsg.encode{ status = false, errmsg = errmsg} -end - -function ret_success(result) - return ipcmsg.encode{ status = true, errmsg = "success", result = result} -end - -req = ipcmsg.decode(msg) - ---print("DEBUG: msg decoded") - --- path must be absolute for production so users cannot load scripts from --- non secured dirs -modules_path = "./modules/" - -if type(req.mod) ~= "string" then - return ret_error("mod is missing in message or is bad format") -end - -if type(req.func) ~= "string" then - return ret_error("func is missing in message or is wrong format") -end - --- make sure we dont have any path elements in modname so we cannot pass --- modnames like '../myevilmod' -mfile = modules_path..string.gsub(req.mod, ".*/", "")..".lua" - --- load the module -m = dofile(mfile) ---print("DEBUG: mfile:", mfile) ---print("DEBUG: '"..req.func.."' type:", type(m[req.func])) - --- 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("Calling '"..req.mod.."."..req.func.."'") - ---print("DEBUG: args:", req.args) --- execute the func and pack the return values into a table -result = { m[req.func](unpack(req.args)) } ---result = { m[func](unpack(req.args or {})) } - ---print("DEBUG: result:", result) - -return ret_success(result) diff --git a/privsep.lua b/privsep.lua deleted file mode 100644 index b6e8f95..0000000 --- a/privsep.lua +++ /dev/null @@ -1,40 +0,0 @@ - -ipcmsg = require("cmsgpack") -ipcmsg.encode = ipcmsg.pack -ipcmsg.decode = ipcmsg.unpack - -socket = require("socket") -socket.unix = require("socket.unix") - - -local modules_path = "./modules" - -local privsep = {} - -function privsep.call_privileged(mod, func, sectoken, args) - local c = assert(socket.unix()) - assert(c:connect("/var/run/privsep/root.sock")) - - local req = { mod = mod, func = func, args = args, sectoken = sectoken } - c:send(ipcmsg.encode(req)) - local retmsg, errmsg = c:receive("*a") - if retmsg then - local data = ipcmsg.decode(retmsg) - return unpack(data.result or {}) - end - return nil -end - -function privsep.wrap(modname, sessionid) - local mod = dofile(modules_path.."/"..modname..".lua") - local f = {} - for k,v in pairs(mod) do - f[k] = function(...) - return privsep.call_privileged(modname, k, sessionid, {...}) - end - end - return f -end - -return privsep - diff --git a/bin/Makefile b/server/Makefile index 07a522a..0f14b48 100644 --- a/bin/Makefile +++ b/server/Makefile @@ -1,5 +1,6 @@ COMPILE_PROG = $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $($@-objs) $($@-libs) +COMPILE_LUALIB = $(CC) $(CFLAGS) $(LDFLAGS) -shared -o $@ $($@-objs) $($@-libs) CFLAGS ?= -g diff --git a/bin/conn.c b/server/conn.c index f953d09..f167cdd 100644 --- a/bin/conn.c +++ b/server/conn.c @@ -22,19 +22,8 @@ #define log_perror(x) perror(x) #endif -#ifndef MSG_MAX_SIZE -#define MSG_MAX_SIZE 16386 -#endif - static struct ev_io accept_io; -struct conn { - struct ev_io io; - struct ev_timer timeout; - size_t num_read; - char msg[MSG_MAX_SIZE]; -}; - static void conn_free(struct ev_loop *loop, struct conn *rm) { int fd = rm->io.fd; @@ -42,6 +31,7 @@ static void conn_free(struct ev_loop *loop, struct conn *rm) ev_io_stop(loop, &rm->io); ev_timer_stop(loop, &rm->timeout); close(fd); + close_lua(rm); free(rm); log_debug("Connection closed"); } @@ -63,8 +53,10 @@ static void conn_recv_cb (struct ev_loop *loop, struct ev_io *w, conn->num_read += len; if (conn->num_read >= sizeof(conn->msg)) goto err; - - call_lua(conn->io.fd, conn->msg, conn->num_read); + + call_lua(conn, conn->msg, conn->num_read); + conn->num_read = 0; + return; err: conn_free(loop, conn); @@ -98,6 +90,8 @@ static void conn_accept_cb(struct ev_loop *loop, struct ev_io *w, 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); } 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/bin/lua-privsep.c b/server/lua-privsep.c index d6d750c..65dc39e 100644 --- a/bin/lua-privsep.c +++ b/server/lua-privsep.c @@ -1,4 +1,7 @@ +#include <stdarg.h> #include <stdio.h> +#include <stdlib.h> + #include <lua.h> #include <lauxlib.h> @@ -35,32 +38,59 @@ static int traceback (lua_State *L) { return 1; } -int call_lua(int fd, const char *msg, size_t msglen) +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 *luamain = PRIVSEP_PATH "privileged-main.lua"; - int traceback_index; const char *retbuf; size_t retsize; + lua_State *L = c->L; - lua_State *L = luaL_newstate(); - luaL_openlibs(L); - - lua_pushcfunction(L, traceback); - traceback_index = lua_gettop(L); - - if (luaL_loadfile(L, luamain)) - return luaL_error(L, "%s", luamain); - + lua_getglobal(L, "handler"); lua_pushlstring(L, msg, msglen); - if (lua_pcall(L, 1, 1, traceback_index)) - return luaL_error(L, "error"); + if (lua_pcall(L, 1, 1, 0)) + return luaL_error(L, lua_tostring(L, -1)); if (!lua_isstring(L, -1)) - error(L, "function must return string"); - + lerror(L, "handler function must return string\n"); + retbuf = lua_tolstring(L, -1, &retsize); - return write(fd, retbuf, 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 Binary files differnew file mode 100755 index 0000000..5be1ad1 --- /dev/null +++ b/server/privsep-server diff --git a/bin/privsep-server.c b/server/privsep-server.c index 76d3f4c..76d3f4c 100644 --- a/bin/privsep-server.c +++ b/server/privsep-server.c |