Skip to content

Commit

Permalink
capability: add {Get,Set,Reset}Ambient API
Browse files Browse the repository at this point in the history
The API is the same as in kernel.org/pub/linux/libs/security/libcap/cap
package, although the implementation is a bit simpler (here we only set
capabilities for the calling thread).

Co-authored-by: lifubang <[email protected]>
Signed-off-by: Kir Kolyshkin <[email protected]>
  • Loading branch information
kolyshkin and lifubang committed Oct 31, 2024
1 parent a902dc6 commit 9463687
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 2 deletions.
21 changes: 21 additions & 0 deletions capability/capability.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,24 @@ func NewFile2(path string) (Capabilities, error) {
func LastCap() (Cap, error) {
return lastCap()
}

// GetAmbient determines if a specific ambient capability is raised in the
// calling thread.
func GetAmbient(c Cap) (bool, error) {
return getAmbient(c)
}

// SetAmbient raises or lowers specified ambient capabilities for the calling
// thread. To complete successfully, the prevailing effective capability set
// must have a raised CAP_SETPCAP. Further, to raise a specific ambient
// capability the inheritable and permitted sets of the calling thread must
// already contain the specified capability.
func SetAmbient(raise bool, caps ...Cap) error {
return setAmbient(raise, caps...)
}

// ResetAmbient resets all of the ambient capabilities for the calling thread
// to their lowered value.
func ResetAmbient() error {
return resetAmbient()
}
30 changes: 28 additions & 2 deletions capability/capability_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ func (c *capsV3) Apply(kind CapType) error {

if kind&AMBS == AMBS {
// Ignore EINVAL as not supported on kernels before 4.3
err = ignoreEINVAL(prctl(pr_CAP_AMBIENT, pr_CAP_AMBIENT_CLEAR_ALL, 0))
err = ignoreEINVAL(resetAmbient())
if err != nil {
return err
}
Expand All @@ -380,7 +380,7 @@ func (c *capsV3) Apply(kind CapType) error {
continue
}
// Ignore EINVAL as not supported on kernels before 4.3
err = ignoreEINVAL(prctl(pr_CAP_AMBIENT, pr_CAP_AMBIENT_RAISE, uintptr(i)))
err = ignoreEINVAL(setAmbient(true, i))
if err != nil {
return err
}
Expand All @@ -390,6 +390,32 @@ func (c *capsV3) Apply(kind CapType) error {
return nil
}

func getAmbient(c Cap) (bool, error) {
res, err := prctlRetInt(pr_CAP_AMBIENT, pr_CAP_AMBIENT_IS_SET, uintptr(c))
if err != nil {
return false, err
}
return res > 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 newFile(path string) (c Capabilities, err error) {
c = &capsFile{path: path}
return
Expand Down
12 changes: 12 additions & 0 deletions capability/capability_noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,15 @@ func newFile(_ string) (Capabilities, error) {
func lastCap() (Cap, error) {
return -1, errNotSup
}

func getAmbient(_ Cap) (bool, error) {
return false, errNotSup
}

func setAmbient(_ bool, _ ...Cap) error {
return errNotSup
}

func resetAmbient() error {
return errNotSup
}
85 changes: 85 additions & 0 deletions capability/capability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,88 @@ func TestApplyOtherProcess(t *testing.T) {
}
}
}

func TestGetSetResetAmbient(t *testing.T) {
if runtime.GOOS != "linux" {
_, err := GetAmbient(Cap(0))
if err == nil {
t.Error(runtime.GOOS, ": want error, got nil")
}
err = SetAmbient(false, Cap(0))
if err == nil {
t.Error(runtime.GOOS, ": want error, got nil")
}
err = ResetAmbient()
if err == nil {
t.Error(runtime.GOOS, ": want error, got nil")
}
return
}

requirePCapSet(t)
out := testInChild(t, childGetSetResetAmbient)
t.Logf("output from child:\n%s", out)
}

func childGetSetResetAmbient() {
runtime.LockOSThread()
log.SetFlags(log.Lshortfile)

pid, err := NewPid2(0)
if err != nil {
log.Fatal(err)
}

list := []Cap{CAP_KILL, CAP_CHOWN, CAP_SYS_CHROOT}
pid.Set(CAPS, list...)
if err = pid.Apply(CAPS); err != nil {
log.Fatal(err)
}

// Set ambient caps from list.
if err = SetAmbient(true, list...); err != nil {
log.Fatal(err)
}

// Check if they were set as expected.
for _, cap := range list {
want := true
got, err := GetAmbient(cap)
if err != nil {
log.Fatalf("GetAmbient(%s): want nil, got error %v", cap, err)
} else if want != got {
log.Fatalf("Get(AMBIENT, %s): want %v, got %v", cap, want, got)
}
}

// Lower one ambient cap.
const unsetIdx = 1
if err = SetAmbient(false, list[unsetIdx]); err != nil {
log.Fatal(err)
}
// Check they are set as expected.
for i, cap := range list {
want := i != unsetIdx
got, err := GetAmbient(cap)
if err != nil {
log.Fatalf("GetAmbient(%s): want nil, got error %v", cap, err)
} else if want != got {
log.Fatalf("Get(AMBIENT, %s): want %v, got %v", cap, want, got)
}
}

// Lower all ambient caps.
if err = ResetAmbient(); err != nil {
log.Fatal(err)
}
for _, cap := range list {
want := false
got, err := GetAmbient(cap)
if err != nil {
log.Fatalf("GetAmbient(%s): want nil, got error %v", cap, err)
} else if want != got {
log.Fatalf("Get(AMBIENT, %s): want %v, got %v", cap, want, got)
}
}
os.Exit(0)
}
8 changes: 8 additions & 0 deletions capability/syscall_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ func prctl(option int, arg2, arg3 uintptr) (err error) {
return
}

func prctlRetInt(option int, arg2, arg3 uintptr) (int, error) {
ret, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, uintptr(option), arg2, arg3)
if err != 0 {
return 0, err
}
return int(ret), nil
}

const (
vfsXattrName = "security.capability"

Expand Down

0 comments on commit 9463687

Please sign in to comment.