|
|
42837f |
From c09e36927f74f955a325381a7d611ea270d04b65 Mon Sep 17 00:00:00 2001
|
|
|
42837f |
From: Jake Hunsaker <jhunsake@redhat.com>
|
|
|
42837f |
Date: Tue, 13 Nov 2018 12:30:45 -0500
|
|
|
42837f |
Subject: [PATCH] [global] Quote options locally everywhere
|
|
|
42837f |
|
|
|
42837f |
An earlier commit passed cluster option values through pipes.quote() in
|
|
|
42837f |
an effort to prevent command injection on the remote nodes, however this
|
|
|
42837f |
did not capture every avenue through which a maliciously designed option
|
|
|
42837f |
value or sos command could take.
|
|
|
42837f |
|
|
|
42837f |
Now, quote the option values at the time of use, and also quote any sos
|
|
|
42837f |
option that isn't an on/off toggle.
|
|
|
42837f |
|
|
|
42837f |
Additionally, re-work how the ovirt/rhv database query is quoted and
|
|
|
42837f |
filter out cluster/datacenter values that are likely to contain SQL
|
|
|
42837f |
code, since we cannot rely on the driver to do this since we don't have
|
|
|
42837f |
an actual connection to the database.
|
|
|
42837f |
|
|
|
42837f |
Original discovery of issues and patch work thanks to Riccardo Schirone.
|
|
|
42837f |
|
|
|
42837f |
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
|
|
|
42837f |
---
|
|
|
42837f |
soscollector/clusters/kubernetes.py | 3 ++-
|
|
|
42837f |
soscollector/clusters/ovirt.py | 32 ++++++++++++++++++++++-------
|
|
|
42837f |
soscollector/configuration.py | 2 +-
|
|
|
42837f |
soscollector/sos_collector.py | 19 +++++++++--------
|
|
|
42837f |
soscollector/sosnode.py | 13 ++++++------
|
|
|
42837f |
5 files changed, 46 insertions(+), 23 deletions(-)
|
|
|
42837f |
|
|
|
42837f |
diff --git a/soscollector/clusters/kubernetes.py b/soscollector/clusters/kubernetes.py
|
|
|
42837f |
index 6c519f3..c5c2094 100644
|
|
|
42837f |
--- a/soscollector/clusters/kubernetes.py
|
|
|
42837f |
+++ b/soscollector/clusters/kubernetes.py
|
|
|
42837f |
@@ -13,6 +13,7 @@
|
|
|
42837f |
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
42837f |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
42837f |
|
|
|
42837f |
+from pipes import quote
|
|
|
42837f |
from soscollector.clusters import Cluster
|
|
|
42837f |
|
|
|
42837f |
|
|
|
42837f |
@@ -32,7 +33,7 @@ class kubernetes(Cluster):
|
|
|
42837f |
def get_nodes(self):
|
|
|
42837f |
self.cmd += ' get nodes'
|
|
|
42837f |
if self.get_option('label'):
|
|
|
42837f |
- self.cmd += ' -l %s ' % self.get_option('label')
|
|
|
42837f |
+ self.cmd += ' -l %s ' % quote(self.get_option('label'))
|
|
|
42837f |
res = self.exec_master_cmd(self.cmd)
|
|
|
42837f |
if res['status'] == 0:
|
|
|
42837f |
nodes = []
|
|
|
42837f |
diff --git a/soscollector/clusters/ovirt.py b/soscollector/clusters/ovirt.py
|
|
|
42837f |
index 1c44c97..0a074ca 100644
|
|
|
42837f |
--- a/soscollector/clusters/ovirt.py
|
|
|
42837f |
+++ b/soscollector/clusters/ovirt.py
|
|
|
42837f |
@@ -16,6 +16,7 @@
|
|
|
42837f |
import os
|
|
|
42837f |
import fnmatch
|
|
|
42837f |
|
|
|
42837f |
+from pipes import quote
|
|
|
42837f |
from soscollector.clusters import Cluster
|
|
|
42837f |
from getpass import getpass
|
|
|
42837f |
|
|
|
42837f |
@@ -31,6 +32,22 @@ class ovirt(Cluster):
|
|
|
42837f |
('no-hypervisors', False, 'Do not collect from hypervisors')
|
|
|
42837f |
]
|
|
|
42837f |
|
|
|
42837f |
+ def _sql_scrub(self, val):
|
|
|
42837f |
+ '''
|
|
|
42837f |
+ Manually sanitize SQL queries since we can't leave this up to the
|
|
|
42837f |
+ driver since we do not have an actual DB connection
|
|
|
42837f |
+ '''
|
|
|
42837f |
+ if not val:
|
|
|
42837f |
+ return '%'
|
|
|
42837f |
+
|
|
|
42837f |
+ invalid_chars = ['\x00', '\\', '\n', '\r', '\032', '"', '\'']
|
|
|
42837f |
+ if any(x in invalid_chars for x in val):
|
|
|
42837f |
+ self.log_warn("WARNING: Cluster option \'%s\' contains invalid "
|
|
|
42837f |
+ "characters. Using '%%' instead." % val)
|
|
|
42837f |
+ return '%'
|
|
|
42837f |
+
|
|
|
42837f |
+ return val
|
|
|
42837f |
+
|
|
|
42837f |
def setup(self):
|
|
|
42837f |
self.pg_pass = False
|
|
|
42837f |
if not self.get_option('no-database'):
|
|
|
42837f |
@@ -38,13 +55,14 @@ class ovirt(Cluster):
|
|
|
42837f |
self.format_db_cmd()
|
|
|
42837f |
|
|
|
42837f |
def format_db_cmd(self):
|
|
|
42837f |
- cluster = self.get_option('cluster') or '%'
|
|
|
42837f |
- datacenter = self.get_option('datacenter') or '%'
|
|
|
42837f |
- self.dbcmd = '/usr/share/ovirt-engine/dbscripts/engine-psql.sh -c \"'
|
|
|
42837f |
- self.dbcmd += ("select host_name from vds_static where cluster_id in "
|
|
|
42837f |
- "(select cluster_id from cluster where name like \'%s\'"
|
|
|
42837f |
- " and storage_pool_id in (select id from storage_pool "
|
|
|
42837f |
- "where name like \'%s\'))\"" % (cluster, datacenter))
|
|
|
42837f |
+ cluster = self._sql_scrub(self.get_option('cluster'))
|
|
|
42837f |
+ datacenter = self._sql_scrub(self.get_option('datacenter'))
|
|
|
42837f |
+ query = ("select host_name from vds_static where cluster_id in "
|
|
|
42837f |
+ "(select cluster_id from cluster where name like '%s'"
|
|
|
42837f |
+ " and storage_pool_id in (select id from storage_pool "
|
|
|
42837f |
+ "where name like '%s'))" % (cluster, datacenter))
|
|
|
42837f |
+ self.dbcmd = ('/usr/share/ovirt-engine/dbscripts/engine-psql.sh '
|
|
|
42837f |
+ '-c {}'.format(quote(query)))
|
|
|
42837f |
self.log_debug('Query command for ovirt DB set to: %s' % self.dbcmd)
|
|
|
42837f |
|
|
|
42837f |
def get_nodes(self):
|
|
|
42837f |
diff --git a/soscollector/configuration.py b/soscollector/configuration.py
|
|
|
42837f |
index ea33835..7f2c3c7 100644
|
|
|
42837f |
--- a/soscollector/configuration.py
|
|
|
42837f |
+++ b/soscollector/configuration.py
|
|
|
42837f |
@@ -138,7 +138,7 @@ class Configuration(dict):
|
|
|
42837f |
try:
|
|
|
42837f |
# there are no instances currently where any cluster option
|
|
|
42837f |
# should contain a legitimate space.
|
|
|
42837f |
- value = pipes.quote(option.split('=')[1].split()[0])
|
|
|
42837f |
+ value = option.split('=')[1].split()[0]
|
|
|
42837f |
except IndexError:
|
|
|
42837f |
# conversion to boolean is handled during validation
|
|
|
42837f |
value = 'True'
|
|
|
42837f |
diff --git a/soscollector/sos_collector.py b/soscollector/sos_collector.py
|
|
|
42837f |
index 70a3dc8..7331f63 100644
|
|
|
42837f |
--- a/soscollector/sos_collector.py
|
|
|
42837f |
+++ b/soscollector/sos_collector.py
|
|
|
42837f |
@@ -32,6 +32,7 @@ from concurrent.futures import ThreadPoolExecutor
|
|
|
42837f |
from .sosnode import SosNode
|
|
|
42837f |
from distutils.sysconfig import get_python_lib
|
|
|
42837f |
from getpass import getpass
|
|
|
42837f |
+from pipes import quote
|
|
|
42837f |
from six.moves import input
|
|
|
42837f |
from textwrap import fill
|
|
|
42837f |
from soscollector import __version__
|
|
|
42837f |
@@ -360,32 +361,34 @@ this utility or remote systems that it connects to.
|
|
|
42837f |
def configure_sos_cmd(self):
|
|
|
42837f |
'''Configures the sosreport command that is run on the nodes'''
|
|
|
42837f |
if self.config['sos_opt_line']:
|
|
|
42837f |
- filt = ['&', '|', '>', '<']
|
|
|
42837f |
+ filt = ['&', '|', '>', '<', ';']
|
|
|
42837f |
if any(f in self.config['sos_opt_line'] for f in filt):
|
|
|
42837f |
self.log_warn('Possible shell script found in provided sos '
|
|
|
42837f |
'command. Ignoring --sos-cmd option entirely.')
|
|
|
42837f |
self.config['sos_opt_line'] = None
|
|
|
42837f |
else:
|
|
|
42837f |
self.config['sos_cmd'] = '%s %s' % (
|
|
|
42837f |
- self.config['sos_cmd'], self.config['sos_opt_line'])
|
|
|
42837f |
+ self.config['sos_cmd'], quote(self.config['sos_opt_line']))
|
|
|
42837f |
self.log_debug("User specified manual sosreport command. "
|
|
|
42837f |
"Command set to %s" % self.config['sos_cmd'])
|
|
|
42837f |
return True
|
|
|
42837f |
if self.config['case_id']:
|
|
|
42837f |
- self.config['sos_cmd'] += ' --case-id=%s' % self.config['case_id']
|
|
|
42837f |
+ self.config['sos_cmd'] += ' --case-id=%s' % (
|
|
|
42837f |
+ quote(self.config['case_id']))
|
|
|
42837f |
if self.config['alloptions']:
|
|
|
42837f |
self.config['sos_cmd'] += ' --alloptions'
|
|
|
42837f |
if self.config['verify']:
|
|
|
42837f |
self.config['sos_cmd'] += ' --verify'
|
|
|
42837f |
if self.config['log_size']:
|
|
|
42837f |
self.config['sos_cmd'] += (' --log-size=%s'
|
|
|
42837f |
- % self.config['log_size'])
|
|
|
42837f |
+ % quote(self.config['log_size']))
|
|
|
42837f |
if self.config['sysroot']:
|
|
|
42837f |
- self.config['sos_cmd'] += ' -s %s' % self.config['sysroot']
|
|
|
42837f |
+ self.config['sos_cmd'] += ' -s %s' % quote(self.config['sysroot'])
|
|
|
42837f |
if self.config['chroot']:
|
|
|
42837f |
- self.config['sos_cmd'] += ' -c %s' % self.config['chroot']
|
|
|
42837f |
+ self.config['sos_cmd'] += ' -c %s' % quote(self.config['chroot'])
|
|
|
42837f |
if self.config['compression']:
|
|
|
42837f |
- self.config['sos_cmd'] += ' -z %s' % self.config['compression']
|
|
|
42837f |
+ self.config['sos_cmd'] += ' -z %s' % (
|
|
|
42837f |
+ quote(self.config['compression']))
|
|
|
42837f |
self.log_debug('Initial sos cmd set to %s' % self.config['sos_cmd'])
|
|
|
42837f |
|
|
|
42837f |
def connect_to_master(self):
|
|
|
42837f |
@@ -408,7 +411,7 @@ this utility or remote systems that it connects to.
|
|
|
42837f |
can still be run if the user sets a --cluster-type manually
|
|
|
42837f |
'''
|
|
|
42837f |
checks = list(self.clusters.values())
|
|
|
42837f |
- for cluster in checks:
|
|
|
42837f |
+ for cluster in self.clusters.values():
|
|
|
42837f |
checks.remove(cluster)
|
|
|
42837f |
cluster.master = self.master
|
|
|
42837f |
if cluster.check_enabled():
|
|
|
42837f |
diff --git a/soscollector/sosnode.py b/soscollector/sosnode.py
|
|
|
42837f |
index fde1e6a..3790433 100644
|
|
|
42837f |
--- a/soscollector/sosnode.py
|
|
|
42837f |
+++ b/soscollector/sosnode.py
|
|
|
42837f |
@@ -26,6 +26,7 @@ import six
|
|
|
42837f |
import time
|
|
|
42837f |
|
|
|
42837f |
from distutils.version import LooseVersion
|
|
|
42837f |
+from pipes import quote
|
|
|
42837f |
from subprocess import Popen, PIPE
|
|
|
42837f |
|
|
|
42837f |
|
|
|
42837f |
@@ -450,7 +451,7 @@ class SosNode():
|
|
|
42837f |
|
|
|
42837f |
label = self.determine_sos_label()
|
|
|
42837f |
if label:
|
|
|
42837f |
- self.sos_cmd = ' %s %s' % (self.sos_cmd, label)
|
|
|
42837f |
+ self.sos_cmd = ' %s %s' % (self.sos_cmd, quote(label))
|
|
|
42837f |
|
|
|
42837f |
if self.config['sos_opt_line']:
|
|
|
42837f |
return True
|
|
|
42837f |
@@ -464,7 +465,7 @@ class SosNode():
|
|
|
42837f |
'enabled but do not exist' % not_only)
|
|
|
42837f |
only = self._fmt_sos_opt_list(self.config['only_plugins'])
|
|
|
42837f |
if only:
|
|
|
42837f |
- self.sos_cmd += ' --only-plugins=%s' % only
|
|
|
42837f |
+ self.sos_cmd += ' --only-plugins=%s' % quote(only)
|
|
|
42837f |
return True
|
|
|
42837f |
|
|
|
42837f |
if self.config['skip_plugins']:
|
|
|
42837f |
@@ -477,7 +478,7 @@ class SosNode():
|
|
|
42837f |
'already not enabled' % not_skip)
|
|
|
42837f |
skipln = self._fmt_sos_opt_list(skip)
|
|
|
42837f |
if skipln:
|
|
|
42837f |
- self.sos_cmd += ' --skip-plugins=%s' % skipln
|
|
|
42837f |
+ self.sos_cmd += ' --skip-plugins=%s' % quote(skipln)
|
|
|
42837f |
|
|
|
42837f |
if self.config['enable_plugins']:
|
|
|
42837f |
# only run enable for plugins that are disabled
|
|
|
42837f |
@@ -490,18 +491,18 @@ class SosNode():
|
|
|
42837f |
'are already enabled or do not exist' % not_on)
|
|
|
42837f |
enable = self._fmt_sos_opt_list(opts)
|
|
|
42837f |
if enable:
|
|
|
42837f |
- self.sos_cmd += ' --enable-plugins=%s' % enable
|
|
|
42837f |
+ self.sos_cmd += ' --enable-plugins=%s' % quote(enable)
|
|
|
42837f |
|
|
|
42837f |
if self.config['plugin_options']:
|
|
|
42837f |
opts = [o for o in self.config['plugin_options']
|
|
|
42837f |
if self._plugin_exists(o.split('.')[0])
|
|
|
42837f |
and self._plugin_option_exists(o.split('=')[0])]
|
|
|
42837f |
if opts:
|
|
|
42837f |
- self.sos_cmd += ' -k %s' % ','.join(o for o in opts)
|
|
|
42837f |
+ self.sos_cmd += ' -k %s' % quote(','.join(o for o in opts))
|
|
|
42837f |
|
|
|
42837f |
if self.config['preset']:
|
|
|
42837f |
if self._preset_exists(self.config['preset']):
|
|
|
42837f |
- self.sos_cmd += ' --preset=%s' % self.config['preset']
|
|
|
42837f |
+ self.sos_cmd += ' --preset=%s' % quote(self.config['preset'])
|
|
|
42837f |
else:
|
|
|
42837f |
self.log_debug('Requested to enable preset %s but preset does '
|
|
|
42837f |
'not exist on node' % self.config['preset'])
|
|
|
42837f |
--
|
|
|
42837f |
2.17.2
|
|
|
42837f |
|