Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions cmd/snap-confine/seccomp-support.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <string.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

Expand All @@ -35,6 +36,21 @@
#include "../libsnap-confine-private/string-utils.h"
#include "../libsnap-confine-private/utils.h"

#ifndef SECCOMP_FILTER_FLAG_LOG
#define SECCOMP_FILTER_FLAG_LOG 2
#endif

#ifndef seccomp
// prototype because we build with -Wstrict-prototypes
int seccomp(unsigned int operation, unsigned int flags, void *args);

int seccomp(unsigned int operation, unsigned int flags, void *args)
{
errno = 0;
return syscall(__NR_seccomp, operation, flags, args);
}
#endif

static const char *filter_profile_dir = "/var/lib/snapd/seccomp/bpf/";

// MAX_BPF_SIZE is an arbitrary limit.
Expand Down Expand Up @@ -202,8 +218,19 @@ int sc_apply_seccomp_bpf(const char *filter_profile)
.len = num_read / sizeof(struct sock_filter),
.filter = (struct sock_filter *)bpf,
};
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) {
die("cannot apply seccomp profile");
if (seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG, &prog) != 0) {
if (errno == ENOSYS) {
debug("kernel doesn't support the seccomp(2) syscall");
} else if (errno == EINVAL) {
debug
("kernel may not support the SECCOMP_FILTER_FLAG_LOG flag");
}

debug
("falling back to prctl(2) syscall to load seccomp filter");
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) {
die("cannot apply seccomp profile");
}
}
// drop privileges again
debug("dropping privileges after loading seccomp profile");
Expand Down
8 changes: 8 additions & 0 deletions cmd/snap-seccomp/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,11 @@ func MockArchUbuntuKernelArchitecture(f func() string) (restore func()) {
archUbuntuKernelArchitecture = realArchUbuntuKernelArchitecture
}
}

func MockErrnoOnDenial(i int16) (retore func()) {
origErrnoOnDenial := errnoOnDenial
errnoOnDenial = i
return func() {
errnoOnDenial = origErrnoOnDenial
}
}
104 changes: 76 additions & 28 deletions cmd/snap-seccomp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ package main
//#define SCMP_ARCH_S390X ARCH_BAD
//#endif
//
//#ifndef SECCOMP_RET_LOG
//#define SECCOMP_RET_LOG 0x7ffc0000U
//#endif
//
//typedef struct seccomp_data kernel_seccomp_data;
//
Expand Down Expand Up @@ -390,6 +393,7 @@ var seccompResolver = map[string]uint64{
const (
SeccompRetAllow = C.SECCOMP_RET_ALLOW
SeccompRetKill = C.SECCOMP_RET_KILL
SeccompRetLog = C.SECCOMP_RET_LOG
)

// UbuntuArchToScmpArch takes a dpkg architecture and converts it to
Expand Down Expand Up @@ -610,49 +614,93 @@ func addSecondaryArches(secFilter *seccomp.ScmpFilter) error {
return nil
}

var errnoOnDenial int16 = C.EPERM

func preprocess(content []byte) (unrestricted, complain bool) {
scanner := bufio.NewScanner(bytes.NewBuffer(content))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
switch line {
case "@unrestricted":
unrestricted = true
case "@complain":
complain = true
}
}
return unrestricted, complain
}

func complainAction() seccomp.ScmpAction {
// XXX: Work around some distributions not having a new enough
// libseccomp-golang that declares ActLog. Instead, we'll guess at its
// value by adding one to ActAllow and then verify that the string
// representation is what we expect for ActLog. The value and string is
// defined in https://github.com/seccomp/libseccomp-golang/pull/29.
//
// Ultimately, the fix for this workaround is to be able to use the
// GetApi() function created in the PR above. It'll tell us if the
// kernel, libseccomp, and libseccomp-golang all support ActLog.
var actLog seccomp.ScmpAction = seccomp.ActAllow + 1

if actLog.String() == "Action: Log system call" {
return actLog
}

// Because ActLog is functionally ActAllow with logging, if we don't
// support ActLog, fallback to ActLog.
return seccomp.ActAllow
}

func compile(content []byte, out string) error {
var err error
var secFilter *seccomp.ScmpFilter

secFilter, err = seccomp.NewFilter(seccomp.ActKill)
unrestricted, complain := preprocess(content)
switch {
case unrestricted:
return osutil.AtomicWrite(out, bytes.NewBufferString("@unrestricted\n"), 0644, 0)
case complain:
var complainAct seccomp.ScmpAction = complainAction()

secFilter, err = seccomp.NewFilter(complainAct)
if err != nil {
if complainAct != seccomp.ActAllow {
// ActLog is only supported in newer versions
// of the kernel, libseccomp, and
// libseccomp-golang. Attempt to fall back to
// ActAllow before erroring out.
complainAct = seccomp.ActAllow
secFilter, err = seccomp.NewFilter(complainAct)
}
}

// Set unrestricted to 'true' to fallback to the pre-ActLog
// behavior of simply setting the allow filter without adding
// any rules.
if complainAct == seccomp.ActAllow {
unrestricted = true
}
default:
secFilter, err = seccomp.NewFilter(seccomp.ActErrno.SetReturnCode(errnoOnDenial))
}
if err != nil {
return fmt.Errorf("cannot create seccomp filter: %s", err)
}

if err := addSecondaryArches(secFilter); err != nil {
return err
}

scanner := bufio.NewScanner(bytes.NewBuffer(content))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())

// special case: unrestricted means we stop early, we just
// write this special tag and evalulate in snap-confine
if line == "@unrestricted" {
return osutil.AtomicWrite(out, bytes.NewBufferString(line+"\n"), 0644, 0)
}
// complain mode is a "allow-all" filter for now until
// we can land https://github.com/snapcore/snapd/pull/3998
if line == "@complain" {
secFilter, err = seccomp.NewFilter(seccomp.ActAllow)
if err != nil {
return fmt.Errorf("cannot create seccomp filter: %s", err)
}
if err := addSecondaryArches(secFilter); err != nil {
return err
if !unrestricted {
scanner := bufio.NewScanner(bytes.NewBuffer(content))
for scanner.Scan() {
if err := parseLine(scanner.Text(), secFilter); err != nil {
return fmt.Errorf("cannot parse line: %s", err)
}
break
}

// look for regular syscall/arg rule
if err := parseLine(line, secFilter); err != nil {
return fmt.Errorf("cannot parse line: %s", err)
if scanner.Err(); err != nil {
return err
}
}
if scanner.Err(); err != nil {
return err
}

if osutil.GetenvBool("SNAP_SECCOMP_DEBUG") {
secFilter.ExportPFC(os.Stdout)
Expand Down
Loading