CVE-2018-10856: Incorrect Permission Assignment for Critical Resource (CWE-732)
value: It has been discovered that podman before version 0.6.1 does not drop capabilities when executing a container as a non-root user. This results in unnecessary privileges being granted to the container.
CWE description: The product specifies permissions for a security-critical resource in a way that allows that resource to be read or modified by unintended actors.
CVSS v3 base score: 8.8
Published: 2018-07-03T01:29Z
Repository: https://github.com/projectatomic/libpod
Language: Go
Files changed: pkg/spec/spec.go, test/e2e/run_test.go
Commit date: 2018-05-31 13:46:08 +0000
Commit message: Clear all caps, except the bounding set, when --user is specified.

Currently we are giving all caps to users when running with podman run --user,
They should get none by default.  If the command line includes --cap-add, then
we need to run with those capabilties.  Similarly we need to drop caps from
bounding set, if user specifies --cap-drop

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #851
Approved by: mheon

Diff: diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 959a2421..5260b9b1 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -386,21 +386,40 @@ func addRlimits(config *CreateConfig, g *generate.Generator) error {
 	}
 	return nil
 }
 
 func setupCapabilities(config *CreateConfig, configSpec *spec.Spec) error {
+	useNotRoot := func(user string) bool {
+		if user == "" || user == "root" || user == "0" {
+			return false
+		}
+		return true
+	}
+
 	var err error
 	var caplist []string
+	bounding := configSpec.Process.Capabilities.Bounding
+	if useNotRoot(config.User) {
+		configSpec.Process.Capabilities.Bounding = caplist
+	}
 	caplist, err = caps.TweakCapabilities(configSpec.Process.Capabilities.Bounding, config.CapAdd, config.CapDrop)
 	if err != nil {
 		return err
 	}
 
 	configSpec.Process.Capabilities.Bounding = caplist
 	configSpec.Process.Capabilities.Permitted = caplist
 	configSpec.Process.Capabilities.Inheritable = caplist
 	configSpec.Process.Capabilities.Effective = caplist
+	configSpec.Process.Capabilities.Ambient = caplist
+	if useNotRoot(config.User) {
+		caplist, err = caps.TweakCapabilities(bounding, config.CapAdd, config.CapDrop)
+		if err != nil {
+			return err
+		}
+	}
+	configSpec.Process.Capabilities.Bounding = caplist
 	return nil
 }
 
 func addDevice(g *generate.Generator, device string) error {
 	dev, err := devices.DeviceFromPath(device, "rwm")
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index cc849295..5f2bccda 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -3,10 +3,11 @@ package integration
 import (
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strings"
 
 	"github.com/mrunalp/fileutils"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 )
@@ -367,10 +368,18 @@ var _ = Describe("Podman run", func() {
 		session.WaitWithDefaultTimeout()
 		Expect(session.ExitCode()).To(Equal(0))
 		Expect(session.OutputToString()).To(Equal("uid=8(mail) gid=21(ftp)"))
 	})
 
+	It("podman run with user, verify caps dropped", func() {
+		session := podmanTest.Podman([]string{"run", "--rm", "--user=1234", ALPINE, "grep", "CapEff", "/proc/self/status"})
+		session.WaitWithDefaultTimeout()
+		Expect(session.ExitCode()).To(Equal(0))
+		capEff := strings.Split(session.OutputToString(), " ")
+		Expect("0000000000000000").To(Equal(capEff[1]))
+	})
+
 	It("podman run with attach stdin outputs container ID", func() {
 		session := podmanTest.Podman([]string{"run", "--attach", "stdin", ALPINE, "printenv"})
 		session.WaitWithDefaultTimeout()
 		Expect(session.ExitCode()).To(Equal(0))
 		ps := podmanTest.Podman([]string{"ps", "-aq", "--no-trunc"})

Fixed code: 	useNotRoot := func(user string) bool {
		if user == "" || user == "root" || user == "0" {
			return false
		}
		return true
	}

	bounding := configSpec.Process.Capabilities.Bounding
	if useNotRoot(config.User) {
		configSpec.Process.Capabilities.Bounding = caplist
	}
	configSpec.Process.Capabilities.Ambient = caplist
	if useNotRoot(config.User) {
		caplist, err = caps.TweakCapabilities(bounding, config.CapAdd, config.CapDrop)
		if err != nil {
			return err
		}
	}
	configSpec.Process.Capabilities.Bounding = caplist
	"strings"
	It("podman run with user, verify caps dropped", func() {
		session := podmanTest.Podman([]string{"run", "--rm", "--user=1234", ALPINE, "grep", "CapEff", "/proc/self/status"})
		session.WaitWithDefaultTimeout()
		Expect(session.ExitCode()).To(Equal(0))
		capEff := strings.Split(session.OutputToString(), " ")
		Expect("0000000000000000").To(Equal(capEff[1]))
	})