Example #1
0
def get_policy(mb_refstate_str):
    """ Convert the mb_refstate_str to JSON and get the measured boot policy.
    :param mb_refstate_str: String representation of measured boot reference state
    :returns: Returns the JSON object of the measured boot reference state and the measured boot policy;
              both can be None if mb_refstate_str was empty
    """

    if mb_refstate_str:
        mb_refstate_data = json.loads(mb_refstate_str)
    else:
        mb_refstate_data = None

    if mb_refstate_data:
        mb_policy_name = config.MEASUREDBOOT_POLICYNAME
        #pylint: disable=import-outside-toplevel
        from keylime.elchecking import policies as eventlog_policies
        #pylint: enable=import-outside-toplevel
        mb_policy = eventlog_policies.get_policy(mb_policy_name)
        if mb_policy is None:
            logger.warning(
                "Invalid measured boot policy name %s -- using accept-all instead.",
                mb_policy_name)
            mb_policy = eventlog_policies.AcceptAll()

        mb_pcrs_config = frozenset(config.MEASUREDBOOT_PCRS)
        mb_pcrs_policy = mb_policy.get_relevant_pcrs()
        if not mb_pcrs_policy <= mb_pcrs_config:
            logger.error(
                "Measured boot policy considers PCRs %s, which are not among the configured set %s",
                set(mb_pcrs_policy - mb_pcrs_config), set(mb_pcrs_config))
    else:
        mb_policy = None

    return mb_refstate_data, mb_policy
Example #2
0
    def check_pcrs(self, agent_id, tpm_policy, pcrs, data, virtual,
                   ima_measurement_list, allowlist, ima_keyring,
                   mb_measurement_list, mb_refstate_str):
        try:
            tpm_policy_ = ast.literal_eval(tpm_policy)
        except ValueError:
            tpm_policy_ = {}
        pcr_allowlist = tpm_policy_.copy()

        if mb_refstate_str:
            mb_refstate_data = json.loads(mb_refstate_str)
        else:
            mb_refstate_data = None

        if 'mask' in pcr_allowlist:
            del pcr_allowlist['mask']
        # convert all pcr num keys to integers
        pcr_allowlist = {int(k): v for k, v in list(pcr_allowlist.items())}

        if mb_refstate_data:
            mb_policy_name = config.MEASUREDBOOT_POLICYNAME
            mb_policy = eventlog_policies.get_policy(mb_policy_name)
            if mb_policy is None:
                logger.warning(
                    "Invalid measured boot policy name %s -- using accept-all instead.",
                    mb_policy_name)
                mb_policy = eventlog_policies.AcceptAll()

            mb_pcrs_config = frozenset(config.MEASUREDBOOT_PCRS)
            mb_pcrs_policy = mb_policy.get_relevant_pcrs()
            if not mb_pcrs_policy <= mb_pcrs_config:
                logger.error(
                    "Measured boot policy considers PCRs %s, which are not among the configured set %s",
                    set(mb_pcrs_policy - mb_pcrs_config), set(mb_pcrs_config))

        if mb_refstate_data and mb_measurement_list:
            mb_measurement_data = self.parse_bootlog(mb_measurement_list)
            if not mb_measurement_data:
                logger.error(
                    "Unable to parse measured boot event log. Check previous messages for a reason for error."
                )
                return False
            log_pcrs = mb_measurement_data.get('pcrs')
            if not isinstance(log_pcrs, dict):
                logger.error(
                    "Parse of measured boot event log has unexpected value for .pcrs: %r",
                    log_pcrs)
                return False
            pcrs_sha256 = log_pcrs.get('sha256')
            if (not isinstance(pcrs_sha256, dict)) or not pcrs_sha256:
                logger.error(
                    "Parse of measured boot event log has unexpected value for .pcrs.sha256: %r",
                    pcrs_sha256)
                return False
        else:
            pcrs_sha256 = {}

        pcrsInQuote = set()
        validatedBindPCR = False
        for line in pcrs:
            tokens = line.split()
            if len(tokens) < 3:
                logger.error("Invalid %sPCR in quote: %s", ("", "v")[virtual],
                             pcrs)
                continue

            # always lower case
            pcrval = tokens[2].lower()
            # convert pcr num to number
            try:
                pcrnum = int(tokens[1])
            except Exception:
                logger.error("Invalid PCR number %s", tokens[1])

            if pcrnum == config.TPM_DATA_PCR and data is not None:
                expectedval = self.sim_extend(data)
                if expectedval != pcrval and not config.STUB_TPM:
                    logger.error(
                        "%sPCR #%s: invalid bind data %s from quote does not match expected value %s",
                        ("", "v")[virtual], pcrnum, pcrval, expectedval)
                    return False
                validatedBindPCR = True
                continue

            # check for ima PCR
            if pcrnum == config.IMA_PCR and not config.STUB_TPM:
                if ima_measurement_list is None:
                    logger.error(
                        "IMA PCR in policy, but no measurement list provided")
                    return False

                if self.__check_ima(agent_id, pcrval, ima_measurement_list,
                                    allowlist, ima_keyring):
                    pcrsInQuote.add(pcrnum)
                    continue

                return False

            # PCRs set by measured boot get their own special handling

            if pcrnum in config.MEASUREDBOOT_PCRS:

                if mb_refstate_data:

                    if not mb_measurement_list:
                        logger.error(
                            "Measured Boot PCR %d in policy, but no measurement list provided",
                            pcrnum)
                        return False

                    val_from_log_int = pcrs_sha256.get(str(pcrnum), 0)
                    val_from_log_hex = hex(val_from_log_int)[2:]
                    val_from_log_hex_stripped = val_from_log_hex.lstrip('0')
                    pcrval_stripped = pcrval.lstrip('0')
                    if val_from_log_hex_stripped != pcrval_stripped:
                        logger.error(
                            "For PCR %d and hash SHA256 the boot event log has value %r but the agent returned %r",
                            pcrnum, val_from_log_hex, pcrval)
                        return False
                pcrsInQuote.add(pcrnum)
                continue

            if pcrnum not in list(pcr_allowlist.keys()):
                if not config.STUB_TPM and len(list(tpm_policy.keys())) > 0:
                    logger.warning(
                        "%sPCR #%s in quote not found in %stpm_policy, skipping.",
                        ("", "v")[virtual], pcrnum, ("", "v")[virtual])
                continue
            if pcrval not in pcr_allowlist[pcrnum] and not config.STUB_TPM:
                logger.error(
                    "%sPCR #%s: %s from quote does not match expected value %s",
                    ("", "v")[virtual], pcrnum, pcrval, pcr_allowlist[pcrnum])
                return False

            pcrsInQuote.add(pcrnum)

        if config.STUB_TPM:
            return True

        if not validatedBindPCR:
            logger.error(
                "Binding %sPCR #%s was not included in the quote, but is required",
                ("", "v")[virtual], config.TPM_DATA_PCR)
            return False

        missing = list(set(list(pcr_allowlist.keys())).difference(pcrsInQuote))
        if len(missing) > 0:
            logger.error("%sPCRs specified in policy not in quote: %s",
                         ("", "v")[virtual], missing)
            return False

        if mb_refstate_data:
            missing = list(
                set(config.MEASUREDBOOT_PCRS).difference(pcrsInQuote))
            if len(missing) > 0:
                logger.error(
                    "%sPCRs specified for measured boot not in quote: %s",
                    ("", "v")[virtual], missing)
                return False
            try:
                reason = mb_policy.evaluate(mb_refstate_data,
                                            mb_measurement_data)
            except Exception as exn:
                logger.error(
                    "Boot attestation for agent %s, configured policy %s, refstate=%s, raised Exception %s",
                    agent_id, config.MEASUREDBOOT_POLICYNAME,
                    json.dumps(mb_refstate_data), str(exn))
                reason = ''
            if reason:
                logger.error(
                    "Boot attestation failed for agent %s, configured policy %s, refstate=%s, reason=%s",
                    agent_id, config.MEASUREDBOOT_POLICYNAME,
                    json.dumps(mb_refstate_data), reason)
                return False

        return True