Repository: ansible/ansible

Issue:
In 2.5, the script module fails with privilege escalation, due to permission issues
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
<!---
Name of the module, plugin, task or feature
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->
Privilege escalation and the script module

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
$ PYTHONPATH=~/ansible-2.5.0b2/lib ~/ansible-2.5.0b2/bin/ansible --version
ansible 2.5.0b2
  config file = /home/centos/ansible.cfg
  configured module search path = [u'/home/centos/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/centos/ansible-2.5.0b2/lib/ansible
  executable location = /home/centos/ansible-2.5.0b2/bin/ansible
  python version = 2.7.5 (default, Aug  4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
$
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
DEFAULT_ROLES_PATH(/etc/ansible/ansible.cfg) = [u'/etc/ansible/roles', u'/usr/share/ansible/roles']

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
CentOS 7.4

##### SUMMARY
Prior to 2.5, script tasks run with privilege escalation applied setfacl to both the housing directory and the target host script.  With 2.5, setfacl is applying solely to the target host script.  The housing directory is not opened to the become_user, so the task fails with permissions issues.

#35666 is possibly related, where there were permission failures for tasks with privilege escalation and deep remote_tmp directories.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->
ansible-playbook -i inventory.ini playbook.yml -vvv

<!--- Paste example playbooks or commands between quotes below -->
inventory.ini
```
target ansible_connection=local
```

playbook.yml
```yaml
---
- hosts: all
  tasks:
  - debug:
      var: ansible_version.full
  - name: By default user
    script: /home/centos/test.sh
  - name: By another user
    become: true
    become_user: another
    script: /home/centos/test.sh
```

```bash
#!/bin/bash

echo "Hi there, $(whoami)"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
I expected the playbook to run to completion, as it does in 2.4.2.  The setfacl call in the "By another user" task applies to both the housing directory and the script.

```
$ ansible-playbook -i inventory.ini playbook.yml -vvv
ansible-playbook 2.4.2.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/centos/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible-playbook
  python version = 2.7.5 (default, Aug  4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
Using /etc/ansible/ansible.cfg as config file
Parsed /home/centos/inventory.ini inventory source with ini plugin

PLAYBOOK: playbook.yml ******************************************************************************************************************************
1 plays in playbook.yml

PLAY [all] ******************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************
Using module file /usr/lib/python2.7/site-packages/ansible/modules/system/setup.py
<target> ESTABLISH LOCAL CONNECTION FOR USER: centos
<target> EXEC /bin/sh -c 'echo ~ && sleep 0'
<target> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/centos/.ansible/tmp/ansible-tmp-1519056612.13-105399086177092 `" && echo ansible-tmp-1519056612.13-105399086177092="` echo /home/centos/.ansible/tmp/ansible-tmp-1519056612.13-105399086177092 `" ) && sleep 0'
<target> PUT /tmp/tmpL2Iqku TO /home/centos/.ansible/tmp/ansible-tmp-1519056612.13-105399086177092/setup.py
<target> EXEC /bin/sh -c 'chmod u+x /home/centos/.ansible/tmp/ansible-tmp-1519056612.13-105399086177092/ /home/centos/.ansible/tmp/ansible-tmp-1519056612.13-105399086177092/setup.py && sleep 0'
<target> EXEC /bin/sh -c '/usr/bin/python /home/centos/.ansible/tmp/ansible-tmp-1519056612.13-105399086177092/setup.py; rm -rf "/home/centos/.ansible/tmp/ansible-tmp-1519056612.13-105399086177092/" > /dev/null 2>&1 && sleep 0'
ok: [target]
META: ran handlers

TASK [debug] ****************************************************************************************************************************************
task path: /home/centos/playbook.yml:4
ok: [target] => {
    "ansible_version.full": "2.4.2.0"
}

TASK [By default user] ******************************************************************************************************************************
task path: /home/centos/playbook.yml:6
<target> ESTABLISH LOCAL CONNECTION FOR USER: centos
<target> EXEC /bin/sh -c 'echo ~ && sleep 0'
<target> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/centos/.ansible/tmp/ansible-tmp-1519056612.49-257402348266617 `" && echo ansible-tmp-1519056612.49-257402348266617="` echo /home/centos/.ansible/tmp/ansible-tmp-1519056612.49-257402348266617 `" ) && sleep 0'
<target> PUT /home/centos/test.sh TO /home/centos/.ansible/tmp/ansible-tmp-1519056612.49-257402348266617/test.sh
<target> EXEC /bin/sh -c 'chmod u+x /home/centos/.ansible/tmp/ansible-tmp-1519056612.49-257402348266617/ /home/centos/.ansible/tmp/ansible-tmp-1519056612.49-257402348266617/test.sh && sleep 0'
<target> EXEC /bin/sh -c ' /home/centos/.ansible/tmp/ansible-tmp-1519056612.49-257402348266617/test.sh && sleep 0'
<target> EXEC /bin/sh -c 'rm -f -r /home/centos/.ansible/tmp/ansible-tmp-1519056612.49-257402348266617/ > /dev/null 2>&1 && sleep 0'
changed: [target] => {
    "changed": true,
    "rc": 0,
    "stderr": "",
    "stdout": "Hi there, centos\n",
    "stdout_lines": [
        "Hi there, centos"
    ]
}

TASK [By another user] ******************************************************************************************************************************
task path: /home/centos/playbook.yml:8
<target> ESTABLISH LOCAL CONNECTION FOR USER: centos
<target> EXEC /bin/sh -c 'echo ~ && sleep 0'
<target> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /tmp/ansible-tmp-1519056612.57-242465962454810 `" && echo ansible-tmp-1519056612.57-242465962454810="` echo /tmp/ansible-tmp-1519056612.57-242465962454810 `" ) && sleep 0'
<target> PUT /home/centos/test.sh TO /tmp/ansible-tmp-1519056612.57-242465962454810/test.sh
<target> EXEC /bin/sh -c 'setfacl -m u:another:r-x /tmp/ansible-tmp-1519056612.57-242465962454810/ /tmp/ansible-tmp-1519056612.57-242465962454810/test.sh && sleep 0'
<target> EXEC /bin/sh -c 'sudo -H -S -n -u another /bin/sh -c '"'"'echo BECOME-SUCCESS-livmgqbneomtrzkguocdhrslwpxwfetg;  /tmp/ansible-tmp-1519056612.57-242465962454810/test.sh'"'"' && sleep 0'
<target> EXEC /bin/sh -c 'rm -f -r /tmp/ansible-tmp-1519056612.57-242465962454810/ > /dev/null 2>&1 && sleep 0'
changed: [target] => {
    "changed": true,
    "rc": 0,
    "stderr": "",
    "stdout": "Hi there, another\n",
    "stdout_lines": [
        "Hi there, another"
    ]
}
META: ran handlers
META: ran handlers

PLAY RECAP ******************************************************************************************************************************************
target                     : ok=4    changed=2    unreachable=0    failed=0

$
```

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

2.5.0b2 fails, complaining about permissions issues.  The setfacl call in the "By another user" task targets only the script, leaving the housing directory unaltered.

<!--- Paste verbatim command output between quotes below -->
```
$ PYTHONPATH=~/ansible-2.5.0b2/lib ~/ansible-2.5.0b2/bin/ansible-playbook -i inventory.ini playbook.yml -vvv
ansible-playbook 2.5.0b2
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/centos/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/centos/ansible-2.5.0b2/lib/ansible
  executable location = /home/centos/ansible-2.5.0b2/bin/ansible-playbook
  python version = 2.7.5 (default, Aug  4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
Using /etc/ansible/ansible.cfg as config file
Parsed /home/centos/inventory.ini inventory source with ini plugin

PLAYBOOK: playbook.yml ******************************************************************************************************************************
1 plays in playbook.yml

PLAY [all] ******************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************
task path: /home/centos/playbook.yml:2
Using module file /home/centos/ansible-2.5.0b2/lib/ansible/modules/system/setup.py
<target> ESTABLISH LOCAL CONNECTION FOR USER: centos
<target> EXEC /bin/sh -c 'echo ~ && sleep 0'
<target> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/centos/.ansible/tmp/ansible-tmp-1519056718.23-110145123590191 `" && echo ansible-tmp-1519056718.23-110145123590191="` echo /home/centos/.ansible/tmp/ansible-tmp-1519056718.23-110145123590191 `" ) && sleep 0'
<target> PUT /home/centos/.ansible/tmp/ansible-local-10853yBdDa0/tmpSAIhCG TO /home/centos/.ansible/tmp/ansible-tmp-1519056718.23-110145123590191/setup.py
<target> EXEC /bin/sh -c 'chmod u+x /home/centos/.ansible/tmp/ansible-tmp-1519056718.23-110145123590191/ /home/centos/.ansible/tmp/ansible-tmp-1519056718.23-110145123590191/setup.py && sleep 0'
<target> EXEC /bin/sh -c '/usr/bin/python /home/centos/.ansible/tmp/ansible-tmp-1519056718.23-110145123590191/setup.py && sleep 0'
<target> EXEC /bin/sh -c 'rm -f -r /home/centos/.ansible/tmp/ansible-tmp-1519056718.23-110145123590191/ > /dev/null 2>&1 && sleep 0'
ok: [target]
META: ran handlers

TASK [debug] ****************************************************************************************************************************************
task path: /home/centos/playbook.yml:4
ok: [target] => {
    "ansible_version.full": "2.5.0b2",
    "failed": false
}

TASK [By default user] ******************************************************************************************************************************
task path: /home/centos/playbook.yml:6
<target> ESTABLISH LOCAL CONNECTION FOR USER: centos
<target> EXEC /bin/sh -c 'echo ~ && sleep 0'
<target> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/centos/.ansible/tmp/ansible-tmp-1519056718.64-9784177752591 `" && echo ansible-tmp-1519056718.64-9784177752591="` echo /home/centos/.ansible/tmp/ansible-tmp-1519056718.64-9784177752591 `" ) && sleep 0'
<target> PUT /home/centos/test.sh TO /home/centos/.ansible/tmp/ansible-tmp-1519056718.64-9784177752591/test.sh
<target> EXEC /bin/sh -c 'chmod u+x /home/centos/.ansible/tmp/ansible-tmp-1519056718.64-9784177752591/test.sh && sleep 0'
<target> EXEC /bin/sh -c ' /home/centos/.ansible/tmp/ansible-tmp-1519056718.64-9784177752591/test.sh && sleep 0'
<target> EXEC /bin/sh -c 'rm -f -r /home/centos/.ansible/tmp/ansible-tmp-1519056718.64-9784177752591/ > /dev/null 2>&1 && sleep 0'
changed: [target] => {
    "changed": true,
    "failed": false,
    "rc": 0,
    "stderr": "",
    "stdout": "Hi there, centos\n",
    "stdout_lines": [
        "Hi there, centos"
    ]
}

TASK [By another user] ******************************************************************************************************************************
task path: /home/centos/playbook.yml:8
<target> ESTABLISH LOCAL CONNECTION FOR USER: centos
<target> EXEC /bin/sh -c 'echo ~ && sleep 0'
<target> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /var/tmp/ansible-tmp-1519056718.73-151571114285502 `" && echo ansible-tmp-1519056718.73-151571114285502="` echo /var/tmp/ansible-tmp-1519056718.73-151571114285502 `" ) && sleep 0'
<target> PUT /home/centos/test.sh TO /var/tmp/ansible-tmp-1519056718.73-151571114285502/test.sh
<target> EXEC /bin/sh -c 'setfacl -m u:another:r-x /var/tmp/ansible-tmp-1519056718.73-151571114285502/test.sh && sleep 0'
<target> EXEC /bin/sh -c 'sudo -H -S -n -u another /bin/sh -c '"'"'echo BECOME-SUCCESS-zejjjxnnwejptupoeojbilzghhckrtbr;  /var/tmp/ansible-tmp-1519056718.73-151571114285502/test.sh'"'"' && sleep 0'
<target> EXEC /bin/sh -c 'rm -f -r /var/tmp/ansible-tmp-1519056718.73-151571114285502/ > /dev/null 2>&1 && sleep 0'
fatal: [target]: FAILED! => {
    "changed": true,
    "failed": true,
    "msg": "non-zero return code",
    "rc": 126,
    "stderr": "/bin/sh: /var/tmp/ansible-tmp-1519056718.73-151571114285502/test.sh: Permission denied\n",
    "stdout": "",
    "stdout_lines": []
}
        to retry, use: --limit @/home/centos/playbook.retry

PLAY RECAP ******************************************************************************************************************************************
target                     : ok=3    changed=1    unreachable=0    failed=1

$
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/commands/script.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/commands/script.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Resolving patch:
diff --git a/lib/ansible/plugins/action/patch.py b/lib/ansible/plugins/action/patch.py
--- a/lib/ansible/plugins/action/patch.py
+++ b/lib/ansible/plugins/action/patch.py
@@ -55,7 +55,7 @@ def run(self, tmp=None, task_vars=None):
 
             tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir, os.path.basename(src))
             self._transfer_file(src, tmp_src)
-            self._fixup_perms2((tmp_src,))
+            self._fixup_perms2((self._connection._shell.tmpdir, tmp_src))
 
             new_module_args = self._task.args.copy()
             new_module_args.update(
diff --git a/lib/ansible/plugins/action/script.py b/lib/ansible/plugins/action/script.py
--- a/lib/ansible/plugins/action/script.py
+++ b/lib/ansible/plugins/action/script.py
@@ -104,7 +104,7 @@ def run(self, tmp=None, task_vars=None):
                 self._transfer_file(source, tmp_src)
 
                 # set file permissions, more permissive when the copy is done as a different user
-                self._fixup_perms2((tmp_src,), execute=True)
+                self._fixup_perms2((self._connection._shell.tmpdir, tmp_src), execute=True)
 
                 # add preparation steps to one ssh roundtrip executing the script
                 env_dict = dict()


Test patch: