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