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