Browse code

Change path breakout detection logic in archive package

Fixes #9375

Signed-off-by: Alexandr Morozov <lk4d4@docker.com>

Alexandr Morozov authored on 2014/11/27 16:00:13
Showing 3 changed files
... ...
@@ -478,3 +478,41 @@ func TestCpVolumePath(t *testing.T) {
478 478
 
479 479
 	logDone("cp - volume path")
480 480
 }
481
+
482
+func TestCpToDot(t *testing.T) {
483
+	out, exitCode, err := dockerCmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test")
484
+	if err != nil || exitCode != 0 {
485
+		t.Fatal("failed to create a container", out, err)
486
+	}
487
+
488
+	cleanedContainerID := stripTrailingCharacters(out)
489
+	defer deleteContainer(cleanedContainerID)
490
+
491
+	out, _, err = dockerCmd(t, "wait", cleanedContainerID)
492
+	if err != nil || stripTrailingCharacters(out) != "0" {
493
+		t.Fatal("failed to set up container", out, err)
494
+	}
495
+
496
+	tmpdir, err := ioutil.TempDir("", "docker-integration")
497
+	if err != nil {
498
+		t.Fatal(err)
499
+	}
500
+	defer os.RemoveAll(tmpdir)
501
+	cwd, err := os.Getwd()
502
+	if err != nil {
503
+		t.Fatal(err)
504
+	}
505
+	defer os.Chdir(cwd)
506
+	if err := os.Chdir(tmpdir); err != nil {
507
+		t.Fatal(err)
508
+	}
509
+	_, _, err = dockerCmd(t, "cp", cleanedContainerID+":/test", ".")
510
+	if err != nil {
511
+		t.Fatalf("couldn't docker cp to \".\" path: %s", err)
512
+	}
513
+	content, err := ioutil.ReadFile("./test")
514
+	if string(content) != "lololol\n" {
515
+		t.Fatal("Wrong content in copied file %q, should be %q", content, "lololol\n")
516
+	}
517
+	logDone("cp - to dot path")
518
+}
... ...
@@ -530,10 +530,13 @@ loop:
530 530
 			}
531 531
 		}
532 532
 
533
-		// Prevent symlink breakout
534 533
 		path := filepath.Join(dest, hdr.Name)
535
-		if !strings.HasPrefix(path, dest) {
536
-			return breakoutError(fmt.Errorf("%q is outside of %q", path, dest))
534
+		rel, err := filepath.Rel(dest, path)
535
+		if err != nil {
536
+			return err
537
+		}
538
+		if strings.HasPrefix(rel, "..") {
539
+			return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
537 540
 		}
538 541
 
539 542
 		// If path exits we almost always just want to remove and replace it
... ...
@@ -92,12 +92,14 @@ func ApplyLayer(dest string, layer ArchiveReader) error {
92 92
 		}
93 93
 
94 94
 		path := filepath.Join(dest, hdr.Name)
95
-		base := filepath.Base(path)
96
-
97
-		// Prevent symlink breakout
98
-		if !strings.HasPrefix(path, dest) {
99
-			return breakoutError(fmt.Errorf("%q is outside of %q", path, dest))
95
+		rel, err := filepath.Rel(dest, path)
96
+		if err != nil {
97
+			return err
98
+		}
99
+		if strings.HasPrefix(rel, "..") {
100
+			return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
100 101
 		}
102
+		base := filepath.Base(path)
101 103
 
102 104
 		if strings.HasPrefix(base, ".wh.") {
103 105
 			originalBase := base[len(".wh."):]