Allow copy to writable files in unwritable dirs (#23509)

unsafe_writes currently allows updating a file that can be updated but
not removed (for instance, when docker mounted).  This change also
allows unsafe_writes to write to writable files in unwritable dirs. For
instance, if a system has made a single file inside of /etc/ writable to
a specific normal user.

Fixes #14961
This commit is contained in:
Toshio Kuratomi 2017-07-20 12:41:57 -07:00 committed by GitHub
parent 063f5d0ca8
commit 3a2afc7825
2 changed files with 46 additions and 36 deletions

View file

@ -2273,20 +2273,29 @@ class AnsibleModule(object):
native_dest_dir = b_dest_dir native_dest_dir = b_dest_dir
native_suffix = os.path.basename(b_dest) native_suffix = os.path.basename(b_dest)
native_prefix = b('.ansible_tmp') native_prefix = b('.ansible_tmp')
error_msg = None
tmp_dest_name = None
try: try:
tmp_dest_fd, tmp_dest_name = tempfile.mkstemp(prefix=native_prefix, dir=native_dest_dir, suffix=native_suffix) tmp_dest_fd, tmp_dest_name = tempfile.mkstemp(prefix=native_prefix, dir=native_dest_dir, suffix=native_suffix)
except (OSError, IOError): except (OSError, IOError):
e = get_exception() e = get_exception()
self.fail_json(msg='The destination directory (%s) is not writable by the current user. Error was: %s' % (os.path.dirname(dest), e)) error_msg = 'The destination directory (%s) is not writable by the current user. Error was: %s' % (os.path.dirname(dest), e)
except TypeError: except TypeError:
# We expect that this is happening because python3.4.x and # We expect that this is happening because python3.4.x and
# below can't handle byte strings in mkstemp(). Traceback # below can't handle byte strings in mkstemp(). Traceback
# would end in something like: # would end in something like:
# file = _os.path.join(dir, pre + name + suf) # file = _os.path.join(dir, pre + name + suf)
# TypeError: can't concat bytes to str # TypeError: can't concat bytes to str
self.fail_json(msg='Failed creating temp file for atomic move. This usually happens when using Python3 less than Python3.5. ' error_msg = ('Failed creating temp file for atomic move. This usually happens when using Python3 less than Python3.5. '
'Please use Python2.x or Python3.5 or greater.', exception=traceback.format_exc()) 'Please use Python2.x or Python3.5 or greater.')
finally:
if error_msg:
if unsafe_writes:
self._unsafe_writes(b_src, b_dest)
else:
self.fail_json(msg=error_msg, exception=traceback.format_exc())
if tmp_dest_name:
b_tmp_dest_name = to_bytes(tmp_dest_name, errors='surrogate_or_strict') b_tmp_dest_name = to_bytes(tmp_dest_name, errors='surrogate_or_strict')
try: try:

View file

@ -360,7 +360,8 @@ def main():
if "permission denied" in to_native(e).lower(): if "permission denied" in to_native(e).lower():
module.fail_json(msg="Destination directory %s is not accessible" % (os.path.dirname(dest))) module.fail_json(msg="Destination directory %s is not accessible" % (os.path.dirname(dest)))
module.fail_json(msg="Destination directory %s does not exist" % (os.path.dirname(dest))) module.fail_json(msg="Destination directory %s does not exist" % (os.path.dirname(dest)))
if not os.access(os.path.dirname(b_dest), os.W_OK):
if not os.access(os.path.dirname(b_dest), os.W_OK) and not module.params['unsafe_writes']:
module.fail_json(msg="Destination %s not writable" % (os.path.dirname(dest))) module.fail_json(msg="Destination %s not writable" % (os.path.dirname(dest)))
backup_file = None backup_file = None