linux/security/integrity/ima/ima_init.c
Jonathan McDowell c0fc127a98 security: ima: rename boot_aggregate when ima is initialised at late_sync
The Linux IMA (Integrity Measurement Architecture) subsystem used for
secure boot, file integrity, or remote attestation cannot be a loadable
module for few reasons listed below:

 o Boot-Time Integrity: IMA’s main role is to measure and appraise files
   before they are used. This includes measuring critical system files
   during early boot (e.g., init, init scripts, login binaries). If IMA
   were a module, it would be loaded too late to cover those.

 o TPM Dependency: IMA integrates tightly with the TPM to record
   measurements into PCRs. The TPM must be initialized early (ideally
   before init_ima()), which aligns with IMA being built-in.

 o Security Model: IMA is part of a Trusted Computing Base (TCB). Making
   it a module would weaken the security model, as a potentially
   compromised system could delay or tamper with its initialization.

IMA must be built-in to ensure it starts measuring from the earliest
possible point in boot which inturn implies TPM must be initialised and
ready to use before IMA.

Unfortunately some TPM drivers (such as Arm FF-A, or SPI attached TPM
devices) are not reliably available during the initcall_late stage,
resulting in a log error:

  ima: No TPM chip found, activating TPM-bypass!

To address this issue, IMA_INIT_LATE_SYNC is introduced.
However, a remote attestation service cannot determine when IMA has been
initialized because the boot_aggregate measurement name remains unchanged,
even though IMA is initialized later at late_initcall_sync when
IMA_INIT_LATE_SYNC is enabled.

Therefore, use a distinct boot_aggregate name when IMA_INIT_LATE_SYNC
is enabled, allowing the remote attestation service to identify
when IMA has been initialized.

Signed-off-by: Jonathan McDowell <noodles@meta.com>
[yeoreum.yun@arm.com: modified to align with the IMA_INIT_LATE_SYNC change]
Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2026-08-04 12:35:02 -04:00

175 lines
4.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
* Authors:
* Reiner Sailer <sailer@watson.ibm.com>
* Leendert van Doorn <leendert@watson.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
* File: ima_init.c
* initialization and cleanup functions
*/
#include <linux/init.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/ima.h>
#include <generated/utsrelease.h>
#include "ima.h"
/* name for boot aggregate entry */
const char boot_aggregate_name[] = "boot_aggregate";
const char boot_aggregate_late_name[] = "boot_aggregate_late";
struct tpm_chip *ima_tpm_chip;
/* Add the boot aggregate to the IMA measurement list and extend
* the PCR register.
*
* Calculate the boot aggregate, a hash over tpm registers 0-7,
* assuming a TPM chip exists, and zeroes if the TPM chip does not
* exist. Add the boot aggregate measurement to the measurement
* list and extend the PCR register.
*
* If a tpm chip does not exist, indicate the core root of trust is
* not hardware based by invalidating the aggregate PCR value.
* (The aggregate PCR value is invalidated by adding one value to
* the measurement list and extending the aggregate PCR value with
* a different value.) Violations add a zero entry to the measurement
* list and extend the aggregate PCR value with ff...ff's.
*/
static int __init ima_add_boot_aggregate(void)
{
static const char op[] = "add_boot_aggregate";
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry;
struct ima_iint_cache tmp_iint, *iint = &tmp_iint;
struct ima_event_data event_data = { .iint = iint };
struct ima_max_digest_data hash;
struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
struct ima_digest_data, hdr);
const char *filename;
int result = -ENOMEM;
int violation = 0;
memset(iint, 0, sizeof(*iint));
memset(&hash, 0, sizeof(hash));
iint->ima_hash = hash_hdr;
iint->ima_hash->algo = ima_hash_algo;
iint->ima_hash->length = hash_digest_size[ima_hash_algo];
if (IS_ENABLED(CONFIG_IMA_INIT_LATE_SYNC))
filename = boot_aggregate_late_name;
else
filename = boot_aggregate_name;
event_data.filename = filename;
/*
* With TPM 2.0 hash agility, TPM chips could support multiple TPM
* PCR banks, allowing firmware to configure and enable different
* banks. The SHA1 bank is not necessarily enabled.
*
* Use the same hash algorithm for reading the TPM PCRs as for
* calculating the boot aggregate digest. Preference is given to
* the configured IMA default hash algorithm. Otherwise, use the
* TCG required banks - SHA256 for TPM 2.0, SHA1 for TPM 1.2.
* Ultimately select SHA1 also for TPM 2.0 if the SHA256 PCR bank
* is not found.
*/
if (ima_tpm_chip) {
result = ima_calc_boot_aggregate(hash_hdr);
if (result < 0) {
audit_cause = "hashing_error";
goto err_out;
}
}
result = ima_alloc_init_template(&event_data, &entry, NULL);
if (result < 0) {
audit_cause = "alloc_entry";
goto err_out;
}
result = ima_store_template(entry, violation, NULL,
filename,
CONFIG_IMA_MEASURE_PCR_IDX);
if (result < 0) {
ima_free_template_entry(entry);
audit_cause = "store_entry";
goto err_out;
}
return 0;
err_out:
integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, filename, op,
audit_cause, result, 0);
return result;
}
#ifdef CONFIG_IMA_LOAD_X509
void __init ima_load_x509(void)
{
int unset_flags = ima_policy_flag & IMA_APPRAISE;
ima_policy_flag &= ~unset_flags;
integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
/* load also EVM key to avoid appraisal */
evm_load_x509();
ima_policy_flag |= unset_flags;
}
#endif
int __init ima_init(void)
{
int rc;
ima_tpm_chip = tpm_default_chip();
if (!ima_tpm_chip)
pr_info("No TPM chip found, activating TPM-bypass!\n");
rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
if (rc)
return rc;
rc = ima_init_crypto();
if (rc)
return rc;
rc = ima_init_template();
if (rc != 0)
return rc;
/* It can be called before ima_init_digests(), it does not use TPM. */
ima_load_kexec_buffer();
rc = ima_init_digests();
if (rc != 0)
return rc;
rc = ima_init_htable();
if (rc != 0)
return rc;
rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
if (rc != 0)
return rc;
ima_init_policy();
rc = ima_fs_init();
if (rc != 0)
return rc;
ima_init_key_queue();
ima_init_reboot_notifier();
ima_measure_critical_data("kernel_info", "kernel_version",
UTS_RELEASE, strlen(UTS_RELEASE), false,
NULL, 0);
return rc;
}