[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1001115: bullseye-pu: package docker.io/20.10.5+dfsg1-1+deb11u1



Package: release.debian.org
Severity: normal
Tags: bullseye moreinfo
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: zhsj@debian.org, arnaudr@kali.org, team@security.debian.org

[ Reason ]

Backport 3 CVE patches.

+ CVE-2021-41089: Create parent directories inside a chroot during docker
  cp to prevent a specially crafted container from changing permissions of
  existing files in the host’s filesystem.
+ CVE-2021-41091: Lock down file permissions to prevent unprivileged users
  from discovering and executing programs in /var/lib/docker.
+ CVE-2021-41092: Ensure default auth config has address field set, to
  prevent credentials being sent to the default registry. (Closes: #998292)

And backport 1 patch to run container which
uses "clone3" syscall (for example glibc 2.34)

[ Impact ]

Except the security patches, user will not able to run container like
fedora:rawhide, ubuntu:impish (same reason with containerd) without disabling
seccomp(lost security protect).

[ Tests ]

+ CVE-2021-41089 I haven't figured out how to trigger the bug, but the patch
  is backported without modification from upstream 20.10 branch.
+ CVE-2021-41091 I have verified the patch. With the patch, non-root user is
  not able to run setuid file in /var/lib/docker.
+ CVE-2021-41092 this patch includes upstream unit test.

+ "clone3" patch
  I have installed it on bullseye vm and succefully run following command:

  docker run --rm -it registry.fedoraproject.org/fedora:rawhide /usr/bin/curl www.debian.org

  Without the patch, this command will fail.

[ Risks ]

The 3 CVE patches are backported from upstream 20.10 branch without modification.
The "clone3" patch is backported with modification, since upstream patch fails on
mips64el and mipsel. But the modification is also provided by upstream, although
they decided not to merged into 20.10 branch, since they want to fix it in conainer
spec, which would take long time.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]

  * Backport patches for CVE-2021-41089 CVE-2021-41091 CVE-2021-41092
    + CVE-2021-41089: Create parent directories inside a chroot during docker
      cp to prevent a specially crafted container from changing permissions of
      existing files in the host’s filesystem.
    + CVE-2021-41091: Lock down file permissions to prevent unprivileged users
      from discovering and executing programs in /var/lib/docker.
    + CVE-2021-41092: Ensure default auth config has address field set, to
      prevent credentials being sent to the default registry. (Closes: #998292)
  * Backport "clone3" syscall workaround in default seccomp policy
    (Closes: #995191)

[ Other info ]

+ Please build docker.io after containerd/1.4.12~ds1-1~deb11u1 has been built.
  So the RPi1/RPi0 workaround(#998909) will be used.
+ The "clone3" hasn't been in testing yet(due to FTBFS on mips64el/mipsel
  previously, but I have fixed it in docker.io/20.10.11+dfsg1-2. It will hopefully
  migrate to testing in 5 days).

  So I have added the moreinfo tag.
diff -Nru docker.io-20.10.5+dfsg1/debian/changelog docker.io-20.10.5+dfsg1/debian/changelog
--- docker.io-20.10.5+dfsg1/debian/changelog	2021-03-10 00:53:21.000000000 +0800
+++ docker.io-20.10.5+dfsg1/debian/changelog	2021-12-04 18:53:03.000000000 +0800
@@ -1,3 +1,18 @@
+docker.io (20.10.5+dfsg1-1+deb11u1) bullseye; urgency=medium
+
+  * Backport patches for CVE-2021-41089 CVE-2021-41091 CVE-2021-41092
+    + CVE-2021-41089: Create parent directories inside a chroot during docker
+      cp to prevent a specially crafted container from changing permissions of
+      existing files in the host’s filesystem.
+    + CVE-2021-41091: Lock down file permissions to prevent unprivileged users
+      from discovering and executing programs in /var/lib/docker.
+    + CVE-2021-41092: Ensure default auth config has address field set, to
+      prevent credentials being sent to the default registry. (Closes: #998292)
+  * Backport "clone3" syscall workaround in default seccomp policy
+    (Closes: #995191)
+
+ -- Shengjing Zhu <zhsj@debian.org>  Sat, 04 Dec 2021 18:53:03 +0800
+
 docker.io (20.10.5+dfsg1-1) unstable; urgency=medium
 
   * Team upload.
diff -Nru docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41089.patch docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41089.patch
--- docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41089.patch	1970-01-01 08:00:00.000000000 +0800
+++ docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41089.patch	2021-12-04 18:08:34.000000000 +0800
@@ -0,0 +1,28 @@
+Description: chrootarchive: don't create parent dirs outside of chroot
+Origin: backport, https://github.com/moby/moby/commit/80f1169e
+--- a/engine/pkg/chrootarchive/archive.go
++++ b/engine/pkg/chrootarchive/archive.go
+@@ -74,13 +74,17 @@
+ 		options.ExcludePatterns = []string{}
+ 	}
+ 
+-	idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
+-	rootIDs := idMapping.RootPair()
++	// If dest is inside a root then directory is created within chroot by extractor.
++	// This case is only currently used by cp.
++	if dest == root {
++		idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
++		rootIDs := idMapping.RootPair()
+ 
+-	dest = filepath.Clean(dest)
+-	if _, err := os.Stat(dest); os.IsNotExist(err) {
+-		if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil {
+-			return err
++		dest = filepath.Clean(dest)
++		if _, err := os.Stat(dest); os.IsNotExist(err) {
++			if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil {
++				return err
++			}
+ 		}
+ 	}
+ 
diff -Nru docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41091.patch docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41091.patch
--- docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41091.patch	1970-01-01 08:00:00.000000000 +0800
+++ docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41091.patch	2021-12-04 18:17:18.000000000 +0800
@@ -0,0 +1,320 @@
+Description: Lock down docker root dir perms
+Origin: backport, https://github.com/moby/moby/commit/93ac040b
+--- a/engine/daemon/container_operations_unix.go
++++ b/engine/daemon/container_operations_unix.go
+@@ -466,5 +466,5 @@
+ 	if err != nil {
+ 		return err
+ 	}
+-	return idtools.MkdirAllAndChown(p, 0701, idtools.CurrentIdentity())
++	return idtools.MkdirAllAndChown(p, 0710, idtools.Identity{UID: idtools.CurrentIdentity().UID, GID: daemon.IdentityMapping().RootPair().GID})
+ }
+--- a/engine/daemon/create.go
++++ b/engine/daemon/create.go
+@@ -212,10 +212,11 @@
+ 	}
+ 	ctr.RWLayer = rwLayer
+ 
+-	if err := idtools.MkdirAndChown(ctr.Root, 0701, idtools.CurrentIdentity()); err != nil {
++	current := idtools.CurrentIdentity()
++	if err := idtools.MkdirAndChown(ctr.Root, 0710, idtools.Identity{UID: current.UID, GID: daemon.IdentityMapping().RootPair().GID}); err != nil {
+ 		return nil, err
+ 	}
+-	if err := idtools.MkdirAndChown(ctr.CheckpointDir(), 0700, idtools.CurrentIdentity()); err != nil {
++	if err := idtools.MkdirAndChown(ctr.CheckpointDir(), 0700, current); err != nil {
+ 		return nil, err
+ 	}
+ 
+--- a/engine/daemon/daemon.go
++++ b/engine/daemon/daemon.go
+@@ -861,7 +861,10 @@
+ 	}
+ 
+ 	daemonRepo := filepath.Join(config.Root, "containers")
+-	if err := idtools.MkdirAllAndChown(daemonRepo, 0701, idtools.CurrentIdentity()); err != nil {
++	if err := idtools.MkdirAllAndChown(daemonRepo, 0710, idtools.Identity{
++		UID: idtools.CurrentIdentity().UID,
++		GID: rootIDs.GID,
++	}); err != nil {
+ 		return nil, err
+ 	}
+ 
+--- a/engine/daemon/daemon_unix.go
++++ b/engine/daemon/daemon_unix.go
+@@ -1216,21 +1216,21 @@
+ 		}
+ 	}
+ 
++	id := idtools.Identity{UID: idtools.CurrentIdentity().UID, GID: remappedRoot.GID}
++	// First make sure the current root dir has the correct perms.
++	if err := idtools.MkdirAllAndChown(config.Root, 0710, id); err != nil {
++		return errors.Wrapf(err, "could not create or set daemon root permissions: %s", config.Root)
++	}
++
+ 	// if user namespaces are enabled we will create a subtree underneath the specified root
+ 	// with any/all specified remapped root uid/gid options on the daemon creating
+ 	// 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 != "" {
+-		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, 0701, id); err != nil {
++		if err := idtools.MkdirAllAndChown(config.Root, 0710, 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
+--- a/engine/daemon/graphdriver/aufs/aufs.go
++++ b/engine/daemon/graphdriver/aufs/aufs.go
+@@ -130,14 +130,23 @@
+ 	}
+ 
+ 	currentID := idtools.CurrentIdentity()
++	_, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
++	if err != nil {
++		return nil, err
++	}
++	dirID := idtools.Identity{
++		UID: currentID.UID,
++		GID: rootGID,
++	}
++
+ 	// Create the root aufs driver dir
+-	if err := idtools.MkdirAllAndChown(root, 0701, currentID); err != nil {
++	if err := idtools.MkdirAllAndChown(root, 0710, dirID); err != nil {
+ 		return nil, err
+ 	}
+ 
+ 	// Populate the dir structure
+ 	for _, p := range paths {
+-		if err := idtools.MkdirAllAndChown(path.Join(root, p), 0701, currentID); err != nil {
++		if err := idtools.MkdirAllAndChown(path.Join(root, p), 0710, dirID); err != nil {
+ 			return nil, err
+ 		}
+ 	}
+--- a/engine/daemon/graphdriver/btrfs/btrfs.go
++++ b/engine/daemon/graphdriver/btrfs/btrfs.go
+@@ -70,7 +70,14 @@
+ 		return nil, graphdriver.ErrPrerequisites
+ 	}
+ 
+-	if err := idtools.MkdirAllAndChown(home, 0701, idtools.CurrentIdentity()); err != nil {
++	remappedRoot := idtools.NewIDMappingsFromMaps(uidMaps, gidMaps)
++	currentID := idtools.CurrentIdentity()
++	dirID := idtools.Identity{
++		UID: currentID.UID,
++		GID: remappedRoot.RootPair().GID,
++	}
++
++	if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil {
+ 		return nil, err
+ 	}
+ 
+@@ -521,7 +528,14 @@
+ 	if err != nil {
+ 		return err
+ 	}
+-	if err := idtools.MkdirAllAndChown(subvolumes, 0701, idtools.CurrentIdentity()); err != nil {
++
++	currentID := idtools.CurrentIdentity()
++	dirID := idtools.Identity{
++		UID: currentID.UID,
++		GID: rootGID,
++	}
++
++	if err := idtools.MkdirAllAndChown(subvolumes, 0710, dirID); err != nil {
+ 		return err
+ 	}
+ 	if parent == "" {
+--- a/engine/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go
++++ b/engine/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go
+@@ -88,7 +88,17 @@
+ 		return nil, graphdriver.ErrNotSupported
+ 	}
+ 
+-	if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0701, idtools.CurrentIdentity()); err != nil {
++	remappedRoot := idtools.NewIDMappingsFromMaps(uidMaps, gidMaps)
++	currentID := idtools.CurrentIdentity()
++	dirID := idtools.Identity{
++		UID: currentID.UID,
++		GID: remappedRoot.RootPair().GID,
++	}
++
++	if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil {
++		return nil, err
++	}
++	if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 700, currentID); err != nil {
+ 		return nil, err
+ 	}
+ 
+@@ -173,11 +183,15 @@
+ 	}
+ 	root := idtools.Identity{UID: rootUID, GID: rootGID}
+ 
+-	currentID := idtools.CurrentIdentity()
+-	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0701, currentID); err != nil {
++	dirID := idtools.Identity{
++		UID: rootUID,
++		GID: rootGID,
++	}
++
++	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0710, dirID); err != nil {
+ 		return err
+ 	}
+-	if err := idtools.MkdirAndChown(dir, 0701, currentID); err != nil {
++	if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil {
+ 		return err
+ 	}
+ 
+@@ -211,7 +225,7 @@
+ 		return nil
+ 	}
+ 
+-	if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0701, currentID); err != nil {
++	if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0710, dirID); err != nil {
+ 		return err
+ 	}
+ 
+--- a/engine/daemon/graphdriver/overlay/overlay.go
++++ b/engine/daemon/graphdriver/overlay/overlay.go
+@@ -156,11 +156,20 @@
+ 		logrus.WithField("storage-driver", "overlay").Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs))
+ 	}
+ 
+-	// Create the driver home dir
+-	if err := idtools.MkdirAllAndChown(home, 0701, idtools.CurrentIdentity()); err != nil {
++	currentID := idtools.CurrentIdentity()
++	_, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
++	if err != nil {
+ 		return nil, err
+ 	}
++	dirID := idtools.Identity{
++		UID: currentID.UID,
++		GID: rootGID,
++	}
+ 
++	// Create the driver home dir
++	if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil {
++		return nil, err
++	}
+ 	d := &Driver{
+ 		home:          home,
+ 		uidMaps:       uidMaps,
+@@ -262,10 +271,11 @@
+ 	root := idtools.Identity{UID: rootUID, GID: rootGID}
+ 
+ 	currentID := idtools.CurrentIdentity()
+-	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0701, currentID); err != nil {
+-		return err
++	dirID := idtools.Identity{
++		UID: currentID.UID,
++		GID: rootGID,
+ 	}
+-	if err := idtools.MkdirAndChown(dir, 0701, currentID); err != nil {
++	if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil {
+ 		return err
+ 	}
+ 
+--- a/engine/daemon/graphdriver/overlay2/overlay.go
++++ b/engine/daemon/graphdriver/overlay2/overlay.go
+@@ -165,7 +165,20 @@
+ 		logger.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs))
+ 	}
+ 
+-	if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0701, idtools.CurrentIdentity()); err != nil {
++	_, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
++	if err != nil {
++		return nil, err
++	}
++
++	cur := idtools.CurrentIdentity()
++	dirID := idtools.Identity{
++		UID: cur.UID,
++		GID: rootGID,
++	}
++	if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil {
++		return nil, err
++	}
++	if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, cur); err != nil {
+ 		return nil, err
+ 	}
+ 
+@@ -334,12 +347,15 @@
+ 		return err
+ 	}
+ 	root := idtools.Identity{UID: rootUID, GID: rootGID}
+-	current := idtools.CurrentIdentity()
++	dirID := idtools.Identity{
++		UID: idtools.CurrentIdentity().UID,
++		GID: rootGID,
++	}
+ 
+-	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0701, current); err != nil {
++	if err := idtools.MkdirAllAndChown(path.Dir(dir), 0710, dirID); err != nil {
+ 		return err
+ 	}
+-	if err := idtools.MkdirAndChown(dir, 0701, current); err != nil {
++	if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil {
+ 		return err
+ 	}
+ 
+--- a/engine/daemon/graphdriver/vfs/driver.go
++++ b/engine/daemon/graphdriver/vfs/driver.go
+@@ -37,8 +37,16 @@
+ 	if err := d.parseOptions(options); err != nil {
+ 		return nil, err
+ 	}
++	_, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
++	if err != nil {
++		return nil, err
++	}
+ 
+-	if err := idtools.MkdirAllAndChown(home, 0701, idtools.CurrentIdentity()); err != nil {
++	dirID := idtools.Identity{
++		UID: idtools.CurrentIdentity().UID,
++		GID: rootGID,
++	}
++	if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil {
+ 		return nil, err
+ 	}
+ 
+@@ -140,7 +148,12 @@
+ 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), 0701, idtools.CurrentIdentity()); err != nil {
++
++	dirID := idtools.Identity{
++		UID: idtools.CurrentIdentity().UID,
++		GID: rootIDs.GID,
++	}
++	if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0710, dirID); err != nil {
+ 		return err
+ 	}
+ 	if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil {
+--- a/engine/daemon/graphdriver/zfs/zfs.go
++++ b/engine/daemon/graphdriver/zfs/zfs.go
+@@ -104,7 +104,16 @@
+ 		return nil, fmt.Errorf("BUG: zfs get all -t filesystem -rHp '%s' should contain '%s'", options.fsName, options.fsName)
+ 	}
+ 
+-	if err := idtools.MkdirAllAndChown(base, 0701, idtools.CurrentIdentity()); err != nil {
++	_, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
++	if err != nil {
++		return nil, err
++	}
++
++	dirID := idtools.Identity{
++		UID: idtools.CurrentIdentity().UID,
++		GID: rootGID,
++	}
++	if err := idtools.MkdirAllAndChown(base, 0710, dirID); err != nil {
+ 		return nil, fmt.Errorf("Failed to create '%s': %v", base, err)
+ 	}
+ 
diff -Nru docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41092.patch docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41092.patch
--- docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41092.patch	1970-01-01 08:00:00.000000000 +0800
+++ docker.io-20.10.5+dfsg1/debian/patches/CVE-2021-41092.patch	2021-12-04 18:41:36.000000000 +0800
@@ -0,0 +1,115 @@
+Description: registry: ensure default auth config has address
+Origin: backport, https://github.com/docker/cli/commit/42d1c027
+Bug-Debian: https://bugs.debian.org/998292
+--- a/cli/cli/command/registry.go
++++ b/cli/cli/command/registry.go
+@@ -63,17 +63,14 @@
+ 		indexServer := registry.GetAuthConfigKey(index)
+ 		isDefaultRegistry := indexServer == ElectAuthServer(context.Background(), cli)
+ 		authConfig, err := GetDefaultAuthConfig(cli, true, indexServer, isDefaultRegistry)
+-		if authConfig == nil {
+-			authConfig = &types.AuthConfig{}
+-		}
+ 		if err != nil {
+ 			fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err)
+ 		}
+-		err = ConfigureAuth(cli, "", "", authConfig, isDefaultRegistry)
++		err = ConfigureAuth(cli, "", "", &authConfig, isDefaultRegistry)
+ 		if err != nil {
+ 			return "", err
+ 		}
+-		return EncodeAuthToBase64(*authConfig)
++		return EncodeAuthToBase64(authConfig)
+ 	}
+ }
+ 
+@@ -92,7 +89,7 @@
+ 
+ // GetDefaultAuthConfig gets the default auth config given a serverAddress
+ // If credentials for given serverAddress exists in the credential store, the configuration will be populated with values in it
+-func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (*types.AuthConfig, error) {
++func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
+ 	if !isDefaultRegistry {
+ 		serverAddress = registry.ConvertToHostname(serverAddress)
+ 	}
+@@ -101,13 +98,15 @@
+ 	if checkCredStore {
+ 		authconfig, err = cli.ConfigFile().GetAuthConfig(serverAddress)
+ 		if err != nil {
+-			return nil, err
++			return types.AuthConfig{
++				ServerAddress: serverAddress,
++			}, err
+ 		}
+ 	}
+ 	authconfig.ServerAddress = serverAddress
+ 	authconfig.IdentityToken = ""
+ 	res := types.AuthConfig(authconfig)
+-	return &res, nil
++	return res, nil
+ }
+ 
+ // ConfigureAuth handles prompting of user's username and password if needed
+--- a/cli/cli/command/registry/login.go
++++ b/cli/cli/command/registry/login.go
+@@ -114,22 +114,19 @@
+ 	var response registrytypes.AuthenticateOKBody
+ 	isDefaultRegistry := serverAddress == authServer
+ 	authConfig, err := command.GetDefaultAuthConfig(dockerCli, opts.user == "" && opts.password == "", serverAddress, isDefaultRegistry)
+-	if authConfig == nil {
+-		authConfig = &types.AuthConfig{}
+-	}
+ 	if err == nil && authConfig.Username != "" && authConfig.Password != "" {
+-		response, err = loginWithCredStoreCreds(ctx, dockerCli, authConfig)
++		response, err = loginWithCredStoreCreds(ctx, dockerCli, &authConfig)
+ 	}
+ 	if err != nil || authConfig.Username == "" || authConfig.Password == "" {
+-		err = command.ConfigureAuth(dockerCli, opts.user, opts.password, authConfig, isDefaultRegistry)
++		err = command.ConfigureAuth(dockerCli, opts.user, opts.password, &authConfig, isDefaultRegistry)
+ 		if err != nil {
+ 			return err
+ 		}
+ 
+-		response, err = clnt.RegistryLogin(ctx, *authConfig)
++		response, err = clnt.RegistryLogin(ctx, authConfig)
+ 		if err != nil && client.IsErrConnectionFailed(err) {
+ 			// If the server isn't responding (yet) attempt to login purely client side
+-			response, err = loginClientSide(ctx, *authConfig)
++			response, err = loginClientSide(ctx, authConfig)
+ 		}
+ 		// If we (still) have an error, give up
+ 		if err != nil {
+@@ -152,7 +149,7 @@
+ 		}
+ 	}
+ 
+-	if err := creds.Store(configtypes.AuthConfig(*authConfig)); err != nil {
++	if err := creds.Store(configtypes.AuthConfig(authConfig)); err != nil {
+ 		return errors.Errorf("Error saving credentials: %v", err)
+ 	}
+ 
+--- a/cli/cli/command/registry_test.go
++++ b/cli/cli/command/registry_test.go
+@@ -145,7 +145,21 @@
+ 			assert.Check(t, is.Equal(tc.expectedErr, err.Error()))
+ 		} else {
+ 			assert.NilError(t, err)
+-			assert.Check(t, is.DeepEqual(tc.expectedAuthConfig, *authconfig))
++			assert.Check(t, is.DeepEqual(tc.expectedAuthConfig, authconfig))
+ 		}
+ 	}
+ }
++
++func TestGetDefaultAuthConfig_HelperError(t *testing.T) {
++	cli := test.NewFakeCli(&fakeClient{})
++	errBuf := new(bytes.Buffer)
++	cli.SetErr(errBuf)
++	cli.ConfigFile().CredentialsStore = "fake-does-not-exist"
++	serverAddress := "test-server-address"
++	expectedAuthConfig := types.AuthConfig{
++		ServerAddress: serverAddress,
++	}
++	authconfig, err := GetDefaultAuthConfig(cli, true, serverAddress, serverAddress == "https://index.docker.io/v1/";)
++	assert.Check(t, is.DeepEqual(expectedAuthConfig, authconfig))
++	assert.Check(t, is.ErrorContains(err, "docker-credential-fake-does-not-exist"))
++}
diff -Nru docker.io-20.10.5+dfsg1/debian/patches/engine-clone3-seccomp.patch docker.io-20.10.5+dfsg1/debian/patches/engine-clone3-seccomp.patch
--- docker.io-20.10.5+dfsg1/debian/patches/engine-clone3-seccomp.patch	1970-01-01 08:00:00.000000000 +0800
+++ docker.io-20.10.5+dfsg1/debian/patches/engine-clone3-seccomp.patch	2021-12-04 18:52:06.000000000 +0800
@@ -0,0 +1,193 @@
+Description: seccomp: add support for "clone3" syscall in default policy
+Origin: backport, https://github.com/moby/moby/commit/97728350
+Bug-Debian: https://bugs.debian.org/995191
+
+It also includes fix in https://github.com/moby/moby/pull/43005 for mips.
+--- a/engine/profiles/seccomp/default.json
++++ b/engine/profiles/seccomp/default.json
+@@ -591,6 +591,7 @@
+ 			"names": [
+ 				"bpf",
+ 				"clone",
++				"clone3",
+ 				"fanotify_init",
+ 				"fsconfig",
+ 				"fsmount",
+@@ -665,6 +666,51 @@
+ 				]
+ 			},
+ 			"excludes": {
++				"caps": [
++					"CAP_SYS_ADMIN"
++				]
++			}
++		},
++		{
++			"names": [
++				"clone3"
++			],
++			"action": "SCMP_ACT_ERRNO",
++			"errnoRet": 38,
++			"args": [],
++			"comment": "ENOSYS for clone3 on non-mips architectures",
++			"includes": {},
++			"excludes": {
++				"caps": [
++					"CAP_SYS_ADMIN"
++				],
++				"arches": [
++					"mips3l64n32",
++					"mips64",
++					"mips64n32",
++					"mipsel",
++					"mipsel64"
++				]
++			}
++		},
++		{
++			"names": [
++				"clone3"
++			],
++			"action": "SCMP_ACT_ERRNO",
++			"errnoRet": 89,
++			"args": [],
++			"comment": "ENOSYS for clone3 on mips architectures",
++			"includes": {
++				"arches": [
++					"mips3l64n32",
++					"mips64",
++					"mips64n32",
++					"mipsel",
++					"mipsel64"
++				]
++			},
++			"excludes": {
+ 				"caps": [
+ 					"CAP_SYS_ADMIN"
+ 				]
+--- a/engine/profiles/seccomp/default_linux.go
++++ b/engine/profiles/seccomp/default_linux.go
+@@ -40,8 +40,26 @@
+ 	}
+ }
+ 
++const (
++	enosys     uint = 0x26 // enosys for non-mips architectures.
++	enosysMIPS uint = 0x59 // enosys for mips architectures.
++)
++
+ // DefaultProfile defines the allowed syscalls for the default seccomp profile.
+ func DefaultProfile() *Seccomp {
++	// The value of ENOSYS differs between MIPS and non-MIPS architectures. While
++	// this is not problematic for the embedded seccomp profile, it prevents the
++	// profile from being saved as a portable JSON file that can be used for both
++	// architectures.
++	// To work around this situation, we include conditional rules for both arches.
++	// and hard-code the value for ENOSYS in both.
++	// For more details, refer to https://github.com/moby/moby/pull/42836#issuecomment-963429850
++	// and https://github.com/opencontainers/runtime-spec/pull/1087#issuecomment-963463475
++	var (
++		nosys     = enosys
++		nosysMIPS = enosysMIPS
++	)
++
+ 	syscalls := []*Syscall{
+ 		{
+ 			Names: []string{
+@@ -522,6 +540,7 @@
+ 			Names: []string{
+ 				"bpf",
+ 				"clone",
++				"clone3",
+ 				"fanotify_init",
+ 				"fsconfig",
+ 				"fsmount",
+@@ -585,6 +604,34 @@
+ 			},
+ 			Excludes: Filter{
+ 				Caps: []string{"CAP_SYS_ADMIN"},
++			},
++		},
++		{
++			Names: []string{
++				"clone3",
++			},
++			Action:   specs.ActErrno,
++			ErrnoRet: &nosys,
++			Args:     []*specs.LinuxSeccompArg{},
++			Comment:  "ENOSYS for clone3 on non-mips architectures",
++			Excludes: Filter{
++				Arches: []string{"mips3l64n32", "mips64", "mips64n32", "mipsel", "mipsel64"},
++				Caps:   []string{"CAP_SYS_ADMIN"},
++			},
++		},
++		{
++			Names: []string{
++				"clone3",
++			},
++			Action:   specs.ActErrno,
++			ErrnoRet: &nosysMIPS,
++			Args:     []*specs.LinuxSeccompArg{},
++			Comment:  "ENOSYS for clone3 on mips architectures",
++			Includes: Filter{
++				Arches: []string{"mips3l64n32", "mips64", "mips64n32", "mipsel", "mipsel64"},
++			},
++			Excludes: Filter{
++				Caps: []string{"CAP_SYS_ADMIN"},
+ 			},
+ 		},
+ 		{
+--- a/engine/profiles/seccomp/seccomp.go
++++ b/engine/profiles/seccomp/seccomp.go
+@@ -45,6 +45,7 @@
+ 	Name     string                   `json:"name,omitempty"`
+ 	Names    []string                 `json:"names,omitempty"`
+ 	Action   specs.LinuxSeccompAction `json:"action"`
++	ErrnoRet *uint                    `json:"errnoRet,omitempty"`
+ 	Args     []*specs.LinuxSeccompArg `json:"args"`
+ 	Comment  string                   `json:"comment"`
+ 	Includes Filter                   `json:"includes"`
+--- a/engine/profiles/seccomp/seccomp_linux.go
++++ b/engine/profiles/seccomp/seccomp_linux.go
+@@ -150,29 +150,25 @@
+ 			}
+ 		}
+ 
++		newCall := specs.LinuxSyscall{
++			Action:   call.Action,
++			ErrnoRet: call.ErrnoRet,
++		}
+ 		if call.Name != "" && len(call.Names) != 0 {
+ 			return nil, errors.New("'name' and 'names' were specified in the seccomp profile, use either 'name' or 'names'")
+ 		}
+-
+ 		if call.Name != "" {
+-			newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall([]string{call.Name}, call.Action, call.Args))
++			newCall.Names = []string{call.Name}
+ 		} else {
+-			newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args))
++			newCall.Names = call.Names
++		}
++		// Loop through all the arguments of the syscall and convert them
++		for _, arg := range call.Args {
++			newCall.Args = append(newCall.Args, *arg)
+ 		}
+-	}
+-
+-	return newConfig, nil
+-}
+ 
+-func createSpecsSyscall(names []string, action specs.LinuxSeccompAction, args []*specs.LinuxSeccompArg) specs.LinuxSyscall {
+-	newCall := specs.LinuxSyscall{
+-		Names:  names,
+-		Action: action,
++		newConfig.Syscalls = append(newConfig.Syscalls, newCall)
+ 	}
+ 
+-	// Loop through all the arguments of the syscall and convert them
+-	for _, arg := range args {
+-		newCall.Args = append(newCall.Args, *arg)
+-	}
+-	return newCall
++	return newConfig, nil
+ }
diff -Nru docker.io-20.10.5+dfsg1/debian/patches/series docker.io-20.10.5+dfsg1/debian/patches/series
--- docker.io-20.10.5+dfsg1/debian/patches/series	2021-03-10 00:53:21.000000000 +0800
+++ docker.io-20.10.5+dfsg1/debian/patches/series	2021-12-04 18:45:47.000000000 +0800
@@ -27,3 +27,7 @@
 test--skip-TestGetRootUIDGID.patch
 test--skip-TestStateRunStop.patch
 test--skip-TestUntarParentPathPermissions
+CVE-2021-41089.patch
+CVE-2021-41091.patch
+CVE-2021-41092.patch
+engine-clone3-seccomp.patch

Reply to: