| Andrew G. Morgan | 0615d99 | 2019-05-19 14:57:20 -0700 | [diff] [blame^] | 1 | package cap |
| 2 | |
| 3 | import ( |
| 4 | "bufio" |
| 5 | "errors" |
| 6 | "strconv" |
| 7 | "strings" |
| 8 | ) |
| 9 | |
| 10 | // String converts a capability Value into its canonical text |
| 11 | // representation. |
| 12 | func (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. |
| 23 | func 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 | |
| 42 | const ( |
| 43 | eBin uint = (1 << Effective) |
| 44 | pBin = (1 << Permitted) |
| 45 | iBin = (1 << Inheritable) |
| 46 | ) |
| 47 | |
| 48 | var combos = []string{"", "e", "p", "ep", "i", "ei", "pi", "epi"} |
| 49 | |
| 50 | // histo generates a histogram of flag state combinations. |
| 51 | func (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). |
| 72 | func (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. |
| 115 | var ErrBadText = errors.New("bad text") |
| 116 | |
| 117 | // FromText converts the canonical text representation for a Set into |
| 118 | // a freshly allocated Set. |
| 119 | func 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 | } |