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.
293 lines
6.9 KiB
293 lines
6.9 KiB
package cap
|
|
|
|
import (
|
|
"errors"
|
|
"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 (
|
|
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 vector of the process returning.
|
|
func (s Secbits) Set() error {
|
|
scwMu.Lock()
|
|
defer scwMu.Unlock()
|
|
return multisc.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
|
|
)
|
|
|
|
// 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&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.Compare(e)
|
|
|
|
if Differs(cf, Inheritable) {
|
|
return ModePure1E
|
|
}
|
|
if Differs(cf, Permitted) || Differs(cf, 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 == 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 {
|
|
scwMu.Lock()
|
|
defer scwMu.Unlock()
|
|
return multisc.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"
|
|
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 vector 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
|
|
// performs a change of UID cap.SETUID is available, and the action
|
|
// does not alter the Permitted Flag of the process' Set.
|
|
func SetUID(uid int) error {
|
|
scwMu.Lock()
|
|
defer scwMu.Unlock()
|
|
return multisc.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 {
|
|
scwMu.Lock()
|
|
defer scwMu.Unlock()
|
|
return multisc.setGroups(gid, suppl)
|
|
}
|