diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2021-08-22 14:32:12 -0700 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2021-08-22 14:32:12 -0700 |
commit | ac297b51c6730c22a06b759bd4235b47c52053bc (patch) | |
tree | 1b2bf1952f6ccf5f1b329fcf8b55dc08f9cfaa05 | |
parent | 25cdfaf7f813a94612279712a2d6d1e1bb39e08b (diff) | |
download | libcap-ac297b51c6730c22a06b759bd4235b47c52053bc.tar.gz |
Revamp the comparison API of *Set and *IAB tuples; add IABGetPID().
Older APIs remain but are documented as deprecated. If we ever need
to release a golang version "2" version of the library, I'll drop
support for deprecated functions, but I have no intention of needing
to do that. In the mean time, the deprecated functions are wrappers
around the new functions.
New API: *Set and *IAB have .Cf() functions now. That return a
[IAB]Diff value. This value, if 0, means the compared pointers
match one another. Non-zero values can be interogated with the
([IAB]Diff).Has() functions.
Also, add an IABGetPID() function. Since the kernel provides no
syscall support for this, we have to resort to parsing the /proc/
files. Implemented mostly for parity with the syscall backed
GetPID() *Set returning API.
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r-- | cap/cap.go | 9 | ||||
-rw-r--r-- | cap/cap_test.go | 2 | ||||
-rw-r--r-- | cap/convenience.go | 6 | ||||
-rw-r--r-- | cap/flags.go | 29 | ||||
-rw-r--r-- | cap/iab.go | 124 |
5 files changed, 157 insertions, 13 deletions
@@ -103,6 +103,15 @@ const ( Inheritable ) +// Diff summarizes the result of the (*Set).Cf() function. +type Diff uint + +const ( + effectiveDiff Diff = 1 << Effective + permittedDiff Diff = 1 << Permitted + inheritableDiff Diff = 1 << Inheritable +) + // String identifies a Flag value by its conventional "e", "p" or "i" // string abbreviation. func (f Flag) String() string { diff --git a/cap/cap_test.go b/cap/cap_test.go index d7c7970..54f1e3f 100644 --- a/cap/cap_test.go +++ b/cap/cap_test.go @@ -199,7 +199,7 @@ func TestIAB(t *testing.T) { if err != nil { t.Fatalf("failed to get init's capabilities: %v", err) } - iab := IABInit() + iab := NewIAB() iab.Fill(Amb, one, Permitted) for i := 0; i < words; i++ { if iab.i[i] != iab.a[i] { diff --git a/cap/convenience.go b/cap/convenience.go index d604ad1..c40b63d 100644 --- a/cap/convenience.go +++ b/cap/convenience.go @@ -101,12 +101,12 @@ func GetMode() Mode { w := GetProc() e := NewSet() - cf, _ := w.Compare(e) + cf, _ := w.Cf(e) - if Differs(cf, Inheritable) { + if cf.Has(Inheritable) { return ModePure1E } - if Differs(cf, Permitted) || Differs(cf, Effective) { + if cf.Has(Permitted) || cf.Has(Effective) { return ModePure1EInit } diff --git a/cap/flags.go b/cap/flags.go index 83a871a..d46634d 100644 --- a/cap/flags.go +++ b/cap/flags.go @@ -153,20 +153,31 @@ func (c *Set) ClearFlag(vec Flag) error { // this function returns a non-zero value of 3 independent bits: // (differE ? 1:0) | (differP ? 2:0) | (differI ? 4:0). The Differs() // function can be used to test for a difference in a specific Flag. +// +// This function is deprecated in favor of (*Set).Cf(). func (c *Set) Compare(d *Set) (uint, error) { + u, err := c.Cf(d) + return uint(u), err +} + +// Cf returns 0 if c and d are identical. A non-zero Diff value +// captures a simple macroscopic summary of how they differ. The +// (Diff).Has() function can be used to determine how the two +// capability sets differ. +func (c *Set) Cf(d *Set) (Diff, error) { if c == nil || len(c.flat) == 0 || d == nil || len(d.flat) == 0 { return 0, ErrBadSet } - var cf uint + var cf Diff for i := 0; i < words; i++ { if c.flat[i][Effective]^d.flat[i][Effective] != 0 { - cf |= (1 << Effective) + cf |= effectiveDiff } if c.flat[i][Permitted]^d.flat[i][Permitted] != 0 { - cf |= (1 << Permitted) + cf |= permittedDiff } if c.flat[i][Inheritable]^d.flat[i][Inheritable] != 0 { - cf |= (1 << Inheritable) + cf |= inheritableDiff } } return cf, nil @@ -174,6 +185,16 @@ func (c *Set) Compare(d *Set) (uint, error) { // Differs processes the result of Compare and determines if the // Flag's components were different. +// +// Use of this function is deprecated in favor of the (Diff).Has() +// function, where Diff is returned as a result of the (*Set).Cf() +// function. func Differs(cf uint, vec Flag) bool { return cf&(1<<vec) != 0 } + +// Has processes the Diff result of (*Set).Cf() and determines if the +// Flag's components were different in that result. +func (cf Diff) Has(vec Flag) bool { + return uint(cf)&(1<<vec) != 0 +} @@ -1,6 +1,11 @@ package cap -import "strings" +import ( + "fmt" + "io/ioutil" + "strconv" + "strings" +) // omask returns the offset and mask for a specific capability. func omask(c Value) (uint, uint32) { @@ -31,6 +36,24 @@ const ( Bound ) +// IABDiff holds the non-error result of an (*IAB).Cf() +// function call. It can be interpreted with the function +// (IABDiff).Has(). +type IABDiff uint + +// iBits, iBits and bBits track the (semi-)independent parts of an +// IABDiff. +const ( + iBits IABDiff = 1 << Inh + aBits IABDiff = 1 << Amb + bBits IABDiff = 1 << Bound +) + +// Has determines if an IAB comparison differs in a specific vector. +func (d IABDiff) Has(v Vector) bool { + return d&(1<<v) != 0 +} + // String identifies a Vector value by its conventional I A or B // string abbreviation. func (v Vector) String() string { @@ -46,8 +69,8 @@ func (v Vector) String() string { } } -// IABInit returns an empty IAB. -func IABInit() *IAB { +// NewIAB returns an empty IAB. +func NewIAB() *IAB { startUp.Do(multisc.cInit) return &IAB{ i: make([]uint32, words), @@ -56,10 +79,15 @@ func IABInit() *IAB { } } +// IABInit is a deprecated alias for the NewIAB function. +func IABInit() *IAB { + return NewIAB() +} + // IABGetProc summarizes the Inh, Amb and Bound capability vectors of // the current process. func IABGetProc() *IAB { - iab := IABInit() + iab := NewIAB() current := GetProc() iab.Fill(Inh, current, Inheritable) for c := MaxBits(); c > 0; { @@ -78,7 +106,7 @@ func IABGetProc() *IAB { // IABFromText parses a string representing an IAB, as generated // by IAB.String(), to generate an IAB. func IABFromText(text string) (*IAB, error) { - iab := IABInit() + iab := NewIAB() if len(text) == 0 { return iab, nil } @@ -119,6 +147,9 @@ func IABFromText(text string) (*IAB, error) { // String serializes an IAB to a string format. func (iab *IAB) String() string { + if iab == nil { + return "<invalid>" + } var vs []string for c := Value(0); c < Value(maxValues); c++ { offset, mask := omask(c) @@ -285,3 +316,86 @@ func (iab *IAB) Fill(vec Vector, c *Set, flag Flag) error { } return nil } + +// Cf compares two IAB values. Its return value is 0 if the compared +// tuples are considered identical. The macroscopic differences can be +// investigated with (IABDiff).Has(). +func (iab *IAB) Cf(alt *IAB) (IABDiff, error) { + if iab == alt { + return 0, nil + } + if iab == nil || alt == nil || len(iab.i) != words || len(alt.i) != words || len(iab.a) != words || len(alt.a) != words || len(iab.nb) != words || len(alt.nb) != words { + return 0, ErrBadValue + } + + var cf IABDiff + for i := 0; i < words; i++ { + if iab.i[i] != alt.i[i] { + cf |= iBits + } + if iab.a[i] != alt.a[i] { + cf |= aBits + } + if iab.nb[i] != alt.nb[i] { + cf |= bBits + } + } + return cf, nil +} + +// parseHex converts the /proc/*/status string into an array of +// uint32s suitable for storage in an IAB structure. +func parseHex(hex string, invert bool) []uint32 { + if len(hex) != 8*words { + // Invalid string + return nil + } + var result []uint32 + for i := 0; i < words; i++ { + upper := 8 * (words - i) + raw, err := strconv.ParseUint(hex[upper-8:upper], 16, 32) + if err != nil { + return nil + } + if invert { + raw = ^raw + } + bits := allMask(uint(i)) & uint32(raw) + result = append(result, bits) + } + return result +} + +// IABGetPID returns the IAB tuple of a specified process. The kernel +// ABI does not support this query via system calls, so the function +// works by parsing the /proc/<pid>/status file content. +func IABGetPID(pid int) (*IAB, error) { + tf := fmt.Sprintf("/proc/%d/status", pid) + d, err := ioutil.ReadFile(tf) + if err != nil { + return nil, err + } + iab := &IAB{} + for _, line := range strings.Split(string(d), "\n") { + if !strings.HasPrefix(line, "Cap") { + continue + } + flavor := line[3:] + if strings.HasPrefix(flavor, "Inh:\t") { + iab.i = parseHex(line[8:], false) + continue + } + if strings.HasPrefix(flavor, "Bnd:\t") { + iab.nb = parseHex(line[8:], true) + continue + } + if strings.HasPrefix(flavor, "Amb:\t") { + iab.a = parseHex(line[8:], false) + continue + } + } + if len(iab.i) != words || len(iab.a) != words || len(iab.nb) != words { + return nil, ErrBadValue + } + return iab, nil +} |