Users can now specify what directory to put xml/html results. Also
fixed a few errors with the OVAL commit.
---
src/bin/secstate | 8 ++-
src/etc/secstate.conf | 3 +-
src/secstate/main.py | 77 ++++++++++++++++++++++++++++--------
src/secstate/util.py | 104 +++++++++++++++++++++++++++---------------------
4 files changed, 124 insertions(+), 68 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 3dce12c..a7bf097 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -153,14 +153,16 @@ def audit(arguments):
help="Specifies the interpreter to use when auditing the
system")
parser.add_option('-p', '--profile', action='store',
type='string', dest='profile',
default=None, help="Specifies the profile to use when auditing
the system")
- parser.add_option('-r', '--results', action='store',
dest='results', default=False,
- help="Export results to the specified file")
+ parser.add_option('--xml', action='store', type='string',
dest='xml',
+ default=None, help="Specifies the directory to write XML
results to (directory will be crated if non-existant)")
+ parser.add_option('--html', action='store', type='string',
dest='html',
+ default=None, help="Specifies the directory to write HTML
results to (directory will be crated if non-existant)")
parser.add_option('-v', '--verbose', action='store_true',
dest='verbose', default=False,
help="Prints out extra information during the audit
process")
parser.add_option('-a', '--all', action='store_true',
dest='all', default=False,
help="Audit everything regardless of selection status")
(options, args) = parser.parse_args(arguments)
- if (not (sec_instance.audit(options.interpreter, args, all=options.all,
verbose=options.verbose, profile=options.profile, f_results=options.results))):
+ if (not (sec_instance.audit(options.interpreter, args, all=options.all,
verbose=options.verbose, profile=options.profile, xml=options.xml, html=options.html))):
return -1
def remediate(arguments):
diff --git a/src/etc/secstate.conf b/src/etc/secstate.conf
index 61f4249..911c04a 100644
--- a/src/etc/secstate.conf
+++ b/src/etc/secstate.conf
@@ -6,7 +6,8 @@ conf_dir=/var/lib/secstate/configs
oval_schema_dir=/usr/share/ovaldi
oval_interpreter=openscap
agressivness=1
-results_stylesheet=/etc/secstate/results_to_html.xsl
+oval_stylesheet=/etc/secstate/results_to_html.xsl
+xccdf_stylesheet=/etc/secstate/results_to_html.xsl
[logging]
debugging=0
diff --git a/src/secstate/main.py b/src/secstate/main.py
index bb6e6d6..edca0f1 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -173,13 +173,20 @@ class Secstate:
return (None, None)
if store_path:
+ if not os.path.isdir(store_path):
+ try:
+ os.makedirs(store_path)
+ except IOError, e:
+ self.log.error("Could not create benchmark directory:
%(dir)s" % {'dir':bench_dir})
+ return (None, None)
+
shutil.copy(oval_file, store_path)
config = ConfigParser.ConfigParser()
config.optionxform = str
id = os.path.splitext(os.path.basename(oval_file))[0]
config.add_section(id)
config.set(id, 'selected', True)
- config.set(id, 'file', os.path.join(store_path, oval_file))
+ config.set(id, 'file', os.path.join(store_path,
os.path.basename(oval_file)))
conf_file = open(os.path.join(self.config.get('secstate',
'conf_dir'), id + ".cfg"), 'w')
config.write(conf_file)
conf_file.close()
@@ -331,6 +338,15 @@ class Secstate:
xccdf = False
oval = False
store_path = None
+ conf_dir = self.config.get('secstate', 'conf_dir')
+
+ if save:
+ if not os.path.isdir(conf_dir):
+ try:
+ os.makedirs(conf_dir)
+ except IOError, e:
+ self.log.error("Could not create directory: %(dir)s" %
{'dir':conf_dir})
+ return (None, None)
if self.content.has_key(content):
(benchmark, oval) = self.import_content(os.path.join(self.benchmark_dir,
content, self.content[content]), save=False)
@@ -358,7 +374,7 @@ class Secstate:
return self.import_benchmark(content, store_path=store_path,
oval_path=os.path.dirname(content))
else:
- return self.import_zipped_content(content, file_type, puppet)
+ return self.import_zipped_content(content, file_type,
store_path=self.config.get('secstate', 'benchmark_dir'), puppet=puppet)
def export(self, benchmark_id, new_file, original=False):
if not self.content.has_key(benchmark_id):
@@ -389,20 +405,23 @@ class Secstate:
return True
def remove_content(self, benchmark_id):
-
if benchmark_id == 'all':
for key in self.content:
- try:
- shutil.rmtree(os.path.join(self.benchmark_dir, key))
- except IOError,e:
- self.log.error("Error removing content: %(error)s" %
{'error':e})
- return False
-
+ self.remove_content(key)
+
elif self.content.has_key(benchmark_id):
+ cfg = ConfigParser.ConfigParser()
+ conf_file = self.content_configs[benchmark_id]
+ fp = open(conf_file)
+ cfg.readfp(fp)
+ fp.close()
try:
- shutil.rmtree(os.path.join(self.benchmark_dir, benchmark_id))
- os.remove(os.path.join(self.config.get('secstate',
'conf_dir'), benchmark_id + ".cfg"))
- except (IOError, OSError), e:
+ if os.path.split(cfg.get(benchmark_id, "file"))[0] !=
self.config.get('secstate', 'oval_dir'):
+ shutil.rmtree(os.path.split(cfg.get(benchmark_id,
"file"))[0])
+ else:
+ os.remove(cfg.get(benchmark_id, "file"))
+ os.remove(conf_file)
+ except IOError,e:
self.log.error("Error removing content: %(error)s" %
{'error':e})
return False
@@ -491,7 +510,7 @@ class Secstate:
oscap.xccdf_benchmark_free(benchmark)
return True
- def audit(self, interpreter, args, profile=None, verbose=False, all=False,
f_results=None):
+ def audit(self, interpreter, args, profile=None, verbose=False, all=False, xml=None,
html=None):
"""
Function: Run an audit on the system agains the given definition model
Input: Interpreter to use, args for interpreter, schema to use, specific
definition or template
@@ -500,8 +519,13 @@ class Secstate:
"""
def_model = None
benchmark = None
+ res_model = None
+ res_benchmark = None
config = None
+ if args == []:
+ args = self.content.keys()
+
for arg in args:
(benchmark, def_model) = self.import_content(arg)
if (benchmark == None) and (def_model == None):
@@ -531,16 +555,33 @@ class Secstate:
if profile!= None:
if oscap.xccdf_benchmark_get_item(benchmark, profile) ==
None:
self.log.error("Profile %(prof)s does not
exist." % {'prof':profile})
+ oscap.xccdf_benchmark_free(benchmark)
+ oscap.oval_definition_model_free(def_model)
return False
- ret = (evaluate_xccdf(benchmark, def_model, arg, sess,
s_profile=profile, verbose=verbose) == 0)
+ (res_benchmark, res_model) = evaluate_xccdf(benchmark, arg, sess,
s_profile=profile, verbose=verbose)
elif def_model != None:
- ret = (evaluate_oval(sess, f_results, verbose) == 0)
+ (res_benchmark, res_model) = evaluate_oval(sess, verbose)
- if ret == False:
- self.log.error("Error auditing %(arg)s" %
{'arg':arg})
- return ret
+ if (res_benchmark == None) and (res_model == None):
+ self.log.error("Error auditing %(arg)s" %
{'arg':arg})
+ oscap.oval_agent_destroy_session(sess)
+ return False
+
+ if xml:
+ export_results(xml, arg, res_benchmark, res_model)
+ if html:
+ xccdf_ss = self.config.get('secstate',
'xccdf_stylesheet')
+ oval_ss = self.config.get('secstate',
'oval_stylesheet')
+ export_results(tempfile.mkdtemp(), arg, res_benchmark, res_model,
xccdf_ss, oval_ss, html_dir=html)
+
+ if res_benchmark != None:
+ oscap.xccdf_benchmark_free(res_benchmark)
+
+ oscap.oval_agent_destroy_session(sess)
+
+ return True
def search(self, search_string, verbose=False):
"""
diff --git a/src/secstate/util.py b/src/secstate/util.py
index aaec22a..8e16326 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -27,6 +27,9 @@ import re
import ConfigParser
import json
import tempfile
+import zipfile
+import libxml2
+import libxslt
import openscap as oscap
@@ -183,7 +186,7 @@ def output_callback(id, result, usr):
print "Rule '%(id)s' result: %(res)s" % {'id':id,
'res':oscap.xccdf_test_result_type_get_text(result)}
return 0
-def evaluate_xccdf(benchmark, def_model, url_XCCDF, sess, s_profile=None,
verbose=False):
+def evaluate_xccdf(benchmark, url_XCCDF, sess, s_profile=None, all=False,
verbose=False):
policy = None
policy_model = oscap.xccdf_policy_model_new(benchmark)
@@ -249,13 +252,15 @@ def evaluate_xccdf(benchmark, def_model, url_XCCDF, sess,
s_profile=None, verbos
"Not Applicable:\t%(na)s\n" \
"Error:\t\t%(err)s\n" \
"Informational:\t%(info)s\n" \
- "Unknown:\t%(unknown)s" % res_dict
-
- oscap.oval_agent_destroy_session(sess)
+ "Unknown:\t%(unknown)s\n" % res_dict
+
+ results_benchmark = oscap.xccdf_benchmark_clone(benchmark)
+ oscap.xccdf_benchmark_add_result(results_benchmark, oscap.xccdf_result_clone(ritem))
+ res_model = oscap.oval_agent_get_results_model(sess)
+
oscap.oval_agent_cb_data_free(usr)
- oscap.oval_definition_model_free(def_model)
oscap.xccdf_policy_model_free(policy_model)
- return 0
+ return (results_benchmark, res_model)
def oval_callback(id, result, usr):
if result == oscap.OVAL_RESULT_TRUE:
@@ -276,39 +281,7 @@ def oval_callback(id, result, usr):
return 0
-def output_oval_results(res_model, res_directives, xml, html):
- if xml:
- if not oscap.oval_results_model_export(res_model, res_directives, xml):
- self.log.error("Error exporting results to %(file)s" %
{'file':xml})
- return False
-
- if html:
- tmp = tempfile.mktemp()
- if not oscap.oval_results_model_export(res_model, res_directives, tmp):
- self.log.error("Error exporting results to %(file)s" %
{'file':html})
- return False
-
- import libxml2
- import libxslt
-
- styledoc = libxml2.parseFile(self.config.get('secstate',
'results_stylesheet'))
- style = libxslt.parseStylesheetDoc(styledoc)
- doc = libxml2.parseFile(tmp)
- result = style.applyStylesheet(doc, None)
- if not style.saveResultToFilename(html, result, 0):
- self.log.error("Error exporting results to %(file)s" %
{'file':html})
- style.freeStylesheet()
- doc.freeDoc()
- result.freeDoc()
- return False
-
- style.freeStylesheet()
- doc.freeDoc()
- result.freeDoc()
-
- return True
-
-def evaluate_oval(sess, f_results, verbose=False):
+def evaluate_oval(sess, verbose=True):
usr = {'false':0,
'true':0,
@@ -320,7 +293,8 @@ def evaluate_oval(sess, f_results, verbose=False):
ret = oscap.oval_agent_eval_system_py(sess, oval_callback, usr)
- print "Evaluation Completed"
+ if verbose:
+ print "Evaluation Completed"
if ret == -1:
if oscap.oscap_err():
@@ -328,8 +302,6 @@ def evaluate_oval(sess, f_results, verbose=False):
'desc':oscap.oscap_err_desc()})
return None
- res_model = oscap.oval_agent_get_results_model(sess)
-
print "--Results--\n" \
"True:\t\t%(true)s\n" \
"False:\t\t%(false)s\n" \
@@ -338,7 +310,32 @@ def evaluate_oval(sess, f_results, verbose=False):
"Not Evaluated:\t%(neval)s\n" \
"Not Applicable:\t%(na)s\n" % usr
- if f_results:
+ res_model = oscap.oval_agent_get_results_model(sess)
+ return (None, res_model)
+
+def export_results(results_dir, id, benchmark=None, res_model=None, xccdf_ss=None,
oval_ss=None, html_dir=None):
+ if not os.path.isdir(results_dir):
+ try:
+ os.makedirs(results_dir)
+ except IOError, e:
+ self.log.error("Could not create benchmark directory: %(dir)s" %
{'dir':results_dir})
+
+ if html_dir != None:
+ if not os.path.isdir(html_dir):
+ try:
+ os.makedirs(html_dir)
+ except IOError, e:
+ self.log.error("Could not create benchmark directory: %(dir)s"
% {'dir':html_dir})
+
+ if benchmark != None:
+ xccdf_xml = os.path.join(results_dir, id + ".xccdf.xml")
+ oscap.xccdf_benchmark_export(benchmark, xccdf_xml)
+
+ if xccdf_ss != None:
+ result_to_html(xccdf_xml, xccdf_ss, os.path.join(html_dir, id +
".xccdf.html"))
+
+ if res_model != None:
+ oval_xml= os.path.join(results_dir, id + ".oval.xml")
res_direct = oscap.oval_result_directives_new(res_model)
oscap.oval_result_directives_set_reported(res_direct, oscap.OVAL_RESULT_TRUE |
oscap.OVAL_RESULT_FALSE |
@@ -349,10 +346,25 @@ def evaluate_oval(sess, f_results, verbose=False):
oscap.oval_result_directives_set_content(res_direct, oscap.OVAL_RESULT_FALSE,
oscap.OVAL_DIRECTIVE_CONTENT_FULL)
oscap.oval_result_directives_set_content(res_direct, oscap.OVAL_RESULT_TRUE,
oscap.OVAL_DIRECTIVE_CONTENT_FULL)
- output_oval_results(res_model, res_direct, f_xml, f_html)
- oscap.oval_result_directives_free(res_direct)
+ oscap.oval_results_model_export(res_model, res_direct, oval_xml)
+
+ # Create html results
+ if oval_ss != None:
+ result_to_html(oval_xml, oval_ss, os.path.join(html_dir, id +
".oval.html"))
+
+ return True
- return res_model
+def result_to_html(input, stylesheet, output):
+ styledoc = libxml2.parseFile(stylesheet)
+ style = libxslt.parseStylesheetDoc(styledoc)
+ doc = libxml2.parseFile(input)
+ result = style.applyStylesheet(doc, None)
+ if not style.saveResultToFilename(output, result, 0):
+ sys.stderr.write("Error exporitng results to %(file)s" %
{'file':output})
+
+ style.freeStylesheet()
+ doc.freeDoc()
+ result.freeDoc()
def is_benchmark(benchmark):
tree = xml.dom.minidom.parse(benchmark)
--
1.7.1.1