Blame SOURCES/sos-bz1290954-sosreport-prepare-report-in-a-private-subdirectory.patch

28e5f0
From 6038fdf8617319a13b0b42f3283ec2066d54b283 Mon Sep 17 00:00:00 2001
28e5f0
From: "Bryn M. Reeves" <bmr@redhat.com>
28e5f0
Date: Thu, 19 Nov 2015 18:46:36 +0000
28e5f0
Subject: [PATCH] [policies] move hash determination to policies
28e5f0
28e5f0
It's crazy having the Policy classes call a function in the
28e5f0
utilities module only to have that function then load the Policy
28e5f0
module, call policy.get_preferred_hash_algorithm() and then test
28e5f0
the result.
28e5f0
28e5f0
Get rid of the get_hash_name() function in the utilities module
28e5f0
and simplify the calls in the policies module to obtain the
28e5f0
preferred hash name.
28e5f0
28e5f0
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
28e5f0
---
28e5f0
 sos/policies/__init__.py | 22 +++++++++++++++-------
28e5f0
 sos/utilities.py         | 12 ------------
28e5f0
 tests/utilities_tests.py |  2 +-
28e5f0
 3 files changed, 16 insertions(+), 20 deletions(-)
28e5f0
28e5f0
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
28e5f0
index a403bb9..bf9afde 100644
28e5f0
--- a/sos/policies/__init__.py
28e5f0
+++ b/sos/policies/__init__.py
28e5f0
@@ -10,7 +10,6 @@ from os import environ
28e5f0
 
28e5f0
 from sos.utilities import (ImporterHelper,
28e5f0
                            import_module,
28e5f0
-                           get_hash_name,
28e5f0
                            shell_out)
28e5f0
 from sos.plugins import IndependentPlugin
28e5f0
 from sos import _sos as _
28e5f0
@@ -286,17 +285,17 @@ No changes will be made to system configuration.
28e5f0
         considered to be a superuser"""
28e5f0
         return (os.getuid() == 0)
28e5f0
 
28e5f0
-    def _create_checksum(self, final_filename=None):
28e5f0
+    def _create_checksum(self, hash_name, final_filename=None):
28e5f0
         if not final_filename:
28e5f0
             return False
28e5f0
 
28e5f0
         archive_fp = open(final_filename, 'rb')
28e5f0
-        digest = hashlib.new(get_hash_name())
28e5f0
+        digest = hashlib.new(hash_name)
28e5f0
         digest.update(archive_fp.read())
28e5f0
         archive_fp.close()
28e5f0
         return digest.hexdigest()
28e5f0
 
28e5f0
-    def get_preferred_hash_algorithm(self):
28e5f0
+    def get_preferred_hash_name(self):
28e5f0
         """Returns the string name of the hashlib-supported checksum algorithm
28e5f0
         to use"""
28e5f0
         return "md5"
28e5f0
@@ -309,10 +308,11 @@ No changes will be made to system configuration.
28e5f0
 
28e5f0
         self._print()
28e5f0
 
28e5f0
+        hash_name = self.get_preferred_hash_name()
28e5f0
         if not build:
28e5f0
             # store checksum into file
28e5f0
-            fp = open(final_filename + "." + get_hash_name(), "w")
28e5f0
-            checksum = self._create_checksum(final_filename)
28e5f0
+            fp = open(final_filename + "." + hash_name, "w")
28e5f0
+            checksum = self._create_checksum(hash_name, final_filename)
28e5f0
             if checksum:
28e5f0
                 fp.write(checksum + "\n")
28e5f0
             fp.close()
28e5f0
@@ -373,20 +373,28 @@ class LinuxPolicy(Policy):
28e5f0
     vendor = "None"
28e5f0
     PATH = "/bin:/sbin:/usr/bin:/usr/sbin"
28e5f0
 
28e5f0
+    _preferred_hash_name = None
28e5f0
+
28e5f0
     def __init__(self, sysroot=None):
28e5f0
         super(LinuxPolicy, self).__init__(sysroot=sysroot)
28e5f0
 
28e5f0
-    def get_preferred_hash_algorithm(self):
28e5f0
+    def get_preferred_hash_name(self):
28e5f0
+
28e5f0
+        if self._preferred_hash_name:
28e5f0
+            return self._preferred_hash_name
28e5f0
+
28e5f0
         checksum = "md5"
28e5f0
         try:
28e5f0
             fp = open("/proc/sys/crypto/fips_enabled", "r")
28e5f0
         except:
28e5f0
+            self._preferred_hash_name = checksum
28e5f0
             return checksum
28e5f0
 
28e5f0
         fips_enabled = fp.read()
28e5f0
         if fips_enabled.find("1") >= 0:
28e5f0
             checksum = "sha256"
28e5f0
         fp.close()
28e5f0
+        self._preferred_hash_name = checksum
28e5f0
         return checksum
28e5f0
 
28e5f0
     def default_runlevel(self):
28e5f0
diff --git a/sos/utilities.py b/sos/utilities.py
28e5f0
index d3a1048..63e7ee4 100644
28e5f0
--- a/sos/utilities.py
28e5f0
+++ b/sos/utilities.py
28e5f0
@@ -52,18 +52,6 @@ def fileobj(path_or_file, mode='r'):
28e5f0
         return closing(path_or_file)
28e5f0
 
28e5f0
 
28e5f0
-def get_hash_name():
28e5f0
-    """Returns the algorithm used when computing a hash"""
28e5f0
-    import sos.policies
28e5f0
-    policy = sos.policies.load()
28e5f0
-    try:
28e5f0
-        name = policy.get_preferred_hash_algorithm()
28e5f0
-        hashlib.new(name)
28e5f0
-        return name
28e5f0
-    except:
28e5f0
-        return 'sha256'
28e5f0
-
28e5f0
-
28e5f0
 def convert_bytes(bytes_, K=1 << 10, M=1 << 20, G=1 << 30, T=1 << 40):
28e5f0
     """Converts a number of bytes to a shorter, more human friendly format"""
28e5f0
     fn = float(bytes_)
28e5f0
diff --git a/tests/utilities_tests.py b/tests/utilities_tests.py
28e5f0
index c464692..f1b60e2 100644
28e5f0
--- a/tests/utilities_tests.py
28e5f0
+++ b/tests/utilities_tests.py
28e5f0
@@ -5,7 +5,7 @@ import unittest
28e5f0
 import six
28e5f0
 from six import StringIO
28e5f0
 
28e5f0
-from sos.utilities import grep, get_hash_name, is_executable, sos_get_command_output, find, tail, shell_out
28e5f0
+from sos.utilities import grep, is_executable, sos_get_command_output, find, tail, shell_out
28e5f0
 import sos
28e5f0
 
28e5f0
 TEST_DIR = os.path.dirname(__file__)
28e5f0
-- 
28e5f0
2.4.3
28e5f0
28e5f0
From 7f2727749d0c37095a20c5d4cf6f9a2e086a2375 Mon Sep 17 00:00:00 2001
28e5f0
From: "Bryn M. Reeves" <bmr@redhat.com>
28e5f0
Date: Thu, 19 Nov 2015 19:07:50 +0000
28e5f0
Subject: [PATCH] [policies] refactor Policy.display_results() args
28e5f0
28e5f0
Pass explicit archive and build directory arguments to the
28e5f0
Policy.display_results() method rather than a single path name
28e5f0
argument and a boolean to indicate whether it is an archive file
28e5f0
or a build directory path.
28e5f0
28e5f0
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
28e5f0
---
28e5f0
 sos/policies/__init__.py | 24 ++++++++++++++----------
28e5f0
 sos/sosreport.py         | 12 +++++++++---
28e5f0
 2 files changed, 23 insertions(+), 13 deletions(-)
28e5f0
28e5f0
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
28e5f0
index bf9afde..5b0b706 100644
28e5f0
--- a/sos/policies/__init__.py
28e5f0
+++ b/sos/policies/__init__.py
28e5f0
@@ -285,11 +285,11 @@ No changes will be made to system configuration.
28e5f0
         considered to be a superuser"""
28e5f0
         return (os.getuid() == 0)
28e5f0
 
28e5f0
-    def _create_checksum(self, hash_name, final_filename=None):
28e5f0
-        if not final_filename:
28e5f0
+    def _create_checksum(self, hash_name, archive=None):
28e5f0
+        if not archive:
28e5f0
             return False
28e5f0
 
28e5f0
-        archive_fp = open(final_filename, 'rb')
28e5f0
+        archive_fp = open(archive, 'rb')
28e5f0
         digest = hashlib.new(hash_name)
28e5f0
         digest.update(archive_fp.read())
28e5f0
         archive_fp.close()
28e5f0
@@ -300,29 +300,33 @@ No changes will be made to system configuration.
28e5f0
         to use"""
28e5f0
         return "md5"
28e5f0
 
28e5f0
-    def display_results(self, final_filename=None, build=False):
28e5f0
+    def display_results(self, archive, directory):
28e5f0
+        # Display results is called from the tail of SoSReport.final_work()
28e5f0
+        #
28e5f0
+        # Logging is already shutdown and all terminal output must use the
28e5f0
+        # print() call.
28e5f0
 
28e5f0
         # make sure a report exists
28e5f0
-        if not final_filename:
28e5f0
+        if not archive and not directory:
28e5f0
             return False
28e5f0
 
28e5f0
         self._print()
28e5f0
 
28e5f0
         hash_name = self.get_preferred_hash_name()
28e5f0
-        if not build:
28e5f0
+        if archive:
28e5f0
             # store checksum into file
28e5f0
-            fp = open(final_filename + "." + hash_name, "w")
28e5f0
-            checksum = self._create_checksum(hash_name, final_filename)
28e5f0
+            fp = open(archive + "." + hash_name, "w")
28e5f0
+            checksum = self._create_checksum(hash_name, archive)
28e5f0
             if checksum:
28e5f0
                 fp.write(checksum + "\n")
28e5f0
             fp.close()
28e5f0
 
28e5f0
             self._print(_("Your sosreport has been generated and saved "
28e5f0
-                        "in:\n  %s") % final_filename)
28e5f0
+                        "in:\n  %s") % archive)
28e5f0
         else:
28e5f0
             checksum = None
28e5f0
             self._print(_("sosreport build tree is located at : %s" %
28e5f0
-                        final_filename))
28e5f0
+                        directory))
28e5f0
 
28e5f0
         self._print()
28e5f0
         if checksum:
28e5f0
diff --git a/sos/sosreport.py b/sos/sosreport.py
28e5f0
index a1f1b96..f3e0f34 100644
28e5f0
--- a/sos/sosreport.py
28e5f0
+++ b/sos/sosreport.py
28e5f0
@@ -1436,6 +1436,10 @@ class SoSReport(object):
28e5f0
         # this must come before archive creation to ensure that log
28e5f0
         # files are closed and cleaned up at exit.
28e5f0
         self._finish_logging()
28e5f0
+
28e5f0
+        archive = None    # archive path
28e5f0
+        directory = None  # report directory path (--build)
28e5f0
+
28e5f0
         # package up the results for the support organization
28e5f0
         if not self.opts.build:
28e5f0
             old_umask = os.umask(0o077)
28e5f0
@@ -1443,7 +1447,7 @@ class SoSReport(object):
28e5f0
                 print(_("Creating compressed archive..."))
28e5f0
             # compression could fail for a number of reasons
28e5f0
             try:
28e5f0
-                final_filename = self.archive.finalize(
28e5f0
+                archive = self.archive.finalize(
28e5f0
                     self.opts.compression_type)
28e5f0
             except (OSError, IOError) as e:
28e5f0
                 if e.errno in fatal_fs_errors:
28e5f0
@@ -1460,8 +1464,10 @@ class SoSReport(object):
28e5f0
             finally:
28e5f0
                 os.umask(old_umask)
28e5f0
         else:
28e5f0
-            final_filename = self.archive.get_archive_path()
28e5f0
-        self.policy.display_results(final_filename, build=self.opts.build)
28e5f0
+            directory = self.archive.get_archive_path()
28e5f0
+
28e5f0
+        self.policy.display_results(archive, directory)
28e5f0
+
28e5f0
         self.tempfile_util.clean()
28e5f0
         return True
28e5f0
 
28e5f0
-- 
28e5f0
2.4.3
28e5f0
28e5f0
From 19e2bbccb6a86d6ea94f5c82860bed4d2276bbf3 Mon Sep 17 00:00:00 2001
28e5f0
From: "Bryn M. Reeves" <bmr@redhat.com>
28e5f0
Date: Thu, 19 Nov 2015 19:19:42 +0000
28e5f0
Subject: [PATCH] [sosreport] move archive checksumming to sosreport
28e5f0
28e5f0
Although the digest algorithm is policy controlled the actual
28e5f0
mechanism to checksum the archive does not belong in the policies
28e5f0
module: historically this was done to keep the code that calculates
28e5f0
the checksum close to the UI code that reports it.
28e5f0
28e5f0
Move the calculation to the main SoSReport class's final_work()
28e5f0
method and add a 'checksum' argument to the display_results()
28e5f0
method so that the value can be reported.
28e5f0
28e5f0
In future it may make sense to push the checksum code directly into
28e5f0
the archive class.
28e5f0
28e5f0
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
28e5f0
---
28e5f0
 sos/policies/__init__.py | 22 +---------------------
28e5f0
 sos/sosreport.py         | 26 +++++++++++++++++++++++++-
28e5f0
 2 files changed, 26 insertions(+), 22 deletions(-)
28e5f0
28e5f0
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
28e5f0
index 5b0b706..20e0e3b 100644
28e5f0
--- a/sos/policies/__init__.py
28e5f0
+++ b/sos/policies/__init__.py
28e5f0
@@ -13,7 +13,6 @@ from sos.utilities import (ImporterHelper,
28e5f0
                            shell_out)
28e5f0
 from sos.plugins import IndependentPlugin
28e5f0
 from sos import _sos as _
28e5f0
-import hashlib
28e5f0
 from textwrap import fill
28e5f0
 from six import print_
28e5f0
 from six.moves import input
28e5f0
@@ -285,22 +284,12 @@ No changes will be made to system configuration.
28e5f0
         considered to be a superuser"""
28e5f0
         return (os.getuid() == 0)
28e5f0
 
28e5f0
-    def _create_checksum(self, hash_name, archive=None):
28e5f0
-        if not archive:
28e5f0
-            return False
28e5f0
-
28e5f0
-        archive_fp = open(archive, 'rb')
28e5f0
-        digest = hashlib.new(hash_name)
28e5f0
-        digest.update(archive_fp.read())
28e5f0
-        archive_fp.close()
28e5f0
-        return digest.hexdigest()
28e5f0
-
28e5f0
     def get_preferred_hash_name(self):
28e5f0
         """Returns the string name of the hashlib-supported checksum algorithm
28e5f0
         to use"""
28e5f0
         return "md5"
28e5f0
 
28e5f0
-    def display_results(self, archive, directory):
28e5f0
+    def display_results(self, archive, directory, checksum):
28e5f0
         # Display results is called from the tail of SoSReport.final_work()
28e5f0
         #
28e5f0
         # Logging is already shutdown and all terminal output must use the
28e5f0
@@ -312,19 +301,10 @@ No changes will be made to system configuration.
28e5f0
 
28e5f0
         self._print()
28e5f0
 
28e5f0
-        hash_name = self.get_preferred_hash_name()
28e5f0
         if archive:
28e5f0
-            # store checksum into file
28e5f0
-            fp = open(archive + "." + hash_name, "w")
28e5f0
-            checksum = self._create_checksum(hash_name, archive)
28e5f0
-            if checksum:
28e5f0
-                fp.write(checksum + "\n")
28e5f0
-            fp.close()
28e5f0
-
28e5f0
             self._print(_("Your sosreport has been generated and saved "
28e5f0
                         "in:\n  %s") % archive)
28e5f0
         else:
28e5f0
-            checksum = None
28e5f0
             self._print(_("sosreport build tree is located at : %s" %
28e5f0
                         directory))
28e5f0
 
28e5f0
diff --git a/sos/sosreport.py b/sos/sosreport.py
28e5f0
index f3e0f34..f7a5f11 100644
28e5f0
--- a/sos/sosreport.py
28e5f0
+++ b/sos/sosreport.py
28e5f0
@@ -33,6 +33,7 @@ from stat import ST_UID, ST_GID, ST_MODE, ST_CTIME, ST_ATIME, ST_MTIME, S_IMODE
28e5f0
 from time import strftime, localtime
28e5f0
 from collections import deque
28e5f0
 import tempfile
28e5f0
+import hashlib
28e5f0
 
28e5f0
 from sos import _sos as _
28e5f0
 from sos import __version__
28e5f0
@@ -1432,6 +1433,18 @@ class SoSReport(object):
28e5f0
                     raise
28e5f0
                 self._log_plugin_exception(plugname, "postproc")
28e5f0
 
28e5f0
+    def _create_checksum(self, archive=None):
28e5f0
+        if not archive:
28e5f0
+            return False
28e5f0
+
28e5f0
+        hash_name = self.policy.get_preferred_hash_name()
28e5f0
+
28e5f0
+        archive_fp = open(archive, 'rb')
28e5f0
+        digest = hashlib.new(hash_name)
28e5f0
+        digest.update(archive_fp.read())
28e5f0
+        archive_fp.close()
28e5f0
+        return digest.hexdigest()
28e5f0
+
28e5f0
     def final_work(self):
28e5f0
         # this must come before archive creation to ensure that log
28e5f0
         # files are closed and cleaned up at exit.
28e5f0
@@ -1466,7 +1479,18 @@ class SoSReport(object):
28e5f0
         else:
28e5f0
             directory = self.archive.get_archive_path()
28e5f0
 
28e5f0
-        self.policy.display_results(archive, directory)
28e5f0
+        hash_name = self.policy.get_preferred_hash_name()
28e5f0
+        checksum = None
28e5f0
+
28e5f0
+        if hash_name and not self.opts.build:
28e5f0
+            # store checksum into file
28e5f0
+            fp = open(archive + "." + hash_name, "w")
28e5f0
+            checksum = self._create_checksum(archive)
28e5f0
+            if checksum:
28e5f0
+                fp.write(checksum + "\n")
28e5f0
+            fp.close()
28e5f0
+
28e5f0
+        self.policy.display_results(archive, directory, checksum)
28e5f0
 
28e5f0
         self.tempfile_util.clean()
28e5f0
         return True
28e5f0
-- 
28e5f0
2.4.3
28e5f0
28e5f0
From 4a9b919a7f1b9542a23982e49cc9035e84551e13 Mon Sep 17 00:00:00 2001
28e5f0
From: "Bryn M. Reeves" <bmr@redhat.com>
28e5f0
Date: Fri, 20 Nov 2015 18:48:33 +0000
28e5f0
Subject: [PATCH] [sosreport] prepare report in a private subdirectory
28e5f0
28e5f0
To avoid file creation races in shared temporary directories like
28e5f0
/tmp and /var/tmp use a private (0700) subdirectory to build the
28e5f0
FileCacheArchive and subsequent archive and compressed archive
28e5f0
files: only create a file in the containing directory when it can
28e5f0
be done as a single atomic rename.
28e5f0
28e5f0
This prevents sos from writing to an arbitrary location under the
28e5f0
control of another user: a malicious user could steal data or over
28e5f0
write files in /etc resulting in a local privilege escalation.
28e5f0
28e5f0
There remains a further race since once the archive name is known
28e5f0
the checksum file name becomes predictable: as the checksum file
28e5f0
is also prepared in the subdirectory and moved into place the
28e5f0
result is always either success or an error that is reported to
28e5f0
the user.
28e5f0
28e5f0
The correct checksum value is still reported to the user via the
28e5f0
terminal.
28e5f0
28e5f0
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
28e5f0
---
28e5f0
 sos/sosreport.py | 100 ++++++++++++++++++++++++++++++++++++++++++-------------
28e5f0
 1 file changed, 77 insertions(+), 23 deletions(-)
28e5f0
28e5f0
diff --git a/sos/sosreport.py b/sos/sosreport.py
28e5f0
index f7a5f11..fe251ed 100644
28e5f0
--- a/sos/sosreport.py
28e5f0
+++ b/sos/sosreport.py
28e5f0
@@ -32,6 +32,7 @@ from sos.utilities import ImporterHelper
28e5f0
 from stat import ST_UID, ST_GID, ST_MODE, ST_CTIME, ST_ATIME, ST_MTIME, S_IMODE
28e5f0
 from time import strftime, localtime
28e5f0
 from collections import deque
28e5f0
+from shutil import rmtree
28e5f0
 import tempfile
28e5f0
 import hashlib
28e5f0
28e5f0
@@ -678,6 +679,7 @@ class SoSReport(object):
28e5f0
         self.tempfile_util = None
28e5f0
         self._args = args
28e5f0
         self.sysroot = "/"
28e5f0
+        self.sys_tmp = None
28e5f0
 
28e5f0
         try:
28e5f0
             import signal
28e5f0
@@ -678,15 +678,22 @@ class SoSReport(object):
28e5f0
 
28e5f0
         self._is_root = self.policy.is_root()
28e5f0
 
28e5f0
-        self.tmpdir = os.path.abspath(
28e5f0
-            self.policy.get_tmp_dir(self.opts.tmp_dir))
28e5f0
-        if not os.path.isdir(self.tmpdir) \
28e5f0
-                or not os.access(self.tmpdir, os.W_OK):
28e5f0
+        # system temporary directory to use
28e5f0
+        tmp = os.path.abspath(self.policy.get_tmp_dir(self.opts.tmp_dir))
28e5f0
+
28e5f0
+        if not os.path.isdir(tmp) \
28e5f0
+                or not os.access(tmp, os.W_OK):
28e5f0
             # write directly to stderr as logging is not initialised yet
28e5f0
-            sys.stderr.write("temporary directory %s " % self.tmpdir
28e5f0
+            sys.stderr.write("temporary directory %s " % tmp
28e5f0
                              + "does not exist or is not writable\n")
28e5f0
             self._exit(1)
28e5f0
+
28e5f0
+        self.sys_tmp = tmp
28e5f0
+
28e5f0
+        # our (private) temporary directory
28e5f0
+        self.tmpdir = tempfile.mkdtemp(prefix="sos.", dir=self.sys_tmp)
28e5f0
         self.tempfile_util = TempFileUtil(self.tmpdir)
28e5f0
+
28e5f0
         self._set_directories()
28e5f0
 
28e5f0
         self._setup_logging()
28e5f0
@@ -1433,27 +1442,34 @@ class SoSReport(object):
28e5f0
                     raise
28e5f0
                 self._log_plugin_exception(plugname, "postproc")
28e5f0
 
28e5f0
-    def _create_checksum(self, archive=None):
28e5f0
+    def _create_checksum(self, archive, hash_name):
28e5f0
         if not archive:
28e5f0
             return False
28e5f0
 
28e5f0
-        hash_name = self.policy.get_preferred_hash_name()
28e5f0
-
28e5f0
         archive_fp = open(archive, 'rb')
28e5f0
         digest = hashlib.new(hash_name)
28e5f0
         digest.update(archive_fp.read())
28e5f0
         archive_fp.close()
28e5f0
         return digest.hexdigest()
28e5f0
 
28e5f0
+    def _write_checksum(self, archive, hash_name, checksum):
28e5f0
+            # store checksum into file
28e5f0
+            fp = open(archive + "." + hash_name, "w")
28e5f0
+            if checksum:
28e5f0
+                fp.write(checksum + "\n")
28e5f0
+            fp.close()
28e5f0
+
28e5f0
     def final_work(self):
28e5f0
-        # this must come before archive creation to ensure that log
28e5f0
+        # This must come before archive creation to ensure that log
28e5f0
         # files are closed and cleaned up at exit.
28e5f0
+        #
28e5f0
+        # All subsequent terminal output must use print().
28e5f0
         self._finish_logging()
28e5f0
 
28e5f0
         archive = None    # archive path
28e5f0
         directory = None  # report directory path (--build)
28e5f0
 
28e5f0
-        # package up the results for the support organization
28e5f0
+        # package up and compress the results
28e5f0
         if not self.opts.build:
28e5f0
             old_umask = os.umask(0o077)
28e5f0
             if not self.opts.quiet:
28e5f0
@@ -1464,10 +1480,9 @@ class SoSReport(object):
28e5f0
                     self.opts.compression_type)
28e5f0
             except (OSError, IOError) as e:
28e5f0
                 if e.errno in fatal_fs_errors:
28e5f0
-                    self.ui_log.error("")
28e5f0
-                    self.ui_log.error(" %s while finalizing archive"
28e5f0
-                                      % e.strerror)
28e5f0
-                    self.ui_log.error("")
28e5f0
+                    print("")
28e5f0
+                    print(_(" %s while finalizing archive" % e.strerror))
28e5f0
+                    print("")
28e5f0
                     self._exit(1)
28e5f0
             except:
28e5f0
                 if self.opts.debug:
28e5f0
@@ -1477,18 +1492,55 @@ class SoSReport(object):
28e5f0
             finally:
28e5f0
                 os.umask(old_umask)
28e5f0
         else:
28e5f0
+            # move the archive root out of the private tmp directory.
28e5f0
             directory = self.archive.get_archive_path()
28e5f0
+            dir_name = os.path.basename(directory)
28e5f0
+            try:
28e5f0
+                os.rename(directory, os.path.join(self.sys_tmp, dir_name))
28e5f0
+            except (OSError, IOError):
28e5f0
+                    print(_("Error moving directory: %s" % directory))
28e5f0
+                    return False
28e5f0
 
28e5f0
-        hash_name = self.policy.get_preferred_hash_name()
28e5f0
         checksum = None
28e5f0
 
28e5f0
-        if hash_name and not self.opts.build:
28e5f0
-            # store checksum into file
28e5f0
-            fp = open(archive + "." + hash_name, "w")
28e5f0
-            checksum = self._create_checksum(archive)
28e5f0
-            if checksum:
28e5f0
-                fp.write(checksum + "\n")
28e5f0
-            fp.close()
28e5f0
+        if not self.opts.build:
28e5f0
+            # compute and store the archive checksum
28e5f0
+            hash_name = self.policy.get_preferred_hash_name()
28e5f0
+            checksum = self._create_checksum(archive, hash_name)
28e5f0
+            self._write_checksum(archive, hash_name, checksum)
28e5f0
+
28e5f0
+            # output filename is in the private tmpdir - move it to the
28e5f0
+            # containing directory.
28e5f0
+            final_name = os.path.join(self.sys_tmp, os.path.basename(archive))
28e5f0
+
28e5f0
+            archive_hash = archive + "." + hash_name
28e5f0
+            final_hash = final_name + "." + hash_name
28e5f0
+
28e5f0
+            # move the archive and checksum file
28e5f0
+            try:
28e5f0
+                    os.rename(archive, final_name)
28e5f0
+                    archive = final_name
28e5f0
+            except (OSError, IOError):
28e5f0
+                    print(_("Error moving archive file: %s" % archive))
28e5f0
+                    return False
28e5f0
+
28e5f0
+            # There is a race in the creation of the final checksum file:
28e5f0
+            # since the archive has already been published and the checksum
28e5f0
+            # file name is predictable once the archive name is known a
28e5f0
+            # malicious user could attempt to create a symbolic link in order
28e5f0
+            # to misdirect writes to a file of the attacker's choosing.
28e5f0
+            #
28e5f0
+            # To mitigate this we write the checksum inside the private tmp
28e5f0
+            # directory and use an atomic rename that is guaranteed to either
28e5f0
+            # succeed or fail: at worst the move will fail and be reported to
28e5f0
+            # the user. The correct checksum value is still written to the
28e5f0
+            # terminal and nothing is written to a location under the control
28e5f0
+            # of the user creating the link.
28e5f0
+            try:
28e5f0
+                    os.rename(archive_hash, final_hash)
28e5f0
+            except (OSError, IOError):
28e5f0
+                    print(_("Error moving checksum file: %s" % archive_hash))
28e5f0
+                    return False
28e5f0
 
28e5f0
         self.policy.display_results(archive, directory, checksum)
28e5f0
 
28e5f0
@@ -1546,8 +1598,10 @@ class SoSReport(object):
28e5f0
                     self.archive.cleanup()
28e5f0
                 if self.tempfile_util:
28e5f0
                     self.tempfile_util.clean()
28e5f0
+                if self.tmpdir:
28e5f0
+                    rmtree(self.tmpdir)
28e5f0
             except:
28e5f0
-                pass
28e5f0
+                raise
28e5f0
 
28e5f0
         return False
28e5f0
 
28e5f0
-- 
28e5f0
2.4.3
28e5f0
28e5f0
From 08121d877741e33333a1ae01280f6898d7d4ca15 Mon Sep 17 00:00:00 2001
28e5f0
From: "Bryn M. Reeves" <bmr@redhat.com>
28e5f0
Date: Thu, 10 Dec 2015 11:50:49 +0000
28e5f0
Subject: [PATCH] [sosreport] clean up private temporary directory
28e5f0
28e5f0
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
28e5f0
---
28e5f0
 sos/sosreport.py | 7 ++++++-
28e5f0
 1 file changed, 6 insertions(+), 1 deletion(-)
28e5f0
28e5f0
diff --git a/sos/sosreport.py b/sos/sosreport.py
28e5f0
index fe251ed..bc7792a 100644
28e5f0
--- a/sos/sosreport.py
28e5f0
+++ b/sos/sosreport.py
28e5f0
@@ -1544,7 +1544,12 @@ class SoSReport(object):
28e5f0
28e5f0
         self.policy.display_results(archive, directory, checksum)
28e5f0
28e5f0
-        self.tempfile_util.clean()
28e5f0
+        # clean up
28e5f0
+        if self.tempfile_util:
28e5f0
+            self.tempfile_util.clean()
28e5f0
+        if self.tmpdir:
28e5f0
+            rmtree(self.tmpdir)
28e5f0
+
28e5f0
         return True
28e5f0
28e5f0
     def verify_plugins(self):
28e5f0
-- 
28e5f0
2.4.3
28e5f0
28e5f0
From a7a2dd8eb1791257f80fd2ae2993b06472690aea Mon Sep 17 00:00:00 2001
28e5f0
From: "Bryn M. Reeves" <bmr@redhat.com>
28e5f0
Date: Wed, 13 Jan 2016 10:57:59 +0000
28e5f0
Subject: [PATCH] [sosreport] report correct final path with --build
28e5f0
28e5f0
Ensure the correct path (in the system temporary directory) is
28e5f0
reported when the --build option is used.
28e5f0
28e5f0
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
28e5f0
---
28e5f0
 sos/sosreport.py | 4 +++-
28e5f0
 1 file changed, 3 insertions(+), 1 deletion(-)
28e5f0
28e5f0
diff --git a/sos/sosreport.py b/sos/sosreport.py
28e5f0
index bc7792a..f61ea4a 100644
28e5f0
--- a/sos/sosreport.py
28e5f0
+++ b/sos/sosreport.py
28e5f0
@@ -1496,7 +1496,9 @@ class SoSReport(object):
28e5f0
             directory = self.archive.get_archive_path()
28e5f0
             dir_name = os.path.basename(directory)
28e5f0
             try:
28e5f0
-                os.rename(directory, os.path.join(self.sys_tmp, dir_name))
28e5f0
+                final_dir = os.path.join(self.sys_tmp, dir_name)
28e5f0
+                os.rename(directory, final_dir)
28e5f0
+                directory = final_dir
28e5f0
             except (OSError, IOError):
28e5f0
                     print(_("Error moving directory: %s" % directory))
28e5f0
                     return False
28e5f0
-- 
28e5f0
2.4.3
28e5f0