From 65c2b737129b5837f4a03660aeb1191ced275a57 Mon Sep 17 00:00:00 2001 From: Alberto Planas Date: Mon, 17 Jan 2022 14:33:20 +0100 Subject: [PATCH] validators: create validators module Move from the config module some validators, and create the validators module. Add tests for them. Signed-off-by: Alberto Planas --- keylime/cloud_verifier_common.py | 4 +- keylime/common/validators.py | 34 +++++++++++++ keylime/config.py | 32 ------------- keylime/ima.py | 15 +++--- keylime/keylime_agent.py | 4 +- test/test_validators.py | 82 ++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 44 deletions(-) create mode 100644 keylime/common/validators.py create mode 100644 test/test_validators.py diff --git a/keylime/cloud_verifier_common.py b/keylime/cloud_verifier_common.py index 6c004b320..94967f928 100644 --- a/keylime/cloud_verifier_common.py +++ b/keylime/cloud_verifier_common.py @@ -16,7 +16,7 @@ from keylime.failure import Failure, Component from keylime.tpm.tpm_main import tpm from keylime.tpm.tpm_abstract import TPM_Utilities -from keylime.common import algorithms +from keylime.common import algorithms, validators from keylime import ima_file_signatures # setup logging @@ -300,7 +300,7 @@ def validate_agent_data(agent_data): lists = json.loads(agent_data['allowlist']) # Validate exlude list contains valid regular expressions - is_valid, _, err_msg = config.valid_exclude_list(lists.get('exclude')) + is_valid, _, err_msg = validators.valid_exclude_list(lists.get('exclude')) if not is_valid: err_msg += " Exclude list regex is misformatted. Please correct the issue and try again." diff --git a/keylime/common/validators.py b/keylime/common/validators.py new file mode 100644 index 000000000..acc67e0a9 --- /dev/null +++ b/keylime/common/validators.py @@ -0,0 +1,34 @@ +"""Validators module.""" +import re + + +def valid_regex(regex): + """Check if string is a valid regular expression.""" + if regex is None: + return True, None, None + + try: + compiled_regex = re.compile(regex) + except re.error as regex_err: + err = "Invalid regex: " + regex_err.msg + "." + return False, None, err + + return True, compiled_regex, None + + +def valid_exclude_list(exclude_list): + """Check if the list is composed of valid regex.""" + if not exclude_list: + return True, None, None + + combined_regex = "(" + ")|(".join(exclude_list) + ")" + return valid_regex(combined_regex) + + +def valid_hex(value): + """Check if the string is a valid hex number representation.""" + try: + int(value, 16) + except Exception: + return False + return True diff --git a/keylime/config.py b/keylime/config.py index db988dfce..0bfaa782c 100644 --- a/keylime/config.py +++ b/keylime/config.py @@ -5,7 +5,6 @@ import os import os.path import configparser -import re from typing import Optional import yaml @@ -179,37 +178,6 @@ def yaml_to_dict(arry, add_newlines=True, logger=None) -> Optional[dict]: return None -def valid_exclude_list(exclude_list): - if not exclude_list: - return True, None, None - - combined_regex = "(" + ")|(".join(exclude_list) + ")" - return valid_regex(combined_regex) - - -def valid_regex(regex): - if regex is None: - return True, None, None - - try: - compiled_regex = re.compile(regex) - except re.error as regex_err: - err = "Invalid regex: " + regex_err.msg + "." - return False, None, err - - return True, compiled_regex, None - - -def valid_hex(value: str): - if not value.isalnum(): - return False - try: - int(value, 16) - return True - except ValueError: - return False - - if STUB_IMA: IMA_ML = '../scripts/ima/ascii_runtime_measurements' else: diff --git a/keylime/ima.py b/keylime/ima.py index d31dc58d1..c892e0ba1 100644 --- a/keylime/ima.py +++ b/keylime/ima.py @@ -15,11 +15,11 @@ from keylime import config from keylime import gpg -from keylime import keylime_logging from keylime import ima_ast -from keylime.agentstates import AgentAttestState from keylime import ima_file_signatures -from keylime.common.algorithms import Hash +from keylime import keylime_logging +from keylime.agentstates import AgentAttestState +from keylime.common import algorithms, validators from keylime.failure import Failure, Component @@ -223,15 +223,14 @@ def _process_measurement_list(agentAttestState, lines, hash_alg, lists=None, m2w allow_list = None exclude_list = None - ima_log_hash_alg = Hash.SHA1 + ima_log_hash_alg = algorithms.Hash.SHA1 if allow_list is not None: try: - ima_log_hash_alg = Hash(allow_list["ima"]["log_hash_alg"]) + ima_log_hash_alg = algorithms.Hash(allow_list["ima"]["log_hash_alg"]) except ValueError: logger.warning("Specified IMA log hash algorithm %s is not a valid algorithm! Defaulting to SHA1.", allow_list["ima"]["log_hash_alg"]) - if boot_aggregates and allow_list: if 'boot_aggregate' not in allow_list['hashes'] : allow_list['hashes']['boot_aggregate'] = [] @@ -240,7 +239,7 @@ def _process_measurement_list(agentAttestState, lines, hash_alg, lists=None, m2w if val not in allow_list['hashes']['boot_aggregate'] : allow_list['hashes']['boot_aggregate'].append(val) - is_valid, compiled_regex, err_msg = config.valid_exclude_list(exclude_list) + is_valid, compiled_regex, err_msg = validators.valid_exclude_list(exclude_list) if not is_valid: # This should not happen as the exclude list has already been validated # by the verifier before acceping it. This is a safety net just in case. @@ -312,7 +311,7 @@ def _process_measurement_list(agentAttestState, lines, hash_alg, lists=None, m2w def process_measurement_list(agentAttestState, lines, lists=None, m2w=None, pcrval=None, ima_keyrings=None, - boot_aggregates=None, hash_alg=Hash.SHA1): + boot_aggregates=None, hash_alg=algorithms.Hash.SHA1): failure = Failure(Component.IMA) try: running_hash, failure = _process_measurement_list(agentAttestState, lines, hash_alg, lists=lists, m2w=m2w, diff --git a/keylime/keylime_agent.py b/keylime/keylime_agent.py index 471240773..bf413283b 100644 --- a/keylime/keylime_agent.py +++ b/keylime/keylime_agent.py @@ -43,7 +43,7 @@ from keylime import secure_mount from keylime import web_util from keylime import api_version as keylime_api_version -from keylime.common import algorithms +from keylime.common import algorithms, validators from keylime.tpm.tpm_main import tpm from keylime.tpm.tpm_abstract import TPM_Utilities from keylime.tpm.tpm2_objects import pubkey_from_tpm2b_public @@ -131,7 +131,7 @@ def do_GET(self): # Sanitization assurance (for tpm.run() tasks below) if not (nonce.isalnum() and - (pcrmask is None or config.valid_hex(pcrmask)) and + (pcrmask is None or validators.valid_hex(pcrmask)) and ima_ml_entry.isalnum()): logger.warning('GET quote returning 400 response. parameters should be strictly alphanumeric') web_util.echo_json_response( diff --git a/test/test_validators.py b/test/test_validators.py new file mode 100644 index 000000000..b925ec38c --- /dev/null +++ b/test/test_validators.py @@ -0,0 +1,82 @@ +import unittest + +from keylime.common import validators + + +class TestValidRegex(unittest.TestCase): + """Tests for valid_regex.""" + + def test_none(self): + """Check that None is a valid regex.""" + self.assertEqual(validators.valid_regex(None), (True, None, None)) + + def test_valid(self): + """Check a well formed regex.""" + value = validators.valid_regex(r"a.*") + self.assertTrue(value[0]) + self.assertEqual(value[1].pattern, r"a.*") + self.assertEqual(value[2], None) + + def test_invalid(self): + """Check a not valid regex.""" + value = validators.valid_regex(r"a[") + self.assertEqual( + value, (False, None, "Invalid regex: unterminated character set.") + ) + + +class TestValidExcludeList(unittest.TestCase): + """Tests for valid_exclude_list.""" + + def test_none(self): + """Check that the empty list is valid.""" + self.assertEqual(validators.valid_exclude_list(None), (True, None, None)) + + def test_single(self): + """Check a single exclude list element.""" + value = validators.valid_exclude_list([r"a.*"]) + self.assertTrue(value[0]) + self.assertEqual(value[1].pattern, r"(a.*)") + self.assertEqual(value[2], None) + + def test_multi(self): + """Check a multiple elements exclude list.""" + value = validators.valid_exclude_list([r"a.*", r"b.*"]) + self.assertTrue(value[0]) + self.assertEqual(value[1].pattern, r"(a.*)|(b.*)") + self.assertEqual(value[2], None) + + def test_invalid(self): + """Check an invalid exclude list.""" + value = validators.valid_exclude_list([r"a["]) + self.assertEqual( + value, (False, None, "Invalid regex: unterminated character set.") + ) + + +class TestValidHex(unittest.TestCase): + """Tests for valid_hex.""" + + def test_none(self): + """Check that None is not valid.""" + self.assertFalse(validators.valid_hex(None)) + + def test_empty(self): + """Check that the empty string is not valid.""" + self.assertFalse(validators.valid_hex("")) + + def test_valid_lower(self): + """Check a valid lower case hexadecimal number.""" + self.assertTrue(validators.valid_hex("123abc")) + + def test_valid_upper(self): + """Check a valid upper case hexadecimal number.""" + self.assertTrue(validators.valid_hex("123ABC")) + + def test_invalid(self): + """Check and invalid hexadecimal number.""" + self.assertFalse(validators.valid_hex("123xyz")) + + +if __name__ == "__main__": + unittest.main()