From a01e58cae34fc2f3563a1ff72fedb311d62e6662 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Tue, 8 Apr 2025 10:18:18 -0500 Subject: [PATCH] Support prompt matching with ssh_askpass (#84927) --- lib/ansible/cli/_ssh_askpass.py | 7 +++++++ lib/ansible/plugins/connection/ssh.py | 22 +++++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/ansible/cli/_ssh_askpass.py b/lib/ansible/cli/_ssh_askpass.py index 33543391012..c5d414cdbd6 100644 --- a/lib/ansible/cli/_ssh_askpass.py +++ b/lib/ansible/cli/_ssh_askpass.py @@ -33,6 +33,13 @@ def main() -> t.Never: # We must be running after the ansible fork is shutting down sys.exit(1) cfg = json.loads(shm.buf.tobytes().rstrip(b'\x00')) + + try: + if cfg['prompt'] not in sys.argv[1]: + sys.exit(1) + except IndexError: + sys.exit(1) + sys.stdout.buffer.write(cfg['password'].encode('utf-8')) sys.stdout.flush() shm.buf[:] = b'\x00' * shm.size diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py index 43ee5efc074..1e8e946788c 100644 --- a/lib/ansible/plugins/connection/ssh.py +++ b/lib/ansible/plugins/connection/ssh.py @@ -79,8 +79,10 @@ DOCUMENTATION = """ - name: ansible_ssh_password_mechanism sshpass_prompt: description: - - Password prompt that sshpass should search for. Supported by sshpass 1.06 and up. + - Password prompt that C(sshpass)/C(SSH_ASKPASS) should search for. + - Supported by sshpass 1.06 and up when O(password_mechanism) set to V(sshpass). - Defaults to C(Enter PIN for) when pkcs11_provider is set. + - Defaults to C(assword) when O(password_mechanism) set to V(ssh_askpass). default: '' type: string ini: @@ -430,6 +432,9 @@ SSH_DEBUG = re.compile(r'^debug\d+: .*') _HAS_RESOURCE_TRACK = sys.version_info[:2] >= (3, 13) +PKCS11_DEFAULT_PROMPT = 'Enter PIN for ' +SSH_ASKPASS_DEFAULT_PROMPT = 'assword' + class AnsibleControlPersistBrokenPipeError(AnsibleError): """ ControlPersist broken pipe """ @@ -735,7 +740,7 @@ class Connection(ConnectionBase): password_prompt = self.get_option('sshpass_prompt') if not password_prompt and pkcs11_provider: # Set default password prompt for pkcs11_provider to make it clear its a PIN - password_prompt = 'Enter PIN for ' + password_prompt = PKCS11_DEFAULT_PROMPT if password_prompt: b_command += [b'-P', to_bytes(password_prompt, errors='surrogate_or_strict')] @@ -965,9 +970,16 @@ class Connection(ConnectionBase): kwargs['track'] = False self.shm = shm = SharedMemory(create=True, size=16384, **kwargs) # type: ignore[arg-type] - data = json.dumps( - {'password': conn_password}, - ).encode('utf-8') + sshpass_prompt = self.get_option('sshpass_prompt') + if not sshpass_prompt and pkcs11_provider: + sshpass_prompt = PKCS11_DEFAULT_PROMPT + elif not sshpass_prompt: + sshpass_prompt = SSH_ASKPASS_DEFAULT_PROMPT + + data = json.dumps({ + 'password': conn_password, + 'prompt': sshpass_prompt, + }).encode('utf-8') shm.buf[:len(data)] = bytearray(data) shm.close()