This is my Linux Security Summit North America (LSS-NA) 2019 note taken in Day 1 tutorial session "The Why and How of libseccomp" by Tom Hromatka, Oracle & Paul Moore, Cisco.
-
Why libseccomp
- Focus on containing bugs and limiting the risks - what mitigation is for
- system hardening, access controls, and syscall filtering (libseccomp)
-
Syscall filtering
- Enables the kernel to intercept syscalls and take action
- Configured on a per-thread or per-process basis
-
History
- seccomp mode 1 first appeared in Linux 2.6.23; it has limitations and is not flexible
- seccomp mode 2, aka, seccomp-bpf first appeared in Linux 3.5; syscall filters are defined by a filtering language; supports more architectures/ABIs
Room for improvement
- Syscalls are different for each architecture
- BPF language is 32-bit
- Good, non-trivial filters can be cumbersome
- Learning curve may be too steep for developments
seccomp vs SELinux?
- seccomp: discretionary; SELinux: mandatory
Whitelist vs Blacklist?
- Positives and negatives, mostly a balance between security and flexibility
Source file: lss.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <seccomp.h>
#include <errno.h>
#include <string.h>
static const char const lss[] = "LSS NA 2019\n";
void call_child(void)
{
scmp_filter_ctx ctx;
int rc;
// whitelist
ctx = seccomp_init(SCMP_ACT_ERRNO(EPERM));
// blacklist
// ctx = seccomp_init(SCMP_ALLOW);
//rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW,
// SCMP_SYS(write), 0);
rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW,
SCMP_SYS(write), 2,
SCMP_A0(SCMP_CMP_EQ, STDOUT_FILENO),
SCMP_A1(SCMP_CMP_EQ, (uint64_t)(lss)));
if (rc < 0) {
fprintf(stdout, "rule add failed %d\n",
rc);
goto out;
}
rc = seccomp_load(ctx);
if (rc < 0) {
fprintf(stdout, "load failed %d\n",
rc);
goto out;
}
//fprintf(stdout, "hello world1\n");
//fprintf(stderr, "hello world2\n");
write(STDOUT_FILENO, lss, strlen(lss));
//fprintf(stdout, lss);
out:
seccomp_release(ctx);
}
int main(void)
{
pid_t cpid;
fprintf(stdout, "main\n");
cpid = fork();
if (cpid < 0) {
fprintf(stdout, "fork failed\n");
exit(-1);
}
if (cpid == 0) {
// in the child
fprintf(stdout, "child\n");
call_child();
}
else {
fprintf(stdout, "parent\n");
}
}
The code above demonstrates a whitelist approach of syscall and argument
filtering of the write()
system call (and its first two arguments).
Compile the above code with gcc lss.c -lseccomp
. You may need to install
libseccomp headers and libraries for compilation to succeed. Run the code with
./a.out
(or strace -ff ./a.out
). You should see the following output
main
parent
child
LSS NA 2019