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