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
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