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