aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2021-08-22 14:32:12 -0700
committerAndrew G. Morgan <morgan@kernel.org>2021-08-22 14:32:12 -0700
commitac297b51c6730c22a06b759bd4235b47c52053bc (patch)
tree1b2bf1952f6ccf5f1b329fcf8b55dc08f9cfaa05
parent25cdfaf7f813a94612279712a2d6d1e1bb39e08b (diff)
downloadlibcap-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.go9
-rw-r--r--cap/cap_test.go2
-rw-r--r--cap/convenience.go6
-rw-r--r--cap/flags.go29
-rw-r--r--cap/iab.go124
5 files changed, 157 insertions, 13 deletions
diff --git a/cap/cap.go b/cap/cap.go
index 908e2bb..d4b241e 100644
--- a/cap/cap.go
+++ b/cap/cap.go
@@ -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
+}
diff --git a/cap/iab.go b/cap/iab.go
index 77f2dbc..816118f 100644
--- a/cap/iab.go
+++ b/cap/iab.go
@@ -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
+}