package cap import ( "fmt" "io/ioutil" "strconv" "strings" "sync" ) // 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: // Inh, Amb and Bound. The Bound vector is the logical inverse (two's // complement) of the process' Bounding set. That is, raising a Value // in the Bound (think blocked) vector is equivalent to dropping that // Value from the process' Bounding set. This convention is used to // support the empty IAB as being mostly harmless. type IAB struct { mu sync.RWMutex a, i, nb []uint32 } // Vector enumerates which of the inheritable IAB capability vectors // is being manipulated. type Vector uint // Inh, Amb, Bound enumerate the IAB vector components. (Vector) Inh // is equivalent to (Flag) Inheritable. They are named differently for // syntax/type checking reasons. const ( Inh Vector = iota Amb 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<" } } // NewIAB returns an empty IAB. func NewIAB() *IAB { startUp.Do(multisc.cInit) return &IAB{ i: make([]uint32, words), a: make([]uint32, words), nb: make([]uint32, words), } } // good confirms the iab looks to be initialized. func (iab *IAB) good() error { if iab == nil || len(iab.i) == 0 || len(iab.i) != words || len(iab.a) != words || len(iab.nb) != words { return ErrBadValue } return nil } // Dup returns a duplicate copy of the IAB. func (iab *IAB) Dup() (*IAB, error) { if err := iab.good(); err != nil { return nil, err } v := NewIAB() iab.mu.RLock() defer iab.mu.RUnlock() copy(v.i, iab.i) copy(v.a, iab.a) copy(v.nb, iab.nb) return v, nil } // IABInit allocates a new IAB tuple. // // Deprecated: Replace with NewIAB. // // Example, replace this: // // iab := IABInit() // // with this: // // iab := NewIAB() func IABInit() *IAB { return NewIAB() } // IABGetProc summarizes the Inh, Amb and Bound capability vectors of // the current process. func IABGetProc() *IAB { iab := NewIAB() 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, as generated // by IAB.String(), to generate an IAB. func IABFromText(text string) (*IAB, error) { iab := NewIAB() 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 { if err := iab.good(); err != nil { return "" } var vs []string iab.mu.RLock() defer iab.mu.RUnlock() 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, ",") } // iabSetProc uses a syscaller to apply an IAB tuple to the process. // The iab is known to be locked by the caller. func (sc *syscaller) iabSetProc(iab *IAB) (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 = sc.setProc(working); err != nil { return } } defer func() { if err2 := sc.setProc(temp); err == nil { err = err2 } }() if err = sc.resetAmbient(); err != nil { return } for c := Value(maxValues); c > 0; { c-- offset, mask := omask(c) if iab.a[offset]&mask != 0 { err = sc.setAmbient(true, c) } if err == nil && iab.nb[offset]&mask != 0 { err = sc.dropBound(c) } if err != nil { return } } return } // SetProc attempts to change the Inheritable, Ambient and Bounding // capability vectors of the current process using the content, // iab. The Bounding vector strongly affects the potential for setting // other bits, so this function carefully performs the combined // operation in the most flexible manner. func (iab *IAB) SetProc() error { if err := iab.good(); err != nil { return err } state, sc := scwStateSC() defer scwSetState(launchBlocked, state, -1) iab.mu.RLock() defer iab.mu.RUnlock() return sc.iabSetProc(iab) } // 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 err := iab.good(); err != nil { return false, err } if val >= MaxBits() { return false, ErrBadValue } iab.mu.RLock() defer iab.mu.RUnlock() 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 vals in the specified vector to the // raised value. Note, the Ambient vector cannot contain values not raised // in the Inh 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. Note, raising a Bound vector bit is // equivalent to lowering the Bounding vector of the process (when // successfully applied with (*IAB).SetProc()). func (iab *IAB) SetVector(vec Vector, raised bool, vals ...Value) error { if err := iab.good(); err != nil { return err } iab.mu.Lock() defer iab.mu.Unlock() 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 Inh, Amb and Bound capability vectors from // one of the flag vectors of a Set. Note, filling the Inh vector // will mask the Amb vector, and filling the Amb vector may raise // entries in the Inh vector. Further, when filling the Bound vector, // the bits are inverted from what you might expect - that is lowered // bits from the Set will be raised in the Bound vector. func (iab *IAB) Fill(vec Vector, c *Set, flag Flag) error { if err := iab.good(); err != nil { return err } // work with a copy to avoid potential deadlock. s, err := c.Dup() if err != nil { return err } iab.mu.Lock() defer iab.mu.Unlock() for i := 0; i < words; i++ { flat := s.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 } // 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 err := iab.good(); err != nil { return 0, err } if iab == alt { return 0, nil } // Avoid holding two locks at once. ref, err := alt.Dup() if err != nil { return 0, err } iab.mu.RLock() defer iab.mu.RUnlock() var cf IABDiff for i := 0; i < words; i++ { if iab.i[i] != ref.i[i] { cf |= iBits } if iab.a[i] != ref.a[i] { cf |= aBits } if iab.nb[i] != ref.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 } var procRoot = "/proc" // ProcRoot sets the local mount point for the Linux /proc filesystem. // It defaults to "/proc", but might be mounted elsewhere on any given // system. The function returns the previous value of the local mount // point. If the user attempts to set it to "", the value is left // unchanged. func ProcRoot(path string) string { was := procRoot if path != "" { procRoot = path } return was } // 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//status file content. func IABGetPID(pid int) (*IAB, error) { tf := fmt.Sprintf("%s/%d/status", procRoot, 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 }