This is a step along the path toward support for remediation content development, but it is only one step.
This patchset also cleans up some other code. It has not been tested, but it appears to emit "<sub>" elements in the manner OpenSCAP can process, so that parameters can be provided to remediation scripts.
Documentation/instructions are still needed. It should be apparent from the example that scripts which use parameters will need to "source" a support file and also declare which environment variables they expect to have populated by XCCDF values (using the "populate" function).
This patch is only to enable example remediation content, which may be suitable for testing. The SSG project/community will be ready for remediation content en masse when there is evidence of sufficient interest in the checking mechanisms, upon which any remediation effort would naturally depend.
Jeffrey Blank (4): example remediation script which takes a parameter incomplete support file for bash remediations * does at least warn when undefined variable exists rewrite of combinefixes.py to handle parameters for OpenSCAP remedation generation made xccdf-addfixes insert all text and child nodes of a fix
RHEL6/input/fixes/bash/set_system_login_banner.sh | 6 ++ RHEL6/input/fixes/bash/templates/support.sh | 9 ++ RHEL6/transforms/combinefixes.py | 97 +++++++++----------- RHEL6/transforms/xccdf-addfixes.xslt | 2 +- 4 files changed, 60 insertions(+), 54 deletions(-) create mode 100644 RHEL6/input/fixes/bash/set_system_login_banner.sh create mode 100644 RHEL6/input/fixes/bash/templates/support.sh
Signed-off-by: Jeffrey Blank blank@eclipse.ncsc.mil --- RHEL6/input/fixes/bash/set_system_login_banner.sh | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) create mode 100644 RHEL6/input/fixes/bash/set_system_login_banner.sh
diff --git a/RHEL6/input/fixes/bash/set_system_login_banner.sh b/RHEL6/input/fixes/bash/set_system_login_banner.sh new file mode 100644 index 0000000..ed172eb --- /dev/null +++ b/RHEL6/input/fixes/bash/set_system_login_banner.sh @@ -0,0 +1,6 @@ +source ./templates/support.sh +populate login_banner_text + +cat <<EOF >/etc/issue +$login_banner_text +EOF
Signed-off-by: Jeffrey Blank blank@eclipse.ncsc.mil --- RHEL6/input/fixes/bash/templates/support.sh | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) create mode 100644 RHEL6/input/fixes/bash/templates/support.sh
diff --git a/RHEL6/input/fixes/bash/templates/support.sh b/RHEL6/input/fixes/bash/templates/support.sh new file mode 100644 index 0000000..e25ce4d --- /dev/null +++ b/RHEL6/input/fixes/bash/templates/support.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +function populate { +# code to populate environment variables needed (for unit testing) +if [ -z "${!1}" ]; then + echo "$1 is not defined. Exiting." + exit +fi +}
The comment should probably read instead: # code still needed to populate environment variables (for unit testing)"
On Sun, Jun 2, 2013 at 3:05 PM, Jeffrey Blank blank@eclipse.ncsc.milwrote:
Signed-off-by: Jeffrey Blank blank@eclipse.ncsc.mil
RHEL6/input/fixes/bash/templates/support.sh | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) create mode 100644 RHEL6/input/fixes/bash/templates/support.sh
diff --git a/RHEL6/input/fixes/bash/templates/support.sh b/RHEL6/input/fixes/bash/templates/support.sh new file mode 100644 index 0000000..e25ce4d --- /dev/null +++ b/RHEL6/input/fixes/bash/templates/support.sh @@ -0,0 +1,9 @@ +#!/bin/bash
+function populate { +# code to populate environment variables needed (for unit testing) +if [ -z "${!1}" ]; then
echo "$1 is not defined. Exiting."
exit
+fi
+}
1.7.1
Signed-off-by: Jeffrey Blank blank@eclipse.ncsc.mil --- RHEL6/transforms/combinefixes.py | 97 +++++++++++++++++--------------------- 1 files changed, 44 insertions(+), 53 deletions(-)
diff --git a/RHEL6/transforms/combinefixes.py b/RHEL6/transforms/combinefixes.py index f9d8b7f..71fbe6f 100755 --- a/RHEL6/transforms/combinefixes.py +++ b/RHEL6/transforms/combinefixes.py @@ -1,58 +1,49 @@ #!/usr/bin/python
-import sys, os - -header = '''<fix-content system="urn:xccdf:fix:script:sh" xmlns="http://checklists.nist.gov/xccdf/1.1">\n''' -footer = '</fix-content>\n' -fixGroupHeader = '''<fix-group id="bash" system="urn:xccdf:fix:script:sh" xmlns="http://checklists.nist.gov/xccdf/1.1">\n''' -fixGroupFooter = '</fix-group>\n' -fixCommonGroupHeader = '''<fix-common-group id="bash-common" xmlns="http://checklists.nist.gov/xccdf/1.1">\n''' -fixCommonGroupFooter = '</fix-common-group>\n' - -def encode(text): - text = text.replace('&','&') - text = text.replace('>','>') - text = text.replace('<','<') - return text +import sys, os, re, lxml.etree as etree + +def substitute_vars(fix): + # brittle and troubling code to assign environment vars to XCCDF values + m = re.match("(\s*source\s+\S+)\n+(\s*populate\s+)(\S+)\n(.*)", fix.text, re.DOTALL) + if not m: + # no need to alter fix.text + return + # otherwise, create node to populate environment variable + varname = m.group(3) + mainscript = m.group(4) + fix.text = varname + "=" + '"' + # new <sub> element to reference XCCDF variable + xccdf_sub = etree.SubElement(fix, "sub", idref=varname) + xccdf_sub.tail = '"' + mainscript + fix.append(xccdf_sub) +
def main(): - if len(sys.argv) < 2: - print "Provide a directory name, which contains the fixes." - sys.exit(1) - - fixDir = sys.argv[1] - output = sys.argv[2] - out = open(output,'w') - out.write(header) - out.write(fixGroupHeader) - for filename in os.listdir(fixDir): - if filename.endswith(".sh"): - body = "" - with open( fixDir + "/" + filename, 'r') as f: - body = body + encode(f.read()) - fixName = os.path.splitext(filename)[0] - out.write("<fix rule=""+fixName+"">\n") - out.write(body.rstrip()) - out.write("</fix>\n") - - out.write(fixGroupFooter) - - out.write(fixCommonGroupHeader) - for filename in os.listdir(fixDir): - if filename.endswith("common"): - body = "" - with open( fixDir + "/" + filename, 'r') as f: - body = body + encode(f.read()) - fixName = os.path.splitext(filename)[0] - out.write("<fix-common id=""+fixName+"">\n") - out.write(body.rstrip()) - out.write("</fix-common>\n") - - out.write(fixCommonGroupFooter) - out.write(footer) - - out.close() - sys.exit(0) - + if len(sys.argv) < 2: + print "Provide a directory name, which contains the fixes." + sys.exit(1) + + fixdir = sys.argv[1] + output = sys.argv[2] + + fixcontent = etree.Element("fix-content", system="urn:xccdf:fix:script:sh", xmlns="http://checklists.nist.gov/xccdf/1.1") + fixgroup = etree.SubElement(fixcontent, "fix-group", id="bash", system="urn:xccdf:fix:script:sh", xmlns="http://checklists.nist.gov/xccdf/1.1") + + for filename in os.listdir(fixdir): + if filename.endswith(".sh"): + # create and populate new fix element based on shell file + fixname = os.path.splitext(filename)[0] + fix = etree.SubElement(fixgroup, "fix", rule=fixname) + with open( fixdir + "/" + filename, 'r') as f: + # assignment automatically escapes shell characters for XML + fix.text = f.read() + # replace instance of bash function "populate" with XCCDF variable substitution + substitute_vars(fix) + + tree = etree.ElementTree(fixcontent) + tree.write(output, pretty_print=True) + + sys.exit(0) + if __name__ == "__main__": - main() + main()
On Sun, 2 Jun 2013 15:05:21 -0400 Jeffrey Blank blank@eclipse.ncsc.mil wrote:
Signed-off-by: Jeffrey Blank blank@eclipse.ncsc.mil
RHEL6/transforms/combinefixes.py | 97 +++++++++++++++++--------------------- 1 files changed, 44 insertions(+), 53 deletions(-)
diff --git a/RHEL6/transforms/combinefixes.py b/RHEL6/transforms/combinefixes.py index f9d8b7f..71fbe6f 100755 --- a/RHEL6/transforms/combinefixes.py +++ b/RHEL6/transforms/combinefixes.py @@ -1,58 +1,49 @@ #!/usr/bin/python
-import sys, os
-header = '''<fix-content system="urn:xccdf:fix:script:sh" xmlns="http://checklists.nist.gov/xccdf/1.1">\n''' -footer = '</fix-content>\n' -fixGroupHeader = '''<fix-group id="bash" system="urn:xccdf:fix:script:sh" xmlns="http://checklists.nist.gov/xccdf/1.1">\n''' -fixGroupFooter = '</fix-group>\n' -fixCommonGroupHeader = '''<fix-common-group id="bash-common" xmlns="http://checklists.nist.gov/xccdf/1.1">\n''' -fixCommonGroupFooter = '</fix-common-group>\n' - -def encode(text):
- text = text.replace('&','&')
- text = text.replace('>','>')
- text = text.replace('<','<')
- return text
+import sys, os, re, lxml.etree as etree
Jeff, this is great! The lxml.etree is pretty cool. How do you know or how to find out about these gems? I'm really familiar with "cpan" for perl, is there something similar for python?
On 6/3/13 9:59 AM, Brian Millett wrote:
On Sun, 2 Jun 2013 15:05:21 -0400 Jeffrey Blankblank@eclipse.ncsc.mil wrote:
Signed-off-by: Jeffrey Blankblank@eclipse.ncsc.mil
RHEL6/transforms/combinefixes.py | 97 +++++++++++++++++--------------------- 1 files changed, 44 insertions(+), 53 deletions(-)
diff --git a/RHEL6/transforms/combinefixes.py b/RHEL6/transforms/combinefixes.py index f9d8b7f..71fbe6f 100755 --- a/RHEL6/transforms/combinefixes.py +++ b/RHEL6/transforms/combinefixes.py @@ -1,58 +1,49 @@ #!/usr/bin/python
-import sys, os
-header = '''<fix-content system="urn:xccdf:fix:script:sh" xmlns="http://checklists.nist.gov/xccdf/1.1">\n''' -footer = '</fix-content>\n' -fixGroupHeader = '''<fix-group id="bash" system="urn:xccdf:fix:script:sh" xmlns="http://checklists.nist.gov/xccdf/1.1">\n''' -fixGroupFooter = '</fix-group>\n' -fixCommonGroupHeader = '''<fix-common-group id="bash-common" xmlns="http://checklists.nist.gov/xccdf/1.1">\n''' -fixCommonGroupFooter = '</fix-common-group>\n' - -def encode(text):
- text = text.replace('&','&')
- text = text.replace('>','>')
- text = text.replace('<','<')
- return text
+import sys, os, re, lxml.etree as etree
Jeff, this is great! The lxml.etree is pretty cool. How do you know or how to find out about these gems? I'm really familiar with "cpan" for perl, is there something similar for python?
Check out https://pypi.python.org/pypi, specifically the "List Packages" link!
Signed-off-by: Jeffrey Blank blank@eclipse.ncsc.mil --- RHEL6/transforms/xccdf-addfixes.xslt | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/RHEL6/transforms/xccdf-addfixes.xslt b/RHEL6/transforms/xccdf-addfixes.xslt index d3c4598..0a7e1e2 100644 --- a/RHEL6/transforms/xccdf-addfixes.xslt +++ b/RHEL6/transforms/xccdf-addfixes.xslt @@ -19,7 +19,7 @@ <xsl:if test="@rule=$rule_id"> <xsl:element name="fix" namespace="http://checklists.nist.gov/xccdf/1.1%22%3E <xsl:attribute name="system"><xsl:value-of select="$fixsystem"/></xsl:attribute> - <xsl:value-of select="text()"/> + <xsl:apply-templates select="node()"/> </xsl:element> </xsl:if> </xsl:for-each>
On 6/2/13 3:05 PM, Jeffrey Blank wrote:
This is a step along the path toward support for remediation content development, but it is only one step.
This patchset also cleans up some other code. It has not been tested, but it appears to emit "<sub>" elements in the manner OpenSCAP can process, so that parameters can be provided to remediation scripts.
Documentation/instructions are still needed. It should be apparent from the example that scripts which use parameters will need to "source" a support file and also declare which environment variables they expect to have populated by XCCDF values (using the "populate" function).
This patch is only to enable example remediation content, which may be suitable for testing. The SSG project/community will be ready for remediation content en masse when there is evidence of sufficient interest in the checking mechanisms, upon which any remediation effort would naturally depend.
Jeffrey Blank (4): example remediation script which takes a parameter incomplete support file for bash remediations * does at least warn when undefined variable exists rewrite of combinefixes.py to handle parameters for OpenSCAP remedation generation made xccdf-addfixes insert all text and child nodes of a fix
RHEL6/input/fixes/bash/set_system_login_banner.sh | 6 ++ RHEL6/input/fixes/bash/templates/support.sh | 9 ++ RHEL6/transforms/combinefixes.py | 97 +++++++++----------- RHEL6/transforms/xccdf-addfixes.xslt | 2 +- 4 files changed, 60 insertions(+), 54 deletions(-) create mode 100644 RHEL6/input/fixes/bash/set_system_login_banner.sh create mode 100644 RHEL6/input/fixes/bash/templates/support.sh
This is very clever. In the XCCDF it renders as:
<fix system="urn:xccdf:fix:script:sh">login_banner_text="<sub idref="login_banner_text"/>" cat <<EOF >/etc/issue $login_banner_text EOF
</fix>
Then in the bash remediation scripts:
# XCCDF rule: set_system_login_banner # CCE-26974-6 login_banner_text="You are accessing a U.S. Government (USG) Information System (IS) that is provided for USG-authorized use only.[\s\n]*By using this IS (which includes any device attached to this IS), you consent to the following conditions:[\s\n]*-[\s\n]*The USG routinely intercepts and monitors communications on this IS for purposes including, but not limited to, penetration testing, COMSEC monitoring, network operations and defense, personnel misconduct (PM), law enforcement (LE), and counterintelligence (CI) investigations.[\s\n]*-[\s\n]*At any time, the USG may inspect and seize data stored on this IS.[\s\n]*-[\s\n]*Communications using, or data stored on, this IS are not private, are subject to routine monitoring, interception, and search, and may be disclosed or used for any USG-authorized purpose.[\s\n]*-[\s\n]*This IS includes security measures (e.g., authentication and access controls) to protect USG interests -- not for your personal benefit or privacy.[\s\n]*-[\s\n]*Notwithstanding the above, using this IS does not constitute consent to PM, LE or CI investigative searching or monitoring of the content of privileged communications, or work product, related to personal representation or services by attorneys, psychotherapists, or clergy, and their assistants. Such communications and work product are private and confidential. See User Agreement for details." cat <<EOF >/etc/issue $login_banner_text EOF
This is a *fantastic* approach. I'll see what I can do around generating a doc that transforms refine values that people can source as variable names, likely won't have anything until Thurs.
Ack!
scap-security-guide@lists.fedorahosted.org