|
|
b58328 |
From 84317f86f9c96805cf365784794142e65cfbb310 Mon Sep 17 00:00:00 2001
|
|
|
b58328 |
From: Rob Crittenden <rcritten@redhat.com>
|
|
|
b58328 |
Date: Tue, 2 Jul 2019 13:44:48 -0400
|
|
|
b58328 |
Subject: [PATCH] CVE-2019-10195: Don't log passwords embedded in commands in
|
|
|
b58328 |
calls using batch
|
|
|
b58328 |
|
|
|
b58328 |
A raw batch request was fully logged which could expose parameters
|
|
|
b58328 |
we don't want logged, like passwords.
|
|
|
b58328 |
|
|
|
b58328 |
Override _repr_iter to use the individual commands to log the
|
|
|
b58328 |
values so that values are properly obscured.
|
|
|
b58328 |
|
|
|
b58328 |
In case of errors log the full value on when the server is in
|
|
|
b58328 |
debug mode.
|
|
|
b58328 |
|
|
|
b58328 |
Reported by Jamison Bennett from Cloudera
|
|
|
b58328 |
|
|
|
b58328 |
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
|
b58328 |
Reviewed-by: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
|
b58328 |
---
|
|
|
b58328 |
ipaserver/plugins/batch.py | 96 ++++++++++++++++++++++++++++----------
|
|
|
b58328 |
1 file changed, 72 insertions(+), 24 deletions(-)
|
|
|
b58328 |
|
|
|
b58328 |
diff --git a/ipaserver/plugins/batch.py b/ipaserver/plugins/batch.py
|
|
|
b58328 |
index 2794db895a014a6129654889289815d4286cf7f4..9df367d16234d1840a2e5297cdd5c3c59fa4828f 100644
|
|
|
b58328 |
--- a/ipaserver/plugins/batch.py
|
|
|
b58328 |
+++ b/ipaserver/plugins/batch.py
|
|
|
b58328 |
@@ -92,35 +92,82 @@ class batch(Command):
|
|
|
b58328 |
Output('results', (list, tuple), doc='')
|
|
|
b58328 |
)
|
|
|
b58328 |
|
|
|
b58328 |
+ def _validate_request(self, request):
|
|
|
b58328 |
+ """
|
|
|
b58328 |
+ Check that an individual request in a batch is parseable and the
|
|
|
b58328 |
+ commands exists.
|
|
|
b58328 |
+ """
|
|
|
b58328 |
+ if 'method' not in request:
|
|
|
b58328 |
+ raise errors.RequirementError(name='method')
|
|
|
b58328 |
+ if 'params' not in request:
|
|
|
b58328 |
+ raise errors.RequirementError(name='params')
|
|
|
b58328 |
+ name = request['method']
|
|
|
b58328 |
+ if (name not in self.api.Command or
|
|
|
b58328 |
+ isinstance(self.api.Command[name], Local)):
|
|
|
b58328 |
+ raise errors.CommandError(name=name)
|
|
|
b58328 |
+
|
|
|
b58328 |
+ # If params are not formated as a tuple(list, dict)
|
|
|
b58328 |
+ # the following lines will raise an exception
|
|
|
b58328 |
+ # that triggers an internal server error
|
|
|
b58328 |
+ # Raise a ConversionError instead to report the issue
|
|
|
b58328 |
+ # to the client
|
|
|
b58328 |
+ try:
|
|
|
b58328 |
+ a, kw = request['params']
|
|
|
b58328 |
+ newkw = dict((str(k), v) for k, v in kw.items())
|
|
|
b58328 |
+ api.Command[name].args_options_2_params(*a, **newkw)
|
|
|
b58328 |
+ except (AttributeError, ValueError, TypeError):
|
|
|
b58328 |
+ raise errors.ConversionError(
|
|
|
b58328 |
+ name='params',
|
|
|
b58328 |
+ error=_(u'must contain a tuple (list, dict)'))
|
|
|
b58328 |
+ except Exception as e:
|
|
|
b58328 |
+ raise errors.ConversionError(
|
|
|
b58328 |
+ name='params',
|
|
|
b58328 |
+ error=str(e))
|
|
|
b58328 |
+
|
|
|
b58328 |
+ def _repr_iter(self, **params):
|
|
|
b58328 |
+ """
|
|
|
b58328 |
+ Iterate through the request and use the Command _repr_intr so
|
|
|
b58328 |
+ that sensitive information (passwords) is not exposed.
|
|
|
b58328 |
+
|
|
|
b58328 |
+ In case of a malformatted request redact the entire thing.
|
|
|
b58328 |
+ """
|
|
|
b58328 |
+ exceptions = False
|
|
|
b58328 |
+ for arg in (params.get('methods', [])):
|
|
|
b58328 |
+ try:
|
|
|
b58328 |
+ self._validate_request(arg)
|
|
|
b58328 |
+ except Exception:
|
|
|
b58328 |
+ # redact the whole request since we don't know what's in it
|
|
|
b58328 |
+ exceptions = True
|
|
|
b58328 |
+ yield u'********'
|
|
|
b58328 |
+ continue
|
|
|
b58328 |
+
|
|
|
b58328 |
+ name = arg['method']
|
|
|
b58328 |
+ a, kw = arg['params']
|
|
|
b58328 |
+ newkw = dict((str(k), v) for k, v in kw.items())
|
|
|
b58328 |
+ param = api.Command[name].args_options_2_params(
|
|
|
b58328 |
+ *a, **newkw)
|
|
|
b58328 |
+
|
|
|
b58328 |
+ yield '{}({})'.format(
|
|
|
b58328 |
+ api.Command[name].name,
|
|
|
b58328 |
+ ', '.join(api.Command[name]._repr_iter(**param))
|
|
|
b58328 |
+ )
|
|
|
b58328 |
+
|
|
|
b58328 |
+ if exceptions:
|
|
|
b58328 |
+ logger.debug('batch: %s',
|
|
|
b58328 |
+ ', '.join(super(batch, self)._repr_iter(**params)))
|
|
|
b58328 |
+
|
|
|
b58328 |
def execute(self, methods=None, **options):
|
|
|
b58328 |
results = []
|
|
|
b58328 |
for arg in (methods or []):
|
|
|
b58328 |
params = dict()
|
|
|
b58328 |
name = None
|
|
|
b58328 |
try:
|
|
|
b58328 |
- if 'method' not in arg:
|
|
|
b58328 |
- raise errors.RequirementError(name='method')
|
|
|
b58328 |
- if 'params' not in arg:
|
|
|
b58328 |
- raise errors.RequirementError(name='params')
|
|
|
b58328 |
+ self._validate_request(arg)
|
|
|
b58328 |
name = arg['method']
|
|
|
b58328 |
- if (name not in self.api.Command or
|
|
|
b58328 |
- isinstance(self.api.Command[name], Local)):
|
|
|
b58328 |
- raise errors.CommandError(name=name)
|
|
|
b58328 |
-
|
|
|
b58328 |
- # If params are not formated as a tuple(list, dict)
|
|
|
b58328 |
- # the following lines will raise an exception
|
|
|
b58328 |
- # that triggers an internal server error
|
|
|
b58328 |
- # Raise a ConversionError instead to report the issue
|
|
|
b58328 |
- # to the client
|
|
|
b58328 |
- try:
|
|
|
b58328 |
- a, kw = arg['params']
|
|
|
b58328 |
- newkw = dict((str(k), v) for k, v in kw.items())
|
|
|
b58328 |
- params = api.Command[name].args_options_2_params(
|
|
|
b58328 |
- *a, **newkw)
|
|
|
b58328 |
- except (AttributeError, ValueError, TypeError):
|
|
|
b58328 |
- raise errors.ConversionError(
|
|
|
b58328 |
- name='params',
|
|
|
b58328 |
- error=_(u'must contain a tuple (list, dict)'))
|
|
|
b58328 |
+ a, kw = arg['params']
|
|
|
b58328 |
+ newkw = dict((str(k), v) for k, v in kw.items())
|
|
|
b58328 |
+ params = api.Command[name].args_options_2_params(
|
|
|
b58328 |
+ *a, **newkw)
|
|
|
b58328 |
newkw.setdefault('version', options['version'])
|
|
|
b58328 |
|
|
|
b58328 |
result = api.Command[name](*a, **newkw)
|
|
|
b58328 |
@@ -132,8 +179,9 @@ class batch(Command):
|
|
|
b58328 |
)
|
|
|
b58328 |
result['error']=None
|
|
|
b58328 |
except Exception as e:
|
|
|
b58328 |
- if isinstance(e, errors.RequirementError) or \
|
|
|
b58328 |
- isinstance(e, errors.CommandError):
|
|
|
b58328 |
+ if (isinstance(e, errors.RequirementError) or
|
|
|
b58328 |
+ isinstance(e, errors.CommandError) or
|
|
|
b58328 |
+ isinstance(e, errors.ConversionError)):
|
|
|
b58328 |
logger.info(
|
|
|
b58328 |
'%s: batch: %s',
|
|
|
b58328 |
context.principal, # pylint: disable=no-member
|
|
|
b58328 |
--
|
|
|
b58328 |
2.23.0
|
|
|
b58328 |
|