CVE-2022-27649: Incorrect Default Permissions (CWE-276)
value: A flaw was found in Podman, where containers were started incorrectly with non-empty default permissions. A vulnerability was found in Moby (Docker Engine), where containers were started incorrectly with non-empty inheritable Linux process capabilities. This flaw allows an attacker with access to programs with inheritable file capabilities to elevate those capabilities to the permitted set when execve(2) runs.
CWE description: During installation, installed file permissions are set to allow anyone to modify those files.
CVSS v3 base score: 7.5
Published: 2022-04-04T20:15Z
Repository: https://github.com/containers/podman
Language: Go
Files changed: libpod/oci_conmon_exec_linux.go, pkg/specgen/generate/security.go, test/e2e/run_test.go
Commit date: 2022-03-23 09:05:18 +0100
Commit message: do not set the inheritable capabilities

The kernel never sets the inheritable capabilities for a process, they
are only set by userspace.  Emulate the same behavior.

Closes: CVE-2022-27649

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>

Diff: diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go
index aa970bbde..65123b37e 100644
--- a/libpod/oci_conmon_exec_linux.go
+++ b/libpod/oci_conmon_exec_linux.go
@@ -756,15 +756,18 @@ func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessio
 	if options.Privileged {
 		pspec.Capabilities.Bounding = allCaps
 	} else {
 		pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding
 	}
+
+	// Always unset the inheritable capabilities similarly to what the Linux kernel does
+	// They are used only when using capabilities with uid != 0.
+	pspec.Capabilities.Inheritable = []string{}
+
 	if execUser.Uid == 0 {
 		pspec.Capabilities.Effective = pspec.Capabilities.Bounding
-		pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
 		pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
-		pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
 	} else {
 		if user == c.config.User {
 			pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective
 			pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective
 			pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective
diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go
index 9c6709905..988c29832 100644
--- a/pkg/specgen/generate/security.go
+++ b/pkg/specgen/generate/security.go
@@ -144,18 +144,21 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
 		}
 	}
 
 	configSpec := g.Config
 	configSpec.Process.Capabilities.Ambient = []string{}
+
+	// Always unset the inheritable capabilities similarly to what the Linux kernel does
+	// They are used only when using capabilities with uid != 0.
+	configSpec.Process.Capabilities.Inheritable = []string{}
 	configSpec.Process.Capabilities.Bounding = caplist
 
 	user := strings.Split(s.User, ":")[0]
 
 	if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" {
 		configSpec.Process.Capabilities.Effective = caplist
 		configSpec.Process.Capabilities.Permitted = caplist
-		configSpec.Process.Capabilities.Inheritable = caplist
 	} else {
 		mergedCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil)
 		if err != nil {
 			return errors.Wrapf(err, "capabilities requested by user are not valid: %q", strings.Join(s.CapAdd, ","))
 		}
@@ -173,16 +176,16 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
 				userCaps = append(userCaps, c)
 			}
 		}
 		configSpec.Process.Capabilities.Effective = userCaps
 		configSpec.Process.Capabilities.Permitted = userCaps
-		configSpec.Process.Capabilities.Inheritable = userCaps
 
 		// Ambient capabilities were added to Linux 4.3.  Set ambient
 		// capabilities only when the kernel supports them.
 		if supportAmbientCapabilities() {
 			configSpec.Process.Capabilities.Ambient = userCaps
+			configSpec.Process.Capabilities.Inheritable = userCaps
 		}
 	}
 
 	g.SetProcessNoNewPrivileges(s.NoNewPrivileges)
 
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 91a2eddad..f4a6e5733 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -496,11 +496,11 @@ var _ = Describe("Podman run", func() {
 		Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
 
 		session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapInh", "/proc/self/status"})
 		session.WaitWithDefaultTimeout()
 		Expect(session).Should(Exit(0))
-		Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
+		Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
 
 		session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "grep", "CapBnd", "/proc/self/status"})
 		session.WaitWithDefaultTimeout()
 		Expect(session).Should(Exit(0))
 		Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
@@ -531,11 +531,11 @@ var _ = Describe("Podman run", func() {
 		Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
 
 		session = podmanTest.Podman([]string{"run", "--user=0:0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"})
 		session.WaitWithDefaultTimeout()
 		Expect(session).Should(Exit(0))
-		Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
+		Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
 
 		if os.Geteuid() > 0 {
 			if os.Getenv("SKIP_USERNS") != "" {
 				Skip("Skip userns tests.")
 			}
@@ -548,11 +548,11 @@ var _ = Describe("Podman run", func() {
 			Expect(session.OutputToString()).To(ContainSubstring("0000000000000002"))
 
 			session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--privileged", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"})
 			session.WaitWithDefaultTimeout()
 			Expect(session).Should(Exit(0))
-			Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
+			Expect(session.OutputToString()).To(ContainSubstring("0000000000000002"))
 
 			session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"})
 			session.WaitWithDefaultTimeout()
 			Expect(session).Should(Exit(0))
 			Expect(session.OutputToString()).To(ContainSubstring("0000000000000002"))

Vulnerable code: 		pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
		pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
		configSpec.Process.Capabilities.Inheritable = caplist
		configSpec.Process.Capabilities.Inheritable = userCaps
		Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
		Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
			Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
Fixed code: 
	// Always unset the inheritable capabilities similarly to what the Linux kernel does
	// They are used only when using capabilities with uid != 0.
	pspec.Capabilities.Inheritable = []string{}


	// Always unset the inheritable capabilities similarly to what the Linux kernel does
	// They are used only when using capabilities with uid != 0.
	configSpec.Process.Capabilities.Inheritable = []string{}
			configSpec.Process.Capabilities.Inheritable = userCaps
		Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
		Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
			Expect(session.OutputToString()).To(ContainSubstring("0000000000000002"))