xref: /aosp_15_r20/external/libcap/cap/text.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1package cap
2
3import (
4	"bufio"
5	"errors"
6	"strconv"
7	"strings"
8)
9
10// String converts a capability Value into its canonical text
11// representation.
12func (v Value) String() string {
13	name, ok := names[v]
14	if ok {
15		return name
16	}
17	// Un-named capabilities are referred to numerically (in decimal).
18	return strconv.Itoa(int(v))
19}
20
21// FromName converts a named capability Value to its binary
22// representation.
23func FromName(name string) (Value, error) {
24	startUp.Do(multisc.cInit)
25	v, ok := bits[name]
26	if ok {
27		if v >= Value(words*32) {
28			return 0, ErrBadValue
29		}
30		return v, nil
31	}
32	i, err := strconv.Atoi(name)
33	if err != nil {
34		return 0, err
35	}
36	if i >= 0 && i < int(words*32) {
37		return Value(i), nil
38	}
39	return 0, ErrBadValue
40}
41
42const (
43	eBin uint = (1 << Effective)
44	pBin      = (1 << Permitted)
45	iBin      = (1 << Inheritable)
46)
47
48var combos = []string{"", "e", "p", "ep", "i", "ei", "ip", "eip"}
49
50// histo generates a histogram of flag state combinations.
51// Note: c is locked by or private to the caller.
52func (c *Set) histo(bins []int, patterns []uint, from, limit Value) uint {
53	for v := from; v < limit; v++ {
54		b := uint(v & 31)
55		u, bit, err := bitOf(0, v)
56		if err != nil {
57			break
58		}
59		x := uint((c.flat[u][Effective]&bit)>>b) * eBin
60		x |= uint((c.flat[u][Permitted]&bit)>>b) * pBin
61		x |= uint((c.flat[u][Inheritable]&bit)>>b) * iBin
62		bins[x]++
63		patterns[uint(v)] = x
64	}
65	// Note, in the loop, we use >= to pick the smallest value for
66	// m with the highest bin value. That is ties break towards
67	// m=0.
68	m := uint(7)
69	for t := m; t > 0; {
70		t--
71		if bins[t] >= bins[m] {
72			m = t
73		}
74	}
75	return m
76}
77
78// String converts a full capability Set into a single short readable
79// string representation (which may contain spaces). See the
80// cap.FromText() function for an explanation of its return values.
81//
82// Note (*cap.Set).String() may evolve to generate more compact
83// strings representing the a given Set over time, but it should
84// maintain compatibility with the libcap:cap_to_text() function for
85// any given release. Further, it will always be an inverse of
86// cap.FromText().
87func (c *Set) String() string {
88	if err := c.good(); err != nil {
89		return "<invalid>"
90	}
91	bins := make([]int, 8)
92	patterns := make([]uint, maxValues)
93
94	c.mu.RLock()
95	defer c.mu.RUnlock()
96
97	// Note, in order to have a *Set pointer, startUp.Do(cInit)
98	// must have been called which sets maxValues.
99	m := c.histo(bins, patterns, 0, Value(maxValues))
100
101	// Background state is the most popular of the named bits.
102	vs := []string{"=" + combos[m]}
103	for i := uint(8); i > 0; {
104		i--
105		if i == m || bins[i] == 0 {
106			continue
107		}
108		var list []string
109		for j, p := range patterns {
110			if p != i {
111				continue
112			}
113			list = append(list, Value(j).String())
114		}
115		x := strings.Join(list, ",")
116		var y, z string
117		if cf := i & ^m; cf != 0 {
118			op := "+"
119			if len(vs) == 1 && vs[0] == "=" {
120				// Special case "= foo+..." == "foo=...".
121				// Prefer because it
122				vs = nil
123				op = "="
124			}
125			y = op + combos[cf]
126		}
127		if cf := m & ^i; cf != 0 {
128			z = "-" + combos[cf]
129		}
130		vs = append(vs, x+y+z)
131	}
132
133	// The unnamed bits can only add to the above named ones since
134	// unnamed ones are always defaulted to lowered.
135	uBins := make([]int, 8)
136	uPatterns := make([]uint, 32*words)
137	c.histo(uBins, uPatterns, Value(maxValues), 32*Value(words))
138	for i := uint(7); i > 0; i-- {
139		if uBins[i] == 0 {
140			continue
141		}
142		var list []string
143		for j, p := range uPatterns {
144			if p != i {
145				continue
146			}
147			list = append(list, Value(j).String())
148		}
149		vs = append(vs, strings.Join(list, ",")+"+"+combos[i])
150	}
151
152	return strings.Join(vs, " ")
153}
154
155// ErrBadText is returned if the text for a capability set cannot be parsed.
156var ErrBadText = errors.New("bad text")
157
158// FromText converts the canonical text representation for a Set into
159// a freshly allocated Set.
160//
161// The format follows the following pattern: a set of space separated
162// sequences. Each sequence applies over the previous sequence to
163// build up a Set. The format of a sequence is:
164//
165//   [comma list of cap_values][[ops][flags]]*
166//
167// Examples:
168//
169//   "all=ep"
170//   "cap_chown,cap_setuid=ip cap_setuid+e"
171//   "=p cap_setpcap-p+i"
172//
173// Here "all" refers to all named capabilities known to the hosting
174// kernel, and "all" is assumed if no capabilities are listed before
175// an "=".
176//
177// The ops values, "=", "+" and "-" imply "reset and raise", "raise"
178// and "lower" respectively. The "e", "i" and "p" characters
179// correspond to the capabilities of the corresponding Flag: "e"
180// (Effective); "i" (Inheritable); "p" (Permitted).
181//
182// This syntax is overspecified and there are many ways of building
183// the same final Set state. Any sequence that includes a '=' resets
184// the accumulated state of all Flags ignoring earlier sequences. On
185// each of the following lines we give three or more examples of ways
186// to specify a common Set. The last entry on each line is the one
187// generated by (*cap.Set).String() from that Set.
188//
189//    "=p all+ei"  "all=pie"   "=pi all+e"   "=eip"
190//
191//    "cap_setuid=p cap_chown=i"  "cap_chown=ip-p"   "cap_chown=i"
192//
193//    "cap_chown=-p"   "all="   "cap_setuid=pie-pie"   "="
194//
195// Note: FromText() is tested at release time to completely match the
196// import ability of the libcap:cap_from_text() function.
197func FromText(text string) (*Set, error) {
198	c := NewSet()
199	scanner := bufio.NewScanner(strings.NewReader(text))
200	scanner.Split(bufio.ScanWords)
201	chunks := 0
202	for scanner.Scan() {
203		chunks++
204
205		// Parsing for xxx([-+=][eip]+)+
206		t := scanner.Text()
207		i := strings.IndexAny(t, "=+-")
208		if i < 0 {
209			return nil, ErrBadText
210		}
211		var vs []Value
212		sep := t[i]
213		if vals := t[:i]; vals == "all" {
214			for v := Value(0); v < Value(maxValues); v++ {
215				vs = append(vs, v)
216			}
217		} else if vals != "" {
218			for _, name := range strings.Split(vals, ",") {
219				v, err := FromName(name)
220				if err != nil {
221					return nil, ErrBadText
222				}
223				vs = append(vs, v)
224			}
225		} else if sep != '=' {
226			if vals == "" {
227				// Only "=" supports ""=="all".
228				return nil, ErrBadText
229			}
230		} else if j := i + 1; j+1 < len(t) {
231			switch t[j] {
232			case '+':
233				sep = 'P'
234				i++
235			case '-':
236				sep = 'M'
237				i++
238			}
239		}
240		i++
241
242		// There are 5 ways to set: =, =+, =-, +, -. We call
243		// the 2nd and 3rd of these 'P' and 'M'.
244
245		for {
246			// read [eip]+ setting flags.
247			var fE, fP, fI bool
248			for ok := true; ok && i < len(t); i++ {
249				switch t[i] {
250				case 'e':
251					fE = true
252				case 'i':
253					fI = true
254				case 'p':
255					fP = true
256				default:
257					ok = false
258				}
259				if !ok {
260					break
261				}
262			}
263
264			if !(fE || fI || fP) {
265				if sep != '=' {
266					return nil, ErrBadText
267				}
268			}
269
270			switch sep {
271			case '=', 'P', 'M', '+':
272				if sep != '+' {
273					c.Clear()
274					if sep == 'M' {
275						break
276					}
277				}
278				if keep := len(vs) == 0; keep {
279					if sep != '=' {
280						return nil, ErrBadText
281					}
282					c.forceFlag(Effective, fE)
283					c.forceFlag(Permitted, fP)
284					c.forceFlag(Inheritable, fI)
285					break
286				}
287				// =, + and P for specific values are left.
288				if fE {
289					c.SetFlag(Effective, true, vs...)
290				}
291				if fP {
292					c.SetFlag(Permitted, true, vs...)
293				}
294				if fI {
295					c.SetFlag(Inheritable, true, vs...)
296				}
297			case '-':
298				if fE {
299					c.SetFlag(Effective, false, vs...)
300				}
301				if fP {
302					c.SetFlag(Permitted, false, vs...)
303				}
304				if fI {
305					c.SetFlag(Inheritable, false, vs...)
306				}
307			}
308
309			if i == len(t) {
310				break
311			}
312
313			switch t[i] {
314			case '+', '-':
315				sep = t[i]
316				i++
317			default:
318				return nil, ErrBadText
319			}
320		}
321	}
322	if chunks == 0 {
323		return nil, ErrBadText
324	}
325	return c, nil
326}
327