[PATCH] Properly handle extended profiles
by Josh Adams
Carry over refvals and setval's to extended profiles. Also added
checks to warn about refvals and setvals referencing non-existant
values.
Fixes bugs #8152,8153
---
src/secstate/main.py | 48 +++++++++++++++++++++++++++++++++++-------------
1 files changed, 35 insertions(+), 13 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index c8a603e..ea0425f 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -267,25 +267,47 @@ class Secstate:
val_instance = val.instances[0]
benchmark.vals[val.id] = value_instance_to_value(val_instance)
+ profiles = []
current_profile = benchmark.config.get(benchmark.id, 'profile')
prof_item = benchmark.get_item(current_profile)
if prof_item == None:
self.log.error("Import failed: profile '%(prof)s' does not exist" % {'prof':current_profile})
return None
+ profiles.append(prof_item.to_profile())
- profile = prof_item.to_profile()
- prof_sel = get_profile_selections(benchmark, profile)
- for key,val in prof_sel.items():
- benchmark.selections[key] = val
+ tmp_prof = profiles[0]
+ while tmp_prof.extends != None:
+ tmp_prof_item = benchmark.get_item(tmp_prof.extends)
+ if tmp_prof_item == None:
+ self.log.error("Import failed: profile '%(prof)s does not exist" % {'prof':tmp_prof})
+ return None
+ tmp_prof = tmp_prof_item.to_profile()
+ profiles.append(tmp_prof)
+
+ # Start with the base profile first
+ profiles.reverse()
+ for profile in profiles:
+ prof_sel = get_profile_selections(benchmark, profile)
+ for key,val in prof_sel.items():
+ benchmark.selections[key] = val
+
+ for setval in profile.setvalues:
+ if not benchmark.get_item(setval.item) != None:
+ if not benchmark.get_item(setval.item).prohibit_changes:
+ benchmark.vals[setval.item] = setval.value
+ else:
+ self.log.error("Set value references non-existant value: %(val)s" % {'val':setval.item})
+ continue
- for setval in profile.setvalues:
- if not benchmark.get_item(setval.item).prohibit_changes:
- benchmark.vals[setval.item] = setval.value
+ for refval in profile.refine_values:
+ value_item = benchmark.get_item(refval.item)
+ if value_item == None:
+ self.log.error("Refine value references non-existant value: %(val)s" % {'val':refval.item})
+ continue
+ value = value_item.to_value()
- for refval in profile.refine_values:
- value = benchmark.get_item(refval.item).to_value()
- if not value.prohibit_changes:
- benchmark.vals[refval.item] = value_instance_to_value(value.get_instance_by_selector(refval.selector))
+ if not value.prohibit_changes:
+ benchmark.vals[refval.item] = value_instance_to_value(value.get_instance_by_selector(refval.selector))
puppet_files = get_puppet_files(benchmark)
if store_path == None and self.content.has_key(benchmark.id):
--
1.7.2.3
13 years, 7 months
[PATCH] Fixed issue remediating archived content
by Josh Adams
Also added code to track tempfile creation and handle their cleanup.
Fixes bug #8096
---
src/bin/secstate | 39 +++++++++++++++++++++------------------
src/secstate/main.py | 19 +++++++++++++++----
src/secstate/util.py | 1 -
3 files changed, 36 insertions(+), 23 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index ebaf9c8..31fb4f7 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -24,6 +24,7 @@
import sys
import ConfigParser
import os
+import shutil
from optparse import OptionParser
import openscap_api as oscap
@@ -57,67 +58,69 @@ def main():
subcommand = sys.argv[1]
except IndexError, e:
usage()
- return -1
+ return_code = -1
arg_num = 2
if (subcommand == '-c') or (subcommand == '--config'):
if not sec_instance.setConfigFile(sys.argv[arg_num]):
usage()
- return -1
+ return_code = -1
arg_num += 2
try:
subcommand = sys.argv[arg_num - 1]
except IndexError:
usage()
- return -1
+ return_code = -1
if subcommand == 'help':
- return help(sys.argv[arg_num:])
+ return_code = help(sys.argv[arg_num:])
elif subcommand == 'export':
- return export(sys.argv[arg_num:])
+ return_code = export(sys.argv[arg_num:])
elif subcommand == 'search':
- return search(sys.argv[arg_num:])
+ return_code = search(sys.argv[arg_num:])
elif subcommand == 'list':
- return list_content(sys.argv[arg_num:])
+ return_code = list_content(sys.argv[arg_num:])
elif subcommand == 'show':
- return show(sys.argv[arg_num:])
+ return_code = show(sys.argv[arg_num:])
elif os.geteuid() != 0:
sys.stderr.write("secstate must be run as root!\n")
- return -1
+ return_code = -1
elif subcommand == 'import':
- return import_content(sys.argv[arg_num:])
+ return_code = import_content(sys.argv[arg_num:])
elif subcommand == 'remove':
- return remove_content(sys.argv[arg_num:])
+ return_code = remove_content(sys.argv[arg_num:])
elif subcommand == 'select':
- return select(sys.argv[arg_num:], True)
+ return_code = select(sys.argv[arg_num:], True)
elif subcommand == 'deselect':
- return select(sys.argv[arg_num:], False)
+ return_code = select(sys.argv[arg_num:], False)
elif subcommand == 'audit':
- return audit(sys.argv[arg_num:])
+ return_code = audit(sys.argv[arg_num:])
elif subcommand == 'remediate':
- return remediate(sys.argv[arg_num:])
+ return_code = remediate(sys.argv[arg_num:])
elif subcommand == 'mitigate':
- return mitigate(sys.argv[arg_num:])
+ return_code = mitigate(sys.argv[arg_num:])
elif subcommand == 'save':
- return save_profile(sys.argv[arg_num:])
+ return_code = save_profile(sys.argv[arg_num:])
else:
sys.stderr.write("Uknown subcommand: %(command)s" % {'command':subcommand})
usage()
- return -1
+ return_code = -1
+
+ return return_code
def help(arg):
if arg == []:
diff --git a/src/secstate/main.py b/src/secstate/main.py
index f2025cd..c8a603e 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -54,6 +54,17 @@ class Secstate:
self.load_content()
self.log = self.getLogger()
self.benchmark_dir = self.config.get('secstate', 'benchmark_dir')
+ self.tempfiles = {'dirs':[], 'files':[]}
+
+ def __del__(self):
+ try:
+ for dir in self.tempfiles['dirs']:
+ shutil.rmtree(dir)
+
+ for tmpfile in self.tempfiles['files']:
+ os.remove(tmpfile)
+ except IOError,e:
+ sec_instance.log.error("Error cleaning up temporary files: %(err)s" % {'err':e})
def setConfigFile(self, conf):
config = ConfigParser.ConfigParser()
@@ -349,6 +360,7 @@ class Secstate:
Output: Success of failure of the import
"""
extract_path = tempfile.mkdtemp()
+ self.tempfiles['dirs'].append(extract_path)
if type[0] == "application/x-tar":
tar_file = None
@@ -407,11 +419,8 @@ class Secstate:
if benchmark == None:
return None
- # Delete temporary directory now that we are done with it
- shutil.rmtree(extract_path)
return benchmark
-
def import_content(self, content, changes=True, save=False, active_profile=NONE_PROFILE):
"""
Function: Validates XCCDF/OVAL content and optionally saves it to the data store
@@ -474,6 +483,7 @@ class Secstate:
benchmark_file = self.content[benchmark_id]
else:
benchmark_file = tempfile.mktemp()
+ self.tempfiles['files'].append(benchmark_file)
benchmark = self.import_content(benchmark_id)
if benchmark.export(benchmark_file) == None:
self.log.error("Error exporting benchmark to %(file)s" % {'file':new_file})
@@ -1049,6 +1059,7 @@ class Secstate:
site_pp = self.config.get('secstate', 'site_pp')
else:
(site_pp_buf, site_pp) = tempfile.mkstemp()
+ self.tempfiles['files'].append(site_pp)
for puppet in benchmark.puppet:
os.write(site_pp_buf, 'import "%(file)s"\n' % {'file':puppet})
os.close(site_pp_buf)
@@ -1073,6 +1084,7 @@ class Secstate:
else:
handle, fname = tempfile.mkstemp(suffix='.yaml')
+ self.tempfiles['files'].append(fname)
os.write(handle, template % dict_to_external(puppet_content))
os.close(handle)
puppet_args = ['/usr/bin/puppet', '--external_node', '/usr/libexec/secstate/secstate_external_node %s' % fname, '--node_terminus', 'exec', site_pp]
@@ -1083,7 +1095,6 @@ class Secstate:
if verbose:
puppet_args.append('--verbose')
subprocess.call(puppet_args)
- os.unlink(fname)
return True
diff --git a/src/secstate/util.py b/src/secstate/util.py
index d30a169..1b208cd 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -27,7 +27,6 @@ import time
import re
import ConfigParser
import json
-import tempfile
import zipfile
import libxml2
import libxslt
--
1.7.2.2
13 years, 7 months
[PATCH] Fixed remediating archives.
by Josh Adams
Also added code to keep track of tempfiles and cleanup on exit.
Fixes bug #8096
---
src/bin/secstate | 49 +++++++++++++++++++++++++++++++------------------
src/secstate/main.py | 9 +++++----
src/secstate/util.py | 1 -
3 files changed, 36 insertions(+), 23 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index ebaf9c8..c560510 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -24,6 +24,7 @@
import sys
import ConfigParser
import os
+import shutil
from optparse import OptionParser
import openscap_api as oscap
@@ -57,67 +58,79 @@ def main():
subcommand = sys.argv[1]
except IndexError, e:
usage()
- return -1
+ return_code = -1
arg_num = 2
if (subcommand == '-c') or (subcommand == '--config'):
if not sec_instance.setConfigFile(sys.argv[arg_num]):
usage()
- return -1
+ return_code = -1
arg_num += 2
try:
subcommand = sys.argv[arg_num - 1]
except IndexError:
usage()
- return -1
+ return_code = -1
if subcommand == 'help':
- return help(sys.argv[arg_num:])
+ return_code = help(sys.argv[arg_num:])
elif subcommand == 'export':
- return export(sys.argv[arg_num:])
+ return_code = export(sys.argv[arg_num:])
elif subcommand == 'search':
- return search(sys.argv[arg_num:])
+ return_code = search(sys.argv[arg_num:])
elif subcommand == 'list':
- return list_content(sys.argv[arg_num:])
+ return_code = list_content(sys.argv[arg_num:])
elif subcommand == 'show':
- return show(sys.argv[arg_num:])
+ return_code = show(sys.argv[arg_num:])
elif os.geteuid() != 0:
sys.stderr.write("secstate must be run as root!\n")
- return -1
+ return_code = -1
elif subcommand == 'import':
- return import_content(sys.argv[arg_num:])
+ return_code = import_content(sys.argv[arg_num:])
elif subcommand == 'remove':
- return remove_content(sys.argv[arg_num:])
+ return_code = remove_content(sys.argv[arg_num:])
elif subcommand == 'select':
- return select(sys.argv[arg_num:], True)
+ return_code = select(sys.argv[arg_num:], True)
elif subcommand == 'deselect':
- return select(sys.argv[arg_num:], False)
+ return_code = select(sys.argv[arg_num:], False)
elif subcommand == 'audit':
- return audit(sys.argv[arg_num:])
+ return_code = audit(sys.argv[arg_num:])
elif subcommand == 'remediate':
- return remediate(sys.argv[arg_num:])
+ return_code = remediate(sys.argv[arg_num:])
elif subcommand == 'mitigate':
- return mitigate(sys.argv[arg_num:])
+ return_code = mitigate(sys.argv[arg_num:])
elif subcommand == 'save':
- return save_profile(sys.argv[arg_num:])
+ return_code = save_profile(sys.argv[arg_num:])
else:
sys.stderr.write("Uknown subcommand: %(command)s" % {'command':subcommand})
usage()
- return -1
+ return_code = -1
+
+ try:
+ for dir in sec_instance.tempfiles['dirs']:
+ shutil.rmtree(dir)
+
+ for tmpfile in sec_instance.tempfiles['files']:
+ os.remove(tmpfile)
+ except IOError,e:
+ sec_instance.log.error("Error cleaning up temporary files: %(err)s" % {'err':e})
+ return_code = -1
+
+ return return_code
def help(arg):
if arg == []:
diff --git a/src/secstate/main.py b/src/secstate/main.py
index f2025cd..60bb864 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -54,6 +54,7 @@ class Secstate:
self.load_content()
self.log = self.getLogger()
self.benchmark_dir = self.config.get('secstate', 'benchmark_dir')
+ self.tempfiles = {'dirs':[], 'files':[]}
def setConfigFile(self, conf):
config = ConfigParser.ConfigParser()
@@ -349,6 +350,7 @@ class Secstate:
Output: Success of failure of the import
"""
extract_path = tempfile.mkdtemp()
+ self.tempfiles['dirs'].append(extract_path)
if type[0] == "application/x-tar":
tar_file = None
@@ -407,11 +409,8 @@ class Secstate:
if benchmark == None:
return None
- # Delete temporary directory now that we are done with it
- shutil.rmtree(extract_path)
return benchmark
-
def import_content(self, content, changes=True, save=False, active_profile=NONE_PROFILE):
"""
Function: Validates XCCDF/OVAL content and optionally saves it to the data store
@@ -474,6 +473,7 @@ class Secstate:
benchmark_file = self.content[benchmark_id]
else:
benchmark_file = tempfile.mktemp()
+ self.tempfiles['files'].append(benchmark_file)
benchmark = self.import_content(benchmark_id)
if benchmark.export(benchmark_file) == None:
self.log.error("Error exporting benchmark to %(file)s" % {'file':new_file})
@@ -1049,6 +1049,7 @@ class Secstate:
site_pp = self.config.get('secstate', 'site_pp')
else:
(site_pp_buf, site_pp) = tempfile.mkstemp()
+ self.tempfiles['files'].append(site_pp)
for puppet in benchmark.puppet:
os.write(site_pp_buf, 'import "%(file)s"\n' % {'file':puppet})
os.close(site_pp_buf)
@@ -1073,6 +1074,7 @@ class Secstate:
else:
handle, fname = tempfile.mkstemp(suffix='.yaml')
+ self.tempfiles['files'].append(fname)
os.write(handle, template % dict_to_external(puppet_content))
os.close(handle)
puppet_args = ['/usr/bin/puppet', '--external_node', '/usr/libexec/secstate/secstate_external_node %s' % fname, '--node_terminus', 'exec', site_pp]
@@ -1083,7 +1085,6 @@ class Secstate:
if verbose:
puppet_args.append('--verbose')
subprocess.call(puppet_args)
- os.unlink(fname)
return True
diff --git a/src/secstate/util.py b/src/secstate/util.py
index d30a169..1b208cd 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -27,7 +27,6 @@ import time
import re
import ConfigParser
import json
-import tempfile
import zipfile
import libxml2
import libxslt
--
1.7.2.2
13 years, 7 months
[PATCH] Abort import if referenced puppet does not exist
by Josh Adams
Fixes bug #7948
---
src/secstate/main.py | 47 ++++++++++++++++++++++++++++-------------------
1 files changed, 28 insertions(+), 19 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 20a8eaf..707ddd0 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -280,8 +280,14 @@ class Secstate:
if store_path == None:
if self.content.has_key(benchmark.id):
puppet_files = map(lambda x: os.path.join(self.config.get('secstate', 'puppet_dir'), x), puppet_files)
- else:
- puppet_files = map(lambda x: os.path.join(os.path.dirname(benchmark_file), x), puppet_files)
+ else:
+ puppet_files = map(lambda x: os.path.join(os.path.dirname(benchmark_file), x), puppet_files)
+
+ for pup in puppet_files:
+ if not os.path.isfile(pup):
+ self.log.error("Error loading associated puppet content: '%(file)s'" % {'file':pup})
+ return False
+
benchmark.__dict__['puppet'] = puppet_files
benchmark.config.set(benchmark.id, 'puppet', json.dumps(list(puppet_files)))
@@ -302,30 +308,33 @@ class Secstate:
shutil.copy(benchmark_file, directory)
benchmark.config.set(id, 'file', os.path.join(directory, os.path.basename(benchmark_file)))
benchmark.config.set(id, 'selected', True)
- conf_file = open(os.path.join(self.config.get('secstate', 'conf_dir'), id + ".cfg"), 'w')
- benchmark.config.write(conf_file)
- conf_file.close()
for oval in list(set(oval_files)):
shutil.copy(os.path.join(oval_path, oval), directory)
- for puppet in benchmark.puppet:
- new_puppet = os.path.join(os.path.dirname(benchmark_file), puppet)
- puppet_dir = self.config.get('secstate', 'puppet_dir')
- if not os.path.isdir(puppet_dir):
- os.makedirs(puppet_dir)
+ puppet_dir = self.config.get('secstate', 'puppet_dir')
+ if not os.path.isdir(puppet_dir):
+ os.makedirs(puppet_dir)
+ for puppet in benchmark.puppet:
if os.path.isfile(os.path.join(puppet_dir, puppet)):
- old = open(os.path.join(puppet_dir, puppet))
- new = open(new_puppet)
+ old_file = os.path.join(puppet_dir, os.path.basename(puppet))
+ old = open(old_file)
+ new = open(puppet)
if old.read() != new.read():
self.log.info("A puppet file named '%(name)s' has already been imported. Moving old file to '%(name)s.old'" % {'name':puppet})
- os.rename(os.path.join(puppet_dir, puppet), os.path.join(puppet_dir, puppet + '.old'))
+ os.rename(old_file, old_file + '.old')
else:
continue
- shutil.copy(os.path.join(os.path.dirname(benchmark_file), puppet), self.config.get('secstate', 'puppet_dir'))
- puppet = os.path.join(puppet_dir, puppet)
+ shutil.copy(os.path.join(os.path.dirname(benchmark_file), os.path.basename(puppet)), self.config.get('secstate', 'puppet_dir'))
+
+ benchmark.__dict__['puppet'] = map(lambda x: os.path.join(self.config.get('secstate', 'puppet_dir'), os.path.basename(x)), benchmark.puppet)
+ benchmark.config.set(benchmark.id, 'puppet', json.dumps(benchmark.puppet))
+
+ conf_file = open(os.path.join(self.config.get('secstate', 'conf_dir'), id + ".cfg"), 'w')
+ benchmark.config.write(conf_file)
+ conf_file.close()
except (IOError, OSError), e:
self.log.error("Error importing content: %(error)s" % {'error':e})
@@ -334,7 +343,7 @@ class Secstate:
return benchmark
- def import_zipped_content(self, zip, type, store_path, puppet, changes=False, active_profile=NONE_PROFILE):
+ def import_zipped_content(self, zip, type, store_path, changes=False, active_profile=NONE_PROFILE):
"""
Function: Validate and copy content from zipped file to repository
Input: Zipped file contating content and bool whether it contains puppet content
@@ -404,7 +413,7 @@ class Secstate:
return benchmark
- def import_content(self, content, puppet=False, changes=True, save=False, active_profile=NONE_PROFILE):
+ def import_content(self, content, changes=True, save=False, active_profile=NONE_PROFILE):
"""
Function: Validates XCCDF/OVAL content and optionally saves it to the data store
Input: File containing content
@@ -424,7 +433,7 @@ class Secstate:
return None
if self.content.has_key(content):
- return self.import_content(self.content[content], puppet, changes, active_profile=active_profile)
+ return self.import_content(self.content[content], changes, active_profile=active_profile)
if save:
store_path = self.config.get('secstate', 'benchmark_dir')
@@ -449,7 +458,7 @@ class Secstate:
return self.import_benchmark(content, store_path=store_path, oval_path=os.path.dirname(content), changes=changes, active_profile=active_profile)
else:
- return self.import_zipped_content(content, file_type, store_path=store_path, puppet=puppet, changes=changes, active_profile=active_profile)
+ return self.import_zipped_content(content, file_type, store_path=store_path, changes=changes, active_profile=active_profile)
def export(self, benchmark_id, new_file, original=False):
if not self.content.has_key(benchmark_id):
--
1.7.2.2
13 years, 7 months
[PATCH] Do not check for root when using -h or --help
by Josh Adams
Fixes bug #6967
---
src/bin/secstate | 37 +++++++++++++++++++++++++++++++++----
1 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index ebaf9c8..c33ae8b 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -51,6 +51,13 @@ Sub-commands:
sec_instance = secstate.Secstate(CONFIG_FILE)
+def root_check(command):
+ if os.geteuid() != 0:
+ sys.stderr.write("%s must be run as root!\n" % command)
+ return False
+ else:
+ return True
+
def main():
try:
@@ -86,10 +93,6 @@ def main():
elif subcommand == 'show':
return show(sys.argv[arg_num:])
- elif os.geteuid() != 0:
- sys.stderr.write("secstate must be run as root!\n")
- return -1
-
elif subcommand == 'import':
return import_content(sys.argv[arg_num:])
@@ -158,6 +161,9 @@ def import_content(arguments):
help="Selects the active profile")
(options, args) = parser.parse_args(arguments)
+ if not root_check('import'):
+ return -1
+
for arg in args:
content = sec_instance.import_content(arg, options.puppet, save=True, active_profile=options.profile)
if content == None:
@@ -175,6 +181,10 @@ def export(arguments):
def remove_content(arguments):
parser = OptionParser(usage="secstate remove [options] <ContentID>")
(options, args) = parser.parse_args(arguments)
+
+ if not root_check('remove'):
+ return -1
+
for arg in args:
if (not (sec_instance.remove_content(arg))):
return -1
@@ -188,6 +198,10 @@ def select(arguments, value):
parser.add_option('-r', '--recurse', action='store_true', dest='recurse', default=False,
help="Recursively %(sel)sselect rules inside of groups or benchmarks" % {'sel':sel})
(options, args) = parser.parse_args(arguments)
+
+ if not root_check('select'):
+ return -1
+
if len(args) == 1:
if not sec_instance.select(args[0], args[0], value, recurse=options.recurse):
return -1
@@ -213,6 +227,10 @@ def audit(arguments):
parser.add_option('-r', '--rule', action='store', type='string', dest='rule', default=None,
help="Audit only the specified rule")
(options, args) = parser.parse_args(arguments)
+
+ if not root_check('audit'):
+ return -1
+
if (not (sec_instance.audit(args, all=options.all, verbose=options.verbose, profile=options.profile, results_dir=options.output, output_xml=options.xml, output_html=options.html, rule=options.rule))):
return -1
@@ -229,6 +247,9 @@ def remediate(arguments):
parser.add_option('-v', '--verbose', action='store_false', dest='verbose', help="Prints out extra information during the remediate process")
parser.add_option('-y', '--yes', action='store_true', dest='yes', help="Respond 'yes' to all prompts")
options, args = parser.parse_args(arguments)
+
+ if not root_check('remediate'):
+ return -1
kwargs = {}
@@ -294,6 +315,10 @@ def show(arguments):
def save_profile(arguments):
parser = OptionParser(usage="secstate save [options] <BenchmarkID> <ProfileName>")
(options, args) = parser.parse_args(arguments)
+
+ if not root_check('save'):
+ return -1
+
if len(args) != 2:
sys.stderr.write("Wrong number of arguments passed to save\n'secstate save [options] <benchmark> <profile name>'\n")
return -1
@@ -308,6 +333,10 @@ def mitigate(arguments):
parser.add_option('-a', '--authority', action='store', type='string', dest='authority', default=None,
help="Show extra information about the item being shown")
(options, args) = parser.parse_args(arguments)
+
+ if not root_check('mitigate'):
+ return -1
+
if options.remark == None:
options.remark = raw_input("Please enter a remark for this mitigation. Press Enter when finished\n")
if len(args) != 2:
--
1.7.2.2
13 years, 7 months
[PATCH] Implemented the 'mitigate' function
by Josh Adams
Rules that are mitigated have overrides and are not remediated.
Fixes bug #7965
---
src/bin/secstate | 19 +++++++++++++++++++
src/secstate/main.py | 39 ++++++++++++++++++++++++++++++++++++++-
src/secstate/util.py | 37 +++++++++++++++++++++++++++++--------
3 files changed, 86 insertions(+), 9 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index c219e3f..ebaf9c8 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -107,6 +107,9 @@ def main():
elif subcommand == 'remediate':
return remediate(sys.argv[arg_num:])
+
+ elif subcommand == 'mitigate':
+ return mitigate(sys.argv[arg_num:])
elif subcommand == 'save':
return save_profile(sys.argv[arg_num:])
@@ -298,6 +301,22 @@ def save_profile(arguments):
if not sec_instance.save_profile(args[0], args[1]):
return -1
+def mitigate(arguments):
+ parser = OptionParser(usage="secstate mitigate [options] <BenchmarkID> <RuleID>")
+ parser.add_option('-r', '--remark', action='store', type='string', dest='remark', default=None,
+ help="Show extra information about the item being shown")
+ parser.add_option('-a', '--authority', action='store', type='string', dest='authority', default=None,
+ help="Show extra information about the item being shown")
+ (options, args) = parser.parse_args(arguments)
+ if options.remark == None:
+ options.remark = raw_input("Please enter a remark for this mitigation. Press Enter when finished\n")
+ if len(args) != 2:
+ sys.stderr.write("Wrong number of arguments passed to remediate\n'secstate remediate [options] <ContentID> <ItemID> <Remark>'\n")
+ return -1
+
+ if not sec_instance.mitigate(args[0], args[1], options.remark, options.authority):
+ return -1
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 01405dd..3f4702a 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -285,6 +285,11 @@ class Secstate:
benchmark.__dict__['puppet'] = puppet_files
benchmark.config.set(benchmark.id, 'puppet', json.dumps(list(puppet_files)))
+ benchmark.__dict__['mitigations'] = {}
+ if benchmark.config.has_section('mitigations'):
+ for opt,val in benchmark.config.items('mitigations'):
+ benchmark.mitigations[opt] = json.loads(val)
+
if store_path != None:
id = get_benchmark_id(benchmark_file)
directory = os.path.join(bench_dir, id)
@@ -1036,12 +1041,13 @@ class Secstate:
os.close(site_pp_buf)
self.log.info("-- Remediating %(id)s --" % {'id':bench_id})
+ ignore_ids = []
try:
- ignore_ids = []
for key in benchmark.selections:
if benchmark.selections[key] == False:
ignore_ids.append(key)
ignore_ids += passing_ids
+ ignore_ids += benchmark.mitigations.keys()
puppet_content = parse_puppet_fixes(benchmark, ignore_ids)
except SecstateException, se:
sys.stderr.write('Error: %s\n' % str(se))
@@ -1067,4 +1073,35 @@ class Secstate:
os.unlink(fname)
return True
+
+ def mitigate(self, content_id, item_id, remark, authority=None):
+ if not self.content.has_key(content_id):
+ self.log.error("No content '%(id)s' has been imported" % {'id':content_id})
+ return False
+
+ benchmark = self.import_content(content_id)
+ if not benchmark.__dict__.has_key('oval'):
+ self.log.error("Cannot mitigate OVAL content")
+ return False
+
+ item = benchmark.get_item(item_id)
+ if item == None:
+ self.log.error("%(bench)s does not contain item %(item)s" % {'bench':benchmark_id, 'item':item_id})
+ return False
+
+ if not benchmark.config.has_section('mitigations'):
+ benchmark.config.add_section('mitigations')
+ mitg_dict = {'remark':remark, 'authority':authority}
+ benchmark.config.set('mitigations', item_id, json.dumps(mitg_dict))
+
+ try:
+ fp = open(self.content_configs[content_id], 'w')
+ benchmark.config.write(fp)
+ fp.close()
+ except IOError, e:
+ self.log.error("Error saving changes: %(err)s" % {'err':e})
+ return False
+
+ return True
+
diff --git a/src/secstate/util.py b/src/secstate/util.py
index fbc5d33..901c9a4 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -57,6 +57,8 @@ def xccdf_reporter(msg, usr):
result = msg.user2num
if result == oscap.xccdf.XCCDF_RESULT_PASS:
usr['pass'] += 1
+ elif msg.user1str in usr['mitigations']:
+ usr['mitg_total'] += 1
elif result == oscap.xccdf.XCCDF_RESULT_FAIL:
usr['fail'] += 1
elif result == oscap.xccdf.XCCDF_RESULT_UNKNOWN:
@@ -101,7 +103,9 @@ def evaluate_xccdf(benchmark, url_XCCDF, s_profile=None, all=False, verbose=Fals
'nselect':0,
'info':0,
'fixed':0,
- 'verbose':verbose}
+ 'mitg_total':0,
+ 'verbose':verbose,
+ 'mitigations':benchmark.mitigations.keys()}
policy_model.register_output_callback(xccdf_reporter, res_dict)
@@ -129,10 +133,26 @@ def evaluate_xccdf(benchmark, url_XCCDF, s_profile=None, all=False, verbose=Fals
score = policy.get_score(ritem, model.system)
ritem.add_score(score)
+ for rule_res in ritem.rule_results:
+ id = rule_res.idref
+ if benchmark.mitigations.has_key(id):
+ override = oscap.xccdf.override_new()
+ override.new_result = oscap.xccdf.XCCDF_RESULT_INFORMATIONAL
+ override.old_result = rule_res.result
+ remark = oscap.common.text_new()
+ remark.text = str(benchmark.mitigations[id]['remark'])
+ override.remark = remark
+ authority = benchmark.mitigations[id]['authority']
+ if authority != None:
+ override.authority = authority
+ rule_res.add_override(override)
+ rule_res.result = override.new_result
+
ritem.end_time = time.time()
print "--Results for '%(id)s' (Profile: '%(prof)s')--" % {'id':benchmark.id, 'prof':s_profile}
print "Passed:\t\t%(pass)s\n" \
+ "Mitigated:\t%(mitg_total)s\n" \
"Failed:\t\t%(fail)s\n" \
"Fixed:\t\t%(fixed)s\n" \
"Not Selected:\t%(nselect)s\n" \
@@ -400,13 +420,14 @@ def apply_changes_profile(benchmark):
prof.add_title(new_title)
prof.id = section
- for id,val in benchmark.config.items(section):
- if id != 'extends':
- sel_dict = json.loads(val)
- select = oscap.xccdf.select_new()
- select.item = id
- select.selected = sel_dict['selected']
- prof.add_select(select)
+ if section != 'mitigations':
+ for id,val in benchmark.config.items(section):
+ if id != 'extends':
+ sel_dict = json.loads(val)
+ select = oscap.xccdf.select_new()
+ select.item = id
+ select.selected = sel_dict['selected']
+ prof.add_select(select)
benchmark.add_profile(prof)
--
1.7.2.2
13 years, 7 months
[PATCH] Abort import if puppet content does not exist
by Josh Adams
Fixes bug #7948
---
src/secstate/main.py | 13 +++++++++----
1 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 01405dd..d138114 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -282,6 +282,11 @@ class Secstate:
puppet_files = map(lambda x: os.path.join(self.config.get('secstate', 'puppet_dir'), x), puppet_files)
else:
puppet_files = map(lambda x: os.path.join(os.path.dirname(benchmark_file), x), puppet_files)
+ for pup in puppet_files:
+ if not os.path.isfile(pup):
+ self.log.error("Error loading associated puppet content: '%(file)s'" % {'file':pup})
+ return False
+
benchmark.__dict__['puppet'] = puppet_files
benchmark.config.set(benchmark.id, 'puppet', json.dumps(list(puppet_files)))
@@ -329,7 +334,7 @@ class Secstate:
return benchmark
- def import_zipped_content(self, zip, type, store_path, puppet, changes=False, active_profile=NONE_PROFILE):
+ def import_zipped_content(self, zip, type, store_path, changes=False, active_profile=NONE_PROFILE):
"""
Function: Validate and copy content from zipped file to repository
Input: Zipped file contating content and bool whether it contains puppet content
@@ -399,7 +404,7 @@ class Secstate:
return benchmark
- def import_content(self, content, puppet=False, changes=True, save=False, active_profile=NONE_PROFILE):
+ def import_content(self, content, changes=True, save=False, active_profile=NONE_PROFILE):
"""
Function: Validates XCCDF/OVAL content and optionally saves it to the data store
Input: File containing content
@@ -419,7 +424,7 @@ class Secstate:
return None
if self.content.has_key(content):
- return self.import_content(self.content[content], puppet, changes, active_profile=active_profile)
+ return self.import_content(self.content[content], changes, active_profile=active_profile)
if save:
store_path = self.config.get('secstate', 'benchmark_dir')
@@ -444,7 +449,7 @@ class Secstate:
return self.import_benchmark(content, store_path=store_path, oval_path=os.path.dirname(content), changes=changes, active_profile=active_profile)
else:
- return self.import_zipped_content(content, file_type, store_path=store_path, puppet=puppet, changes=changes, active_profile=active_profile)
+ return self.import_zipped_content(content, file_type, store_path=store_path, changes=changes, active_profile=active_profile)
def export(self, benchmark_id, new_file, original=False):
if not self.content.has_key(benchmark_id):
--
1.7.2.2
13 years, 7 months
[PATCH] Pass ovalid param into transform for OVAL files
by Josh Adams
Fixes bug #7947
---
src/secstate/util.py | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/src/secstate/util.py b/src/secstate/util.py
index fbc5d33..6a3f8bd 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -236,6 +236,7 @@ def export_xml(results_dir, id, benchmark=None, sessions=None):
def result_to_html(input, stylesheet, output, media=None, about=None, help=None):
output_dir = os.path.dirname(output)
+ transform_params = {'path': output_dir}
try:
shutil.copy(stylesheet, output_dir)
except IOError,e:
@@ -247,6 +248,8 @@ def result_to_html(input, stylesheet, output, media=None, about=None, help=None)
style = libxslt.parseStylesheetDoc(styledoc)
if os.path.isfile(input):
doc = libxml2.parseFile(input)
+ if not is_benchmark(input):
+ transform_params['ovalid'] = "'%s'" % os.path.basename(input.split('.results.xml')[0])
else:
doc = libxml2.parseDoc(input)
@@ -254,7 +257,7 @@ def result_to_html(input, stylesheet, output, media=None, about=None, help=None)
sys.stderr.write("Error parsing input: '%(in)s'" % {'in':input})
return False
- result = style.applyStylesheet(doc, {'path':output_dir})
+ result = style.applyStylesheet(doc, transform_params)
if not style.saveResultToFilename(output, result, 0):
sys.stderr.write("Error exporitng results to %(file)s" % {'file':output})
return False
--
1.7.2.2
13 years, 7 months
[PATCH] Use tempfile to create uniqe directory
by Josh Adams
Fixes bug #7923
---
src/secstate/main.py | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 01405dd..f42fd02 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -642,8 +642,9 @@ class Secstate:
res_benchmark = None
if results_dir == None:
- results_dir = "audit-%(hostname)s-%(date)s" % {'hostname':os.uname()[1],
- 'date':time.strftime("%a-%B-%d-%H_%M_%S-%Y")}
+ unique_str = "audit-%(hostname)s-%(date)s" % {'hostname':os.uname()[1],
+ 'date':time.strftime("%a-%B-%d-%H_%M_%S-%Y")}
+ results_dir = tempfile.mkdtemp(prefix=unique_str, dir=os.getcwd())
if args == []:
args = self.content.keys()
--
1.7.2.2
13 years, 7 months
[PATCH] Print error message when auditing w/o content
by Josh Adams
Fixes bug #7949
---
src/secstate/main.py | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 01405dd..47eca49 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -562,7 +562,7 @@ class Secstate:
if benchmark.config.has_option(benchmark_id, 'profile'):
active_profile = benchmark.config.get(benchmark_id, 'profile')
# Only create 'Custom' profile if modifying an original profile
- if (active_profile != "Custom") and (not benchmark.config.has_section(active_profile)):
+ if (active_profile != "Custom") and (not benchmark.config.has_section(active_profile)):
if not benchmark.config.has_section("Custom"):
benchmark.config.add_section("Custom")
benchmark.config.set('Custom', 'extends', active_profile)
@@ -649,6 +649,10 @@ class Secstate:
args = self.content.keys()
file_index = []
+ if len(args) == 0:
+ self.log.error("No content has been imported")
+ return False
+
for arg in args:
scanned_content = self.import_content(arg)
if scanned_content == None:
--
1.7.2.2
13 years, 7 months