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