You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
7.4 KiB
326 lines
7.4 KiB
package cap
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// String converts a capability Value into its canonical text
|
|
// representation.
|
|
func (v Value) String() string {
|
|
name, ok := names[v]
|
|
if ok {
|
|
return name
|
|
}
|
|
// Un-named capabilities are referred to numerically (in decimal).
|
|
return strconv.Itoa(int(v))
|
|
}
|
|
|
|
// FromName converts a named capability Value to its binary
|
|
// representation.
|
|
func FromName(name string) (Value, error) {
|
|
startUp.Do(multisc.cInit)
|
|
v, ok := bits[name]
|
|
if ok {
|
|
if v >= Value(words*32) {
|
|
return 0, ErrBadValue
|
|
}
|
|
return v, nil
|
|
}
|
|
i, err := strconv.Atoi(name)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if i >= 0 && i < int(words*32) {
|
|
return Value(i), nil
|
|
}
|
|
return 0, ErrBadValue
|
|
}
|
|
|
|
const (
|
|
eBin uint = (1 << Effective)
|
|
pBin = (1 << Permitted)
|
|
iBin = (1 << Inheritable)
|
|
)
|
|
|
|
var combos = []string{"", "e", "p", "ep", "i", "ei", "ip", "eip"}
|
|
|
|
// histo generates a histogram of flag state combinations.
|
|
func (c *Set) histo(bins []int, patterns []uint, from, limit Value) uint {
|
|
for v := from; v < limit; v++ {
|
|
b := uint(v & 31)
|
|
u, bit, err := bitOf(0, v)
|
|
if err != nil {
|
|
break
|
|
}
|
|
x := uint((c.flat[u][Effective]&bit)>>b) * eBin
|
|
x |= uint((c.flat[u][Permitted]&bit)>>b) * pBin
|
|
x |= uint((c.flat[u][Inheritable]&bit)>>b) * iBin
|
|
bins[x]++
|
|
patterns[uint(v)] = x
|
|
}
|
|
// Note, in the loop, we use >= to pick the smallest value for
|
|
// m with the highest bin value. That is ties break towards
|
|
// m=0.
|
|
m := uint(7)
|
|
for t := m; t > 0; {
|
|
t--
|
|
if bins[t] >= bins[m] {
|
|
m = t
|
|
}
|
|
}
|
|
return m
|
|
}
|
|
|
|
// String converts a full capability Set into a single short readable
|
|
// string representation (which may contain spaces). See the
|
|
// cap.FromText() function for an explanation of its return values.
|
|
//
|
|
// Note (*cap.Set).String() may evolve to generate more compact
|
|
// strings representing the a given Set over time, but it should
|
|
// maintain compatibility with the libcap:cap_to_text() function for
|
|
// any given release. Further, it will always be an inverse of
|
|
// cap.FromText().
|
|
func (c *Set) String() string {
|
|
if c == nil || len(c.flat) == 0 {
|
|
return "<invalid>"
|
|
}
|
|
bins := make([]int, 8)
|
|
patterns := make([]uint, maxValues)
|
|
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
// Note, in order to have a *Set pointer, startUp.Do(cInit)
|
|
// must have been called which sets maxValues.
|
|
m := c.histo(bins, patterns, 0, Value(maxValues))
|
|
|
|
// Background state is the most popular of the named bits.
|
|
vs := []string{"=" + combos[m]}
|
|
for i := uint(8); i > 0; {
|
|
i--
|
|
if i == m || bins[i] == 0 {
|
|
continue
|
|
}
|
|
var list []string
|
|
for j, p := range patterns {
|
|
if p != i {
|
|
continue
|
|
}
|
|
list = append(list, Value(j).String())
|
|
}
|
|
x := strings.Join(list, ",")
|
|
var y, z string
|
|
if cf := i & ^m; cf != 0 {
|
|
op := "+"
|
|
if len(vs) == 1 && vs[0] == "=" {
|
|
// Special case "= foo+..." == "foo=...".
|
|
// Prefer because it
|
|
vs = nil
|
|
op = "="
|
|
}
|
|
y = op + combos[cf]
|
|
}
|
|
if cf := m & ^i; cf != 0 {
|
|
z = "-" + combos[cf]
|
|
}
|
|
vs = append(vs, x+y+z)
|
|
}
|
|
|
|
// The unnamed bits can only add to the above named ones since
|
|
// unnamed ones are always defaulted to lowered.
|
|
uBins := make([]int, 8)
|
|
uPatterns := make([]uint, 32*words)
|
|
c.histo(uBins, uPatterns, Value(maxValues), 32*Value(words))
|
|
for i := uint(7); i > 0; i-- {
|
|
if uBins[i] == 0 {
|
|
continue
|
|
}
|
|
var list []string
|
|
for j, p := range uPatterns {
|
|
if p != i {
|
|
continue
|
|
}
|
|
list = append(list, Value(j).String())
|
|
}
|
|
vs = append(vs, strings.Join(list, ",")+"+"+combos[i])
|
|
}
|
|
|
|
return strings.Join(vs, " ")
|
|
}
|
|
|
|
// ErrBadText is returned if the text for a capability set cannot be parsed.
|
|
var ErrBadText = errors.New("bad text")
|
|
|
|
// FromText converts the canonical text representation for a Set into
|
|
// a freshly allocated Set.
|
|
//
|
|
// The format follows the following pattern: a set of space separated
|
|
// sequences. Each sequence applies over the previous sequence to
|
|
// build up a Set. The format of a sequence is:
|
|
//
|
|
// [comma list of cap_values][[ops][flags]]*
|
|
//
|
|
// Examples:
|
|
//
|
|
// "all=ep"
|
|
// "cap_chown,cap_setuid=ip cap_setuid+e"
|
|
// "=p cap_setpcap-p+i"
|
|
//
|
|
// Here "all" refers to all named capabilities known to the hosting
|
|
// kernel, and "all" is assumed if no capabilities are listed before
|
|
// an "=".
|
|
//
|
|
// The ops values, "=", "+" and "-" imply "reset and raise", "raise"
|
|
// and "lower" respectively. The "e", "i" and "p" characters
|
|
// correspond to the capabilities of the corresponding Flag: "e"
|
|
// (Effective); "i" (Inheritable); "p" (Permitted).
|
|
//
|
|
// This syntax is overspecified and there are many ways of building
|
|
// the same final Set state. Any sequence that includes a '=' resets
|
|
// the accumulated state of all Flags ignoring earlier sequences. On
|
|
// each of the following lines we give three or more examples of ways
|
|
// to specify a common Set. The last entry on each line is the one
|
|
// generated by (*cap.Set).String() from that Set.
|
|
//
|
|
// "=p all+ei" "all=pie" "=pi all+e" "=eip"
|
|
//
|
|
// "cap_setuid=p cap_chown=i" "cap_chown=ip-p" "cap_chown=i"
|
|
//
|
|
// "cap_chown=-p" "all=" "cap_setuid=pie-pie" "="
|
|
//
|
|
// Note: FromText() is tested at release time to completely match the
|
|
// import ability of the libcap:cap_from_text() function.
|
|
func FromText(text string) (*Set, error) {
|
|
c := NewSet()
|
|
scanner := bufio.NewScanner(strings.NewReader(text))
|
|
scanner.Split(bufio.ScanWords)
|
|
chunks := 0
|
|
for scanner.Scan() {
|
|
chunks++
|
|
|
|
// Parsing for xxx([-+=][eip]+)+
|
|
t := scanner.Text()
|
|
i := strings.IndexAny(t, "=+-")
|
|
if i < 0 {
|
|
return nil, ErrBadText
|
|
}
|
|
var vs []Value
|
|
sep := t[i]
|
|
if vals := t[:i]; vals == "all" {
|
|
for v := Value(0); v < Value(maxValues); v++ {
|
|
vs = append(vs, v)
|
|
}
|
|
} else if vals != "" {
|
|
for _, name := range strings.Split(vals, ",") {
|
|
v, err := FromName(name)
|
|
if err != nil {
|
|
return nil, ErrBadText
|
|
}
|
|
vs = append(vs, v)
|
|
}
|
|
} else if sep != '=' {
|
|
if vals == "" {
|
|
// Only "=" supports ""=="all".
|
|
return nil, ErrBadText
|
|
}
|
|
} else if j := i + 1; j+1 < len(t) {
|
|
switch t[j] {
|
|
case '+':
|
|
sep = 'P'
|
|
i++
|
|
case '-':
|
|
sep = 'M'
|
|
i++
|
|
}
|
|
}
|
|
i++
|
|
|
|
// There are 5 ways to set: =, =+, =-, +, -. We call
|
|
// the 2nd and 3rd of these 'P' and 'M'.
|
|
|
|
for {
|
|
// read [eip]+ setting flags.
|
|
var fE, fP, fI bool
|
|
for ok := true; ok && i < len(t); i++ {
|
|
switch t[i] {
|
|
case 'e':
|
|
fE = true
|
|
case 'i':
|
|
fI = true
|
|
case 'p':
|
|
fP = true
|
|
default:
|
|
ok = false
|
|
}
|
|
if !ok {
|
|
break
|
|
}
|
|
}
|
|
|
|
if !(fE || fI || fP) {
|
|
if sep != '=' {
|
|
return nil, ErrBadText
|
|
}
|
|
}
|
|
|
|
switch sep {
|
|
case '=', 'P', 'M', '+':
|
|
if sep != '+' {
|
|
c.Clear()
|
|
if sep == 'M' {
|
|
break
|
|
}
|
|
}
|
|
if keep := len(vs) == 0; keep {
|
|
if sep != '=' {
|
|
return nil, ErrBadText
|
|
}
|
|
c.forceFlag(Effective, fE)
|
|
c.forceFlag(Permitted, fP)
|
|
c.forceFlag(Inheritable, fI)
|
|
break
|
|
}
|
|
// =, + and P for specific values are left.
|
|
if fE {
|
|
c.SetFlag(Effective, true, vs...)
|
|
}
|
|
if fP {
|
|
c.SetFlag(Permitted, true, vs...)
|
|
}
|
|
if fI {
|
|
c.SetFlag(Inheritable, true, vs...)
|
|
}
|
|
case '-':
|
|
if fE {
|
|
c.SetFlag(Effective, false, vs...)
|
|
}
|
|
if fP {
|
|
c.SetFlag(Permitted, false, vs...)
|
|
}
|
|
if fI {
|
|
c.SetFlag(Inheritable, false, vs...)
|
|
}
|
|
}
|
|
|
|
if i == len(t) {
|
|
break
|
|
}
|
|
|
|
switch t[i] {
|
|
case '+', '-':
|
|
sep = t[i]
|
|
i++
|
|
default:
|
|
return nil, ErrBadText
|
|
}
|
|
}
|
|
}
|
|
if chunks == 0 {
|
|
return nil, ErrBadText
|
|
}
|
|
return c, nil
|
|
}
|