mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
pv: Limit CRL download size
Add a dedicated HKD verification error for CRL downloads that exceed the
maximum file size of 10 MiB and cover the max_filesize behavior in the
helper tests and add tests for it.
This commit adds a new development/test dependency as it verifies that
the correct messages are being logged.
Fixes: c6f621d0dc ("rust: Add library for pv tools")
Assisted-by: IBM Bob:1.0.5
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
cf70a27d76
commit
5feee12827
18
rust/Cargo.lock
generated
18
rust/Cargo.lock
generated
@@ -367,7 +367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -917,7 +917,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -958,6 +958,7 @@ dependencies = [
|
||||
"s390_pv_core",
|
||||
"serde",
|
||||
"serde_test",
|
||||
"testing_logger",
|
||||
"thiserror",
|
||||
"zerocopy",
|
||||
]
|
||||
@@ -1118,7 +1119,7 @@ dependencies = [
|
||||
"getrandom 0.4.2",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1128,7 +1129,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "testing_logger"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d92b727cb45d33ae956f7f46b966b25f1bc712092aeef9dba5ac798fc89f720"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -29,3 +29,4 @@ pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.12.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_test = "1.0.177"
|
||||
testing_logger = "0.1.1"
|
||||
|
||||
@@ -179,6 +179,8 @@ pub enum HkdVerifyErrorType {
|
||||
IbmSignInvalid(#[source] openssl::x509::X509VerifyResult, u32),
|
||||
#[error("Too many redirections during CRL download")]
|
||||
TooManyRedirectionsCrlDownload,
|
||||
#[error("CRL download exceeds maximum file size of {} MiB", .0 / (1024 * 1024))]
|
||||
CrlDownloadTooLarge(u64),
|
||||
#[error("CRL download failed")]
|
||||
CrlDownloadFailed,
|
||||
}
|
||||
|
||||
@@ -354,6 +354,7 @@ trait HttpClient {
|
||||
fn follow_location(&mut self, enable: bool) -> Result<()>;
|
||||
fn redirect_url(&self) -> Result<Option<String>>;
|
||||
fn get_ref(&self) -> &[u8];
|
||||
fn max_filesize(&mut self, size: u64) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Production HTTP client implementation
|
||||
@@ -392,8 +393,13 @@ mod prod_client {
|
||||
}
|
||||
|
||||
fn perform(&mut self) -> Result<()> {
|
||||
self.handle.perform()?;
|
||||
Ok(())
|
||||
match self.handle.perform() {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) if err.is_filesize_exceeded() => {
|
||||
Err(Error::HkdVerify(CrlDownloadTooLarge(CRL_FILE_SIZE_MAX)))
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&mut self, enable: bool) -> Result<(), Error> {
|
||||
@@ -424,6 +430,11 @@ mod prod_client {
|
||||
fn redirect_url(&self) -> Result<Option<String>> {
|
||||
Ok(self.handle.redirect_url()?.map(|s| s.to_string()))
|
||||
}
|
||||
|
||||
fn max_filesize(&mut self, size: u64) -> Result<()> {
|
||||
self.handle.max_filesize(size)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Searches for CRL Distribution points and downloads the CRL. Stops after the first successful
|
||||
@@ -448,6 +459,8 @@ mod prod_client {
|
||||
}
|
||||
}
|
||||
|
||||
const CRL_FILE_SIZE_MAX: u64 = 10 * 1024 * 1024; // 10 MiB
|
||||
|
||||
/// Searches for CRL Distribution points and downloads the CRL. Stops after the first successful
|
||||
/// download.
|
||||
///
|
||||
@@ -477,6 +490,7 @@ fn download_first_crl_from_x509_impl<H: HttpClient>(
|
||||
// redirections.
|
||||
client.follow_location(false)?;
|
||||
client.timeout(CRL_TIMEOUT_MAX)?;
|
||||
client.max_filesize(CRL_FILE_SIZE_MAX)?;
|
||||
client.useragent("s390-tools-pv-crl")?;
|
||||
|
||||
for i in 0..CRL_MAX_REDIRECTIONS {
|
||||
@@ -649,6 +663,7 @@ mod tests {
|
||||
url: String,
|
||||
responses: HashMap<String, MockResponse>,
|
||||
perform_count: usize,
|
||||
max_filesize: Option<u64>,
|
||||
}
|
||||
|
||||
impl MockHttpClient {
|
||||
@@ -657,6 +672,7 @@ mod tests {
|
||||
url: String::new(),
|
||||
responses,
|
||||
perform_count: 0,
|
||||
max_filesize: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -692,6 +708,12 @@ mod tests {
|
||||
fn perform(&mut self) -> Result<()> {
|
||||
self.perform_count += 1;
|
||||
if let Some(response) = self.responses.get(&self.url) {
|
||||
if self
|
||||
.max_filesize
|
||||
.is_some_and(|max_filesize| response.data.len() as u64 > max_filesize)
|
||||
{
|
||||
return Err(Error::HkdVerify(CrlDownloadTooLarge(CRL_FILE_SIZE_MAX)));
|
||||
}
|
||||
if response.should_fail {
|
||||
bail_hkd_verify!(CrlDownloadFailed);
|
||||
}
|
||||
@@ -716,6 +738,11 @@ mod tests {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
fn max_filesize(&mut self, size: u64) -> Result<()> {
|
||||
self.max_filesize = Some(size);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to create mock response with data
|
||||
@@ -1185,4 +1212,67 @@ mod tests {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
mod max_filesize {
|
||||
use super::*;
|
||||
use crate::test_utils::get_cert_asset_path;
|
||||
|
||||
#[test]
|
||||
fn download_just_over_max_filesize() {
|
||||
testing_logger::setup();
|
||||
|
||||
let cert = create_cert_with_crl_dps(&["http://example.com/test.crl"]);
|
||||
|
||||
// Create data just over the 10 MiB limit
|
||||
let over_limit_data = vec![0u8; 10 * 1024 * 1024 + 1];
|
||||
|
||||
let mut responses = HashMap::new();
|
||||
responses.insert(
|
||||
"http://example.com/test.crl".to_string(),
|
||||
mock_response(over_limit_data),
|
||||
);
|
||||
|
||||
let result = download_with_mock(&cert, responses);
|
||||
// When file size exceeds limit, the download fails and function tries next URL
|
||||
// Since there's only one URL, it returns Ok(None) after exhausting all options
|
||||
assert!(result.is_ok());
|
||||
assert!(result.unwrap().is_none());
|
||||
|
||||
// Verify that error was logged about file size exceeding limit
|
||||
testing_logger::validate(|captured_logs| {
|
||||
assert!(
|
||||
captured_logs
|
||||
.iter()
|
||||
.any(|log| { log.body.contains("CRL download exceeds maximum file size") }),
|
||||
"Expected log message about CRL file size exceeding maximum limit"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn download_fallback_after_size_exceeded() {
|
||||
let cert = create_cert_with_crl_dps(&[
|
||||
"http://primary.example.com/test.crl",
|
||||
"http://backup.example.com/test.crl",
|
||||
]);
|
||||
let crl_data = std::fs::read(get_cert_asset_path("inter_ca.crl")).unwrap();
|
||||
|
||||
let mut responses = HashMap::new();
|
||||
// First URL has file that's too large
|
||||
responses.insert(
|
||||
"http://primary.example.com/test.crl".to_string(),
|
||||
mock_response(vec![0u8; (CRL_FILE_SIZE_MAX + 1).try_into().unwrap()]),
|
||||
);
|
||||
// Second URL has valid CRL
|
||||
responses.insert(
|
||||
"http://backup.example.com/test.crl".to_string(),
|
||||
mock_response(crl_data),
|
||||
);
|
||||
|
||||
let result = download_with_mock(&cert, responses);
|
||||
// Should fall back to second URL and succeed
|
||||
assert!(result.is_ok());
|
||||
assert!(result.unwrap().is_some());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user