diff --git a/.changelog/196.txt b/.changelog/196.txt new file mode 100644 index 00000000..3168dbb3 --- /dev/null +++ b/.changelog/196.txt @@ -0,0 +1,3 @@ +```release-note:bug +linux: fix linux capabilities +``` diff --git a/providers/linux/capabilities_linux.go b/providers/linux/capabilities_linux.go index 40bf454d..b89f0b8c 100644 --- a/providers/linux/capabilities_linux.go +++ b/providers/linux/capabilities_linux.go @@ -83,40 +83,36 @@ func capabilityName(num int) string { return strconv.Itoa(num) } -func readCapabilities(content []byte) (*types.CapabilityInfo, error) { - var cap types.CapabilityInfo - - err := parseKeyValue(content, ':', func(key, value []byte) error { +func decodeCapabilityLine(content string, capInfo *types.CapabilityInfo) error { + return parseKeyValue([]byte(content), ':', func(key, value []byte) error { var err error switch string(key) { case "CapInh": - cap.Inheritable, err = decodeBitMap(string(value), capabilityName) + capInfo.Inheritable, err = decodeBitMap(string(value), capabilityName) if err != nil { return err } case "CapPrm": - cap.Permitted, err = decodeBitMap(string(value), capabilityName) + capInfo.Permitted, err = decodeBitMap(string(value), capabilityName) if err != nil { return err } case "CapEff": - cap.Effective, err = decodeBitMap(string(value), capabilityName) + capInfo.Effective, err = decodeBitMap(string(value), capabilityName) if err != nil { return err } case "CapBnd": - cap.Bounding, err = decodeBitMap(string(value), capabilityName) + capInfo.Bounding, err = decodeBitMap(string(value), capabilityName) if err != nil { return err } case "CapAmb": - cap.Ambient, err = decodeBitMap(string(value), capabilityName) + capInfo.Ambient, err = decodeBitMap(string(value), capabilityName) if err != nil { return err } } return nil }) - - return &cap, err } diff --git a/providers/linux/process_linux.go b/providers/linux/process_linux.go index 52bae255..9610da53 100644 --- a/providers/linux/process_linux.go +++ b/providers/linux/process_linux.go @@ -18,6 +18,7 @@ package linux import ( + "bufio" "bytes" "io/ioutil" "os" @@ -213,13 +214,29 @@ func (p *process) Seccomp() (*types.SeccompInfo, error) { return readSeccompFields(content) } +// Returns error if at least one capability errored out, while still +// returning the successful ones func (p *process) Capabilities() (*types.CapabilityInfo, error) { - content, err := ioutil.ReadFile(p.path("status")) + var gotErr error + f, err := os.OpenFile(p.path("status"), os.O_RDONLY, 0644) if err != nil { return nil, err } + defer f.Close() + scanner := bufio.NewScanner(bufio.NewReader(f)) + var capInfo types.CapabilityInfo + for scanner.Scan() { + line := scanner.Text() + if len(line) != 24 || line[:3] != "Cap" { + continue + } + err = decodeCapabilityLine(line, &capInfo) + if err != nil && gotErr == nil { + gotErr = err + } + } - return readCapabilities(content) + return &capInfo, gotErr } func (p *process) User() (types.UserInfo, error) { diff --git a/system_test.go b/system_test.go index efb872e3..44590cf0 100644 --- a/system_test.go +++ b/system_test.go @@ -310,3 +310,25 @@ func TestProcesses(t *testing.T) { info.StartTime) } } + +func TestCapabilities(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("capabilities is linux only") + } + init, err := Process(1) + if err != nil { + t.Fatal(err) + } + v, ok := init.(types.Capabilities) + if !ok { + t.Fatal("capabilities is expected to be implemented for linux") + } + capInfo, err := v.Capabilities() + if err != nil { + t.Fatal(err) + } + totalCaps := 41 + assert.EqualValues(t, len(capInfo.Permitted), totalCaps) + assert.EqualValues(t, len(capInfo.Effective), totalCaps) + assert.EqualValues(t, len(capInfo.Bounding), totalCaps) +}