4c4c1b
From c140ecdc9b416ab4efd4d21d14acd63b6adbdd42 Mon Sep 17 00:00:00 2001
4c4c1b
From: Matthew Heon <matthew.heon@pm.me>
4c4c1b
Date: Mon, 10 Feb 2020 13:37:38 -0500
4c4c1b
Subject: [PATCH] Do not copy up when volume is not empty
4c4c1b
4c4c1b
When Docker performs a copy up, it first verifies that the volume
4c4c1b
being copied into is empty; thus, for volumes that have been
4c4c1b
modified elsewhere (e.g. manually copying into then), the copy up
4c4c1b
will not be performed at all. Duplicate this behavior in Podman
4c4c1b
by checking if the volume is empty before copying.
4c4c1b
4c4c1b
Furthermore, move setting copyup to false further up. This will
4c4c1b
prevent a potential race where copy up could happen more than
4c4c1b
once if Podman was killed after some files had been copied but
4c4c1b
before the DB was updated.
4c4c1b
4c4c1b
This resolves CVE-2020-1726.
4c4c1b
4c4c1b
Signed-off-by: Matthew Heon <matthew.heon@pm.me>
4c4c1b
---
4c4c1b
 libpod/container_internal.go | 28 ++++++++++++++++++++++------
4c4c1b
 test/e2e/run_volume_test.go  | 24 ++++++++++++++++++++++++
4c4c1b
 2 files changed, 46 insertions(+), 6 deletions(-)
4c4c1b
4c4c1b
diff -up ./libpod-5cc92849f7fc9dd734ca2fd8f3ae8830b9a7eb26/libpod/container_internal.go.CVE-2020-1726 ./libpod-5cc92849f7fc9dd734ca2fd8f3ae8830b9a7eb26/libpod/container_internal.go
4c4c1b
--- libpod-5cc92849f7fc9dd734ca2fd8f3ae8830b9a7eb26/libpod/container_internal.go.CVE-2020-1726	2020-02-13 22:37:15.002058706 +0100
4c4c1b
+++ libpod-5cc92849f7fc9dd734ca2fd8f3ae8830b9a7eb26/libpod/container_internal.go	2020-02-13 22:37:15.005058742 +0100
4c4c1b
@@ -1358,18 +1358,34 @@ func (c *Container) mountNamedVolume(v *
4c4c1b
 	}
4c4c1b
 	if vol.state.NeedsCopyUp {
4c4c1b
 		logrus.Debugf("Copying up contents from container %s to volume %s", c.ID(), vol.Name())
4c4c1b
+
4c4c1b
+		// Set NeedsCopyUp to false immediately, so we don't try this
4c4c1b
+		// again when there are already files copied.
4c4c1b
+		vol.state.NeedsCopyUp = false
4c4c1b
+		if err := vol.save(); err != nil {
4c4c1b
+			return nil, err
4c4c1b
+		}
4c4c1b
+
4c4c1b
+		// If the volume is not empty, we should not copy up.
4c4c1b
+		volMount := vol.MountPoint()
4c4c1b
+		contents, err := ioutil.ReadDir(volMount)
4c4c1b
+		if err != nil {
4c4c1b
+			return nil, errors.Wrapf(err, "error listing contents of volume %s mountpoint when copying up from container %s", vol.Name(), c.ID())
4c4c1b
+		}
4c4c1b
+		if len(contents) > 0 {
4c4c1b
+			// The volume is not empty. It was likely modified
4c4c1b
+			// outside of Podman. For safety, let's not copy up into
4c4c1b
+			// it. Fixes CVE-2020-1726.
4c4c1b
+			return vol, nil
4c4c1b
+		}
4c4c1b
+
4c4c1b
 		srcDir, err := securejoin.SecureJoin(mountpoint, v.Dest)
4c4c1b
 		if err != nil {
4c4c1b
 			return nil, errors.Wrapf(err, "error calculating destination path to copy up container %s volume %s", c.ID(), vol.Name())
4c4c1b
 		}
4c4c1b
-		if err := c.copyWithTarFromImage(srcDir, vol.MountPoint()); err != nil && !os.IsNotExist(err) {
4c4c1b
+		if err := c.copyWithTarFromImage(srcDir, volMount); err != nil && !os.IsNotExist(err) {
4c4c1b
 			return nil, errors.Wrapf(err, "error copying content from container %s into volume %s", c.ID(), vol.Name())
4c4c1b
 		}
4c4c1b
-
4c4c1b
-		vol.state.NeedsCopyUp = false
4c4c1b
-		if err := vol.save(); err != nil {
4c4c1b
-			return nil, err
4c4c1b
-		}
4c4c1b
 	}
4c4c1b
 	return vol, nil
4c4c1b
 }
4c4c1b
diff -up ./libpod-5cc92849f7fc9dd734ca2fd8f3ae8830b9a7eb26/test/e2e/run_volume_test.go.CVE-2020-1726 ./libpod-5cc92849f7fc9dd734ca2fd8f3ae8830b9a7eb26/test/e2e/run_volume_test.go
4c4c1b
--- libpod-5cc92849f7fc9dd734ca2fd8f3ae8830b9a7eb26/test/e2e/run_volume_test.go.CVE-2020-1726	2020-02-13 22:37:15.030059039 +0100
4c4c1b
+++ libpod-5cc92849f7fc9dd734ca2fd8f3ae8830b9a7eb26/test/e2e/run_volume_test.go	2020-02-13 22:37:15.033059075 +0100
4c4c1b
@@ -375,4 +375,28 @@ var _ = Describe("Podman run with volume
4c4c1b
 		volMount.WaitWithDefaultTimeout()
4c4c1b
 		Expect(volMount.ExitCode()).To(Not(Equal(0)))
4c4c1b
 	})
4c4c1b
+
4c4c1b
+	It("Podman fix for CVE-2020-1726", func() {
4c4c1b
+		volName := "testVol"
4c4c1b
+		volCreate := podmanTest.Podman([]string{"volume", "create", volName})
4c4c1b
+		volCreate.WaitWithDefaultTimeout()
4c4c1b
+		Expect(volCreate.ExitCode()).To(Equal(0))
4c4c1b
+
4c4c1b
+		volPath := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{.Mountpoint}}", volName})
4c4c1b
+		volPath.WaitWithDefaultTimeout()
4c4c1b
+		Expect(volPath.ExitCode()).To(Equal(0))
4c4c1b
+		path := volPath.OutputToString()
4c4c1b
+
4c4c1b
+		fileName := "thisIsATestFile"
4c4c1b
+		file, err := os.Create(filepath.Join(path, fileName))
4c4c1b
+		Expect(err).To(BeNil())
4c4c1b
+		defer file.Close()
4c4c1b
+
4c4c1b
+		runLs := podmanTest.Podman([]string{"run", "-t", "-i", "--rm", "-v", fmt.Sprintf("%v:/etc/ssl", volName), ALPINE, "ls", "-1", "/etc/ssl"})
4c4c1b
+		runLs.WaitWithDefaultTimeout()
4c4c1b
+		Expect(runLs.ExitCode()).To(Equal(0))
4c4c1b
+		outputArr := runLs.OutputToStringArray()
4c4c1b
+		Expect(len(outputArr)).To(Equal(1))
4c4c1b
+		Expect(strings.Contains(outputArr[0], fileName)).To(BeTrue())
4c4c1b
+	})
4c4c1b
 })