blob: a31ac0940c00445c9a62002aff61986b6b68a629 [file] [log] [blame]
package cap
import (
"errors"
"fmt"
"syscall"
"unsafe"
)
// This file contains convenience functions for libcap, to help
// users do the right thing with respect to capabilities for
// common actions.
// Secbits capture the prctl settable secure-bits of a process.
type Secbits uint
// SecbitNoRoot etc are the bitmasks associated with the supported
// Secbit masks. Source: uapi/linux/securebits.h
const (
SecbitNoRoot Secbits = 1 << iota
SecbitNoRootLocked
SecbitNoSetUIDFixup
SecbitNoSetUIDFixupLocked
SecbitKeepCaps
SecbitKeepCapsLocked
SecbitNoCapAmbientRaise
SecbitNoCapAmbientRaiseLocked
)
const (
securedBasicBits = SecbitNoRoot | SecbitNoRootLocked | SecbitNoSetUIDFixup | SecbitNoSetUIDFixupLocked | SecbitKeepCapsLocked
securedAmbientBits = securedBasicBits | SecbitNoCapAmbientRaise | SecbitNoCapAmbientRaiseLocked
)
// defines from uapi/linux/prctl.h
const (
prGetKeepCaps = 7
prSetKeepCaps = 8
prGetSecureBits = 27
prSetSecureBits = 28
prSetNoNewPrivs = 38
)
// GetSecbits returns the current setting of the process' Secbits.
func GetSecbits() Secbits {
v, err := multisc.prctlrcall(prGetSecureBits, 0, 0)
if err != nil {
panic(err)
}
return Secbits(v)
}
func (sc *syscaller) setSecbits(s Secbits) error {
_, err := sc.prctlwcall(prSetSecureBits, uintptr(s), 0)
return err
}
// Set attempts to force the process Secbits to a value. This function
// will raise cap.SETPCAP in order to achieve this operation, and will
// completely lower the Effective Flag of the process upon returning.
func (s Secbits) Set() error {
state, sc := scwStateSC()
defer scwSetState(launchBlocked, state, -1)
return sc.setSecbits(s)
}
// Mode summarizes a complicated secure-bits and capability mode in a
// libcap preferred way.
type Mode uint
// ModeUncertain etc are how libcap summarizes security modes
// involving capabilities and secure-bits.
const (
ModeUncertain Mode = iota
ModeNoPriv
ModePure1EInit
ModePure1E
ModeHybrid
)
// GetMode assesses the current process state and summarizes it as
// a Mode. This function always succeeds. Unfamiliar modes are
// declared ModeUncertain.
func GetMode() Mode {
b := GetSecbits()
if b == 0 {
return ModeHybrid
}
if b&securedBasicBits != securedBasicBits {
return ModeUncertain
}
for c := Value(0); ; c++ {
v, err := GetAmbient(c)
if err != nil {
if c != 0 && b != securedAmbientBits {
return ModeUncertain
}
break
}
if v {
return ModeUncertain
}
}
w := GetProc()
e := NewSet()
cf, _ := w.Cf(e)
if cf.Has(Inheritable) {
return ModePure1E
}
if cf.Has(Permitted) || cf.Has(Effective) {
return ModePure1EInit
}
for c := Value(0); ; c++ {
v, err := GetBound(c)
if err != nil {
break
}
if v {
return ModePure1EInit
}
}
return ModeNoPriv
}
// ErrBadMode is the error returned when an attempt is made to set an
// unrecognized libcap security mode.
var ErrBadMode = errors.New("unsupported mode")
func (sc *syscaller) setMode(m Mode) error {
w := GetProc()
defer func() {
w.ClearFlag(Effective)
sc.setProc(w)
}()
if err := w.SetFlag(Effective, true, SETPCAP); err != nil {
return err
}
if err := sc.setProc(w); err != nil {
return err
}
if m == ModeHybrid {
return sc.setSecbits(0)
}
if m == ModeNoPriv || m == ModePure1EInit {
w.ClearFlag(Inheritable)
} else if m != ModePure1E {
return ErrBadMode
}
sb := securedAmbientBits
if _, err := GetAmbient(0); err != nil {
sb = securedBasicBits
} else if err := sc.resetAmbient(); err != nil {
return err
}
if err := sc.setSecbits(sb); err != nil {
return err
}
if m != ModeNoPriv {
return nil
}
for c := Value(0); sc.dropBound(c) == nil; c++ {
}
w.ClearFlag(Permitted)
// For good measure.
sc.prctlwcall6(prSetNoNewPrivs, 1, 0, 0, 0, 0)
return nil
}
// Set attempts to enter the specified mode. An attempt is made to
// enter the mode, so if you prefer this operation to be a no-op if
// entering the same mode, call only if CurrentMode() disagrees with
// the desired mode.
//
// This function will raise cap.SETPCAP in order to achieve this
// operation, and will completely lower the Effective Flag of the
// process' Set before returning. This function may fail for lack of
// permission or because (some of) the Secbits are already locked for
// the current process.
func (m Mode) Set() error {
state, sc := scwStateSC()
defer scwSetState(launchBlocked, state, -1)
return sc.setMode(m)
}
// String returns the libcap conventional string for this mode.
func (m Mode) String() string {
switch m {
case ModeUncertain:
return "UNCERTAIN"
case ModeNoPriv:
return "NOPRIV"
case ModePure1EInit:
return "PURE1E_INIT"
case ModePure1E:
return "PURE1E"
case ModeHybrid:
return "HYBRID"
default:
return "UNKNOWN"
}
}
func (sc *syscaller) setUID(uid int) error {
w := GetProc()
defer func() {
w.ClearFlag(Effective)
sc.setProc(w)
}()
if err := w.SetFlag(Effective, true, SETUID); err != nil {
return err
}
// these may or may not work depending on whether or not they
// are locked. We try them just in case.
sc.prctlwcall(prSetKeepCaps, 1, 0)
defer sc.prctlwcall(prSetKeepCaps, 0, 0)
if err := sc.setProc(w); err != nil {
return err
}
if _, _, err := sc.w3(syscall.SYS_SETUID, uintptr(uid), 0, 0); err != 0 {
return err
}
return nil
}
// SetUID is a convenience function for robustly setting the UID and
// all other variants of UID (EUID etc) to the specified value without
// dropping the privilege of the current process. This function will
// raise cap.SETUID in order to achieve this operation, and will
// completely lower the Effective Flag of the process before
// returning. Unlike the traditional method of dropping privilege when
// changing from [E]UID=0 to some other UID, this function only can
// perform any change of UID if cap.SETUID is available, and this
// operation will not alter the Permitted Flag of the process' Set.
func SetUID(uid int) error {
state, sc := scwStateSC()
defer scwSetState(launchBlocked, state, -1)
return sc.setUID(uid)
}
//go:uintptrescapes
func (sc *syscaller) setGroups(gid int, suppl []int) error {
w := GetProc()
defer func() {
w.ClearFlag(Effective)
sc.setProc(w)
}()
if err := w.SetFlag(Effective, true, SETGID); err != nil {
return err
}
if err := sc.setProc(w); err != nil {
return err
}
if _, _, err := sc.w3(syscall.SYS_SETGID, uintptr(gid), 0, 0); err != 0 {
return err
}
if len(suppl) == 0 {
if _, _, err := sc.w3(sysSetGroupsVariant, 0, 0, 0); err != 0 {
return err
}
return nil
}
// On linux gid values are 32-bits.
gs := make([]uint32, len(suppl))
for i, g := range suppl {
gs[i] = uint32(g)
}
if _, _, err := sc.w3(sysSetGroupsVariant, uintptr(len(suppl)), uintptr(unsafe.Pointer(&gs[0])), 0); err != 0 {
return err
}
return nil
}
// SetGroups is a convenience function for robustly setting the GID
// and all other variants of GID (EGID etc) to the specified value, as
// well as setting all of the supplementary groups. This function will
// raise cap.SETGID in order to achieve this operation, and will
// completely lower the Effective Flag of the process Set before
// returning.
func SetGroups(gid int, suppl ...int) error {
state, sc := scwStateSC()
defer scwSetState(launchBlocked, state, -1)
return sc.setGroups(gid, suppl)
}
//go:uintptrescapes
// Prctlw is a convenience function for performing a syscall.Prctl()
// call that executes on all the threads of the process. It is called
// Prctlw because it is only appropriate to call this function when it
// is writing thread state that the caller wants to set on all OS
// threads of the process to observe POSIX semantics when Linux
// doesn't natively honor them. (Check prctl documentation for when it
// is appropriate to use this vs. a normal syscall.Prctl() call.)
func Prctlw(prVal uintptr, args ...uintptr) (int, error) {
if n := len(args); n > 5 {
return -1, fmt.Errorf("prctl supports up to 5 arguments (not %d)", n)
}
state, sc := scwStateSC()
defer scwSetState(launchBlocked, state, -1)
as := make([]uintptr, 5)
copy(as, args)
return sc.prctlwcall6(prVal, as[0], as[1], as[2], as[3], as[4])
}
//go:uintptrescapes
// Prctl is a convenience function that performs a syscall.Prctl()
// that either reads state using a single OS thread, or performs a
// Prctl that is treated as a process wide setting. It is provided for
// symmetry reasons, but is equivalent to simply calling the
// corresponding syscall function.
func Prctl(prVal uintptr, args ...uintptr) (int, error) {
if n := len(args); n > 5 {
return -1, fmt.Errorf("prctl supports up to 5 arguments (not %d)", n)
}
as := make([]uintptr, 5)
copy(as, args)
return singlesc.prctlrcall6(prVal, as[0], as[1], as[2], as[3], as[4])
}