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