// Copyright 2023 The Capability Authors. // Copyright 2013 Suryandaru Triandana // All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package capability import ( "bufio" "errors" "fmt" "io" "os" "strconv" "strings" "sync" "syscall" ) const ( linuxCapVer1 = 0x19980330 // No longer supported. linuxCapVer2 = 0x20071026 // No longer supported. linuxCapVer3 = 0x20080522 ) var lastCap = sync.OnceValues(func() (Cap, error) { f, err := os.Open("/proc/sys/kernel/cap_last_cap") if err != nil { return 0, err } buf := make([]byte, 11) l, err := f.Read(buf) f.Close() if err != nil { return 0, err } buf = buf[:l] last, err := strconv.Atoi(strings.TrimSpace(string(buf))) if err != nil { return 0, err } return Cap(last), nil }) func capUpperMask() uint32 { last, err := lastCap() if err != nil || last < 32 { return 0 } return (uint32(1) << (uint(last) - 31)) - 1 } func mkStringCap(c Capabilities, which CapType) (ret string) { last, err := lastCap() if err != nil { return "" } for i, first := Cap(0), true; i <= last; i++ { if !c.Get(which, i) { continue } if first { first = false } else { ret += ", " } ret += i.String() } return } func mkString(c Capabilities, max CapType) (ret string) { ret = "{" for i := CapType(1); i <= max; i <<= 1 { ret += " " + i.String() + "=\"" if c.Empty(i) { ret += "empty" } else if c.Full(i) { ret += "full" } else { ret += c.StringCap(i) } ret += "\"" } ret += " }" return } var capVersion = sync.OnceValues(func() (uint32, error) { var hdr capHeader err := capget(&hdr, nil) return hdr.version, err }) func newPid(pid int) (c Capabilities, retErr error) { ver, err := capVersion() if err != nil { retErr = fmt.Errorf("unable to get capability version from the kernel: %w", err) return } switch ver { case linuxCapVer1, linuxCapVer2: retErr = errors.New("old/unsupported capability version (kernel older than 2.6.26?)") default: // Either linuxCapVer3, or an unknown/future version (such as v4). // In the latter case, we fall back to v3 as the latest version known // to this package, as kernel should be backward-compatible to v3. p := new(capsV3) p.hdr.version = linuxCapVer3 p.hdr.pid = int32(pid) c = p } return } func ignoreEINVAL(err error) error { if errors.Is(err, syscall.EINVAL) { err = nil } return err } type capsV3 struct { hdr capHeader data [2]capData bounds [2]uint32 ambient [2]uint32 } func (c *capsV3) Get(which CapType, what Cap) bool { var i uint if what > 31 { i = uint(what) >> 5 what %= 32 } switch which { case EFFECTIVE: return (1< 31 { i = uint(what) >> 5 what %= 32 } if which&EFFECTIVE != 0 { c.data[i].effective |= 1 << uint(what) } if which&PERMITTED != 0 { c.data[i].permitted |= 1 << uint(what) } if which&INHERITABLE != 0 { c.data[i].inheritable |= 1 << uint(what) } if which&BOUNDING != 0 { c.bounds[i] |= 1 << uint(what) } if which&AMBIENT != 0 { c.ambient[i] |= 1 << uint(what) } } } func (c *capsV3) Unset(which CapType, caps ...Cap) { for _, what := range caps { var i uint if what > 31 { i = uint(what) >> 5 what %= 32 } if which&EFFECTIVE != 0 { c.data[i].effective &= ^(1 << uint(what)) } if which&PERMITTED != 0 { c.data[i].permitted &= ^(1 << uint(what)) } if which&INHERITABLE != 0 { c.data[i].inheritable &= ^(1 << uint(what)) } if which&BOUNDING != 0 { c.bounds[i] &= ^(1 << uint(what)) } if which&AMBIENT != 0 { c.ambient[i] &= ^(1 << uint(what)) } } } func (c *capsV3) Fill(kind CapType) { if kind&CAPS == CAPS { c.data[0].effective = 0xffffffff c.data[0].permitted = 0xffffffff c.data[0].inheritable = 0 c.data[1].effective = 0xffffffff c.data[1].permitted = 0xffffffff c.data[1].inheritable = 0 } if kind&BOUNDS == BOUNDS { c.bounds[0] = 0xffffffff c.bounds[1] = 0xffffffff } if kind&AMBS == AMBS { c.ambient[0] = 0xffffffff c.ambient[1] = 0xffffffff } } func (c *capsV3) Clear(kind CapType) { if kind&CAPS == CAPS { c.data[0].effective = 0 c.data[0].permitted = 0 c.data[0].inheritable = 0 c.data[1].effective = 0 c.data[1].permitted = 0 c.data[1].inheritable = 0 } if kind&BOUNDS == BOUNDS { c.bounds[0] = 0 c.bounds[1] = 0 } if kind&AMBS == AMBS { c.ambient[0] = 0 c.ambient[1] = 0 } } func (c *capsV3) StringCap(which CapType) (ret string) { return mkStringCap(c, which) } func (c *capsV3) String() (ret string) { return mkString(c, BOUNDING) } func (c *capsV3) Load() (err error) { err = capget(&c.hdr, &c.data[0]) if err != nil { return } path := "/proc/self/status" if c.hdr.pid != 0 { path = fmt.Sprintf("/proc/%d/status", c.hdr.pid) } f, err := os.Open(path) if err != nil { return } b := bufio.NewReader(f) for { line, e := b.ReadString('\n') if e != nil { if e != io.EOF { err = e } break } if val, ok := strings.CutPrefix(line, "CapBnd:\t"); ok { _, err = fmt.Sscanf(val, "%08x%08x", &c.bounds[1], &c.bounds[0]) if err != nil { break } continue } if val, ok := strings.CutPrefix(line, "CapAmb:\t"); ok { _, err = fmt.Sscanf(val, "%08x%08x", &c.ambient[1], &c.ambient[0]) if err != nil { break } continue } } f.Close() return } func (c *capsV3) Apply(kind CapType) error { if c.hdr.pid != 0 { return errors.New("unable to modify capabilities of another process") } last, err := LastCap() if err != nil { return err } if kind&BOUNDS == BOUNDS { var data [2]capData err = capget(&c.hdr, &data[0]) if err != nil { return err } if (1< 0, nil } func setAmbient(raise bool, caps ...Cap) error { op := pr_CAP_AMBIENT_RAISE if !raise { op = pr_CAP_AMBIENT_LOWER } for _, val := range caps { err := prctl(pr_CAP_AMBIENT, op, uintptr(val)) if err != nil { return err } } return nil } func resetAmbient() error { return prctl(pr_CAP_AMBIENT, pr_CAP_AMBIENT_CLEAR_ALL, 0) } func getBound(c Cap) (bool, error) { res, err := prctlRetInt(syscall.PR_CAPBSET_READ, uintptr(c), 0) if err != nil { return false, err } return res > 0, nil } func dropBound(caps ...Cap) error { for _, val := range caps { err := prctl(syscall.PR_CAPBSET_DROP, uintptr(val), 0) if err != nil { return err } } return nil } func newFile(path string) (c Capabilities, err error) { c = &capsFile{path: path} return } type capsFile struct { path string data vfscapData } func (c *capsFile) Get(which CapType, what Cap) bool { var i uint if what > 31 { if c.data.version == 1 { return false } i = uint(what) >> 5 what %= 32 } switch which { case EFFECTIVE: return (1< 31 { if c.data.version == 1 { continue } i = uint(what) >> 5 what %= 32 } if which&EFFECTIVE != 0 { c.data.effective[i] |= 1 << uint(what) } if which&PERMITTED != 0 { c.data.data[i].permitted |= 1 << uint(what) } if which&INHERITABLE != 0 { c.data.data[i].inheritable |= 1 << uint(what) } } } func (c *capsFile) Unset(which CapType, caps ...Cap) { for _, what := range caps { var i uint if what > 31 { if c.data.version == 1 { continue } i = uint(what) >> 5 what %= 32 } if which&EFFECTIVE != 0 { c.data.effective[i] &= ^(1 << uint(what)) } if which&PERMITTED != 0 { c.data.data[i].permitted &= ^(1 << uint(what)) } if which&INHERITABLE != 0 { c.data.data[i].inheritable &= ^(1 << uint(what)) } } } func (c *capsFile) Fill(kind CapType) { if kind&CAPS == CAPS { c.data.effective[0] = 0xffffffff c.data.data[0].permitted = 0xffffffff c.data.data[0].inheritable = 0 if c.data.version == 2 { c.data.effective[1] = 0xffffffff c.data.data[1].permitted = 0xffffffff c.data.data[1].inheritable = 0 } } } func (c *capsFile) Clear(kind CapType) { if kind&CAPS == CAPS { c.data.effective[0] = 0 c.data.data[0].permitted = 0 c.data.data[0].inheritable = 0 if c.data.version == 2 { c.data.effective[1] = 0 c.data.data[1].permitted = 0 c.data.data[1].inheritable = 0 } } } func (c *capsFile) StringCap(which CapType) (ret string) { return mkStringCap(c, which) } func (c *capsFile) String() (ret string) { return mkString(c, INHERITABLE) } func (c *capsFile) Load() (err error) { return getVfsCap(c.path, &c.data) } func (c *capsFile) Apply(kind CapType) (err error) { if kind&CAPS == CAPS { return setVfsCap(c.path, &c.data) } return }