Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0126a77
snap-confine, snap-seccomp: Default to SECCOMP_RET_ERRNO
tyhicks Jul 27, 2017
25d3024
cmd/snap-seccomp: Use seccomp's SECCOMP_RET_LOG mode for devmode
tyhicks Jul 28, 2017
3926506
snap-confine: Add curly braces to single line conditional blocks
tyhicks Oct 3, 2017
b1ba332
snap-seccomp: Add comment describing seccomp fallback behavior
tyhicks Oct 3, 2017
1b97890
snap-seccomp: update tests for new ActErrno behaviour
mvo5 Oct 5, 2017
c09d060
snap-seccomp: simplify tests, remove use of main.SeccompRetKill
mvo5 Oct 5, 2017
34af16c
snap-seccomp: update github.com/mvo5/libseccomp-golang revision
mvo5 Oct 5, 2017
0e1758b
snap-seccomp: implement preprocessing suggestion from jdstrand
mvo5 Oct 5, 2017
c24e6d3
run `make fmt`
mvo5 Oct 6, 2017
b8bed79
Merge remote-tracking branch 'upstream/master' into HEAD
mvo5 Oct 6, 2017
32f2598
Merge remote-tracking branch 'upstream/master' into HEAD
mvo5 Nov 8, 2017
beb7751
Merge remote-tracking branch 'upstream/master' into HEAD
mvo5 Jan 16, 2018
bb359f1
debian: ensure we use the right libseccomp-dev version
mvo5 Jan 16, 2018
f77345f
cmd/snap-confine: add seccomp prototype (we build with -Wstrict-proto…
mvo5 Jan 16, 2018
dd0e16f
debian: drop version in seccomp-dev b-d
mvo5 Jan 29, 2018
570c9e3
Merge remote-tracking branch 'upstream/master' into HEAD
mvo5 Jan 29, 2018
3edf853
Merge remote-tracking branch 'origin/master' into seccomp-logging
tyhicks Mar 2, 2018
00e4c9e
snap-seccomp: conditionally use ActLog for devmode
tyhicks Mar 2, 2018
3ff75cd
address review feedback; implement system-key for seccomp logging, ad…
Mar 5, 2018
d496b76
small test setup cleanup
Mar 7, 2018
4bf7516
interfaces/seccomp: mock seccomp actions for testing
zyga Mar 7, 2018
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is seccomp defined in the pre-processor usually?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the seccomp() syscall may not have a glibc wrapper:

https://lwn.net/Articles/655028/

It is important that we wrap it in that situation.

// 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

//#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