[PATCH] Create /usr/share/secstate if nonexistant
by Josh Adams
Fixes bug #7035
---
src/secstate/main.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 1b22d67..d57baad 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -107,7 +107,7 @@ class Secstate:
bench_dir = self.benchmark_dir
if not os.path.isdir(bench_dir):
try:
- os.mkdir(bench_dir)
+ os.makedirs(bench_dir)
except IOError, e:
self.log.error("Could not create benchmark directory: %(dir)s" % {'dir':bench_dir})
return (None, None)
--
1.7.0.1
13 years, 10 months
[PATCH] Fix removal of non-existent content
by Josh Adams
Attempting to remove a benchmark that does not exist will not delete the
database file.
Fixes bug #7158
---
src/secstate/main.py | 22 ++++++++++------------
1 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 1b22d67..91c2937 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -300,12 +300,7 @@ class Secstate:
return True
def remove_content(self, benchmark_id):
- try:
- db_file = open(self.config.get('secstate', 'benchmark_database'), 'w')
- except IOError, e:
- self.log.error("Error writing changes to database: %(error)s" % {'error':e})
- return False
-
+
if benchmark_id == 'all':
for key in self.database:
try:
@@ -315,11 +310,8 @@ class Secstate:
return False
self.database.clear()
- pickle.dump(self.database, db_file)
- db_file.close()
- return True
- if self.database.has_key(benchmark_id):
+ elif self.database.has_key(benchmark_id):
try:
shutil.rmtree(os.path.join(self.benchmark_dir, benchmark_id))
except (IOError, OSError), e:
@@ -327,13 +319,19 @@ class Secstate:
return False
del self.database[benchmark_id]
- pickle.dump(self.database, db_file)
- return True
else:
self.log.error("Could not find %(benchmark)s in database" % {'benchmark':benchmark_id})
return False
+ try:
+ db_file = open(self.config.get('secstate', 'benchmark_database'), 'w')
+ except IOError, e:
+ self.log.error("Error writing changes to database: %(error)s" % {'error':e})
+ return False
+ pickle.dump(self.database, db_file)
+ return True
+
def select(self, benchmark_id, item_id, selected):
"""
Function: Set the specified item to be selected, as well as its subelements
--
1.7.0.1
13 years, 10 months
[PATCH 1/1] Add fix elements to the Password Complexity example.
by mkeeler@tresys.com
From: Matt Keeler <mkeeler(a)tresys.com>
Add a /etc/passwd owner,group owner, and mode example
Added a puppet manifest which contains remediation classes for both of the tests.
---
testing/data/EtcPasswdPerms/2-15Passwd.xml | 108 ++++++++++++++++++++
testing/data/EtcPasswdPerms/Passwd.xccdf.xml | 105 +++++++++++++++++++
testing/data/PasswordComplexity/PassComp.xccdf.xml | 20 ++++
testing/data/PuppetManifests/passwd.pp | 26 +++++
4 files changed, 259 insertions(+), 0 deletions(-)
create mode 100644 testing/data/EtcPasswdPerms/2-15Passwd.xml
create mode 100644 testing/data/EtcPasswdPerms/Passwd.xccdf.xml
create mode 100644 testing/data/PuppetManifests/passwd.pp
diff --git a/testing/data/EtcPasswdPerms/2-15Passwd.xml b/testing/data/EtcPasswdPerms/2-15Passwd.xml
new file mode 100644
index 0000000..8e9ceb3
--- /dev/null
+++ b/testing/data/EtcPasswdPerms/2-15Passwd.xml
@@ -0,0 +1,108 @@
+<oval_definitions xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5" xsi:schemaLocation="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix unix-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5#linux linux-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5#independent independent-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5 oval-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-common-5 oval-common-schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <generator>
+ <product_name xmlns="http://oval.mitre.org/XMLSchema/oval-common-5">squashed circle</product_name>
+ <product_version xmlns="http://oval.mitre.org/XMLSchema/oval-common-5">0.5</product_version>
+ <schema_version xmlns="http://oval.mitre.org/XMLSchema/oval-common-5">5.6</schema_version>
+ <timestamp xmlns="http://oval.mitre.org/XMLSchema/oval-common-5">2010-05-25T12:22:05</timestamp>
+ </generator>
+
+ <!-- OVAL Definitions Section -->
+ <definitions>
+ <definition class="compliance" id="oval:com.tresys.com.passwd:def:1000" version="1">
+ <metadata>
+ <title>/etc/passwd owner</title>
+ <affected family="unix">
+ <platform>Red Hat Enterprise Linux 5</platform>
+ </affected>
+ <reference ref_id="PasswdOwner" source="UNIX STIG" />
+ <description>Password File owner</description>
+ </metadata>
+ <criteria operator="AND">
+ <criterion test_ref="oval:com.tresys.oval.passwd:tst:1000" />
+ </criteria>
+ </definition>
+
+ <definition class="compliance" id="oval:com.tresys.com.passwd:def:1001" version="1">
+ <metadata>
+ <title>/etc/passwd group</title>
+ <affected family="unix">
+ <platform>Red Hat Enterprise Linux 5</platform>
+ </affected>
+ <reference ref_id="PasswdGroup" source="UNIX STIG" />
+ <description>Password File owner</description>
+ </metadata>
+ <criteria operator="AND">
+ <criterion test_ref="oval:com.tresys.oval.passwd:tst:1001" />
+ </criteria>
+ </definition>
+
+ <definition class="compliance" id="oval:com.tresys.com.passwd:def:1002" version="1">
+ <metadata>
+ <title>/etc/passwd mode</title>
+ <affected family="unix">
+ <platform>Red Hat Enterprise Linux 5</platform>
+ </affected>
+ <reference ref_id="PasswdMode" source="UNIX STIG" />
+ <description>Password File owner</description>
+ </metadata>
+ <criteria operator="AND">
+ <criterion test_ref="oval:com.tresys.oval.passwd:tst:1002" />
+ </criteria>
+ </definition>
+ </definitions>
+
+ <!-- OVAL Tests Section -->
+ <tests>
+ <file_test check="all" comment="check owner of /etc/passwd" id="oval:com.tresys.oval.passwd:tst:1000" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix">
+ <object object_ref="oval:com.tresys.oval.passwd.obj:1002" />
+ <state state_ref="oval:com.tresys.oval.passwd:ste:1000" />
+ </file_test>
+
+ <file_test check="all" comment="check owner of /etc/passwd" id="oval:com.tresys.oval.passwd:tst:1001" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix">
+ <object object_ref="oval:com.tresys.oval.passwd.obj:1002" />
+ <state state_ref="oval:com.tresys.oval.passwd:ste:1001" />
+ </file_test>
+
+ <file_test check="all" comment="check owner of /etc/passwd" id="oval:com.tresys.oval.passwd:tst:1002" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix">
+ <object object_ref="oval:com.tresys.oval.passwd.obj:1002" />
+ <state state_ref="oval:com.tresys.oval.passwd:ste:1002" />
+ </file_test>
+ </tests>
+
+ <!-- OVAL Objects Section -->
+ <objects>
+ <file_object comment="/etc/passwd" id="oval:com.tresys.oval.passwd:obj:1002" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix">
+ <filepath>/etc/passwd</filepath>
+ </file_object>
+ </objects>
+
+ <!-- OVAL States Section -->
+ <states>
+ <file_state id="oval:com.tresys.oval.passwd:ste:1000" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix">
+ <user_id operation="equals" var_ref="oval:com.tresys.oval.passwd:var:1004" />
+ </file_state>
+
+ <file_state id="oval:com.tresys.oval.passwd:ste:1001" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix">
+ <group_id operation="equals" var_ref="oval:com.tresys.oval.passwd:var:1005" />
+ </file_state>
+
+ <file_state id="oval:com.tresys.oval.passwd:ste:1002" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix">
+ <uexec operation="equals" var_ref="oval:com.tresys.oval.passwd:var:1006" />
+ <gwrite operation="equals" var_ref="oval:com.tresys.oval.passwd:var:1007" />
+ <gexec operation="equals" var_ref="oval:com.tresys.oval.passwd:var:1008" />
+ <owrite operation="equals" var_ref="oval:com.tresys.oval.passwd:var:1009" />
+ <oexec operation="equals" var_ref="oval:com.tresys.oval.passwd:var:1010" />
+ </file_state>
+ </states>
+
+ <!-- Oval Variables Section -->
+ <variables>
+ <external_variable comment="owner" datatype="string" id="oval:com.tresys.oval.passwd:var:1004" version="1" />
+ <external_variable comment="group" datatype="string" id="oval:com.tresys.oval.passwd:var:1005" version="1" />
+ <external_variable comment="uexec" datatype="string" id="oval:com.tresys.oval.passwd:var:1006" version="1" />
+ <external_variable comment="gwrite" datatype="string" id="oval:com.tresys.oval.passwd:var:1007" version="1" />
+ <external_variable comment="gexec" datatype="string" id="oval:com.tresys.oval.passwd:var:1008" version="1" />
+ <external_variable comment="owrite" datatype="string" id="oval:com.tresys.oval.passwd:var:1009" version="1" />
+ <external_variable comment="oexec" datatype="string" id="oval:com.tresys.oval.passwd:var:1010" version="1" />
+ </variables>
+</oval_definitions>
\ No newline at end of file
diff --git a/testing/data/EtcPasswdPerms/Passwd.xccdf.xml b/testing/data/EtcPasswdPerms/Passwd.xccdf.xml
new file mode 100644
index 0000000..72771a0
--- /dev/null
+++ b/testing/data/EtcPasswdPerms/Passwd.xccdf.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This XCCDF document was auto generated by the Recommendation Tracker. -->
+<Benchmark xmlns="http://checklists.nist.gov/xccdf/1.1"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cdf="http://checklists.nist.gov/xccdf/1.1"
+ xmlns:cpe="http://cpe.mitre.org/dictionary/2.0"
+ xmlns:dsig="http://w3.org/2000/09/xmldsig#"
+ xmlns:xhtml="http://www.w3.org/1999/xhtml"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://checklists.nist.gov/xccdf/1.1 http://nvd.nist.gov/schema/xccdf-1.1.4.xsd"
+ id="Passwd">
+<!-- Review the status value. -->
+<status date="2010-06-11">draft</status>
+ <title>PasswordFilePermissions</title>
+ <!-- Consider adding front-matter. -->
+
+<!-- Consider adding rear-matter. -->
+<!-- Consider adding a reference for the benchamrk. -->
+<!-- Consider adding application wide plafrom information. -->
+
+<!-- Review version information. -->
+<version>1.0</version>
+ <!-- Consider adding scoring model information. -->
+<Value id="Passwd-V-2-2" type="string" operator="equals">
+ <title>PasswdOwner</title>
+ <description/>
+ <value>root</value>
+ </Value>
+ <Value id="Passwd-V-2-4" type="string" operator="equals">
+ <title>PasswdGroup</title>
+ <description/>
+ <value>root</value>
+ </Value>
+ <Value id="Passwd-V-2-6" type="string" operator="equals">
+ <title>PasswdOwnerExec</title>
+ <description/>
+ <value>0</value>
+ </Value>
+ <Value id="Passwd-V-2-8" type="string" operator="equals">
+ <title>PasswdGroupWrite</title>
+ <description/>
+ <value>0</value>
+ </Value>
+ <Value id="Passwd-V-2-10" type="string" operator="equals">
+ <title>PasswdGroupExec</title>
+ <description/>
+ <value>0</value>
+ </Value>
+ <Value id="Passwd-V-2-12" type="string" operator="equals">
+ <title>PasswdOtherWrite</title>
+ <description/>
+ <value>0</value>
+ </Value>
+ <Value id="Passwd-V-2-14" type="string" operator="equals">
+ <title>PasswdOtherExec</title>
+ <description/>
+ <value>0</value>
+ </Value>
+ <Rule id="Passwd-R-2-1">
+ <title>Passwd_Ownership</title>
+ <description>/etc/passwd should be owned by root</description>
+ <rationale>Who Cares</rationale>
+ <fixtext>Change the owner to root</fixtext>
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
+ <check-export value-id="Passwd-V-2-2" export-name="oval:com.tresys.oval.passwd:var:1004"/>
+ <check-content-ref href="2-15Passwd.xml" name="oval:com.tresys.com.passwd:def:1000"/>
+ </check>
+ <fix system="urn:xccdf:fix:script:puppet">
+ class : passwd
+ parameter : etc_passwd_owner : root
+ </fix>
+ </Rule>
+ <Rule id="Passwd-R-2-2">
+ <title>Passwd_Group_Ownership</title>
+ <description>/etc/passwd should be owned by root</description>
+ <rationale>Who Cares</rationale>
+ <fixtext>Change the owner to root</fixtext>
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
+ <check-export value-id="Passwd-V-2-4" export-name="oval:com.tresys.oval.passwd:var:1005"/>
+ <check-content-ref href="2-15Passwd.xml" name="oval:com.tresys.com.passwd:def:1001"/>
+ </check>
+ <fix system="urn:xccdf:fix:script:puppet">
+ class : passwd
+ parameter : etc_passwd_group_owner : root
+ </fix>
+ </Rule>
+ <Rule id="Passwd-R-2-3">
+ <title>Passwd_Mode</title>
+ <description>/etc/passwd should be 644 or less</description>
+ <rationale>Who Cares</rationale>
+ <fixtext>Change the mode to 644</fixtext>
+ <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
+ <check-export value-id="Passwd-V-2-6" export-name="oval:com.tresys.oval.passwd:var:1006"/>
+ <check-export value-id="Passwd-V-2-8" export-name="oval:com.tresys.oval.passwd:var:1007"/>
+ <check-export value-id="Passwd-V-2-10" export-name="oval:com.tresys.oval.passwd:var:1008"/>
+ <check-export value-id="Passwd-V-2-12" export-name="oval:com.tresys.oval.passwd:var:1009"/>
+ <check-export value-id="Passwd-V-2-14" export-name="oval:com.tresys.oval.passwd:var:1010"/>
+ <check-content-ref href="2-15Passwd.xml" name="oval:com.tresys.com.passwd:def:1002"/>
+ </check>
+ <fix system="urn:xccdf:fix:script:puppet">
+ class : passwd
+ parameter : etc_passwd_mode : 644
+ </fix>
+ </Rule>
+</Benchmark>
diff --git a/testing/data/PasswordComplexity/PassComp.xccdf.xml b/testing/data/PasswordComplexity/PassComp.xccdf.xml
index ae2ea8b..6810723 100644
--- a/testing/data/PasswordComplexity/PassComp.xccdf.xml
+++ b/testing/data/PasswordComplexity/PassComp.xccdf.xml
@@ -37,6 +37,10 @@
<check-content-ref href="2-19PasswordComplexity_Lowercase.xml"
name="oval:com.tresys.oval.rhel:def:1000" />
</check>
+ <fix system="urn:xccdf:fix:script:puppet">
+ class : passwd_complexity
+ array : cracklib_args : lcredit=-2
+ </fix>
</Rule>
<Rule id="PassComp-R-2-2" selected="true" role="full">
<title>Min. Length</title>
@@ -48,6 +52,10 @@
<check-content-ref href="2-20PasswordComplexity_MinLen.xml"
name="oval:com.tresys.oval.rhel:def:1001" />
</check>
+ <fix system="urn:xccdf:fix:script:puppet">
+ class : passwd_complexity
+ array : cracklib_args : ucredit=-2
+ </fix>
</Rule>
<Rule id="PassComp-R-2-3">
<title>Numeric</title>
@@ -58,6 +66,10 @@
<check-content-ref href="2-21PasswordComplexity_Numeric.xml"
name="oval:com.tresys.oval.rhel:def:1002" />
</check>
+ <fix system="urn:xccdf:fix:script:puppet">
+ class : passwd_complexity
+ array : cracklib_args : dcredit=-1
+ </fix>
</Rule>
<Rule id="PassComp-R-2-4">
<title>Special</title>
@@ -68,6 +80,10 @@
<check-content-ref href="2-22PasswordComplexity_Special.xml"
name="oval:com.tresys.oval.rhel:def:1003" />
</check>
+ <fix system="urn:xccdf:fix:script:puppet">
+ class : passwd_complexity
+ array : cracklib_args : ocredit=-1
+ </fix>
</Rule>
<Rule id="PassComp-R-2-5">
<title>Uppercase</title>
@@ -78,6 +94,10 @@
<check-content-ref href="2-23PasswordComplexity_Uppercase.xml"
name="oval:com.tresys.oval.rhel:def:1004" />
</check>
+ <fix system="urn:xccdf:fix:script:puppet">
+ class : passwd_complexity
+ array : cracklib_args : ucredit=-2
+ </fix>
</Rule>
</Group>
</Group>
diff --git a/testing/data/PuppetManifests/passwd.pp b/testing/data/PuppetManifests/passwd.pp
new file mode 100644
index 0000000..90eeb7b
--- /dev/null
+++ b/testing/data/PuppetManifests/passwd.pp
@@ -0,0 +1,26 @@
+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
+ }
+ }
+}
+
+class passwd_complexity {
+ pam {"pam_cracklib.so":
+ type => "password",
+ control => "requisite",
+ module_args => $cracklib_args,
+ args_membership => minimum,
+ }
+}
+
--
1.6.5.2
13 years, 10 months
[PATCH 1/1] Add array support for puppet parameters. Array items can be split among several different fix elements. The get aggregated by secstate into a single array which is given to puppet as an external variable.
by mkeeler@tresys.com
From: Matt Keeler <mkeeler(a)tresys.com>
---
src/secstate/util.py | 15 +++++++++++++--
1 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 6856212..32eb63a 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -450,7 +450,7 @@ def parse_puppet_fixes(benchmark, ignore_ids=[]):
fixes = xccdf_get_fixes(benchmark, ignore_ids)
all_puppet = {'classes' : set(), 'environment' : "", 'parameters' : {}}
- line_reg = re.compile(r'\s*(class|environment|parameter)\s*:\s*((\S+)\s*:\s*(\S+)|\S+)\s*', re.IGNORECASE)
+ line_reg = re.compile(r'\s*(class|environment|parameter|array)\s*:\s*((\S+)\s*:\s*(\S+)|\S+)\s*', re.IGNORECASE)
for fix in fixes:
if oscap.xccdf_fix_get_system(fix) == 'urn:xccdf:fix:script:puppet':
@@ -471,6 +471,10 @@ def parse_puppet_fixes(benchmark, ignore_ids=[]):
#collision deteced - cant do this.
raise SecstateException('Puppet Variable Collision')
all_puppet['parameters'][mtch.group(3)] = mtch.group(4)
+ elif mtch.group(1).lower() == 'array':
+ if not all_puppet['parameters'].has_key(mtch.group(3)):
+ all_puppet['parameters'][mtch.group(3)] = set()
+ all_puppet['parameters'][mtch.group(3)].add(mtch.group(4))
else:
#assume comment line
pass
@@ -489,6 +493,13 @@ def dict_to_external(puppet_dict):
content.append('environment: %s' % environ)
content.append('parameters:')
for item in puppet_dict['parameters']:
- content.append(' %s: "%s"' % (item, puppet_dict['parameters'][item]))
+ value = puppet_dict['parameters'][item]
+ if isinstance(value, str):
+ content.append(' %s: %s' % (item, puppet_dict['parameters'][item]))
+ elif isinstance(value, set):
+ content.append(' %s:' % item)
+ for set_item in value:
+ content.append(' - "%s"' % set_item)
+
return '\n'.join(content)
--
1.6.5.2
13 years, 10 months
[PATCH 1/1] Change to use a new external nodes script. Add an external nodes script which cats the yaml file created by secstate. This is a work around so that puppet doesnt need to execute the temp file that we generate. The necessary changes have also been made to the make file and spec file.
by mkeeler@tresys.com
From: Matt Keeler <mkeeler(a)tresys.com>
---
Makefile | 1 +
dist/secstate.spec | 1 +
src/bin/secstate_external_node | 2 ++
src/secstate/main.py | 9 ++++-----
4 files changed, 8 insertions(+), 5 deletions(-)
create mode 100644 src/bin/secstate_external_node
diff --git a/Makefile b/Makefile
index 4413c7f..e0244bb 100644
--- a/Makefile
+++ b/Makefile
@@ -110,6 +110,7 @@ install:
$(verbose)test -d $(PYTHON_LIB) || $(INSTALL) -d $(PYTHON_LIB)
$(verbose)test -d $(PYTHON_LIB_SECSTATE) || $(INSTALL) -d $(PYTHON_LIB_SECSTATE)
$(verbose)$(INSTALL) src/bin/$(PKG) $(BINDIR)/$(PKG)
+ $(verbose)$(INSTALL) src/bin/secstate_external_node $(BINDIR)/secstate_external_node
$(verbose)$(INSTALL) src/secstate/*.py $(PYTHON_LIB_SECSTATE)
$(verbose)$(INSTALL) src/etc/$(PKG).conf $(SYSCONFDIR)/$(PKG)/$(PKG).conf
$(verbose)$(INSTALL) src/etc/results_to_html.xsl $(SYSCONFDIR)/$(PKG)/results_to_html.xsl
diff --git a/dist/secstate.spec b/dist/secstate.spec
index ab08d8b..e66afcc 100644
--- a/dist/secstate.spec
+++ b/dist/secstate.spec
@@ -47,6 +47,7 @@ rm -rf $RPM_BUILD_DIR/%{name}-%{version}
%dir %{_datadir}/secstate/
%dir %{_datadir}/secstate/benchmarks/
%{_bindir}/secstate
+%{_bindir}/secstate_external_node
%dir %{python_sitelib}/secstate
%{python_sitelib}/secstate/__init__.py*
diff --git a/src/bin/secstate_external_node b/src/bin/secstate_external_node
new file mode 100644
index 0000000..a331f9c
--- /dev/null
+++ b/src/bin/secstate_external_node
@@ -0,0 +1,2 @@
+#!/bin/bash
+cat $1
\ No newline at end of file
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 5543203..d16b973 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -29,7 +29,6 @@ from logging.handlers import SysLogHandler
import tarfile
import zipfile
import tempfile
-import re
import subprocess
import time
@@ -617,7 +616,7 @@ class Secstate:
self.log.error('Error: Invalid Benchmark ID or Puppet Library')
return False
passing_ids = self.get_passed_result_ids(xccdf_results)
- template = '#!/bin/sh\ncat <<"END"\n%s\nEND\nexit 0\n'
+ template = '%s\n'
if self.database.has_key(bench_id):
(benchmark, tmp_model) = self.import_content(os.path.join(self.benchmark_dir, bench_id, self.database[bench_id]))
if not benchmark:
@@ -629,11 +628,10 @@ class Secstate:
sys.stderr.write('Error: %s\n' % str(se))
return False
else:
- handle, fname = tempfile.mkstemp(suffix='.sh')
+ handle, fname = tempfile.mkstemp(suffix='.yaml')
os.write(handle, template % dict_to_external(puppet_content))
os.close(handle)
- os.chmod(fname, 755)
- puppet_args = ['/usr/bin/puppet', '--external_node', fname, '--node_terminus', 'exec', puppet_lib]
+ puppet_args = ['/usr/bin/puppet', '--external_node', '/usr/sbin/secstat_external_node %s' % fname, '--node_terminus', 'exec', puppet_lib]
if log_dest:
puppet_args.extend(['-l', log_dest])
subprocess.call(puppet_args)
@@ -641,3 +639,4 @@ class Secstate:
else:
self.log.error("Could not find %(benchmark)s in database" % {'benchmark':bench_id})
return False
+
--
1.6.5.2
13 years, 10 months
[PATCH] Changes to imported benchmarks stored separately
by Josh Adams
Changes that are made to imported benchmarks (currently just
selections) are now stored in config files in the same directory as the
benchmark. Also implemented an initial export command that will export
an imported benchmark and associated oval to a zipfile. Also added
'close' statements for all files that are opened.
Has a couple small fixes not in the last patch I sent.
Fixes bugs #7147 and #7148
---
src/bin/secstate | 12 +++++
src/secstate/main.py | 119 +++++++++++++++++++++++++++++++++----------------
src/secstate/util.py | 25 ++++++++++-
3 files changed, 116 insertions(+), 40 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 0c28ecc..0dfdbdb 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -70,6 +70,9 @@ def main():
if subcommand == 'import':
return import_content(sys.argv[arg_num:])
+ elif subcommand == 'export':
+ return export(sys.argv[arg_num:])
+
elif subcommand == 'remove':
return remove_content(sys.argv[arg_num:])
@@ -118,6 +121,15 @@ def import_content(arguments):
oscap.oval_definition_model_free(def_model)
oscap.xccdf_benchmark_free(benchmark)
+def export(arguments):
+ parser = OptionParser(usage="secstate export [options] <benchmark> <file>")
+ parser.add_option('-o', '--original', action='store_true', dest='original', default=False,
+ help="Exports the specified XCCDF content to the specified file")
+ (options, args) = parser.parse_args(arguments)
+ for arg in args[1:]:
+ if not sec_instance.export(args[0], arg, options.original):
+ return -1
+
def remove_content(arguments):
parser = OptionParser(usage="secstate remove [options] <content>")
(options, args) = parser.parse_args(arguments)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 5543203..1b22d67 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -32,6 +32,7 @@ import tempfile
import re
import subprocess
import time
+import mimetypes
import openscap as oscap
from secstate.util import *
@@ -46,11 +47,13 @@ class Secstate:
def setConfigFile(self, conf):
config = ConfigParser.ConfigParser()
try:
- config.readfp(open(conf))
+ fp = open(conf)
+ config.readfp(fp)
except IOError,e:
self.log.error("Could not open config file: %(error)s" % {'error':e})
return None
self.config = config
+ fp.close()
def isDebugging(self):
return self.config.getint('logging', 'debugging') > 0
@@ -84,8 +87,9 @@ class Secstate:
db = open(db_file)
database = pickle.load(db)
except IOError,e:
- self.log.error("Error loading database: %(file)s" % {'file':db_file})
- return None
+ self.log.error("Error loading database: %(file)s" % {'file':db_file})
+ return None
+ db.close()
return database
else:
@@ -160,6 +164,7 @@ class Secstate:
return (None, None)
pickle.dump(self.database, db_file)
+ db_file.close()
return (benchmark, def_model)
def import_zipped_content(self, zip, type, store_path, puppet):
@@ -191,6 +196,8 @@ class Secstate:
for member in tar_file.getmembers():
tar_file.extract(member, extract_path)
+ tar_file.close()
+
elif type[0] == "application/zip":
if sys.version_info < (2, 6):
self.log.error("Zip file support requires Python >= 2.6")
@@ -199,6 +206,8 @@ class Secstate:
zip_files = zipfile.ZipFile(zip, 'r')
zip_files.extractall(extract_path)
+ zip_files.close()
+
else:
self.log.error("Unsupported file type: %(content)s" % {'content':zip})
return (None, None)
@@ -224,13 +233,19 @@ class Secstate:
return (benchmark, def_model)
- def import_content(self, content, store_path=None, oval=False, xccdf=False, cpe=False, puppet=False):
+ def import_content(self, content, store_path=None, oval=False, xccdf=False, cpe=False, puppet=False, changes=True):
"""
Function: Validates XCCDF/OVAL content and optionally saves it to the data store
Input: File containing content
Output: Returns a tuple containing a validated benchmar and definition model
"""
- import mimetypes
+ if self.database.has_key(content):
+ (benchmark, oval) = self.import_benchmark(os.path.join(self.benchmark_dir, content, self.database[content]), oval_path=os.path.join(self.benchmark_dir, content))
+ if changes and (benchmark != None):
+ benchmark = apply_changes(benchmark, os.path.join(self.benchmark_dir, content, str(content + ".cfg")))
+
+ return (benchmark, oval)
+
file_type = mimetypes.guess_type(content)
if file_type[0] == "text/xml":
if not (oval or xccdf):
@@ -256,6 +271,34 @@ class Secstate:
else:
return self.import_zipped_content(content, file_type, store_path, puppet)
+ def export(self, benchmark_id, new_file, original=False):
+ if not self.database.has_key(benchmark_id):
+ self.log.error("No benchmark '%(id)s' has been imported" % {'id':benchmark_id})
+ return False
+
+ benchmark_file = None
+ archive = zipfile.ZipFile(new_file, 'w')
+ if original:
+ benchmark_file = os.path.join(self.benchmark_dir, benchmark_id, self.database[benchmark_id])
+ else:
+ benchmark_file = tempfile.mktemp()
+ (benchmark, oval) = self.import_content(benchmark_id)
+ if oscap.xccdf_benchmark_export(benchmark, benchmark_file) == None:
+ self.log.error("Error exporting benchmark to %(file)s" % {'file':new_file})
+ return False
+ oscap.xccdf_benchmark_free(benchmark)
+ oscap.oval_definition_model_free(oval)
+
+ archive.write(benchmark_file, self.database[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))
+ if (file_type[0] == "text/xml") and (not is_benchmark(os.path.join(bench_dir, content))):
+ archive.write(os.path.join(bench_dir, content), content)
+
+ archive.close()
+ return True
+
def remove_content(self, benchmark_id):
try:
db_file = open(self.config.get('secstate', 'benchmark_database'), 'w')
@@ -273,6 +316,7 @@ class Secstate:
self.database.clear()
pickle.dump(self.database, db_file)
+ db_file.close()
return True
if self.database.has_key(benchmark_id):
@@ -296,6 +340,21 @@ class Secstate:
Input: Benchmark id, id of rule or group, boolean value to set the items selected status
Output: Succes or failure
"""
+ bench_cfg = ConfigParser.ConfigParser()
+ bench_cfg.optionxform = str
+ conf_path = os.path.join(self.benchmark_dir, benchmark_id, str(benchmark_id + ".cfg"))
+ if os.path.isfile(conf_path):
+ try:
+ fp = open(conf_path)
+ bench_cfg.readfp(fp)
+ except IOError, e:
+ self.log.error("Could not open config file: %(error)s" % {'error':e})
+ return False
+ fp.close()
+
+ if not bench_cfg.has_section('selections'):
+ bench_cfg.add_section('selections')
+
if not self.database.has_key(benchmark_id):
self.log.error("No benchmark %(id)s in datastore" % {'id':benchmark_id})
return False
@@ -308,14 +367,10 @@ class Secstate:
return False
if item_id == 'all':
- for item in xccdf_get_items(benchmark, oscap.XCCDF_GROUP):
- oscap.xccdf_group_set_selected(item, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(item),
- 'val':selected})
-
- for item in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
- oscap.xccdf_rule_set_selected(item, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_rule_get_id(item),
+
+ for item in xccdf_get_items(benchmark, oscap.XCCDF_ITEM):
+ bench_cfg.set('selections', oscap.xccdf_item_get_id(item), selected)
+ self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_item_get_id(item),
'val':selected})
else:
@@ -325,26 +380,20 @@ class Secstate:
'item_id':item_id})
return False
- oscap.xccdf_item_set_selected(item, selected)
+ bench_cfg.set('selections', item_id, selected)
+
self.log.debug("Setting %(id)s to %(val)s" % {'id':item_id,
'val':selected})
if oscap.xccdf_item_get_type(item) == oscap.XCCDF_GROUP:
- root_group = oscap.xccdf_item_to_group(item)
- for group in xccdf_get_items(benchmark, oscap.XCCDF_GROUP, oscap.xccdf_group_get_content(root_group)):
- oscap.xccdf_group_set_selected(group, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(group),
+ for sub in xccdf_get_items(benchmark, oscap.XCCDF_ITEM, oscap.xccdf_item_get_content(item)):
+ bench_cfg.set('selections', oscap.xccdf_item_get_id(sub), selected)
+ self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_item_get_id(sub),
'val':selected})
- for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE, oscap.xccdf_group_get_content(root_group)):
- oscap.xccdf_rule_set_selected(rule, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(group),
- 'val':selected})
-
- if not oscap.xccdf_benchmark_export(benchmark, benchmark_file):
- self.log.error("Error writing changes to file: %(file)s" % {'file':benchmark_file})
- return False
-
+ fp = open(conf_path, 'w')
+ bench_cfg.write(fp)
+ fp.close()
oscap.xccdf_benchmark_free(benchmark)
return True
@@ -397,14 +446,7 @@ class Secstate:
else:
for arg in args:
- benchmark_path = ""
- if self.database.has_key(arg):
- benchmark_path = os.path.join(self.benchmark_dir, arg, self.database[arg])
-
- elif os.path.isfile(arg):
- benchmark_path = arg
-
- (benchmark, def_model) = self.import_content(benchmark_path)
+ (benchmark, def_model) = self.import_content(arg)
if (benchmark == None) or (def_model == None):
self.log.error("Error importing benchmark: %(bench)s" % {'bench':arg})
return False
@@ -471,7 +513,7 @@ class Secstate:
def show(self, item_id, verbose=False):
for key in self.database:
- (benchmark, def_model) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
+ (benchmark, def_model) = self.import_content(key)
if (benchmark == None) or (def_model == None):
self.log.error("Error importing content: %(file)s" % {'flie':key})
return None
@@ -573,8 +615,7 @@ class Secstate:
def list_content(self, arg=None, recurse=False, show_all=False):
ret = False
for key in self.database:
-
- (benchmark, def_model) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
+ (benchmark, def_model) = self.import_content(key)
if benchmark == None:
self.log.error("Error loading benchmark: %(id)s" % {'id':key})
return False
@@ -619,7 +660,7 @@ class Secstate:
passing_ids = self.get_passed_result_ids(xccdf_results)
template = '#!/bin/sh\ncat <<"END"\n%s\nEND\nexit 0\n'
if self.database.has_key(bench_id):
- (benchmark, tmp_model) = self.import_content(os.path.join(self.benchmark_dir, bench_id, self.database[bench_id]))
+ (benchmark, tmp_model) = self.import_content(bench_id)
if not benchmark:
self.log.error("Benchmark was None")
return False
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 6856212..1da13bd 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -19,10 +19,12 @@
# File: util.py
# This file is the core implementation of the secstate tool.
+import os
import sys
import xml.dom.minidom
import time
import re
+import ConfigParser
import openscap as oscap
@@ -387,7 +389,11 @@ def xccdf_get_items(template, type, items=None):
result.extend(xccdf_get_items(template, type, item))
if type == oscap.XCCDF_ITEM:
- result.extend(xccdf_get_items(template, type, item))
+ if (item_type == oscap.XCCDF_GROUP) or (item_type == oscap.XCCDF_BENCHMARK):
+ result.append(item)
+ result.extend(xccdf_get_items(template, type, oscap.xccdf_item_get_content(item)))
+ else:
+ result.append(item)
return result
@@ -415,6 +421,23 @@ def xccdf_rule_get_defs(rule):
return defs
+def apply_changes(benchmark, conf):
+ config = ConfigParser.ConfigParser()
+ if os.path.isfile(conf):
+ try:
+ fp = open(conf)
+ config.readfp(fp)
+ except IOError,e:
+ sys.stderr.write("Error opening config file: %(err)s\n" % {'err':e})
+ return None
+ fp.close()
+
+ for id in config.options("selections"):
+ item = oscap.xccdf_benchmark_get_item(benchmark, id)
+ oscap.xccdf_item_set_selected(item, config.getboolean('selections', id))
+
+ return benchmark
+
def xccdf_get_fixes(benchmark, ignore_ids=[]):
"""
Function: Get all fixes for rules in the XCCDF document
--
1.7.0.1
13 years, 10 months
[PATCH] Changes to imported benchmarks stored separately
by Josh Adams
Changes that are made to imported benchmarks (currently just
selections) are now stored in config files in the same directory as the
benchmark. Also implemented an initial export command that will export
an imported benchmark and associated oval to a zipfile. Also added
'close' statements for all files that are opened.
Fixes bugs #7147 and #7148
---
src/bin/secstate | 12 +++++
src/secstate/main.py | 118 +++++++++++++++++++++++++++++++++----------------
src/secstate/util.py | 24 ++++++++++-
3 files changed, 114 insertions(+), 40 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 0c28ecc..0dfdbdb 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -70,6 +70,9 @@ def main():
if subcommand == 'import':
return import_content(sys.argv[arg_num:])
+ elif subcommand == 'export':
+ return export(sys.argv[arg_num:])
+
elif subcommand == 'remove':
return remove_content(sys.argv[arg_num:])
@@ -118,6 +121,15 @@ def import_content(arguments):
oscap.oval_definition_model_free(def_model)
oscap.xccdf_benchmark_free(benchmark)
+def export(arguments):
+ parser = OptionParser(usage="secstate export [options] <benchmark> <file>")
+ parser.add_option('-o', '--original', action='store_true', dest='original', default=False,
+ help="Exports the specified XCCDF content to the specified file")
+ (options, args) = parser.parse_args(arguments)
+ for arg in args[1:]:
+ if not sec_instance.export(args[0], arg, options.original):
+ return -1
+
def remove_content(arguments):
parser = OptionParser(usage="secstate remove [options] <content>")
(options, args) = parser.parse_args(arguments)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index 5543203..6c568f8 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -32,6 +32,7 @@ import tempfile
import re
import subprocess
import time
+import mimetypes
import openscap as oscap
from secstate.util import *
@@ -46,11 +47,13 @@ class Secstate:
def setConfigFile(self, conf):
config = ConfigParser.ConfigParser()
try:
- config.readfp(open(conf))
+ fp = open(conf)
+ config.readfp(fp)
except IOError,e:
self.log.error("Could not open config file: %(error)s" % {'error':e})
return None
self.config = config
+ fp.close()
def isDebugging(self):
return self.config.getint('logging', 'debugging') > 0
@@ -84,8 +87,9 @@ class Secstate:
db = open(db_file)
database = pickle.load(db)
except IOError,e:
- self.log.error("Error loading database: %(file)s" % {'file':db_file})
- return None
+ self.log.error("Error loading database: %(file)s" % {'file':db_file})
+ return None
+ db.close()
return database
else:
@@ -160,6 +164,7 @@ class Secstate:
return (None, None)
pickle.dump(self.database, db_file)
+ db_file.close()
return (benchmark, def_model)
def import_zipped_content(self, zip, type, store_path, puppet):
@@ -191,6 +196,8 @@ class Secstate:
for member in tar_file.getmembers():
tar_file.extract(member, extract_path)
+ tar_file.close()
+
elif type[0] == "application/zip":
if sys.version_info < (2, 6):
self.log.error("Zip file support requires Python >= 2.6")
@@ -199,6 +206,8 @@ class Secstate:
zip_files = zipfile.ZipFile(zip, 'r')
zip_files.extractall(extract_path)
+ zip_files.close()
+
else:
self.log.error("Unsupported file type: %(content)s" % {'content':zip})
return (None, None)
@@ -224,13 +233,19 @@ class Secstate:
return (benchmark, def_model)
- def import_content(self, content, store_path=None, oval=False, xccdf=False, cpe=False, puppet=False):
+ def import_content(self, content, store_path=None, oval=False, xccdf=False, cpe=False, puppet=False, changes=True):
"""
Function: Validates XCCDF/OVAL content and optionally saves it to the data store
Input: File containing content
Output: Returns a tuple containing a validated benchmar and definition model
"""
- import mimetypes
+ if self.database.has_key(content):
+ (benchmark, oval) = self.import_benchmark(os.path.join(self.benchmark_dir, content, self.database[content]), oval_path=os.path.join(self.benchmark_dir, content))
+ if changes and (benchmark != None):
+ benchmark = apply_changes(benchmark, os.path.join(self.benchmark_dir, content, str(content + ".cfg")))
+
+ return (benchmark, oval)
+
file_type = mimetypes.guess_type(content)
if file_type[0] == "text/xml":
if not (oval or xccdf):
@@ -256,6 +271,34 @@ class Secstate:
else:
return self.import_zipped_content(content, file_type, store_path, puppet)
+ def export(self, benchmark_id, new_file, original=False):
+ if not self.database.has_key(benchmark_id):
+ self.log.error("No benchmark '%(id)s' has been imported" % {'id':benchmark_id})
+ return False
+
+ benchmark_file = None
+ archive = zipfile.ZipFile(new_file, 'w')
+ if original:
+ benchmark_file = os.path.join(self.benchmark_dir, benchmark_id, self.database[benchmark_id])
+ else:
+ benchmark_file = tempfile.mktemp()
+ (benchmark, oval) = self.import_content(benchmark_id)
+ if oscap.xccdf_benchmark_export(benchmark, benchmark_file) == None:
+ self.log.error("Error exporting benchmark to %(file)s" % {'file':new_file})
+ return False
+ oscap.xccdf_benchmark_free(benchmark)
+ oscap.oval_definition_model_free(oval)
+
+ archive.write(benchmark_file, self.database[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))
+ if (file_type[0] == "text/xml") and (not is_benchmark(os.path.join(bench_dir, content))):
+ archive.write(os.path.join(bench_dir, content), content)
+
+ archive.close()
+ return True
+
def remove_content(self, benchmark_id):
try:
db_file = open(self.config.get('secstate', 'benchmark_database'), 'w')
@@ -273,6 +316,7 @@ class Secstate:
self.database.clear()
pickle.dump(self.database, db_file)
+ db_file.close()
return True
if self.database.has_key(benchmark_id):
@@ -296,6 +340,20 @@ class Secstate:
Input: Benchmark id, id of rule or group, boolean value to set the items selected status
Output: Succes or failure
"""
+ bench_cfg = ConfigParser.ConfigParser()
+ conf_path = os.path.join(self.benchmark_dir, benchmark_id, str(benchmark_id + ".cfg"))
+ if os.path.isfile(conf_path):
+ try:
+ fp = open(conf_path)
+ bench_cfg.readfp(fp)
+ except IOError, e:
+ self.log.error("Could not open config file: %(error)s" % {'error':e})
+ return False
+ fp.close()
+
+ if not bench_cfg.has_section('selections'):
+ bench_cfg.add_section('selections')
+
if not self.database.has_key(benchmark_id):
self.log.error("No benchmark %(id)s in datastore" % {'id':benchmark_id})
return False
@@ -308,14 +366,10 @@ class Secstate:
return False
if item_id == 'all':
- for item in xccdf_get_items(benchmark, oscap.XCCDF_GROUP):
- oscap.xccdf_group_set_selected(item, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(item),
- 'val':selected})
-
- for item in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
- oscap.xccdf_rule_set_selected(item, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_rule_get_id(item),
+
+ for item in xccdf_get_items(benchmark, oscap.XCCDF_ITEM):
+ bench_cfg.set('selections', oscap.xccdf_item_get_id(item), selected)
+ self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_item_get_id(item),
'val':selected})
else:
@@ -325,26 +379,20 @@ class Secstate:
'item_id':item_id})
return False
- oscap.xccdf_item_set_selected(item, selected)
+ bench_cfg.set('selections', item_id, selected)
+
self.log.debug("Setting %(id)s to %(val)s" % {'id':item_id,
'val':selected})
if oscap.xccdf_item_get_type(item) == oscap.XCCDF_GROUP:
- root_group = oscap.xccdf_item_to_group(item)
- for group in xccdf_get_items(benchmark, oscap.XCCDF_GROUP, oscap.xccdf_group_get_content(root_group)):
- oscap.xccdf_group_set_selected(group, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(group),
+ for sub in xccdf_get_items(benchmark, oscap.XCCDF_ITEM, oscap.xccdf_item_get_content(item)):
+ bench_cfg.set('selections', oscap.xccdf_item_get_id(sub), selected)
+ self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_item_get_id(sub),
'val':selected})
- for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE, oscap.xccdf_group_get_content(root_group)):
- oscap.xccdf_rule_set_selected(rule, selected)
- self.log.debug("Setting %(id)s to %(val)s" % {'id':oscap.xccdf_group_get_id(group),
- 'val':selected})
-
- if not oscap.xccdf_benchmark_export(benchmark, benchmark_file):
- self.log.error("Error writing changes to file: %(file)s" % {'file':benchmark_file})
- return False
-
+ fp = open(conf_path, 'w')
+ bench_cfg.write(fp)
+ fp.close()
oscap.xccdf_benchmark_free(benchmark)
return True
@@ -397,14 +445,7 @@ class Secstate:
else:
for arg in args:
- benchmark_path = ""
- if self.database.has_key(arg):
- benchmark_path = os.path.join(self.benchmark_dir, arg, self.database[arg])
-
- elif os.path.isfile(arg):
- benchmark_path = arg
-
- (benchmark, def_model) = self.import_content(benchmark_path)
+ (benchmark, def_model) = self.import_content(arg)
if (benchmark == None) or (def_model == None):
self.log.error("Error importing benchmark: %(bench)s" % {'bench':arg})
return False
@@ -471,7 +512,7 @@ class Secstate:
def show(self, item_id, verbose=False):
for key in self.database:
- (benchmark, def_model) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
+ (benchmark, def_model) = self.import_content(key)
if (benchmark == None) or (def_model == None):
self.log.error("Error importing content: %(file)s" % {'flie':key})
return None
@@ -573,8 +614,7 @@ class Secstate:
def list_content(self, arg=None, recurse=False, show_all=False):
ret = False
for key in self.database:
-
- (benchmark, def_model) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
+ (benchmark, def_model) = self.import_content(key)
if benchmark == None:
self.log.error("Error loading benchmark: %(id)s" % {'id':key})
return False
@@ -619,7 +659,7 @@ class Secstate:
passing_ids = self.get_passed_result_ids(xccdf_results)
template = '#!/bin/sh\ncat <<"END"\n%s\nEND\nexit 0\n'
if self.database.has_key(bench_id):
- (benchmark, tmp_model) = self.import_content(os.path.join(self.benchmark_dir, bench_id, self.database[bench_id]))
+ (benchmark, tmp_model) = self.import_content(bench_id)
if not benchmark:
self.log.error("Benchmark was None")
return False
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 6856212..3877b91 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -23,6 +23,7 @@ import sys
import xml.dom.minidom
import time
import re
+import ConfigParser
import openscap as oscap
@@ -387,7 +388,11 @@ def xccdf_get_items(template, type, items=None):
result.extend(xccdf_get_items(template, type, item))
if type == oscap.XCCDF_ITEM:
- result.extend(xccdf_get_items(template, type, item))
+ if (item_type == oscap.XCCDF_GROUP) or (item_type == oscap.XCCDF_BENCHMARK):
+ result.append(item)
+ result.extend(xccdf_get_items(template, type, oscap.xccdf_item_get_content(item)))
+ else:
+ result.append(item)
return result
@@ -415,6 +420,23 @@ def xccdf_rule_get_defs(rule):
return defs
+def apply_changes(benchmark, conf):
+ config = ConfigParser.ConfigParser()
+ if os.path.isfile(conf):
+ try:
+ fp = open(conf)
+ config.readfp(fp)
+ except IOError,e:
+ sys.stderr.write("Error opening config file: %(err)s\n" % {'err':e})
+ return None
+ fp.close()
+
+ for id in config.options("selections"):
+ item = oscap.xccdf_benchmark_get_item(benchmark, id)
+ oscap.xccdf_item_set_selected(item, config.getboolean('selections', id))
+
+ return benchmark
+
def xccdf_get_fixes(benchmark, ignore_ids=[]):
"""
Function: Get all fixes for rules in the XCCDF document
--
1.7.0.1
13 years, 10 months
[PATCH 1/2] Remove directories that were created on failed import
by Josh Adams
Fixes bug 7151
---
src/secstate/main.py | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/src/secstate/main.py b/src/secstate/main.py
index c3b84d7..4771b7c 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -109,6 +109,7 @@ 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()
@@ -123,6 +124,7 @@ 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)
@@ -130,6 +132,7 @@ 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:
@@ -140,6 +143,7 @@ class Secstate:
db_file = open(self.config.get('secstate', 'benchmark_database'), 'w')
except IOError, e:
self.log.error("Could not update database: %(error)s" % {'error':e})
+ shutil.rmtree(bench_dir)
return (None, None)
pickle.dump(self.database, db_file)
@@ -151,6 +155,7 @@ class Secstate:
shutil.copy(oval, directory)
except (IOError, OSError), e:
self.log.error("Error importing content: %(error)s" % {'error':e})
+ shutil.rmtree(bench_dir)
return (None, None)
return (benchmark, def_model)
--
1.7.0.1
13 years, 10 months
[PATCH] Updates the list command to meet Marshall's spec
by Josh Adams
Gives much more detailed output than before and can be recursive.
Fixes bug #7146
---
src/bin/secstate | 6 ++-
src/secstate/main.py | 93 +++++++++++++++++++++++++++++---------------------
src/secstate/util.py | 17 +++++++++
3 files changed, 75 insertions(+), 41 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 8e379cb..0c28ecc 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -203,14 +203,16 @@ def list_content(arguments):
parser = OptionParser(usage="secstate list [options] <string>")
parser.add_option('-r', '--recurse', action='store_true', dest='recurse', default=False,
help="Recurse through an items content")
+ parser.add_option('-a', '--all', action='store_true', dest='all', default=False,
+ help="Show items regardless of selection status")
(options, args) = parser.parse_args(arguments)
if args == []:
- if not sec_instance.list_content(recurse=options.recurse):
+ if not sec_instance.list_content(recurse=options.recurse, show_all=options.all):
return -1
else:
for arg in args:
- if not sec_instance.list_content(arg, options.recurse):
+ if not sec_instance.list_content(arg, options.recurse, options.all):
return -1
return 0
diff --git a/src/secstate/main.py b/src/secstate/main.py
index c3b84d7..fa85059 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -437,7 +437,7 @@ class Secstate:
if (title != None) and (description != None):
if (search_string in title) or (search_string in description):
print "\t%(id)s:" % {'id':id}
- print "\t\tTitle: %(title)s" % {'title':title}
+ print "\t\tTitle: '%(title)s'" % {'title':title}
print "\t\tDescription: %(description)s\n" % {'description':description}
for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
@@ -456,7 +456,7 @@ class Secstate:
if (title != None) and (description != None):
if (search_string in title) or (search_string in description):
print "\t%(id)s:" % {'id':id}
- print "\t\tTitle: %(title)s" % {'title':title}
+ print "\t\tTitle: '%(title)s'" % {'title':title}
print "\t\tDescription: %(description)s\n" % {'description':description}
oscap.xccdf_benchmark_free(benchmark)
@@ -478,7 +478,7 @@ class Secstate:
break
else:
print "%(id)s:" % {'id':oscap.oval_definition_get_id(item)}
- print "\t%(title)s" % {'title':oscap.oval_definition_get_title(defn)}
+ print "\t'%(title)s'" % {'title':oscap.oval_definition_get_title(defn)}
print "\t%(desc)s" % {'desc':oscap.oval_definition_get_description(defn)}
oscap.xccdf_benchmark_free(benchmark)
oscap.oval_definition_model_free(def_model)
@@ -488,7 +488,7 @@ class Secstate:
print "%(id)s:" % {'id':oscap.xccdf_item_get_id(item)}
titles = oscap.xccdf_item_get_title(item)
for title in oscap_text_generator(titles):
- print "\tTitle: %(title)s" % {'title':oscap.oscap_text_get_text(title)}
+ print "\tTitle: '%(title)s'" % {'title':oscap.oscap_text_get_text(title)}
descriptions = oscap.xccdf_item_get_description(item)
for description in oscap_text_generator(descriptions):
@@ -523,50 +523,65 @@ class Secstate:
oscap.oval_definition_model_free(def_model)
return True
- def list_content(self, arg=None, recurse=False):
- for key in self.database:
- (benchmark, oval) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
- if benchmark == None:
- self.log.error("Error loading benchmark: %(id)s" % {'id':key})
- return False
+ def sublist(self, benchmark, def_model, arg, recurse, show_all, tabs=0):
+ tabstr = "\t" * tabs
+ selected = ""
+ item = None
+ if arg == oscap.xccdf_benchmark_get_id(benchmark):
+ item = oscap.xccdf_benchmark_to_item(benchmark)
+ else:
+ item = oscap.xccdf_benchmark_get_item(benchmark, arg)
- if arg == None:
- titles = oscap.xccdf_benchmark_get_title(benchmark)
- for title in oscap_text_generator(titles):
- print "%(id)s: %(title)s" % {'id':key, 'title':oscap.oscap_text_get_text(title)}
+ if item == None:
+ defn = oscap.oval_definition_model_get_definition(def_model, arg)
+ if defn == None:
+ return
+ print "%(indent)sDefinition - ID: %(id)s, Title: '%(title)s'" % {'indent':tabstr, 'id':arg,
+ 'title':oscap.oval_definition_get_title(defn)}
- if recurse:
- content = oscap.xccdf_benchmark_get_content(benchmark)
+ else:
+ titles = oscap.xccdf_item_get_title(item)
+ for title in oscap_text_generator(titles):
+ if show_all:
+ if oscap.xccdf_item_get_selected(item):
+ selected = "[X]"
+ else:
+ selected = "[ ]"
+
+ if not oscap.xccdf_item_get_selected(item):
+ 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 recurse:
+ type = oscap.xccdf_item_get_type(item)
+ if (type == oscap.XCCDF_GROUP) or (type == oscap.XCCDF_BENCHMARK):
+ content = oscap.xccdf_item_get_content(item)
for sub in xccdf_item_generator(content):
- titles = oscap.xccdf_item_get_title(sub)
- for title in oscap_text_generator(titles):
- print "\t%(id)s:\t%(title)s" % {'id':oscap.xccdf_item_get_id(sub),
- 'title':oscap.oscap_text_get_text(title)}
+ self.sublist(benchmark, def_model, oscap.xccdf_item_get_id(sub), recurse, show_all, tabs+1)
- else:
- item = oscap.xccdf_benchmark_get_item(benchmark, arg)
- if item == None:
- pass
- titles = oscap.xccdf_item_get_title(item)
- for title in oscap_text_generator(titles):
- print "%(id)s: %(title)s" % {'id':arg, 'title':oscap.oscap_text_get_text(title)}
+ def list_content(self, arg=None, recurse=False, show_all=False):
+ ret = False
+ for key in self.database:
- if recurse:
- type = oscap.xccdf_item_get_type(item)
- if type == oscap.XCCDF_GROUP:
- content = oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))
- for sub in xccdf_item_generator(content):
- titles = oscap.xccdf_item_get_title(sub)
- for title in oscap_text_generator(titles):
- print "\t%(id)s:\t%(title)s" % {'id':oscap.xccdf_item_get_id(sub),
- 'title':oscap.oscap_text_get_text(title)}
-
+ (benchmark, def_model) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
+ if benchmark == None:
+ self.log.error("Error loading benchmark: %(id)s" % {'id':key})
+ return False
+
+ if (arg == None) or (arg == key):
+ ret = self.sublist(benchmark, def_model, key, recurse, show_all)
+
+ else:
+ ret = self.sublist(benchmark, def_model, arg, recurse, show_all)
oscap.xccdf_benchmark_free(benchmark)
- oscap.oval_definition_model_free(oval)
+ oscap.oval_definition_model_free(def_model)
- return True
+ return ret
def get_passed_result_ids(self, xccdf_results):
if xccdf_results == None:
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 5e84a4a..6856212 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -429,6 +429,23 @@ def xccdf_get_fixes(benchmark, ignore_ids=[]):
fixes.extend(xccdf_fix_list(oscap.xccdf_rule_get_fixes(rule)))
return fixes
+def item_get_type_str(item):
+ type = oscap.xccdf_item_get_type(item)
+ if type == oscap.XCCDF_BENCHMARK:
+ return "Benchmark"
+ elif type == oscap.XCCDF_GROUP:
+ return "Group"
+ elif type == oscap.XCCDF_RULE:
+ return "Rule"
+ elif type == oscap.XCCDF_PROFILE:
+ return "Profile"
+ elif type == oscap.XCCDF_RESULT:
+ return "TestResult"
+ elif type == oscap.XCCDF_VALUE:
+ return "Value"
+ else:
+ return "Item"
+
def parse_puppet_fixes(benchmark, ignore_ids=[]):
fixes = xccdf_get_fixes(benchmark, ignore_ids)
all_puppet = {'classes' : set(), 'environment' : "", 'parameters' : {}}
--
1.7.0.1
13 years, 10 months
[PATCH] Update list command to match Marshall's spec
by Josh Adams
Fixes bug #7146
---
src/bin/secstate | 6 ++-
src/secstate/main.py | 89 ++++++++++++++++++++++++++++++-------------------
src/secstate/util.py | 17 +++++++++
3 files changed, 75 insertions(+), 37 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index 8e379cb..0c28ecc 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -203,14 +203,16 @@ def list_content(arguments):
parser = OptionParser(usage="secstate list [options] <string>")
parser.add_option('-r', '--recurse', action='store_true', dest='recurse', default=False,
help="Recurse through an items content")
+ parser.add_option('-a', '--all', action='store_true', dest='all', default=False,
+ help="Show items regardless of selection status")
(options, args) = parser.parse_args(arguments)
if args == []:
- if not sec_instance.list_content(recurse=options.recurse):
+ if not sec_instance.list_content(recurse=options.recurse, show_all=options.all):
return -1
else:
for arg in args:
- if not sec_instance.list_content(arg, options.recurse):
+ if not sec_instance.list_content(arg, options.recurse, options.all):
return -1
return 0
diff --git a/src/secstate/main.py b/src/secstate/main.py
index c3b84d7..f93305a 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -437,7 +437,7 @@ class Secstate:
if (title != None) and (description != None):
if (search_string in title) or (search_string in description):
print "\t%(id)s:" % {'id':id}
- print "\t\tTitle: %(title)s" % {'title':title}
+ print "\t\tTitle: '%(title)s'" % {'title':title}
print "\t\tDescription: %(description)s\n" % {'description':description}
for rule in xccdf_get_items(benchmark, oscap.XCCDF_RULE):
@@ -456,7 +456,7 @@ class Secstate:
if (title != None) and (description != None):
if (search_string in title) or (search_string in description):
print "\t%(id)s:" % {'id':id}
- print "\t\tTitle: %(title)s" % {'title':title}
+ print "\t\tTitle: '%(title)s'" % {'title':title}
print "\t\tDescription: %(description)s\n" % {'description':description}
oscap.xccdf_benchmark_free(benchmark)
@@ -478,7 +478,7 @@ class Secstate:
break
else:
print "%(id)s:" % {'id':oscap.oval_definition_get_id(item)}
- print "\t%(title)s" % {'title':oscap.oval_definition_get_title(defn)}
+ print "\t'%(title)s'" % {'title':oscap.oval_definition_get_title(defn)}
print "\t%(desc)s" % {'desc':oscap.oval_definition_get_description(defn)}
oscap.xccdf_benchmark_free(benchmark)
oscap.oval_definition_model_free(def_model)
@@ -488,7 +488,7 @@ class Secstate:
print "%(id)s:" % {'id':oscap.xccdf_item_get_id(item)}
titles = oscap.xccdf_item_get_title(item)
for title in oscap_text_generator(titles):
- print "\tTitle: %(title)s" % {'title':oscap.oscap_text_get_text(title)}
+ print "\tTitle: '%(title)s'" % {'title':oscap.oscap_text_get_text(title)}
descriptions = oscap.xccdf_item_get_description(item)
for description in oscap_text_generator(descriptions):
@@ -523,50 +523,69 @@ class Secstate:
oscap.oval_definition_model_free(def_model)
return True
- def list_content(self, arg=None, recurse=False):
+ def sublist(self, benchmark, def_model, arg, recurse, show_all, tabs=0):
+ tabstr = "\t" * tabs
+ item = oscap.xccdf_benchmark_get_item(benchmark, arg)
+ if item == None:
+ defn = oscap.oval_definition_model_get_definition(def_model, arg)
+ if defn == None:
+ pass
+ print tabstr + "Definition - ID: %(id)s, Title: '%(title)s'" % {'id':arg, 'title':oscap.oval_definition_get_title(defn)}
+
+ else:
+ titles = oscap.xccdf_item_get_title(item)
+ for title in oscap_text_generator(titles):
+ if show_all:
+ if oscap.xccdf_item_get_selected(item):
+ print tabstr + "[X]%(type)s - ID: %(id)s, Title: '%(title)s'" % {'type':item_get_type_str(item), 'id':arg,
+ 'title':oscap.oscap_text_get_text(title)}
+ else:
+ print tabstr + "[ ]%(type)s - ID: %(id)s, Title: '%(title)s'" % {'type':item_get_type_str(item), 'id':arg,
+ 'title':oscap.oscap_text_get_text(title)}
+
+
+ elif oscap.xccdf_item_get_selected(item):
+ print tabstr + "%(type)s - ID: %(id)s, Title: '%(title)s'" % {'type':item_get_type_str(item), 'id':arg,
+ 'title':oscap.oscap_text_get_text(title)}
+ else:
+ if not recurse or (tabs == 0):
+ print tabstr + "[ ]%(type)s - ID: %(id)s, Title: '%(title)s'" % {'type':item_get_type_str(item), 'id':arg,
+ 'title':oscap.oscap_text_get_text(title)}
+ if recurse:
+ if oscap.xccdf_item_get_type(item) == oscap.XCCDF_GROUP:
+ content = oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))
+ for sub in xccdf_item_generator(content):
+ self.sublist(benchmark, def_model, oscap.xccdf_item_get_id(sub), recurse, show_all, tabs+1)
+
+
+ def list_content(self, arg=None, recurse=False, show_all=False):
+ ret = False
for key in self.database:
- (benchmark, oval) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
+ if arg == None:
+ return self.list_content(key, recurse, show_all)
+
+ (benchmark, def_model) = self.import_content(os.path.join(self.benchmark_dir, key, self.database[key]))
if benchmark == None:
self.log.error("Error loading benchmark: %(id)s" % {'id':key})
return False
- if arg == None:
+
+ elif arg == key:
titles = oscap.xccdf_benchmark_get_title(benchmark)
for title in oscap_text_generator(titles):
- print "%(id)s: %(title)s" % {'id':key, 'title':oscap.oscap_text_get_text(title)}
+ print "Benchmark - ID: %(id)s, Title: '%(title)s'" % {'id':key, 'title':oscap.oscap_text_get_text(title)}
if recurse:
- content = oscap.xccdf_benchmark_get_content(benchmark)
- for sub in xccdf_item_generator(content):
- titles = oscap.xccdf_item_get_title(sub)
- for title in oscap_text_generator(titles):
- print "\t%(id)s:\t%(title)s" % {'id':oscap.xccdf_item_get_id(sub),
- 'title':oscap.oscap_text_get_text(title)}
-
+ items = oscap.xccdf_benchmark_get_content(benchmark)
+ for item in xccdf_item_generator(items):
+ ret = self.sublist(benchmark, def_model, oscap.xccdf_item_get_id(item), recurse, show_all, tabs=1)
else:
- item = oscap.xccdf_benchmark_get_item(benchmark, arg)
- if item == None:
- pass
-
- titles = oscap.xccdf_item_get_title(item)
- for title in oscap_text_generator(titles):
- print "%(id)s: %(title)s" % {'id':arg, 'title':oscap.oscap_text_get_text(title)}
-
- if recurse:
- type = oscap.xccdf_item_get_type(item)
- if type == oscap.XCCDF_GROUP:
- content = oscap.xccdf_group_get_content(oscap.xccdf_item_to_group(item))
- for sub in xccdf_item_generator(content):
- titles = oscap.xccdf_item_get_title(sub)
- for title in oscap_text_generator(titles):
- print "\t%(id)s:\t%(title)s" % {'id':oscap.xccdf_item_get_id(sub),
- 'title':oscap.oscap_text_get_text(title)}
-
+ ret = self.sublist(benchmark, def_model, arg, recurse, show_all)
oscap.xccdf_benchmark_free(benchmark)
- oscap.oval_definition_model_free(oval)
+ oscap.oval_definition_model_free(def_model)
- return True
+ return ret
def get_passed_result_ids(self, xccdf_results):
if xccdf_results == None:
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 5e84a4a..6856212 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -429,6 +429,23 @@ def xccdf_get_fixes(benchmark, ignore_ids=[]):
fixes.extend(xccdf_fix_list(oscap.xccdf_rule_get_fixes(rule)))
return fixes
+def item_get_type_str(item):
+ type = oscap.xccdf_item_get_type(item)
+ if type == oscap.XCCDF_BENCHMARK:
+ return "Benchmark"
+ elif type == oscap.XCCDF_GROUP:
+ return "Group"
+ elif type == oscap.XCCDF_RULE:
+ return "Rule"
+ elif type == oscap.XCCDF_PROFILE:
+ return "Profile"
+ elif type == oscap.XCCDF_RESULT:
+ return "TestResult"
+ elif type == oscap.XCCDF_VALUE:
+ return "Value"
+ else:
+ return "Item"
+
def parse_puppet_fixes(benchmark, ignore_ids=[]):
fixes = xccdf_get_fixes(benchmark, ignore_ids)
all_puppet = {'classes' : set(), 'environment' : "", 'parameters' : {}}
--
1.7.0.1
13 years, 10 months