IAB Go support.

Under Linux there are three inheritable vectors worth of capability
vectors. A and B require privilege to set.

Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
diff --git a/cap/cap_test.go b/cap/cap_test.go
index 7b1f39f..662afab 100644
--- a/cap/cap_test.go
+++ b/cap/cap_test.go
@@ -140,3 +140,71 @@
 		}
 	}
 }
+
+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 := IABInit()
+	iab.Fill(Amb, one, Permitted)
+	for i := 0; i < words; i++ {
+		if iab.i[i] != iab.a[i] {
+			t.Errorf("[%d] i=0x%08x != a=0x%08x", i, 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] i=0x%08x != a=0x%08x", i, 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)
+			}
+		}
+	}
+}
diff --git a/cap/iab.go b/cap/iab.go
new file mode 100644
index 0000000..c39e260
--- /dev/null
+++ b/cap/iab.go
@@ -0,0 +1,261 @@
+package cap
+
+import "strings"
+
+// omask returns the offset and mask for a specific capability.
+func omask(c Value) (uint, uint32) {
+	u := uint(c)
+	return u >> 5, uint32(1) << (u & 31)
+}
+
+// IAB holds a summary of all of the inheritable capability vectors:
+// Inheritable, Ambient and Bounding sets. The Bounding set entries
+// are inverted insofar as a raised bit here implies that when applied
+// to the current process, the capability is dropped - think of this
+// as synonymous with Blocked. This convention is used to support the
+// empty IAB as being mostly harmless.
+type IAB struct {
+	a, i, nb []uint32
+}
+
+// Vector enumerates which of the inheritable IAB capability vectors
+// is being manipulated.
+type Vector int
+
+// Inh, Amb, Bound enumerate the IAB vector components. IMB.Inh is
+// equivalent Set.Inheritable but they are named differently for
+// syntax/type checking reasons.
+const (
+	Inh Vector = iota
+	Amb
+	Bound
+)
+
+// IABInit() returns an empty IAB.
+func IABInit() *IAB {
+	startUp.Do(cInit)
+	return &IAB{
+		i:  make([]uint32, words),
+		a:  make([]uint32, words),
+		nb: make([]uint32, words),
+	}
+}
+
+// IABGetProc summarizes the Inheritable, Ambient and Bounding
+// capabilty vectors of the current process.
+func IABGetProc() *IAB {
+	iab := IABInit()
+	current := GetProc()
+	iab.Fill(Inh, current, Inheritable)
+	for c := MaxBits(); c > 0; {
+		c--
+		offset, mask := omask(c)
+		if a, _ := GetAmbient(c); a {
+			iab.a[offset] |= mask
+		}
+		if b, err := GetBound(c); err == nil && !b {
+			iab.nb[offset] |= mask
+		}
+	}
+	return iab
+}
+
+// IABFromText parses a string representing an IAB set, as generated
+// by IAB.String(), to generate an IAB.
+func IABFromText(text string) (*IAB, error) {
+	iab := IABInit()
+	if len(text) == 0 {
+		return iab, nil
+	}
+	for _, f := range strings.Split(text, ",") {
+		var i, a, nb bool
+		var j int
+		for j = 0; j < len(f); j++ {
+			switch f[j : j+1] {
+			case "!":
+				nb = true
+			case "^":
+				i = true
+				a = true
+			case "%":
+				i = true
+			default:
+				goto done
+			}
+		}
+	done:
+		c, err := FromName(f[j:])
+		if err != nil {
+			return nil, err
+		}
+		offset, mask := omask(c)
+		if i || !nb {
+			iab.i[offset] |= mask
+		}
+		if a {
+			iab.a[offset] |= mask
+		}
+		if nb {
+			iab.nb[offset] |= mask
+		}
+	}
+	return iab, nil
+}
+
+// String serializes an IAB to a string format.
+func (iab *IAB) String() string {
+	var vs []string
+	for c := Value(0); c < Value(maxValues); c++ {
+		offset, mask := omask(c)
+		i := (iab.i[offset] & mask) != 0
+		a := (iab.a[offset] & mask) != 0
+		nb := (iab.nb[offset] & mask) != 0
+		var cs []string
+		if nb {
+			cs = append(cs, "!")
+		}
+		if a {
+			cs = append(cs, "^")
+		} else if nb && i {
+			cs = append(cs, "%")
+		}
+		if nb || a || i {
+			vs = append(vs, strings.Join(cs, "")+c.String())
+		}
+	}
+	return strings.Join(vs, ",")
+}
+
+// SetProc attempts to change the Inheritable, Ambient and Bounding
+// capabilty vectors of the current process.
+func (iab *IAB) SetProc() (err error) {
+	temp := GetProc()
+	var raising uint32
+	for i := 0; i < words; i++ {
+		newI := iab.i[i]
+		oldIP := temp.flat[i][Inheritable] | temp.flat[i][Permitted]
+		raising |= (newI & ^oldIP) | iab.a[i] | iab.nb[i]
+		temp.flat[i][Inheritable] = newI
+	}
+	working, err2 := temp.Dup()
+	if err2 != nil {
+		err = err2
+		return
+	}
+	if raising != 0 {
+		if err = working.SetFlag(Effective, true, SETPCAP); err != nil {
+			return
+		}
+		if err = working.SetProc(); err != nil {
+			return
+		}
+	}
+	defer func() {
+		if err2 := temp.SetProc(); err == nil {
+			err = err2
+		}
+	}()
+	if err = ResetAmbient(); err != nil {
+		return
+	}
+	for c := Value(maxValues); c > 0; {
+		c--
+		offset, mask := omask(c)
+		if iab.a[offset]&mask != 0 {
+			err = SetAmbient(true, c)
+		}
+		if err == nil && iab.nb[offset]&mask != 0 {
+			err = DropBound(c)
+		}
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+// GetVector returns the raised state of the specific capability bit
+// of the indicated vector.
+func (iab *IAB) GetVector(vec Vector, val Value) (bool, error) {
+	if val >= MaxBits() {
+		return false, ErrBadValue
+	}
+	offset, mask := omask(val)
+	switch vec {
+	case Inh:
+		return (iab.i[offset] & mask) != 0, nil
+	case Amb:
+		return (iab.a[offset] & mask) != 0, nil
+	case Bound:
+		return (iab.nb[offset] & mask) != 0, nil
+	default:
+		return false, ErrBadValue
+	}
+}
+
+// SetVector sets all of the values in the specified vector to the
+// raised value.  Note, the A vector cannot contain values not raised
+// in the I vector, so setting values directly in one vector may have
+// the side effect of mirroring the value in the other vector to
+// maintain this constraint.
+func (iab *IAB) SetVector(vec Vector, raised bool, vals ...Value) error {
+	for _, val := range vals {
+		if val >= Value(maxValues) {
+			return ErrBadValue
+		}
+		offset, mask := omask(val)
+		switch vec {
+		case Inh:
+			if raised {
+				iab.i[offset] |= mask
+			} else {
+				iab.i[offset] &= ^mask
+				iab.a[offset] &= ^mask
+			}
+		case Amb:
+			if raised {
+				iab.a[offset] |= mask
+				iab.i[offset] |= mask
+			} else {
+				iab.a[offset] &= ^mask
+			}
+		case Bound:
+			if raised {
+				iab.nb[offset] |= mask
+			} else {
+				iab.nb[offset] &= ^mask
+			}
+		default:
+			return ErrBadValue
+		}
+	}
+	return nil
+}
+
+// Fill fills one of the Inheritable, Ambient and Bounding
+// capability vectors from one of the flag vectors of a Set.  Note,
+// filling the I vector will mask the A vector, and filling the A
+// vector may raise entries in the I vector. Further, when filling the
+// Bounding vector, the bits are inverted - that is lowered bits from
+// the Set will be raised in the Bounding vector.
+func (iab *IAB) Fill(vec Vector, c *Set, flag Flag) error {
+	if len(c.flat) != 0 || flag > Inheritable {
+		return ErrBadSet
+	}
+	for i := 0; i < words; i++ {
+		flat := c.flat[i][flag]
+		switch vec {
+		case Inh:
+			iab.i[i] = flat
+			iab.a[i] &= ^flat
+		case Amb:
+			iab.a[i] = flat
+			iab.i[i] |= ^flat
+		case Bound:
+			iab.nb[i] = ^flat
+		default:
+			return ErrBadSet
+		}
+	}
+	return nil
+}
diff --git a/go/compare-cap.go b/go/compare-cap.go
index e57e946..19c618a 100644
--- a/go/compare-cap.go
+++ b/go/compare-cap.go
@@ -281,6 +281,45 @@
 		log.Fatalf("all decode failed in Go: got=%q, want=%q", got, want)
 	}
 
+	iab, err := cap.IABFromText("cap_chown,!cap_setuid,^cap_setgid")
+	if err != nil {
+		log.Fatalf("failed to initialize iab from text: %v", err)
+	}
+	cIAB := C.cap_iab_init()
+	defer C.cap_free(unsafe.Pointer(cIAB))
+	for c := cap.MaxBits(); c > 0; {
+		c--
+		if en, err := iab.GetVector(cap.Inh, c); err != nil {
+			log.Fatalf("failed to read iab.i[%v]", c)
+		} else if en {
+			if C.cap_iab_set_vector(cIAB, C.CAP_IAB_INH, C.cap_value_t(int(c)), C.CAP_SET) != 0 {
+				log.Fatalf("failed to set C's AIB.I %v: %v", c)
+			}
+		}
+		if en, err := iab.GetVector(cap.Amb, c); err != nil {
+			log.Fatalf("failed to read iab.a[%v]", c)
+		} else if en {
+			if C.cap_iab_set_vector(cIAB, C.CAP_IAB_AMB, C.cap_value_t(int(c)), C.CAP_SET) != 0 {
+				log.Fatalf("failed to set C's AIB.A %v: %v", c)
+			}
+		}
+		if en, err := iab.GetVector(cap.Bound, c); err != nil {
+			log.Fatalf("failed to read iab.b[%v]", c)
+		} else if en {
+			if C.cap_iab_set_vector(cIAB, C.CAP_IAB_BOUND, C.cap_value_t(int(c)), C.CAP_SET) != 0 {
+				log.Fatalf("failed to set C's AIB.B %v: %v", c)
+			}
+		}
+	}
+	iabC := C.cap_iab_to_text(cIAB)
+	if iabC == nil {
+		log.Fatalf("failed to get text from C for %q", iab)
+	}
+	defer C.cap_free(unsafe.Pointer(iabC))
+	if got, want := C.GoString(iabC), iab.String(); got != want {
+		log.Fatalf("IAB for Go and C differ: got=%q, want=%q", got, want)
+	}
+
 	// Next, we attempt to manipulate some file capabilities on
 	// the running program.  These are optional, based on whether
 	// the current program is capable enough and do not involve