Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

caution tape on makedirs_safe #55241

Merged
merged 1 commit into from Apr 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/ansible/plugins/connection/__init__.py
Expand Up @@ -207,7 +207,7 @@ def put_file(self, in_path, out_path):
@ensure_connect
@abstractmethod
def fetch_file(self, in_path, out_path):
"""Fetch a file from remote to local"""
"""Fetch a file from remote to local; callers are expected to have pre-created the directory chain for out_path"""
pass

@abstractmethod
Expand Down
6 changes: 2 additions & 4 deletions lib/ansible/plugins/connection/psrp.py
Expand Up @@ -277,7 +277,6 @@
from ansible.plugins.shell.powershell import _common_args
from ansible.utils.display import Display
from ansible.utils.hashing import secure_hash
from ansible.utils.path import makedirs_safe

HAS_PYPSRP = True
PYPSRP_IMP_ERR = None
Expand Down Expand Up @@ -538,12 +537,11 @@ def fetch_file(self, in_path, out_path):
raise AnsibleError("failed to setup file stream for fetch '%s': %s"
% (out_path, to_native(stderr)))
elif stdout.strip() == '[DIR]':
# in_path was a dir so we need to create the dir locally
makedirs_safe(out_path)
# to be consistent with other connection plugins, we assume the caller has created the target dir
return

b_out_path = to_bytes(out_path, errors='surrogate_or_strict')
makedirs_safe(os.path.dirname(b_out_path))
# to be consistent with other connection plugins, we assume the caller has created the target dir
offset = 0
with open(b_out_path, 'wb') as out_file:
while True:
Expand Down
4 changes: 1 addition & 3 deletions lib/ansible/plugins/connection/winrm.py
Expand Up @@ -123,7 +123,6 @@
from ansible.plugins.connection import ConnectionBase
from ansible.plugins.shell.powershell import _parse_clixml
from ansible.utils.hashing import secure_hash
from ansible.utils.path import makedirs_safe
from ansible.utils.display import Display

# getargspec is deprecated in favour of getfullargspec in Python 3 but
Expand Down Expand Up @@ -623,9 +622,9 @@ def fetch_file(self, in_path, out_path):
super(Connection, self).fetch_file(in_path, out_path)
in_path = self._shell._unquote(in_path)
out_path = out_path.replace('\\', '/')
# consistent with other connection plugins, we assume the caller has created the target dir
display.vvv('FETCH "%s" TO "%s"' % (in_path, out_path), host=self._winrm_host)
buffer_size = 2**19 # 0.5MB chunks
makedirs_safe(os.path.dirname(out_path))
out_file = None
try:
offset = 0
Expand Down Expand Up @@ -668,7 +667,6 @@ def fetch_file(self, in_path, out_path):
else:
data = base64.b64decode(result.std_out.strip())
if data is None:
makedirs_safe(out_path)
break
else:
if not out_file:
Expand Down
14 changes: 10 additions & 4 deletions lib/ansible/utils/path.py
Expand Up @@ -60,11 +60,17 @@ def unfrackpath(path, follow=True, basedir=None):


def makedirs_safe(path, mode=None):
'''Safe way to create dirs in muliprocess/thread environments.

:arg path: A byte or text string representing a directory to be created
'''
A *potentially insecure* way to ensure the existence of a directory chain. The "safe" in this function's name
refers only to its ability to ignore `EEXIST` in the case of multiple callers operating on the same part of
the directory chain. This function is not safe to use under world-writable locations when the first level of the
path to be created contains a predictable component. Always create a randomly-named element first if there is any
chance the parent directory might be world-writable (eg, /tmp) to prevent symlink hijacking and potential
disclosure or modification of sensitive file contents.

:arg path: A byte or text string representing a directory chain to be created
:kwarg mode: If given, the mode to set the directory to
:raises AnsibleError: If the directory cannot be created and does not already exists.
:raises AnsibleError: If the directory cannot be created and does not already exist.
:raises UnicodeDecodeError: if the path is not decodable in the utf-8 encoding.
'''

Expand Down