diff --git a/CMakeLists.txt b/CMakeLists.txt index 83c5223..eb92f80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ endif(COMMAND cmake_policy) project (pkcs11 C) set(PKCS11_PROXY_SRCS gck-rpc-module.c gck-rpc-message.c gck-rpc-util.c egg-buffer.c) -set(PKCS11_DAEMON_SRCS egg-buffer.c gck-rpc-daemon-standalone.c gck-rpc-dispatch.c gck-rpc-message.c gck-rpc-util.c) +set(PKCS11_DAEMON_SRCS egg-buffer.c gck-rpc-daemon-standalone.c gck-rpc-dispatch.c gck-rpc-message.c gck-rpc-util.c syscall-reporter.c syscall-names.h) add_definitions(-Wall) add_library(pkcs11-proxy SHARED ${PKCS11_PROXY_SRCS}) @@ -38,3 +38,8 @@ target_link_libraries (pkcs11-daemon dl pthread) install_targets (/lib pkcs11-proxy) install_targets (/bin pkcs11-daemon) + +add_custom_command( + OUTPUT syscall-names.h + COMMAND ${CMAKE_SOURCE_DIR}/mksyscalls.sh + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) diff --git a/gck-rpc-daemon-standalone.c b/gck-rpc-daemon-standalone.c index a0039f2..3f17e1e 100644 --- a/gck-rpc-daemon-standalone.c +++ b/gck-rpc-daemon-standalone.c @@ -42,6 +42,63 @@ #define SOCKET_PATH "tcp://127.0.0.1" +#include "seccomp-bpf.h" +#include "syscall-reporter.h" + +static int install_syscall_filter(void) +{ + struct sock_filter filter[] = { + /* Validate architecture. */ + VALIDATE_ARCHITECTURE, + /* Grab the system call number. */ + EXAMINE_SYSCALL, + /* List allowed syscalls. */ + ALLOW_SYSCALL(rt_sigreturn), +#ifdef __NR_sigreturn + ALLOW_SYSCALL(sigreturn), +#endif + ALLOW_SYSCALL(exit_group), + ALLOW_SYSCALL(exit), + ALLOW_SYSCALL(read), + ALLOW_SYSCALL(write), + ALLOW_SYSCALL(futex), + ALLOW_SYSCALL(brk), + ALLOW_SYSCALL(open), + ALLOW_SYSCALL(fstat64), + ALLOW_SYSCALL(mmap2), + ALLOW_SYSCALL(mprotect), + ALLOW_SYSCALL(close), + ALLOW_SYSCALL(access), + ALLOW_SYSCALL(munmap), + ALLOW_SYSCALL(time), + ALLOW_SYSCALL(_llseek), + ALLOW_SYSCALL(stat64), + ALLOW_SYSCALL(fcntl64), + ALLOW_SYSCALL(mlock), + ALLOW_SYSCALL(munlock), + KILL_PROCESS, + }; + struct sock_fprog prog = { + .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), + .filter = filter, + }; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + perror("prctl(NO_NEW_PRIVS)"); + goto failed; + } + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { + perror("prctl(SECCOMP)"); + goto failed; + } + return 0; + +failed: + if (errno == EINVAL) + fprintf(stderr, "SECCOMP_FILTER is not available. :(\n"); + return 1; +} + #if 0 /* Sample configuration for loading NSS remotely */ static CK_C_INITIALIZE_ARGS p11_init_args = { @@ -72,6 +129,12 @@ int main(int argc, char *argv[]) int sock, ret; CK_RV rv; + + if (install_syscall_reporter()) + return 1; + if (install_syscall_filter()) + return 1; + /* The module to load is the argument */ if (argc != 2 && argc != 3) usage(); diff --git a/mksyscalls.sh b/mksyscalls.sh new file mode 100755 index 0000000..47b4a96 --- /dev/null +++ b/mksyscalls.sh @@ -0,0 +1,4 @@ +#!/bin/bash +(echo "static const char *syscall_names[] = {" +echo "#include " | cpp -dM | grep '^#define __NR_' | LC_ALL=C sed -r -n -e 's/^\#define[ \t]+__NR_([a-z0-9_]+)[ \t]+([0-9]+)(.*)/ [\2] = "\1",/p' +echo "};")> syscall-names.h diff --git a/seccomp-bpf.h b/seccomp-bpf.h new file mode 100644 index 0000000..b123787 --- /dev/null +++ b/seccomp-bpf.h @@ -0,0 +1,81 @@ +/* + * seccomp example for x86 (32-bit and 64-bit) with BPF macros + * + * Copyright (c) 2012 The Chromium OS Authors + * Authors: + * Will Drewry + * Kees Cook + * + * The code may be used by anyone for any purpose, and can serve as a + * starting point for developing applications using mode 2 seccomp. + */ +#ifndef _SECCOMP_BPF_H_ +#define _SECCOMP_BPF_H_ + +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include + +#include +#ifndef PR_SET_NO_NEW_PRIVS +# define PR_SET_NO_NEW_PRIVS 38 +#endif + +#include +#include +#include +#ifdef HAVE_LINUX_SECCOMP_H +# include +#endif +#ifndef SECCOMP_MODE_FILTER +# define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */ +# define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */ +# define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */ +# define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ +struct seccomp_data { + int nr; + __u32 arch; + __u64 instruction_pointer; + __u64 args[6]; +}; +#endif +#ifndef SYS_SECCOMP +# define SYS_SECCOMP 1 +#endif + +#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) + +#endif /* _SECCOMP_BPF_H_ */ diff --git a/syscall-reporter.c b/syscall-reporter.c new file mode 100644 index 0000000..ec3b254 --- /dev/null +++ b/syscall-reporter.c @@ -0,0 +1,76 @@ +/* + * syscall reporting example for seccomp + * + * Copyright (c) 2012 The Chromium OS Authors + * Authors: + * Will Drewry + * Kees Cook + * + * The code may be used by anyone for any purpose, and can serve as a + * starting point for developing applications using mode 2 seccomp. + */ +#include "syscall-reporter.h" +#include "syscall-names.h" + +const char * const msg_needed = "Looks like you also need syscall: "; + +/* Since "sprintf" is technically not signal-safe, reimplement %d here. */ +static void write_uint(char *buf, unsigned int val) +{ + int width = 0; + unsigned int tens; + + if (val == 0) { + strcpy(buf, "0"); + return; + } + for (tens = val; tens; tens /= 10) + ++ width; + buf[width] = '\0'; + for (tens = val; tens; tens /= 10) + buf[--width] = '0' + (tens % 10); +} + +static void reporter(int nr, siginfo_t *info, void *void_context) +{ + char buf[128]; + ucontext_t *ctx = (ucontext_t *)(void_context); + unsigned int syscall; + if (info->si_code != SYS_SECCOMP) + return; + if (!ctx) + return; + syscall = ctx->uc_mcontext.gregs[REG_SYSCALL]; + strcpy(buf, msg_needed); + if (syscall < sizeof(syscall_names)) { + strcat(buf, syscall_names[syscall]); + strcat(buf, "("); + } + write_uint(buf + strlen(buf), syscall); + if (syscall < sizeof(syscall_names)) + strcat(buf, ")"); + strcat(buf, "\n"); + write(STDERR_FILENO, buf, strlen(buf)); + _exit(1); +} + +int install_syscall_reporter(void) +{ + struct sigaction act; + sigset_t mask; + memset(&act, 0, sizeof(act)); + sigemptyset(&mask); + sigaddset(&mask, SIGSYS); + + act.sa_sigaction = &reporter; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGSYS, &act, NULL) < 0) { + perror("sigaction"); + return -1; + } + if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) { + perror("sigprocmask"); + return -1; + } + return 0; +} diff --git a/syscall-reporter.h b/syscall-reporter.h new file mode 100644 index 0000000..ea4847e --- /dev/null +++ b/syscall-reporter.h @@ -0,0 +1,28 @@ +/* + * syscall reporting example for seccomp + * + * Copyright (c) 2012 The Chromium OS Authors + * Authors: + * Kees Cook + * Will Drewry + * + * The code may be used by anyone for any purpose, and can serve as a + * starting point for developing applications using mode 2 seccomp. + */ +#ifndef _BPF_REPORTER_H_ +#define _BPF_REPORTER_H_ + +#include "seccomp-bpf.h" + +/* Since this redfines "KILL_PROCESS" into a TRAP for the reporter hook, + * we want to make sure it stands out in the build as it should not be + * used in the final program. + */ +#warning "You've included the syscall reporter. Do not use in production!" +#undef KILL_PROCESS +#define KILL_PROCESS \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP) + +extern int install_syscall_reporter(void); + +#endif