openssh

changeset 10678:f40779d28db5

- (djm) [Makefile.in configure.ac sandbox-seccomp-filter.c] Add sandbox mode for Linux's new seccomp filter; patch from Will Drewry; feedback and ok dtucker@
author djm
date Wed, 04 Apr 2012 01:27:57 +0000
parents f8fdc5104986
children 37678d290982
files ChangeLog Makefile.in configure.ac sandbox-seccomp-filter.c
diffstat 4 files changed, 292 insertions(+), 2 deletions(-) [+]
line diff
     1.1 --- a/ChangeLog	Fri Mar 30 03:07:07 2012 +0000
     1.2 +++ b/ChangeLog	Wed Apr 04 01:27:57 2012 +0000
     1.3 @@ -1,3 +1,8 @@
     1.4 +20120404
     1.5 + - (djm) [Makefile.in configure.ac sandbox-seccomp-filter.c] Add sandbox
     1.6 +   mode for Linux's new seccomp filter; patch from Will Drewry; feedback
     1.7 +   and ok dtucker@
     1.8 +
     1.9  20120330
    1.10   - (dtucker) [contrib/redhat/openssh.spec] Bug #1992: remove now-gone WARNING
    1.11     file from spec file.  From crighter at nuclioss com.
     2.1 --- a/Makefile.in	Fri Mar 30 03:07:07 2012 +0000
     2.2 +++ b/Makefile.in	Wed Apr 04 01:27:57 2012 +0000
     2.3 @@ -90,7 +90,8 @@
     2.4  	loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
     2.5  	sftp-server.o sftp-common.o \
     2.6  	roaming_common.o roaming_serv.o \
     2.7 -	sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o
     2.8 +	sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
     2.9 +	sandbox-seccomp-filter.o
    2.10  
    2.11  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
    2.12  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
     3.1 --- a/configure.ac	Fri Mar 30 03:07:07 2012 +0000
     3.2 +++ b/configure.ac	Wed Apr 04 01:27:57 2012 +0000
     3.3 @@ -116,6 +116,35 @@
     3.4  	#include <sys/types.h>
     3.5  	#include <sys/resource.h>
     3.6  ])
     3.7 +AC_CHECK_DECL([PR_SET_NO_NEW_PRIVS], [have_linux_no_new_privs=1], , [
     3.8 +	#include <sys/types.h>
     3.9 +	#include <linux/prctl.h>
    3.10 +])
    3.11 +if test "x$have_linux_no_new_privs" = "x1" ; then
    3.12 +AC_CHECK_DECL([SECCOMP_MODE_FILTER], [have_seccomp_filter=1], , [
    3.13 +	#include <sys/types.h>
    3.14 +	#include <linux/seccomp.h>
    3.15 +])
    3.16 +fi
    3.17 +if test "x$have_seccomp_filter" = "x1" ; then
    3.18 +AC_MSG_CHECKING([kernel for seccomp_filter support])
    3.19 +AC_RUN_IFELSE([AC_LANG_PROGRAM([[
    3.20 +		#include <errno.h>
    3.21 +		#include <linux/seccomp.h>
    3.22 +		#include <stdlib.h>
    3.23 +		#include <sys/prctl.h>
    3.24 +	]],
    3.25 +	[[ errno = 0;
    3.26 +	   prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, 0, 0);
    3.27 +	   exit(errno == EFAULT ? 0 : 1); ]])],
    3.28 +	[ AC_MSG_RESULT([yes]) ], [
    3.29 +		AC_MSG_RESULT([no])
    3.30 +		# Disable seccomp filter as a target
    3.31 +		have_seccomp_filter=0
    3.32 +	],
    3.33 +	[ AC_MSG_RESULT([cross-compiling, assuming yes]) ]
    3.34 +)
    3.35 +fi
    3.36  
    3.37  use_stack_protector=1
    3.38  AC_ARG_WITH([stackprotect],
    3.39 @@ -657,6 +686,22 @@
    3.40  		AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
    3.41  		    [Prepend the address family to IP tunnel traffic])
    3.42  	fi
    3.43 +	AC_CHECK_HEADERS([linux/seccomp.h linux/filter.h linux/audit.h])
    3.44 +	AC_CHECK_FUNCS([prctl])
    3.45 +	have_seccomp_audit_arch=1
    3.46 +	case "$host" in
    3.47 +	x86_64-*)
    3.48 +		AC_DEFINE([SECCOMP_AUDIT_ARCH], [AUDIT_ARCH_X86_64],
    3.49 +		    [Specify the system call convention in use])
    3.50 +		;;
    3.51 +	i*86-*)
    3.52 +		AC_DEFINE([SECCOMP_AUDIT_ARCH], [AUDIT_ARCH_I386],
    3.53 +		    [Specify the system call convention in use])
    3.54 +		;;
    3.55 +	*)
    3.56 +		have_seccomp_audit_arch=0
    3.57 +		;;
    3.58 +	esac
    3.59  	;;
    3.60  mips-sony-bsd|mips-sony-newsos4)
    3.61  	AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to acquire controlling tty])
    3.62 @@ -2518,7 +2563,7 @@
    3.63  # Decide which sandbox style to use
    3.64  sandbox_arg=""
    3.65  AC_ARG_WITH([sandbox],
    3.66 -	[  --with-sandbox=style    Specify privilege separation sandbox (no, darwin, rlimit, systrace)],
    3.67 +	[  --with-sandbox=style    Specify privilege separation sandbox (no, darwin, rlimit, systrace, seccomp_filter)],
    3.68  	[
    3.69  		if test "x$withval" = "xyes" ; then
    3.70  			sandbox_arg=""
    3.71 @@ -2541,6 +2586,23 @@
    3.72  		AC_MSG_ERROR([Darwin seatbelt sandbox requires sandbox.h and sandbox_init function])
    3.73  	SANDBOX_STYLE="darwin"
    3.74  	AC_DEFINE([SANDBOX_DARWIN], [1], [Sandbox using Darwin sandbox_init(3)])
    3.75 +elif test "x$sandbox_arg" = "xseccomp_filter" || \
    3.76 +     ( test -z "$sandbox_arg" && \
    3.77 +       test "x$have_seccomp_filter" == "x1" && \
    3.78 +       test "x$ac_cv_header_linux_audit_h" = "xyes" && \
    3.79 +       test "x$have_seccomp_audit_arch" = "x1" && \
    3.80 +       test "x$have_linux_no_new_privs" = "x1" && \
    3.81 +       test "x$ac_cv_func_prctl" = "xyes" ) ; then
    3.82 +	test "x$have_seccomp_audit_arch" != "x1" && \
    3.83 +		AC_MSG_ERROR([seccomp_filter sandbox not supported on $host])
    3.84 +	test "x$have_linux_no_new_privs" != "x1" && \
    3.85 +		AC_MSG_ERROR([seccomp_filter sandbox requires PR_SET_NO_NEW_PRIVS])
    3.86 +	test "x$have_seccomp_filter" != "x1" && \
    3.87 +		AC_MSG_ERROR([seccomp_filter sandbox requires seccomp headers])
    3.88 +	test "x$ac_cv_func_prctl" != "xyes" && \
    3.89 +		AC_MSG_ERROR([seccomp_filter sandbox requires prctl function])
    3.90 +	SANDBOX_STYLE="seccomp_filter"
    3.91 +	AC_DEFINE([SANDBOX_SECCOMP_FILTER], [1], [Sandbox using seccomp filter])
    3.92  elif test "x$sandbox_arg" = "xrlimit" || \
    3.93       ( test -z "$sandbox_arg" && test "x$ac_cv_func_setrlimit" = "xyes" ) ; then
    3.94  	test "x$ac_cv_func_setrlimit" != "xyes" && \
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/sandbox-seccomp-filter.c	Wed Apr 04 01:27:57 2012 +0000
     4.3 @@ -0,0 +1,222 @@
     4.4 +/*
     4.5 + * Copyright (c) 2012 Will Drewry <wad@dataspill.org>
     4.6 + *
     4.7 + * Permission to use, copy, modify, and distribute this software for any
     4.8 + * purpose with or without fee is hereby granted, provided that the above
     4.9 + * copyright notice and this permission notice appear in all copies.
    4.10 + *
    4.11 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    4.12 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    4.13 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    4.14 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    4.15 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    4.16 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    4.17 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    4.18 + */
    4.19 +
    4.20 +/*
    4.21 + * Uncomment the SANDBOX_SECCOMP_FILTER_DEBUG macro below to help diagnose
    4.22 + * filter breakage during development. *Do not* use this in production,
    4.23 + * as it relies on making library calls that are unsafe in signal context.
    4.24 + *
    4.25 + * Instead, live systems the auditctl(8) may be used to monitor failures.
    4.26 + * E.g.
    4.27 + *   auditctl -a task,always -F uid=<privsep uid>
    4.28 + */
    4.29 +/* #define SANDBOX_SECCOMP_FILTER_DEBUG 1 */
    4.30 +
    4.31 +#ifdef SANDBOX_SECCOMP_FILTER_DEBUG
    4.32 +/* Use the kernel headers in case of an older toolchain. */
    4.33 +# include <asm/siginfo.h>
    4.34 +# define __have_siginfo_t 1
    4.35 +# define __have_sigval_t 1
    4.36 +# define __have_sigevent_t 1
    4.37 +#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */
    4.38 +
    4.39 +#include "includes.h"
    4.40 +
    4.41 +#ifdef SANDBOX_SECCOMP_FILTER
    4.42 +
    4.43 +#include <sys/types.h>
    4.44 +#include <sys/resource.h>
    4.45 +#include <sys/prctl.h>
    4.46 +
    4.47 +#include <linux/audit.h>
    4.48 +#include <linux/filter.h>
    4.49 +#include <linux/seccomp.h>
    4.50 +
    4.51 +#include <asm/unistd.h>
    4.52 +
    4.53 +#include <errno.h>
    4.54 +#include <signal.h>
    4.55 +#include <stdarg.h>
    4.56 +#include <stddef.h>  /* for offsetof */
    4.57 +#include <stdio.h>
    4.58 +#include <stdlib.h>
    4.59 +#include <string.h>
    4.60 +#include <unistd.h>
    4.61 +
    4.62 +#include "log.h"
    4.63 +#include "ssh-sandbox.h"
    4.64 +#include "xmalloc.h"
    4.65 +
    4.66 +/* Linux seccomp_filter sandbox */
    4.67 +#define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL
    4.68 +
    4.69 +/* Use a signal handler to emit violations when debugging */
    4.70 +#ifdef SANDBOX_SECCOMP_FILTER_DEBUG
    4.71 +# undef SECCOMP_FILTER_FAIL
    4.72 +# define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP
    4.73 +#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */
    4.74 +
    4.75 +/* Simple helpers to avoid manual errors (but larger BPF programs). */
    4.76 +#define SC_DENY(_nr, _errno) \
    4.77 +	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \
    4.78 +	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO|(_errno))
    4.79 +#define SC_ALLOW(_nr) \
    4.80 +	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \
    4.81 +	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
    4.82 +
    4.83 +/* Syscall filtering set for preauth. */
    4.84 +static const struct sock_filter preauth_insns[] = {
    4.85 +	/* Ensure the syscall arch convention is as expected. */
    4.86 +	BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
    4.87 +		offsetof(struct seccomp_data, arch)),
    4.88 +	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0),
    4.89 +	BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL),
    4.90 +	/* Load the syscall number for checking. */
    4.91 +	BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
    4.92 +		offsetof(struct seccomp_data, nr)),
    4.93 +	SC_DENY(open, EACCES),
    4.94 +	SC_ALLOW(getpid),
    4.95 +	SC_ALLOW(gettimeofday),
    4.96 +	SC_ALLOW(time),
    4.97 +	SC_ALLOW(read),
    4.98 +	SC_ALLOW(write),
    4.99 +	SC_ALLOW(close),
   4.100 +	SC_ALLOW(brk),
   4.101 +	SC_ALLOW(poll),
   4.102 +#ifdef __NR__newselect
   4.103 +	SC_ALLOW(_newselect),
   4.104 +#else
   4.105 +	SC_ALLOW(select),
   4.106 +#endif
   4.107 +	SC_ALLOW(madvise),
   4.108 +	SC_ALLOW(mmap),
   4.109 +	SC_ALLOW(munmap),
   4.110 +	SC_ALLOW(exit_group),
   4.111 +#ifdef __NR_rt_sigprocmask
   4.112 +	SC_ALLOW(rt_sigprocmask),
   4.113 +#else
   4.114 +	SC_ALLOW(sigprocmask),
   4.115 +#endif
   4.116 +	BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL),
   4.117 +};
   4.118 +
   4.119 +static const struct sock_fprog preauth_program = {
   4.120 +	.len = (unsigned short)(sizeof(preauth_insns)/sizeof(preauth_insns[0])),
   4.121 +	.filter = (struct sock_filter *)preauth_insns,
   4.122 +};
   4.123 +
   4.124 +struct ssh_sandbox {
   4.125 +	pid_t child_pid;
   4.126 +};
   4.127 +
   4.128 +struct ssh_sandbox *
   4.129 +ssh_sandbox_init(void)
   4.130 +{
   4.131 +	struct ssh_sandbox *box;
   4.132 +
   4.133 +	/*
   4.134 +	 * Strictly, we don't need to maintain any state here but we need
   4.135 +	 * to return non-NULL to satisfy the API.
   4.136 +	 */
   4.137 +	debug3("%s: preparing seccomp filter sandbox", __func__);
   4.138 +	box = xcalloc(1, sizeof(*box));
   4.139 +	box->child_pid = 0;
   4.140 +
   4.141 +	return box;
   4.142 +}
   4.143 +
   4.144 +#ifdef SANDBOX_SECCOMP_FILTER_DEBUG
   4.145 +extern struct monitor *pmonitor;
   4.146 +void mm_log_handler(LogLevel level, const char *msg, void *ctx);
   4.147 +
   4.148 +static void
   4.149 +ssh_sandbox_violation(int signum, siginfo_t *info, void *void_context)
   4.150 +{
   4.151 +	char msg[256];
   4.152 +
   4.153 +	snprintf(msg, sizeof(msg),
   4.154 +	    "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)",
   4.155 +	    __func__, info->si_arch, info->si_syscall, info->si_call_addr);
   4.156 +	mm_log_handler(SYSLOG_LEVEL_FATAL, msg, pmonitor);
   4.157 +	_exit(1);
   4.158 +}
   4.159 +
   4.160 +static void
   4.161 +ssh_sandbox_child_debugging(void)
   4.162 +{
   4.163 +	struct sigaction act;
   4.164 +	sigset_t mask;
   4.165 +
   4.166 +	debug3("%s: installing SIGSYS handler", __func__);
   4.167 +	memset(&act, 0, sizeof(act));
   4.168 +	sigemptyset(&mask);
   4.169 +	sigaddset(&mask, SIGSYS);
   4.170 +
   4.171 +	act.sa_sigaction = &ssh_sandbox_violation;
   4.172 +	act.sa_flags = SA_SIGINFO;
   4.173 +	if (sigaction(SIGSYS, &act, NULL) == -1)
   4.174 +		fatal("%s: sigaction(SIGSYS): %s", __func__, strerror(errno));
   4.175 +	if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
   4.176 +		fatal("%s: sigprocmask(SIGSYS): %s",
   4.177 +		      __func__, strerror(errno));
   4.178 +}
   4.179 +#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */
   4.180 +
   4.181 +void
   4.182 +ssh_sandbox_child(struct ssh_sandbox *box)
   4.183 +{
   4.184 +	struct rlimit rl_zero;
   4.185 +
   4.186 +	/* Set rlimits for completeness if possible. */
   4.187 +	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
   4.188 +	if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1)
   4.189 +		fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s",
   4.190 +			__func__, strerror(errno));
   4.191 +	if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1)
   4.192 +		fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s",
   4.193 +			__func__, strerror(errno));
   4.194 +	if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
   4.195 +		fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s",
   4.196 +			__func__, strerror(errno));
   4.197 +
   4.198 +#ifdef SANDBOX_SECCOMP_FILTER_DEBUG
   4.199 +	ssh_sandbox_child_debugging();
   4.200 +#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */
   4.201 +
   4.202 +	debug3("%s: setting PR_SET_NO_NEW_PRIVS", __func__);
   4.203 +	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
   4.204 +		fatal("%s: prctl(PR_SET_NO_NEW_PRIVS): %s",
   4.205 +		      __func__, strerror(errno));
   4.206 +	debug3("%s: attaching seccomp filter program", __func__);
   4.207 +	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1)
   4.208 +		fatal("%s: prctl(PR_SET_SECCOMP): %s",
   4.209 +		      __func__, strerror(errno));
   4.210 +}
   4.211 +
   4.212 +void
   4.213 +ssh_sandbox_parent_finish(struct ssh_sandbox *box)
   4.214 +{
   4.215 +	free(box);
   4.216 +	debug3("%s: finished", __func__);
   4.217 +}
   4.218 +
   4.219 +void
   4.220 +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid)
   4.221 +{
   4.222 +	box->child_pid = child_pid;
   4.223 +}
   4.224 +
   4.225 +#endif /* SANDBOX_SECCOMP_FILTER */