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]) }