https://bugzilla.mindrot.org/show_bug.cgi?id=2142 --- a/Makefile.in +++ a/Makefile.in @@ -106,7 +106,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ sftp-server.o sftp-common.o \ roaming_common.o roaming_serv.o \ sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ - sandbox-seccomp-filter.o sandbox-capsicum.o + sandbox-seccomp-filter.o sandbox-libseccomp-filter.o sandbox-capsicum.o MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5 --- a/configure.ac +++ a/configure.ac @@ -2867,11 +2867,22 @@ else fi AC_SUBST([SSH_PRIVSEP_USER]) +AC_CHECK_DECL([SCMP_ARCH_NATIVE], [have_libseccomp_filter=1], , [ + #include + #include +]) +if test "x$have_libseccomp_filter" = "x1" ; then + AC_CHECK_LIB([seccomp], [seccomp_init], + [LIBS="$LIBS -lseccomp"], + [have_libseccomp_filter=0]) +fi + if test "x$have_linux_no_new_privs" = "x1" ; then AC_CHECK_DECL([SECCOMP_MODE_FILTER], [have_seccomp_filter=1], , [ #include #include ]) + fi if test "x$have_seccomp_filter" = "x1" ; then AC_MSG_CHECKING([kernel for seccomp_filter support]) @@ -2898,7 +2909,7 @@ fi # Decide which sandbox style to use sandbox_arg="" AC_ARG_WITH([sandbox], - [ --with-sandbox=style Specify privilege separation sandbox (no, darwin, rlimit, systrace, seccomp_filter, capsicum)], + [ --with-sandbox=style Specify privilege separation sandbox (no, darwin, rlimit, systrace, seccomp_filter, libseccomp_filter, capsicum)], [ if test "x$withval" = "xyes" ; then sandbox_arg="" @@ -3008,6 +3019,13 @@ elif test "x$sandbox_arg" = "xdarwin" || \ AC_MSG_ERROR([Darwin seatbelt sandbox requires sandbox.h and sandbox_init function]) SANDBOX_STYLE="darwin" AC_DEFINE([SANDBOX_DARWIN], [1], [Sandbox using Darwin sandbox_init(3)]) +elif test "x$sandbox_arg" = "xlibseccomp_filter" || \ + ( test -z "$sandbox_arg" && \ + test "x$have_libseccomp_filter" = "x1" ) ; then + test "x$have_libseccomp_filter" != "x1" && \ + AC_MSG_ERROR([libseccomp_filter sandbox not supported on $host]) + SANDBOX_STYLE="libseccomp_filter" + AC_DEFINE([SANDBOX_LIBSECCOMP_FILTER], [1], [Sandbox using libseccomp filter]) elif test "x$sandbox_arg" = "xseccomp_filter" || \ ( test -z "$sandbox_arg" && \ test "x$have_seccomp_filter" = "x1" && \ --- a/sandbox-libseccomp-filter.c +++ a/sandbox-libseccomp-filter.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2012 Will Drewry + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_LIBSECCOMP_FILTER + +#include +#include +#include + +#include +#include +#include +#include /* for offsetof */ +#include +#include +#include +#include + +#include "log.h" +#include "ssh-sandbox.h" +#include "xmalloc.h" + +struct ssh_sandbox { + pid_t child_pid; +}; + +struct ssh_sandbox * +ssh_sandbox_init(struct monitor *monitor) +{ + struct ssh_sandbox *box; + + /* + * Strictly, we don't need to maintain any state here but we need + * to return non-NULL to satisfy the API. + */ + debug3("%s: preparing libseccomp filter sandbox", __func__); + box = xcalloc(1, sizeof(*box)); + box->child_pid = 0; + + return box; +} + +static int +seccomp_add_secondary_archs(scmp_filter_ctx *c) +{ +#if defined(__i386__) || defined(__x86_64__) + int r; + r = seccomp_arch_add(c, SCMP_ARCH_X86); + if (r < 0 && r != -EEXIST) + return r; + r = seccomp_arch_add(c, SCMP_ARCH_X86_64); + if (r < 0 && r != -EEXIST) + return r; + r = seccomp_arch_add(c, SCMP_ARCH_X32); + if (r < 0 && r != -EEXIST) + return r; +#endif + return 0; +} + +struct scmp_action_def { + uint32_t action; + int syscall; +}; + +static const struct scmp_action_def preauth_insns[] = { + {SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open)}, + {SCMP_ACT_ERRNO(EACCES), SCMP_SYS(stat)}, + {SCMP_ACT_ALLOW, SCMP_SYS(getpid)}, + {SCMP_ACT_ALLOW, SCMP_SYS(getpid)}, + {SCMP_ACT_ALLOW, SCMP_SYS(gettimeofday)}, + {SCMP_ACT_ALLOW, SCMP_SYS(clock_gettime)}, +#ifdef __NR_time /* not defined on EABI ARM */ + {SCMP_ACT_ALLOW, SCMP_SYS(time)}, +#endif + {SCMP_ACT_ALLOW, SCMP_SYS(read)}, + {SCMP_ACT_ALLOW, SCMP_SYS(write)}, + {SCMP_ACT_ALLOW, SCMP_SYS(close)}, +#ifdef __NR_shutdown /* not defined on archs that go via socketcall(2) */ + {SCMP_ACT_ALLOW, SCMP_SYS(shutdown)}, +#endif + {SCMP_ACT_ALLOW, SCMP_SYS(brk)}, + {SCMP_ACT_ALLOW, SCMP_SYS(poll)}, +#ifdef __NR__newselect + {SCMP_ACT_ALLOW, SCMP_SYS(_newselect)}, +#endif + {SCMP_ACT_ALLOW, SCMP_SYS(select)}, + {SCMP_ACT_ALLOW, SCMP_SYS(madvise)}, +#ifdef __NR_mmap2 /* EABI ARM only has mmap2() */ + {SCMP_ACT_ALLOW, SCMP_SYS(mmap2)}, +#endif +#ifdef __NR_mmap + {SCMP_ACT_ALLOW, SCMP_SYS(mmap)}, +#endif +#ifdef __dietlibc__ + {SCMP_ACT_ALLOW, SCMP_SYS(mremap)}, + {SCMP_ACT_ALLOW, SCMP_SYS(exit)}, +#endif + {SCMP_ACT_ALLOW, SCMP_SYS(munmap)}, + {SCMP_ACT_ALLOW, SCMP_SYS(exit_group)}, +#ifdef __NR_rt_sigprocmask + {SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask)}, +#else + {SCMP_ACT_ALLOW, SCMP_SYS(sigprocmask)}, +#endif + {0, 0} +}; + + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + scmp_filter_ctx *seccomp; + struct rlimit rl_zero; + const struct scmp_action_def *insn; + int r; + + /* Set rlimits for completeness if possible. */ + rl_zero.rlim_cur = rl_zero.rlim_max = 0; + if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", + __func__, strerror(errno)); + if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", + __func__, strerror(errno)); + if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", + __func__, strerror(errno)); + + seccomp = seccomp_init(SCMP_ACT_KILL); + if (!seccomp) + fatal("%s:libseccomp activation failed", __func__); + if (seccomp_add_secondary_archs(seccomp)) + fatal("%s:libseccomp secondary arch setup failed", __func__); + + for (insn = preauth_insns; insn->action; insn++) { + if (seccomp_rule_add(seccomp, insn->action, insn->syscall, 0) < 0) + fatal("%s:libseccomp rule failed", __func__); + } + + if ((r = seccomp_load(seccomp)) < 0) + fatal("%s:libseccomp unable to load filter %d", __func__, r); + + seccomp_release(seccomp); +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + free(box); + debug3("%s: finished", __func__); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + box->child_pid = child_pid; +} + +#endif /* SANDBOX_LIBSECCOMP_FILTER */