2.7: user: do not pass ssh_key_passphrase on cmdline (#47445)
* user: do not pass ssh_key_passphrase on cmdline CVE-2018-16837 Co-authored-by: Toshio Kuratomi <a.badger@gmail.com> (cherry picked from commita0aa53d1a1
) * Ignore user module use of subprocess. (cherry picked from commit8d00afc013
) * Fix python3 problem in user module cve fix (cherry picked from commit9088671c4e
) * Fix changelog entry for user module CVE fix (cherry picked from commit210a43ebeb
)
This commit is contained in:
parent
b14c45a16c
commit
b618339c32
4 changed files with 84 additions and 5 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- user module - do not pass ssh_key_passphrase on cmdline (CVE-2018-16837)
|
|
@ -355,13 +355,15 @@ import grp
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import platform
|
import platform
|
||||||
|
import pty
|
||||||
import pwd
|
import pwd
|
||||||
|
import select
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
import socket
|
||||||
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils._text import to_native
|
from ansible.module_utils._text import to_native, to_bytes, to_text
|
||||||
from ansible.module_utils.basic import load_platform_subclass, AnsibleModule
|
from ansible.module_utils.basic import load_platform_subclass, AnsibleModule
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -860,13 +862,58 @@ class User(object):
|
||||||
cmd.append(self.ssh_comment)
|
cmd.append(self.ssh_comment)
|
||||||
cmd.append('-f')
|
cmd.append('-f')
|
||||||
cmd.append(ssh_key_file)
|
cmd.append(ssh_key_file)
|
||||||
cmd.append('-N')
|
|
||||||
if self.ssh_passphrase is not None:
|
if self.ssh_passphrase is not None:
|
||||||
cmd.append(self.ssh_passphrase)
|
if self.module.check_mode:
|
||||||
|
self.module.debug('In check mode, would have run: "%s"' % cmd)
|
||||||
|
return (0, '', '')
|
||||||
|
|
||||||
|
master_in_fd, slave_in_fd = pty.openpty()
|
||||||
|
master_out_fd, slave_out_fd = pty.openpty()
|
||||||
|
master_err_fd, slave_err_fd = pty.openpty()
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['LC_ALL'] = 'C'
|
||||||
|
try:
|
||||||
|
p = subprocess.Popen([to_bytes(c) for c in cmd],
|
||||||
|
stdin=slave_in_fd,
|
||||||
|
stdout=slave_out_fd,
|
||||||
|
stderr=slave_err_fd,
|
||||||
|
preexec_fn=os.setsid,
|
||||||
|
env=env)
|
||||||
|
out_buffer = b''
|
||||||
|
err_buffer = b''
|
||||||
|
while p.poll() is None:
|
||||||
|
r, w, e = select.select([master_out_fd, master_err_fd], [], [], 1)
|
||||||
|
first_prompt = b'Enter passphrase (empty for no passphrase):'
|
||||||
|
second_prompt = b'Enter same passphrase again'
|
||||||
|
prompt = first_prompt
|
||||||
|
for fd in r:
|
||||||
|
if fd == master_out_fd:
|
||||||
|
chunk = os.read(master_out_fd, 10240)
|
||||||
|
out_buffer += chunk
|
||||||
|
if prompt in out_buffer:
|
||||||
|
os.write(master_in_fd, to_bytes(self.ssh_passphrase, errors='strict') + b'\r')
|
||||||
|
prompt = second_prompt
|
||||||
|
else:
|
||||||
|
chunk = os.read(master_err_fd, 10240)
|
||||||
|
err_buffer += chunk
|
||||||
|
if prompt in err_buffer:
|
||||||
|
os.write(master_in_fd, to_bytes(self.ssh_passphrase, errors='strict') + b'\r')
|
||||||
|
prompt = second_prompt
|
||||||
|
if b'Overwrite (y/n)?' in out_buffer or b'Overwrite (y/n)?' in err_buffer:
|
||||||
|
# The key was created between us checking for existence and now
|
||||||
|
return (None, 'Key already exists', '')
|
||||||
|
|
||||||
|
rc = p.returncode
|
||||||
|
out = to_native(out_buffer)
|
||||||
|
err = to_native(err_buffer)
|
||||||
|
except OSError as e:
|
||||||
|
return (1, '', to_native(e))
|
||||||
else:
|
else:
|
||||||
|
cmd.append('-N')
|
||||||
cmd.append('')
|
cmd.append('')
|
||||||
|
|
||||||
(rc, out, err) = self.execute_command(cmd)
|
(rc, out, err) = self.execute_command(cmd)
|
||||||
|
|
||||||
if rc == 0 and not self.module.check_mode:
|
if rc == 0 and not self.module.check_mode:
|
||||||
# If the keys were successfully created, we should be able
|
# If the keys were successfully created, we should be able
|
||||||
# to tweak ownership.
|
# to tweak ownership.
|
||||||
|
|
|
@ -493,3 +493,32 @@
|
||||||
- result.bakup
|
- result.bakup
|
||||||
- shadow_backups.files | map(attribute='path') | list | length > 0
|
- shadow_backups.files | map(attribute='path') | list | length > 0
|
||||||
when: ansible_os_family == 'Solaris'
|
when: ansible_os_family == 'Solaris'
|
||||||
|
|
||||||
|
|
||||||
|
# Test creating ssh key with passphrase
|
||||||
|
- name: Remove ansibulluser
|
||||||
|
user:
|
||||||
|
name: ansibulluser
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Create user with ssh key
|
||||||
|
user:
|
||||||
|
name: ansibulluser
|
||||||
|
state: present
|
||||||
|
generate_ssh_key: yes
|
||||||
|
ssh_key_file: "{{ output_dir }}/test_id_rsa"
|
||||||
|
ssh_key_passphrase: secret_passphrase
|
||||||
|
|
||||||
|
- name: Unlock ssh key
|
||||||
|
command: "ssh-keygen -y -f {{ output_dir }}/test_id_rsa -P secret_passphrase"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Check that ssh key was unlocked successfully
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.rc == 0
|
||||||
|
|
||||||
|
- name: Clean ssh key
|
||||||
|
file:
|
||||||
|
path: "{{ output_dir }}/test_id_rsa"
|
||||||
|
state: absent
|
||||||
|
|
|
@ -1141,6 +1141,7 @@ lib/ansible/modules/system/svc.py E322
|
||||||
lib/ansible/modules/system/svc.py E324
|
lib/ansible/modules/system/svc.py E324
|
||||||
lib/ansible/modules/system/ufw.py E322
|
lib/ansible/modules/system/ufw.py E322
|
||||||
lib/ansible/modules/system/ufw.py E326
|
lib/ansible/modules/system/ufw.py E326
|
||||||
|
lib/ansible/modules/system/user.py E210
|
||||||
lib/ansible/modules/system/user.py E324
|
lib/ansible/modules/system/user.py E324
|
||||||
lib/ansible/modules/system/user.py E327
|
lib/ansible/modules/system/user.py E327
|
||||||
lib/ansible/modules/system/vdo.py E324
|
lib/ansible/modules/system/vdo.py E324
|
||||||
|
|
Loading…
Reference in a new issue