Skip to content
Merged
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
6 changes: 5 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ jobs:
tests:
name: Fedora tests
runs-on: ubuntu-latest
container:
image: quay.io/keylime/keylime-ci:latest
steps:
- uses: actions/checkout@v4
- name: Set git safe.directory for the working directory
run : git config --system --add safe.directory "$PWD"
- name: Run tests
run: docker run --security-opt seccomp=tests/seccomp-profile.json -v $(pwd):/tmp/code_under_test -w /tmp/code_under_test quay.io/keylime/keylime-ci:latest dbus-run-session -- /tmp/code_under_test/tests/run.sh
run: bash tests/run.sh
- uses: actions/upload-artifact@v4
with:
name: tarpaulin-report
Expand Down
26 changes: 24 additions & 2 deletions keylime-agent/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,34 +501,39 @@
&config.agent.agent_data_path,
keylime_dir,
DEFAULT_AGENT_DATA_PATH,
false,
);

let mut ima_ml_path = config_get_file_path(
"ima_ml_path",
&config.agent.ima_ml_path,
root_path,
DEFAULT_IMA_ML_PATH,
false,
);

let mut measuredboot_ml_path = config_get_file_path(
"measuredboot_ml_path",
&config.agent.measuredboot_ml_path,
root_path,
DEFAULT_MEASUREDBOOT_ML_PATH,
false,
);

let mut server_key = config_get_file_path(
"server_key",
&config.agent.server_key,
keylime_dir,
DEFAULT_SERVER_KEY,
false,
);

let mut server_cert = config_get_file_path(
"server_cert",
&config.agent.server_cert,
keylime_dir,
DEFAULT_SERVER_CERT,
false,
);

let trusted_client_ca: String =
Expand All @@ -540,6 +545,7 @@
t,
keylime_dir,
DEFAULT_TRUSTED_CLIENT_CA,
false,
)
})
.collect::<Vec<_>>()
Expand All @@ -550,13 +556,15 @@
&config.agent.iak_cert,
keylime_dir,
DEFAULT_IAK_CERT,
true,
);

let mut idevid_cert = config_get_file_path(
"idevid_cert",
&config.agent.idevid_cert,
keylime_dir,
DEFAULT_IDEVID_CERT,
true,
);

let ek_handle = match config.agent.ek_handle.as_ref() {
Expand Down Expand Up @@ -630,6 +638,7 @@
&config.agent.revocation_cert,
keylime_dir,
&format!("secure/unzipped/{DEFAULT_REVOCATION_CERT}"),
false,
);

Ok(KeylimeConfig {
Expand Down Expand Up @@ -657,18 +666,23 @@
/// Expand a file path from the configuration file.
///
/// If the string is set as "default", return the provided default path relative from the provided work_dir.
/// If the string is empty, use again the default value
/// If the string is empty, use the default value unless the 'leave_empty' is 'true'
/// If the string is a relative path, return the path relative from the provided work_dir
/// If the string is an absolute path, return the path without change.
fn config_get_file_path(
option: &str,
path: &str,
work_dir: &Path,
default: &str,
leave_empty: bool,
) -> String {
match path {
"default" => work_dir.join(default).display().to_string(),
"" => {
if leave_empty {
return "".to_string();
}

Check warning on line 685 in keylime-agent/src/config.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/config.rs#L684-L685

Added lines #L684 - L685 were not covered by tests
warn!("Empty string provided in configuration option {option}, using default {default}");
work_dir.join(default).display().to_string()
}
Expand Down Expand Up @@ -1107,7 +1121,7 @@

let translated: Vec<String> = list
.iter()
.map(|e| config_get_file_path("test", e, workdir, default))
.map(|e| config_get_file_path("test", e, workdir, default, false))
.collect();

assert_eq!(
Expand All @@ -1122,5 +1136,13 @@
],
translated
);

let translated =
config_get_file_path("test", "", workdir, "default", true);
assert_eq!("", translated);

let translated =
config_get_file_path("test", "", workdir, "default", false);
assert_eq!("/workdir/default", translated);
}
}
4 changes: 4 additions & 0 deletions keylime-agent/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ pub(crate) enum Error {
Conversion(String),
#[error("Configuration error")]
Configuration(#[from] crate::config::KeylimeConfigError),
#[error("Device ID error")]
DeviceID(#[from] keylime::device_id::DeviceIDError),
#[error("Device ID builder error")]
DeviceIDBuilder(#[from] keylime::device_id::DeviceIDBuilderError),
#[error("Reqwest error: {0}")]
Reqwest(#[from] reqwest::Error),
#[error("Registrar error: received {code} from {addr}")]
Expand Down
194 changes: 47 additions & 147 deletions keylime-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,11 @@
try_join,
};
use keylime::{
crypto, crypto::x509::CertificateBuilder, ima::MeasurementList,
list_parser::parse_list, tpm,
crypto::{self, x509::CertificateBuilder},
device_id::{DeviceID, DeviceIDBuilder},
ima::MeasurementList,
list_parser::parse_list,
tpm::{self, IAKResult, IDevIDResult},
};
use log::*;
use openssl::{
Expand Down Expand Up @@ -324,137 +327,6 @@
config.agent.tpm_signing_alg.as_ref(),
)?;

let iak_cert: Option<X509>;
let idevid_cert: Option<X509>;
// Attempt to load the IAK and IDevID certificates
if config.agent.enable_iak_idevid {
iak_cert = match config.agent.iak_cert.as_ref() {
"" => {
debug!("The iak_cert option was not set in the configuration file");
None
}
path => {
let iak_path = Path::new(&path);
if iak_path.exists() {
debug!(
"Loading IAK certificate from {}",
iak_path.display()
);
let iakcert = match crypto::load_x509_der(iak_path) {
Ok(cert) => cert,
Err(error) => crypto::load_x509_pem(iak_path)?,
};
Some(iakcert)
} else {
debug!("Can not find IAK certificate");
None
}
}
};
idevid_cert = match config.agent.idevid_cert.as_ref() {
"" => {
debug!("The idevid_cert option was not set in the configuration file");
None
}
path => {
let idevid_path = Path::new(&path);
if idevid_path.exists() {
debug!(
"Loading IDevID certificate from {}",
idevid_path.display()
);
let idevcert = match crypto::load_x509_der(idevid_path) {
Ok(cert) => cert,
Err(error) => crypto::load_x509_pem(idevid_path)?,
};
Some(idevcert)
} else {
debug!("Can not find IDevID certificate");
None
}
}
};
} else {
iak_cert = None;
idevid_cert = None;
}
/// Regenerate the IAK and IDevID keys or collect and authorise persisted ones and check that the keys match the certificates that have been loaded
let (iak, idevid) = if config.agent.enable_iak_idevid {
/// Try to detect which template has been used by checking the certificate
let (asym_alg, name_alg) = tpm::get_idevid_template(
&crypto::match_cert_to_template(
&iak_cert.clone().ok_or(Error::Other(
"IAK/IDevID enabled but cert could not be used"
.to_string(),
))?,
)?,
config.agent.iak_idevid_template.as_str(),
config.agent.iak_idevid_asymmetric_alg.as_str(),
config.agent.iak_idevid_name_alg.as_str(),
)?;

/// IDevID recreation/collection
let idevid = if config.agent.idevid_handle.trim().is_empty() {
/// If handle is not set in config, recreate IDevID according to template
info!("Recreating IDevID.");
let regen_idev = ctx.create_idevid(asym_alg, name_alg)?;
ctx.flush_context(regen_idev.handle.into())?;
// Flush after creating to make room for AK and EK and IAK
regen_idev
} else {
info!("Collecting persisted IDevID.");
ctx.idevid_from_handle(
config.agent.idevid_handle.as_str(),
config.agent.idevid_password.as_str(),
)?
};
/// Check that recreated/collected IDevID key matches the one in the certificate
if crypto::check_x509_key(
&idevid_cert.clone().ok_or(Error::Other(
"IAK/IDevID enabled but IDevID cert could not be used"
.to_string(),
))?,
idevid.clone().public,
)? {
info!("IDevID matches certificate.");
} else {
error!("IDevID template does not match certificate. Check template in configuration.");
return Err(Error::Configuration(config::KeylimeConfigError::Generic("IDevID template does not match certificate. Check template in configuration.".to_string())));
}

/// IAK recreation/collection
let iak = if config.agent.iak_handle.trim().is_empty() {
/// If handle is not set in config, recreate IAK according to template
info!("Recreating IAK.");
ctx.create_iak(asym_alg, name_alg)?
} else {
/// If a handle has been set, try to collect from the handle
/// If there is an IAK password, add the password to the handle
info!("Collecting persisted IAK.");
ctx.iak_from_handle(
config.agent.iak_handle.as_str(),
config.agent.iak_password.as_str(),
)?
};
/// Check that recreated/collected IAK key matches the one in the certificate
if crypto::check_x509_key(
&iak_cert.clone().ok_or(Error::Other(
"IAK/IDevID enabled but IAK cert could not be used"
.to_string(),
))?,
iak.clone().public,
)? {
info!("IAK matches certificate.");
} else {
error!("IAK template does not match certificate. Check template in configuration.");
return Err(Error::Configuration(config::KeylimeConfigError::Generic("IAK template does not match certificate. Check template in configuration.".to_string())));
}

(Some(iak), Some(idevid))
} else {
(None, None)
};

// Gather EK values and certs
let ek_result = match config.agent.ek_handle.as_ref() {
"" => ctx.create_ek(tpm_encryption_alg, None)?,
Expand Down Expand Up @@ -562,13 +434,41 @@

info!("Agent UUID: {}", agent_uuid);

let (attest, signature) = if config.agent.enable_iak_idevid {
let qualifying_data = config.agent.uuid.as_bytes();
let (attest, signature) = ctx.certify_credential_with_iak(
Data::try_from(qualifying_data).unwrap(), //#[allow_ci]
ak_handle,
iak.as_ref().unwrap().handle, //#[allow_ci]
)?;
// If using IAK/IDevID is enabled, obtain IAK/IDevID and respective certificates
let mut device_id = if config.agent.enable_iak_idevid {
let mut builder = DeviceIDBuilder::new()
.iak_handle(&config.agent.iak_handle)
.iak_password(&config.agent.iak_password)
.iak_default_template(config::DEFAULT_IAK_IDEVID_TEMPLATE)
.iak_template(&config.agent.iak_idevid_template)
.iak_asym_alg(&config.agent.iak_idevid_asymmetric_alg)
.iak_hash_alg(&config.agent.iak_idevid_name_alg)
.idevid_handle(&config.agent.idevid_handle)
.idevid_cert_path(&config.agent.idevid_cert)
.idevid_password(&config.agent.idevid_password)
.idevid_default_template(config::DEFAULT_IAK_IDEVID_TEMPLATE)
.idevid_template(&config.agent.iak_idevid_template)
.idevid_asym_alg(&config.agent.iak_idevid_asymmetric_alg)
.idevid_hash_alg(&config.agent.iak_idevid_name_alg);

Check warning on line 452 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L438-L452

Added lines #L438 - L452 were not covered by tests

if !&config.agent.iak_cert.is_empty() {
builder = builder.iak_cert_path(&config.agent.iak_cert);

Check warning on line 455 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L454-L455

Added lines #L454 - L455 were not covered by tests
}

if !&config.agent.idevid_cert.is_empty() {
builder = builder.idevid_cert_path(&config.agent.idevid_cert);

Check warning on line 459 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L458-L459

Added lines #L458 - L459 were not covered by tests
}

Some(builder.build(&mut ctx)?)

Check warning on line 462 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L462

Added line #L462 was not covered by tests
} else {
None

Check warning on line 464 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L464

Added line #L464 was not covered by tests
};

let (attest, signature) = if let Some(dev_id) = &mut device_id {
let qualifying_data = Data::try_from(agent_uuid.as_bytes())?;
let (attest, signature) =

Check warning on line 469 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L467-L469

Added lines #L467 - L469 were not covered by tests
dev_id.certify(qualifying_data, ak_handle, &mut ctx)?;

info!("AK certified with IAK.");

// // For debugging certify(), the following checks the generated signature
Expand Down Expand Up @@ -705,8 +605,8 @@
{
// Request keyblob material
let keyblob = if config.agent.enable_iak_idevid {
let (Some(iak), Some(idevid), Some(attest), Some(signature)) =
(iak, idevid, attest, signature)
let (Some(dev_id), Some(attest), Some(signature)) =

Check warning on line 608 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L608

Added line #L608 was not covered by tests
(&device_id, attest, signature)
else {
error!(
"IDevID and IAK are enabled but could not be generated"
Expand All @@ -725,15 +625,15 @@
ek_result.ek_cert,
&PublicBuffer::try_from(ak.public)?.marshall()?,
Some(
&PublicBuffer::try_from(iak.public.clone())?
&PublicBuffer::try_from(dev_id.iak_pubkey.clone())?

Check warning on line 628 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L628

Added line #L628 was not covered by tests
.marshall()?,
),
Some(
&PublicBuffer::try_from(idevid.public.clone())?
&PublicBuffer::try_from(dev_id.idevid_pubkey.clone())?

Check warning on line 632 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L632

Added line #L632 was not covered by tests
.marshall()?,
),
idevid_cert,
iak_cert,
dev_id.idevid_cert.clone(),
dev_id.iak_cert.clone(),

Check warning on line 636 in keylime-agent/src/main.rs

View check run for this annotation

Codecov / codecov/patch

keylime-agent/src/main.rs#L635-L636

Added lines #L635 - L636 were not covered by tests
Some(attest.marshall()?),
Some(signature.marshall()?),
mtls_cert,
Expand Down Expand Up @@ -1090,7 +990,7 @@
TSSError(#[from] tss_esapi::Error),
}

impl<'a> Drop for QuoteData<'a> {
impl Drop for QuoteData<'_> {
/// Flush the created AK when dropping
fn drop(&mut self) {
self.tpmcontext
Expand Down
Loading
Loading