[PATCH 1/4] add dereferencing sub elements function and use it in parse_puppet_fixes
by mkeeler@tresys.com
From: Matt Keeler <mkeeler(a)tresys.com>
fix name errors
finish implementing the sub element support
---
src/secstate/util.py | 40 +++++++++++++++++++++++++++++++---------
1 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 8e16326..355dda1 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -58,14 +58,13 @@ def iterator_to_generator_and_list(iterator_type):
return gen,lst
-xccdf_generator_list_types = ['xccdf_item', 'xccdf_notice', 'xccdf_status', 'xccdf_reference',
- 'xccdf_identity', 'xccdf_model', 'xccdf_result', 'xccdf_profile',
- 'xccdf_select', 'xccdf_value', 'xccdf_value_binding', 'xccdf_setvalue', 'xccdf_refine_value',
- 'xccdf_refine_rule', 'xccdf_ident', 'xccdf_check', 'xccdf_profile_note',
- 'xccdf_fixtext', 'xccdf_check_content_ref', 'xccdf_check_import',
- 'xccdf_fix', 'xccdf_check_export', 'xccdf_warning', 'xccdf_instance',
- 'xccdf_message', 'xccdf_override', 'xccdf_rule_result', 'xccdf_score',
- 'xccdf_target_fact', 'xccdf_plain_text', 'xccdf_model']
+xccdf_generator_list_types = ['xccdf_check', 'xccdf_check_content_ref', 'xccdf_check_export', 'xccdf_check_import',
+ 'xccdf_fix', 'xccdf_fixtext', 'xccdf_ident', 'xccdf_identity', 'xccdf_instance',
+ 'xccdf_item', 'xccdf_message', 'xccdf_model', 'xccdf_notice', 'xccdf_override',
+ 'xccdf_plain_text', 'xccdf_profile', 'xccdf_profile_note', 'xccdf_reference',
+ 'xccdf_refine_rule', 'xccdf_refine_value', 'xccdf_result', 'xccdf_rule_result',
+ 'xccdf_score', 'xccdf_select', 'xccdf_setvalue', 'xccdf_status', 'xccdf_target_fact',
+ 'xccdf_value', 'xccdf_value_binding', 'xccdf_value_instance', 'xccdf_warning']
for type in xccdf_generator_list_types:
gen_func, list_func = iterator_to_generator_and_list('%s_iterator' % type)
@@ -550,7 +549,7 @@ def parse_puppet_fixes(benchmark, ignore_ids=[]):
for fix in fixes:
if oscap.xccdf_fix_get_system(fix) == 'urn:xccdf:fix:script:puppet':
- content = oscap.xccdf_fix_get_content(fix)
+ content = dereference_sub_elements(fix, benchmark)
for line in content.split('\n'):
mtch = line_reg.match(line)
@@ -599,3 +598,26 @@ def dict_to_external(puppet_dict):
return '\n'.join(content)
+
+def dereference_sub_elements(fix, benchmark):
+ sub_element_re = r'<sub\s+.*idref="(.*?)"\s*/\s*>'
+ replacement_re = r'<sub\s+.*idref="%s"\s*/\s*>'
+
+ content = oscap.xccdf_fix_get_content(fix)
+ ids = [m.group(1) for m in re.finditer(sub_element_re, content)]
+ for id in ids:
+ #lookup the actual value the id points too
+ value_obj = oscap.xccdf_item_to_value(oscap.xccdf_benchmark_get_item(benchmark, id))
+ value_instance = oscap.xccdf_value_instance_iterator_next(oscap.xccdf_value_get_instances(value_obj))
+
+ type = oscap.xccdf_value_instance_get_type(value_instance)
+ if type == oscap.XCCDF_TYPE_NUMBER:
+ value = str(oscap.xccdf_value_instance_get_value_number(value_instance))
+ elif type == oscap.XCCDF_TYPE_STRING:
+ value = oscap.xccdf_value_instance_get_value_string(value_instance)
+ elif type == oscap.XCCDF_TYPE_BOOLEAN:
+ value = str(oscap.xccdf_value_instance_get_value_boolean(value_instance)).lower()
+
+ content = re.sub(replacement_re % id, re.escape(value), content)
+ return content
+
\ No newline at end of file
--
1.6.5.2
13 years, 10 months
[PATCH] Only list selected items
by Josh Adams
Only list selected items unless 'show_all' is set. Also fixed a
potential segfault.
---
src/secstate/main.py | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index e91649d..84a6caf 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -755,7 +755,7 @@ class Secstate:
else:
item = oscap.xccdf_benchmark_get_item(benchmark, arg)
if item == None:
- self.sublist(None, bench_cfg, def_model, arg, recurse, show_all, tabs)
+ return self.sublist(None, bench_cfg, def_model, arg, recurse, show_all, tabs)
is_selected = oscap.xccdf_item_get_selected(item)
@@ -788,9 +788,10 @@ class Secstate:
if not recurse or (tabs == 0):
selected = "[ ]"
- print "%(indent)s%(sel)s%(type)s - ID: %(id)s, Title: '%(title)s'" % {'indent':tabstr, 'sel':selected,
- 'type':item_get_type_str(item), 'id':arg,
- 'title':oscap.oscap_text_get_text(title)}
+ if show_all or is_selected or not recurse:
+ print "%(indent)s%(sel)s%(type)s - ID: %(id)s, Title: '%(title)s'" % {'indent':tabstr, 'sel':selected,
+ 'type':item_get_type_str(item), 'id':arg,
+ 'title':oscap.oscap_text_get_text(title)}
if recurse:
type = oscap.xccdf_item_get_type(item)
if (type == oscap.XCCDF_GROUP) or (type == oscap.XCCDF_BENCHMARK):
--
1.7.1.1
13 years, 10 months
[PATCH] Fixe issues with the 'select' command
by Josh Adams
Propogate selection status to parents of rules and groups, Automatically
switch to the 'Custom' profile if changes have been made. Also fxed issue
where the benchmark was always being selected or deslected.
---
src/bin/secstate | 10 ++++++----
src/secstate/main.py | 10 ++++++++++
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 8cd84f2..2473ae5 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -140,11 +140,13 @@ def select(arguments, value):
parser.add_option('-r', '--recurse', action='store_true', dest='recurse', default=False,
help="Recursively select or deselect rules inside of groups or benchmarks")
(options, args) = parser.parse_args(arguments)
- #if len(args) == 1:
- #if not sec_instance.select(args[1], None
- for arg in args:
- if (not (sec_instance.select(args[0], arg, value, recurse=options.recurse, message=options.message))):
+ if len(args) == 1:
+ if not sec_instance.select(args[0], args[0], value, recurse=options.recurse, message=options.message):
return -1
+ else:
+ for arg in args[1:]:
+ if (not (sec_instance.select(args[0], arg, value, recurse=options.recurse, message=options.message))):
+ return -1
def audit(arguments):
parser = OptionParser(usage="secstate audit [options] <benchmark>")
diff --git a/src/secstate/main.py b/src/secstate/main.py
index dcad413..fe87427 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -511,7 +511,17 @@ class Secstate:
bench_cfg.set('selections', item_id, json.dumps(sel_dict))
self.log.debug("Setting %(id)s to %(val)s" % {'id':item_id,
'val':selected})
+ if selected:
+ parent = oscap.xccdf_item_get_parent(item)
+ parent_id = oscap.xccdf_item_get_id(parent)
+ while parent_id != benchmark_id:
+ bench_cfg.set('selections', parent_id, json.dumps(sel_dict))
+ self.log.debug("Setting %(id)s to %(val)s" % {'id':parent_id,
+ 'val':selected})
+ parent = oscap.xccdf_item_get_parent(parent)
+ parent_id = oscap.xccdf_item_get_id(parent)
+ bench_cfg.set(benchmark_id, 'profile', 'Custom')
if recurse:
if (item_type == oscap.XCCDF_GROUP) or (item_type == oscap.XCCDF_BENCHMARK):
for sub in xccdf_get_items(benchmark, oscap.XCCDF_ITEM, oscap.xccdf_item_get_content(item)):
--
1.7.1.1
13 years, 10 months
[PATCH] Change selected profile to custom after change
by Josh Adams
If the user makes a change to the profile, automatically set the default
profile to "Custom". Also fixed an issue where the benchmark itself
would also be selected or deslected eveytime something was changed.
---
src/bin/secstate | 10 ++++++----
src/secstate/main.py | 1 +
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 8cd84f2..c7c5280 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -140,11 +140,13 @@ def select(arguments, value):
parser.add_option('-r', '--recurse', action='store_true', dest='recurse', default=False,
help="Recursively select or deselect rules inside of groups or benchmarks")
(options, args) = parser.parse_args(arguments)
- #if len(args) == 1:
- #if not sec_instance.select(args[1], None
- for arg in args:
- if (not (sec_instance.select(args[0], arg, value, recurse=options.recurse, message=options.message))):
+ if len(args) == 1:
+ if not sec_instance.select(args[0], args[0], value):
return -1
+ else:
+ for arg in args[1:]:
+ if (not (sec_instance.select(args[0], arg, value, recurse=options.recurse, message=options.message))):
+ return -1
def audit(arguments):
parser = OptionParser(usage="secstate audit [options] <benchmark>")
diff --git a/src/secstate/main.py b/src/secstate/main.py
index dcad413..5a8e7b9 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -512,6 +512,7 @@ class Secstate:
self.log.debug("Setting %(id)s to %(val)s" % {'id':item_id,
'val':selected})
+ bench_cfg.set(benchmark_id, 'profile', 'Custom')
if recurse:
if (item_type == oscap.XCCDF_GROUP) or (item_type == oscap.XCCDF_BENCHMARK):
for sub in xccdf_get_items(benchmark, oscap.XCCDF_ITEM, oscap.xccdf_item_get_content(item)):
--
1.7.1.1
13 years, 10 months
[PATCH] Added ability to export OVAL files
by Josh Adams
Also fixed a small issue when exporirting benchmarks.
---
src/secstate/main.py | 9 ++++++---
1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index dcad413..4816cc6 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -396,10 +396,14 @@ class Secstate:
self.log.error("No benchmark '%(id)s' has been imported" % {'id':benchmark_id})
return False
+ if os.path.split(self.content[benchmark_id])[0] == self.config.get('secstate', 'oval_dir'):
+ shutil.copy(self.content[benchmark_id], new_file)
+ return True
+
benchmark_file = None
archive = zipfile.ZipFile(new_file, 'w')
if original:
- benchmark_file = os.path.join(self.benchmark_dir, benchmark_id, self.content[benchmark_id])
+ benchmark_file = self.content[benchmark_id]
else:
benchmark_file = tempfile.mktemp()
(benchmark, oval) = self.import_content(benchmark_id)
@@ -408,8 +412,7 @@ class Secstate:
return False
oscap.xccdf_benchmark_free(benchmark)
oscap.oval_definition_model_free(oval)
-
- archive.write(benchmark_file, self.content[benchmark_id])
+ archive.write(benchmark_file, os.path.basename(self.content[benchmark_id]))
bench_dir = os.path.join(self.benchmark_dir, benchmark_id)
for content in os.listdir(bench_dir):
file_type = mimetypes.guess_type(os.path.join(bench_dir, content))
--
1.7.1.1
13 years, 10 months
[PATCH] Use our custom provider instead of the file class for the etc/passwd example
by mkeeler@tresys.com
From: Matt Keeler <mkeeler(a)tresys.com>
---
testing/data/EtcPasswdPerms/Passwd.xccdf.xml | 6 +++-
testing/data/PuppetManifests/passwd.pp | 46 +++++++++++++++++--------
2 files changed, 36 insertions(+), 16 deletions(-)
diff --git a/testing/data/EtcPasswdPerms/Passwd.xccdf.xml b/testing/data/EtcPasswdPerms/Passwd.xccdf.xml
index 03f5474..a22f509 100644
--- a/testing/data/EtcPasswdPerms/Passwd.xccdf.xml
+++ b/testing/data/EtcPasswdPerms/Passwd.xccdf.xml
@@ -99,7 +99,11 @@
</check>
<fix system="urn:xccdf:fix:script:puppet">
class : passwd
- parameter : etc_passwd_mode : 644
+ parameter : etc_passwd_gwrite : false
+ parameter : etc_passwd_owrite : false
+ parameter : etc_passwd_uexec : false
+ parameter : etc_passwd_gexec : false
+ parameter : etc_passwd_oexec : false
</fix>
</Rule>
</Benchmark>
diff --git a/testing/data/PuppetManifests/passwd.pp b/testing/data/PuppetManifests/passwd.pp
index 90eeb7b..7acca06 100644
--- a/testing/data/PuppetManifests/passwd.pp
+++ b/testing/data/PuppetManifests/passwd.pp
@@ -1,20 +1,36 @@
class passwd {
- file { '/etc/passwd' :
- mode => $etc_passwd_mode? {
- '' => undef,
- default => $etc_passwd_mode
- },
- owner => $etc_passwd_owner? {
- '' => undef,
- default => $etc_passwd_owner
- },
- group => $etc_passwd_group_owner? {
- '' => undef,
- default => $etc_passwd_group_owner
- }
- }
+ file_perms { '/etc/passwd' :
+ owrite => $etc_passwd_owrite? {
+ '' => undef,
+ default => $etc_passwd_owrite
+ },
+ gwrite => $etc_passwd_gwrite? {
+ '' => undef,
+ default => $etc_passwd_gwrite
+ },
+ uexec => $etc_passwd_uexec? {
+ '' => undef,
+ default => $etc_passwd_uexec
+ },
+ gexec =>$etc_passwd_gexec? {
+ '' => undef,
+ default => $etc_passwd_gexec
+ },
+ oexec =>$etc_passwd_oexec? {
+ '' => undef,
+ default => $etc_passwd_oexec
+ },
+ owner => $etc_passwd_owner? {
+ '' => undef,
+ default => $etc_passwd_owner
+ },
+ group => $etc_passwd_group_owner? {
+ '' => undef,
+ default => $etc_passwd_group_owner
+ }
+ }
}
-
+
class passwd_complexity {
pam {"pam_cracklib.so":
type => "password",
--
1.6.5.2
13 years, 10 months
[PATCH] Fixed a couple of bugs
by Josh Adams
import_content was trying to save everything, even when it shouldn't.
Also fixed a mistake when running list on a single benchmark.
---
src/bin/secstate | 2 +-
src/secstate/main.py | 7 ++++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index a7bf097..8cd84f2 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -111,7 +111,7 @@ def import_content(arguments):
(options, args) = parser.parse_args(arguments)
for arg in args:
- (benchmark, def_model) = sec_instance.import_content(arg, options.cpe, options.puppet)
+ (benchmark, def_model) = sec_instance.import_content(arg, options.cpe, options.puppet, save=True)
if (benchmark == None) and (def_model == None):
return -1
oscap.oval_definition_model_free(def_model)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index dfa12e9..e22172f 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -344,7 +344,7 @@ class Secstate:
return (benchmark, def_model)
- def import_content(self, content, cpe=False, puppet=False, changes=True, save=True):
+ def import_content(self, content, cpe=False, puppet=False, changes=True, save=False):
"""
Function: Validates XCCDF/OVAL content and optionally saves it to the data store
Input: File containing content
@@ -544,7 +544,7 @@ class Secstate:
for arg in args:
(benchmark, def_model) = self.import_content(arg)
if (benchmark == None) and (def_model == None):
- self.log.error("Error importing benchmark: %(bench)s" % {'bench':arg})
+ self.log.error("Error importing content: %(bench)s" % {'bench':arg})
return False
else:
@@ -804,7 +804,8 @@ class Secstate:
ret = self.sublist(benchmark, config, def_model, key, recurse, show_all)
else:
- ret = self.sublist(benchmark, config, def_model, arg, recurse, show_all)
+ if not self.content.has_key(arg):
+ ret = self.sublist(benchmark, config, def_model, arg, recurse, show_all)
oscap.xccdf_benchmark_free(benchmark)
oscap.oval_definition_model_free(def_model)
--
1.7.1.1
13 years, 10 months
[PATCH] Create empty profile if none exists
by Josh Adams
The audit function requires a profile to be present, even if it is just
an empty profile. This commit adds an empty "Custom" profile on import
if there are no existing profiles in the benchmark.
---
src/secstate/main.py | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index edca0f1..730258a 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -241,6 +241,12 @@ class Secstate:
shutil.rmtree(bench_dir)
return (None, None)
+ profiles = oscap.xccdf_benchmark_get_profiles(benchmark)
+ if not oscap.xccdf_profile_iterator_has_more(profiles):
+ profile = oscap.xccdf_profile_new()
+ oscap.xccdf_profile_set_id(profile, "Custom")
+ oscap.xccdf_benchmark_add_profile(benchmark, profile)
+
if store_path != None:
id = get_benchmark_id(benchmark_file)
directory = os.path.join(bench_dir, id)
--
1.7.1.1
13 years, 10 months
[PATCH] Do not remove all bencmharks on failed import
by Josh Adams
If an import of a benchmark fails, it will only delete the directory
with its id, not all benchmarks.
Fixes bug #7194
---
src/secstate/main.py | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index edca0f1..1a9aef4 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -215,7 +215,6 @@ class Secstate:
benchmark = oscap.xccdf_benchmark_import(benchmark_file)
if benchmark == None:
self.log.error("Error importing benchmark %(file)s" % {'file':benchmark_file})
- shutil.rmtree(bench_dir)
return (None, None)
def_model = oscap.oval_definition_model_new()
@@ -230,7 +229,6 @@ class Secstate:
oscap.oval_definition_model_free(def_model)
oscap.oval_definition_model_free(tmp)
self.log.error('Error combining definition models')
- shutil.rmtree(bench_dir)
return (None, None)
oscap.oval_definition_model_free(tmp)
@@ -238,12 +236,15 @@ class Secstate:
if not oscap.oval_definition_model_is_valid(def_model):
self.log.error("Definition model is invalid")
oscap.oval_definition_model_free(def_model)
- shutil.rmtree(bench_dir)
return (None, None)
if store_path != None:
id = get_benchmark_id(benchmark_file)
directory = os.path.join(bench_dir, id)
+ if os.path.isdir(directory):
+ self.log.error("A benchmark named %(id)s already exists: %(dir)s" % {'id':id, 'dir':directory})
+ return (None, None)
+
try:
os.mkdir(directory)
shutil.copy(benchmark_file, directory)
@@ -259,7 +260,7 @@ class Secstate:
shutil.copy(os.path.join(oval_path, oval), directory)
except (IOError, OSError), e:
self.log.error("Error importing content: %(error)s" % {'error':e})
- shutil.rmtree(bench_dir)
+ shutil.rmtree(directory)
return (None, None)
return (benchmark, def_model)
--
1.7.1.1
13 years, 10 months
[PATCH] Fix issue when configs directory does not exist.
by Josh Adams
Create /var/lib/secstate/configs on startup if it does not exist.
---
src/secstate/main.py | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index edca0f1..62fd3ca 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -39,6 +39,14 @@ from secstate.util import *
class Secstate:
def __init__(self, conf_file):
self.setConfigFile(conf_file)
+ self.conf_dir = self.config.get('secstate', 'conf_dir')
+ if not os.path.isdir(self.conf_dir):
+ try:
+ os.makedirs(self.conf_dir)
+ except IOError, e:
+ self.log.error("Could not create config directory: %(dir)s" % {'dir':self.conf_dir})
+ return (None, None)
+
self.content = self.get_content_dict()
self.content_configs = self.get_content_configs()
self.log = self.getLogger()
--
1.7.1.1
13 years, 10 months