diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2016-02-23 03:07:51 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2016-02-23 05:50:12 +0100 |
commit | b089348a6dc558350390e163389a6d590c396ac7 (patch) | |
tree | 48a47e484557e65251d20a82f6a0e47eee8713de | |
parent | Fix build warnings (diff) | |
download | mulder-listen-daemon-b089348a6dc558350390e163389a6d590c396ac7.tar.xz mulder-listen-daemon-b089348a6dc558350390e163389a6d590c396ac7.zip |
Use epoll and bpf
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | mulderd.c | 154 | ||||
-rw-r--r-- | seccomp-bpf.h | 44 |
3 files changed, 165 insertions, 42 deletions
@@ -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 @@ -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) |