From 040fb4435a570d7cf807b9a947d2bd0b331c1eb3 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Mon, 1 May 2017 10:18:15 -0500 Subject: [PATCH] Fix password prompt matching (#24081) * Fix password prompt matching * Add some tests for check_password_prompt * Prevent pep8 line ends with a space error --- lib/ansible/plugins/connection/__init__.py | 6 +- .../plugins/connection/test_connection.py | 81 +++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/lib/ansible/plugins/connection/__init__.py b/lib/ansible/plugins/connection/__init__.py index 6ba4aa2fa66..59351d25f00 100644 --- a/lib/ansible/plugins/connection/__init__.py +++ b/lib/ansible/plugins/connection/__init__.py @@ -254,10 +254,8 @@ class ConnectionBase(with_metaclass(ABCMeta, object)): return False elif isinstance(self._play_context.prompt, string_types): b_prompt = to_bytes(self._play_context.prompt).strip() - b_lines = b_output.splitlines(True) - if not b_lines: - return False - return b_lines[-1].strip().endswith(b_prompt) or b_lines[0].strip().endswith(b_prompt) + b_lines = b_output.splitlines() + return any(l.strip().startswith(b_prompt) for l in b_lines) else: return self._play_context.prompt(b_output) diff --git a/test/units/plugins/connection/test_connection.py b/test/units/plugins/connection/test_connection.py index dfcc3fbfdbe..3e96612bab3 100644 --- a/test/units/plugins/connection/test_connection.py +++ b/test/units/plugins/connection/test_connection.py @@ -45,6 +45,9 @@ class TestConnectionBaseClass(unittest.TestCase): def setUp(self): self.play_context = PlayContext() + self.play_context.prompt = ( + '[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: ' + ) self.in_stream = StringIO() def tearDown(self): @@ -132,3 +135,81 @@ class TestConnectionBaseClass(unittest.TestCase): def test_network_cli_connection_module(self): self.assertIsInstance(NetworkCliConnection(self.play_context, self.in_stream), NetworkCliConnection) self.assertIsInstance(NetworkCliConnection(self.play_context, self.in_stream), ParamikoConnection) + + def test_check_password_prompt(self): + local = ( + b'[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: \n' + b'BECOME-SUCCESS-ouzmdnewuhucvuaabtjmweasarviygqq\n' + ) + + ssh_pipelining_vvvv = b''' +debug3: mux_master_read_cb: channel 1 packet type 0x10000002 len 251 +debug2: process_mux_new_session: channel 1: request tty 0, X 1, agent 1, subsys 0, term "xterm-256color", cmd "/bin/sh -c 'sudo -H -S -p "[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: " -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-ouzmdnewuhucvuaabtjmweasarviygqq; /bin/true'"'"' && sleep 0'", env 0 +debug3: process_mux_new_session: got fds stdin 9, stdout 10, stderr 11 +debug2: client_session2_setup: id 2 +debug1: Sending command: /bin/sh -c 'sudo -H -S -p "[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: " -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-ouzmdnewuhucvuaabtjmweasarviygqq; /bin/true'"'"' && sleep 0' +debug2: channel 2: request exec confirm 1 +debug2: channel 2: rcvd ext data 67 +[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: debug2: channel 2: written 67 to efd 11 +BECOME-SUCCESS-ouzmdnewuhucvuaabtjmweasarviygqq +debug3: receive packet: type 98 +''' # noqa + + ssh_nopipelining_vvvv = b''' +debug3: mux_master_read_cb: channel 1 packet type 0x10000002 len 251 +debug2: process_mux_new_session: channel 1: request tty 1, X 1, agent 1, subsys 0, term "xterm-256color", cmd "/bin/sh -c 'sudo -H -S -p "[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: " -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-ouzmdnewuhucvuaabtjmweasarviygqq; /bin/true'"'"' && sleep 0'", env 0 +debug3: mux_client_request_session: session request sent +debug3: send packet: type 98 +debug1: Sending command: /bin/sh -c 'sudo -H -S -p "[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: " -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-ouzmdnewuhucvuaabtjmweasarviygqq; /bin/true'"'"' && sleep 0' +debug2: channel 2: request exec confirm 1 +debug2: exec request accepted on channel 2 +[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: debug3: receive packet: type 2 +debug3: Received SSH2_MSG_IGNORE +debug3: Received SSH2_MSG_IGNORE + +BECOME-SUCCESS-ouzmdnewuhucvuaabtjmweasarviygqq +debug3: receive packet: type 98 +''' # noqa + + ssh_novvvv = ( + b'[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: \n' + b'BECOME-SUCCESS-ouzmdnewuhucvuaabtjmweasarviygqq\n' + ) + + dns_issue = ( + b'timeout waiting for privilege escalation password prompt:\n' + b'sudo: sudo: unable to resolve host tcloud014\n' + b'[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: \n' + b'BECOME-SUCCESS-ouzmdnewuhucvuaabtjmweasarviygqq\n' + ) + + nothing = b'' + + in_front = b''' +debug1: Sending command: /bin/sh -c 'sudo -H -S -p "[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: " -u root /bin/sh -c '"'"'echo +''' + + class ConnectionFoo(ConnectionBase): + @property + def transport(self): + pass + def _connect(self): + pass + def exec_command(self): + pass + def put_file(self): + pass + def fetch_file(self): + pass + def close(self): + pass + + c = ConnectionFoo(self.play_context, self.in_stream) + + self.assertTrue(c.check_password_prompt(local)) + self.assertTrue(c.check_password_prompt(ssh_pipelining_vvvv)) + self.assertTrue(c.check_password_prompt(ssh_nopipelining_vvvv)) + self.assertTrue(c.check_password_prompt(ssh_novvvv)) + self.assertTrue(c.check_password_prompt(dns_issue)) + self.assertFalse(c.check_password_prompt(nothing)) + self.assertFalse(c.check_password_prompt(in_front))