summaryrefslogtreecommitdiff
path: root/linux_priv.c
diff options
context:
space:
mode:
authorStanisław Pitucha <viraptor@gmail.com>2014-12-13 20:27:53 +1100
committerdormando <dormando@rydia.net>2017-08-23 23:59:11 -0700
commit78c260a2ea8a3662720562ef2c0364eac36dfa4a (patch)
tree7453fad7c38002c7ab332aeb2b3e5990c96d6675 /linux_priv.c
parent3e8f5e25f06dc7649038e8a0a229acd5b627882d (diff)
downloadmemcached-78c260a2ea8a3662720562ef2c0364eac36dfa4a.tar.gz
Add drop_privileges() for Linux
Implement an aggressive version of drop_privileges(). Additionally add similar initialization function for threads drop_worker_privileges(). This version is similar to Solaris one and prohibits memcached from making any not approved syscalls. Current list narrows down the allowed calls to socket sends/recvs, accept, epoll handling, futex (and dependencies - mmap), getrusage (for stats), and signal / exit handling. Any incorrect behaviour will result in EACCES returned. This should be restricted further to KILL in the future (after more testing). The feature is only tested for i386 and x86_64. It depends on bpf filters and seccomp enabled in the kernel. It also requires libsecomp for abstraction to seccomp filters. All are available since Linux 3.5. Seccomp filtering can be enabled at compile time with --enable-seccomp. In case of local customisations which require more rights, memcached allows disabling drop_privileges() with "-o no_drop_privileges" at startup. Tests have to run with "-o relaxed_privileges", since they require disk access after the tests complete. This adds a few allowed syscalls, but does not disable the protection system completely.
Diffstat (limited to 'linux_priv.c')
-rw-r--r--linux_priv.c111
1 files changed, 111 insertions, 0 deletions
diff --git a/linux_priv.c b/linux_priv.c
new file mode 100644
index 0000000..811d6e8
--- /dev/null
+++ b/linux_priv.c
@@ -0,0 +1,111 @@
+#include "config.h"
+#include <seccomp.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "memcached.h"
+
+// In the future when the system is more tested this could be switched
+// to SCMP_ACT_KILL instead.
+#define DENY_ACTION SCMP_ACT_ERRNO(EACCES)
+
+void drop_privileges(void) {
+ scmp_filter_ctx ctx = seccomp_init(DENY_ACTION);
+ if (ctx == NULL) {
+ return;
+ }
+
+ int rc = 0;
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(shmctl), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
+
+#ifdef MEMCACHED_DEBUG
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
+#endif
+
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = seccomp_load(ctx);
+ if (rc < 0) {
+ goto fail;
+ }
+
+fail:
+ seccomp_release(ctx);
+}
+
+void drop_worker_privileges(void) {
+ scmp_filter_ctx ctx = seccomp_init(DENY_ACTION);
+ if (ctx == NULL) {
+ return;
+ }
+
+ int rc = 0;
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpeername), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getrusage), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvfrom), 0);
+
+ // for spawning the LRU crawler
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_robust_list), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(madvise), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
+
+ // stat
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockname), 0);
+
+ if (settings.shutdown_command) {
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(tgkill), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettid), 0);
+ }
+
+ if (settings.relaxed_privileges) {
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
+ } else {
+ rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_EQ, 1));
+ }
+
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = seccomp_load(ctx);
+ if (rc < 0) {
+ goto fail;
+ }
+
+fail:
+ seccomp_release(ctx);
+}