Skip to content

Commit

Permalink
providers/linux: Fix linux Capabilities
Browse files Browse the repository at this point in the history
I believe this never worked, as readCapabilities() expected a Capabilities line
but was passed the full file as input, it also would allocate its own
CapabilityInfo and return it, even though it would fill one member.

So change all this:
  o Scan the file line by line, as to avoid loading a full file into memory,
    then parse out only the desired lines and pass down.
  o Pass the structure as a parameter and let each member be filled.
  o Error out if we failed to report at least one of the capabilities.
  o Add a test against init(1) as it will """always""" be run as root and have
    all capabilities, test is a bit conservative but should be ok for now.
  • Loading branch information
haesbaert committed Dec 4, 2023
1 parent 35e55cd commit 6ad5c63
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .changelog/196.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
linux: fix linux capabilities
```
18 changes: 7 additions & 11 deletions providers/linux/capabilities_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
21 changes: 19 additions & 2 deletions providers/linux/process_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package linux

import (
"bufio"
"bytes"
"io/ioutil"
"os"
Expand Down Expand Up @@ -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) {
Expand Down
22 changes: 22 additions & 0 deletions system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

0 comments on commit 6ad5c63

Please sign in to comment.