xref: /aosp_15_r20/external/libcap/cap/iab.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1package cap
2
3import (
4	"fmt"
5	"io/ioutil"
6	"strconv"
7	"strings"
8	"sync"
9)
10
11// omask returns the offset and mask for a specific capability.
12func omask(c Value) (uint, uint32) {
13	u := uint(c)
14	return u >> 5, uint32(1) << (u & 31)
15}
16
17// IAB holds a summary of all of the inheritable capability vectors:
18// Inh, Amb and Bound. The Bound vector is the logical inverse (two's
19// complement) of the process' Bounding set. That is, raising a Value
20// in the Bound (think blocked) vector is equivalent to dropping that
21// Value from the process' Bounding set. This convention is used to
22// support the empty IAB as being mostly harmless.
23type IAB struct {
24	mu       sync.RWMutex
25	a, i, nb []uint32
26}
27
28// Vector enumerates which of the inheritable IAB capability vectors
29// is being manipulated.
30type Vector uint
31
32// Inh, Amb, Bound enumerate the IAB vector components. (Vector) Inh
33// is equivalent to (Flag) Inheritable. They are named differently for
34// syntax/type checking reasons.
35const (
36	Inh Vector = iota
37	Amb
38	Bound
39)
40
41// IABDiff holds the non-error result of an (*IAB).Cf()
42// function call. It can be interpreted with the function
43// (IABDiff).Has().
44type IABDiff uint
45
46// iBits, iBits and bBits track the (semi-)independent parts of an
47// IABDiff.
48const (
49	iBits IABDiff = 1 << Inh
50	aBits IABDiff = 1 << Amb
51	bBits IABDiff = 1 << Bound
52)
53
54// Has determines if an IAB comparison differs in a specific vector.
55func (d IABDiff) Has(v Vector) bool {
56	return d&(1<<v) != 0
57}
58
59// String identifies a Vector value by its conventional I A or B
60// string abbreviation.
61func (v Vector) String() string {
62	switch v {
63	case Inh:
64		return "I"
65	case Amb:
66		return "A"
67	case Bound:
68		return "B"
69	default:
70		return "<Error>"
71	}
72}
73
74// NewIAB returns an empty IAB.
75func NewIAB() *IAB {
76	startUp.Do(multisc.cInit)
77	return &IAB{
78		i:  make([]uint32, words),
79		a:  make([]uint32, words),
80		nb: make([]uint32, words),
81	}
82}
83
84// good confirms the iab looks to be initialized.
85func (iab *IAB) good() error {
86	if iab == nil || len(iab.i) == 0 || len(iab.i) != words || len(iab.a) != words || len(iab.nb) != words {
87		return ErrBadValue
88	}
89	return nil
90}
91
92// Dup returns a duplicate copy of the IAB.
93func (iab *IAB) Dup() (*IAB, error) {
94	if err := iab.good(); err != nil {
95		return nil, err
96	}
97	v := NewIAB()
98	iab.mu.RLock()
99	defer iab.mu.RUnlock()
100	copy(v.i, iab.i)
101	copy(v.a, iab.a)
102	copy(v.nb, iab.nb)
103	return v, nil
104}
105
106// IABInit allocates a new IAB tuple.
107//
108// Deprecated: Replace with NewIAB.
109//
110// Example, replace this:
111//
112//    iab := IABInit()
113//
114// with this:
115//
116//    iab := NewIAB()
117func IABInit() *IAB {
118	return NewIAB()
119}
120
121// IABGetProc summarizes the Inh, Amb and Bound capability vectors of
122// the current process.
123func IABGetProc() *IAB {
124	iab := NewIAB()
125	current := GetProc()
126	iab.Fill(Inh, current, Inheritable)
127	for c := MaxBits(); c > 0; {
128		c--
129		offset, mask := omask(c)
130		if a, _ := GetAmbient(c); a {
131			iab.a[offset] |= mask
132		}
133		if b, err := GetBound(c); err == nil && !b {
134			iab.nb[offset] |= mask
135		}
136	}
137	return iab
138}
139
140// IABFromText parses a string representing an IAB, as generated
141// by IAB.String(), to generate an IAB.
142func IABFromText(text string) (*IAB, error) {
143	iab := NewIAB()
144	if len(text) == 0 {
145		return iab, nil
146	}
147	for _, f := range strings.Split(text, ",") {
148		var i, a, nb bool
149		var j int
150		for j = 0; j < len(f); j++ {
151			switch f[j : j+1] {
152			case "!":
153				nb = true
154			case "^":
155				i = true
156				a = true
157			case "%":
158				i = true
159			default:
160				goto done
161			}
162		}
163	done:
164		c, err := FromName(f[j:])
165		if err != nil {
166			return nil, err
167		}
168		offset, mask := omask(c)
169		if i || !nb {
170			iab.i[offset] |= mask
171		}
172		if a {
173			iab.a[offset] |= mask
174		}
175		if nb {
176			iab.nb[offset] |= mask
177		}
178	}
179	return iab, nil
180}
181
182// String serializes an IAB to a string format.
183func (iab *IAB) String() string {
184	if err := iab.good(); err != nil {
185		return "<invalid>"
186	}
187	var vs []string
188	iab.mu.RLock()
189	defer iab.mu.RUnlock()
190	for c := Value(0); c < Value(maxValues); c++ {
191		offset, mask := omask(c)
192		i := (iab.i[offset] & mask) != 0
193		a := (iab.a[offset] & mask) != 0
194		nb := (iab.nb[offset] & mask) != 0
195		var cs []string
196		if nb {
197			cs = append(cs, "!")
198		}
199		if a {
200			cs = append(cs, "^")
201		} else if nb && i {
202			cs = append(cs, "%")
203		}
204		if nb || a || i {
205			vs = append(vs, strings.Join(cs, "")+c.String())
206		}
207	}
208	return strings.Join(vs, ",")
209}
210
211// iabSetProc uses a syscaller to apply an IAB tuple to the process.
212// The iab is known to be locked by the caller.
213func (sc *syscaller) iabSetProc(iab *IAB) (err error) {
214	temp := GetProc()
215	var raising uint32
216	for i := 0; i < words; i++ {
217		newI := iab.i[i]
218		oldIP := temp.flat[i][Inheritable] | temp.flat[i][Permitted]
219		raising |= (newI & ^oldIP) | iab.a[i] | iab.nb[i]
220		temp.flat[i][Inheritable] = newI
221	}
222	working, err2 := temp.Dup()
223	if err2 != nil {
224		err = err2
225		return
226	}
227	if raising != 0 {
228		if err = working.SetFlag(Effective, true, SETPCAP); err != nil {
229			return
230		}
231		if err = sc.setProc(working); err != nil {
232			return
233		}
234	}
235	defer func() {
236		if err2 := sc.setProc(temp); err == nil {
237			err = err2
238		}
239	}()
240	if err = sc.resetAmbient(); err != nil {
241		return
242	}
243	for c := Value(maxValues); c > 0; {
244		c--
245		offset, mask := omask(c)
246		if iab.a[offset]&mask != 0 {
247			err = sc.setAmbient(true, c)
248		}
249		if err == nil && iab.nb[offset]&mask != 0 {
250			err = sc.dropBound(c)
251		}
252		if err != nil {
253			return
254		}
255	}
256	return
257}
258
259// SetProc attempts to change the Inheritable, Ambient and Bounding
260// capability vectors of the current process using the content,
261// iab. The Bounding vector strongly affects the potential for setting
262// other bits, so this function carefully performs the combined
263// operation in the most flexible manner.
264func (iab *IAB) SetProc() error {
265	if err := iab.good(); err != nil {
266		return err
267	}
268	state, sc := scwStateSC()
269	defer scwSetState(launchBlocked, state, -1)
270	iab.mu.RLock()
271	defer iab.mu.RUnlock()
272	return sc.iabSetProc(iab)
273}
274
275// GetVector returns the raised state of the specific capability bit
276// of the indicated vector.
277func (iab *IAB) GetVector(vec Vector, val Value) (bool, error) {
278	if err := iab.good(); err != nil {
279		return false, err
280	}
281	if val >= MaxBits() {
282		return false, ErrBadValue
283	}
284	iab.mu.RLock()
285	defer iab.mu.RUnlock()
286	offset, mask := omask(val)
287	switch vec {
288	case Inh:
289		return (iab.i[offset] & mask) != 0, nil
290	case Amb:
291		return (iab.a[offset] & mask) != 0, nil
292	case Bound:
293		return (iab.nb[offset] & mask) != 0, nil
294	default:
295		return false, ErrBadValue
296	}
297}
298
299// SetVector sets all of the vals in the specified vector to the
300// raised value.  Note, the Ambient vector cannot contain values not raised
301// in the Inh vector, so setting values directly in one vector may have
302// the side effect of mirroring the value in the other vector to
303// maintain this constraint. Note, raising a Bound vector bit is
304// equivalent to lowering the Bounding vector of the process (when
305// successfully applied with (*IAB).SetProc()).
306func (iab *IAB) SetVector(vec Vector, raised bool, vals ...Value) error {
307	if err := iab.good(); err != nil {
308		return err
309	}
310	iab.mu.Lock()
311	defer iab.mu.Unlock()
312	for _, val := range vals {
313		if val >= Value(maxValues) {
314			return ErrBadValue
315		}
316		offset, mask := omask(val)
317		switch vec {
318		case Inh:
319			if raised {
320				iab.i[offset] |= mask
321			} else {
322				iab.i[offset] &= ^mask
323				iab.a[offset] &= ^mask
324			}
325		case Amb:
326			if raised {
327				iab.a[offset] |= mask
328				iab.i[offset] |= mask
329			} else {
330				iab.a[offset] &= ^mask
331			}
332		case Bound:
333			if raised {
334				iab.nb[offset] |= mask
335			} else {
336				iab.nb[offset] &= ^mask
337			}
338		default:
339			return ErrBadValue
340		}
341	}
342	return nil
343}
344
345// Fill fills one of the Inh, Amb and Bound capability vectors from
346// one of the flag vectors of a Set.  Note, filling the Inh vector
347// will mask the Amb vector, and filling the Amb vector may raise
348// entries in the Inh vector. Further, when filling the Bound vector,
349// the bits are inverted from what you might expect - that is lowered
350// bits from the Set will be raised in the Bound vector.
351func (iab *IAB) Fill(vec Vector, c *Set, flag Flag) error {
352	if err := iab.good(); err != nil {
353		return err
354	}
355	// work with a copy to avoid potential deadlock.
356	s, err := c.Dup()
357	if err != nil {
358		return err
359	}
360	iab.mu.Lock()
361	defer iab.mu.Unlock()
362	for i := 0; i < words; i++ {
363		flat := s.flat[i][flag]
364		switch vec {
365		case Inh:
366			iab.i[i] = flat
367			iab.a[i] &= flat
368		case Amb:
369			iab.a[i] = flat
370			iab.i[i] |= flat
371		case Bound:
372			iab.nb[i] = ^flat
373		default:
374			return ErrBadSet
375		}
376	}
377	return nil
378}
379
380// Cf compares two IAB values. Its return value is 0 if the compared
381// tuples are considered identical. The macroscopic differences can be
382// investigated with (IABDiff).Has().
383func (iab *IAB) Cf(alt *IAB) (IABDiff, error) {
384	if err := iab.good(); err != nil {
385		return 0, err
386	}
387	if iab == alt {
388		return 0, nil
389	}
390	// Avoid holding two locks at once.
391	ref, err := alt.Dup()
392	if err != nil {
393		return 0, err
394	}
395	iab.mu.RLock()
396	defer iab.mu.RUnlock()
397
398	var cf IABDiff
399	for i := 0; i < words; i++ {
400		if iab.i[i] != ref.i[i] {
401			cf |= iBits
402		}
403		if iab.a[i] != ref.a[i] {
404			cf |= aBits
405		}
406		if iab.nb[i] != ref.nb[i] {
407			cf |= bBits
408		}
409	}
410	return cf, nil
411}
412
413// parseHex converts the /proc/*/status string into an array of
414// uint32s suitable for storage in an IAB structure.
415func parseHex(hex string, invert bool) []uint32 {
416	if len(hex) != 8*words {
417		// Invalid string
418		return nil
419	}
420	var result []uint32
421	for i := 0; i < words; i++ {
422		upper := 8 * (words - i)
423		raw, err := strconv.ParseUint(hex[upper-8:upper], 16, 32)
424		if err != nil {
425			return nil
426		}
427		if invert {
428			raw = ^raw
429		}
430		bits := allMask(uint(i)) & uint32(raw)
431		result = append(result, bits)
432	}
433	return result
434}
435
436var procRoot = "/proc"
437
438// ProcRoot sets the local mount point for the Linux /proc filesystem.
439// It defaults to "/proc", but might be mounted elsewhere on any given
440// system. The function returns the previous value of the local mount
441// point. If the user attempts to set it to "", the value is left
442// unchanged.
443func ProcRoot(path string) string {
444	was := procRoot
445	if path != "" {
446		procRoot = path
447	}
448	return was
449}
450
451// IABGetPID returns the IAB tuple of a specified process. The kernel
452// ABI does not support this query via system calls, so the function
453// works by parsing the /proc/<pid>/status file content.
454func IABGetPID(pid int) (*IAB, error) {
455	tf := fmt.Sprintf("%s/%d/status", procRoot, pid)
456	d, err := ioutil.ReadFile(tf)
457	if err != nil {
458		return nil, err
459	}
460	iab := &IAB{}
461	for _, line := range strings.Split(string(d), "\n") {
462		if !strings.HasPrefix(line, "Cap") {
463			continue
464		}
465		flavor := line[3:]
466		if strings.HasPrefix(flavor, "Inh:\t") {
467			iab.i = parseHex(line[8:], false)
468			continue
469		}
470		if strings.HasPrefix(flavor, "Bnd:\t") {
471			iab.nb = parseHex(line[8:], true)
472			continue
473		}
474		if strings.HasPrefix(flavor, "Amb:\t") {
475			iab.a = parseHex(line[8:], false)
476			continue
477		}
478	}
479	if len(iab.i) != words || len(iab.a) != words || len(iab.nb) != words {
480		return nil, ErrBadValue
481	}
482	return iab, nil
483}
484