From 1e672a0fec9d2199ada7b8004eedc8b40f51b178 Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Fri, 23 May 2014 10:06:09 -0500 Subject: [PATCH] Fixes for su on freebsd Addresses multiple issues when using su on freebsd including * su prompt differs between platforms, so turned that check into a regex comparison instead of a simple string comparison * not using '-c' after su causes problems, so added that for all platforms * fixed quoting issues due to multiple uses of '-c' introduced by the above fix Fixes #7503 Fixes #7507 --- lib/ansible/runner/connection_plugins/paramiko_ssh.py | 8 +++++++- lib/ansible/runner/connection_plugins/ssh.py | 9 ++++++++- lib/ansible/utils/__init__.py | 4 ++-- test/units/TestUtils.py | 5 +++-- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/ansible/runner/connection_plugins/paramiko_ssh.py b/lib/ansible/runner/connection_plugins/paramiko_ssh.py index 3ae76fe547f..22f57a6afe6 100644 --- a/lib/ansible/runner/connection_plugins/paramiko_ssh.py +++ b/lib/ansible/runner/connection_plugins/paramiko_ssh.py @@ -31,6 +31,7 @@ import random import logging import traceback import fcntl +import re import sys from termios import tcflush, TCIFLUSH from binascii import hexlify @@ -210,12 +211,17 @@ class Connection(object): shcmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) elif self.runner.su or su: shcmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd) + prompt_re = re.compile(prompt) vvv("EXEC %s" % shcmd, host=self.host) sudo_output = '' try: chan.exec_command(shcmd) if self.runner.sudo_pass or self.runner.su_pass: - while not sudo_output.endswith(prompt) and success_key not in sudo_output: + while True: + if success_key in sudo_output or \ + (self.runner.sudo_pass and sudo_output.endswith(prompt)) or \ + (self.runner.su_pass and prompt_re.match(sudo_output)): + break chunk = chan.recv(bufsize) if not chunk: if 'unknown user' in sudo_output: diff --git a/lib/ansible/runner/connection_plugins/ssh.py b/lib/ansible/runner/connection_plugins/ssh.py index 17895729e47..8a12356cd58 100644 --- a/lib/ansible/runner/connection_plugins/ssh.py +++ b/lib/ansible/runner/connection_plugins/ssh.py @@ -17,6 +17,7 @@ # import os +import re import subprocess import shlex import pipes @@ -268,6 +269,7 @@ class Connection(object): if su and su_user: sudocmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd) + prompt_re = re.compile(prompt) ssh_cmd.append(sudocmd) elif not self.runner.sudo or not sudoable: prompt = None @@ -308,7 +310,12 @@ class Connection(object): sudo_output = '' sudo_errput = '' - while not sudo_output.endswith(prompt) and success_key not in sudo_output: + while True: + if success_key in sudo_output or \ + (self.runner.sudo_pass and sudo_output.endswith(prompt)) or \ + (self.runner.su_pass and prompt_re.match(sudo_output)): + break + rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout], self.runner.timeout) if p.stderr in rfd: diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index b1916371fc8..1b9bab7b2d4 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -952,9 +952,9 @@ def make_su_cmd(su_user, executable, cmd): """ # TODO: work on this function randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32)) - prompt = 'assword: ' + prompt = '[Pp]assword: ?$' success_key = 'SUDO-SUCCESS-%s' % randbits - sudocmd = '%s %s %s %s -c %s' % ( + sudocmd = '%s %s %s -c "%s -c %s"' % ( C.DEFAULT_SU_EXE, C.DEFAULT_SU_FLAGS, su_user, executable or '$SHELL', pipes.quote('echo %s; %s' % (success_key, cmd)) ) diff --git a/test/units/TestUtils.py b/test/units/TestUtils.py index c60a0d82910..6241fd77981 100644 --- a/test/units/TestUtils.py +++ b/test/units/TestUtils.py @@ -3,6 +3,7 @@ import unittest import os import os.path +import re import tempfile import yaml import passlib.hash @@ -511,8 +512,8 @@ class TestUtils(unittest.TestCase): cmd = ansible.utils.make_su_cmd('root', '/bin/sh', '/bin/ls') self.assertTrue(isinstance(cmd, tuple)) self.assertEqual(len(cmd), 3) - self.assertTrue(' root /bin/sh' in cmd[0]) - self.assertTrue(cmd[1] == 'assword: ') + self.assertTrue(' root -c "/bin/sh' in cmd[0]) + self.assertTrue(re.compile(cmd[1])) self.assertTrue('echo SUDO-SUCCESS-' in cmd[0] and cmd[2].startswith('SUDO-SUCCESS-')) def test_to_unicode(self):