예제 #1
0
def validate_agent_data(agent_data):
    if agent_data is None:
        return False, None

    # validate that the allowlist is proper JSON
    lists = json.loads(agent_data['allowlist'])

    # Validate exlude list contains valid regular expressions
    is_valid, _, err_msg = config.valid_exclude_list(lists.get('exclude'))
    if not is_valid:
        err_msg += " Exclude list regex is misformatted. Please correct the issue and try again."

    return is_valid, err_msg
예제 #2
0
파일: ima.py 프로젝트: polymath-is/keylime
def process_measurement_list(lines, lists=None, m2w=None, pcrval=None):
    errs = [0, 0, 0, 0]
    runninghash = START_HASH
    found_pcr = (pcrval is None)

    if lists is not None:
        lists = ast.literal_eval(lists)
        allowlist = lists['allowlist']
        exclude_list = lists['exclude']
    else:
        allowlist = None
        exclude_list = None

    is_valid, compiled_regex, err_msg = config.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.
        err_msg += " Exclude list will be ignored."
        logger.error(err_msg)

    for line in lines:
        line = line.strip()
        if line == '':
            continue

        tokens = line.split(None, 4)
        if len(tokens) != 5:
            logger.error("invalid measurement list file line: -%s-" % (line))
            return None

        # print tokens
        # pcr = tokens[0]
        template_hash = codecs.decode(tokens[1], 'hex')
        mode = tokens[2]

        if mode == "ima-ng":
            filedata_hash, path, error = _extract_from_ima_ng(
                tokens, template_hash)
        elif mode == 'ima':
            filedata_hash, path, error = _extract_from_ima(
                tokens, template_hash)
        else:
            raise Exception("unsupported ima template mode: %s" % mode)

        errs[0] += error
        if template_hash == START_HASH:
            template_hash = FF_HASH

        # update hash
        runninghash = hashlib.sha1(runninghash + template_hash).digest()

        if not found_pcr:
            found_pcr = \
                (codecs.encode(runninghash, 'hex').decode('utf-8') == pcrval)

        # write out the new hash
        if m2w is not None:
            m2w.write(
                "%s %s\n" %
                (codecs.encode(filedata_hash, 'hex').decode('utf-8'), path))

        if allowlist is not None:

            # just skip if it is a weird overwritten path
            if template_hash == FF_HASH:
                # print "excluding ffhash %s"%path
                continue

            # determine if path matches any exclusion list items
            if compiled_regex is not None and compiled_regex.match(path):
                logger.debug("IMA: ignoring excluded path %s" % path)
                continue

            accept_list = allowlist.get(path, None)
            if accept_list is None:
                logger.warning("File not found in allowlist: %s" % (path))
                errs[1] += 1
                continue

            # print('codecs.encode', codecs.encode(filedata_hash, 'hex').decode('utf-8'))
            # print('accept_list:', accept_list)
            if codecs.encode(filedata_hash,
                             'hex').decode('utf-8') not in accept_list:
                logger.warning(
                    "Hashes for file %s don't match %s not in %s" %
                    (path, codecs.encode(filedata_hash,
                                         'hex').decode('utf-8'), accept_list))
                errs[2] += 1
                continue

        errs[3] += 1

    # check PCR value has been found
    if not found_pcr:
        logger.error("IMA measurement list does not match TPM PCR %s" % pcrval)
        return None

    # clobber the retval if there were IMA file errors
    if sum(errs[:3]) > 0:
        logger.error("IMA ERRORS: template-hash %d fnf %d hash %d good %d" %
                     tuple(errs))
        return None

    return codecs.encode(runninghash, 'hex').decode('utf-8')
예제 #3
0
def _process_measurement_list(agentAttestState,
                              lines,
                              lists=None,
                              m2w=None,
                              pcrval=None,
                              ima_keyring=None,
                              boot_aggregates=None):
    running_hash = agentAttestState.get_pcr_state(config.IMA_PCR)
    found_pcr = (pcrval is None)
    errors = {}
    pcrval_bytes = b''
    if pcrval is not None:
        pcrval_bytes = codecs.decode(pcrval.encode('utf-8'), 'hex')

    if lists is not None:
        if isinstance(lists, str):
            lists = ast.literal_eval(lists)
        allow_list = lists['allowlist']
        exclude_list = lists['exclude']
    else:
        allow_list = None
        exclude_list = None

    if boot_aggregates and allow_list:
        if 'boot_aggregate' not in allow_list['hashes']:
            allow_list['hashes']['boot_aggregate'] = []
        for alg in boot_aggregates.keys():
            for val in boot_aggregates[alg]:
                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)
    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.
        err_msg += " Exclude list will be ignored."
        logger.error(err_msg)

    ima_validator = ima_ast.Validator({
        ima_ast.ImaSig:
        functools.partial(_validate_ima_sig, compiled_regex, ima_keyring,
                          allow_list),
        ima_ast.ImaNg:
        functools.partial(_validate_ima_ng, compiled_regex, allow_list),
        ima_ast.Ima:
        functools.partial(_validate_ima_ng, compiled_regex, allow_list),
        ima_ast.ImaBuf:
        functools.partial(_validate_ima_buf, compiled_regex, allow_list),
    })

    for linenum, line in enumerate(lines):
        line = line.strip()
        if line == '':
            continue

        try:
            entry = ima_ast.Entry(line, ima_validator)

            # update hash
            running_hash = hashlib.sha1(running_hash +
                                        entry.template_hash).digest()

            if not entry.valid():
                errors[type(entry.mode)] = errors.get(type(entry.mode), 0) + 1

            if not found_pcr:
                # End of list should equal pcr value
                found_pcr = (running_hash == pcrval_bytes)
                if found_pcr:
                    logger.debug('Found match at linenum %s' % (linenum + 1))
                    # We always want to have the very last line for the attestation, so
                    # we keep the previous runninghash, which is not the last one!
                    agentAttestState.update_ima_attestation(
                        int(entry.pcr), running_hash, linenum + 1)

            # Keep old functionality for writing the parsed files with hashes into a file
            if m2w is not None and (type(entry.mode) in [
                    ima_ast.Ima, ima_ast.ImaNg, ima_ast.ImaSig
            ]):
                hash_value = codecs.encode(entry.mode.digest.hash, "hex")
                path = entry.mode.path.name
                m2w.write(f"{hash_value} {path}\n")
        except ima_ast.ParserError:
            logger.error(
                f"Line was not parsable into a valid IMA entry: {line}")

    # iterative attestation may send us no log; compare last know PCR 10 state
    # against current PCR state
    if not found_pcr:
        found_pcr = (running_hash == pcrval_bytes)

    # check PCR value has been found
    if not found_pcr:
        logger.error("IMA measurement list does not match TPM PCR %s" % pcrval)
        return None

    # Check if any validators failed
    if sum(errors.values()) > 0:
        error_msg = "IMA ERRORS: Some entries couldn't be validated. Number of failures in modes: "
        error_msg += ", ".join(
            [f'{k.__name__ } {v}' for k, v in errors.items()])
        logger.error(error_msg + ".")
        return None

    return codecs.encode(running_hash, 'hex').decode('utf-8')