Browse code

Merge pull request #9467 from icecrime/9401-restrict_add_chown_scope

Reduce permissions changes scope after ADD/COPY

Michael Crosby authored on 2014/12/10 03:40:23
Showing 2 changed files
... ...
@@ -615,7 +615,7 @@ func (b *Builder) addContext(container *daemon.Container, orig, dest string, dec
615 615
 	}
616 616
 
617 617
 	if fi.IsDir() {
618
-		return copyAsDirectory(origPath, destPath, destExists)
618
+		return copyAsDirectory(origPath, destPath)
619 619
 	}
620 620
 
621 621
 	// If we are adding a remote file (or we've been told not to decompress), do not try to untar it
... ...
@@ -649,37 +649,31 @@ func (b *Builder) addContext(container *daemon.Container, orig, dest string, dec
649 649
 		resPath = path.Join(destPath, path.Base(origPath))
650 650
 	}
651 651
 
652
-	return fixPermissions(resPath, 0, 0)
652
+	return fixPermissions(origPath, resPath, 0, 0)
653 653
 }
654 654
 
655
-func copyAsDirectory(source, destination string, destinationExists bool) error {
655
+func copyAsDirectory(source, destination string) error {
656 656
 	if err := chrootarchive.CopyWithTar(source, destination); err != nil {
657 657
 		return err
658 658
 	}
659
-
660
-	if destinationExists {
661
-		files, err := ioutil.ReadDir(source)
662
-		if err != nil {
663
-			return err
664
-		}
665
-
666
-		for _, file := range files {
667
-			if err := fixPermissions(filepath.Join(destination, file.Name()), 0, 0); err != nil {
668
-				return err
669
-			}
670
-		}
671
-		return nil
672
-	}
673
-
674
-	return fixPermissions(destination, 0, 0)
659
+	return fixPermissions(source, destination, 0, 0)
675 660
 }
676 661
 
677
-func fixPermissions(destination string, uid, gid int) error {
678
-	return filepath.Walk(destination, func(path string, info os.FileInfo, err error) error {
679
-		if err := os.Lchown(path, uid, gid); err != nil && !os.IsNotExist(err) {
662
+func fixPermissions(source, destination string, uid, gid int) error {
663
+	// We Walk on the source rather than on the destination because we don't
664
+	// want to change permissions on things we haven't created or modified.
665
+	return filepath.Walk(source, func(fullpath string, info os.FileInfo, err error) error {
666
+		// Do not alter the walk root itself as it potentially existed before.
667
+		if source == fullpath {
668
+			return nil
669
+		}
670
+		// Path is prefixed by source: substitute with destination instead.
671
+		cleaned, err := filepath.Rel(source, fullpath)
672
+		if err != nil {
680 673
 			return err
681 674
 		}
682
-		return nil
675
+		fullpath = path.Join(destination, cleaned)
676
+		return os.Lchown(fullpath, uid, gid)
683 677
 	})
684 678
 }
685 679
 
... ...
@@ -1066,6 +1066,31 @@ ADD . /`,
1066 1066
 	logDone("build - add etc directory to root")
1067 1067
 }
1068 1068
 
1069
+// Testing #9401
1070
+func TestBuildAddPreservesFilesSpecialBits(t *testing.T) {
1071
+	name := "testaddpreservesfilesspecialbits"
1072
+	defer deleteImages(name)
1073
+	ctx, err := fakeContext(`FROM busybox
1074
+ADD suidbin /usr/bin/suidbin
1075
+RUN chmod 4755 /usr/bin/suidbin
1076
+RUN [ $(ls -l /usr/bin/suidbin | awk '{print $1}') = '-rwsr-xr-x' ]
1077
+ADD ./data/ /
1078
+RUN [ $(ls -l /usr/bin/suidbin | awk '{print $1}') = '-rwsr-xr-x' ]`,
1079
+		map[string]string{
1080
+			"suidbin":             "suidbin",
1081
+			"/data/usr/test_file": "test1",
1082
+		})
1083
+	if err != nil {
1084
+		t.Fatal(err)
1085
+	}
1086
+	defer ctx.Close()
1087
+
1088
+	if _, err := buildImageFromContext(name, ctx, true); err != nil {
1089
+		t.Fatal(err)
1090
+	}
1091
+	logDone("build - add preserves files special bits")
1092
+}
1093
+
1069 1094
 func TestBuildCopySingleFileToRoot(t *testing.T) {
1070 1095
 	name := "testcopysinglefiletoroot"
1071 1096
 	defer deleteImages(name)