Blame linux_priv.c

Packit 4e8bc4
#include "config.h"
Packit 4e8bc4
#include <seccomp.h>
Packit 4e8bc4
#include <termios.h>
Packit 4e8bc4
#include <errno.h>
Packit 4e8bc4
#include <stdlib.h>
Packit 4e8bc4
#include <sys/ioctl.h>
Packit 4e8bc4
#include <signal.h>
Packit 4e8bc4
#include <string.h>
Packit 4e8bc4
#include "memcached.h"
Packit 4e8bc4
Packit 4e8bc4
static char *kill_msg;
Packit 4e8bc4
// Make sure to preserve the ??? position in the string, or correct the offsets
Packit 4e8bc4
// in the syssig handler.
Packit 4e8bc4
#define KILL_MSG_STR "Seccomp policy failure. Caught syscall ???. This is " \
Packit 4e8bc4
    "either an exploit attempt, or your system is not supported yet.\n"
Packit 4e8bc4
Packit 4e8bc4
static void handle_syssig(int signum, siginfo_t *si, void *thread_context) {
Packit 4e8bc4
#if defined(si_syscall)
Packit 4e8bc4
    int syscall_no = si->si_syscall;
Packit 4e8bc4
#else
Packit 4e8bc4
    // If system has no support for si_syscal, the information may not be
Packit 4e8bc4
    // precise.
Packit 4e8bc4
    int syscall_no = si->si_value.sival_int;
Packit 4e8bc4
#endif
Packit 4e8bc4
Packit 4e8bc4
    // Replace the characters in the kill message with the syscall number. We
Packit 4e8bc4
    // can't safely printf (even write is not really valid, but we're crashing
Packit 4e8bc4
    // anyway).
Packit 4e8bc4
Packit 4e8bc4
    kill_msg[39] = (syscall_no / 100) % 10 + '0';
Packit 4e8bc4
    kill_msg[40] = (syscall_no / 10) % 10 + '0';
Packit 4e8bc4
    kill_msg[41] = syscall_no % 10 + '0';
Packit 4e8bc4
    if (write(2, kill_msg, strlen(kill_msg)) == -1) {
Packit 4e8bc4
        // An error occurred, but we can't do anything about it here. This check
Packit 4e8bc4
        // is mostly to avoid the "ignoring return value of 'write'" error on
Packit 4e8bc4
        // distributions with broken gcc (no "ignore via cast to void" support).
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    // We can't use the nice exit() version because it causes at_exit handlers
Packit 4e8bc4
    // to be looked up and run. We can't take any locks while handling the
Packit 4e8bc4
    // signal, so _exit() is the only thing to do safely.
Packit 4e8bc4
    _exit(EXIT_FAILURE);
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
static const struct sigaction act = {
Packit 4e8bc4
    .sa_sigaction = handle_syssig,
Packit 4e8bc4
    .sa_flags = SA_SIGINFO,
Packit 4e8bc4
};
Packit 4e8bc4
Packit 4e8bc4
void setup_privilege_violations_handler(void) {
Packit 4e8bc4
    kill_msg = malloc(strlen(KILL_MSG_STR)+1);
Packit 4e8bc4
    strcpy(kill_msg, KILL_MSG_STR);
Packit 4e8bc4
Packit 4e8bc4
    sigaction(SIGSYS, &act, NULL);
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
// If anything crosses the policy, kill the process.
Packit 4e8bc4
#define DENY_ACTION SCMP_ACT_TRAP
Packit 4e8bc4
Packit 4e8bc4
void drop_privileges(void) {
Packit 4e8bc4
    scmp_filter_ctx ctx = seccomp_init(DENY_ACTION);
Packit 4e8bc4
    if (ctx == NULL) {
Packit 4e8bc4
        return;
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    int rc = 0;
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_pwait), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(shmctl), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, SCMP_A1(SCMP_CMP_EQ, TIOCGWINSZ));
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, SCMP_A1(SCMP_CMP_EQ, TCGETS));
Packit 4e8bc4
Packit 4e8bc4
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clock_gettime), 0);
Packit 4e8bc4
#endif
Packit 4e8bc4
Packit 4e8bc4
#ifdef MEMCACHED_DEBUG
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(readv), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
Packit 4e8bc4
Packit 4e8bc4
    if (settings.relaxed_privileges) {
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mkdir), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0);
Packit 4e8bc4
    }
Packit 4e8bc4
#endif
Packit 4e8bc4
Packit 4e8bc4
    if (rc != 0) {
Packit 4e8bc4
        goto fail;
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    rc = seccomp_load(ctx);
Packit 4e8bc4
    if (rc < 0) {
Packit 4e8bc4
        goto fail;
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    seccomp_release(ctx);
Packit 4e8bc4
    return;
Packit 4e8bc4
Packit 4e8bc4
fail:
Packit 4e8bc4
    seccomp_release(ctx);
Packit 4e8bc4
    fprintf(stderr, "Failed to set a seccomp profile on the main thread\n");
Packit 4e8bc4
    exit(EXIT_FAILURE);
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
void drop_worker_privileges(void) {
Packit 4e8bc4
    scmp_filter_ctx ctx = seccomp_init(DENY_ACTION);
Packit 4e8bc4
    if (ctx == NULL) {
Packit 4e8bc4
        return;
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    int rc = 0;
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_pwait), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(poll), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(readv), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpeername), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getrusage), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvfrom), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, SCMP_A1(SCMP_CMP_EQ, TIOCGWINSZ));
Packit 4e8bc4
Packit 4e8bc4
    // for spawning the LRU crawler
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_robust_list), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(madvise), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
Packit 4e8bc4
Packit 4e8bc4
    // stat
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockname), 0);
Packit 4e8bc4
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
Packit 4e8bc4
Packit 4e8bc4
    if (settings.shutdown_command) {
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(tgkill), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(tkill), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettid), 0);
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    if (settings.relaxed_privileges) {
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mkdir), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 0);
Packit 4e8bc4
    } else {
Packit 4e8bc4
        // stdout
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_EQ, 1));
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 1, SCMP_A0(SCMP_CMP_EQ, 1));
Packit 4e8bc4
        // stderr
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_EQ, 2));
Packit 4e8bc4
        rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 1, SCMP_A0(SCMP_CMP_EQ, 2));
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    if (rc != 0) {
Packit 4e8bc4
        goto fail;
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    rc = seccomp_load(ctx);
Packit 4e8bc4
    if (rc < 0) {
Packit 4e8bc4
        goto fail;
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    seccomp_release(ctx);
Packit 4e8bc4
    return;
Packit 4e8bc4
Packit 4e8bc4
fail:
Packit 4e8bc4
    seccomp_release(ctx);
Packit 4e8bc4
    fprintf(stderr, "Failed to set a seccomp profile on a worker thread\n");
Packit 4e8bc4
    exit(EXIT_FAILURE);
Packit 4e8bc4
}