mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
passwordstore: Prevent using path as password (#4192)
Given a password stored in _path/to/secret_, requesting the password _path/to_ will literally return `path/to`. This can lead to using weak passwords by accident/mess up logic in code, based on the state of the password store. This is worked around by applying the same logic `pass` uses: If a password was returned, check if there is a .gpg file it could have come from. If not, treat it as missing. Fixes ansible-collections/community.general#4185
This commit is contained in:
parent
1e4b8e30a9
commit
da49c0968d
2 changed files with 27 additions and 21 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- passwordstore lookup plugin - prevent returning path names as passwords by accident (https://github.com/ansible-collections/community.general/issues/4185, https://github.com/ansible-collections/community.general/pull/4192).
|
|
@ -154,6 +154,7 @@ display = Display()
|
||||||
|
|
||||||
# backhacked check_output with input for python 2.7
|
# backhacked check_output with input for python 2.7
|
||||||
# http://stackoverflow.com/questions/10103551/passing-data-to-subprocess-check-output
|
# http://stackoverflow.com/questions/10103551/passing-data-to-subprocess-check-output
|
||||||
|
# note: contains special logic for calling 'pass', so not a drop-in replacement for check_output
|
||||||
def check_output2(*popenargs, **kwargs):
|
def check_output2(*popenargs, **kwargs):
|
||||||
if 'stdout' in kwargs:
|
if 'stdout' in kwargs:
|
||||||
raise ValueError('stdout argument not allowed, it will be overridden.')
|
raise ValueError('stdout argument not allowed, it will be overridden.')
|
||||||
|
@ -175,9 +176,10 @@ def check_output2(*popenargs, **kwargs):
|
||||||
process.wait()
|
process.wait()
|
||||||
raise
|
raise
|
||||||
retcode = process.poll()
|
retcode = process.poll()
|
||||||
if retcode != 0 or \
|
if retcode == 0 and (b'encryption failed: Unusable public key' in b_out or
|
||||||
b'encryption failed: Unusable public key' in b_out or \
|
b'encryption failed: Unusable public key' in b_err):
|
||||||
b'encryption failed: Unusable public key' in b_err:
|
retcode = 78 # os.EX_CONFIG
|
||||||
|
if retcode != 0:
|
||||||
cmd = kwargs.get("args")
|
cmd = kwargs.get("args")
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
cmd = popenargs[0]
|
cmd = popenargs[0]
|
||||||
|
@ -228,12 +230,11 @@ class LookupModule(LookupBase):
|
||||||
# Collect pass environment variables from the plugin's parameters.
|
# Collect pass environment variables from the plugin's parameters.
|
||||||
self.env = os.environ.copy()
|
self.env = os.environ.copy()
|
||||||
|
|
||||||
# Set PASSWORD_STORE_DIR if directory is set
|
# Set PASSWORD_STORE_DIR
|
||||||
if self.paramvals['directory']:
|
if os.path.isdir(self.paramvals['directory']):
|
||||||
if os.path.isdir(self.paramvals['directory']):
|
self.env['PASSWORD_STORE_DIR'] = self.paramvals['directory']
|
||||||
self.env['PASSWORD_STORE_DIR'] = self.paramvals['directory']
|
else:
|
||||||
else:
|
raise AnsibleError('Passwordstore directory \'{0}\' does not exist'.format(self.paramvals['directory']))
|
||||||
raise AnsibleError('Passwordstore directory \'{0}\' does not exist'.format(self.paramvals['directory']))
|
|
||||||
|
|
||||||
# Set PASSWORD_STORE_UMASK if umask is set
|
# Set PASSWORD_STORE_UMASK if umask is set
|
||||||
if 'umask' in self.paramvals:
|
if 'umask' in self.paramvals:
|
||||||
|
@ -261,19 +262,20 @@ class LookupModule(LookupBase):
|
||||||
if ':' in line:
|
if ':' in line:
|
||||||
name, value = line.split(':', 1)
|
name, value = line.split(':', 1)
|
||||||
self.passdict[name.strip()] = value.strip()
|
self.passdict[name.strip()] = value.strip()
|
||||||
|
if os.path.isfile(os.path.join(self.paramvals['directory'], self.passname + ".gpg")):
|
||||||
|
# Only accept password as found, if there a .gpg file for it (might be a tree node otherwise)
|
||||||
|
return True
|
||||||
except (subprocess.CalledProcessError) as e:
|
except (subprocess.CalledProcessError) as e:
|
||||||
if e.returncode != 0 and 'not in the password store' in e.output:
|
# 'not in password store' is the expected error if a password wasn't found
|
||||||
# if pass returns 1 and return string contains 'is not in the password store.'
|
if 'not in the password store' not in e.output:
|
||||||
# We need to determine if this is valid or Error.
|
|
||||||
if self.paramvals['missing'] == 'error':
|
|
||||||
raise AnsibleError('passwordstore: passname {0} not found and missing=error is set'.format(self.passname))
|
|
||||||
else:
|
|
||||||
if self.paramvals['missing'] == 'warn':
|
|
||||||
display.warning('passwordstore: passname {0} not found'.format(self.passname))
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
raise AnsibleError(e)
|
raise AnsibleError(e)
|
||||||
return True
|
|
||||||
|
if self.paramvals['missing'] == 'error':
|
||||||
|
raise AnsibleError('passwordstore: passname {0} not found and missing=error is set'.format(self.passname))
|
||||||
|
elif self.paramvals['missing'] == 'warn':
|
||||||
|
display.warning('passwordstore: passname {0} not found'.format(self.passname))
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def get_newpass(self):
|
def get_newpass(self):
|
||||||
if self.paramvals['nosymbols']:
|
if self.paramvals['nosymbols']:
|
||||||
|
@ -329,7 +331,9 @@ class LookupModule(LookupBase):
|
||||||
result = []
|
result = []
|
||||||
self.paramvals = {
|
self.paramvals = {
|
||||||
'subkey': 'password',
|
'subkey': 'password',
|
||||||
'directory': variables.get('passwordstore'),
|
'directory': variables.get('passwordstore', os.environ.get(
|
||||||
|
'PASSWORD_STORE_DIR',
|
||||||
|
os.path.expanduser('~/.password-store'))),
|
||||||
'create': False,
|
'create': False,
|
||||||
'returnall': False,
|
'returnall': False,
|
||||||
'overwrite': False,
|
'overwrite': False,
|
||||||
|
|
Loading…
Reference in a new issue