Version in base suite: 18.09.1+dfsg1-7.1+deb10u2 Base version: docker.io_18.09.1+dfsg1-7.1+deb10u2 Target version: docker.io_18.09.1+dfsg1-7.1+deb10u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/d/docker.io/docker.io_18.09.1+dfsg1-7.1+deb10u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/d/docker.io/docker.io_18.09.1+dfsg1-7.1+deb10u3.dsc changelog | 10 patches/cve-2020-15157.patch | 206 +++++++++++ patches/cve-2020-15257.patch | 744 +++++++++++++++++++++++++++++++++++++++++ patches/cve-2021-21284-1.patch | 103 +++++ patches/cve-2021-21284-2.patch | 52 ++ patches/cve-2021-21284-3.patch | 347 +++++++++++++++++++ patches/cve-2021-21285.patch | 53 ++ patches/series | 6 8 files changed, 1521 insertions(+) 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 2020-06-14 20:12:29.000000000 +0000 +++ docker.io-18.09.1+dfsg1/debian/changelog 2021-02-21 17:18:35.000000000 +0000 @@ -1,3 +1,13 @@ +docker.io (18.09.1+dfsg1-7.1+deb10u3) buster-security; urgency=medium + + * Backport upstream patches for: + - CVE-2020-15157 + - CVE-2020-15257 + - CVE-2021-21284 + - CVE-2021-21285 + + -- Felix Geyer Sun, 21 Feb 2021 18:18:35 +0100 + docker.io (18.09.1+dfsg1-7.1+deb10u2) buster-security; urgency=medium * Add upstream patch for CVE-2020-13401 (Closes: #962141) diff -Nru docker.io-18.09.1+dfsg1/debian/patches/cve-2020-15157.patch docker.io-18.09.1+dfsg1/debian/patches/cve-2020-15157.patch --- docker.io-18.09.1+dfsg1/debian/patches/cve-2020-15157.patch 1970-01-01 00:00:00.000000000 +0000 +++ docker.io-18.09.1+dfsg1/debian/patches/cve-2020-15157.patch 2021-02-21 17:06:17.000000000 +0000 @@ -0,0 +1,206 @@ +From 1ead8d9deb3b175bf40413b8c47b3d19c2262726 Mon Sep 17 00:00:00 2001 +From: Sergey Kanzhelev +Date: Thu, 24 Sep 2020 18:35:46 +0000 +Subject: [PATCH] treat manifest provided URLs differently + +--- + remotes/docker/fetcher.go | 25 ++++-- + remotes/docker/fetcher_test.go | 137 +++++++++++++++++++++++++++++++++ + 2 files changed, 154 insertions(+), 8 deletions(-) + +diff --git a/containerd/remotes/docker/fetcher.go b/containerd/remotes/docker/fetcher.go +index 4a2ce3c393..1708b68fea 100644 +--- a/containerd/remotes/docker/fetcher.go ++++ b/containerd/remotes/docker/fetcher.go +@@ -56,6 +56,23 @@ func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.R + } + + return newHTTPReadSeeker(desc.Size, func(offset int64) (io.ReadCloser, error) { ++ if len(desc.URLs) > 0 { ++ db := *r.dockerBase ++ db.auth = nil // do not authenticate ++ nr := dockerFetcher{ ++ dockerBase: &db, ++ } ++ for _, u := range desc.URLs { ++ log.G(ctx).WithField("url", u).Debug("trying alternative url") ++ rc, err := nr.open(ctx, u, desc.MediaType, offset) ++ if err != nil { ++ log.G(ctx).WithField("error", err).Debug("error trying url") ++ continue // try one of the other urls. ++ } ++ ++ return rc, nil ++ } ++ } + for _, u := range urls { + rc, err := r.open(ctx, u, desc.MediaType, offset) + if err != nil { +@@ -142,14 +159,6 @@ func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int + func (r *dockerFetcher) getV2URLPaths(ctx context.Context, desc ocispec.Descriptor) ([]string, error) { + var urls []string + +- if len(desc.URLs) > 0 { +- // handle fetch via external urls. +- for _, u := range desc.URLs { +- log.G(ctx).WithField("url", u).Debug("adding alternative url") +- urls = append(urls, u) +- } +- } +- + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList, + images.MediaTypeDockerSchema1Manifest, +diff --git a/containerd/remotes/docker/fetcher_test.go b/containerd/remotes/docker/fetcher_test.go +index 8b7beb758d..b72de6249b 100644 +--- a/rcontainerd/remotes/docker/fetcher_test.go ++++ b/containerd/remotes/docker/fetcher_test.go +@@ -23,7 +23,12 @@ import ( + "math/rand" + "net/http" + "net/http/httptest" ++ "net/url" + "testing" ++ ++ "github.com/containerd/containerd/images" ++ digest "github.com/opencontainers/go-digest" ++ ocispec "github.com/opencontainers/image-spec/specs-go/v1" + ) + + func TestFetcherOpen(t *testing.T) { +@@ -92,3 +97,135 @@ func TestFetcherOpen(t *testing.T) { + t.Fatal("expected error opening with invalid server response") + } + } ++ ++func TestFetcherFetch(t *testing.T) { ++ content := make([]byte, 128) ++ rand.New(rand.NewSource(1)).Read(content) ++ start := 0 ++ ++ s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ++ t.Helper() ++ ++ if r.RequestURI == "/404" { ++ // no authorization must be provided with the initial GET ++ if r.Header["Authorization"] != nil { ++ t.Errorf("no authorization can be used with manifest-specified URLs") ++ return ++ } ++ ++ rw.WriteHeader(http.StatusNotFound) ++ return ++ } ++ ++ if r.RequestURI == "/401" { ++ if r.Header["Authorization"] == nil { ++ rw.Header().Set("Docker-Distribution-Api-Version", "registry/2.0") ++ rw.Header().Set("WWW-Authenticate", "Basic realm=\"https://url\"") ++ rw.WriteHeader(http.StatusUnauthorized) ++ return ++ } ++ ++ // no authorization must be provided for manifest-defined URLs ++ t.Errorf("no authorization can be used with manifest-specified URLs") ++ return ++ } ++ ++ if r.Header["Authorization"] == nil { ++ rw.Header().Set("Docker-Distribution-Api-Version", "registry/2.0") ++ rw.Header().Set("WWW-Authenticate", "Basic realm=\"https://url\"") ++ rw.WriteHeader(http.StatusUnauthorized) ++ return ++ } ++ ++ // authorizer must set Authorize header for the manifest URL ++ if start > 0 { ++ rw.Header().Set("content-range", fmt.Sprintf("bytes %d-127/128", start)) ++ } ++ rw.Header().Set("content-length", fmt.Sprintf("%d", len(content[start:]))) ++ rw.Write(content[start:]) ++ })) ++ defer s.Close() ++ ++ baseURL, _ := url.Parse(s.URL) ++ db := &dockerBase{ ++ client: s.Client(), ++ base: *baseURL, ++ } ++ db.auth = NewAuthorizer(db.client, func(a string) (string, string, error) { ++ return "Authorize", "Basic blah", nil ++ }) ++ ++ f := dockerFetcher{dockerBase: db} ++ ++ ctx := context.Background() ++ ++ desc := ocispec.Descriptor{ ++ MediaType: images.MediaTypeDockerSchema2Manifest, ++ Digest: digest.FromBytes([]byte("digest")), ++ Size: 10, ++ URLs: []string{fmt.Sprintf("%s/404", s.URL), fmt.Sprintf("%s/401", s.URL)}, ++ Annotations: map[string]string{}, ++ } ++ ++ rc, err := f.Fetch(ctx, desc) ++ if err != nil { ++ t.Fatalf("failed to open: %+v", err) ++ } ++ b, err := ioutil.ReadAll(rc) ++ if err != nil { ++ t.Fatal(err) ++ } ++ expected := content[0:] ++ if len(b) != len(expected) { ++ t.Errorf("unexpected length %d, expected %d", len(b), len(expected)) ++ return ++ } ++ for i, c := range expected { ++ if b[i] != c { ++ t.Errorf("unexpected byte %x at %d, expected %x", b[i], i, c) ++ return ++ } ++ } ++} ++ ++func TestFetcherGetV2URLPaths(t *testing.T) { ++ content := make([]byte, 128) ++ rand.New(rand.NewSource(1)).Read(content) ++ start := 0 ++ ++ s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ++ if start > 0 { ++ rw.Header().Set("content-range", fmt.Sprintf("bytes %d-127/128", start)) ++ } ++ rw.Header().Set("content-length", fmt.Sprintf("%d", len(content[start:]))) ++ rw.Write(content[start:]) ++ })) ++ defer s.Close() ++ ++ f := dockerFetcher{&dockerBase{ ++ client: s.Client(), ++ }} ++ ctx := context.Background() ++ ++ desc := ocispec.Descriptor{ ++ MediaType: images.MediaTypeDockerSchema2Manifest, ++ Digest: "digest", ++ Size: 10, ++ URLs: []string{"first", "second"}, ++ Annotations: map[string]string{}, ++ } ++ ++ urls, err := f.getV2URLPaths(ctx, desc) ++ ++ if err != nil { ++ t.Errorf("unexpected error %v", err) ++ return ++ } ++ ++ // blobs and manifest/digest ++ // URLs from the descriptor should not be added to the list of alternative sources ++ if len(urls) != 2 { ++ t.Errorf("unexpected number of urls: %d, expected %d", len(urls), 2) ++ return ++ } ++} diff -Nru docker.io-18.09.1+dfsg1/debian/patches/cve-2020-15257.patch docker.io-18.09.1+dfsg1/debian/patches/cve-2020-15257.patch --- docker.io-18.09.1+dfsg1/debian/patches/cve-2020-15257.patch 1970-01-01 00:00:00.000000000 +0000 +++ docker.io-18.09.1+dfsg1/debian/patches/cve-2020-15257.patch 2021-02-21 17:06:17.000000000 +0000 @@ -0,0 +1,744 @@ +From 3519233e1b5a408c7e92b0af4293000820a0089b Mon Sep 17 00:00:00 2001 +From: Tianon Gravi +Date: Tue, 24 Nov 2020 12:38:31 +0000 +Subject: [PATCH] Fix CVE-2020-15257 + +This is the 1.2 backport. It's the Samuel Karp patch with additional changes: + + - Add ReadAddress function from commit 84a24711e88 + - Add "horten the unix socket path for shim" commit (a631796fda6) + +Below is the original commit message: + +----------------------------------------------------------------------- + +Use path based unix socket for shims + +This allows filesystem based ACLs for configuring access to the socket of a +shim. + +Co-authored-by: Samuel Karp +Signed-off-by: Samuel Karp +Signed-off-by: Michael Crosby +Signed-off-by: Michael Crosby + +----------------------------------------------------------------------- + +containerd-shim: use path-based unix socket + +This allows filesystem-based ACLs for configuring access to the socket +of a shim. + +Ported from Michael Crosby's similar patch for v2 shims. + +Signed-off-by: Samuel Karp + +----------------------------------------------------------------------- + +Co-authored-by: Paulo Flabiano Smorigo +Co-authored-by: varsha teratipally +Signed-off-by: Tianon Gravi +--- + cmd/containerd-shim/main_unix.go | 16 +++-- + cmd/ctr/commands/shim/shim.go | 12 +++- + container_test.go | 58 +++++++++++++++ + runtime/v1/linux/bundle.go | 37 +++++++++- + runtime/v1/shim/client/client.go | 117 ++++++++++++++++++++++++++++--- + runtime/v2/runc/service.go | 18 +++-- + runtime/v2/shim/shim.go | 7 +- + runtime/v2/shim/shim_unix.go | 8 +-- + runtime/v2/shim/util.go | 20 ++++++ + runtime/v2/shim/util_unix.go | 97 ++++++++++++++++++++++--- + runtime/v2/shim/util_windows.go | 6 ++ + 11 files changed, 358 insertions(+), 38 deletions(-) + +diff --git a/containerd/cmd/containerd-shim/main_unix.go b/containerd/cmd/containerd-shim/main_unix.go +index eb34ee7d19..66e0eda257 100644 +--- a/containerd/cmd/containerd-shim/main_unix.go ++++ b/containerd/cmd/containerd-shim/main_unix.go +@@ -65,7 +65,7 @@ var ( + func init() { + flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs") + flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim") +- flag.StringVar(&socketFlag, "socket", "", "abstract socket path to serve") ++ flag.StringVar(&socketFlag, "socket", "", "socket path to serve") + flag.StringVar(&addressFlag, "address", "", "grpc address back to main containerd") + flag.StringVar(&workdirFlag, "workdir", "", "path used to storge large temporary data") + flag.StringVar(&runtimeRootFlag, "runtime-root", proc.RuncRoot, "root directory for the runtime") +@@ -196,10 +196,18 @@ func serve(ctx context.Context, server *ttrpc.Server, path string) error { + l, err = net.FileListener(os.NewFile(3, "socket")) + path = "[inherited from parent]" + } else { +- if len(path) > 106 { +- return errors.Errorf("%q: unix socket path too long (> 106)", path) ++ const ( ++ abstractSocketPrefix = "\x00" ++ socketPathLimit = 106 ++ ) ++ p := strings.TrimPrefix(path, "unix://") ++ if len(p) == len(path) { ++ p = abstractSocketPrefix + p + } +- l, err = net.Listen("unix", "\x00"+path) ++ if len(p) > socketPathLimit { ++ return errors.Errorf("%q: unix socket path too long (> %d)", p, socketPathLimit) ++ } ++ l, err = net.Listen("unix", p) + } + if err != nil { + return err +diff --git a/containerd/cmd/ctr/commands/shim/shim.go b/containerd/cmd/ctr/commands/shim/shim.go +index ec08cc68bb..3dbb8b062f 100644 +--- a/containerd/cmd/ctr/commands/shim/shim.go ++++ b/containerd/cmd/ctr/commands/shim/shim.go +@@ -231,7 +231,7 @@ func getTaskService(context *cli.Context) (task.TaskService, error) { + return nil, errors.New("socket path must be specified") + } + +- conn, err := net.Dial("unix", "\x00"+bindSocket) ++ conn, err := connectToAddress(bindSocket) + if err != nil { + return nil, err + } +@@ -243,3 +243,13 @@ func getTaskService(context *cli.Context) (task.TaskService, error) { + + return task.NewTaskClient(client), nil + } ++ ++// as we changed the socket address from abstract, we need to have a backward ++// compatibility to handle the abstract sockets as well. ++func connectToAddress(address string) (net.Conn, error) { ++ conn, err := net.Dial("unix", address) ++ if err != nil { ++ return net.Dial("unix", "\x00"+address) ++ } ++ return conn, err ++} +diff --git a/containerd/container_test.go b/containerd/container_test.go +index aa04a31ad3..2e825acddc 100644 +--- a/containerd/container_test.go ++++ b/containerd/container_test.go +@@ -32,7 +32,9 @@ import ( + // Register the typeurl + "github.com/containerd/containerd/cio" + "github.com/containerd/containerd/containers" ++ "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/oci" ++ "github.com/containerd/containerd/platforms" + _ "github.com/containerd/containerd/runtime" + "github.com/containerd/typeurl" + specs "github.com/opencontainers/runtime-spec/specs-go" +@@ -1577,3 +1579,59 @@ func TestShortRunningTaskPid(t *testing.T) { + } + defer task.Delete(ctx, WithProcessKill) + } ++ ++func TestShimSockLength(t *testing.T) { ++ t.Parallel() ++ ++ // Max length of namespace should be 76 ++ namespace := strings.Repeat("n", 76) ++ ++ ctx, cancel := context.WithCancel(context.Background()) ++ defer cancel() ++ ++ ctx = namespaces.WithNamespace(ctx, namespace) ++ ++ client, err := newClient(t, address) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer client.Close() ++ ++ image, err := client.Pull(ctx, testImage, ++ WithPlatformMatcher(platforms.Default()), ++ WithPullUnpack, ++ ) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ id := strings.Repeat("c", 64) ++ ++ // We don't have limitation with length of container name, ++ // but 64 bytes of sha256 is the common case ++ container, err := client.NewContainer(ctx, id, ++ WithNewSnapshot(id, image), ++ WithNewSpec(oci.WithImageConfig(image), withExitStatus(0)), ++ ) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer container.Delete(ctx, WithSnapshotCleanup) ++ ++ task, err := container.NewTask(ctx, empty()) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer task.Delete(ctx) ++ ++ statusC, err := task.Wait(ctx) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ if err := task.Start(ctx); err != nil { ++ t.Fatal(err) ++ } ++ ++ <-statusC ++} +diff --git a/containerd/runtime/v1/linux/bundle.go b/containerd/runtime/v1/linux/bundle.go +index d73866a2fd..84c06f2ab4 100644 +--- a/containerd/runtime/v1/linux/bundle.go ++++ b/containerd/runtime/v1/linux/bundle.go +@@ -20,6 +20,8 @@ package linux + + import ( + "context" ++ "crypto/sha256" ++ "fmt" + "io/ioutil" + "os" + "path/filepath" +@@ -88,7 +90,7 @@ func ShimRemote(c *Config, daemonAddress, cgroup string, exitHandler func()) Shi + return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { + config := b.shimConfig(ns, c, ropts) + return config, +- client.WithStart(c.Shim, b.shimAddress(ns), daemonAddress, cgroup, c.ShimDebug, exitHandler) ++ client.WithStart(c.Shim, b.shimAddress(ns, daemonAddress), daemonAddress, cgroup, c.ShimDebug, exitHandler) + } + } + +@@ -102,7 +104,7 @@ func ShimLocal(c *Config, exchange *exchange.Exchange) ShimOpt { + // ShimConnect is a ShimOpt for connecting to an existing remote shim + func ShimConnect(c *Config, onClose func()) ShimOpt { + return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { +- return b.shimConfig(ns, c, ropts), client.WithConnect(b.shimAddress(ns), onClose) ++ return b.shimConfig(ns, c, ropts), client.WithConnect(b.decideShimAddress(ns), onClose) + } + } + +@@ -114,6 +116,11 @@ func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientO + + // Delete deletes the bundle from disk + func (b *bundle) Delete() error { ++ address, _ := b.loadAddress() ++ if address != "" { ++ // we don't care about errors here ++ client.RemoveSocket(address) ++ } + err := os.RemoveAll(b.path) + if err == nil { + return os.RemoveAll(b.workDir) +@@ -126,10 +133,34 @@ func (b *bundle) Delete() error { + return errors.Wrapf(err, "Failed to remove both bundle and workdir locations: %v", err2) + } + +-func (b *bundle) shimAddress(namespace string) string { ++func (b *bundle) legacyShimAddress(namespace string) string { + return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock") + } + ++const socketRoot = "/run/containerd" ++ ++func (b *bundle) shimAddress(namespace, socketPath string) string { ++ d := sha256.Sum256([]byte(filepath.Join(socketPath, namespace, b.id))) ++ return fmt.Sprintf("unix://%s/%x", filepath.Join(socketRoot, "s"), d) ++} ++ ++func (b *bundle) loadAddress() (string, error) { ++ addressPath := filepath.Join(b.path, "address") ++ data, err := ioutil.ReadFile(addressPath) ++ if err != nil { ++ return "", err ++ } ++ return string(data), nil ++} ++ ++func (b *bundle) decideShimAddress(namespace string) string { ++ address, err := b.loadAddress() ++ if err != nil { ++ return b.legacyShimAddress(namespace) ++ } ++ return address ++} ++ + func (b *bundle) shimConfig(namespace string, c *Config, runcOptions *runctypes.RuncOptions) shim.Config { + var ( + criuPath string +diff --git a/containerd/runtime/v1/shim/client/client.go b/containerd/runtime/v1/shim/client/client.go +index 880dd74985..3238ac3e8c 100644 +--- a/containerd/runtime/v1/shim/client/client.go ++++ b/containerd/runtime/v1/shim/client/client.go +@@ -20,10 +20,12 @@ package client + + import ( + "context" ++ "fmt" + "io" + "net" + "os" + "os/exec" ++ "path/filepath" + "strings" + "sync" + "syscall" +@@ -54,9 +56,17 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa + return func(ctx context.Context, config shim.Config) (_ shimapi.ShimService, _ io.Closer, err error) { + socket, err := newSocket(address) + if err != nil { +- return nil, nil, err ++ if !eaddrinuse(err) { ++ return nil, nil, err ++ } ++ if err := RemoveSocket(address); err != nil { ++ return nil, nil, errors.Wrap(err, "remove already used socket") ++ } ++ if socket, err = newSocket(address); err != nil { ++ return nil, nil, err ++ } + } +- defer socket.Close() ++ + f, err := socket.File() + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to get fd for socket %s", address) +@@ -101,12 +111,18 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa + go func() { + cmd.Wait() + exitHandler() ++ socket.Close() ++ RemoveSocket(address) + }() + log.G(ctx).WithFields(logrus.Fields{ + "pid": cmd.Process.Pid, + "address": address, + "debug": debug, + }).Infof("shim %s started", binary) ++ ++ if err := writeAddress(filepath.Join(config.Path, "address"), address); err != nil { ++ return nil, nil, err ++ } + // set shim in cgroup if it is provided + if cgroup != "" { + if err := setCgroup(cgroup, cmd); err != nil { +@@ -128,6 +144,26 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa + } + } + ++func eaddrinuse(err error) bool { ++ cause := errors.Cause(err) ++ netErr, ok := cause.(*net.OpError) ++ if !ok { ++ return false ++ } ++ if netErr.Op != "listen" { ++ return false ++ } ++ syscallErr, ok := netErr.Err.(*os.SyscallError) ++ if !ok { ++ return false ++ } ++ errno, ok := syscallErr.Err.(syscall.Errno) ++ if !ok { ++ return false ++ } ++ return errno == syscall.EADDRINUSE ++} ++ + func newCommand(binary, daemonAddress string, debug bool, config shim.Config, socket *os.File) (*exec.Cmd, error) { + selfExe, err := os.Executable() + if err != nil { +@@ -166,31 +202,92 @@ func newCommand(binary, daemonAddress string, debug bool, config shim.Config, so + return cmd, nil + } + ++// writeAddress writes a address file atomically ++func writeAddress(path, address string) error { ++ path, err := filepath.Abs(path) ++ if err != nil { ++ return err ++ } ++ tempPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path))) ++ f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) ++ if err != nil { ++ return err ++ } ++ _, err = f.WriteString(address) ++ f.Close() ++ if err != nil { ++ return err ++ } ++ return os.Rename(tempPath, path) ++} ++ ++const ( ++ abstractSocketPrefix = "\x00" ++ socketPathLimit = 106 ++) ++ ++type socket string ++ ++func (s socket) isAbstract() bool { ++ return !strings.HasPrefix(string(s), "unix://") ++} ++ ++func (s socket) path() string { ++ path := strings.TrimPrefix(string(s), "unix://") ++ // if there was no trim performed, we assume an abstract socket ++ if len(path) == len(s) { ++ path = abstractSocketPrefix + path ++ } ++ return path ++} ++ + func newSocket(address string) (*net.UnixListener, error) { +- if len(address) > 106 { +- return nil, errors.Errorf("%q: unix socket path too long (> 106)", address) ++ if len(address) > socketPathLimit { ++ return nil, errors.Errorf("%q: unix socket path too long (> %d)", address, socketPathLimit) ++ } ++ var ( ++ sock = socket(address) ++ path = sock.path() ++ ) ++ if !sock.isAbstract() { ++ if err := os.MkdirAll(filepath.Dir(path), 0600); err != nil { ++ return nil, errors.Wrapf(err, "%s", path) ++ } + } +- l, err := net.Listen("unix", "\x00"+address) ++ l, err := net.Listen("unix", path) + if err != nil { +- return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", address) ++ return nil, errors.Wrapf(err, "failed to listen to unix socket %q (abstract: %t)", address, sock.isAbstract()) ++ } ++ if err := os.Chmod(path, 0600); err != nil { ++ l.Close() ++ return nil, err + } + + return l.(*net.UnixListener), nil + } + ++// RemoveSocket removes the socket at the specified address if ++// it exists on the filesystem ++func RemoveSocket(address string) error { ++ sock := socket(address) ++ if !sock.isAbstract() { ++ return os.Remove(sock.path()) ++ } ++ return nil ++} ++ + func connect(address string, d func(string, time.Duration) (net.Conn, error)) (net.Conn, error) { + return d(address, 100*time.Second) + } + +-func annonDialer(address string, timeout time.Duration) (net.Conn, error) { +- address = strings.TrimPrefix(address, "unix://") +- return net.DialTimeout("unix", "\x00"+address, timeout) ++func anonDialer(address string, timeout time.Duration) (net.Conn, error) { ++ return net.DialTimeout("unix", socket(address).path(), timeout) + } + + // WithConnect connects to an existing shim + func WithConnect(address string, onClose func()) Opt { + return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) { +- conn, err := connect(address, annonDialer) ++ conn, err := connect(address, anonDialer) + if err != nil { + return nil, nil, err + } +diff --git a/containerd/runtime/v2/runc/service.go b/containerd/runtime/v2/runc/service.go +index 347216ec76..675af57dad 100644 +--- a/containerd/runtime/v2/runc/service.go ++++ b/containerd/runtime/v2/runc/service.go +@@ -143,20 +143,26 @@ func (s *service) StartShim(ctx context.Context, id, containerdBinary, container + if err != nil { + return "", err + } +- address, err := shim.SocketAddress(ctx, id) ++ address, err := shim.SocketAddress(ctx, containerdAddress, id) + if err != nil { + return "", err + } + socket, err := shim.NewSocket(address) + if err != nil { +- return "", err ++ if !shim.SocketEaddrinuse(err) { ++ return "", err ++ } ++ if err := shim.RemoveSocket(address); err != nil { ++ return "", errors.Wrap(err, "remove already used socket") ++ } ++ if socket, err = shim.NewSocket(address); err != nil { ++ return "", err ++ } + } +- defer socket.Close() + f, err := socket.File() + if err != nil { + return "", err + } +- defer f.Close() + + cmd.ExtraFiles = append(cmd.ExtraFiles, f) + +@@ -165,6 +171,7 @@ func (s *service) StartShim(ctx context.Context, id, containerdBinary, container + } + defer func() { + if err != nil { ++ _ = shim.RemoveSocket(address) + cmd.Process.Kill() + } + }() +@@ -582,6 +589,9 @@ func (s *service) Connect(ctx context.Context, r *taskAPI.ConnectRequest) (*task + + func (s *service) Shutdown(ctx context.Context, r *taskAPI.ShutdownRequest) (*ptypes.Empty, error) { + s.cancel() ++ if address, err := shim.ReadAddress("address"); err == nil { ++ _ = shim.RemoveSocket(address) ++ } + os.Exit(0) + return empty, nil + } +diff --git a/containerd/runtime/v2/shim/shim.go b/containerd/runtime/v2/shim/shim.go +index 39484c1912..b5fb3ff6d0 100644 +--- a/containerd/runtime/v2/shim/shim.go ++++ b/containerd/runtime/v2/shim/shim.go +@@ -77,7 +77,7 @@ func parseFlags() { + flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs") + flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim") + flag.StringVar(&idFlag, "id", "", "id of the task") +- flag.StringVar(&socketFlag, "socket", "", "abstract socket path to serve") ++ flag.StringVar(&socketFlag, "socket", "", "socket path to serve") + flag.StringVar(&bundlePath, "bundle", "", "path to the bundle if not workdir") + + flag.StringVar(&addressFlag, "address", "", "grpc address back to main containerd") +@@ -239,11 +239,14 @@ func serve(ctx context.Context, server *ttrpc.Server, path string) error { + return err + } + go func() { +- defer l.Close() + if err := server.Serve(ctx, l); err != nil && + !strings.Contains(err.Error(), "use of closed network connection") { + logrus.WithError(err).Fatal("containerd-shim: ttrpc server failure") + } ++ l.Close() ++ if address, err := ReadAddress("address"); err == nil { ++ _ = RemoveSocket(address) ++ } + }() + return nil + } +diff --git a/containerd/runtime/v2/shim/shim_unix.go b/containerd/runtime/v2/shim/shim_unix.go +index 1a54821637..0d5694331d 100644 +--- a/containerd/runtime/v2/shim/shim_unix.go ++++ b/containerd/runtime/v2/shim/shim_unix.go +@@ -59,15 +59,15 @@ func serveListener(path string) (net.Listener, error) { + l, err = net.FileListener(os.NewFile(3, "socket")) + path = "[inherited from parent]" + } else { +- if len(path) > 106 { +- return nil, errors.Errorf("%q: unix socket path too long (> 106)", path) ++ if len(path) > socketPathLimit { ++ return nil, errors.Errorf("%q: unix socket path too long (> %d)", path, socketPathLimit) + } +- l, err = net.Listen("unix", "\x00"+path) ++ l, err = net.Listen("unix", path) + } + if err != nil { + return nil, err + } +- logrus.WithField("socket", path).Debug("serving api on abstract socket") ++ logrus.WithField("socket", path).Debug("serving api on socket") + return l, nil + } + +diff --git a/containerd/runtime/v2/shim/util.go b/containerd/runtime/v2/shim/util.go +index b7034ce50b..a5da124f9c 100644 +--- a/containerd/runtime/v2/shim/util.go ++++ b/containerd/runtime/v2/shim/util.go +@@ -19,6 +19,7 @@ package shim + import ( + "context" + "fmt" ++ "io/ioutil" + "net" + "os" + "os/exec" +@@ -126,3 +127,22 @@ func WriteAddress(path, address string) error { + } + return os.Rename(tempPath, path) + } ++ ++// ErrNoAddress is returned when the address file has no content ++var ErrNoAddress = errors.New("no shim address") ++ ++// ReadAddress returns the shim's socket address from the path ++func ReadAddress(path string) (string, error) { ++ path, err := filepath.Abs(path) ++ if err != nil { ++ return "", err ++ } ++ data, err := ioutil.ReadFile(path) ++ if err != nil { ++ return "", err ++ } ++ if len(data) == 0 { ++ return "", ErrNoAddress ++ } ++ return string(data), nil ++} +diff --git a/containerd/runtime/v2/shim/util_unix.go b/containerd/runtime/v2/shim/util_unix.go +index 262fe2b363..d8a57a1da8 100644 +--- a/containerd/runtime/v2/shim/util_unix.go ++++ b/containerd/runtime/v2/shim/util_unix.go +@@ -20,7 +20,10 @@ package shim + + import ( + "context" ++ "crypto/sha256" ++ "fmt" + "net" ++ "os" + "path/filepath" + "strings" + "syscall" +@@ -31,6 +34,8 @@ import ( + "github.com/pkg/errors" + ) + ++const socketPathLimit = 106 ++ + func getSysProcAttr() *syscall.SysProcAttr { + return &syscall.SysProcAttr{ + Setpgid: true, +@@ -42,29 +47,101 @@ func SetScore(pid int) error { + return sys.SetOOMScore(pid, sys.OOMScoreMaxKillable) + } + +-// SocketAddress returns an abstract socket address +-func SocketAddress(ctx context.Context, id string) (string, error) { ++const socketRoot = "/run/containerd" ++ ++// SocketAddress returns a socket address ++func SocketAddress(ctx context.Context, socketPath, id string) (string, error) { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return "", err + } +- return filepath.Join(string(filepath.Separator), "containerd-shim", ns, id, "shim.sock"), nil ++ d := sha256.Sum256([]byte(filepath.Join(socketPath, ns, id))) ++ return fmt.Sprintf("unix://%s/%x", filepath.Join(socketRoot, "s"), d), nil + } + +-// AnonDialer returns a dialer for an abstract socket ++// AnonDialer returns a dialer for a socket + func AnonDialer(address string, timeout time.Duration) (net.Conn, error) { +- address = strings.TrimPrefix(address, "unix://") +- return net.DialTimeout("unix", "\x00"+address, timeout) ++ return net.DialTimeout("unix", socket(address).path(), timeout) + } + + // NewSocket returns a new socket + func NewSocket(address string) (*net.UnixListener, error) { +- if len(address) > 106 { +- return nil, errors.Errorf("%q: unix socket path too long (> 106)", address) ++ var ( ++ sock = socket(address) ++ path = sock.path() ++ ) ++ if !sock.isAbstract() { ++ if err := os.MkdirAll(filepath.Dir(path), 0600); err != nil { ++ return nil, errors.Wrapf(err, "%s", path) ++ } + } +- l, err := net.Listen("unix", "\x00"+address) ++ l, err := net.Listen("unix", path) + if err != nil { +- return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", address) ++ return nil, err ++ } ++ if err := os.Chmod(path, 0600); err != nil { ++ os.Remove(sock.path()) ++ l.Close() ++ return nil, err + } + return l.(*net.UnixListener), nil + } ++ ++const abstractSocketPrefix = "\x00" ++ ++type socket string ++ ++func (s socket) isAbstract() bool { ++ return !strings.HasPrefix(string(s), "unix://") ++} ++ ++func (s socket) path() string { ++ path := strings.TrimPrefix(string(s), "unix://") ++ // if there was no trim performed, we assume an abstract socket ++ if len(path) == len(s) { ++ path = abstractSocketPrefix + path ++ } ++ return path ++} ++ ++// RemoveSocket removes the socket at the specified address if ++// it exists on the filesystem ++func RemoveSocket(address string) error { ++ sock := socket(address) ++ if !sock.isAbstract() { ++ return os.Remove(sock.path()) ++ } ++ return nil ++} ++ ++// SocketEaddrinuse returns true if the provided error is caused by the ++// EADDRINUSE error number ++func SocketEaddrinuse(err error) bool { ++ netErr, ok := err.(*net.OpError) ++ if !ok { ++ return false ++ } ++ if netErr.Op != "listen" { ++ return false ++ } ++ syscallErr, ok := netErr.Err.(*os.SyscallError) ++ if !ok { ++ return false ++ } ++ errno, ok := syscallErr.Err.(syscall.Errno) ++ if !ok { ++ return false ++ } ++ return errno == syscall.EADDRINUSE ++} ++ ++// CanConnect returns true if the socket provided at the address ++// is accepting new connections ++func CanConnect(address string) bool { ++ conn, err := AnonDialer(address, 100*time.Millisecond) ++ if err != nil { ++ return false ++ } ++ conn.Close() ++ return true ++} +diff --git a/containerd/runtime/v2/shim/util_windows.go b/containerd/runtime/v2/shim/util_windows.go +index 594a0f75b3..4562119738 100644 +--- a/containerd/runtime/v2/shim/util_windows.go ++++ b/containerd/runtime/v2/shim/util_windows.go +@@ -88,3 +88,9 @@ func NewSocket(address string) (net.Listener, error) { + } + return l, nil + } ++ ++// RemoveSocket removes the socket at the specified address if ++// it exists on the filesystem ++func RemoveSocket(address string) error { ++ return nil ++} diff -Nru docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-1.patch docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-1.patch --- docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-1.patch 2021-02-21 17:06:17.000000000 +0000 @@ -0,0 +1,103 @@ +From 1342c51d5e809d2994e6f7e490c8d2b3b12c28ae Mon Sep 17 00:00:00 2001 +From: Brian Goff +Date: Tue, 6 Oct 2020 19:30:07 +0000 +Subject: [PATCH] Ensure MkdirAllAndChown also sets perms + +Generally if we ever need to change perms of a dir, between versions, +this ensures the permissions actually change when we think it should +change without having to handle special cases if it already existed. + +Signed-off-by: Brian Goff +(cherry picked from commit edb62a3ace8c4303822a391b38231e577f8c2ee8) +Signed-off-by: Tibor Vass +--- + pkg/idtools/idtools.go | 11 ++++++++--- + pkg/idtools/idtools_unix.go | 14 ++++++++++---- + 2 files changed, 18 insertions(+), 7 deletions(-) + +diff --git a/engine/pkg/idtools/idtools.go b/engine/pkg/idtools/idtools.go +index b3af7a4226e6..bb147b7e0e86 100644 +--- a/engine/pkg/idtools/idtools.go ++++ b/engine/pkg/idtools/idtools.go +@@ -35,13 +35,13 @@ const ( + + // MkdirAllAndChown creates a directory (include any along the path) and then modifies + // ownership to the requested uid/gid. If the directory already exists, this +-// function will still change ownership to the requested uid/gid pair. ++// function will still change ownership and permissions. + func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error { + return mkdirAs(path, mode, owner, true, true) + } + + // MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid. +-// If the directory already exists, this function still changes ownership. ++// If the directory already exists, this function still changes ownership and permissions. + // Note that unlike os.Mkdir(), this function does not return IsExist error + // in case path already exists. + func MkdirAndChown(path string, mode os.FileMode, owner Identity) error { +@@ -50,7 +50,7 @@ func MkdirAndChown(path string, mode os.FileMode, owner Identity) error { + + // MkdirAllAndChownNew creates a directory (include any along the path) and then modifies + // ownership ONLY of newly created directories to the requested uid/gid. If the +-// directories along the path exist, no change of ownership will be performed ++// directories along the path exist, no change of ownership or permissions will be performed + func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error { + return mkdirAs(path, mode, owner, true, false) + } +@@ -262,3 +262,8 @@ func parseSubidFile(path, username string) (ranges, error) { + } + return rangeList, nil + } ++ ++// CurrentIdentity returns the identity of the current process ++func CurrentIdentity() Identity { ++ return Identity{UID: os.Getuid(), GID: os.Getegid()} ++} +diff --git a/engine/pkg/idtools/idtools_unix.go b/engine/pkg/idtools/idtools_unix.go +index fb239743a01a..329d5d04edcc 100644 +--- a/engine/pkg/idtools/idtools_unix.go ++++ b/engine/pkg/idtools/idtools_unix.go +@@ -39,7 +39,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting + } + + // short-circuit--we were called with an existing directory and chown was requested +- return lazyChown(path, owner.UID, owner.GID, stat) ++ return setPermissions(path, mode, owner.UID, owner.GID, stat) + } + + if os.IsNotExist(err) { +@@ -70,7 +70,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting + // even if it existed, we will chown the requested path + any subpaths that + // didn't exist when we called MkdirAll + for _, pathComponent := range paths { +- if err := lazyChown(pathComponent, owner.UID, owner.GID, nil); err != nil { ++ if err := setPermissions(pathComponent, mode, owner.UID, owner.GID, nil); err != nil { + return err + } + } +@@ -213,10 +213,11 @@ func callGetent(args string) (io.Reader, error) { + return bytes.NewReader(out), nil + } + +-// lazyChown performs a chown only if the uid/gid don't match what's requested ++// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested + // Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the + // dir is on an NFS share, so don't call chown unless we absolutely must. +-func lazyChown(p string, uid, gid int, stat *system.StatT) error { ++// Likewise for setting permissions. ++func setPermissions(p string, mode os.FileMode, uid, gid int, stat *system.StatT) error { + if stat == nil { + var err error + stat, err = system.Stat(p) +@@ -224,6 +225,11 @@ func lazyChown(p string, uid, gid int, stat *system.StatT) error { + return err + } + } ++ if os.FileMode(stat.Mode()).Perm() != mode.Perm() { ++ if err := os.Chmod(p, mode.Perm()); err != nil { ++ return err ++ } ++ } + if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) { + return nil + } diff -Nru docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-2.patch docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-2.patch --- docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-2.patch 2021-02-21 17:06:17.000000000 +0000 @@ -0,0 +1,52 @@ +From 5eff67a2c294b7e72607e0949ebc0de21710e4d3 Mon Sep 17 00:00:00 2001 +From: Brian Goff +Date: Tue, 6 Oct 2020 19:40:30 +0000 +Subject: [PATCH] Do not set DOCKER_TMP to be owned by remapped root + +The remapped root does not need access to this dir. +Having this owned by the remapped root opens the host up to an +uprivileged user on the host being able to escalate privileges. + +While it would not be normal for the remapped UID to be used outside of +the container context, it could happen. + +Signed-off-by: Brian Goff +(cherry picked from commit bfedd2725971303efb7a2fe5d6990317b381622f) +Signed-off-by: Tibor Vass +--- + daemon/daemon.go | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/engine/daemon/daemon.go b/engine/daemon/daemon.go +index acc619a6af14..24205cd1dc3d 100644 +--- a/engine/daemon/daemon.go ++++ b/engine/daemon/daemon.go +@@ -748,7 +748,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S + } + + // set up the tmpDir to use a canonical path +- tmp, err := prepareTempDir(config.Root, rootIDs) ++ tmp, err := prepareTempDir(config.Root) + if err != nil { + return nil, fmt.Errorf("Unable to get the TempDir under %s: %s", config.Root, err) + } +@@ -1289,7 +1289,7 @@ func (daemon *Daemon) Subnets() ([]net.IPNet, []net.IPNet) { + // prepareTempDir prepares and returns the default directory to use + // for temporary files. + // If it doesn't exist, it is created. If it exists, its content is removed. +-func prepareTempDir(rootDir string, rootIdentity idtools.Identity) (string, error) { ++func prepareTempDir(rootDir string) (string, error) { + var tmpDir string + if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" { + tmpDir = filepath.Join(rootDir, "tmp") +@@ -1307,9 +1307,7 @@ func prepareTempDir(rootDir string, rootIdentity idtools.Identity) (string, erro + } + } + } +- // We don't remove the content of tmpdir if it's not the default, +- // it may hold things that do not belong to us. +- return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, rootIdentity) ++ return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, idtools.CurrentIdentity()) + } + + func (daemon *Daemon) setGenericResources(conf *config.Config) error { diff -Nru docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-3.patch docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-3.patch --- docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21284-3.patch 2021-02-21 17:06:17.000000000 +0000 @@ -0,0 +1,347 @@ +From 67de83e70bca92ae6a08e28a03b3fc8fcca9f3f1 Mon Sep 17 00:00:00 2001 +From: Brian Goff +Date: Tue, 6 Oct 2020 19:43:24 +0000 +Subject: [PATCH] Use real root with 0701 perms + +Various dirs in /var/lib/docker contain data that needs to be mounted +into a container. For this reason, these dirs are set to be owned by the +remapped root user, otherwise there can be permissions issues. +However, this uneccessarily exposes these dirs to an unprivileged user +on the host. + +Instead, set the ownership of these dirs to the real root (or rather the +UID/GID of dockerd) with 0701 permissions, which allows the remapped +root to enter the directories but not read/write to them. +The remapped root needs to enter these dirs so the container's rootfs +can be configured... e.g. to mount /etc/resolve.conf. + +This prevents an unprivileged user from having read/write access to +these dirs on the host. +The flip side of this is now any user can enter these directories. + +Signed-off-by: Brian Goff +(cherry picked from commit e908cc39018c015084ffbffbc5703ccba5c2fbb7) + +Cherry-pick conflict with eb14d936bfc296f0a85bf4dc9e9bb1f4b4a01282: +Kept old `container` variable name. +Signed-off-by: Tibor Vass +--- + daemon/container_operations_unix.go | 2 +- + daemon/create.go | 6 ++---- + daemon/daemon.go | 2 +- + daemon/daemon_unix.go | 14 ++++++++++---- + daemon/graphdriver/aufs/aufs.go | 9 +++------ + daemon/graphdriver/btrfs/btrfs.go | 10 +++------- + daemon/graphdriver/overlay/overlay.go | 16 +++++++--------- + daemon/graphdriver/overlay2/overlay.go | 12 ++++-------- + daemon/graphdriver/vfs/driver.go | 5 ++--- + daemon/graphdriver/zfs/zfs.go | 6 +----- + volume/local/local.go | 11 +++++++++-- + 11 files changed, 43 insertions(+), 50 deletions(-) + +diff --git a/engine/daemon/container_operations_unix.go b/engine/daemon/container_operations_unix.go +index 3fcdc1913bed..ad8cb4c83aa7 100644 +--- a/engine/daemon/container_operations_unix.go ++++ b/engine/daemon/container_operations_unix.go +@@ -411,5 +411,5 @@ func (daemon *Daemon) setupContainerMountsRoot(c *container.Container) error { + if err != nil { + return err + } +- return idtools.MkdirAllAndChown(p, 0700, daemon.idMapping.RootPair()) ++ return idtools.MkdirAllAndChown(p, 0701, idtools.CurrentIdentity()) + } +diff --git a/engine/daemon/create.go b/engine/daemon/create.go +index f9db0ca83454..9f8590b860d1 100644 +--- a/engine/daemon/create.go ++++ b/engine/daemon/create.go +@@ -194,12 +194,10 @@ func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr + } + container.RWLayer = rwLayer + +- rootIDs := daemon.idMapping.RootPair() +- +- if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil { ++ if err := idtools.MkdirAndChown(container.Root, 0701, idtools.CurrentIdentity()); err != nil { + return nil, err + } +- if err := idtools.MkdirAndChown(container.CheckpointDir(), 0700, rootIDs); err != nil { ++ if err := idtools.MkdirAndChown(container.CheckpointDir(), 0700, idtools.CurrentIdentity()); err != nil { + return nil, err + } + +diff --git a/engine/daemon/daemon.go b/engine/daemon/daemon.go +index 24205cd1dc3d..e75cee6c8c18 100644 +--- a/engine/daemon/daemon.go ++++ b/engine/daemon/daemon.go +@@ -813,7 +813,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S + } + + daemonRepo := filepath.Join(config.Root, "containers") +- if err := idtools.MkdirAllAndChown(daemonRepo, 0700, rootIDs); err != nil { ++ if err := idtools.MkdirAllAndChown(daemonRepo, 0701, idtools.CurrentIdentity()); err != nil { + return nil, err + } + +diff --git a/engine/daemon/daemon_unix.go b/engine/daemon/daemon_unix.go +index a14f029c92cc..44dd67a61dab 100644 +--- a/engine/daemon/daemon_unix.go ++++ b/engine/daemon/daemon_unix.go +@@ -1206,7 +1206,7 @@ func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) + return &idtools.IdentityMapping{}, nil + } + +-func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) error { ++func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools.Identity) error { + config.Root = rootDir + // the docker root metadata directory needs to have execute permissions for all users (g+x,o+x) + // so that syscalls executing as non-root, operating on subdirectories of the graph root +@@ -1231,10 +1231,16 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools + // a new subdirectory with ownership set to the remapped uid/gid (so as to allow + // `chdir()` to work for containers namespaced to that uid/gid) + if config.RemappedRoot != "" { +- config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIdentity.UID, rootIdentity.GID)) ++ id := idtools.CurrentIdentity() ++ // First make sure the current root dir has the correct perms. ++ if err := idtools.MkdirAllAndChown(config.Root, 0701, id); err != nil { ++ return errors.Wrapf(err, "could not create or set daemon root permissions: %s", config.Root) ++ } ++ ++ config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", remappedRoot.UID, remappedRoot.GID)) + logrus.Debugf("Creating user namespaced daemon root: %s", config.Root) + // Create the root directory if it doesn't exist +- if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIdentity); err != nil { ++ if err := idtools.MkdirAllAndChown(config.Root, 0701, id); err != nil { + return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err) + } + // we also need to verify that any pre-existing directories in the path to +@@ -1247,7 +1253,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools + if dirPath == "/" { + break + } +- if !idtools.CanAccess(dirPath, rootIdentity) { ++ if !idtools.CanAccess(dirPath, remappedRoot) { + return fmt.Errorf("a subdirectory in your graphroot path (%s) restricts access to the remapped root uid/gid; please fix by allowing 'o+x' permissions on existing directories", config.Root) + } + } +diff --git a/engine/daemon/graphdriver/aufs/aufs.go b/engine/daemon/graphdriver/aufs/aufs.go +index bbd19a82b000..086e75b97866 100644 +--- a/engine/daemon/graphdriver/aufs/aufs.go ++++ b/engine/daemon/graphdriver/aufs/aufs.go +@@ -130,18 +130,15 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap + locker: locker.New(), + } + +- rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) +- if err != nil { +- return nil, err +- } ++ currentID := idtools.CurrentIdentity() + // Create the root aufs driver dir +- if err := idtools.MkdirAllAndChown(root, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { ++ if err := idtools.MkdirAllAndChown(root, 0701, currentID); err != nil { + return nil, err + } + + // Populate the dir structure + for _, p := range paths { +- if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { ++ if err := idtools.MkdirAllAndChown(path.Join(root, p), 0701, currentID); err != nil { + return nil, err + } + } +diff --git a/engine/daemon/graphdriver/btrfs/btrfs.go b/engine/daemon/graphdriver/btrfs/btrfs.go +index fcaedc6eab18..b1b287904c5e 100644 +--- a/engine/daemon/graphdriver/btrfs/btrfs.go ++++ b/engine/daemon/graphdriver/btrfs/btrfs.go +@@ -70,11 +70,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap + return nil, graphdriver.ErrPrerequisites + } + +- rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) +- if err != nil { +- return nil, err +- } +- if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { ++ if err := idtools.MkdirAllAndChown(home, 0701, idtools.CurrentIdentity()); err != nil { + return nil, err + } + +@@ -525,7 +521,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { + if err != nil { + return err + } +- if err := idtools.MkdirAllAndChown(subvolumes, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { ++ if err := idtools.MkdirAllAndChown(subvolumes, 0701, idtools.CurrentIdentity()); err != nil { + return err + } + if parent == "" { +@@ -560,7 +556,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { + if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil { + return err + } +- if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { ++ if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.CurrentIdentity()); err != nil { + return err + } + if err := ioutil.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil { +diff --git a/engine/daemon/graphdriver/overlay/overlay.go b/engine/daemon/graphdriver/overlay/overlay.go +index e837a595e17c..3edffd252bd4 100644 +--- a/engine/daemon/graphdriver/overlay/overlay.go ++++ b/engine/daemon/graphdriver/overlay/overlay.go +@@ -156,12 +156,8 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap + logrus.WithField("storage-driver", "overlay").Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs)) + } + +- rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) +- if err != nil { +- return nil, err +- } + // Create the driver home dir +- if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { ++ if err := idtools.MkdirAllAndChown(home, 0701, idtools.CurrentIdentity()); err != nil { + return nil, err + } + +@@ -265,10 +261,11 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr + } + root := idtools.Identity{UID: rootUID, GID: rootGID} + +- if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { ++ currentID := idtools.CurrentIdentity() ++ if err := idtools.MkdirAllAndChown(path.Dir(dir), 0701, currentID); err != nil { + return err + } +- if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { ++ if err := idtools.MkdirAndChown(dir, 0701, currentID); err != nil { + return err + } + +@@ -281,6 +278,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr + + // Toplevel images are just a "root" dir + if parent == "" { ++ // This must be 0755 otherwise unprivileged users will in the container will not be able to read / in the container + return idtools.MkdirAndChown(path.Join(dir, "root"), 0755, root) + } + +@@ -301,7 +299,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr + if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { + return err + } +- return ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666) ++ return ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0600) + } + + // Otherwise, copy the upper and the lower-id from the parent +@@ -311,7 +309,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr + return err + } + +- if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil { ++ if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0600); err != nil { + return err + } + +diff --git a/engine/daemon/graphdriver/overlay2/overlay.go b/engine/daemon/graphdriver/overlay2/overlay.go +index 7b2475ea7e8d..1db8ac4cd8ab 100644 +--- a/engine/daemon/graphdriver/overlay2/overlay.go ++++ b/engine/daemon/graphdriver/overlay2/overlay.go +@@ -165,12 +165,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap + logger.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs)) + } + +- rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) +- if err != nil { +- return nil, err +- } +- // Create the driver home dir +- if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { ++ if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0701, idtools.CurrentIdentity()); err != nil { + return nil, err + } + +@@ -339,11 +334,12 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr + return err + } + root := idtools.Identity{UID: rootUID, GID: rootGID} ++ current := idtools.CurrentIdentity() + +- if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { ++ if err := idtools.MkdirAllAndChown(path.Dir(dir), 0701, current); err != nil { + return err + } +- if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { ++ if err := idtools.MkdirAndChown(dir, 0701, current); err != nil { + return err + } + +diff --git a/engine/daemon/graphdriver/vfs/driver.go b/engine/daemon/graphdriver/vfs/driver.go +index d7f14ecb622d..3134c6631fdd 100644 +--- a/engine/daemon/graphdriver/vfs/driver.go ++++ b/engine/daemon/graphdriver/vfs/driver.go +@@ -38,8 +38,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap + home: home, + idMapping: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), + } +- rootIDs := d.idMapping.RootPair() +- if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil { ++ if err := idtools.MkdirAllAndChown(home, 0701, idtools.CurrentIdentity()); err != nil { + return nil, err + } + +@@ -141,7 +140,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { + func (d *Driver) create(id, parent string, size uint64) error { + dir := d.dir(id) + rootIDs := d.idMapping.RootPair() +- if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil { ++ if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0701, idtools.CurrentIdentity()); err != nil { + return err + } + if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil { +diff --git a/engine/daemon/graphdriver/zfs/zfs.go b/engine/daemon/graphdriver/zfs/zfs.go +index c83446cf8f77..b887b0950203 100644 +--- a/engine/daemon/graphdriver/zfs/zfs.go ++++ b/engine/daemon/graphdriver/zfs/zfs.go +@@ -103,11 +103,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri + return nil, fmt.Errorf("BUG: zfs get all -t filesystem -rHp '%s' should contain '%s'", options.fsName, options.fsName) + } + +- rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) +- if err != nil { +- return nil, fmt.Errorf("Failed to get root uid/guid: %v", err) +- } +- if err := idtools.MkdirAllAndChown(base, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { ++ if err := idtools.MkdirAllAndChown(base, 0701, idtools.CurrentIdentity()); err != nil { + return nil, fmt.Errorf("Failed to create '%s': %v", base, err) + } + +diff --git a/engine/volume/local/local.go b/engine/volume/local/local.go +index 6dc894873dd8..aeb98aba61ff 100644 +--- a/engine/volume/local/local.go ++++ b/engine/volume/local/local.go +@@ -49,7 +49,7 @@ type activeMount struct { + func New(scope string, rootIdentity idtools.Identity) (*Root, error) { + rootDirectory := filepath.Join(scope, volumesPathName) + +- if err := idtools.MkdirAllAndChown(rootDirectory, 0700, rootIdentity); err != nil { ++ if err := idtools.MkdirAllAndChown(rootDirectory, 0701, idtools.CurrentIdentity()); err != nil { + return nil, err + } + +@@ -146,8 +146,15 @@ func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error + } + + path := r.DataPath(name) ++ volRoot := filepath.Dir(path) ++ // Root dir does not need to be accessed by the remapped root ++ if err := idtools.MkdirAllAndChown(volRoot, 0701, idtools.CurrentIdentity()); err != nil { ++ return nil, errors.Wrapf(errdefs.System(err), "error while creating volume root path '%s'", volRoot) ++ } ++ ++ // Remapped root does need access to the data path + if err := idtools.MkdirAllAndChown(path, 0755, r.rootIdentity); err != nil { +- return nil, errors.Wrapf(errdefs.System(err), "error while creating volume path '%s'", path) ++ return nil, errors.Wrapf(errdefs.System(err), "error while creating volume data path '%s'", path) + } + + var err error diff -Nru docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21285.patch docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21285.patch --- docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21285.patch 1970-01-01 00:00:00.000000000 +0000 +++ docker.io-18.09.1+dfsg1/debian/patches/cve-2021-21285.patch 2021-02-21 17:06:17.000000000 +0000 @@ -0,0 +1,53 @@ +From 420b1d36250f9cfdc561f086f25a213ecb669b6f Mon Sep 17 00:00:00 2001 +From: Brian Goff +Date: Mon, 12 Oct 2020 18:08:28 +0000 +Subject: [PATCH] pull: Validate layer digest format + +Otherwise a malformed or empty digest may cause a panic. + +Signed-off-by: Brian Goff +(cherry picked from commit a7d4af84bd2f189b921c3ec60796aa825e3a0f2a) +Signed-off-by: Tibor Vass +--- + builder/builder-next/adapters/containerimage/pull.go | 3 +++ + distribution/pull_v2.go | 6 ++++++ + 2 files changed, 9 insertions(+) + +diff --git a/engine/builder/builder-next/adapters/containerimage/pull.go b/engine/builder/builder-next/adapters/containerimage/pull.go +index 729ef52ae865..9d6e07e27d8a 100644 +--- a/engine/builder/builder-next/adapters/containerimage/pull.go ++++ b/engine/builder/builder-next/adapters/containerimage/pull.go +@@ -526,6 +526,9 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { + layers := make([]xfer.DownloadDescriptor, 0, len(mfst.Layers)) + + for i, desc := range mfst.Layers { ++ if err := desc.Digest.Validate(); err != nil { ++ return nil, errors.Wrap(err, "layer digest could not be validated") ++ } + ongoing.add(desc) + layers = append(layers, &layerDescriptor{ + desc: desc, +diff --git a/engine/distribution/pull_v2.go b/engine/distribution/pull_v2.go +index 3307458fdff6..cb47264b9a6c 100644 +--- a/engine/distribution/pull_v2.go ++++ b/engine/distribution/pull_v2.go +@@ -477,6 +477,9 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv + // to top-most, so that the downloads slice gets ordered correctly. + for i := len(verifiedManifest.FSLayers) - 1; i >= 0; i-- { + blobSum := verifiedManifest.FSLayers[i].BlobSum ++ if err = blobSum.Validate(); err != nil { ++ return "", "", errors.Wrapf(err, "could not validate layer digest %q", blobSum) ++ } + + var throwAway struct { + ThrowAway bool `json:"throwaway,omitempty"` +@@ -575,6 +578,9 @@ func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.De + // Note that the order of this loop is in the direction of bottom-most + // to top-most, so that the downloads slice gets ordered correctly. + for _, d := range mfst.Layers { ++ if err := d.Digest.Validate(); err != nil { ++ return "", "", errors.Wrapf(err, "could not validate layer digest %q", d.Digest) ++ } + layerDescriptor := &v2LayerDescriptor{ + digest: d.Digest, + repo: p.repo, 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 2020-06-14 20:12:29.000000000 +0000 +++ docker.io-18.09.1+dfsg1/debian/patches/series 2021-02-21 17:06:17.000000000 +0000 @@ -21,6 +21,12 @@ cve-2019-13509-04-DebugRequestMiddleware-Remove-path-handling.patch cve-2019-14271-Initialize-nss-libraries-in-Glibc.patch cve-2020-13401-disable-IPv6-router-advertisements.patch +cve-2020-15157.patch +cve-2020-15257.patch +cve-2021-21284-1.patch +cve-2021-21284-2.patch +cve-2021-21284-3.patch +cve-2021-21285.patch engine-contrib-debootstrap-curl-follow-location.patch engine-test-noinstall.patch