Codebase list python-rtslib-fb / 75e7377
rtslib: safely call shutil.copy() Previously we had to replace shutil.copyfile() with shutil.copy(), because we want to copy the file permissions to the destination file along with the data. It appears that shutil.copy() is opening the destination file with wide access (0666) first, and then it starts copying the data and at the end it is copying the permissions from source file to destination. If we closely notice there appears a window between destination file is opened vs permissions are set on the destination file, which could allow a user to get the contents of the file when opening it at the right time. The behavior is a bit unsteady here, it is noticed that, when saveconfig.json file exists, then on shutil.copy(), destination file is opened and a mask 0600 is applied on the file, in case shutil.copy() had to open a new destination saveconfig.json file, then mask 0644 is applied. Thanks and Credits to 'Stefan Cornelius <scorneli@redhat.com>' for reporting this, here is the strace he shared from RHEL-7/python-2.7.5 env: Case 1: When /etc/target/saveconfig.json doesn't exist: open("/etc/target/saveconfig.json.temp", O_RDONLY) = 3 open("/etc/target/saveconfig.json", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4 fstat(3, {st_mode=S_IFREG|0600, st_size=71, ...}) = 0 fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 [...] chmod("/etc/target/saveconfig.json", 0600) = 0}")")}") Case 2: When /etc/target/saveconfig.json already exist: open("/etc/target/saveconfig.json.temp", O_RDONLY) = 3 open("/etc/target/saveconfig.json", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4 fstat(3, {st_mode=S_IFREG|0600, st_size=71, ...}) = 0 fstat(4, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0 [...] chmod("/etc/target/saveconfig.json", 0600) = 0}")")}") Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com> Prasanna Kumar Kalever 3 years ago
1 changed file(s) with 3 addition(s) and 2 deletion(s). Raw diff Collapse all Expand all
475475 # prevent the file from being created if it exists due to a race
476476 try:
477477 fdesc = os.open(tmp_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL, mode)
478 finally:
479 os.umask(umask_original)
478 except OSError:
479 raise ExecutionError("Could not open %s" % tmp_file)
480480
481481 with os.fdopen(fdesc, 'w') as f:
482482 f.write(json.dumps(saveconf, sort_keys=True, indent=2))
487487
488488 # copy along with permissions
489489 shutil.copy(tmp_file, save_file)
490 os.umask(umask_original)
490491 os.remove(tmp_file)
491492
492493 def restore_from_file(self, restore_file=None, clear_existing=True,