Skip to content

Instantly share code, notes, and snippets.

@syncom
Last active January 11, 2022 01:24
Show Gist options
  • Save syncom/d525332374a21c74883bd22b43d94120 to your computer and use it in GitHub Desktop.
Save syncom/d525332374a21c74883bd22b43d94120 to your computer and use it in GitHub Desktop.
Linux Security Summit North America 2019 note: libseccomp tutorial

The why and how of libseccomp

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

Tutorial

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment