Skip to content

Add UEFI log handler, use it to send UEFI count #1022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ actix-web = { version = "4", default-features = false, features = ["macros", "o
anyhow = { version = "1.0", features = ["backtrace"] }
assert_cmd = { version = "2.0.16" }
base64 = "0.22"
byteorder = "1.5.0"
cfg-if = "1"
chrono = { version = "0.4.40", features = ["serde"] }
clap = { version = "4.5", features = ["derive"] }
Expand All @@ -42,6 +43,8 @@ reqwest = {version = "0.12", default-features = false, features = ["json", "nati
serde = "1.0.80"
serde_derive = "1.0.80"
serde_json = { version = "1.0", features = ["raw_value"] }
sha1 = "0.10.6"
sha2 = "0.10.9"
signal-hook = "0.3"
static_assertions = "1"
tempfile = "3.4.0"
Expand Down
53 changes: 30 additions & 23 deletions keylime-push-model-agent/src/struct_filler.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Keylime Authors
use keylime::algorithms::HashAlgorithm;
use keylime::config::KeylimeConfig;
use keylime::config::PushModelConfigTrait;
use keylime::context_info::ContextInfo;
use keylime::structures;
use keylime::uefi::uefi_log_handler;
use log::error;

pub trait StructureFiller {
Expand Down Expand Up @@ -46,11 +48,32 @@ impl StructureFiller for FillerFromHardware<'_> {

pub struct FillerFromHardware<'a> {
pub tpm_context_info: &'a mut ContextInfo,
pub uefi_log_handler: Option<uefi_log_handler::UefiLogHandler>,
}

impl<'a> FillerFromHardware<'a> {
pub fn new(tpm_context_info: &'a mut ContextInfo) -> Self {
FillerFromHardware { tpm_context_info }
// TODO: Change config obtaining here to avoid repetitions
let global_config = KeylimeConfig::new();
let ml_path = match global_config {
Ok(config) => config.agent.measuredboot_ml_path.clone(),
Err(_) => "".to_string(),
};
let uefi_log_handler =
uefi_log_handler::UefiLogHandler::new(&ml_path);
match uefi_log_handler {
Ok(handler) => FillerFromHardware {
tpm_context_info,
uefi_log_handler: Some(handler),
},
Err(e) => {
error!("Failed to create UEFI log handler: {}", e);
FillerFromHardware {
tpm_context_info,
uefi_log_handler: None,
}
}
}
}
// TODO: Change this function to use the attestation request appropriately
// Add self to the function signature to use the tpm_context
Expand All @@ -71,6 +94,10 @@ impl<'a> FillerFromHardware<'a> {
error!("Failed to get PCR banks for SHA256");
vec![]
});
let uefi_count = self
.uefi_log_handler
.as_ref()
.map_or(0, |handler| handler.get_entry_count());
structures::AttestationRequest {
data: structures::RequestData {
type_: "attestation".to_string(),
Expand Down Expand Up @@ -101,17 +128,7 @@ impl<'a> FillerFromHardware<'a> {
evidence_type: "uefi_log".to_string(),
capabilities: structures::LogCapabilities {
evidence_version: Some(config.get_uefi_logs_evidence_version()),
entry_count: keylime::file_ops::read_file(config.get_measuredboot_ml_count_file().as_str())
.map(|content| {
content
.trim()
.parse::<u32>()
.unwrap_or(0)
})
.unwrap_or_else(|_| {
error!("Failed to read UEFI logs entry count file");
0
}),
entry_count: uefi_count,
supports_partial_access: config.get_uefi_logs_supports_partial_access(),
appendable: config.get_uefi_logs_appendable(),
formats: config.get_uefi_logs_formats(),
Expand All @@ -121,17 +138,7 @@ impl<'a> FillerFromHardware<'a> {
evidence_type: "ima_log".to_string(),
capabilities: structures::LogCapabilities {
evidence_version: None,
entry_count: keylime::file_ops::read_file(config.get_ima_ml_count_file().as_str())
.map(|content| {
content
.trim()
.parse::<u32>()
.unwrap_or(0)
})
.unwrap_or_else(|_| {
error!("Failed to read IMA log entry count file");
0
}),
entry_count: 0, // Placeholder, will be filled later
supports_partial_access: config.get_ima_logs_supports_partial_access(),
appendable: config.get_ima_logs_appendable(),
formats: config.get_ima_logs_formats(),
Expand Down
3 changes: 3 additions & 0 deletions keylime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ version.workspace = true
actix-web.workspace = true
anyhow.workspace = true
base64.workspace = true
byteorder.workspace = true
chrono.workspace = true
config.workspace = true
glob.workspace = true
Expand All @@ -25,6 +26,8 @@ reqwest.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
sha1.workspace = true
sha2.workspace = true
static_assertions.workspace = true
tempfile.workspace = true
thiserror.workspace = true
Expand Down
1 change: 1 addition & 0 deletions keylime/src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ mod tests {
fn test_cert_no_server_cert() {
// Ensure the test cert file does not exist before the test
const TEST_KEY_PATH: &str = "test_key.pem";
let _ = std::fs::remove_file(TEST_KEY_PATH);
let config = CertificateConfig {
agent_uuid: "test-uuid".to_string(),
contact_ip: "1.2.3.4".to_string(),
Expand Down
66 changes: 28 additions & 38 deletions keylime/src/config/push_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,23 @@ pub const DEFAULT_UEFI_LOGS_APPENDABLE: bool = true;
pub const DEFAULT_UEFI_LOGS_EVIDENCE_VERSION: &str = "2.1";
pub const DEFAULT_UEFI_LOGS_FORMATS: &[&str] = &["application/octet-stream"];
pub const DEFAULT_UEFI_LOGS_SUPPORTS_PARTIAL_ACCESS: bool = true;
pub const DEFAULT_MEASUREDBOOT_ML_DIRECTORY_PATH: &str =
"/sys/kernel/security/tpm0";
pub static DEFAULT_MEASUREDBOOT_ML_COUNT_FILE: Lazy<String> =
Lazy::new(|| format!("{}/count", DEFAULT_MEASUREDBOOT_ML_DIRECTORY_PATH));

pub const DEFAULT_UEFI_LOGS_BINARY_PATH: &str = "/sys/kernel/security/tpm0";
pub const DEFAULT_UEFI_LOGS_BINARY_FILE: &str = "binary_bios_measurements";
pub static DEFAULT_UEFI_LOGS_BINARY_FILE_PATH: Lazy<String> =
Lazy::new(|| {
format!(
"{}/{}",
DEFAULT_UEFI_LOGS_BINARY_PATH, DEFAULT_UEFI_LOGS_BINARY_FILE
)
});

pub trait PushModelConfigTrait {
fn get_certification_keys_server_identifier(&self) -> String;
fn get_contact_ip(&self) -> String;
fn get_contact_port(&self) -> u32;
fn get_enable_iak_idevid(&self) -> bool;
fn get_ek_handle(&self) -> String;
fn get_measuredboot_ml_directory_path(&self) -> String;
fn get_measuredboot_ml_count_file(&self) -> String;
fn get_ima_logs_appendable(&self) -> bool;
fn get_ima_logs_formats(&self) -> Vec<String>;
fn get_ima_logs_supports_partial_access(&self) -> bool;
Expand All @@ -51,6 +55,7 @@ pub trait PushModelConfigTrait {
fn get_registrar_api_versions(&self) -> Vec<String>;
fn get_api_versions(&self) -> Vec<String>;
fn get_uefi_logs_appendable(&self) -> bool;
fn get_uefi_logs_binary_file_path(&self) -> String;
fn get_uefi_logs_evidence_version(&self) -> String;
fn get_uefi_logs_formats(&self) -> Vec<String>;
fn get_uefi_logs_supports_partial_access(&self) -> bool;
Expand Down Expand Up @@ -78,11 +83,6 @@ impl Default for PushModelConfig {
.to_string()
.clone(),
ima_ml_count_file: DEFAULT_IMA_ML_COUNT_FILE.to_string().clone(),
measuredboot_ml_directory_path:
DEFAULT_MEASUREDBOOT_ML_DIRECTORY_PATH.to_string().clone(),
measuredboot_ml_count_file: DEFAULT_MEASUREDBOOT_ML_COUNT_FILE
.to_string()
.clone(),
registrar_ip: DEFAULT_REGISTRAR_IP.to_string(),
registrar_port: DEFAULT_REGISTRAR_PORT,
registrar_api_versions: DEFAULT_REGISTRAR_API_VERSIONS
Expand All @@ -93,6 +93,8 @@ impl Default for PushModelConfig {
server_key: DEFAULT_SERVER_KEY.to_string(),
server_key_password: DEFAULT_SERVER_KEY_PASSWORD.to_string(),
uefi_logs_appendable: DEFAULT_UEFI_LOGS_APPENDABLE,
uefi_logs_binary_file_path: DEFAULT_UEFI_LOGS_BINARY_FILE_PATH
.to_string(),
uefi_logs_evidence_version: DEFAULT_UEFI_LOGS_EVIDENCE_VERSION
.to_string(),
uefi_logs_formats: DEFAULT_UEFI_LOGS_FORMATS
Expand Down Expand Up @@ -125,8 +127,6 @@ pub struct PushModelConfig {
ima_logs_supports_partial_access: bool,
ima_ml_directory_path: String,
ima_ml_count_file: String,
measuredboot_ml_directory_path: String,
measuredboot_ml_count_file: String,
registrar_api_versions: Vec<String>,
registrar_ip: String,
registrar_port: u32,
Expand All @@ -136,6 +136,7 @@ pub struct PushModelConfig {
tpm_encryption_alg: String,
tpm_hash_alg: String,
tpm_signing_alg: String,
uefi_logs_binary_file_path: String,
uefi_logs_evidence_version: String,
uefi_logs_supports_partial_access: bool,
uefi_logs_appendable: bool,
Expand Down Expand Up @@ -190,14 +191,6 @@ impl PushModelConfigTrait for PushModelConfig {
self.ima_ml_directory_path.clone()
}

fn get_measuredboot_ml_directory_path(&self) -> String {
self.measuredboot_ml_directory_path.clone()
}

fn get_measuredboot_ml_count_file(&self) -> String {
self.measuredboot_ml_count_file.clone()
}

fn get_registrar_ip(&self) -> String {
self.registrar_ip.clone()
}
Expand Down Expand Up @@ -226,6 +219,10 @@ impl PushModelConfigTrait for PushModelConfig {
self.uefi_logs_appendable
}

fn get_uefi_logs_binary_file_path(&self) -> String {
self.uefi_logs_binary_file_path.clone()
}

fn get_uefi_logs_evidence_version(&self) -> String {
self.uefi_logs_evidence_version.clone()
}
Expand Down Expand Up @@ -265,9 +262,9 @@ impl PushModelConfigTrait for PushModelConfig {
enable_iak_idevid: {}, ek_handle: {},
ima_logs_appendable: {}, ima_logs_formats: {:?}, ima_logs_supports_partial_access: {},
ima_ml_directory_path: {}, ima_ml_count_file: {},
measuredboot_ml_directory_path: {}, measuredboot_ml_count_file: {},
registrar_ip: {}, registrar_port: {}, server_cert: {},
server_key: {}, server_key_password: {},
uefi_logs_binary_file_path: {},
uefi_logs_evidence_version: {}, uefi_logs_supports_partial_access: {},
uefi_logs_appendable: {}, uefi_logs_formats: {:?},
tpm_encryption_alg: {}, tpm_hash_alg: {}, tpm_signing_alg: {},
Expand All @@ -282,13 +279,12 @@ impl PushModelConfigTrait for PushModelConfig {
self.ima_logs_supports_partial_access,
self.ima_ml_directory_path,
self.ima_ml_count_file,
self.measuredboot_ml_directory_path,
self.measuredboot_ml_count_file,
self.registrar_ip,
self.registrar_port,
self.server_cert,
self.server_key,
self.server_key_password,
self.uefi_logs_binary_file_path,
self.uefi_logs_evidence_version,
self.uefi_logs_supports_partial_access,
self.uefi_logs_appendable,
Expand Down Expand Up @@ -343,19 +339,18 @@ mod tests {
pmc.get_ima_ml_count_file()
== DEFAULT_IMA_ML_COUNT_FILE.to_string()
);
assert!(
pmc.get_measuredboot_ml_directory_path()
== DEFAULT_MEASUREDBOOT_ML_DIRECTORY_PATH
);
assert!(
pmc.get_measuredboot_ml_count_file()
== DEFAULT_MEASUREDBOOT_ML_COUNT_FILE.to_string()
);
assert!(pmc.get_registrar_ip() == DEFAULT_REGISTRAR_IP);
assert!(pmc.get_registrar_port() == DEFAULT_REGISTRAR_PORT);
assert!(pmc.get_server_cert() == DEFAULT_SERVER_CERT);
assert!(pmc.get_server_key() == DEFAULT_SERVER_KEY);
assert!(pmc.get_server_key_password() == DEFAULT_SERVER_KEY_PASSWORD);
assert!(
pmc.get_uefi_logs_appendable() == DEFAULT_UEFI_LOGS_APPENDABLE
);
assert!(
pmc.get_uefi_logs_binary_file_path()
== DEFAULT_UEFI_LOGS_BINARY_FILE_PATH.to_string()
);
assert!(
pmc.get_uefi_logs_evidence_version()
== DEFAULT_UEFI_LOGS_EVIDENCE_VERSION
Expand Down Expand Up @@ -384,7 +379,7 @@ mod tests {

#[test]
fn test_display_config() {
let pmc = PushModelConfig::default();
let pmc = PushModelConfig::new();
let display_string = pmc.to_string();
assert!(display_string
.contains(&pmc.get_certification_keys_server_identifier()));
Expand All @@ -404,11 +399,6 @@ mod tests {
));
assert!(display_string.contains(&pmc.get_ima_ml_directory_path()));
assert!(display_string.contains(&pmc.get_ima_ml_count_file()));
assert!(display_string
.contains(&pmc.get_measuredboot_ml_directory_path()));
assert!(
display_string.contains(&pmc.get_measuredboot_ml_count_file())
);
assert!(display_string.contains(&pmc.get_registrar_ip()));
assert!(
display_string.contains(&pmc.get_registrar_port().to_string())
Expand Down
2 changes: 2 additions & 0 deletions keylime/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ pub enum Error {
CertificateGeneration(
#[from] crate::crypto::x509::CertificateBuilderError,
),
#[error("UEFI Log parser error: {0}")]
UEFILog(String),
#[error("{0}")]
Other(String),
}
Expand Down
1 change: 1 addition & 0 deletions keylime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod secure_mount;
pub mod serialization;
pub mod structures;
pub mod tpm;
pub mod uefi;
pub mod version;

#[macro_use]
Expand Down
2 changes: 1 addition & 1 deletion keylime/src/structures/capabilities_negotiation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub enum EvidenceSupported {
pub struct LogCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub evidence_version: Option<String>,
pub entry_count: u32,
pub entry_count: usize,
pub supports_partial_access: bool,
pub appendable: bool,
pub formats: Vec<String>,
Expand Down
3 changes: 3 additions & 0 deletions keylime/src/uefi/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod uefi_log_handler;

pub use uefi_log_handler::*;
Loading