specific directives.
*-F* _level_::
-This option configures a system call filter when *chronyd* is compiled with
-support for the Linux secure computing (seccomp) facility. In level 1 the
-process is killed when a forbidden system call is made, in level -1 the SIGSYS
-signal is thrown instead and in level 0 the filter is disabled. The default
-value is 0.
+This option configures system call filters loaded by *chronyd* processes if it
+was compiled with support for the Linux secure computing (seccomp) facility.
+Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At
+levels 1 and 2, *chronyd* will be killed if it makes a system call which is
+blocked by the filters. The level can be specified as a negative number to
+trigger the SIGSYS signal instead of SIGKILL, which can be useful for
+debugging. The default value is 0.
+
-It is recommended to enable the filter only when it is known to work on the
-version of the system where *chrony* is installed as the filter needs to allow
-also system calls made from libraries that *chronyd* is using (e.g. libc) and
-different versions or implementations of the libraries might make different
-system calls. If the filter is missing some system call, *chronyd* could be
-killed even in normal operation.
+At level 1, the filters allow only selected system calls that are normally
+expected to be made by *chronyd*. Other system calls are blocked. This level is
+recommended only if it is known to work on the version of the system where
+*chrony* is installed. The filters need to allow also system calls made by
+libraries that *chronyd* is using (e.g. libc), but different versions or
+implementations of the libraries might make different system calls. If the
+filters are missing a system call, *chronyd* could be killed even in normal
+operation.
+
-The filter cannot be used with the *mailonchange* directive.
+At level 2, the filters block only a small number of specific system calls
+(e.g. fork and exec). This approach should avoid false positives, but the
+protection of the system against a compromised *chronyd* process is much more
+limited.
++
+The filters cannot be enabled with the *mailonchange* directive.
*-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the
void
SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
{
- const int syscalls[] = {
+ const int allowed[] = {
/* Clock */
SCMP_SYS(adjtimex),
SCMP_SYS(clock_adjtime),
SCMP_SYS(uname),
};
+ const int denied_any[] = {
+ SCMP_SYS(execve),
+ SCMP_SYS(execveat),
+ SCMP_SYS(fork),
+ SCMP_SYS(ptrace),
+ SCMP_SYS(vfork),
+ };
+
+ const int denied_ntske[] = {
+ SCMP_SYS(ioctl),
+ SCMP_SYS(setsockopt),
+ SCMP_SYS(socket),
+ };
+
const int socket_domains[] = {
AF_NETLINK, AF_UNIX, AF_INET,
#ifdef FEAT_IPV6
#endif
};
+ unsigned int default_action, deny_action;
scmp_filter_ctx *ctx;
int i;
+ /* Sign of the level determines the deny action (kill or SIGSYS).
+ At level 1, selected syscalls are allowed, others are denied.
+ At level 2, selected syscalls are denied, others are allowed. */
+
+ deny_action = level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP;
+ if (level < 0)
+ level = -level;
+
+ switch (level) {
+ case 1:
+ default_action = deny_action;
+ break;
+ case 2:
+ default_action = SCMP_ACT_ALLOW;
+ break;
+ default:
+ LOG_FATAL("Unsupported filter level");
+ }
+
if (context == SYS_MAIN_PROCESS) {
/* Check if the chronyd configuration is supported */
check_seccomp_applicability();
- /* Start the helper process, which will run without any seccomp filter. It
- will be used for getaddrinfo(), for which it's difficult to maintain a
- list of required system calls (with glibc it depends on what NSS modules
- are installed and enabled on the system). */
- PRV_StartHelper();
+ /* At level 1, start a helper process which will not have a seccomp filter.
+ It will be used for getaddrinfo(), for which it is difficult to maintain
+ a list of required system calls (with glibc it depends on what NSS
+ modules are installed and enabled on the system). */
+ if (default_action != SCMP_ACT_ALLOW)
+ PRV_StartHelper();
}
- ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP);
+ ctx = seccomp_init(default_action);
if (ctx == NULL)
LOG_FATAL("Failed to initialize seccomp");
- /* Add system calls that are always allowed */
- for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) {
- if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) < 0)
- goto add_failed;
+ if (default_action != SCMP_ACT_ALLOW) {
+ for (i = 0; i < sizeof (allowed) / sizeof (*allowed); i++) {
+ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, allowed[i], 0) < 0)
+ goto add_failed;
+ }
+ } else {
+ for (i = 0; i < sizeof (denied_any) / sizeof (*denied_any); i++) {
+ if (seccomp_rule_add(ctx, deny_action, denied_any[i], 0) < 0)
+ goto add_failed;
+ }
+
+ if (context == SYS_NTSKE_HELPER) {
+ for (i = 0; i < sizeof (denied_ntske) / sizeof (*denied_ntske); i++) {
+ if (seccomp_rule_add(ctx, deny_action, denied_ntske[i], 0) < 0)
+ goto add_failed;
+ }
+ }
}
- if (context == SYS_MAIN_PROCESS) {
+ if (default_action != SCMP_ACT_ALLOW && context == SYS_MAIN_PROCESS) {
/* Allow opening sockets in selected domains */
for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
if (seccomp_load(ctx) < 0)
LOG_FATAL("Failed to load seccomp rules");
- LOG(context == SYS_MAIN_PROCESS ? LOGS_INFO : LOGS_DEBUG, "Loaded seccomp filter");
+ LOG(context == SYS_MAIN_PROCESS ? LOGS_INFO : LOGS_DEBUG,
+ "Loaded seccomp filter (level %d)", level);
seccomp_release(ctx);
return;