Hi,

On Tue, Jun 18, 2019 at 10:18:47PM +0200, Paul Gevers wrote:
> I don't like to rush you, but be aware that the time slot to fix this is
> closing. The package needs to be ready to migrate at 2019-06-25 13:00
> UTC [1]. If the package isn't ready, we'll remove it from buster (fixing
> some headaches for the security team, but a shame nevertheless).
> 

Hope it's still in time...

Please unblock docker.io/18.09.1+dfsg1-7.1

The debdiff is:

diff -Nru docker.io-18.09.1+dfsg1/debian/changelog 
docker.io-18.09.1+dfsg1/debian/changelog
--- docker.io-18.09.1+dfsg1/debian/changelog    2019-05-13 10:34:45.000000000 
+0800
+++ docker.io-18.09.1+dfsg1/debian/changelog    2019-06-23 01:25:10.000000000 
+0800
@@ -1,3 +1,15 @@
+docker.io (18.09.1+dfsg1-7.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+
+  [ Hideki Yamane ]
+  * upstream site moved to mobyproject.org
+
+  [ Arnaud Rebillout ]
+  * Add patch for CVE-2018-15664 (Closes: #929662).
+
+ -- Shengjing Zhu <z...@debian.org>  Sun, 23 Jun 2019 01:25:10 +0800
+
 docker.io (18.09.1+dfsg1-7) unstable; urgency=medium
 
   * Add patch to revert using iptables-legacy (Closes: #921600).
diff -Nru docker.io-18.09.1+dfsg1/debian/control 
docker.io-18.09.1+dfsg1/debian/control
--- docker.io-18.09.1+dfsg1/debian/control      2019-05-13 10:34:45.000000000 
+0800
+++ docker.io-18.09.1+dfsg1/debian/control      2019-06-23 01:25:10.000000000 
+0800
@@ -139,7 +139,7 @@
     ,pkg-config
     ,procps
     ,tzdata
-Homepage: https://dockerproject.org
+Homepage: https://mobyproject.org
 Vcs-Browser: https://salsa.debian.org/docker-team/docker
 Vcs-Git: https://salsa.debian.org/docker-team/docker.git
 XS-Go-Import-Path: github.com/docker/docker
diff -Nru 
docker.io-18.09.1+dfsg1/debian/patches/cve-2018-15664-01-pass-root-to-chroot-to-for-chroot-untar.patch
 
docker.io-18.09.1+dfsg1/debian/patches/cve-2018-15664-01-pass-root-to-chroot-to-for-chroot-untar.patch
--- 
docker.io-18.09.1+dfsg1/debian/patches/cve-2018-15664-01-pass-root-to-chroot-to-for-chroot-untar.patch
      1970-01-01 08:00:00.000000000 +0800
+++ 
docker.io-18.09.1+dfsg1/debian/patches/cve-2018-15664-01-pass-root-to-chroot-to-for-chroot-untar.patch
      2019-06-23 01:25:10.000000000 +0800
@@ -0,0 +1,186 @@
+From: Brian Goff <cpugu...@gmail.com>
+Date: Thu, 30 May 2019 11:15:09 -0700
+Subject: [PATCH] Pass root to chroot to for chroot Untar
+
+This is useful for preventing CVE-2018-15664 where a malicious container
+process can take advantage of a race on symlink resolution/sanitization.
+
+Before this change chrootarchive would chroot to the destination
+directory which is attacker controlled. With this patch we always chroot
+to the container's root which is not attacker controlled.
+
+Signed-off-by: Brian Goff <cpugu...@gmail.com>
+Origin: upstream, https://github.com/moby/moby/pull/39292
+---
+ daemon/archive.go                      |  7 ++-
+ pkg/chrootarchive/archive.go           | 24 ++++++--
+ pkg/chrootarchive/archive_unix.go      | 34 ++++++++++--
+ pkg/chrootarchive/archive_windows.go   |  2 +-
+ 5 files changed, 55 insertions(+), 12 deletions(-)
+
+diff --git a/engine/daemon/archive.go b/engine/daemon/archive.go
+index 9c7971b56ea3..9f56ca750392 100644
+--- a/engine/daemon/archive.go
++++ b/engine/daemon/archive.go
+@@ -31,11 +31,12 @@ type archiver interface {
+ }
+ 
+ // helper functions to extract or archive
+-func extractArchive(i interface{}, src io.Reader, dst string, opts 
*archive.TarOptions) error {
++func extractArchive(i interface{}, src io.Reader, dst string, opts 
*archive.TarOptions, root string) error {
+       if ea, ok := i.(extractor); ok {
+               return ea.ExtractArchive(src, dst, opts)
+       }
+-      return chrootarchive.Untar(src, dst, opts)
++
++      return chrootarchive.UntarWithRoot(src, dst, opts, root)
+ }
+ 
+ func archivePath(i interface{}, src string, opts *archive.TarOptions) 
(io.ReadCloser, error) {
+@@ -367,7 +368,7 @@ func (daemon *Daemon) containerExtractToDir(container 
*container.Container, path
+               }
+       }
+ 
+-      if err := extractArchive(driver, content, resolvedPath, options); err 
!= nil {
++      if err := extractArchive(driver, content, resolvedPath, options, 
container.BaseFS.Path()); err != nil {
+               return err
+       }
+ 
+diff --git a/engine/pkg/chrootarchive/archive.go 
b/engine/pkg/chrootarchive/archive.go
+index 2d9d662830b7..7ebca3774c3d 100644
+--- a/engine/pkg/chrootarchive/archive.go
++++ b/engine/pkg/chrootarchive/archive.go
+@@ -27,18 +27,34 @@ func NewArchiver(idMapping *idtools.IdentityMapping) 
*archive.Archiver {
+ // The archive may be compressed with one of the following algorithms:
+ //  identity (uncompressed), gzip, bzip2, xz.
+ func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) 
error {
+-      return untarHandler(tarArchive, dest, options, true)
++      return untarHandler(tarArchive, dest, options, true, dest)
++}
++
++// UntarWithRoot is the same as `Untar`, but allows you to pass in a root 
directory
++// The root directory is the directory that will be chrooted to.
++// `dest` must be a path within `root`, if it is not an error will be 
returned.
++//
++// `root` should set to a directory which is not controlled by any potentially
++// malicious process.
++//
++// This should be used to prevent a potential attacker from manipulating 
`dest`
++// such that it would provide access to files outside of `dest` through things
++// like symlinks. Normally `ResolveSymlinksInScope` would handle this, however
++// sanitizing symlinks in this manner is inherrently racey:
++// ref: CVE-2018-15664
++func UntarWithRoot(tarArchive io.Reader, dest string, options 
*archive.TarOptions, root string) error {
++      return untarHandler(tarArchive, dest, options, true, root)
+ }
+ 
+ // UntarUncompressed reads a stream of bytes from `archive`, parses it as a 
tar archive,
+ // and unpacks it into the directory at `dest`.
+ // The archive must be an uncompressed stream.
+ func UntarUncompressed(tarArchive io.Reader, dest string, options 
*archive.TarOptions) error {
+-      return untarHandler(tarArchive, dest, options, false)
++      return untarHandler(tarArchive, dest, options, false, dest)
+ }
+ 
+ // Handler for teasing out the automatic decompression
+-func untarHandler(tarArchive io.Reader, dest string, options 
*archive.TarOptions, decompress bool) error {
++func untarHandler(tarArchive io.Reader, dest string, options 
*archive.TarOptions, decompress bool, root string) error {
+       if tarArchive == nil {
+               return fmt.Errorf("Empty archive")
+       }
+@@ -69,5 +85,5 @@ func untarHandler(tarArchive io.Reader, dest string, options 
*archive.TarOptions
+               r = decompressedArchive
+       }
+ 
+-      return invokeUnpack(r, dest, options)
++      return invokeUnpack(r, dest, options, root)
+ }
+diff --git a/engine/pkg/chrootarchive/archive_unix.go 
b/engine/pkg/chrootarchive/archive_unix.go
+index 5df8afd66205..96f07c4bb4d6 100644
+--- a/engine/pkg/chrootarchive/archive_unix.go
++++ b/engine/pkg/chrootarchive/archive_unix.go
+@@ -10,6 +10,7 @@ import (
+       "io"
+       "io/ioutil"
+       "os"
++      "path/filepath"
+       "runtime"
+ 
+       "github.com/docker/docker/pkg/archive"
+@@ -30,11 +31,21 @@ func untar() {
+               fatal(err)
+       }
+ 
+-      if err := chroot(flag.Arg(0)); err != nil {
++      dst := flag.Arg(0)
++      var root string
++      if len(flag.Args()) > 1 {
++              root = flag.Arg(1)
++      }
++
++      if root == "" {
++              root = dst
++      }
++
++      if err := chroot(root); err != nil {
+               fatal(err)
+       }
+ 
+-      if err := archive.Unpack(os.Stdin, "/", options); err != nil {
++      if err := archive.Unpack(os.Stdin, dst, options); err != nil {
+               fatal(err)
+       }
+       // fully consume stdin in case it is zero padded
+@@ -45,7 +56,7 @@ func untar() {
+       os.Exit(0)
+ }
+ 
+-func invokeUnpack(decompressedArchive io.Reader, dest string, options 
*archive.TarOptions) error {
++func invokeUnpack(decompressedArchive io.Reader, dest string, options 
*archive.TarOptions, root string) error {
+ 
+       // We can't pass a potentially large exclude list directly via cmd line
+       // because we easily overrun the kernel's max argument/environment size
+@@ -57,7 +68,21 @@ func invokeUnpack(decompressedArchive io.Reader, dest 
string, options *archive.T
+               return fmt.Errorf("Untar pipe failure: %v", err)
+       }
+ 
+-      cmd := reexec.Command("docker-untar", dest)
++      if root != "" {
++              relDest, err := filepath.Rel(root, dest)
++              if err != nil {
++                      return err
++              }
++              if relDest == "." {
++                      relDest = "/"
++              }
++              if relDest[0] != '/' {
++                      relDest = "/" + relDest
++              }
++              dest = relDest
++      }
++
++      cmd := reexec.Command("docker-untar", dest, root)
+       cmd.Stdin = decompressedArchive
+ 
+       cmd.ExtraFiles = append(cmd.ExtraFiles, r)
+@@ -69,6 +94,7 @@ func invokeUnpack(decompressedArchive io.Reader, dest 
string, options *archive.T
+               w.Close()
+               return fmt.Errorf("Untar error on re-exec cmd: %v", err)
+       }
++
+       //write the options to the pipe for the untar exec to read
+       if err := json.NewEncoder(w).Encode(options); err != nil {
+               w.Close()
+diff --git a/engine/pkg/chrootarchive/archive_windows.go 
b/engine/pkg/chrootarchive/archive_windows.go
+index f2973132a391..bd5712c5c04c 100644
+--- a/engine/pkg/chrootarchive/archive_windows.go
++++ b/engine/pkg/chrootarchive/archive_windows.go
+@@ -14,7 +14,7 @@ func chroot(path string) error {
+ 
+ func invokeUnpack(decompressedArchive io.ReadCloser,
+       dest string,
+-      options *archive.TarOptions) error {
++      options *archive.TarOptions, root string) error {
+       // Windows is different to Linux here because Windows does not support
+       // chroot. Hence there is no point sandboxing a chrooted process to
+       // do the unpack. We call inline instead within the daemon process.
diff -Nru 
docker.io-18.09.1+dfsg1/debian/patches/cve-2018-15664-02-add-chroot-for-tar-packing-operations.patch
 
docker.io-18.09.1+dfsg1/debian/patches/cve-2018-15664-02-add-chroot-for-tar-packing-operations.patch
--- 
docker.io-18.09.1+dfsg1/debian/patches/cve-2018-15664-02-add-chroot-for-tar-packing-operations.patch
        1970-01-01 08:00:00.000000000 +0800
+++ 
docker.io-18.09.1+dfsg1/debian/patches/cve-2018-15664-02-add-chroot-for-tar-packing-operations.patch
        2019-06-23 01:25:10.000000000 +0800
@@ -0,0 +1,248 @@
+From: Brian Goff <cpugu...@gmail.com>
+Date: Thu, 30 May 2019 14:55:52 -0700
+Subject: [PATCH] Add chroot for tar packing operations
+
+Previously only unpack operations were supported with chroot.
+This adds chroot support for packing operations.
+This prevents potential breakouts when copying data from a container.
+
+Signed-off-by: Brian Goff <cpugu...@gmail.com>
+Origin: upstream, https://github.com/moby/moby/pull/39292
+---
+ daemon/archive.go                      |  8 +--
+ daemon/export.go                       |  2 +-
+ pkg/chrootarchive/archive.go           |  8 +++
+ pkg/chrootarchive/archive_unix.go      | 98 +++++++++++++++++++++++++-
+ pkg/chrootarchive/archive_windows.go   |  7 ++
+ pkg/chrootarchive/init_unix.go         |  1 +
+ 6 files changed, 117 insertions(+), 7 deletions(-)
+
+diff --git a/engine/daemon/archive.go b/engine/daemon/archive.go
+index 9f56ca750392..109376b4b566 100644
+--- a/engine/daemon/archive.go
++++ b/engine/daemon/archive.go
+@@ -39,11 +39,11 @@ func extractArchive(i interface{}, src io.Reader, dst 
string, opts *archive.TarO
+       return chrootarchive.UntarWithRoot(src, dst, opts, root)
+ }
+ 
+-func archivePath(i interface{}, src string, opts *archive.TarOptions) 
(io.ReadCloser, error) {
++func archivePath(i interface{}, src string, opts *archive.TarOptions, root 
string) (io.ReadCloser, error) {
+       if ap, ok := i.(archiver); ok {
+               return ap.ArchivePath(src, opts)
+       }
+-      return archive.TarWithOptions(src, opts)
++      return chrootarchive.Tar(src, opts, root)
+ }
+ 
+ // ContainerCopy performs a deprecated operation of archiving the resource at
+@@ -239,7 +239,7 @@ func (daemon *Daemon) containerArchivePath(container 
*container.Container, path
+       sourceDir, sourceBase := driver.Dir(resolvedPath), 
driver.Base(resolvedPath)
+       opts := archive.TarResourceRebaseOpts(sourceBase, driver.Base(absPath))
+ 
+-      data, err := archivePath(driver, sourceDir, opts)
++      data, err := archivePath(driver, sourceDir, opts, 
container.BaseFS.Path())
+       if err != nil {
+               return nil, nil, err
+       }
+@@ -433,7 +433,7 @@ func (daemon *Daemon) containerCopy(container 
*container.Container, resource str
+       archive, err := archivePath(driver, basePath, &archive.TarOptions{
+               Compression:  archive.Uncompressed,
+               IncludeFiles: filter,
+-      })
++      }, container.BaseFS.Path())
+       if err != nil {
+               return nil, err
+       }
+diff --git a/engine/daemon/export.go b/engine/daemon/export.go
+index 27bc35967d22..01593f4e8a4f 100644
+--- a/engine/daemon/export.go
++++ b/engine/daemon/export.go
+@@ -70,7 +70,7 @@ func (daemon *Daemon) containerExport(container 
*container.Container) (arch io.R
+               Compression: archive.Uncompressed,
+               UIDMaps:     daemon.idMapping.UIDs(),
+               GIDMaps:     daemon.idMapping.GIDs(),
+-      })
++      }, basefs.Path())
+       if err != nil {
+               rwlayer.Unmount()
+               return nil, err
+diff --git a/engine/pkg/chrootarchive/archive.go 
b/engine/pkg/chrootarchive/archive.go
+index 7ebca3774c3d..6ff61e6a767a 100644
+--- a/engine/pkg/chrootarchive/archive.go
++++ b/engine/pkg/chrootarchive/archive.go
+@@ -87,3 +87,11 @@ func untarHandler(tarArchive io.Reader, dest string, 
options *archive.TarOptions
+ 
+       return invokeUnpack(r, dest, options, root)
+ }
++
++// Tar tars the requested path while chrooted to the specified root.
++func Tar(srcPath string, options *archive.TarOptions, root string) 
(io.ReadCloser, error) {
++      if options == nil {
++              options = &archive.TarOptions{}
++      }
++      return invokePack(srcPath, options, root)
++}
+diff --git a/engine/pkg/chrootarchive/archive_unix.go 
b/engine/pkg/chrootarchive/archive_unix.go
+index 96f07c4bb4d6..ea2879dc002f 100644
+--- a/engine/pkg/chrootarchive/archive_unix.go
++++ b/engine/pkg/chrootarchive/archive_unix.go
+@@ -12,9 +12,11 @@ import (
+       "os"
+       "path/filepath"
+       "runtime"
++      "strings"
+ 
+       "github.com/docker/docker/pkg/archive"
+       "github.com/docker/docker/pkg/reexec"
++      "github.com/pkg/errors"
+ )
+ 
+ // untar is the entry-point for docker-untar on re-exec. This is not used on
+@@ -24,7 +26,7 @@ func untar() {
+       runtime.LockOSThread()
+       flag.Parse()
+ 
+-      var options *archive.TarOptions
++      var options archive.TarOptions
+ 
+       //read the options from the pipe "ExtraFiles"
+       if err := json.NewDecoder(os.NewFile(3, "options")).Decode(&options); 
err != nil {
+@@ -45,7 +47,7 @@ func untar() {
+               fatal(err)
+       }
+ 
+-      if err := archive.Unpack(os.Stdin, dst, options); err != nil {
++      if err := archive.Unpack(os.Stdin, dst, &options); err != nil {
+               fatal(err)
+       }
+       // fully consume stdin in case it is zero padded
+@@ -57,6 +59,9 @@ func untar() {
+ }
+ 
+ func invokeUnpack(decompressedArchive io.Reader, dest string, options 
*archive.TarOptions, root string) error {
++      if root == "" {
++              return errors.New("must specify a root to chroot to")
++      }
+ 
+       // We can't pass a potentially large exclude list directly via cmd line
+       // because we easily overrun the kernel's max argument/environment size
+@@ -112,3 +117,92 @@ func invokeUnpack(decompressedArchive io.Reader, dest 
string, options *archive.T
+       }
+       return nil
+ }
++
++func tar() {
++      runtime.LockOSThread()
++      flag.Parse()
++
++      src := flag.Arg(0)
++      var root string
++      if len(flag.Args()) > 1 {
++              root = flag.Arg(1)
++      }
++
++      if root == "" {
++              root = src
++      }
++
++      if err := realChroot(root); err != nil {
++              fatal(err)
++      }
++
++      var options archive.TarOptions
++      if err := json.NewDecoder(os.Stdin).Decode(&options); err != nil {
++              fatal(err)
++      }
++
++      rdr, err := archive.TarWithOptions(src, &options)
++      if err != nil {
++              fatal(err)
++      }
++      defer rdr.Close()
++
++      if _, err := io.Copy(os.Stdout, rdr); err != nil {
++              fatal(err)
++      }
++
++      os.Exit(0)
++}
++
++func invokePack(srcPath string, options *archive.TarOptions, root string) 
(io.ReadCloser, error) {
++      if root == "" {
++              return nil, errors.New("root path must not be empty")
++      }
++
++      relSrc, err := filepath.Rel(root, srcPath)
++      if err != nil {
++              return nil, err
++      }
++      if relSrc == "." {
++              relSrc = "/"
++      }
++      if relSrc[0] != '/' {
++              relSrc = "/" + relSrc
++      }
++
++      // make sure we didn't trim a trailing slash with the call to `Rel`
++      if strings.HasSuffix(srcPath, "/") && !strings.HasSuffix(relSrc, "/") {
++              relSrc += "/"
++      }
++
++      cmd := reexec.Command("docker-tar", relSrc, root)
++
++      errBuff := bytes.NewBuffer(nil)
++      cmd.Stderr = errBuff
++
++      tarR, tarW := io.Pipe()
++      cmd.Stdout = tarW
++
++      stdin, err := cmd.StdinPipe()
++      if err != nil {
++              return nil, errors.Wrap(err, "error getting options pipe for 
tar process")
++      }
++
++      if err := cmd.Start(); err != nil {
++              return nil, errors.Wrap(err, "tar error on re-exec cmd")
++      }
++
++      go func() {
++              err := cmd.Wait()
++              err = errors.Wrapf(err, "error processing tar file: %s", 
errBuff)
++              tarW.CloseWithError(err)
++      }()
++
++      if err := json.NewEncoder(stdin).Encode(options); err != nil {
++              stdin.Close()
++              return nil, errors.Wrap(err, "tar json encode to pipe failed")
++      }
++      stdin.Close()
++
++      return tarR, nil
++}
+diff --git a/engine/pkg/chrootarchive/archive_windows.go 
b/engine/pkg/chrootarchive/archive_windows.go
+index bd5712c5c04c..de87113e9544 100644
+--- a/engine/pkg/chrootarchive/archive_windows.go
++++ b/engine/pkg/chrootarchive/archive_windows.go
+@@ -20,3 +20,10 @@ func invokeUnpack(decompressedArchive io.ReadCloser,
+       // do the unpack. We call inline instead within the daemon process.
+       return archive.Unpack(decompressedArchive, longpath.AddPrefix(dest), 
options)
+ }
++
++func invokePack(srcPath string, options *archive.TarOptions, root string) 
(io.ReadCloser, error) {
++      // Windows is different to Linux here because Windows does not support
++      // chroot. Hence there is no point sandboxing a chrooted process to
++      // do the pack. We call inline instead within the daemon process.
++      return archive.TarWithOptions(srcPath, options)
++}
+diff --git a/engine/pkg/chrootarchive/init_unix.go 
b/engine/pkg/chrootarchive/init_unix.go
+index a15e4bb83c40..c24fea7d9c13 100644
+--- a/engine/pkg/chrootarchive/init_unix.go
++++ b/engine/pkg/chrootarchive/init_unix.go
+@@ -14,6 +14,7 @@ import (
+ func init() {
+       reexec.Register("docker-applyLayer", applyLayer)
+       reexec.Register("docker-untar", untar)
++      reexec.Register("docker-tar", tar)
+ }
+ 
+ func fatal(err error) {
diff -Nru docker.io-18.09.1+dfsg1/debian/patches/series 
docker.io-18.09.1+dfsg1/debian/patches/series
--- docker.io-18.09.1+dfsg1/debian/patches/series       2019-05-13 
10:34:45.000000000 +0800
+++ docker.io-18.09.1+dfsg1/debian/patches/series       2019-06-23 
01:25:10.000000000 +0800
@@ -12,6 +12,9 @@
 cli-fix-manpages-build-script.patch
 cli-fix-registry-debug-message-go-1.11.patch
 
+cve-2018-15664-01-pass-root-to-chroot-to-for-chroot-untar.patch
+cve-2018-15664-02-add-chroot-for-tar-packing-operations.patch
+
 engine-contrib-debootstrap-curl-follow-location.patch
 engine-test-noinstall.patch
 

Attachment: signature.asc
Description: PGP signature

Reply via email to