blob: 52afd4345f209f622d1a0a6fe697fceed6c16ad3 [file] [log] [blame]
package cap
import (
"fmt"
"testing"
)
func TestAllMask(t *testing.T) {
oldMask := maxValues
oldWords := words
defer func() {
maxValues = oldMask
words = oldWords
}()
maxValues = 35
words = 3
vs := []struct {
val Value
index uint
bit uint32
mask uint32
}{
{val: CHOWN, index: 0, bit: 0x1, mask: ^uint32(0)},
{val: 38, index: 1, bit: (1 << 6), mask: 0x7},
{val: 34, index: 1, bit: (1 << 2), mask: 0x7},
{val: 65, index: 2, bit: (1 << 1), mask: 0},
}
for i, v := range vs {
index, bit, err := bitOf(Inheritable, v.val)
if err != nil {
t.Fatalf("[%d] %v(%d) - not bitOf: %v", i, v.val, v.val, err)
} else if index != v.index {
t.Errorf("[%d] %v(%d) - index: got=%d want=%d", i, v.val, v.val, index, v.index)
}
if bit != v.bit {
t.Errorf("[%d] %v(%d) - bit: got=%b want=%b", i, v.val, v.val, bit, v.bit)
}
if mask := allMask(index); mask != v.mask {
t.Errorf("[%d] %v(%d) - mask: got=%b want=%b", i, v.val, v.val, mask, v.mask)
}
}
}
func TestString(t *testing.T) {
a := CHOWN
if got, want := a.String(), "cap_chown"; got != want {
t.Fatalf("pretty basic failure: got=%q, want=%q", got, want)
}
}
func TestText(t *testing.T) {
vs := []struct {
from, to string
err error
}{
{"", "", ErrBadText},
{"=", "=", nil},
{"= cap_chown+iep cap_chown-i", "cap_chown=ep", nil},
{"= cap_setfcap,cap_chown+iep cap_chown-i", "cap_setfcap=eip cap_chown+ep", nil},
{"cap_setfcap,cap_chown=iep cap_chown-i", "cap_setfcap=eip cap_chown+ep", nil},
{"=i =p", "=p", nil},
{"all+pie", "=eip", nil},
{"all=p+ie-e", "=ip", nil},
}
for i, v := range vs {
c, err := FromText(v.from)
if err != v.err {
t.Errorf("[%d] parsing %q failed: got=%v, want=%v", i, v.from, err, v.err)
continue
}
if err != nil {
continue
}
to := c.String()
if to != v.to {
t.Errorf("[%d] failed to stringify cap: %q -> got=%q, want=%q", i, v.from, to, v.to)
}
if d, err := FromText(to); err != nil {
t.Errorf("[%d] failed to reparse %q: %v", i, to, err)
} else if got := d.String(); got != to {
t.Errorf("[%d] failed to stringify %q getting %q", i, to, got)
}
}
}
func same(a, b *Set) error {
if (a == nil) != (b == nil) {
return fmt.Errorf("nil-ness miscompare: %q vs %v", a, b)
}
if a == nil {
return nil
}
if a.nsRoot != b.nsRoot {
return fmt.Errorf("capabilities differ in nsRoot: a=%d b=%d", a.nsRoot, b.nsRoot)
}
for i, f := range a.flat {
g := b.flat[i]
for s := Effective; s <= Inheritable; s++ {
if got, want := f[s], g[s]; got != want {
return fmt.Errorf("capabilities differ: a[%d].flat[%v]=0x%08x b[%d].flat[%v]=0x%08x", i, s, got, i, s, want)
}
}
}
return nil
}
func confirmExpectedExport(t *testing.T, info string, c *Set, size uint) {
if ex, err := c.Export(); err != nil {
t.Fatalf("[%s] failed to export empty set: %v", info, err)
} else if n := 5 + 3*size; uint(len(ex)) != n {
t.Fatalf("[%s] wrong length: got=%d [%0x] want=%d", info, len(ex), ex, n)
} else if im, err := Import(ex); err != nil {
t.Fatalf("[%s] failed to import empty set: %v", info, err)
} else if got, want := im.String(), c.String(); got != want {
t.Fatalf("[%s] import != export: got=%q want=%q [%02x]", info, got, want, ex)
}
}
func TestImportExport(t *testing.T) {
wantQ := "=ep cap_chown-e 63+ip"
if q, err := FromText(wantQ); err != nil {
t.Fatalf("failed to parse %q: %v", wantQ, err)
} else if gotQ := q.String(); gotQ != wantQ {
t.Fatalf("static test failed %q -> q -> %q", wantQ, gotQ)
}
// Sanity check empty import/export.
c := NewSet()
confirmExpectedExport(t, "empty", c, MinExtFlagSize)
// Now keep flipping bits on and off and validate that all
// forms of import/export work.
for i := uint(0); i < 7000; i += 13 {
s := Flag(i % 3)
v := Value(i % (maxValues + 3))
c.SetFlag(s, i&17 < 8, v)
if ex, err := c.Export(); err != nil {
t.Fatalf("[%d] failed to export (%q): %v", i, c, err)
} else if im, err := Import(ex); err != nil {
t.Fatalf("[%d] failed to import (%q) set: %v", i, c, err)
} else if got, want := im.String(), c.String(); got != want {
t.Fatalf("[%d] import != export: got=%q want=%q [%02x]", i, got, want, ex)
} else if parsed, err := FromText(got); err != nil {
t.Fatalf("[%d] failed to parse %q: %v", i, got, err)
} else if err := same(c, parsed); err != nil {
t.Fatalf("[%d] miscompare (%q vs. %q): %v", i, got, parsed, err)
}
}
oMin := MinExtFlagSize
for j := uint(0); j < 5; j++ {
t.Logf("exporting with min flag size %d", j)
MinExtFlagSize = j
c = NewSet()
for i := uint(0); i < maxValues; i++ {
s := Flag(i % 3)
v := Value(i)
c.SetFlag(s, true, v)
size := 1 + i/8
if size < MinExtFlagSize {
size = MinExtFlagSize
}
confirmExpectedExport(t, fmt.Sprintf("%d added %d %v %v", j, i, s, v), c, size)
}
}
MinExtFlagSize = oMin
}
func TestIAB(t *testing.T) {
vs := []struct {
text string
bad bool
}{
{text: "cup_full", bad: true},
{text: ""},
{text: "!%cap_chown"},
{text: "!cap_chown,^cap_setuid"},
{text: "cap_chown,cap_setuid"},
{text: "^cap_chown,cap_setuid"},
{text: "^cap_chown,!cap_setuid"},
}
for i, v := range vs {
want := v.text
iab, err := IABFromText(want)
if err != nil {
if v.bad {
continue
}
t.Errorf("[%d] want=%q, got=%q", i, want, iab)
continue
}
if got := iab.String(); got != want {
t.Errorf("[%d] got=%q want=%q", i, got, want)
}
}
one, err := GetPID(1)
if err != nil {
t.Fatalf("failed to get init's capabilities: %v", err)
}
iab := NewIAB()
if err := iab.Fill(Amb, one, Permitted); err != nil {
t.Fatalf("failed to fill Amb from Permitted: %v", err)
}
for i := 0; i < words; i++ {
if iab.i[i] != iab.a[i] {
t.Errorf("[%d: %q] i=0x%08x != a=0x%08x", i, one, iab.i[i], iab.a[i])
}
}
one.ClearFlag(Inheritable)
iab.Fill(Inh, one, Inheritable)
for i := 0; i < words; i++ {
if iab.i[i] != iab.a[i] {
t.Errorf("[%d: %q] i=0x%08x != a=0x%08x", i, one, iab.i[i], iab.a[i])
}
}
for n := uint(0); n < 1000; n += 13 {
enabled := ((n % 5) & 2) != 0
vec := Vector(n % 3)
c := Value(n % maxValues)
if err := iab.SetVector(vec, enabled, c); err != nil {
t.Errorf("[%d] failed to set vec=%v enabled=%v %q in %q", n, vec, enabled, c, iab)
continue
}
replay, err := IABFromText(iab.String())
if err != nil {
t.Errorf("failed to replay: %v", err)
continue
}
for i := 0; i < words; i++ {
if replay.i[i] != iab.i[i] || replay.a[i] != iab.a[i] || replay.nb[i] != iab.nb[i] {
t.Errorf("[%d,%d] got=%q want=%q", n, i, replay, iab)
}
}
}
}
func TestFuncLaunch(t *testing.T) {
if _, err := FuncLauncher(func(data interface{}) error {
return nil
}).Launch(nil); err != nil {
t.Fatalf("trivial launcher failed: %v", err)
}
for i := 0; i < 100; i++ {
expect := i & 1
before, err := Prctl(prGetKeepCaps)
if err != nil {
t.Fatalf("failed to get PR_KEEP_CAPS: %v", err)
}
if before != expect {
t.Fatalf("invalid initial state: got=%d want=%d", before, expect)
}
if _, err := FuncLauncher(func(data interface{}) error {
was, ok := data.(int)
if !ok {
return fmt.Errorf("data was not an int: %v", data)
}
if _, err := Prctlw(prSetKeepCaps, uintptr(1-was)); err != nil {
return err
}
if v, err := Prctl(prGetKeepCaps); err != nil {
return err
} else if v == was {
return fmt.Errorf("PR_KEEP_CAPS unchanged: got=%d, want=%v", v, 1-was)
}
// All good.
return nil
}).Launch(before); err != nil {
t.Fatalf("trivial launcher failed: %v", err)
}
// Now validate that the main process is still OK.
if after, err := Prctl(prGetKeepCaps); err != nil {
t.Fatalf("failed to get PR_KEEP_CAPS: %v", err)
} else if before != after {
t.Fatalf("FuncLauncher leaked privileged state: got=%v want=%v", after, before)
}
// Now force the other way
if _, err := Prctlw(prSetKeepCaps, uintptr(1-expect)); err != nil {
t.Fatalf("[%d] attempt to flip PR_KEEP_CAPS failed: %v", i, err)
}
}
}
func TestFill(t *testing.T) {
c, err := FromText("cap_setfcap=p")
if err != nil {
t.Fatalf("failed to parse: %v", err)
}
c.Fill(Effective, Permitted)
c.ClearFlag(Permitted)
c.Fill(Inheritable, Effective)
c.ClearFlag(Effective)
if got, want := c.String(), "cap_setfcap=i"; got != want {
t.Errorf("Fill failed: got=%q want=%q", got, want)
}
}