From 8c7ceaab816652cef68a25342d4ba51821d6500c Mon Sep 17 00:00:00 2001 From: elotje Date: Wed, 15 Jun 2016 17:16:24 +0200 Subject: [PATCH] Unprivileged become on HP-UX/UNIX (Fixes #16249) (#16275) Problem: When setting the file permissions on the remote server for unprivileged users ansible expects that a chown will fail for unprivileged users. For some systems (e.g. HP-UX) this is not the case. Solution: Change the order how ansible sets the remote permissions. * If the remote_user sudo's to an unprivileged user then we attempt to grant the unprivileged user access via file system acls. * If granting file system acls fails we try to change the owner of the file with chown which only works in case the remote_user is privileged or the remote systems allows chown calls by unprivileged users (e.g. HP-UX) * If the chown fails we can set the file to be world readable so that the second unprivileged user can read the file. Since this could allow other users to get access to private information we only do this ansible is configured with "allow_world_readable_tmpfiles" in the ansible.cfg --- lib/ansible/plugins/action/__init__.py | 60 ++++++++++++-------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index fc885909fe1..02df152569e 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -301,17 +301,17 @@ class ActionBase(with_metaclass(ABCMeta, object)): information. We achieve this in one of these ways: * If no sudo is performed or the remote_user is sudo'ing to - themselves, we don't have to change permisions. + themselves, we don't have to change permissions. * If the remote_user sudo's to a privileged user (for instance, root), we don't have to change permissions - * If the remote_user is a privileged user and sudo's to an - unprivileged user then we change the owner of the file to the - unprivileged user so they can read it. - * If the remote_user is an unprivieged user and we're sudo'ing to - a second unprivileged user then we attempt to grant the second - unprivileged user access via file system acls. - * If granting file system acls fails we can set the file to be world - readable so that the second unprivileged user can read the file. + * If the remote_user sudo's to an unprivileged user then we attempt to + grant the unprivileged user access via file system acls. + * If granting file system acls fails we try to change the owner of the + file with chown which only works in case the remote_user is + privileged or the remote systems allows chown calls by unprivileged + users (e.g. HP-UX) + * If the chown fails we can set the file to be world readable so that + the second unprivileged user can read the file. Since this could allow other users to get access to private information we only do this ansible is configured with "allow_world_readable_tmpfiles" in the ansible.cfg @@ -333,35 +333,31 @@ class ActionBase(with_metaclass(ABCMeta, object)): # Unprivileged user that's different than the ssh user. Let's get # to work! - # Try chown'ing the file. This will only work if our SSH user has - # root privileges, but since we can't reliably determine that from - # the username (think "toor" on FreeBSD), let's just try first and - # apologize later: - res = self._remote_chown(remote_path, self._play_context.become_user, recursive=recursive) - if res['rc'] == 0: - # root can read things that don't have read bit but can't - # execute them without the execute bit, so we might need to - # set that even if we're root. We just ran chown successfully, - # so apparently we are root. + # Try to use file system acls to make the files readable for sudo'd + # user + if execute: + mode = 'rx' + else: + mode = 'rX' + + res = self._remote_set_user_facl(remote_path, self._play_context.become_user, mode, recursive=recursive, sudoable=False) + if res['rc'] != 0: + # File system acls failed; let's try to use chown next + # Set executable bit first as on some systems an + # unprivileged user can use chown if execute: res = self._remote_chmod('u+x', remote_path, recursive=recursive) if res['rc'] != 0: raise AnsibleError('Failed to set file mode on remote temporary files (rc: {0}, err: {1})'.format(res['rc'], res['stderr'])) - elif remote_user == 'root': - raise AnsibleError('Failed to change ownership of the temporary files Ansible needs to create despite connecting as root. Unprivileged become user would be unable to read the file.') - else: - # Chown'ing failed. We're probably lacking root privileges; let's try something else. - if execute: - mode = 'rx' - else: - mode = 'rX' - # Try to use fs acls to solve this problem - res = self._remote_set_user_facl(remote_path, self._play_context.become_user, mode, recursive=recursive, sudoable=False) - if res['rc'] != 0: + res = self._remote_chown(remote_path, self._play_context.become_user, recursive=recursive) + if res['rc'] != 0 and remote_user == 'root': + # chown failed even if remove_user is root + raise AnsibleError('Failed to change ownership of the temporary files Ansible needs to create despite connecting as root. Unprivileged become user would be unable to read the file.') + elif res['rc'] != 0: if C.ALLOW_WORLD_READABLE_TMPFILES: - # fs acls failed -- do things this insecure way only - # if the user opted in in the config file + # chown and fs acls failed -- do things this insecure + # way only if the user opted in in the config file display.warning('Using world-readable permissions for temporary files Ansible needs to create when becoming an unprivileged user which may be insecure. For information on securing this, see https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user') res = self._remote_chmod('a+%s' % mode, remote_path, recursive=recursive) if res['rc'] != 0: