Skip to content

Commit 4adfd35

Browse files
committed
checks: avoid ok verdicts for partial evidence
1 parent 7e1d83e commit 4adfd35

5 files changed

Lines changed: 64 additions & 28 deletions

File tree

src/check_bios_boot.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@ static size_t check_efivars_boot(check_result_t *results, size_t max_results) {
7070
bool usb_in_order = false;
7171
bool net_in_order = false;
7272
bool cd_in_order = false;
73+
size_t unreadable = 0;
7374

7475
for (size_t i = 0; i < order.order_count; i++) {
7576
bythos_efi_boot_entry_t entry = {0};
7677
if (!read_boot_entry(order.order[i], &entry)) {
78+
unreadable++;
7779
continue;
7880
}
7981

@@ -98,18 +100,24 @@ static size_t check_efivars_boot(check_result_t *results, size_t max_results) {
98100

99101
if (usb_in_order) {
100102
EMIT("EFI USB boot", CHECK_WARN, "active entry in EFI boot order");
103+
} else if (unreadable > 0) {
104+
EMIT_SKIP("EFI USB boot", SKIP_OUTPUT_UNPARSEABLE, "some boot entries unreadable; USB presence unconfirmed");
101105
} else {
102106
EMIT("EFI USB boot", CHECK_OK, "no active entry in EFI boot order");
103107
}
104108

105109
if (net_in_order) {
106110
EMIT("EFI network boot", CHECK_WARN, "active entry in EFI boot order");
111+
} else if (unreadable > 0) {
112+
EMIT_SKIP("EFI network boot", SKIP_OUTPUT_UNPARSEABLE, "some boot entries unreadable; network presence unconfirmed");
107113
} else {
108114
EMIT("EFI network boot", CHECK_OK, "no active entry in EFI boot order");
109115
}
110116

111117
if (cd_in_order) {
112118
EMIT("EFI CD/DVD boot", CHECK_WARN, "active entry in EFI boot order");
119+
} else if (unreadable > 0) {
120+
EMIT_SKIP("EFI CD/DVD boot", SKIP_OUTPUT_UNPARSEABLE, "some boot entries unreadable; CD/DVD presence unconfirmed");
113121
} else {
114122
EMIT("EFI CD/DVD boot", CHECK_OK, "no active entry in EFI boot order");
115123
}

src/check_bolt.c

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,34 @@
1111

1212
static const char *const BOLT_SYSFS_BASE = "/sys/bus/thunderbolt/devices";
1313

14-
static bool read_tb_domain_attr(const char *attr, char *buffer, size_t size) {
14+
typedef enum {
15+
TB_DMA_UNREADABLE,
16+
TB_DMA_ALL_ON,
17+
TB_DMA_SOME_OFF,
18+
} tb_dma_state_t;
19+
20+
static tb_dma_state_t read_tb_dma_state(void) {
1521
DIR *dir = opendir(BOLT_SYSFS_BASE);
16-
if (dir == NULL) return false;
22+
if (dir == NULL) return TB_DMA_UNREADABLE;
1723

24+
size_t readable = 0, unprotected = 0;
1825
struct dirent *entry;
1926
while ((entry = bythos_readdir_safe(dir, NULL)) != NULL) {
2027
if (strncmp(entry->d_name, "domain", 6) != 0) continue;
2128

2229
char path[PATH_MAX];
23-
if (snprintf(path, sizeof(path), "%s/%s/%s",
24-
BOLT_SYSFS_BASE, entry->d_name, attr) >= (int)sizeof(path)) continue;
30+
if (snprintf(path, sizeof(path), "%s/%s/iommu_dma_protection",
31+
BOLT_SYSFS_BASE, entry->d_name) >= (int)sizeof(path)) continue;
2532

26-
if (bythos_read_file_text(path, buffer, size)) {
27-
closedir(dir);
28-
return true;
29-
}
33+
char val[8] = {0};
34+
if (!bythos_read_file_text(path, val, sizeof(val))) continue;
35+
readable++;
36+
if (strcmp(bythos_trim(val), "1") != 0) unprotected++;
3037
}
3138

3239
closedir(dir);
33-
return false;
40+
if (readable == 0) return TB_DMA_UNREADABLE;
41+
return unprotected > 0 ? TB_DMA_SOME_OFF : TB_DMA_ALL_ON;
3442
}
3543

3644
static bool tb_controller_present(void) {
@@ -57,16 +65,16 @@ size_t bythos_check_bolt_dma(check_result_t *results, size_t max_results) {
5765
return used;
5866
}
5967

60-
char val[8] = {0};
61-
if (!read_tb_domain_attr("iommu_dma_protection", val, sizeof(val))) {
68+
switch (read_tb_dma_state()) {
69+
case TB_DMA_UNREADABLE:
6270
EMIT_SKIP_FEATURE("Thunderbolt DMA protection", "iommu_dma_protection");
63-
} else {
64-
char *v = bythos_trim(val);
65-
if (strcmp(v, "1") == 0) {
66-
EMIT("Thunderbolt DMA protection", CHECK_OK, "pre-boot DMA active");
67-
} else {
68-
EMIT("Thunderbolt DMA protection", CHECK_WARN, "pre-boot DMA inactive");
69-
}
71+
break;
72+
case TB_DMA_ALL_ON:
73+
EMIT("Thunderbolt DMA protection", CHECK_OK, "pre-boot DMA active");
74+
break;
75+
case TB_DMA_SOME_OFF:
76+
EMIT("Thunderbolt DMA protection", CHECK_WARN, "pre-boot DMA inactive on at least one domain");
77+
break;
7078
}
7179

7280
return used;

src/check_dci.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,9 @@ size_t bythos_check_dci(check_result_t *results, size_t max_results) {
8484
size_t bythos_check_chipsec(check_result_t *results, size_t max_results) {
8585
size_t used = 0;
8686

87-
if (bythos_command_exists("chipsec_main")) {
88-
EMIT("platform firmware deep audit", CHECK_OK, "available: chipsec_main");
89-
} else if (bythos_command_exists("chipsec")) {
90-
EMIT("platform firmware deep audit", CHECK_OK, "available: chipsec");
87+
if (bythos_command_exists("chipsec_main") || bythos_command_exists("chipsec")) {
88+
EMIT_SKIP("platform firmware deep audit", SKIP_NOT_CONFIGURED,
89+
"chipsec available; run manually for a deep audit");
9190
} else {
9291
EMIT_SKIP_TOOL_INSTALL("platform firmware deep audit", "chipsec");
9392
}

src/check_luks.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@ size_t bythos_check_luks(check_result_t *results, size_t max_results) {
3030
{
3131
char buffer[8192] = {0};
3232
int status = -1;
33+
bool truncated = false;
3334
bythos_lsblk_posture_t posture = {0};
3435

3536
if (!bythos_command_exists("lsblk")) {
3637
EMIT_SKIP_TOOL_INSTALL("LUKS block devices", "util-linux");
37-
} else if (!bythos_capture_argv_status(lsblk_argv, buffer, sizeof(buffer), &status)) {
38+
} else if (!bythos_capture_argv_status_ex(lsblk_argv, buffer, sizeof(buffer), &status, &truncated)) {
3839
EMIT("LUKS block devices", CHECK_WARN, "unable to inspect block devices");
3940
} else if (status != 0) {
4041
EMIT("LUKS block devices", CHECK_WARN, "lsblk inspection failed");
42+
} else if (truncated) {
43+
EMIT("LUKS block devices", CHECK_WARN, "lsblk output truncated; device list incomplete");
4144
} else {
4245
bythos_parse_lsblk_posture(buffer, &posture);
4346
if (posture.luks_count > 0) {
@@ -84,6 +87,7 @@ size_t bythos_check_luks(check_result_t *results, size_t max_results) {
8487
};
8588
char lsblk_buf[4096] = {0};
8689
int lsblk_status = -1;
90+
bool lsblk_truncated = false;
8791
bool tpm_present = bythos_file_exists("/sys/class/tpm/tpm0");
8892

8993
if (!bythos_command_exists("lsblk")) {
@@ -98,14 +102,20 @@ size_t bythos_check_luks(check_result_t *results, size_t max_results) {
98102
EMIT_SKIP_TOOL_INSTALL("LUKS TPM binding", "cryptsetup");
99103
EMIT_SKIP_TOOL_INSTALL("LUKS Secure Boot binding", "cryptsetup");
100104
EMIT_SKIP_TOOL_INSTALL("LUKS boot chain binding", "cryptsetup");
101-
} else if (!bythos_capture_argv_status(lsblk_fstype_argv, lsblk_buf,
102-
sizeof(lsblk_buf), &lsblk_status) ||
105+
} else if (!bythos_capture_argv_status_ex(lsblk_fstype_argv, lsblk_buf,
106+
sizeof(lsblk_buf), &lsblk_status, &lsblk_truncated) ||
103107
lsblk_status != 0) {
104108
EMIT_SKIP_EXEC("LUKS version", "lsblk");
105109
EMIT_SKIP_EXEC("LUKS dm-integrity", "lsblk");
106110
EMIT_SKIP_EXEC("LUKS TPM binding", "lsblk");
107111
EMIT_SKIP_EXEC("LUKS Secure Boot binding", "lsblk");
108112
EMIT_SKIP_EXEC("LUKS boot chain binding", "lsblk");
113+
} else if (lsblk_truncated) {
114+
EMIT_SKIP("LUKS version", SKIP_OUTPUT_UNPARSEABLE, "lsblk output truncated");
115+
EMIT_SKIP("LUKS dm-integrity", SKIP_OUTPUT_UNPARSEABLE, "lsblk output truncated");
116+
EMIT_SKIP("LUKS TPM binding", SKIP_OUTPUT_UNPARSEABLE, "lsblk output truncated");
117+
EMIT_SKIP("LUKS Secure Boot binding", SKIP_OUTPUT_UNPARSEABLE, "lsblk output truncated");
118+
EMIT_SKIP("LUKS boot chain binding", SKIP_OUTPUT_UNPARSEABLE, "lsblk output truncated");
109119
} else {
110120
size_t luks_found = 0;
111121
size_t luks_no_token = 0;

src/check_microcode.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ size_t bythos_check_microcode(check_result_t *results, size_t max_results) {
1414
const char *cpuinfo_path = "/proc/cpuinfo";
1515

1616
{
17+
bythos_cpu_vendor_t vendor = bythos_cpu_vendor();
1718
char microcode_line[256] = {0};
1819
char revision[128] = {0};
1920

20-
if (!bythos_first_line_with_prefix(cpuinfo_path, "microcode", microcode_line, sizeof(microcode_line))) {
21+
if (vendor != BYTHOS_CPU_VENDOR_INTEL && vendor != BYTHOS_CPU_VENDOR_AMD) {
22+
EMIT_SKIP_VENDOR("CPU microcode", "x86-only check");
23+
} else if (!bythos_first_line_with_prefix(cpuinfo_path, "microcode", microcode_line, sizeof(microcode_line))) {
2124
EMIT("CPU microcode", CHECK_WARN, "revision not visible");
2225
} else if (bythos_extract_microcode_revision(microcode_line, revision, sizeof(revision))) {
2326
char detail[160];
@@ -35,7 +38,7 @@ size_t bythos_check_microcode(check_result_t *results, size_t max_results) {
3538
EMIT_SKIP("CPU vulnerabilities", SKIP_FEATURE_ABSENT,
3639
"kernel vulnerabilities sysfs not exposed");
3740
} else {
38-
size_t total = 0, vulnerable = 0;
41+
size_t total = 0, vulnerable = 0, unknown = 0;
3942
char first_vuln[64] = {0};
4043
struct dirent *entry;
4144
while ((entry = bythos_readdir_safe(d, NULL)) != NULL) {
@@ -53,6 +56,8 @@ size_t bythos_check_microcode(check_result_t *results, size_t max_results) {
5356
if (first_vuln[0] == '\0') {
5457
snprintf(first_vuln, sizeof(first_vuln), "%.60s", entry->d_name);
5558
}
59+
} else if (strncmp(lower, "unknown", 7) == 0) {
60+
unknown++;
5661
}
5762
}
5863
closedir(d);
@@ -64,6 +69,11 @@ size_t bythos_check_microcode(check_result_t *results, size_t max_results) {
6469
snprintf(detail, sizeof(detail), "%zu of %zu vulnerable (e.g. %s)",
6570
vulnerable, total, first_vuln);
6671
EMIT("CPU vulnerabilities", CHECK_WARN, detail);
72+
} else if (unknown > 0) {
73+
char detail[BYTHOS_DETAIL_MAX];
74+
snprintf(detail, sizeof(detail),
75+
"%zu of %zu indeterminate (kernel reports unknown)", unknown, total);
76+
EMIT("CPU vulnerabilities", CHECK_WARN, detail);
6777
} else {
6878
char detail[BYTHOS_DETAIL_MAX];
6979
snprintf(detail, sizeof(detail), "%zu checks; all mitigated or not affected",
@@ -74,7 +84,8 @@ size_t bythos_check_microcode(check_result_t *results, size_t max_results) {
7484
}
7585

7686
if (bythos_command_exists("spectre-meltdown-checker")) {
77-
EMIT("CPU vulnerability scan", CHECK_OK, "available: spectre-meltdown-checker");
87+
EMIT_SKIP("CPU vulnerability scan", SKIP_NOT_CONFIGURED,
88+
"spectre-meltdown-checker available; run manually for a deep scan");
7889
} else {
7990
EMIT_SKIP_TOOL_INSTALL("CPU vulnerability scan", "spectre-meltdown-checker");
8091
}

0 commit comments

Comments
 (0)