diff --git a/configure.ac b/configure.ac index 773f9eea..9c026c5d 100644 --- a/configure.ac +++ b/configure.ac @@ -514,7 +514,12 @@ dnl ** if test "x$want_capsicum" = "xyes"; then AC_CHECK_LIB(c, cap_enter, [ - AC_DEFINE(HAVE_CAPSICUM,, Build with Capsicum support) + AC_CHECK_LIB(nv, nvlist_create, [ + AC_DEFINE(HAVE_CAPSICUM,, Build with Capsicum support) + LIBS="$LIBS -lnv" + ], [ + want_capsicum="no, nvlist_create not found" + ]) ], [ want_capsicum="no, cap_enter not found" ]) diff --git a/src/core/capsicum.c b/src/core/capsicum.c index 69a379e2..19ca858e 100644 --- a/src/core/capsicum.c +++ b/src/core/capsicum.c @@ -19,21 +19,147 @@ */ #include "module.h" +#include "network.h" #include "signals.h" #include "commands.h" +#include #include +#include +#include +#include #include +static int symbiontfds[2]; + +gboolean capsicum_enabled(void) +{ + u_int mode; + int error; + + error = cap_getmode(&mode); + if (error != 0) + return FALSE; + + if (mode == 0) + return FALSE; + + return TRUE; +} + +int capsicum_net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) +{ + nvlist_t *nvl; + int error, saved_errno, sock; + + /* Send request to the symbiont. */ + nvl = nvlist_create(0); + nvlist_add_binary(nvl, "ip", ip, sizeof(*ip)); + nvlist_add_number(nvl, "port", port); + if (my_ip != NULL) { + /* nvlist_add_binary(3) can't handle NULL values. */ + nvlist_add_binary(nvl, "my_ip", my_ip, sizeof(*my_ip)); + } + error = nvlist_send(symbiontfds[1], nvl); + nvlist_destroy(nvl); + if (error != 0) { + g_warning("nvlist_send: %s", strerror(errno)); + return -1; + } + + /* Receive response. */ + nvl = nvlist_recv(symbiontfds[1], 0); + if (nvl == NULL) { + g_warning("nvlist_recv: %s", strerror(errno)); + return -1; + } + if (nvlist_exists_descriptor(nvl, "sock")) { + sock = nvlist_take_descriptor(nvl, "sock"); + } else { + sock = -1; + } + saved_errno = nvlist_get_number(nvl, "errno"); + nvlist_destroy(nvl); + errno = saved_errno; + + return sock; +} + static void cmd_capsicum(const char *data, SERVER_REC *server, void *item) { command_runsub("capsicum", data, server, item); } +static int start_symbiont(void) +{ + pid_t pid; + nvlist_t *nvl; + IPADDR *ip, *my_ip; + int childfd, error, port, saved_errno, sock; + + error = socketpair(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, symbiontfds); + if (error != 0) { + g_warning("socketpair: %s", strerror(errno)); + return 1; + } + + pid = pdfork(&childfd, PD_CLOEXEC); + if (pid < 0) { + g_warning("pdfork: %s", strerror(errno)); + return 1; + } + + if (pid > 0) { + close(symbiontfds[0]); + return 0; + } + + /* We're the child, running outside the Capsicum sandbox. */ + setproctitle("capsicum symbiont"); + close(symbiontfds[1]); + close(0); + close(1); + close(2); + for (;;) { + /* Receive parameters from the main irssi process. */ + nvl = nvlist_recv(symbiontfds[0], 0); + if (nvl == NULL) + exit(1); + ip = nvlist_get_binary(nvl, "ip", NULL); + port = (int)nvlist_get_number(nvl, "port"); + if (nvlist_exists(nvl, "my_ip")) + my_ip = nvlist_get_binary(nvl, "my_ip", NULL); + else + my_ip = NULL; + + /* Connect. */ + sock = net_connect_ip_handle(ip, port, my_ip); + saved_errno = errno; + + /* Send back the socket fd. */ + nvlist_destroy(nvl); + nvl = nvlist_create(0); + + if (sock != -1) + nvlist_move_descriptor(nvl, "sock", sock); + nvlist_add_number(nvl, "errno", saved_errno); + error = nvlist_send(symbiontfds[0], nvl); + if (error != 0) + exit(1); + nvlist_destroy(nvl); + } +} + static void cmd_capsicum_enter(void) { int error; + error = start_symbiont(); + if (error != 0) { + signal_emit("capability mode failed", 1, strerror(errno)); + return; + } + error = cap_enter(); if (error != 0) { signal_emit("capability mode failed", 1, strerror(errno)); diff --git a/src/core/capsicum.h b/src/core/capsicum.h index 75c70080..f8cec28e 100644 --- a/src/core/capsicum.h +++ b/src/core/capsicum.h @@ -1,6 +1,9 @@ #ifndef __CAPSICUM_H #define __CAPSICUM_H +gboolean capsicum_enabled(void); +int capsicum_net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip); + void capsicum_init(void); void capsicum_deinit(void); diff --git a/src/core/network.c b/src/core/network.c index 3e1b7c70..b182d19f 100644 --- a/src/core/network.c +++ b/src/core/network.c @@ -20,6 +20,9 @@ #include "module.h" #include "network.h" +#ifdef HAVE_CAPSICUM +#include "capsicum.h" +#endif #include @@ -144,8 +147,7 @@ GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) return net_connect_ip(ip, port, my_ip); } -/* Connect to socket with ip address */ -GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) +int net_connect_ip_handle(IPADDR *ip, int port, IPADDR *my_ip) { union sockaddr_union so; int handle, ret, opt = 1; @@ -161,7 +163,7 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) handle = socket(ip->family, SOCK_STREAM, 0); if (handle == -1) - return NULL; + return -1; /* set socket options */ fcntl(handle, F_SETFL, O_NONBLOCK); @@ -176,7 +178,7 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) close(handle); errno = old_errno; - return NULL; + return -1; } } @@ -190,9 +192,30 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) int old_errno = errno; close(handle); errno = old_errno; - return NULL; + return -1; } + return handle; +} + + +/* Connect to socket with ip address */ +GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip) +{ + int handle = -1; + +#ifdef HAVE_CAPSICUM + if (capsicum_enabled()) + handle = capsicum_net_connect_ip(ip, port, my_ip); + else + handle = net_connect_ip_handle(ip, port, my_ip); +#else + handle = net_connect_ip_handle(ip, port, my_ip); +#endif + + if (handle == -1) + return (NULL); + return g_io_channel_new(handle); } diff --git a/src/core/network.h b/src/core/network.h index 8757f78c..5ccacc0d 100644 --- a/src/core/network.h +++ b/src/core/network.h @@ -36,6 +36,8 @@ GIOChannel *g_io_channel_new(int handle); /* returns 1 if IPADDRs are the same */ int net_ip_compare(IPADDR *ip1, IPADDR *ip2); +int net_connect_ip_handle(IPADDR *ip, int port, IPADDR *my_ip); + /* Connect to socket */ GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) G_GNUC_DEPRECATED; /* Connect to socket with ip address and SSL*/