Package: release.debian.org
Severity: normal
Tags: bullseye moreinfo
User: [email protected]
Usertags: pu
X-Debbugs-Cc: [email protected], [email protected], [email protected]
[ 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 <[email protected]> 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