diff --git a/capability/capability.go b/capability/capability.go index 2c337d9..11e47be 100644 --- a/capability/capability.go +++ b/capability/capability.go @@ -163,3 +163,14 @@ func SetAmbient(raise bool, caps ...Cap) error { func ResetAmbient() error { return resetAmbient() } + +// GetBound determines if a specific bounding capability is raised in the +// calling thread. +func GetBound(c Cap) (bool, error) { + return getBound(c) +} + +// DropBound lowers the specified bounding set capability. +func DropBound(caps ...Cap) error { + return dropBound(caps...) +} diff --git a/capability/capability_linux.go b/capability/capability_linux.go index feba59b..7856e34 100644 --- a/capability/capability_linux.go +++ b/capability/capability_linux.go @@ -416,6 +416,24 @@ 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 diff --git a/capability/capability_noop.go b/capability/capability_noop.go index 02f2326..b766e44 100644 --- a/capability/capability_noop.go +++ b/capability/capability_noop.go @@ -36,3 +36,11 @@ func setAmbient(_ bool, _ ...Cap) error { func resetAmbient() error { return errNotSup } + +func getBound(_ Cap) (bool, error) { + return false, errNotSup +} + +func dropBound(_ ...Cap) error { + return errNotSup +} diff --git a/capability/capability_test.go b/capability/capability_test.go index 3e1054a..c9a24f9 100644 --- a/capability/capability_test.go +++ b/capability/capability_test.go @@ -313,3 +313,63 @@ func childGetSetResetAmbient() { } os.Exit(0) } + +func TestGetBound(t *testing.T) { + if runtime.GOOS != "linux" { + _, err := GetBound(Cap(0)) + if err == nil { + t.Error(runtime.GOOS, ": want error, got nil") + } + return + } + + last, err := LastCap() + if err != nil { + t.Fatalf("LastCap: %v", err) + } + for i := Cap(0); i < Cap(63); i++ { + wantErr := i > last + set, err := GetBound(i) + t.Logf("GetBound(%q): %v, %v", i, set, err) + if wantErr && err == nil { + t.Errorf("GetBound(%q): want err, got nil", i) + } else if !wantErr && err != nil { + t.Errorf("GetBound(%q): want nil, got error %v", i, err) + } + } +} + +func TestDropBound(t *testing.T) { + if runtime.GOOS != "linux" { + err := DropBound(Cap(0)) + if err == nil { + t.Error(runtime.GOOS, ": want error, got nil") + } + return + } + + requirePCapSet(t) + out := testInChild(t, childDropBound) + t.Logf("output from child:\n%s", out) +} + +func childDropBound() { + runtime.LockOSThread() + log.SetFlags(log.Lshortfile) + + for i := Cap(2); i < Cap(4); i++ { + err := DropBound(i) + if err != nil { + log.Fatalf("DropBound(%q): want nil, got error %v", i, err) + } + set, err := GetBound(i) + if err != nil { + log.Fatalf("GetBound(%q): want nil, got error %v", i, err) + } + if set { + log.Fatalf("GetBound(%q): want false, got true", i) + } + } + + os.Exit(0) +}