Blame SOURCES/0003-rtslib-safely-call-shutil.copy.patch

a5c0ca
From 75e73778dce1cb7a2816a936240ef75adfbd6ed9 Mon Sep 17 00:00:00 2001
a5c0ca
From: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
a5c0ca
Date: Thu, 16 Jul 2020 17:21:28 +0530
a5c0ca
Subject: [PATCH] rtslib: safely call shutil.copy()
a5c0ca
a5c0ca
Previously we had to replace shutil.copyfile() with shutil.copy(),
a5c0ca
because we want to copy the file permissions to the destination file
a5c0ca
along with the data.
a5c0ca
a5c0ca
It appears that shutil.copy() is opening the destination file with
a5c0ca
wide access (0666) first, and then it starts copying the data and
a5c0ca
at the end it is copying the permissions from source file to destination.
a5c0ca
a5c0ca
If we closely notice there appears a window between destination file
a5c0ca
is opened vs permissions are set on the destination file, which could
a5c0ca
allow a user to get the contents of the file when opening it at the
a5c0ca
right time.
a5c0ca
a5c0ca
The behavior is a bit unsteady here, it is noticed that, when
a5c0ca
saveconfig.json file exists, then on shutil.copy(), destination file is
a5c0ca
opened and a mask 0600 is applied on the file, in case shutil.copy() had
a5c0ca
to open a new destination saveconfig.json file, then mask 0644 is applied.
a5c0ca
a5c0ca
Thanks and Credits to 'Stefan Cornelius <scorneli@redhat.com>' for
a5c0ca
reporting this, here is the strace he shared from RHEL-7/python-2.7.5
a5c0ca
env:
a5c0ca
a5c0ca
Case 1: When /etc/target/saveconfig.json doesn't exist:
a5c0ca
a5c0ca
open("/etc/target/saveconfig.json.temp", O_RDONLY) = 3
a5c0ca
open("/etc/target/saveconfig.json", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4
a5c0ca
fstat(3, {st_mode=S_IFREG|0600, st_size=71, ...}) = 0
a5c0ca
fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
a5c0ca
[...]
a5c0ca
chmod("/etc/target/saveconfig.json", 0600) = 0}")")}")
a5c0ca
a5c0ca
Case 2: When /etc/target/saveconfig.json already exist:
a5c0ca
a5c0ca
open("/etc/target/saveconfig.json.temp", O_RDONLY) = 3
a5c0ca
open("/etc/target/saveconfig.json", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4
a5c0ca
fstat(3, {st_mode=S_IFREG|0600, st_size=71, ...}) = 0
a5c0ca
fstat(4, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
a5c0ca
[...]
a5c0ca
chmod("/etc/target/saveconfig.json", 0600) = 0}")")}")
a5c0ca
a5c0ca
Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
a5c0ca
---
a5c0ca
 rtslib/root.py | 5 +++--
a5c0ca
 1 file changed, 3 insertions(+), 2 deletions(-)
a5c0ca
a5c0ca
diff --git a/rtslib/root.py b/rtslib/root.py
a5c0ca
index 2c5cf43..4ecc03c 100644
a5c0ca
--- a/rtslib/root.py
a5c0ca
+++ b/rtslib/root.py
a5c0ca
@@ -476,8 +476,8 @@ class RTSRoot(CFSNode):
a5c0ca
         # prevent the file from being created if it exists due to a race
a5c0ca
         try:
a5c0ca
             fdesc = os.open(tmp_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL, mode)
a5c0ca
-        finally:
a5c0ca
-            os.umask(umask_original)
a5c0ca
+        except OSError:
a5c0ca
+            raise ExecutionError("Could not open %s" % tmp_file)
a5c0ca
 
a5c0ca
         with os.fdopen(fdesc, 'w') as f:
a5c0ca
             f.write(json.dumps(saveconf, sort_keys=True, indent=2))
a5c0ca
@@ -488,6 +488,7 @@ class RTSRoot(CFSNode):
a5c0ca
 
a5c0ca
         # copy along with permissions
a5c0ca
         shutil.copy(tmp_file, save_file)
a5c0ca
+        os.umask(umask_original)
a5c0ca
         os.remove(tmp_file)
a5c0ca
 
a5c0ca
     def restore_from_file(self, restore_file=None, clear_existing=True,
a5c0ca
-- 
a5c0ca
2.26.2
a5c0ca