1239844Sdes/*
2239844Sdes * Copyright (c) 2012 Will Drewry <wad@dataspill.org>
3239844Sdes *
4239844Sdes * Permission to use, copy, modify, and distribute this software for any
5239844Sdes * purpose with or without fee is hereby granted, provided that the above
6239844Sdes * copyright notice and this permission notice appear in all copies.
7239844Sdes *
8239844Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9239844Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10239844Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11239844Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12239844Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13239844Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14239844Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15239844Sdes */
16239844Sdes
17239844Sdes/*
18239844Sdes * Uncomment the SANDBOX_SECCOMP_FILTER_DEBUG macro below to help diagnose
19239844Sdes * filter breakage during development. *Do not* use this in production,
20239844Sdes * as it relies on making library calls that are unsafe in signal context.
21239844Sdes *
22239844Sdes * Instead, live systems the auditctl(8) may be used to monitor failures.
23239844Sdes * E.g.
24239844Sdes *   auditctl -a task,always -F uid=<privsep uid>
25239844Sdes */
26239844Sdes/* #define SANDBOX_SECCOMP_FILTER_DEBUG 1 */
27239844Sdes
28239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG
29239844Sdes/* Use the kernel headers in case of an older toolchain. */
30239844Sdes# include <asm/siginfo.h>
31239844Sdes# define __have_siginfo_t 1
32239844Sdes# define __have_sigval_t 1
33239844Sdes# define __have_sigevent_t 1
34239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */
35239844Sdes
36239844Sdes#include "includes.h"
37239844Sdes
38239844Sdes#ifdef SANDBOX_SECCOMP_FILTER
39239844Sdes
40239844Sdes#include <sys/types.h>
41239844Sdes#include <sys/resource.h>
42239844Sdes#include <sys/prctl.h>
43239844Sdes
44239844Sdes#include <linux/audit.h>
45239844Sdes#include <linux/filter.h>
46239844Sdes#include <linux/seccomp.h>
47248613Sdes#include <elf.h>
48239844Sdes
49239844Sdes#include <asm/unistd.h>
50239844Sdes
51239844Sdes#include <errno.h>
52239844Sdes#include <signal.h>
53239844Sdes#include <stdarg.h>
54239844Sdes#include <stddef.h>  /* for offsetof */
55239844Sdes#include <stdio.h>
56239844Sdes#include <stdlib.h>
57239844Sdes#include <string.h>
58239844Sdes#include <unistd.h>
59239844Sdes
60239844Sdes#include "log.h"
61239844Sdes#include "ssh-sandbox.h"
62239844Sdes#include "xmalloc.h"
63239844Sdes
64239844Sdes/* Linux seccomp_filter sandbox */
65239844Sdes#define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL
66239844Sdes
67239844Sdes/* Use a signal handler to emit violations when debugging */
68239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG
69239844Sdes# undef SECCOMP_FILTER_FAIL
70239844Sdes# define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP
71239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */
72239844Sdes
73239844Sdes/* Simple helpers to avoid manual errors (but larger BPF programs). */
74239844Sdes#define SC_DENY(_nr, _errno) \
75239844Sdes	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \
76239844Sdes	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO|(_errno))
77239844Sdes#define SC_ALLOW(_nr) \
78239844Sdes	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \
79239844Sdes	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
80239844Sdes
81239844Sdes/* Syscall filtering set for preauth. */
82239844Sdesstatic const struct sock_filter preauth_insns[] = {
83239844Sdes	/* Ensure the syscall arch convention is as expected. */
84239844Sdes	BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
85239844Sdes		offsetof(struct seccomp_data, arch)),
86239844Sdes	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0),
87239844Sdes	BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL),
88239844Sdes	/* Load the syscall number for checking. */
89239844Sdes	BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
90239844Sdes		offsetof(struct seccomp_data, nr)),
91239844Sdes	SC_DENY(open, EACCES),
92239844Sdes	SC_ALLOW(getpid),
93239844Sdes	SC_ALLOW(gettimeofday),
94263970Sdes	SC_ALLOW(clock_gettime),
95248613Sdes#ifdef __NR_time /* not defined on EABI ARM */
96239844Sdes	SC_ALLOW(time),
97248613Sdes#endif
98239844Sdes	SC_ALLOW(read),
99239844Sdes	SC_ALLOW(write),
100239844Sdes	SC_ALLOW(close),
101263970Sdes#ifdef __NR_shutdown /* not defined on archs that go via socketcall(2) */
102263970Sdes	SC_ALLOW(shutdown),
103263970Sdes#endif
104239844Sdes	SC_ALLOW(brk),
105239844Sdes	SC_ALLOW(poll),
106239844Sdes#ifdef __NR__newselect
107239844Sdes	SC_ALLOW(_newselect),
108239844Sdes#else
109239844Sdes	SC_ALLOW(select),
110239844Sdes#endif
111239844Sdes	SC_ALLOW(madvise),
112248613Sdes#ifdef __NR_mmap2 /* EABI ARM only has mmap2() */
113248613Sdes	SC_ALLOW(mmap2),
114248613Sdes#endif
115248613Sdes#ifdef __NR_mmap
116239844Sdes	SC_ALLOW(mmap),
117248613Sdes#endif
118239844Sdes	SC_ALLOW(munmap),
119239844Sdes	SC_ALLOW(exit_group),
120239844Sdes#ifdef __NR_rt_sigprocmask
121239844Sdes	SC_ALLOW(rt_sigprocmask),
122239844Sdes#else
123239844Sdes	SC_ALLOW(sigprocmask),
124239844Sdes#endif
125239844Sdes	BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL),
126239844Sdes};
127239844Sdes
128239844Sdesstatic const struct sock_fprog preauth_program = {
129239844Sdes	.len = (unsigned short)(sizeof(preauth_insns)/sizeof(preauth_insns[0])),
130239844Sdes	.filter = (struct sock_filter *)preauth_insns,
131239844Sdes};
132239844Sdes
133239844Sdesstruct ssh_sandbox {
134239844Sdes	pid_t child_pid;
135239844Sdes};
136239844Sdes
137239844Sdesstruct ssh_sandbox *
138263970Sdesssh_sandbox_init(struct monitor *monitor)
139239844Sdes{
140239844Sdes	struct ssh_sandbox *box;
141239844Sdes
142239844Sdes	/*
143239844Sdes	 * Strictly, we don't need to maintain any state here but we need
144239844Sdes	 * to return non-NULL to satisfy the API.
145239844Sdes	 */
146239844Sdes	debug3("%s: preparing seccomp filter sandbox", __func__);
147239844Sdes	box = xcalloc(1, sizeof(*box));
148239844Sdes	box->child_pid = 0;
149239844Sdes
150239844Sdes	return box;
151239844Sdes}
152239844Sdes
153239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG
154239844Sdesextern struct monitor *pmonitor;
155239844Sdesvoid mm_log_handler(LogLevel level, const char *msg, void *ctx);
156239844Sdes
157239844Sdesstatic void
158239844Sdesssh_sandbox_violation(int signum, siginfo_t *info, void *void_context)
159239844Sdes{
160239844Sdes	char msg[256];
161239844Sdes
162239844Sdes	snprintf(msg, sizeof(msg),
163239844Sdes	    "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)",
164239844Sdes	    __func__, info->si_arch, info->si_syscall, info->si_call_addr);
165239844Sdes	mm_log_handler(SYSLOG_LEVEL_FATAL, msg, pmonitor);
166239844Sdes	_exit(1);
167239844Sdes}
168239844Sdes
169239844Sdesstatic void
170239844Sdesssh_sandbox_child_debugging(void)
171239844Sdes{
172239844Sdes	struct sigaction act;
173239844Sdes	sigset_t mask;
174239844Sdes
175239844Sdes	debug3("%s: installing SIGSYS handler", __func__);
176239844Sdes	memset(&act, 0, sizeof(act));
177239844Sdes	sigemptyset(&mask);
178239844Sdes	sigaddset(&mask, SIGSYS);
179239844Sdes
180239844Sdes	act.sa_sigaction = &ssh_sandbox_violation;
181239844Sdes	act.sa_flags = SA_SIGINFO;
182239844Sdes	if (sigaction(SIGSYS, &act, NULL) == -1)
183239844Sdes		fatal("%s: sigaction(SIGSYS): %s", __func__, strerror(errno));
184239844Sdes	if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
185239844Sdes		fatal("%s: sigprocmask(SIGSYS): %s",
186239844Sdes		      __func__, strerror(errno));
187239844Sdes}
188239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */
189239844Sdes
190239844Sdesvoid
191239844Sdesssh_sandbox_child(struct ssh_sandbox *box)
192239844Sdes{
193239844Sdes	struct rlimit rl_zero;
194239849Sdes	int nnp_failed = 0;
195239844Sdes
196239844Sdes	/* Set rlimits for completeness if possible. */
197239844Sdes	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
198239844Sdes	if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1)
199239844Sdes		fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s",
200239844Sdes			__func__, strerror(errno));
201239844Sdes	if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1)
202239844Sdes		fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s",
203239844Sdes			__func__, strerror(errno));
204239844Sdes	if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
205239844Sdes		fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s",
206239844Sdes			__func__, strerror(errno));
207239844Sdes
208239844Sdes#ifdef SANDBOX_SECCOMP_FILTER_DEBUG
209239844Sdes	ssh_sandbox_child_debugging();
210239844Sdes#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */
211239844Sdes
212239844Sdes	debug3("%s: setting PR_SET_NO_NEW_PRIVS", __func__);
213239849Sdes	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
214239849Sdes		debug("%s: prctl(PR_SET_NO_NEW_PRIVS): %s",
215239844Sdes		      __func__, strerror(errno));
216239849Sdes		nnp_failed = 1;
217239849Sdes	}
218239844Sdes	debug3("%s: attaching seccomp filter program", __func__);
219239844Sdes	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1)
220239849Sdes		debug("%s: prctl(PR_SET_SECCOMP): %s",
221239844Sdes		      __func__, strerror(errno));
222239849Sdes	else if (nnp_failed)
223239849Sdes		fatal("%s: SECCOMP_MODE_FILTER activated but "
224239849Sdes		    "PR_SET_NO_NEW_PRIVS failed", __func__);
225239844Sdes}
226239844Sdes
227239844Sdesvoid
228239844Sdesssh_sandbox_parent_finish(struct ssh_sandbox *box)
229239844Sdes{
230239844Sdes	free(box);
231239844Sdes	debug3("%s: finished", __func__);
232239844Sdes}
233239844Sdes
234239844Sdesvoid
235239844Sdesssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid)
236239844Sdes{
237239844Sdes	box->child_pid = child_pid;
238239844Sdes}
239239844Sdes
240239844Sdes#endif /* SANDBOX_SECCOMP_FILTER */
241