I wrote and tested these on a bleeding-edge Ubuntu box. I have yet to
try this on other Linux flavors. First is a patch to make the secomp
trap handler on Linux more helpfully verbose. Then a patch that can
incrementally tighten the syscall filter to calls listed in a text
file.
The patch in the previous mail had the arguments backward for the
syscall resolving function
From 8b99fd609647ff0567c83d0d05991c874ce8278e Mon Sep 17 00:00:00 2001
From: James Browning <jamesb.f...@gmail.com>
Date: Mon, 27 Feb 2023 16:30:51 -0800
Subject: [PATCH 1/2] Attempt to make seccomp errors useful not Lassie
---
ntpd/ntp_sandbox.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/ntpd/ntp_sandbox.c b/ntpd/ntp_sandbox.c
index 369602357..8d82c8edd 100644
--- a/ntpd/ntp_sandbox.c
+++ b/ntpd/ntp_sandbox.c
@@ -568,8 +568,11 @@ static void catchTrap(int sig, siginfo_t *si, void *u)
UNUSED_ARG(u); /* unused ucontext_t */
msyslog(LOG_ERR, "ERR: SIGSYS: got a trap.\n");
if ( si->si_syscall ) {
- msyslog(LOG_ERR, "ERR: SIGSYS/seccomp bad syscall %d/%#x\n",
- si->si_syscall, si->si_arch);
+ char *call = seccomp_syscall_resolve_num_arch(
+ si->si_arch, si->si_syscall);
+ msyslog(LOG_ERR, "ERR: SIGSYS/seccomp bad syscall %d(%s)/%#x\n",
+ si->si_syscall, call, si->si_arch);
+ free(call);
}
#ifndef BACKTRACE_DISABLED
backtrace_log();
--
2.38.1
From 187249e022aee2ad9ea0d3a2a88c6873d84f8d12 Mon Sep 17 00:00:00 2001
From: James Browning <jamesb.f...@gmail.com>
Date: Mon, 27 Feb 2023 10:39:29 -0800
Subject: [PATCH 2/2] Remove seccomp code from sandbox(), parsing a fixed text
file
---
devel/seccomp-list.txt | 162 +++++++++++++++++++++++++
etc/seccomp.list | 106 +++++++++++++++++
include/ntpd.h | 1 +
ntpd/ntp_sandbox.c | 262 ++++++++++-------------------------------
ntpd/ntpd.c | 10 ++
5 files changed, 341 insertions(+), 200 deletions(-)
create mode 100644 devel/seccomp-list.txt
create mode 100644 etc/seccomp.list
diff --git a/devel/seccomp-list.txt b/devel/seccomp-list.txt
new file mode 100644
index 000000000..60b5d074f
--- /dev/null
+++ b/devel/seccomp-list.txt
@@ -0,0 +1,162 @@
+ SCMP_SYS(accept),
+ SCMP_SYS(access),
+ SCMP_SYS(adjtimex),
+ SCMP_SYS(bind),
+ SCMP_SYS(brk),
+ SCMP_SYS(chdir),
+ SCMP_SYS(clock_adjtime),
+ SCMP_SYS(clock_gettime),
+ SCMP_SYS(clock_settime),
+ SCMP_SYS(close),
+ SCMP_SYS(connect),
+ SCMP_SYS(exit),
+ SCMP_SYS(exit_group),
+ SCMP_SYS(fcntl),
+ SCMP_SYS(fstat),
+ SCMP_SYS(fsync),
+ SCMP_SYS(futex), /* sem_xxx, used by threads */
+ SCMP_SYS(getdents), /* Scanning /etc/ntp.d/ */
+ SCMP_SYS(getegid), /* Needed on Alpine */
+ SCMP_SYS(getgid), /* Needed on Alpine */
+ SCMP_SYS(getdents64),
+
+#ifdef __NR_getrandom
+ SCMP_SYS(getrandom), /* Added in 3.17 kernel */
+#endif
+#ifdef __NR_ugetrlimit
+ SCMP_SYS(ugetrlimit), /* sysconf */
+#endif
+#ifdef __NR_getrlimit
+ SCMP_SYS(getrlimit), /* sysconf */
+ SCMP_SYS(setrlimit),
+#endif
+ SCMP_SYS(getrusage),
+ SCMP_SYS(getsockname),
+ SCMP_SYS(getsockopt),
+ SCMP_SYS(gettimeofday), /* mkstemp */
+ SCMP_SYS(getuid), /* Needed on Alpine */
+ SCMP_SYS(ioctl),
+ SCMP_SYS(link),
+ SCMP_SYS(listen),
+ SCMP_SYS(lseek),
+ SCMP_SYS(membarrier), /* Needed on Alpine 3.11.3 */
+ SCMP_SYS(munmap),
+ SCMP_SYS(newfstatat),
+ SCMP_SYS(open),
+#ifdef __NR_openat
+ SCMP_SYS(openat), /* SUSE */
+#endif
+ SCMP_SYS(poll),
+ SCMP_SYS(pselect6),
+ SCMP_SYS(read),
+ SCMP_SYS(readv), /* nscd getaddrinfo() provider */
+ SCMP_SYS(recvfrom), /* Comment this out for testing.
+ * It will die on the first reply.
+ * (Or maybe sooner if a request arrives.)
+ */
+ SCMP_SYS(recvmsg),
+ SCMP_SYS(rename),
+ SCMP_SYS(rt_sigaction),
+ SCMP_SYS(rt_sigprocmask),
+ SCMP_SYS(rt_sigreturn),
+#ifdef __NR_rseq
+ SCMP_SYS(rseq), /* needed by glibc-2.35+ for resumable sequences */
+#endif
+ SCMP_SYS(sigaction),
+ SCMP_SYS(sigprocmask),
+ SCMP_SYS(sigreturn),
+#ifdef __NR_select
+ SCMP_SYS(select), /* not in ARM */
+#endif
+ SCMP_SYS(sendto),
+ SCMP_SYS(setsid),
+#ifdef __NR_setsockopt
+ SCMP_SYS(setsockopt), /* not in old kernels */
+#endif
+ SCMP_SYS(socket),
+ SCMP_SYS(socketcall), /* old kernels */
+ SCMP_SYS(stat),
+ SCMP_SYS(statfs64), /* from getaddrinfo after lid open */
+#ifdef __NR_time
+ SCMP_SYS(time), /* not in ARM */
+#endif
+ SCMP_SYS(sysinfo),
+#ifdef HAVE_TIMER_CREATE
+ SCMP_SYS(timer_create),
+ SCMP_SYS(timer_gettime),
+ SCMP_SYS(timer_settime),
+#else
+ SCMP_SYS(getitimer),
+ SCMP_SYS(setitimer),
+#endif
+ SCMP_SYS(write),
+ SCMP_SYS(writev), /* Needed on Alpine 3.11.3 */
+ SCMP_SYS(unlink),
+
+/* Don't comment out this block for testing.
+ * pthread_create blocks signals so it will crash
+ * rather than generate a trap.
+ */
+ SCMP_SYS(clone), /* threads */
+ SCMP_SYS(clone3),
+ SCMP_SYS(kill), /* generate signal */
+ SCMP_SYS(madvise),
+ SCMP_SYS(mprotect),
+ SCMP_SYS(set_robust_list),
+ SCMP_SYS(sendmmsg), /* DNS lookup */
+ SCMP_SYS(socketpair),
+ SCMP_SYS(statfs),
+ SCMP_SYS(uname),
+
+
+#ifdef REFCLOCK
+ SCMP_SYS(nanosleep),
+#endif
+#ifdef CLOCK_SHM
+ SCMP_SYS(shmget),
+ SCMP_SYS(shmat),
+ SCMP_SYS(shmdt),
+#endif
+
+ SCMP_SYS(fcntl64),
+ SCMP_SYS(fstat64),
+
+/* Arch Linux */
+ SCMP_SYS(getpid),
+ SCMP_SYS(gettid),
+ SCMP_SYS(geteuid),
+#ifdef __NR_ppoll
+#if !defined(__PNR_ppoll) && \
+ (SCMP_VER_MAJOR == 2) && (SCMP_VER_MINOR == 4) && (SCMP_VER_MICRO == 2)
+ /* Hack for Alpine Linux 3.11.3, 2020-Feb-23
+ * Earlier, Fedora had the same problem.
+ * ppoll is missing from /usr/include/seccomp-syscalls.h
+ */
+ #warning "Hack workaround for seccomp bug."
+ #define __PNR_ppoll -10241
+ #define __SNR_ppoll __PNR_ppoll
+#endif
+ SCMP_SYS(ppoll),
+#endif
+ SCMP_SYS(sendmsg),
+#ifdef __NR_geteuid32
+ SCMP_SYS(geteuid32),
+#endif
+
+#ifdef __NR_mmap
+ /* gentoo 64-bit and 32-bit, Intel and Arm use mmap */
+ SCMP_SYS(mmap),
+#endif
+#if defined(__aarch64__) || defined(__riscv)
+ SCMP_SYS(faccessat),
+ SCMP_SYS(renameat),
+ SCMP_SYS(linkat),
+ SCMP_SYS(unlinkat),
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__powerpc__)
+ SCMP_SYS(_newselect),
+ SCMP_SYS(_llseek),
+ SCMP_SYS(mmap2),
+ SCMP_SYS(send),
+ SCMP_SYS(stat64),
+#endif
diff --git a/etc/seccomp.list b/etc/seccomp.list
new file mode 100644
index 000000000..7b013bc07
--- /dev/null
+++ b/etc/seccomp.list
@@ -0,0 +1,106 @@
+prlimit64 # Fedora 26 64-bit
+###
+seccomp # must be in (pen)ultimate group
+###
+adjtimex
+bind
+brk
+chdir
+clock_adjtime
+clock_gettime
+clock_settime
+clone
+clone3
+close
+connect
+exit
+exit_group
+fcntl
+fcntl64
+fstat
+fstat64
+fsync
+futex
+getdents
+getdents64
+getegid
+geteuid
+geteuid32
+getgid
+getitimer
+getpid
+getrandom
+getrlimit
+getrusage
+getsockname
+getsockopt
+gettid
+gettimeofday
+getuid
+ioctl
+kill
+link
+listen
+_llseek
+lseek
+madvise
+membarrier
+mmap
+mmap2
+mprotect
+munmap
+nanosleep
+newfstatat
+_newselect
+open
+openat
+poll
+ppoll
+prlimit64
+pselect6
+read
+readv
+recvfrom
+recvmsg
+rename
+rseq
+rt_sigaction
+rt_sigprocmask
+rt_sigreturn
+select
+send
+sendmmsg
+sendmsg
+sendto
+setitimer
+setrlimit
+set_robust_list
+setsid
+setsockopt
+sigaction
+sigprocmask
+sigreturn
+socket
+socketcall
+socketpair
+stat
+stat64
+statfs
+statfs64
+sysinfo
+time
+timer_create
+timer_gettime
+timer_settime
+ugetrlimit
+uname
+unlink
+write
+writev
+
+prctl
+access
+readlink
+pipe2
+getresuid
+getresgid
diff --git a/include/ntpd.h b/include/ntpd.h
index e1a7a39a6..bf23e7a1a 100644
--- a/include/ntpd.h
+++ b/include/ntpd.h
@@ -488,6 +488,7 @@ void nts_timer(void);
/* ntp_sandbox.c */
#ifdef HAVE_SECCOMP_H
+bool sandbox_seccomp(char *fname, int skip);
void setup_SIGSYS_trap(void);
#endif
diff --git a/ntpd/ntp_sandbox.c b/ntpd/ntp_sandbox.c
index 8d82c8edd..eaa3cf215 100644
--- a/ntpd/ntp_sandbox.c
+++ b/ntpd/ntp_sandbox.c
@@ -273,7 +273,9 @@ getgroup:
} /* if (droproot) */
# endif /* ENABLE_DROPROOT */
-/* libseccomp sandboxing */
+ return nonroot;
+}
+
#if defined(HAVE_SECCOMP_H)
#ifdef ENABLE_KILL_ON_TRAP
@@ -281,220 +283,80 @@ getgroup:
#else
#define MY_SCMP_ACT SCMP_ACT_TRAP
#endif
- scmp_filter_ctx ctx = seccomp_init(MY_SCMP_ACT);
+bool sandbox_seccomp(char *fname, int skip) {
+ FILE *stream = NULL;
+ int slice = 0;
+ int lineno = 0;
+ size_t maxlen = 0;
+ char *lbuf = NULL;
+ char *token = NULL;
+ if (NULL == fname) {
+ msyslog(LOG_ERR, "INIT: seccomp: unset call list file name");
+ return false;
+ }
setup_SIGSYS_trap();
-
+ scmp_filter_ctx ctx = seccomp_init(MY_SCMP_ACT);
if (NULL == ctx) {
- msyslog(LOG_ERR, "INIT: sandbox: seccomp_init() failed: %s", strerror(errno));
- exit (1);
+ msyslog(LOG_ERR, "INIT: seccomp: seccomp_init() failed: %s", strerror(errno));
+ return false;
}
-int scmp_sc[] = {
-
-#ifdef ENABLE_EARLY_DROPROOT
-/* Initialization uses a few syscalls that are not otherwise used.
- * Collect them here.
- * There are probably a few below that were added before we
- * understood the need for this section.
- * We could make a second pass after initialization to remove
- * these from the list.
- */
-
-#ifdef __NR_prlimit64
- SCMP_SYS(prlimit64), /* 64 bit Fedora 26 with early_droproot*/
-#endif
-#endif /* ENABLE_EARLY_DROPROOT */
-
- SCMP_SYS(accept),
- SCMP_SYS(access),
- SCMP_SYS(adjtimex),
- SCMP_SYS(bind),
- SCMP_SYS(brk),
- SCMP_SYS(chdir),
- SCMP_SYS(clock_adjtime),
- SCMP_SYS(clock_gettime),
- SCMP_SYS(clock_settime),
- SCMP_SYS(close),
- SCMP_SYS(connect),
- SCMP_SYS(exit),
- SCMP_SYS(exit_group),
- SCMP_SYS(fcntl),
- SCMP_SYS(fstat),
- SCMP_SYS(fsync),
- SCMP_SYS(futex), /* sem_xxx, used by threads */
- SCMP_SYS(getdents), /* Scanning /etc/ntp.d/ */
- SCMP_SYS(getegid), /* Needed on Alpine */
- SCMP_SYS(getgid), /* Needed on Alpine */
- SCMP_SYS(getdents64),
-
-#ifdef __NR_getrandom
- SCMP_SYS(getrandom), /* Added in 3.17 kernel */
-#endif
-#ifdef __NR_ugetrlimit
- SCMP_SYS(ugetrlimit), /* sysconf */
-#endif
-#ifdef __NR_getrlimit
- SCMP_SYS(getrlimit), /* sysconf */
- SCMP_SYS(setrlimit),
-#endif
- SCMP_SYS(getrusage),
- SCMP_SYS(getsockname),
- SCMP_SYS(getsockopt),
- SCMP_SYS(gettimeofday), /* mkstemp */
- SCMP_SYS(getuid), /* Needed on Alpine */
- SCMP_SYS(ioctl),
- SCMP_SYS(link),
- SCMP_SYS(listen),
- SCMP_SYS(lseek),
- SCMP_SYS(membarrier), /* Needed on Alpine 3.11.3 */
- SCMP_SYS(munmap),
- SCMP_SYS(newfstatat),
- SCMP_SYS(open),
-#ifdef __NR_openat
- SCMP_SYS(openat), /* SUSE */
-#endif
- SCMP_SYS(poll),
- SCMP_SYS(pselect6),
- SCMP_SYS(read),
- SCMP_SYS(readv), /* nscd getaddrinfo() provider */
- SCMP_SYS(recvfrom), /* Comment this out for testing.
- * It will die on the first reply.
- * (Or maybe sooner if a request arrives.)
- */
- SCMP_SYS(recvmsg),
- SCMP_SYS(rename),
- SCMP_SYS(rt_sigaction),
- SCMP_SYS(rt_sigprocmask),
- SCMP_SYS(rt_sigreturn),
-#ifdef __NR_rseq
- SCMP_SYS(rseq), /* needed by glibc-2.35+ for resumable sequences */
-#endif
- SCMP_SYS(sigaction),
- SCMP_SYS(sigprocmask),
- SCMP_SYS(sigreturn),
-#ifdef __NR_select
- SCMP_SYS(select), /* not in ARM */
-#endif
- SCMP_SYS(sendto),
- SCMP_SYS(setsid),
-#ifdef __NR_setsockopt
- SCMP_SYS(setsockopt), /* not in old kernels */
-#endif
- SCMP_SYS(socket),
- SCMP_SYS(socketcall), /* old kernels */
- SCMP_SYS(stat),
- SCMP_SYS(statfs64), /* from getaddrinfo after lid open */
-#ifdef __NR_time
- SCMP_SYS(time), /* not in ARM */
-#endif
- SCMP_SYS(sysinfo),
-#ifdef HAVE_TIMER_CREATE
- SCMP_SYS(timer_create),
- SCMP_SYS(timer_gettime),
- SCMP_SYS(timer_settime),
-#else
- SCMP_SYS(getitimer),
- SCMP_SYS(setitimer),
-#endif
- SCMP_SYS(write),
- SCMP_SYS(writev), /* Needed on Alpine 3.11.3 */
- SCMP_SYS(unlink),
-
-/* Don't comment out this block for testing.
- * pthread_create blocks signals so it will crash
- * rather than generate a trap.
- */
- SCMP_SYS(clone), /* threads */
- SCMP_SYS(clone3),
- SCMP_SYS(kill), /* generate signal */
- SCMP_SYS(madvise),
- SCMP_SYS(mprotect),
- SCMP_SYS(set_robust_list),
- SCMP_SYS(sendmmsg), /* DNS lookup */
- SCMP_SYS(socketpair),
- SCMP_SYS(statfs),
- SCMP_SYS(uname),
-
-
-#ifdef REFCLOCK
- SCMP_SYS(nanosleep),
-#endif
-#ifdef CLOCK_SHM
- SCMP_SYS(shmget),
- SCMP_SYS(shmat),
- SCMP_SYS(shmdt),
-#endif
-
- SCMP_SYS(fcntl64),
- SCMP_SYS(fstat64),
-
-/* Arch Linux */
- SCMP_SYS(getpid),
- SCMP_SYS(gettid),
- SCMP_SYS(geteuid),
-#ifdef __NR_ppoll
-#if !defined(__PNR_ppoll) && \
- (SCMP_VER_MAJOR == 2) && (SCMP_VER_MINOR == 4) && (SCMP_VER_MICRO == 2)
- /* Hack for Alpine Linux 3.11.3, 2020-Feb-23
- * Earlier, Fedora had the same problem.
- * ppoll is missing from /usr/include/seccomp-syscalls.h
- */
- #warning "Hack workaround for seccomp bug."
- #define __PNR_ppoll -10241
- #define __SNR_ppoll __PNR_ppoll
-#endif
- SCMP_SYS(ppoll),
-#endif
- SCMP_SYS(sendmsg),
-#ifdef __NR_geteuid32
- SCMP_SYS(geteuid32),
-#endif
-
-#ifdef __NR_mmap
- /* gentoo 64-bit and 32-bit, Intel and Arm use mmap */
- SCMP_SYS(mmap),
-#endif
-#if defined(__aarch64__) || defined(__riscv)
- SCMP_SYS(faccessat),
- SCMP_SYS(renameat),
- SCMP_SYS(linkat),
- SCMP_SYS(unlinkat),
-#endif
-#if defined(__i386__) || defined(__arm__) || defined(__powerpc__)
- SCMP_SYS(_newselect),
- SCMP_SYS(_llseek),
- SCMP_SYS(mmap2),
- SCMP_SYS(send),
- SCMP_SYS(stat64),
-#endif
-};
- {
- for (unsigned int i = 0; i < COUNTOF(scmp_sc); i++) {
- if (seccomp_rule_add(ctx,
- SCMP_ACT_ALLOW, scmp_sc[i], 0) < 0) {
- msyslog(LOG_ERR,
- "INIT: sandbox: seccomp_rule_add() failed: %s", strerror(errno));
- exit(1);
- }
+ stream = fopen(fname, "r");
+ if (NULL == stream) {
+ msyslog(LOG_ERR, "INIT: seccomp: failed to open list '%s'", fname);
+ return false;
+ }
+ while (true) {
+ int len;
+ int syscall = __NR_SCMP_ERROR;
+ len = getline(&lbuf, &maxlen, stream);
+ if (-1 == len) {
+ free(lbuf);
+ lbuf = NULL;
+ break;
+ }
+ lineno++;
+ if (0 == strncmp("###", lbuf, 3)) {
+ slice++;
+ continue;
+ }
+ if (slice < skip) {
+ continue;
+ }
+ sscanf(lbuf, "%ms", &token);
+ free(lbuf);
+ lbuf = NULL;
+ if (NULL == token) {
+ continue;
+ }
+ syscall = seccomp_syscall_resolve_name(token);
+ free(token);
+ if (__NR_SCMP_ERROR == syscall) {
+ msyslog(LOG_ERR, "INIT: seccomp: could not resolve syscall '%s' on line %d", token, lineno);
+ continue;
+ }
+ if (seccomp_rule_add(ctx,
+ SCMP_ACT_ALLOW, syscall, 0) < 0) {
+ msyslog(LOG_ERR,
+ "INIT: seccomp: seccomp_rule_add() failed: %s", strerror(errno));
}
}
+ fclose(stream);
if (0) {
/* maybe helps debugging if it's crashing during msyslog */
- msyslog(LOG_NOTICE, "INIT: sandbox: enabling seccomp.");
+ msyslog(LOG_NOTICE, "INIT: seccomp: enabling seccomp.");
}
if (seccomp_load(ctx) < 0) {
- msyslog(LOG_ERR, "INIT: sandbox: seccomp_load() failed: %s", strerror(errno));
- exit(1);
+ msyslog(LOG_ERR, "INIT: seccomp: seccomp_load() failed: %s", strerror(errno));
+ return false;
}
- msyslog(LOG_NOTICE, "INIT: sandbox: seccomp enabled.");
+ msyslog(LOG_NOTICE, "INIT: seccomp: seccomp enabled.");
seccomp_release(ctx);
-
-#endif /* HAVE_SECCOMP_H */
-
- return nonroot;
+ return true;
}
+#endif /* HAVE_SECCOMP_H */
#if defined(HAVE_PRIV_NTP_ADJTIME) && defined(ENABLE_DROPROOT)
void CheckFreeBSDdroproot(uid_t uid) {
diff --git a/ntpd/ntpd.c b/ntpd/ntpd.c
index b76c3b22d..62c91c9e7 100644
--- a/ntpd/ntpd.c
+++ b/ntpd/ntpd.c
@@ -31,6 +31,10 @@
#include "recvbuff.h"
+#ifdef HAVE_SECCOMP_H
+char *f_syscall_list = "/var/lib/ntp/seccomp.list";
+#endif // HAVE_SECCOMP_H
+
void catchQuit (int sig);
static volatile int signo = 0;
/* In an ideal world, 'finish_safe()' would declared as noreturn... */
@@ -766,6 +770,9 @@ main(
msyslog(LOG_INFO, "INIT: successfully locked into RAM");
}
+#ifdef HAVE_SECCOMP_H
+ sandbox_seccomp(f_syscall_list, 0);
+#endif // HAVE_SECCOMP_H
#ifdef ENABLE_EARLY_DROPROOT
/* drop root privileges */
/* This doesn't work on NetBSD or with SHM */
@@ -860,6 +867,9 @@ main(
nts_init(); /* Before droproot */
#endif
+#ifdef HAVE_SECCOMP_H
+ sandbox_seccomp(f_syscall_list, 1);
+#endif // HAVE_SECCOMP_H
#ifndef ENABLE_EARLY_DROPROOT
/* drop root privileges */
if (sandbox(droproot, user, group, chrootdir, interface_interval!=0) && interface_interval) {
--
2.38.1
_______________________________________________
devel mailing list
devel@ntpsec.org
https://lists.ntpsec.org/mailman/listinfo/devel