From 8e47badf696e2f483f01e53c290e476d66352bfd Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 22 Oct 2024 15:13:47 -0700 Subject: [PATCH] capability: Apply: deny for another process Current version of capsV3.Apply has a major problem: if you do something like this: c, err := capability.NewPid(pid) ... c.Set(capability.AMBIENT, capability.CAP_CHOWN) c.Apply(capability.AMBIENT) then the ambient capability will be applied to the current process, rather than the process identified by pid. Same issue for BOUNDS. For CAPS the situation is slightly different: capset(2) man page says: > EPERM The caller attempted to use capset() to modify the capabilities > of a thread other than itself, but lacked sufficient privilege. > For kernels supporting VFS capabilities, this is never permitted. Here "kernels supporting VFS capabilities" means most kernels >= v2.6.24, and all kernels >= v2.6.33. Since Go 1.18+ only supports Linux >= v2.6.32, this pretty much means "all kernels". Meaning, Apply(CAPS) with non-zero pid will try capset and return EPERM. Let's return an error early if pid is set in Apply, and add a test case. Signed-off-by: Kir Kolyshkin --- capability/capability_linux.go | 3 +++ capability/capability_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/capability/capability_linux.go b/capability/capability_linux.go index 0732195..d79ceee 100644 --- a/capability/capability_linux.go +++ b/capability/capability_linux.go @@ -328,6 +328,9 @@ func (c *capsV3) Load() (err error) { } func (c *capsV3) Apply(kind CapType) (err error) { + if c.hdr.pid != 0 { + return errors.New("unable to modify capabilities of another process") + } last, err := LastCap() if err != nil { return err diff --git a/capability/capability_test.go b/capability/capability_test.go index 2b901ad..4e99185 100644 --- a/capability/capability_test.go +++ b/capability/capability_test.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "runtime" + "strings" "testing" . "github.com/moby/sys/capability" @@ -193,3 +194,36 @@ func childAmbientCapSet() { } os.Exit(0) } + +// https://github.com/moby/sys/issues/168 +func TestApplyOtherProcess(t *testing.T) { + if runtime.GOOS != "linux" { + return + } + requirePCapSet(t) + + cmd := exec.Command("sleep", "infinity") + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + _ = cmd.Process.Kill() + _, _ = cmd.Process.Wait() + }) + + pid, err := NewPid2(cmd.Process.Pid) + if err != nil { + t.Fatal(err) + } + pid.Clear(CAPS | BOUNDS | AMBS) + + // See (*capsV3).Apply. + expErr := "unable to modify capabilities of another process" + + for _, arg := range []CapType{CAPS, BOUNDS, AMBS} { + err = pid.Apply(arg) + if !strings.Contains(err.Error(), expErr) { + t.Errorf("Apply(%q): want error to contain %q; got %v", arg, expErr, err) + } + } +}