blob: 0af6290b75bfdb276338caeb7e6b69761afd3609 [file] [log] [blame]
Andrew G. Morgan0615d992019-05-19 14:57:20 -07001package 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(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", "pi", "epi"}
49
50// histo generates a histogram of flag state combinations.
51func (c *Set) histo(m uint, bins []int, patterns []uint, from, limit Value) uint {
52 for v := from; v < limit; v++ {
53 b := uint(v & 31)
54 u, mask, err := bitOf(0, v)
55 if err != nil {
56 break
57 }
58 x := uint((c.flat[u][Effective]&mask)>>b) * eBin
59 x |= uint((c.flat[u][Permitted]&mask)>>b) * pBin
60 x |= uint((c.flat[u][Inheritable]&mask)>>b) * iBin
61 bins[x]++
62 patterns[uint(v)] = x
63 if bins[m] <= bins[x] {
64 m = x
65 }
66 }
67 return m
68}
69
70// String converts a full capability Set into it canonical readable
71// string representation (which may contain spaces).
72func (c *Set) String() string {
73 if c == nil {
74 return "<invalid>"
75 }
76 bins := make([]int, 8)
77 patterns := make([]uint, 32*words)
78
79 c.mu.RLock()
80 defer c.mu.RUnlock()
81
82 // Note, in order to have a *Set pointer, startUp.Do(cInit)
83 // must have been called which sets maxValues.
84 m := c.histo(0, bins, patterns, Value(maxValues), 32*Value(words))
85 // Background state is the most popular of the unnamed bits,
86 // this has the effect of tending to not list numerical values
87 // for unnamed capabilities in the generated text.
88 vs := []string{"=" + combos[m]}
89
90 // Extend the histogram with the remaining Value occurrences.
91 c.histo(m, bins, patterns, 0, Value(maxValues))
92 for i := uint(8); i > 0; {
93 i--
94 if i == m || bins[i] == 0 {
95 continue
96 }
97 var list []string
98 for j, p := range patterns {
99 if p != i {
100 continue
101 }
102 list = append(list, Value(j).String())
103 }
104 if cf := i & ^m; cf != 0 {
105 vs = append(vs, strings.Join(list, ",")+"+"+combos[cf])
106 }
107 if cf := m & ^i; cf != 0 {
108 vs = append(vs, strings.Join(list, ",")+"-"+combos[cf])
109 }
110 }
111 return strings.Join(vs, " ")
112}
113
114// ErrBadText is returned if the text for a capability set cannot be parsed.
115var ErrBadText = errors.New("bad text")
116
117// FromText converts the canonical text representation for a Set into
118// a freshly allocated Set.
119func FromText(text string) (*Set, error) {
120 c := NewSet()
121 scanner := bufio.NewScanner(strings.NewReader(text))
122 scanner.Split(bufio.ScanWords)
123 chunks := 0
124 for scanner.Scan() {
125 chunks++
126 // Parsing for xxx[-+=][eip]+
127 t := scanner.Text()
128 i := strings.IndexAny(t, "=+-")
129 if i < 0 {
130 return nil, ErrBadText
131 }
132 var vs []Value
133 sep := t[i]
134 if vals := t[:i]; vals != "all" && vals != "" {
135 for _, name := range strings.Split(vals, ",") {
136 v, err := FromName(name)
137 if err != nil {
138 return nil, ErrBadText
139 }
140 vs = append(vs, v)
141 }
142 } else if sep != '=' && vals == "" {
143 return nil, ErrBadText // Only "=" supports ""=="all".
144 }
145 sets := t[i+1:]
146 var fE, fP, fI bool
147 for j := 0; j < len(sets); j++ {
148 switch sets[j] {
149 case 'e':
150 fE = true
151 case 'p':
152 fP = true
153 case 'i':
154 fI = true
155 default:
156 return nil, ErrBadText
157 }
158 }
159 if sep == '=' {
160 keep := len(vs) == 0 // '=' means default to off except setting individual values.
161 c.forceFlag(Effective, fE && keep)
162 c.forceFlag(Permitted, fP && keep)
163 c.forceFlag(Inheritable, fI && keep)
164 if keep {
165 continue
166 }
167 }
168 if fE {
169 c.SetFlag(Effective, sep != '-', vs...)
170 }
171 if fP {
172 c.SetFlag(Permitted, sep != '-', vs...)
173 }
174 if fI {
175 c.SetFlag(Inheritable, sep == '+', vs...)
176 }
177 }
178 if chunks == 0 {
179 return nil, ErrBadText
180 }
181 return c, nil
182}