I've prepared an update for cloud-init in stretch to fix CVE-2021-3429
(Bug #985540).  For buster, after discussion with the stable security
team, we've decided not to publish a DSA but instead to release the fix
with the next stable point release.  I haven't worked with LTS much, so
I don't know if there's an equivalent approach here or not.

The debdiff is attached.  If somebody wants to take this and handle
uploading to LTS and whatever followup is needed, that's fine with me.
Otherwise, I can perform the upload if that's all that's required, but
I'd prefer to leave any DLA publication to people that are more actively
involved in such things.

noah

diff -Nru cloud-init-0.7.9/debian/changelog cloud-init-0.7.9/debian/changelog
--- cloud-init-0.7.9/debian/changelog   2017-02-02 13:23:41.000000000 +0000
+++ cloud-init-0.7.9/debian/changelog   2021-03-19 17:35:37.000000000 +0000
@@ -1,3 +1,9 @@
+cloud-init (0.7.9-2+deb9u1) stretch-security; urgency=medium
+
+  * Avoid logging generated passwords (CVE-2021-3429) (Closes: #985540)
+
+ -- Noah Meyerhans <no...@debian.org>  Fri, 19 Mar 2021 17:35:37 +0000
+
 cloud-init (0.7.9-2) unstable; urgency=medium
 
   * Add net-tools as runtime depends (Closes: #853926).
diff -Nru cloud-init-0.7.9/debian/patches/dont_log_generated_passwords.patch 
cloud-init-0.7.9/debian/patches/dont_log_generated_passwords.patch
--- cloud-init-0.7.9/debian/patches/dont_log_generated_passwords.patch  
1970-01-01 00:00:00.000000000 +0000
+++ cloud-init-0.7.9/debian/patches/dont_log_generated_passwords.patch  
2021-03-19 17:34:34.000000000 +0000
@@ -0,0 +1,143 @@
+Index: cloud-init-0.7.9/cloudinit/config/cc_set_passwords.py
+===================================================================
+--- cloud-init-0.7.9.orig/cloudinit/config/cc_set_passwords.py
++++ cloud-init-0.7.9/cloudinit/config/cc_set_passwords.py
+@@ -52,8 +52,6 @@ enabled, disabled, or left to system def
+             - user4:R
+ """
+ 
+-import sys
+-
+ from cloudinit.distros import ug_util
+ from cloudinit import ssh_util
+ from cloudinit import util
+@@ -115,7 +113,9 @@ def handle(_name, cfg, cloud, log, args)
+         if len(randlist):
+             blurb = ("Set the following 'random' passwords\n",
+                      '\n'.join(randlist))
+-            sys.stderr.write("%s\n%s\n" % blurb)
++            util.multi_log(
++                "%s\n%s\n" % blurb, stderr=False, fallback_to_stdout=False
++            )
+ 
+         if expire:
+             expired_users = []
+Index: cloud-init-0.7.9/cloudinit/util.py
+===================================================================
+--- cloud-init-0.7.9.orig/cloudinit/util.py
++++ cloud-init-0.7.9/cloudinit/util.py
+@@ -516,7 +516,7 @@ def find_modules(root_dir):
+ 
+ 
+ def multi_log(text, console=True, stderr=True,
+-              log=None, log_level=logging.DEBUG):
++              log=None, log_level=logging.DEBUG, fallback_to_stdout=True):
+     if stderr:
+         sys.stderr.write(text)
+     if console:
+@@ -525,7 +525,7 @@ def multi_log(text, console=True, stderr
+             with open(conpath, 'w') as wfh:
+                 wfh.write(text)
+                 wfh.flush()
+-        else:
++        elif fallback_to_stdout:
+             # A container may lack /dev/console (arguably a container bug).  
If
+             # it does not exist, then write output to stdout.  this will 
result
+             # in duplicate stderr and stdout messages if stderr was True.
+@@ -660,6 +660,26 @@ def redirect_output(outfmt, errfmt, o_ou
+     if not o_err:
+         o_err = sys.stderr
+ 
++    # pylint: disable=subprocess-popen-preexec-fn
++    def set_subprocess_umask_and_gid():
++        """Reconfigure umask and group ID to create output files securely.
++
++        This is passed to subprocess.Popen as preexec_fn, so it is executed in
++        the context of the newly-created process.  It:
++
++        * sets the umask of the process so created files aren't world-readable
++        * if an adm group exists in the system, sets that as the process' GID
++          (so that the created file(s) are owned by root:adm)
++        """
++        os.umask(0o037)
++        try:
++            group_id = grp.getgrnam("adm").gr_gid
++        except KeyError:
++            # No adm group, don't set a group
++            pass
++        else:
++            os.setgid(group_id)
++
+     if outfmt:
+         LOG.debug("Redirecting %s to %s", o_out, outfmt)
+         (mode, arg) = outfmt.split(" ", 1)
+@@ -669,7 +689,12 @@ def redirect_output(outfmt, errfmt, o_ou
+                 owith = "wb"
+             new_fp = open(arg, owith)
+         elif mode == "|":
+-            proc = subprocess.Popen(arg, shell=True, stdin=subprocess.PIPE)
++            proc = subprocess.Popen(
++                arg,
++                shell=True,
++                stdin=subprocess.PIPE,
++                preexec_fn=set_subprocess_umask_and_gid,
++            )
+             new_fp = proc.stdin
+         else:
+             raise TypeError("Invalid type for output format: %s" % outfmt)
+@@ -691,7 +716,12 @@ def redirect_output(outfmt, errfmt, o_ou
+                 owith = "wb"
+             new_fp = open(arg, owith)
+         elif mode == "|":
+-            proc = subprocess.Popen(arg, shell=True, stdin=subprocess.PIPE)
++            proc = subprocess.Popen(
++                arg,
++                shell=True,
++                stdin=subprocess.PIPE,
++                preexec_fn=set_subprocess_umask_and_gid,
++            )
+             new_fp = proc.stdin
+         else:
+             raise TypeError("Invalid type for error format: %s" % errfmt)
+Index: cloud-init-0.7.9/tests/integration_tests/test_logging.py
+===================================================================
+--- /dev/null
++++ cloud-init-0.7.9/tests/integration_tests/test_logging.py
+@@ -0,0 +1,22 @@
++"""Integration tests relating to cloud-init's logging."""
++
++
++class TestVarLogCloudInitOutput:
++    """Integration tests relating to /var/log/cloud-init-output.log."""
++
++    def test_var_log_cloud_init_output_not_world_readable(self, client):
++        """
++        The log can contain sensitive data, it shouldn't be world-readable.
++
++        LP: #1918303
++        """
++        # Check the file exists
++        assert client.execute("test -f /var/log/cloud-init-output.log").ok
++
++        # Check its permissions are as we expect
++        perms, user, group = client.execute(
++            "stat -c %a:%U:%G /var/log/cloud-init-output.log"
++        ).split(":")
++        assert "640" == perms
++        assert "root" == user
++        assert "adm" == group
+Index: cloud-init-0.7.9/tests/unittests/test_util.py
+===================================================================
+--- cloud-init-0.7.9.orig/tests/unittests/test_util.py
++++ cloud-init-0.7.9/tests/unittests/test_util.py
+@@ -464,6 +464,10 @@ class TestMultiLog(helpers.FilesystemMoc
+         util.multi_log(logged_string)
+         self.assertEqual(logged_string, self.stdout.getvalue())
+ 
++    def test_logs_dont_go_to_stdout_if_fallback_to_stdout_is_false(self):
++        util.multi_log('something', fallback_to_stdout=False)
++        self.assertEqual('', self.stdout.getvalue())
++
+     def test_logs_go_to_log_if_given(self):
+         log = mock.MagicMock()
+         logged_string = 'something very important'
diff -Nru cloud-init-0.7.9/debian/patches/series 
cloud-init-0.7.9/debian/patches/series
--- cloud-init-0.7.9/debian/patches/series      2017-02-02 13:23:41.000000000 
+0000
+++ cloud-init-0.7.9/debian/patches/series      2021-03-19 17:34:55.000000000 
+0000
@@ -7,3 +7,4 @@
 0007-Skip-Cloudstack-tests-that-expect-network.patch
 0008-sysvinit-fs-dependencies.patch
 0009-Drop-all-unused-extended-version-handling.patch
+dont_log_generated_passwords.patch

Attachment: signature.asc
Description: PGP signature

Reply via email to