Skip to content

Commit

Permalink
cmd/snap-seccomp: add 'dump' command (#14798)
Browse files Browse the repository at this point in the history
* cmd/snap-seccomp: add 'dump' command

Add a command to dump the filter rules from a compiled profile to
separate files.

Signed-off-by: Maciej Borzecki <[email protected]>

* tests/main/snap-seccomp: dump profile content

Extend test to dump existing profile.

Signed-off-by: Maciej Borzecki <[email protected]>

---------

Signed-off-by: Maciej Borzecki <[email protected]>
  • Loading branch information
bboozzoo authored Jan 30, 2025
1 parent df374bd commit 6446649
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 14 deletions.
1 change: 1 addition & 0 deletions cmd/snap-seccomp/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var (
VersionInfo = versionInfo
GoSeccompFeatures = goSeccompFeatures
ExportBPF = exportBPF
Dump = dump
)

func MockArchDpkgArchitecture(f func() string) (restore func()) {
Expand Down
76 changes: 62 additions & 14 deletions cmd/snap-seccomp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -842,25 +842,25 @@ func exportBPF(fout *os.File, filter *seccomp.ScmpFilter) (bpfLen int64, err err
// the most restrictive action, thus any explicit deny will take precedence.
// This struct needs to be in sync with seccomp-support.c
type scSeccompFileHeader struct {
header [2]byte
version byte
Header [2]byte
Version byte
// flags
unrestricted byte
Unrestricted byte
// unused
padding [4]byte
Padding [4]byte
// location of allow/deny, all offsets/len in bytes
lenAllowFilter uint32
lenDenyFilter uint32
LenAllowFilter uint32
LenDenyFilter uint32
// reserved for future use
reserved2 [112]byte
Reserved2 [112]byte
}

func writeUnrestrictedFilter(outFile string) error {
hdr := scSeccompFileHeader{
header: [2]byte{'S', 'C'},
version: 0x1,
Header: [2]byte{'S', 'C'},
Version: 0x1,
// tell snap-confine
unrestricted: 0x1,
Unrestricted: 0x1,
}
fout, err := osutil.NewAtomicFile(outFile, 0644, 0, osutil.NoChown, osutil.NoChown)
if err != nil {
Expand All @@ -885,8 +885,8 @@ func writeSeccompFilter(outFile string, filterAllow, filterDeny *seccomp.ScmpFil
// seccomp filters yet and the only way to know is to export to
// a file (until seccomp_export_bpf_mem() becomes available)
hdr := scSeccompFileHeader{
header: [2]byte{'S', 'C'},
version: 0x1,
Header: [2]byte{'S', 'C'},
Version: 0x1,
}
if err := binary.Write(fout, arch.Endian(), hdr); err != nil {
return err
Expand All @@ -901,8 +901,8 @@ func writeSeccompFilter(outFile string, filterAllow, filterDeny *seccomp.ScmpFil
}

// now write final header
hdr.lenAllowFilter = uint32(allowSize)
hdr.lenDenyFilter = uint32(denySize)
hdr.LenAllowFilter = uint32(allowSize)
hdr.LenDenyFilter = uint32(denySize)
if _, err := fout.Seek(0, io.SeekStart); err != nil {
return err
}
Expand Down Expand Up @@ -1040,6 +1040,46 @@ func showSeccompLibraryVersion() error {
return nil
}

func dump(what, prefix string) error {
f, err := os.Open(what)
if err != nil {
return err
}
defer f.Close()

var hdr scSeccompFileHeader

if err := binary.Read(f, arch.Endian(), &hdr); err != nil {
return fmt.Errorf("cannot read header: %w", err)
}

if !bytes.Equal(hdr.Header[:], []byte{'S', 'C'}) || hdr.Version != 0x01 {
return fmt.Errorf("unsupported header: %x version %v", hdr.Header, hdr.Version)
}

var allowRules bytes.Buffer
var denyRules bytes.Buffer

if _, err := io.CopyN(&allowRules, f, int64(hdr.LenAllowFilter)); err != nil {
return fmt.Errorf("cannot copy allow rules: %w", err)
}

if _, err := io.CopyN(&denyRules, f, int64(hdr.LenDenyFilter)); err != nil {
return fmt.Errorf("cannot copy deny rules: %w", err)
}

if err := os.WriteFile(prefix+".allow", allowRules.Bytes(), 0644); err != nil {
return fmt.Errorf("cannot write allow rules to file: %w", err)
}

if err := os.WriteFile(prefix+".deny", denyRules.Bytes(), 0644); err != nil {
// TODO remove allow file?
return fmt.Errorf("cannot write deny rules to file: %w", err)
}

return nil
}

func main() {
var err error
var content []byte
Expand All @@ -1065,6 +1105,14 @@ func main() {
err = showSeccompLibraryVersion()
case "version-info":
err = showVersionInfo()
case "dump":
if len(os.Args) < 4 {
fmt.Println("dump needs <file> and <prefix>")
os.Exit(1)
}
what := os.Args[2]
prefix := os.Args[3]
err = dump(what, prefix)
default:
err = fmt.Errorf("unsupported argument %q", cmd)
}
Expand Down
24 changes: 24 additions & 0 deletions cmd/snap-seccomp/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -923,3 +923,27 @@ func (s *snapSeccompSuite) TestExportBpfErrors(c *C) {
_, err = main.ExportBPF(fout, &seccomp.ScmpFilter{})
c.Check(err, ErrorMatches, "cannot export bpf filter: filter is invalid or uninitialized")
}

func (s *snapSeccompSuite) TestDump(c *C) {
dir := c.MkDir()
bpfPath := filepath.Join(dir, "bpf")
prof := `
execve
uname
~ioctl
`
err := main.Compile([]byte(prof), bpfPath)
c.Assert(err, IsNil)
err = main.Dump(filepath.Join(dir, "foo"), filepath.Join(dir, "foo-dump"))
c.Assert(err, ErrorMatches, "open .*/foo: no such file or directory")

err = main.Dump(filepath.Join(dir, "bpf"), filepath.Join(dir, "bpf"))
c.Assert(err, IsNil)
fi, err := os.Stat(filepath.Join(dir, "bpf.allow"))
c.Assert(err, IsNil)
c.Check(fi.Size() > 10, Equals, true)

fi, err = os.Stat(filepath.Join(dir, "bpf.deny"))
c.Assert(err, IsNil)
c.Check(fi.Size() > 10, Equals, true)
}
5 changes: 5 additions & 0 deletions tests/main/snap-seccomp/task.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ execute: |
SNAP_SECCOMP="$MOUNT_DIR/snapd/current$SNAP_SECCOMP"
fi
# we can dump existing profile
$SNAP_SECCOMP dump "${PROFILE}.bin2" "$PWD/bpf-dump"
test -s "$PWD/bpf-dump.allow"
test -s "$PWD/bpf-dump.deny"
# from the old test_complain
echo "Test that the @complain keyword works"
rm -f "${PROFILE}.bin2"
Expand Down

0 comments on commit 6446649

Please sign in to comment.