blob: cf11a2dd3a80e5306bfc616957199043d9625584 [file] [log] [blame]
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
}