summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2016-02-23 03:07:51 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2016-02-23 05:50:12 +0100
commitb089348a6dc558350390e163389a6d590c396ac7 (patch)
tree48a47e484557e65251d20a82f6a0e47eee8713de
parentFix build warnings (diff)
downloadmulder-listen-daemon-b089348a6dc558350390e163389a6d590c396ac7.tar.xz
mulder-listen-daemon-b089348a6dc558350390e163389a6d590c396ac7.zip
Use epoll and bpf
-rw-r--r--Makefile9
-rw-r--r--mulderd.c154
-rw-r--r--seccomp-bpf.h44
3 files changed, 165 insertions, 42 deletions
diff --git a/Makefile b/Makefile
index 9d061f7..bee96a9 100644
--- a/Makefile
+++ b/Makefile
@@ -1 +1,8 @@
-mulderd:
+CFLAGS ?= -march=native -O3 -fomit-frame-pointer -pipe
+CFLAGS += -std=c11 -Wall
+
+mulderd: mulderd.c seccomp-bpf.h
+clean:
+ rm -f mulderd
+
+.PHONY: clean
diff --git a/mulderd.c b/mulderd.c
index ba7a7eb..165dc74 100644
--- a/mulderd.c
+++ b/mulderd.c
@@ -22,6 +22,7 @@
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
+#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/prctl.h>
@@ -29,8 +30,9 @@
#include <sys/resource.h>
#include <arpa/inet.h>
#include <netinet/in.h>
+#include "seccomp-bpf.h"
-static char truth[] = "\033[32;1m\n\n"
+static const char truth[] = "\033[32;1m\n\n"
" _,--=--._\n"
" ,' _ `.\n"
" - _(_)_o -\n"
@@ -42,14 +44,17 @@ static char truth[] = "\033[32;1m\n\n"
"\n\n\n"
" THE TRUTH IS OUT THERE\n\n\033[0m";
-/*
- * Drops us into a chroot, if possible, and drops privs.
- */
-void drop_privileges()
+
+enum {
+ LISTEN_BACKLOG = 16,
+ EPOLL_EVENTS = 128
+};
+
+static void drop_privileges(void)
{
struct passwd *user;
struct rlimit limit;
-
+
if (!geteuid()) {
user = getpwnam("nobody");
if (!user) {
@@ -77,29 +82,74 @@ void drop_privileges()
exit(EXIT_FAILURE);
}
}
+ limit.rlim_cur = limit.rlim_max = 4194304 /* 4 megs */;
+ setrlimit(RLIMIT_DATA, &limit);
+ setrlimit(RLIMIT_FSIZE, &limit);
+ setrlimit(RLIMIT_MEMLOCK, &limit);
+ setrlimit(RLIMIT_STACK, &limit);
+ limit.rlim_cur = limit.rlim_max = 15728640 /* 15 megabytes */;
+ setrlimit(RLIMIT_AS, &limit);
limit.rlim_cur = limit.rlim_max = 0;
setrlimit(RLIMIT_CORE, &limit);
setrlimit(RLIMIT_NPROC, &limit);
+
if (!geteuid() || !getegid()) {
- fprintf(stderr, "Mysteriously still running as root... Goodbye.\n");
+ fprintf(stderr, "Error: unable to drop privileges.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ perror("prctl(NO_NEW_PRIVS)");
+ exit(EXIT_FAILURE);
+ }
+
+ if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
+ perror("prctl(PR_SET_DUMPABLE)");
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+static void seccomp_enable_filter(void)
+{
+ struct sock_filter filter[] = {
+ VALIDATE_ARCHITECTURE,
+ EXAMINE_SYSCALL,
+ ALLOW_SYSCALL(exit_group),
+ ALLOW_SYSCALL(exit),
+ ALLOW_SYSCALL(read),
+ ALLOW_SYSCALL(write),
+ ALLOW_SYSCALL(epoll_wait),
+ ALLOW_SYSCALL(epoll_ctl),
+ ALLOW_SYSCALL(accept4),
+ ALLOW_SYSCALL(close),
+ ALLOW_SYSCALL(mmap),
+ ALLOW_SYSCALL(fstat),
+ KILL_PROCESS
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
+ .filter = filter
+ };
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+ perror("prctl(SECCOMP)");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
- int listen_fd, connection_fd, flag;
+ int listen_fd, epoll_fd, num_events, i, flag;
struct sockaddr_in6 listen_addr;
struct sockaddr_storage connection_addr;
- struct timeval timeout;
char ipaddr[INET6_ADDRSTRLEN];
struct in6_addr *v6;
time_t now;
socklen_t connection_addr_len;
- pid_t child;
int daemonize = 0, option_index = 0, port = 17, connection_file = -1, option;
- char *pid_file = 0, *connection_log = 0, *time_str;
+ char *pid_file = 0, *connection_log = 0;
FILE *pidfile;
+ struct epoll_event event, events[EPOLL_EVENTS] = { 0 };
static struct option long_options[] = {
{"daemonize", no_argument, NULL, 'd'},
@@ -163,7 +213,7 @@ int main(int argc, char *argv[])
setbuf(stderr, NULL);
}
- listen_fd = socket(AF_INET6, SOCK_STREAM, 0);
+ listen_fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (listen_fd < 0) {
perror("socket");
return EXIT_FAILURE;
@@ -181,11 +231,23 @@ int main(int argc, char *argv[])
perror("bind");
return EXIT_FAILURE;
}
- if (listen(listen_fd, 15) < 0) {
+ if (listen(listen_fd, LISTEN_BACKLOG) < 0) {
perror("listen");
return EXIT_FAILURE;
}
+ epoll_fd = epoll_create1(0);
+ if (epoll_fd == -1) {
+ perror("epoll_create1");
+ return EXIT_FAILURE;
+ }
+ event.events = EPOLLIN | EPOLLET;
+ event.data.fd = listen_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) < 0) {
+ perror("epoll_ctl");
+ return EXIT_FAILURE;
+ }
+
if (pid_file) {
pidfile = fopen(pid_file, "w");
if (!pidfile) {
@@ -213,34 +275,44 @@ int main(int argc, char *argv[])
drop_privileges();
prctl(PR_SET_NAME, "truth beacon");
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
-
- while ((connection_addr_len = sizeof(connection_addr)) &&
- (connection_fd = accept(listen_fd, (struct sockaddr *)&connection_addr, &connection_addr_len)) >= 0) {
- now = time(NULL);
- if (connection_addr.ss_family == AF_INET6) {
- v6 = &(((struct sockaddr_in6 *)&connection_addr)->sin6_addr);
- if (v6->s6_addr32[0] == 0 && v6->s6_addr32[1] == 0 && v6->s6_addr16[4] == 0 && v6->s6_addr16[5] == 0xFFFF)
- inet_ntop(AF_INET, &v6->s6_addr32[3], ipaddr, INET_ADDRSTRLEN);
- else
- inet_ntop(AF_INET6, v6, ipaddr, INET6_ADDRSTRLEN);
- } else if (connection_addr.ss_family == AF_INET)
- inet_ntop(AF_INET, &(((struct sockaddr_in *)&connection_addr)->sin_addr), ipaddr, INET_ADDRSTRLEN);
-
- if (setsockopt(connection_fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
- close(connection_fd);
- perror("setsockopt");
- continue;
+ seccomp_enable_filter();
+
+ while ((num_events = epoll_wait(epoll_fd, events, EPOLL_EVENTS, -1)) >= 0) {
+ for (i = 0; i < num_events; ++i) {
+ if (events[i].data.fd == listen_fd) {
+ if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
+ return EXIT_FAILURE;
+ event.events = EPOLLOUT;
+ for (;;) {
+ connection_addr_len = sizeof(connection_addr);
+ event.data.fd = accept4(listen_fd, (struct sockaddr *)&connection_addr, &connection_addr_len, SOCK_NONBLOCK);
+ now = time(NULL);
+ if (event.data.fd < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ break;
+ perror("accept4");
+ break;
+ }
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, event.data.fd, &event) < 0) {
+ perror("epoll_ctl");
+ close(event.data.fd);
+ }
+ if (connection_addr.ss_family == AF_INET6) {
+ v6 = &(((struct sockaddr_in6 *)&connection_addr)->sin6_addr);
+ if (v6->s6_addr32[0] == 0 && v6->s6_addr32[1] == 0 && v6->s6_addr16[4] == 0 && v6->s6_addr16[5] == 0xFFFF)
+ inet_ntop(AF_INET, &v6->s6_addr32[3], ipaddr, INET_ADDRSTRLEN);
+ else
+ inet_ntop(AF_INET6, v6, ipaddr, INET6_ADDRSTRLEN);
+ } else if (connection_addr.ss_family == AF_INET)
+ inet_ntop(AF_INET, &(((struct sockaddr_in *)&connection_addr)->sin_addr), ipaddr, INET_ADDRSTRLEN);
+ printf("[%llu]\t%s\t%d\n", (unsigned long long)now, ipaddr, ntohs(((struct sockaddr_in6 *)&connection_addr)->sin6_port));
+ }
+ } else if (events[i].events & EPOLLOUT) {
+ if (write(events[i].data.fd, truth, sizeof(truth)) < 0)
+ perror("write");
+ close(events[i].data.fd);
+ }
}
- time_str = ctime(&now);
- time_str[strlen(time_str) - 1] = 0;
- printf("%s\t%s\t%d\n", time_str, ipaddr, ntohs(((struct sockaddr_in6 *)&connection_addr)->sin6_port));
- if (write(connection_fd, truth, sizeof(truth)) < 0)
- perror("write");
- close(connection_fd);
}
-
- close(listen_fd);
- return 0;
+ return EXIT_FAILURE;
}
diff --git a/seccomp-bpf.h b/seccomp-bpf.h
new file mode 100644
index 0000000..f2a259c
--- /dev/null
+++ b/seccomp-bpf.h
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/prctl.h>
+
+#include <linux/unistd.h>
+#include <linux/audit.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+
+#define syscall_nr (offsetof(struct seccomp_data, nr))
+#define arch_nr (offsetof(struct seccomp_data, arch))
+
+#if defined(__i386__)
+# define REG_SYSCALL REG_EAX
+# define ARCH_NR AUDIT_ARCH_I386
+#elif defined(__x86_64__)
+# define REG_SYSCALL REG_RAX
+# define ARCH_NR AUDIT_ARCH_X86_64
+#else
+# warning "Platform does not support seccomp filter yet"
+# define REG_SYSCALL 0
+# define ARCH_NR 0
+#endif
+
+#define VALIDATE_ARCHITECTURE \
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, arch_nr), \
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0), \
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
+
+#define EXAMINE_SYSCALL \
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr)
+
+#define ALLOW_SYSCALL(name) \
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_##name, 0, 1), \
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
+
+#define KILL_PROCESS \
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)