def test_perms_and_ownership(file_reqs): results = GroupTestResult() if not file_reqs: return TestResult(Result.SKIP, 'Unable to load module config file') else: for req in file_reqs: # if the entry doesn't even contain a file to check, skip it if 'file' not in req: stats = None continue else: stats = utils.get_stats_on_file(req['file']) if stats: if 'disallowed_perms' in req: check_name = "Checking perms for: " + req['file'] result = _does_perms_meet_req(stats, req['disallowed_perms']) if result.result == Result.SKIP: logger.info("Got malformed permission requirement " "{}".format(req['disallowed_perms'])) results.add_result(check_name, result) if 'owner' in req or 'group' in req: check_name = "Checking owner/group for: " + req['file'] owner_string = None group_string = None if 'owner' in req: owner_string = req['owner'] if 'group' in req: group_string = req['group'] result = _does_owner_group_meet_req(stats, owners=owner_string, groups=group_string) results.add_result(check_name, result) else: check_name = "Checking controls for: " + req['file'] results.add_result(check_name, TestResult(Result.SKIP, notes="Couldn't check file")) return results
def test_perms_and_ownership(file_reqs): results = GroupTestResult() if not file_reqs: return TestResult(Result.SKIP, 'Unable to load module config file') else: for req in file_reqs: # if the entry doesn't even contain a file to check, skip it if 'file' not in req: stats = None continue else: stats = utils.get_stats_on_file(req['file']) if stats: if 'disallowed_perms' in req: check_name = "Checking perms for: " + req['file'] result = _does_perms_meet_req(stats, req['disallowed_perms']) if result.result == Result.SKIP: logger.info("Got malformed permission requirement " "{}".format(req['disallowed_perms'])) results.add_result(check_name, result) if 'owner' in req or 'group' in req: check_name = "Checking owner/group for: " + req['file'] owner_string = None group_string = None if 'owner' in req: owner_string = req['owner'] if 'group' in req: group_string = req['group'] result = _does_owner_group_meet_req(stats, owners=owner_string, groups=group_string) results.add_result(check_name, result) else: check_name = "Checking controls for: " + req['file'] results.add_result( check_name, TestResult(Result.SKIP, notes="Couldn't check file")) return results
def test_apparmor(): """Uses return from apparmor_status to check installation and level at which AppArmor is monitoring. :returns: A TestResult object containing the result and notes explaining why it did not pass. """ # initial configurations return_result = None logger.debug("Attempting to validate AppArmor is installed.") # check try: cmd = 'apparmor_status' p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) stdout, stderr = p.communicate() if b'apparmor_status: command not found' in stderr: reason = "AppArmor is not installed." logger.debug(reason) return_result = TestResult(Result.FAIL, notes=reason) # enforcing check, no /'s = no directories elif b"//" not in stdout: reason = "AppArmor has no modules loaded." logger.info(reason) return_result = TestResult(Result.FAIL, notes=reason) elif b"//" in stdout: reason = "AppArmor is installed and policy is loaded." logger.info(reason) return_result = TestResult(Result.PASS) else: # wth? logger.debug( "Unexpected error while looking for AppArmor: " " Standard Output from sestatus command: [%s]" " Standard Error from sestatus command: [%s]", stdout, stderr) return_result = TestResult(Result.SKIP, notes="Unexpected error.") except EnvironmentError as e: logger.debug("Unexpected error running apparmor_status: [%s]", e) return_result = TestResult(Result.SKIP, notes="Unexpected error.") return return_result
def test_apparmor(): """Uses return from apparmor_status to check installation and level at which AppArmor is monitoring. :returns: A TestResult object containing the result and notes explaining why it did not pass. """ # initial configurations return_result = None logger.debug("Attempting to validate AppArmor is installed.") # check try: cmd = 'apparmor_status' p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) stdout, stderr = p.communicate() if b'apparmor_status: command not found' in stderr: reason = "AppArmor is not installed." logger.debug(reason) return_result = TestResult(Result.FAIL, notes=reason) # enforcing check, no /'s = no directories elif b"//" not in stdout: reason = "AppArmor has no modules loaded." logger.info(reason) return_result = TestResult(Result.FAIL, notes=reason) elif b"//" in stdout: reason = "AppArmor is installed and policy is loaded." logger.info(reason) return_result = TestResult(Result.PASS) else: # wth? logger.debug("Unexpected error while looking for AppArmor: " " Standard Output from sestatus command: [%s]" " Standard Error from sestatus command: [%s]", stdout, stderr) return_result = TestResult(Result.SKIP, notes="Unexpected error.") except EnvironmentError as e: logger.debug("Unexpected error running apparmor_status: [%s]", e) return_result = TestResult(Result.SKIP, notes="Unexpected error.") return return_result
def test_selinux(): """Uses return from sestatus command to ensure SELinux is installed :returns: A TestResult object containing the result and, on failure, notes explaining why it did not pass. """ # logger return_result = None logger.debug("Attempting to validate SELinux is installed.") # check try: # if sestatus_return is stdout: cmd = 'sestatus' p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) stdout, stderr = p.communicate() if b'sestatus: not found' in stderr: reason = "SELinux is not installed." logger.info(reason) return_result = TestResult(Result.FAIL, notes=reason) elif b'disabled' in stdout: reason = "SELinux is disabled." logger.info(reason) return_result = TestResult(Result.FAIL, notes=reason) elif b'permissive' in stdout: reason = "SELinux is permissive (disabled but logging)." logger.info(reason) return_result = TestResult(Result.FAIL, notes=reason) elif b'enforcing' in stdout: reason = "SELinux is installed and enforcing." logger.info(reason) return_result = TestResult(Result.PASS) else: # wth? logger.debug( "Unexpected error while looking for SELinux: " " Standard Output from sestatus command: [%s]" " Standard Error from sestatus command: [%s]", stdout, stderr) return_result = TestResult(Result.SKIP, notes="Unexpected error.") except EnvironmentError as e: # log no selinux logger.debug("Unexpected error running sestatus: [{}]".format(e)) return_result = TestResult(Result.SKIP, notes="Unexpected error.") return return_result
def test_sysctl_values(sysctl_reqs): results = GroupTestResult() if not sysctl_reqs: return TestResult(Result.SKIP, "Unable to load module config file") else: for requirement in sysctl_reqs: cur_result = None notes = "" # valid tests must have a key and allowed values if 'key' in requirement and 'allowed_values' in requirement: # name a test with specified name if it exists, otherwise just # use the key if 'name' in requirement: test_name = "Sysctl check for " + requirement['name'] else: test_name = "Sysctl check for " + requirement['key'] val_str = requirement['allowed_values'].replace(' ', '') allowed_values = val_str.split(',') try: value = utils.get_sysctl_value(requirement['key']) except utils.ValNotFound: cur_result = Result.SKIP notes = "Could not find a value for sysctl key { " notes += requirement['key'] + " }" else: if value in allowed_values: cur_result = Result.PASS else: cur_result = Result.FAIL notes = "Key { " + requirement['key'] notes += " } expected one of { " + val_str notes += " } but got " + value results.add_result(test_name, TestResult(cur_result, notes=notes)) else: logger.info("Got malformed requirement %s", requirement) return results
def test_selinux(): """Uses return from sestatus command to ensure SELinux is installed :returns: A TestResult object containing the result and, on failure, notes explaining why it did not pass. """ # logger return_result = None logger.debug("Attempting to validate SELinux is installed.") # check try: # if sestatus_return is stdout: cmd = 'sestatus' p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) stdout, stderr = p.communicate() if b'sestatus: not found' in stderr: reason = "SELinux is not installed." logger.info(reason) return_result = TestResult(Result.FAIL, notes=reason) elif b'disabled' in stdout: reason = "SELinux is disabled." logger.info(reason) return_result = TestResult(Result.FAIL, notes=reason) elif b'permissive' in stdout: reason = "SELinux is permissive (disabled but logging)." logger.info(reason) return_result = TestResult(Result.FAIL, notes=reason) elif b'enforcing' in stdout: reason = "SELinux is installed and enforcing." logger.info(reason) return_result = TestResult(Result.PASS) else: # wth? logger.debug("Unexpected error while looking for SELinux: " " Standard Output from sestatus command: [%s]" " Standard Error from sestatus command: [%s]", stdout, stderr) return_result = TestResult(Result.SKIP, notes="Unexpected error.") except EnvironmentError as e: # log no selinux logger.debug("Unexpected error running sestatus: [{}]".format(e)) return_result = TestResult(Result.SKIP, notes="Unexpected error.") return return_result
def test_shellshock(config): logger.debug("Testing shell for 'shellshock/bashbug' vulnerability.") try: cmd = config['exploit_command'] except KeyError: logger.error("Can't find exploit command for shellshock test") else: p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) stdout, stderr = p.communicate() if b'vulnerable' in stdout: reason = "System is vulnerable to Shellshock/Bashbug." logger.info(reason) result = Result.FAIL else: reason = "System is not vulnerable to Shellshock/Bashbug." logger.info(reason) result = Result.PASS return TestResult(result, reason)
def test_accounts_nopassword(): try: import spwd except ImportError: logger.info("Import of spwd failed ") return TestResult(Result.SKIP, "Unable to import 'spwd' module") disabled = [] locked = [] passworded = [] no_password = [] shadow_entries = spwd.getspall() for entry in shadow_entries: # passwords which start with ! have been locked if entry.sp_pwd.startswith('!'): locked.append(entry.sp_nam) # passwords which start with * have been disabled elif entry.sp_pwd.startswith('*'): disabled.append(entry.sp_nam) # blank passwords are bad! elif entry.sp_pwd == "": no_password.append(entry.sp_nam) # otherwise the account has a password else: passworded.append(entry.sp_nam) if len(no_password) > 0: notes = "Account(s) { " + str(no_password) + " } have no password!" test_result = Result.FAIL else: notes = ("Disabled: " + str(len(disabled)) + ", Locked: " + str(len(locked)) + ", Password: "******", No Password: " + str(len(no_password))) test_result = Result.PASS return TestResult(test_result, notes)
def test_secure_communication(): # TODO: check if there is a 'well known' HDP container that either # acts as an intermediate CA (for --tlscacert option), or a server # that clients connect to securely (for --tlscert and tlskey), and # if so, break them into separate tests for better profile coverage logger.debug("Testing for insecure registries.") reason = "No Docker containers found." docker_ps = _get_docker_processes() if docker_ps is None: return TestResult(Result.SKIP, "Docker is not running.") for entry in docker_ps: if '--tlsverify' in entry: if '--tlscert' in entry: if '--tlskey' in entry: if '--tlscacert' in entry: reason = ("Container set to validate certificates, " "has both certificate and key in place, " "and can act as an intermediate CA.") logger.info(reason) else: reason = ("No CA certificate, container cannot act " "as intermediate CA.") logger.info(reason) else: reason = ("A public Certificate exists, but key does not." " Communciation unable to be decrypted.") logger.info(reason) else: reason = ("No certificate available, container will only be" " able to act as client and accept server cert.") else: reason = "Docker is not configured to validate certificates." result = TestResult(Result.FAIL, reason) return result result = TestResult(Result.PASS) return result