mirror of
https://github.com/usnistgov/macos_security.git
synced 2026-02-03 14:03:24 +00:00
feature[scripts] Adding iOS Support in scripts
* Updated generate_guidance * Updated generate_baseline * Updated generate_mapping * Updated generate_scap All to match iOS
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
os: "14.0"
|
||||
platform: macOS
|
||||
version: "Sonoma Guidance, Revision 1.0"
|
||||
cpe: o:apple:macos:14.0
|
||||
date: "2023-XX-XX"
|
||||
|
||||
@@ -131,6 +131,7 @@ def collect_rules():
|
||||
except:
|
||||
#print("expected reference '{}' is missing in key '{}' for rule{}".format(reference, key, rule))
|
||||
rule_yaml[key].update({reference: ["None"]})
|
||||
|
||||
all_rules.append(MacSecurityRule(rule_yaml['title'].replace('|', '\|'),
|
||||
rule_yaml['id'].replace('|', '\|'),
|
||||
rule_yaml['severity'].replace('|', '\|'),
|
||||
@@ -167,11 +168,12 @@ def create_args():
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
def section_title(section_name):
|
||||
def section_title(section_name, platform):
|
||||
os = platform.split(':')[2]
|
||||
titles = {
|
||||
"auth": "authentication",
|
||||
"audit": "auditing",
|
||||
"os": "macos",
|
||||
"os": os,
|
||||
"pwpolicy": "passwordpolicy",
|
||||
"icloud": "icloud",
|
||||
"sysprefs": "systempreferences",
|
||||
@@ -234,7 +236,7 @@ def available_tags(all_rules):
|
||||
print(tag)
|
||||
return
|
||||
|
||||
def output_baseline(rules, os, baseline_tailored_string, benchmark, authors, full_title):
|
||||
def output_baseline(rules, version, baseline_tailored_string, benchmark, authors, full_title):
|
||||
inherent_rules = []
|
||||
permanent_rules = []
|
||||
na_rules = []
|
||||
@@ -262,11 +264,11 @@ def output_baseline(rules, os, baseline_tailored_string, benchmark, authors, ful
|
||||
if section_name not in sections:
|
||||
sections.append(section_name)
|
||||
if baseline_tailored_string:
|
||||
output_text = f'title: "macOS {os}: Security Configuration -{full_title} {baseline_tailored_string}"\n'
|
||||
output_text += f'description: |\n This guide describes the actions to take when securing a macOS {os} system against the{full_title} {baseline_tailored_string} security baseline.\n'
|
||||
output_text = f'title: "{version["platform"]} {version["os"]}: Security Configuration -{full_title} {baseline_tailored_string}"\n'
|
||||
output_text += f'description: |\n This guide describes the actions to take when securing a {version["platform"]} {version["os"]} system against the{full_title} {baseline_tailored_string} security baseline.\n'
|
||||
else:
|
||||
output_text = f'title: "macOS {os}: Security Configuration -{full_title}"\n'
|
||||
output_text += f'description: |\n This guide describes the actions to take when securing a macOS {os} system against the{full_title} security baseline.\n'
|
||||
output_text = f'title: "{version["platform"]} {version["os"]}: Security Configuration -{full_title}"\n'
|
||||
output_text += f'description: |\n This guide describes the actions to take when securing a {version["platform"]} {version["os"]} system against the{full_title} security baseline.\n'
|
||||
|
||||
if benchmark == "recommended":
|
||||
output_text += "\n Information System Security Officers and benchmark creators can use this catalog of settings in order to assist them in security benchmark creation. This list is a catalog, not a checklist or benchmark, and satisfaction of every item is not likely to be possible or sensible in many operational scenarios.\n"
|
||||
@@ -286,7 +288,7 @@ def output_baseline(rules, os, baseline_tailored_string, benchmark, authors, ful
|
||||
|
||||
if len(other_rules) > 0:
|
||||
for section in sections:
|
||||
output_text += (' - section: "{}"\n'.format(section_title(section)))
|
||||
output_text += (' - section: "{}"\n'.format(section_title(section, version["cpe"])))
|
||||
output_text += (" rules:\n")
|
||||
for rule in other_rules:
|
||||
if rule.startswith(section):
|
||||
@@ -552,10 +554,10 @@ def main():
|
||||
# prompt for inclusion, add ODV
|
||||
odv_baseline_rules = odv_query(found_rules, benchmark)
|
||||
baseline_output_file = open(f"{build_path}/{tailored_filename}.yaml", 'w')
|
||||
baseline_output_file.write(output_baseline(odv_baseline_rules, version_yaml["os"], baseline_tailored_string, benchmark, authors, full_title))
|
||||
baseline_output_file.write(output_baseline(odv_baseline_rules, version_yaml, baseline_tailored_string, benchmark, authors, full_title))
|
||||
else:
|
||||
baseline_output_file = open(f"{build_path}/{args.keyword}.yaml", 'w')
|
||||
baseline_output_file.write(output_baseline(found_rules, version_yaml["os"], baseline_tailored_string, benchmark, authors, full_title))
|
||||
baseline_output_file.write(output_baseline(found_rules, version_yaml, baseline_tailored_string, benchmark, authors, full_title))
|
||||
|
||||
# finally revert back to the prior directory
|
||||
os.chdir(original_working_directory)
|
||||
|
||||
@@ -1133,9 +1133,10 @@ def fill_in_odv(resulting_yaml, parent_values):
|
||||
if "$ODV" in resulting_yaml[field]:
|
||||
resulting_yaml[field]=resulting_yaml[field].replace("$ODV", str(odv))
|
||||
|
||||
for result_value in resulting_yaml['result']:
|
||||
if "$ODV" in str(resulting_yaml['result'][result_value]):
|
||||
resulting_yaml['result'][result_value] = odv
|
||||
if 'result' in resulting_yaml:
|
||||
for result_value in resulting_yaml['result']:
|
||||
if "$ODV" in str(resulting_yaml['result'][result_value]):
|
||||
resulting_yaml['result'][result_value] = odv
|
||||
|
||||
if resulting_yaml['mobileconfig_info']:
|
||||
for mobileconfig_type in resulting_yaml['mobileconfig_info']:
|
||||
@@ -1655,7 +1656,8 @@ def main():
|
||||
with open(version_file) as r:
|
||||
version_yaml = yaml.load(r, Loader=yaml.SafeLoader)
|
||||
|
||||
adoc_templates = [ "adoc_rule",
|
||||
adoc_templates = [ "adoc_rule_ios",
|
||||
"adoc_rule",
|
||||
"adoc_supplemental",
|
||||
"adoc_rule_no_setting",
|
||||
"adoc_rule_custom_refs",
|
||||
@@ -1690,6 +1692,9 @@ def main():
|
||||
|
||||
|
||||
# Setup AsciiDoc templates
|
||||
with open(adoc_templates_dict['adoc_rule_ios']) as adoc_rule_ios_file:
|
||||
adoc_rule_ios_template = Template(adoc_rule_ios_file.read())
|
||||
|
||||
with open(adoc_templates_dict['adoc_rule']) as adoc_rule_file:
|
||||
adoc_rule_template = Template(adoc_rule_file.read())
|
||||
|
||||
@@ -2013,23 +2018,42 @@ def main():
|
||||
rule_srg=srg
|
||||
)
|
||||
else:
|
||||
rule_adoc = adoc_rule_template.substitute(
|
||||
rule_title=rule_yaml['title'].replace('|', '\|'),
|
||||
rule_id=rule_yaml['id'].replace('|', '\|'),
|
||||
rule_discussion=rule_yaml['discussion'].replace('|', '\|'),
|
||||
rule_check=rule_yaml['check'], # .replace('|', '\|'),
|
||||
rule_fix=rulefix,
|
||||
rule_cci=cci,
|
||||
rule_80053r5=nist_controls,
|
||||
rule_800171=nist_800171,
|
||||
rule_disa_stig=disa_stig,
|
||||
rule_cis=cis,
|
||||
rule_cmmc=cmmc,
|
||||
rule_cce=cce,
|
||||
rule_tags=tags,
|
||||
rule_srg=srg,
|
||||
rule_result=result_value
|
||||
)
|
||||
if version_yaml['platform'] == "iOS/iPadOS":
|
||||
rule_adoc = adoc_rule_ios_template.substitute(
|
||||
rule_title=rule_yaml['title'].replace('|', '\|'),
|
||||
rule_id=rule_yaml['id'].replace('|', '\|'),
|
||||
rule_discussion=rule_yaml['discussion'].replace('|', '\|'),
|
||||
rule_check=rule_yaml['check'], # .replace('|', '\|'),
|
||||
rule_fix=rulefix,
|
||||
rule_cci=cci,
|
||||
rule_80053r5=nist_controls,
|
||||
rule_800171=nist_800171,
|
||||
rule_disa_stig=disa_stig,
|
||||
rule_cis=cis,
|
||||
rule_cmmc=cmmc,
|
||||
rule_cce=cce,
|
||||
rule_tags=tags,
|
||||
rule_srg=srg,
|
||||
rule_result=result_value
|
||||
)
|
||||
else:
|
||||
rule_adoc = adoc_rule_template.substitute(
|
||||
rule_title=rule_yaml['title'].replace('|', '\|'),
|
||||
rule_id=rule_yaml['id'].replace('|', '\|'),
|
||||
rule_discussion=rule_yaml['discussion'].replace('|', '\|'),
|
||||
rule_check=rule_yaml['check'], # .replace('|', '\|'),
|
||||
rule_fix=rulefix,
|
||||
rule_cci=cci,
|
||||
rule_80053r5=nist_controls,
|
||||
rule_800171=nist_800171,
|
||||
rule_disa_stig=disa_stig,
|
||||
rule_cis=cis,
|
||||
rule_cmmc=cmmc,
|
||||
rule_cce=cce,
|
||||
rule_tags=tags,
|
||||
rule_srg=srg,
|
||||
rule_result=result_value
|
||||
)
|
||||
|
||||
adoc_output_file.write(rule_adoc)
|
||||
|
||||
|
||||
@@ -370,15 +370,15 @@ tags:
|
||||
continue
|
||||
|
||||
|
||||
full_baseline = '''title: "macOS {2} ({3}): Security Configuration - {0}"
|
||||
full_baseline = '''title: "{4} {2} ({3}): Security Configuration - {0}"
|
||||
description: |
|
||||
This guide describes the actions to take when securing a macOS {2} system against the {1}.
|
||||
This guide describes the actions to take when securing a {4} {2} system against the {1}.
|
||||
authors: |
|
||||
|===
|
||||
|Name|Organization
|
||||
|===
|
||||
parent_values: recommended
|
||||
profile:'''.format(other_header,other_header,version_yaml['os'],version_yaml['version'].split(" ")[0])
|
||||
profile:'''.format(other_header,other_header,version_yaml['os'],version_yaml['version'].split(" ")[0],version_yaml['platform'])
|
||||
|
||||
if len(audit) != 0:
|
||||
|
||||
@@ -431,13 +431,22 @@ profile:'''.format(other_header,other_header,version_yaml['os'],version_yaml['ve
|
||||
|
||||
if len(os_section) != 0:
|
||||
full_baseline = full_baseline + '''
|
||||
- section: "ios"
|
||||
rules:'''
|
||||
os_section.sort()
|
||||
for rule in os_section:
|
||||
full_baseline = full_baseline + '''
|
||||
- {}'''.format(rule)
|
||||
|
||||
if len(os_section) != 0 and version_yaml['platform'] == "macOS":
|
||||
full_baseline = full_baseline + '''
|
||||
- section: "macOS"
|
||||
rules:'''
|
||||
os_section.sort()
|
||||
for rule in os_section:
|
||||
full_baseline = full_baseline + '''
|
||||
- {}'''.format(rule)
|
||||
|
||||
|
||||
if len(pwpolicy) != 0:
|
||||
full_baseline = full_baseline + '''
|
||||
- section: "PasswordPolicy"
|
||||
@@ -474,13 +483,15 @@ profile:'''.format(other_header,other_header,version_yaml['os'],version_yaml['ve
|
||||
full_baseline = full_baseline + '''
|
||||
- {}'''.format(rule)
|
||||
|
||||
listofsupplementals = str()
|
||||
for supp_rule in glob.glob('../rules/supplemental/*.yaml',recursive=True):
|
||||
listofsupplementals = listofsupplementals + '''- {}
|
||||
'''.format(os.path.basename(supp_rule).split(".")[0])
|
||||
full_baseline = full_baseline + '''
|
||||
- section: "Supplemental"
|
||||
rules:
|
||||
- supplemental_firewall_pf
|
||||
- supplemental_password_policy
|
||||
- supplemental_smartcard
|
||||
'''
|
||||
{}
|
||||
'''.format(listofsupplementals)
|
||||
|
||||
|
||||
|
||||
@@ -488,9 +499,9 @@ profile:'''.format(other_header,other_header,version_yaml['os'],version_yaml['ve
|
||||
if os.path.isdir("../build/" + other_header.lower() + "/baseline/") == False:
|
||||
os.mkdir("../build/" + other_header.lower() + "/baseline")
|
||||
|
||||
with open("../build/" + other_header.lower() + "/baseline/" + other_header.lower() + ".yaml",'w') as fw:
|
||||
with open("../build/" + other_header.lower() + "/baseline/" + other_header.lower().replace(" ","_") + ".yaml",'w') as fw:
|
||||
fw.write(full_baseline)
|
||||
print(other_header.lower() + ".yaml baseline file created in build/" + other_header + "/baseline/")
|
||||
print(other_header.lower().replace(" ","_") + ".yaml baseline file created in build/" + other_header + "/baseline/")
|
||||
|
||||
print("Move all of the folders in rules into the custom folder.")
|
||||
except:
|
||||
|
||||
@@ -14,8 +14,57 @@ from datetime import datetime
|
||||
import shutil
|
||||
from time import sleep
|
||||
import argparse
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
|
||||
def format_mobileconfig_fix(mobileconfig):
|
||||
"""Takes a list of domains and setting from a mobileconfig, and reformats it for the output of the fix section of the guide.
|
||||
"""
|
||||
rulefix = ""
|
||||
for domain, settings in mobileconfig.items():
|
||||
if domain == "com.apple.ManagedClient.preferences":
|
||||
rulefix = rulefix + \
|
||||
(f"NOTE: The following settings are in the ({domain}) payload. This payload requires the additional settings to be sub-payloads within, containing their defined payload types.\n\n")
|
||||
rulefix = rulefix + format_mobileconfig_fix(settings)
|
||||
else:
|
||||
rulefix = rulefix + (
|
||||
f"Create a configuration profile containing the following keys in the ({domain}) payload type:\n\n")
|
||||
rulefix = rulefix + "[source,xml]\n----\n"
|
||||
for item in settings.items():
|
||||
rulefix = rulefix + (f"<key>{item[0]}</key>\n")
|
||||
|
||||
if type(item[1]) == bool:
|
||||
rulefix = rulefix + \
|
||||
(f"<{str(item[1]).lower()}/>\n")
|
||||
elif type(item[1]) == list:
|
||||
rulefix = rulefix + "<array>\n"
|
||||
for setting in item[1]:
|
||||
rulefix = rulefix + \
|
||||
(f" <string>{setting}</string>\n")
|
||||
rulefix = rulefix + "</array>\n"
|
||||
elif type(item[1]) == int:
|
||||
rulefix = rulefix + \
|
||||
(f"<integer>{item[1]}</integer>\n")
|
||||
elif type(item[1]) == str:
|
||||
rulefix = rulefix + \
|
||||
(f"<string>{item[1]}</string>\n")
|
||||
elif type(item[1]) == dict:
|
||||
rulefix = rulefix + "<dict>\n"
|
||||
for k,v in item[1].items():
|
||||
rulefix = rulefix + \
|
||||
(f" <key>{k}</key>\n")
|
||||
rulefix = rulefix + " <array>\n"
|
||||
for setting in v:
|
||||
rulefix = rulefix + \
|
||||
(f" <string>{setting}</string>\n")
|
||||
rulefix = rulefix + " </array>\n"
|
||||
rulefix = rulefix + "</dict>\n"
|
||||
|
||||
rulefix = rulefix + "----\n\n"
|
||||
|
||||
return rulefix
|
||||
|
||||
def replace_ocil(xccdf, x):
|
||||
regex = r'''([\r\n].*?)(?:=?\r|\n)(.*?(?:def:{}\").*)'''.format(x)
|
||||
substr = '''<check system="http://scap.nist.gov/schema/ocil/2"><check-content-ref href="ocil.xml"/>'''
|
||||
@@ -39,28 +88,37 @@ def create_args():
|
||||
return parser.parse_args()
|
||||
|
||||
def generate_scap(all_rules, all_baselines, args):
|
||||
|
||||
|
||||
export_as = ""
|
||||
|
||||
version_file = "../VERSION.yaml"
|
||||
with open(version_file) as r:
|
||||
version_yaml = yaml.load(r, Loader=yaml.SafeLoader)
|
||||
|
||||
if args.xccdf:
|
||||
export_as = "xccdf"
|
||||
|
||||
if args.oval:
|
||||
export_as = "oval"
|
||||
if "ios" in version_yaml['cpe']:
|
||||
print("OVAL generation is not available on iOS")
|
||||
exit()
|
||||
|
||||
|
||||
if args.oval == None and args.xccdf == None:
|
||||
export_as = "scap"
|
||||
|
||||
version_file = "../VERSION.yaml"
|
||||
with open(version_file) as r:
|
||||
version_yaml = yaml.load(r, Loader=yaml.SafeLoader)
|
||||
if "ios" in version_yaml['cpe']:
|
||||
print("iOS will only export as XCCDF")
|
||||
export_as = "xccdf"
|
||||
|
||||
now = datetime.now()
|
||||
date_time_string = now.strftime("%Y-%m-%dT%H:%M:%S")
|
||||
|
||||
filenameversion = version_yaml['version'].split(",")[1].replace(" ", "_")[1:]
|
||||
output = "../build/macOS_{0}_Security_Compliance_Benchmark-{1}".format(version_yaml['os'],filenameversion)
|
||||
|
||||
if "ios" in version_yaml['cpe']:
|
||||
output = "../build/iOS_{0}_Security_Compliance_Benchmark-{1}".format(version_yaml['os'],filenameversion)
|
||||
|
||||
if export_as == "xccdf":
|
||||
output = output + "_xccdf.xml"
|
||||
|
||||
@@ -91,12 +149,15 @@ def generate_scap(all_rules, all_baselines, args):
|
||||
<oval:product_name xmlns:oval="http://oval.mitre.org/XMLSchema/oval-common-5">macOS Security Compliance Project</oval:product_name>
|
||||
</generator>'''.format(date_time_string)
|
||||
|
||||
ostype = "macOS"
|
||||
if "ios" in version_yaml['cpe']:
|
||||
ostype = "iOS/iPadOS"
|
||||
xccdfPrefix = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Benchmark xmlns="http://checklists.nist.gov/xccdf/1.2" id="xccdf_gov.nist.mscp.content_benchmark_macOS_{1}" style="SCAP_1.3" resolved="true" xml:lang="en">
|
||||
<status date="{3}">draft</status>
|
||||
<title>macOS {1}: Security Configuration</title>
|
||||
<title>{4} {1}: Security Configuration</title>
|
||||
<description>
|
||||
macOS {1}: Security Configuration
|
||||
{4} {1}: Security Configuration
|
||||
</description>
|
||||
|
||||
|
||||
@@ -113,7 +174,7 @@ def generate_scap(all_rules, all_baselines, args):
|
||||
<contributor xmlns="http://purl.org/dc/elements/1.1/">Dan Brodjieski - National Aeronautics and Space Administration</contributor>
|
||||
<contributor xmlns="http://purl.org/dc/elements/1.1/">Allen Golbig - Jamf</contributor>
|
||||
</metadata>
|
||||
'''.format(date_time_string, version_yaml['os'], version_yaml['version'],date_time_string.split("T")[0] + "Z")
|
||||
'''.format(date_time_string, version_yaml['os'], version_yaml['version'],date_time_string.split("T")[0] + "Z", ostype)
|
||||
|
||||
scapPrefix = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<data-stream-collection xmlns="http://scap.nist.gov/schema/scap/source/1.2" id="scap_gov.nist.mscp.content_collection_macOS_{1}" schematron-version="1.3">
|
||||
@@ -339,6 +400,9 @@ def generate_scap(all_rules, all_baselines, args):
|
||||
cce = rule_yaml['references']['cce'][0]
|
||||
|
||||
if export_as == "scap":
|
||||
mobileconfig_info = ""
|
||||
if rule_yaml['mobileconfig']:
|
||||
mobileconfig_info = escape(format_mobileconfig_fix(rule_yaml['mobileconfig_info']))
|
||||
xccdf_rules = xccdf_rules + '''
|
||||
<Rule id="xccdf_gov.nist.mscp.content_rule_{0}" selected="false" role="full" severity="{1}" weight="1.0">
|
||||
<title>{2}</title>
|
||||
@@ -351,9 +415,13 @@ def generate_scap(all_rules, all_baselines, args):
|
||||
<fixtext>{7}</fixtext>
|
||||
{8}
|
||||
</Rule>
|
||||
'''.format(rule_yaml['id'] + "_" + odv_label, severity, rule_yaml['title'], rule_yaml['discussion'].replace("<","<").replace(">",">").replace("&","&").rstrip(), rule_yaml['check'].replace("<","<").replace(">",">").replace("&","&").rstrip(), result, cce,rule_yaml['fix'].replace("<","<").replace(">",">").replace("&","&"), check_rule, references)
|
||||
'''.format(rule_yaml['id'] + "_" + odv_label, severity, rule_yaml['title'], rule_yaml['discussion'].replace("<","<").replace(">",">").replace("&","&").rstrip(), rule_yaml['check'].replace("<","<").replace(">",">").replace("&","&").rstrip(), result, cce,rule_yaml['fix'].replace("<","<").replace(">",">").replace("&","&") + "\n" + mobileconfig_info, check_rule, references)
|
||||
|
||||
if export_as == "xccdf":
|
||||
mobileconfig_info = ""
|
||||
if rule_yaml['mobileconfig']:
|
||||
mobileconfig_info = escape(format_mobileconfig_fix(rule_yaml['mobileconfig_info']))
|
||||
|
||||
xccdf_rules = xccdf_rules + '''
|
||||
<Rule id="xccdf_gov.nist.mscp.content_rule_{0}" selected="false" role="full" severity="{1}" weight="1.0">
|
||||
<title>{2}</title>
|
||||
@@ -366,10 +434,11 @@ def generate_scap(all_rules, all_baselines, args):
|
||||
<fixtext>{7}</fixtext>
|
||||
|
||||
</Rule>
|
||||
'''.format(rule_yaml['id'] + "_" + odv_label, severity, rule_yaml['title'], rule_yaml['discussion'].replace("<","<").replace(">",">").replace("&","&").rstrip(), rule_yaml['check'].replace("<","<").replace(">",">").replace("&","&").rstrip(), result, cce,rule_yaml['fix'].replace("<","<").replace(">",">").replace("&","&"), references)
|
||||
'''.format(rule_yaml['id'] + "_" + odv_label, severity, rule_yaml['title'], rule_yaml['discussion'].replace("<","<").replace(">",">").replace("&","&").rstrip(), rule_yaml['check'].replace("<","<").replace(">",">").replace("&","&").rstrip(), result, cce,rule_yaml['fix'].replace("<","<").replace(">",">").replace("&","&") + "\n" + mobileconfig_info, references)
|
||||
continue
|
||||
|
||||
|
||||
|
||||
|
||||
if "inherent" in rule_yaml['tags'] or "n_a" in rule_yaml['tags'] or "permanent" in rule_yaml['tags']:
|
||||
xccdf_rules = replace_ocil(xccdf_rules,x)
|
||||
x += 1
|
||||
|
||||
Reference in New Issue
Block a user