510 lines
12 KiB
Go
510 lines
12 KiB
Go
|
// +build linux
|
||
|
|
||
|
// Tests for public API of libseccomp Go bindings
|
||
|
|
||
|
package seccomp
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"syscall"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
// Type Function Tests
|
||
|
|
||
|
type versionErrorTest struct {
|
||
|
err VersionError
|
||
|
str string
|
||
|
}
|
||
|
|
||
|
var versionStr = fmt.Sprintf("%d.%d.%d", verMajor, verMinor, verMicro)
|
||
|
|
||
|
var versionErrorTests = []versionErrorTest{
|
||
|
{
|
||
|
VersionError{
|
||
|
"deadbeef",
|
||
|
"x.y.z",
|
||
|
},
|
||
|
"Libseccomp version too low: deadbeef: " +
|
||
|
"minimum supported is x.y.z: detected " + versionStr,
|
||
|
},
|
||
|
{
|
||
|
VersionError{
|
||
|
"",
|
||
|
"x.y.z",
|
||
|
},
|
||
|
"Libseccomp version too low: minimum supported is x.y.z: " +
|
||
|
"detected " + versionStr,
|
||
|
},
|
||
|
{
|
||
|
VersionError{
|
||
|
"deadbeef",
|
||
|
"",
|
||
|
},
|
||
|
"Libseccomp version too low: " +
|
||
|
"deadbeef: minimum supported is 2.1.0: " +
|
||
|
"detected " + versionStr,
|
||
|
},
|
||
|
{
|
||
|
VersionError{
|
||
|
"",
|
||
|
"",
|
||
|
},
|
||
|
"Libseccomp version too low: minimum supported is 2.1.0: " +
|
||
|
"detected " + versionStr,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
func TestVersionError(t *testing.T) {
|
||
|
for i, test := range versionErrorTests {
|
||
|
str := test.err.Error()
|
||
|
if str != test.str {
|
||
|
t.Errorf("VersionError %d: got %q: expected %q", i, str, test.str)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestActionSetReturnCode(t *testing.T) {
|
||
|
if ActInvalid.SetReturnCode(0x0010) != ActInvalid {
|
||
|
t.Errorf("Able to set a return code on invalid action!")
|
||
|
}
|
||
|
|
||
|
codeSet := ActErrno.SetReturnCode(0x0001)
|
||
|
if codeSet == ActErrno || codeSet.GetReturnCode() != 0x0001 {
|
||
|
t.Errorf("Could not set return code on ActErrno")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSyscallGetName(t *testing.T) {
|
||
|
call1 := ScmpSyscall(0x1)
|
||
|
callFail := ScmpSyscall(0x999)
|
||
|
|
||
|
name, err := call1.GetName()
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting syscall name for number 0x1")
|
||
|
} else if len(name) == 0 {
|
||
|
t.Errorf("Empty name returned for syscall 0x1")
|
||
|
}
|
||
|
fmt.Printf("Got name of syscall 0x1 on native arch as %s\n", name)
|
||
|
|
||
|
_, err = callFail.GetName()
|
||
|
if err == nil {
|
||
|
t.Errorf("Getting nonexistant syscall should error!")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSyscallGetNameByArch(t *testing.T) {
|
||
|
call1 := ScmpSyscall(0x1)
|
||
|
callInvalid := ScmpSyscall(0x999)
|
||
|
archGood := ArchAMD64
|
||
|
archBad := ArchInvalid
|
||
|
|
||
|
name, err := call1.GetNameByArch(archGood)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting syscall name for number 0x1 and arch AMD64")
|
||
|
} else if name != "write" {
|
||
|
t.Errorf("Got incorrect name for syscall 0x1 - expected write, got %s", name)
|
||
|
}
|
||
|
|
||
|
_, err = call1.GetNameByArch(archBad)
|
||
|
if err == nil {
|
||
|
t.Errorf("Bad architecture GetNameByArch() should error!")
|
||
|
}
|
||
|
|
||
|
_, err = callInvalid.GetNameByArch(archGood)
|
||
|
if err == nil {
|
||
|
t.Errorf("Bad syscall GetNameByArch() should error!")
|
||
|
}
|
||
|
|
||
|
_, err = callInvalid.GetNameByArch(archBad)
|
||
|
if err == nil {
|
||
|
t.Errorf("Bad syscall and bad arch GetNameByArch() should error!")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGetSyscallFromName(t *testing.T) {
|
||
|
name1 := "write"
|
||
|
nameInval := "NOTASYSCALL"
|
||
|
|
||
|
syscall, err := GetSyscallFromName(name1)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting syscall number of write: %s", err)
|
||
|
}
|
||
|
fmt.Printf("Got syscall number of write on native arch as %d\n", syscall)
|
||
|
|
||
|
_, err = GetSyscallFromName(nameInval)
|
||
|
if err == nil {
|
||
|
t.Errorf("Getting an invalid syscall should error!")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGetSyscallFromNameByArch(t *testing.T) {
|
||
|
name1 := "write"
|
||
|
nameInval := "NOTASYSCALL"
|
||
|
arch1 := ArchAMD64
|
||
|
archInval := ArchInvalid
|
||
|
|
||
|
syscall, err := GetSyscallFromNameByArch(name1, arch1)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting syscall number of write on AMD64: %s", err)
|
||
|
}
|
||
|
fmt.Printf("Got syscall number of write on AMD64 as %d\n", syscall)
|
||
|
|
||
|
_, err = GetSyscallFromNameByArch(nameInval, arch1)
|
||
|
if err == nil {
|
||
|
t.Errorf("Getting invalid syscall with valid arch should error")
|
||
|
}
|
||
|
|
||
|
_, err = GetSyscallFromNameByArch(name1, archInval)
|
||
|
if err == nil {
|
||
|
t.Errorf("Getting valid syscall for invalid arch should error")
|
||
|
}
|
||
|
|
||
|
_, err = GetSyscallFromNameByArch(nameInval, archInval)
|
||
|
if err == nil {
|
||
|
t.Errorf("Getting invalid syscall for invalid arch should error")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMakeCondition(t *testing.T) {
|
||
|
condition, err := MakeCondition(3, CompareNotEqual, 0x10)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error making condition struct: %s", err)
|
||
|
} else if condition.Argument != 3 || condition.Operand1 != 0x10 ||
|
||
|
condition.Operand2 != 0 || condition.Op != CompareNotEqual {
|
||
|
t.Errorf("Condition struct was filled incorrectly")
|
||
|
}
|
||
|
|
||
|
condition, err = MakeCondition(3, CompareMaskedEqual, 0x10, 0x20)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error making condition struct: %s", err)
|
||
|
} else if condition.Argument != 3 || condition.Operand1 != 0x10 ||
|
||
|
condition.Operand2 != 0x20 || condition.Op != CompareMaskedEqual {
|
||
|
t.Errorf("Condition struct was filled incorrectly")
|
||
|
}
|
||
|
|
||
|
_, err = MakeCondition(7, CompareNotEqual, 0x10)
|
||
|
if err == nil {
|
||
|
t.Errorf("Condition struct with bad syscall argument number should error")
|
||
|
}
|
||
|
|
||
|
_, err = MakeCondition(3, CompareInvalid, 0x10)
|
||
|
if err == nil {
|
||
|
t.Errorf("Condition struct with bad comparison operator should error")
|
||
|
}
|
||
|
|
||
|
_, err = MakeCondition(3, CompareMaskedEqual, 0x10, 0x20, 0x30)
|
||
|
if err == nil {
|
||
|
t.Errorf("MakeCondition with more than 2 arguments should fail")
|
||
|
}
|
||
|
|
||
|
_, err = MakeCondition(3, CompareMaskedEqual)
|
||
|
if err == nil {
|
||
|
t.Errorf("MakeCondition with no arguments should fail")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Utility Function Tests
|
||
|
|
||
|
func TestGetNativeArch(t *testing.T) {
|
||
|
arch, err := GetNativeArch()
|
||
|
if err != nil {
|
||
|
t.Errorf("GetNativeArch should not error!")
|
||
|
}
|
||
|
fmt.Printf("Got native arch of system as %s\n", arch.String())
|
||
|
}
|
||
|
|
||
|
// Filter Tests
|
||
|
|
||
|
func TestFilterCreateRelease(t *testing.T) {
|
||
|
_, err := NewFilter(ActInvalid)
|
||
|
if err == nil {
|
||
|
t.Errorf("Can create filter with invalid action")
|
||
|
}
|
||
|
|
||
|
filter, err := NewFilter(ActKill)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating filter: %s", err)
|
||
|
}
|
||
|
|
||
|
if !filter.IsValid() {
|
||
|
t.Errorf("Filter created by NewFilter was not valid")
|
||
|
}
|
||
|
|
||
|
filter.Release()
|
||
|
|
||
|
if filter.IsValid() {
|
||
|
t.Errorf("Filter is valid after being released")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestFilterReset(t *testing.T) {
|
||
|
filter, err := NewFilter(ActKill)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating filter: %s", err)
|
||
|
}
|
||
|
defer filter.Release()
|
||
|
|
||
|
// Ensure the default action is ActKill
|
||
|
action, err := filter.GetDefaultAction()
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting default action of filter")
|
||
|
} else if action != ActKill {
|
||
|
t.Errorf("Default action of filter was set incorrectly!")
|
||
|
}
|
||
|
|
||
|
// Reset with a different default action
|
||
|
err = filter.Reset(ActAllow)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error resetting filter!")
|
||
|
}
|
||
|
|
||
|
valid := filter.IsValid()
|
||
|
if !valid {
|
||
|
t.Errorf("Filter is no longer valid after reset!")
|
||
|
}
|
||
|
|
||
|
// The default action should no longer be ActKill
|
||
|
action, err = filter.GetDefaultAction()
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting default action of filter")
|
||
|
} else if action != ActAllow {
|
||
|
t.Errorf("Default action of filter was set incorrectly!")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestFilterArchFunctions(t *testing.T) {
|
||
|
filter, err := NewFilter(ActKill)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating filter: %s", err)
|
||
|
}
|
||
|
defer filter.Release()
|
||
|
|
||
|
arch, err := GetNativeArch()
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting native architecture: %s", err)
|
||
|
}
|
||
|
|
||
|
present, err := filter.IsArchPresent(arch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error retrieving arch from filter: %s", err)
|
||
|
} else if !present {
|
||
|
t.Errorf("Filter does not contain native architecture by default")
|
||
|
}
|
||
|
|
||
|
// Adding the native arch again should succeed, as it's already present
|
||
|
err = filter.AddArch(arch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Adding arch to filter already containing it should succeed")
|
||
|
}
|
||
|
|
||
|
// Make sure we don't add the native arch again
|
||
|
prospectiveArch := ArchX86
|
||
|
if arch == ArchX86 {
|
||
|
prospectiveArch = ArchAMD64
|
||
|
}
|
||
|
|
||
|
// Check to make sure this other arch isn't in the filter
|
||
|
present, err = filter.IsArchPresent(prospectiveArch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error retrieving arch from filter: %s", err)
|
||
|
} else if present {
|
||
|
t.Errorf("Arch not added to filter is present")
|
||
|
}
|
||
|
|
||
|
// Try removing the nonexistant arch - should succeed
|
||
|
err = filter.RemoveArch(prospectiveArch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error removing nonexistant arch: %s", err)
|
||
|
}
|
||
|
|
||
|
// Add an arch, see if it's in the filter
|
||
|
err = filter.AddArch(prospectiveArch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not add arch %s to filter: %s",
|
||
|
prospectiveArch.String(), err)
|
||
|
}
|
||
|
|
||
|
present, err = filter.IsArchPresent(prospectiveArch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error retrieving arch from filter: %s", err)
|
||
|
} else if !present {
|
||
|
t.Errorf("Filter does not contain architecture %s after it was added",
|
||
|
prospectiveArch.String())
|
||
|
}
|
||
|
|
||
|
// Remove the arch again, make sure it's not in the filter
|
||
|
err = filter.RemoveArch(prospectiveArch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not remove arch %s from filter: %s",
|
||
|
prospectiveArch.String(), err)
|
||
|
}
|
||
|
|
||
|
present, err = filter.IsArchPresent(prospectiveArch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error retrieving arch from filter: %s", err)
|
||
|
} else if present {
|
||
|
t.Errorf("Filter contains architecture %s after it was removed",
|
||
|
prospectiveArch.String())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestFilterAttributeGettersAndSetters(t *testing.T) {
|
||
|
filter, err := NewFilter(ActKill)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating filter: %s", err)
|
||
|
}
|
||
|
defer filter.Release()
|
||
|
|
||
|
act, err := filter.GetDefaultAction()
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting default action: %s", err)
|
||
|
} else if act != ActKill {
|
||
|
t.Errorf("Default action was set incorrectly")
|
||
|
}
|
||
|
|
||
|
err = filter.SetBadArchAction(ActAllow)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error setting bad arch action: %s", err)
|
||
|
}
|
||
|
|
||
|
act, err = filter.GetBadArchAction()
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting bad arch action")
|
||
|
} else if act != ActAllow {
|
||
|
t.Errorf("Bad arch action was not set correcly!")
|
||
|
}
|
||
|
|
||
|
err = filter.SetNoNewPrivsBit(false)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error setting no new privileges bit")
|
||
|
}
|
||
|
|
||
|
privs, err := filter.GetNoNewPrivsBit()
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting no new privileges bit!")
|
||
|
} else if privs != false {
|
||
|
t.Errorf("No new privileges bit was not set correctly")
|
||
|
}
|
||
|
|
||
|
err = filter.SetBadArchAction(ActInvalid)
|
||
|
if err == nil {
|
||
|
t.Errorf("Setting bad arch action to an invalid action should error")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMergeFilters(t *testing.T) {
|
||
|
filter1, err := NewFilter(ActAllow)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating filter: %s", err)
|
||
|
}
|
||
|
|
||
|
filter2, err := NewFilter(ActAllow)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating filter: %s", err)
|
||
|
}
|
||
|
|
||
|
// Need to remove the native arch and add another to the second filter
|
||
|
// Filters must NOT share architectures to be successfully merged
|
||
|
nativeArch, err := GetNativeArch()
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting native arch: %s", err)
|
||
|
}
|
||
|
|
||
|
prospectiveArch := ArchAMD64
|
||
|
if nativeArch == ArchAMD64 {
|
||
|
prospectiveArch = ArchX86
|
||
|
}
|
||
|
|
||
|
err = filter2.AddArch(prospectiveArch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error adding architecture to filter: %s", err)
|
||
|
}
|
||
|
|
||
|
err = filter2.RemoveArch(nativeArch)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error removing architecture from filter: %s", err)
|
||
|
}
|
||
|
|
||
|
err = filter1.Merge(filter2)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error merging filters: %s", err)
|
||
|
}
|
||
|
|
||
|
if filter2.IsValid() {
|
||
|
t.Errorf("Source filter should not be valid after merging")
|
||
|
}
|
||
|
|
||
|
filter3, err := NewFilter(ActKill)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating filter: %s", err)
|
||
|
}
|
||
|
defer filter3.Release()
|
||
|
|
||
|
err = filter1.Merge(filter3)
|
||
|
if err == nil {
|
||
|
t.Errorf("Attributes should have to match to merge filters")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestRuleAddAndLoad(t *testing.T) {
|
||
|
// Test #1: Add a trivial filter
|
||
|
filter1, err := NewFilter(ActAllow)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating filter: %s", err)
|
||
|
}
|
||
|
defer filter1.Release()
|
||
|
|
||
|
call, err := GetSyscallFromName("getpid")
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting syscall number of getpid: %s", err)
|
||
|
}
|
||
|
|
||
|
call2, err := GetSyscallFromName("setreuid")
|
||
|
if err != nil {
|
||
|
t.Errorf("Error getting syscall number of setreuid: %s", err)
|
||
|
}
|
||
|
|
||
|
uid := syscall.Getuid()
|
||
|
euid := syscall.Geteuid()
|
||
|
|
||
|
err = filter1.AddRule(call, ActErrno.SetReturnCode(0x1))
|
||
|
if err != nil {
|
||
|
t.Errorf("Error adding rule to restrict syscall: %s", err)
|
||
|
}
|
||
|
|
||
|
cond, err := MakeCondition(1, CompareEqual, uint64(euid))
|
||
|
if err != nil {
|
||
|
t.Errorf("Error making rule to restrict syscall: %s", err)
|
||
|
}
|
||
|
|
||
|
cond2, err := MakeCondition(0, CompareEqual, uint64(uid))
|
||
|
if err != nil {
|
||
|
t.Errorf("Error making rule to restrict syscall: %s", err)
|
||
|
}
|
||
|
|
||
|
conditions := []ScmpCondition{cond, cond2}
|
||
|
|
||
|
err = filter1.AddRuleConditional(call2, ActErrno.SetReturnCode(0x2), conditions)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error adding conditional rule: %s", err)
|
||
|
}
|
||
|
|
||
|
err = filter1.Load()
|
||
|
if err != nil {
|
||
|
t.Errorf("Error loading filter: %s", err)
|
||
|
}
|
||
|
|
||
|
// Try making a simple syscall, it should error
|
||
|
pid := syscall.Getpid()
|
||
|
if pid != -1 {
|
||
|
t.Errorf("Syscall should have returned error code!")
|
||
|
}
|
||
|
|
||
|
// Try making a Geteuid syscall that should normally succeed
|
||
|
err = syscall.Setreuid(uid, euid)
|
||
|
if err != syscall.Errno(2) {
|
||
|
t.Errorf("Syscall should have returned error code!")
|
||
|
}
|
||
|
}
|