Skip to content

第一百七十四章:完整性度量

渡劫期

涉及内核源码:

审计回廊之外,林小源来到一座白色的塔楼前。塔楼的表面光滑如镜,没有任何装饰——但仔细看去,镜面上浮现出一串串哈希值,像指纹一样密密麻麻地覆盖着每一块砖石。

一个全身裹在白色长袍中的修士从塔楼中走出,长袍上绣着 "IMA" 三个字母。

"Integrity Measurement Architecture,"修士的声音平静而坚定,"我不关心谁能访问什么——那是 SELinux 的事。我只关心一件事:文件有没有被篡改。"

林小源伸手触碰塔楼的砖石。每一块砖石上都刻着一个 SHA256 哈希值——那是文件在某个时刻的数字指纹。

"每次文件被执行或被访问,"IMA 修士说,"我都会计算它的哈希,记录到 TPM 的度量日志中。如果文件被修改——哪怕只改了一个字节——哈希就对不上了。"

"这和审计有什么区别?"

"审计记录'谁做了什么',我记录'文件是否完整'。审计是事后追溯,我是实时验证。"

"更准确地说,度量列表不是一张普通账本。"IMA 修士指向塔底,"TCG 运行时 IMA 会维护 executables 和敏感文件的 hash 列表,策略还可以受 LSM 数据约束。度量结果可以放进不同模板,不只是文件名和摘要,也能带 inode UID/GID、LSM label、签名、xattr 等字段。"

破关试炼

白塔初试

IMA 度量列表可以通过模板扩展,除摘要和文件名外还能记录哪类安全标签信息?

答对后才能继续滑动和进入下一章。
c
/*
 * IMA (Integrity Measurement Architecture):
 *
 * 功能:
 *   度量 — 计算文件哈希,记录到 TPM
 *   评估 — 验证文件是否被篡改
 *   审计 — 记录完整性事件
 *
 * IMA 策略:
 *   measure — 度量文件哈希
 *   appraise — 验证文件完整性
 *   audit — 记录事件
 *
 * 策略规则:
 *   measure func=FILE_MMAP mask=MAY_EXEC
 *   appraise func=BPRM_CHECK
 *
 * EVM (Extended Verification Module):
 *   保护文件元数据
 *   验证 security xattr
 *   防止权限提升
 *
 * dm-verity:
 *   块设备完整性
 *   保护整个文件系统
 *   用于 Android Verified Boot
 */

/* 模拟完整性度量 */
struct integrity_record {
    char file[128];
    char hash[65]; /* SHA256 */
    int status; /* 0=unknown, 1=valid, 2=invalid */
};

struct ima_log {
    struct integrity_record records[10];
    int count;
};

void ima_measure(struct ima_log *log, const char *file, const char *hash) {
    if (log->count >= 10) return;
    strncpy(log->records[log->count].file, file, 127);
    strncpy(log->records[log->count].hash, hash, 64);
    log->records[log->count].status = 1; /* valid */
    log->count++;
}

int ima_appraise(struct ima_log *log, const char *file, const char *hash) {
    for (int i = 0; i < log->count; i++) {
        if (strcmp(log->records[i].file, file) == 0) {
            if (strcmp(log->records[i].hash, hash) == 0)
                return 1; /* 完整 */
            return 0; /* 被篡改 */
        }
    }
    return -1; /* 未度量 */
}

void print_record(struct integrity_record *r) {
    printf("  文件: %s\n", r->file);
    printf("  哈希: %s\n", r->hash);
    printf("  状态: %s\n",
           r->status == 1 ? "有效" : r->status == 2 ? "无效" : "未知");
}

printf("=== 完整性度量 — 防止篡改 ===\n\n");

printf("IMA/EVM 确保文件完整性:\n\n");

/* 创建 IMA 日志 */
struct ima_log log = { .count = 0 };

/* 度量文件 */
ima_measure(&log, "/usr/bin/cat",
            "abc123def456789012345678901234567890123456789012345678901234abcd");
ima_measure(&log, "/usr/bin/ls",
            "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");

printf("--- IMA 度量日志 ---\n");
for (int i = 0; i < log.count; i++) {
    print_record(&log.records[i]);
    printf("\n");
}

printf("--- 完整性验证 ---\n");
/* 验证文件 */
int r1 = ima_appraise(&log, "/usr/bin/cat",
                      "abc123def456789012345678901234567890123456789012345678901234abcd");
printf("/usr/bin/cat: %s\n", r1 == 1 ? "完整" : "被篡改");

int r2 = ima_appraise(&log, "/usr/bin/ls",
                      "0000000000000000000000000000000000000000000000000000000000000000");
printf("/usr/bin/ls: %s\n", r2 == 1 ? "完整" : "被篡改");

printf("\n--- IMA 策略 ---\n");
printf("measure:\n");
printf("  计算文件哈希\n");
printf("  记录到 TPM\n\n");
printf("appraise:\n");
printf("  验证文件完整性\n");
printf("  拒绝被篡改的文件\n\n");
printf("audit:\n");
printf("  记录完整性事件\n\n");

printf("--- EVM ---\n");
printf("Extended Verification Module:\n");
printf("  保护文件元数据\n");
printf("  验证 security xattr\n");
printf("  防止权限提升\n\n");

printf("--- dm-verity ---\n");
printf("块设备完整性:\n");
printf("  保护整个文件系统\n");
printf("  使用 Merkle 树\n");
printf("  Android Verified Boot\n\n");

printf("--- 查看 IMA ---\n");
printf("cat /sys/kernel/security/ima/policy\n");
printf("cat /sys/kernel/security/integrity/ima/ascii_runtime_measurements\n");

塔楼内部,林小源看到两个并排的房间。左边写着 "measure",右边写着 "appraise"。

"度量和评估,"IMA 修士推开左边的门,"度量是被动的——计算哈希,记录到 TPM 日志。文件照样执行,只是多了一条记录。"他推开右边的门,"评估是主动的——验证哈希是否匹配。如果文件被篡改,直接拒绝执行。"

林小源走进右边的房间,看到一面巨大的验证墙。每个文件执行前都要经过这面墙——墙上的哈希比对引擎会将当前文件的哈希与记录的哈希进行比较。

"不匹配就拒绝?"

"对。被篡改的文件无法执行。这就是 appraise 策略——最严格的完整性保护。"

"但你们不只做 IMA 对吧?"

IMA 修士点了点头,指向塔楼更高处:"EVM——Extended Verification Module。IMA 保护文件内容,EVM 保护文件的元数据。SELinux 的标签、文件的权限位——这些元数据如果被篡改,攻击者就能绕过访问控制。EVM 用 HMAC 签名保护这些元数据。"

"策略也不是只写 measure 和 appraise。"修士展开一卷白绢,"动作有 measuredont_measureappraisedont_appraiseauditdont_audithashdont_hash。条件可以看 func=BPRM_CHECKFILE_MMAPMODULE_CHECKFIRMWARE_CHECKKEY_CHECK,也可以看 uid、euid、文件所有者、文件系统 UUID,甚至看 SELinux 的 subj/obj 标签。"

"默认策略呢?"

"会度量可执行文件、可执行 mmap、root 读取的文件、模块和固件;appraisal 则可以要求 root 拥有的文件通过完整性检查。策略写入 securityfs 的 ima/policy,关闭文件后生效。"

破关试炼

策略之试

IMA policy 中用于检查程序执行路径的 func 名称是什么?

答对后才能继续滑动和进入下一章。

塔楼的最高层,林小源透过窗户看到了远处的 Android 设备。设备的系统分区被一层金色的光网覆盖——那是 dm-verity 的 Merkle 树。

"块设备级别的完整性,"IMA 修士站在他身旁,"dm-verity 保护整个文件系统。每一个数据块都有一个哈希,哈希组成 Merkle 树,树根存在启动分区中。启动时验证整棵树——任何一块数据被篡改,树根就不匹配。"

林小源看着那棵巨大的 Merkle 树在阳光下闪烁。叶子节点是数据块的哈希,中间节点是子节点哈希的哈希,根节点是最终的信任锚。

"Android Verified Boot,"他说,"所以 Android 设备如果系统被篡改,就无法启动。"

"这就是信任链,"IMA 修士说,"从硬件信任根开始,经过 bootloader、内核、initramfs、文件系统——每一层都验证下一层。任何一环断裂,整个链条崩溃。"

"还有一个容易混淆的地方。"修士补充道,"IMA 更偏运行时度量和本地 appraisal;dm-verity 是块设备级别的只读完整性验证。它们可以同在一条信任链上,但检查粒度和位置不同。IMA 还能度量 keyring 里的 key,比如只度量 .builtin_trusted_keys.ima keyring。"

林小源转身走下塔楼。每一步都踩在刻满哈希值的砖石上,那些数字指纹像无数只眼睛,注视着每一个经过的文件。完整性不是一道门,而是一张网——从文件内容到元数据,从内存到磁盘,无所不包。

破关试炼

链根之试

dm-verity 使用哪种哈希树来保护块设备完整性?

答对后才能继续滑动和进入下一章。

道藏笔记

内核启示

审计记录"谁做了什么",IMA 记录"文件有没有被改"。IMA 做度量、appraisal、audit/hash 等动作;策略可以按 BPRM_CHECK、FILE_MMAP、MODULE_CHECK、FIRMWARE_CHECK、KEY_CHECK,也可以按 uid、文件所有者、文件系统、LSM 标签等条件生效。模板还能把摘要、文件名、签名、xattr、inode UID/GID、LSM label 等信息放进 measurement list。

光保文件内容还不够,EVM 保护文件元数据和 security xattr。再往底层走,dm-verity 保护整个块设备,每个数据块一个哈希组成 Merkle 树。IMA 偏运行时度量和本地 appraisal,dm-verity 偏块设备只读完整性验证;二者可以共同服务可信启动和运行时信任链。

完整性度量是克星——篡改无处遁形。


破关试炼

完整性度量之试

完整性度量一章中,计算文件哈希并记录到 TPM 度量日志的架构缩写是什么?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道