Add fallback trust source installation and improve code formatting

This commit is contained in:
2026-02-28 19:12:03 -06:00
parent 11580aebcf
commit 72cb97249d
+83 -25
View File
@@ -11,7 +11,6 @@ use std::path::{Path, PathBuf};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
// Default paths mirror the p11-kit / update-ca-trust layout used by this system.
const DEFAULT_ROOT: &str = "/"; const DEFAULT_ROOT: &str = "/";
const DEFAULT_ANCHORS_DIR: &str = "/etc/ca-certificates/trust-source/anchors"; const DEFAULT_ANCHORS_DIR: &str = "/etc/ca-certificates/trust-source/anchors";
const DEFAULT_EXTRACTED_DIR: &str = "/etc/ca-certificates/extracted"; const DEFAULT_EXTRACTED_DIR: &str = "/etc/ca-certificates/extracted";
@@ -20,7 +19,6 @@ const DEFAULT_SSL_CERT_PEM_LINK: &str = "/etc/ssl/cert.pem";
const DEFAULT_SSL_CA_CERTIFICATES_BUNDLE_LINK: &str = "/etc/ssl/certs/ca-certificates.crt"; const DEFAULT_SSL_CA_CERTIFICATES_BUNDLE_LINK: &str = "/etc/ssl/certs/ca-certificates.crt";
const DEFAULT_SSL_CA_BUNDLE_CRT_LINK: &str = "/etc/ssl/certs/ca-bundle.crt"; const DEFAULT_SSL_CA_BUNDLE_CRT_LINK: &str = "/etc/ssl/certs/ca-bundle.crt";
const DEFAULT_JAVA_CACERTS_LINK: &str = "/etc/ssl/certs/java/cacerts"; const DEFAULT_JAVA_CACERTS_LINK: &str = "/etc/ssl/certs/java/cacerts";
// BLFS make-ca compatibility paths (commonly used by OpenSSL/libgit2 builds on LFS).
const DEFAULT_PKI_TLS_CA_BUNDLE_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.crt"; const DEFAULT_PKI_TLS_CA_BUNDLE_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.crt";
const DEFAULT_PKI_TLS_CA_BUNDLE_TRUST_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.trust.crt"; const DEFAULT_PKI_TLS_CA_BUNDLE_TRUST_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.trust.crt";
const DEFAULT_PKI_TLS_JAVA_CACERTS_LINK: &str = "/etc/pki/tls/java/cacerts"; const DEFAULT_PKI_TLS_JAVA_CACERTS_LINK: &str = "/etc/pki/tls/java/cacerts";
@@ -29,9 +27,11 @@ const DEFAULT_CERTDATA_URL: &str =
const DEFAULT_CERTDATA_OUTPUT: &str = "certdata.txt"; const DEFAULT_CERTDATA_OUTPUT: &str = "certdata.txt";
const DEFAULT_MOZILLA_TRUST_P11KIT: &str = const DEFAULT_MOZILLA_TRUST_P11KIT: &str =
"/usr/share/ca-certificates/trust-source/mozilla.trust.p11-kit"; "/usr/share/ca-certificates/trust-source/mozilla.trust.p11-kit";
const DEFAULT_ETC_MOZILLA_TRUST_P11KIT: &str =
"/etc/ca-certificates/trust-source/mozilla.trust.p11-kit";
const DEFAULT_PKI_MOZILLA_TRUST_P11KIT: &str = "/etc/pki/anchors/mozilla.trust.p11-kit"; const DEFAULT_PKI_MOZILLA_TRUST_P11KIT: &str = "/etc/pki/anchors/mozilla.trust.p11-kit";
// make-ca uses a pinned ISRG Root X1 to bootstrap downloads from hg.mozilla.org const DEFAULT_PKI_CA_TRUST_SOURCE_MOZILLA_TRUST_P11KIT: &str =
// before the local trust store exists. We do the same for certdata fetch/sync. "/etc/pki/ca-trust/source/mozilla.trust.p11-kit";
const MOZILLA_HG_BOOTSTRAP_ROOT_PEM: &str = r#"-----BEGIN CERTIFICATE----- const MOZILLA_HG_BOOTSTRAP_ROOT_PEM: &str = r#"-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
@@ -517,11 +517,17 @@ fn resolve_sync_certdata_output(
} }
if dry_run { if dry_run {
return Ok((build_sync_tmp_workspace_path(0).join(DEFAULT_CERTDATA_OUTPUT), None)); return Ok((
build_sync_tmp_workspace_path(0).join(DEFAULT_CERTDATA_OUTPUT),
None,
));
} }
let workspace = create_sync_tmp_workspace_dir()?; let workspace = create_sync_tmp_workspace_dir()?;
println!("Using temporary certdata workspace: {}", workspace.display()); println!(
"Using temporary certdata workspace: {}",
workspace.display()
);
Ok(( Ok((
workspace.join(DEFAULT_CERTDATA_OUTPUT), workspace.join(DEFAULT_CERTDATA_OUTPUT),
Some(TempWorkspaceCleanup { path: workspace }), Some(TempWorkspaceCleanup { path: workspace }),
@@ -858,8 +864,7 @@ fn write_bootstrap_ca_tempfile() -> Result<PathBuf> {
"{}{}", "{}{}",
MOZILLA_HG_BOOTSTRAP_ROOT_PEM, MOZILLA_HG_BOOTSTRAP_DIGICERT_G2_ROOT_PEM MOZILLA_HG_BOOTSTRAP_ROOT_PEM, MOZILLA_HG_BOOTSTRAP_DIGICERT_G2_ROOT_PEM
); );
fs::write(&path, bundle) fs::write(&path, bundle).with_context(|| format!("failed to write {}", path.display()))?;
.with_context(|| format!("failed to write {}", path.display()))?;
#[cfg(unix)] #[cfg(unix)]
{ {
fs::set_permissions(&path, fs::Permissions::from_mode(0o600)) fs::set_permissions(&path, fs::Permissions::from_mode(0o600))
@@ -1091,6 +1096,7 @@ fn run_certdata_convert(args: CertdataConvertArgs) -> Result<()> {
) )
}; };
let mut rendered_p11kit: Option<String> = None;
if args.dry_run { if args.dry_run {
if let Some(parsed) = &parsed { if let Some(parsed) = &parsed {
let (certs, trusts) = count_cert_and_trust_objects(parsed); let (certs, trusts) = count_cert_and_trust_objects(parsed);
@@ -1100,17 +1106,20 @@ fn run_certdata_convert(args: CertdataConvertArgs) -> Result<()> {
); );
} }
println!("[dry-run] Would write {}", output_host.display()); println!("[dry-run] Would write {}", output_host.display());
if should_write_make_ca_mirror(&args.root, &output_target) { let fallback_targets = detect_trust_source_fallback_targets(&args.root, &output_target);
if !fallback_targets.is_empty() {
println!( println!(
"[dry-run] Would also write BLFS make-ca compatibility source {}", "[dry-run] If extraction is empty, would retry after installing compatibility trust source(s):"
path_in_root(&args.root, Path::new(DEFAULT_PKI_MOZILLA_TRUST_P11KIT)).display()
); );
for target in fallback_targets {
println!(" - {}", path_in_root(&args.root, &target).display());
}
} }
} else { } else {
let parsed = parsed.context("certdata parse result missing")?; let parsed = parsed.context("certdata parse result missing")?;
let p11kit = convert_certdata_to_p11kit_bundle(&parsed)?; let p11kit = convert_certdata_to_p11kit_bundle(&parsed)?;
install_file(&output_host, p11kit.as_bytes(), args.force, false)?; install_file(&output_host, p11kit.as_bytes(), args.force, false)?;
maybe_install_make_ca_mozilla_mirror(&args.root, &output_target, &p11kit, args.force)?; rendered_p11kit = Some(p11kit);
} }
if args.no_extract { if args.no_extract {
@@ -1123,33 +1132,75 @@ fn run_certdata_convert(args: CertdataConvertArgs) -> Result<()> {
.unwrap_or_else(|| PathBuf::from(DEFAULT_EXTRACTED_DIR)); .unwrap_or_else(|| PathBuf::from(DEFAULT_EXTRACTED_DIR));
extract_trust(&args.root, &extract_output, args.dry_run)?; extract_trust(&args.root, &extract_output, args.dry_run)?;
if !args.dry_run { if !args.dry_run {
if let Err(err) = ensure_nonempty_extraction_outputs(&args.root, &extract_output) {
let Some(p11kit) = rendered_p11kit.as_deref() else {
return Err(err);
};
let fallback_installs =
install_fallback_trust_sources(&args.root, &output_target, p11kit, args.force)?;
if fallback_installs == 0 {
return Err(err);
}
println!("Retrying trust extraction after installing compatibility trust source(s).");
extract_trust(&args.root, &extract_output, false)?;
ensure_nonempty_extraction_outputs(&args.root, &extract_output)?; ensure_nonempty_extraction_outputs(&args.root, &extract_output)?;
} }
}
println!("certdata conversion and extraction complete."); println!("certdata conversion and extraction complete.");
Ok(()) Ok(())
} }
fn should_write_make_ca_mirror(root: &Path, output_target: &Path) -> bool { fn detect_trust_source_fallback_targets(_root: &Path, output_target: &Path) -> Vec<PathBuf> {
output_target == Path::new(DEFAULT_MOZILLA_TRUST_P11KIT) if output_target != Path::new(DEFAULT_MOZILLA_TRUST_P11KIT) {
&& path_in_root(root, Path::new("/etc/pki")).exists() return Vec::new();
}
let mut targets = Vec::new();
let fallback_candidates = [
DEFAULT_ETC_MOZILLA_TRUST_P11KIT,
DEFAULT_PKI_MOZILLA_TRUST_P11KIT,
DEFAULT_PKI_CA_TRUST_SOURCE_MOZILLA_TRUST_P11KIT,
];
let output_target = output_target.to_path_buf();
for candidate in fallback_candidates {
let candidate_path = PathBuf::from(candidate);
if candidate_path == output_target || targets.contains(&candidate_path) {
continue;
}
// Always include fallback paths; install_file() will create parent dirs.
// Some chroots do not have /etc/pki* until later packages are installed.
targets.push(candidate_path);
}
targets
} }
fn maybe_install_make_ca_mozilla_mirror( fn install_fallback_trust_sources(
root: &Path, root: &Path,
output_target: &Path, output_target: &Path,
p11kit: &str, p11kit: &str,
force: bool, force: bool,
) -> Result<()> { ) -> Result<usize> {
if !should_write_make_ca_mirror(root, output_target) { let mut installed = 0usize;
return Ok(()); let targets = detect_trust_source_fallback_targets(root, output_target);
if targets.is_empty() {
return Ok(0);
} }
let mirror_host = path_in_root(root, Path::new(DEFAULT_PKI_MOZILLA_TRUST_P11KIT)); for target in targets {
let mirror_host = path_in_root(root, &target);
println!( println!(
"Installing BLFS make-ca compatibility trust source: {}", "Installing compatibility trust source: {}",
mirror_host.display() mirror_host.display()
); );
install_file(&mirror_host, p11kit.as_bytes(), force, false) match install_file(&mirror_host, p11kit.as_bytes(), force, false) {
Ok(()) => installed += 1,
Err(err) => eprintln!(
"warning: unable to install compatibility trust source {}: {err}",
mirror_host.display()
),
}
}
Ok(installed)
} }
fn ensure_nonempty_extraction_outputs(root: &Path, extract_output: &Path) -> Result<()> { fn ensure_nonempty_extraction_outputs(root: &Path, extract_output: &Path) -> Result<()> {
@@ -2317,7 +2368,8 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
#[test] #[test]
fn sync_certdata_output_respects_explicit_path() { fn sync_certdata_output_respects_explicit_path() {
let requested = PathBuf::from("/var/tmp/custom-certdata.txt"); let requested = PathBuf::from("/var/tmp/custom-certdata.txt");
let (resolved, cleanup) = resolve_sync_certdata_output(Some(requested.clone()), false).unwrap(); let (resolved, cleanup) =
resolve_sync_certdata_output(Some(requested.clone()), false).unwrap();
assert_eq!(resolved, requested); assert_eq!(resolved, requested);
assert!(cleanup.is_none()); assert!(cleanup.is_none());
} }
@@ -2326,7 +2378,10 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
fn sync_certdata_output_uses_tmp_workspace_and_cleans_it() { fn sync_certdata_output_uses_tmp_workspace_and_cleans_it() {
let (resolved, cleanup) = resolve_sync_certdata_output(None, false).unwrap(); let (resolved, cleanup) = resolve_sync_certdata_output(None, false).unwrap();
assert!(resolved.starts_with(Path::new("/tmp"))); assert!(resolved.starts_with(Path::new("/tmp")));
assert_eq!(resolved.file_name(), Some(OsStr::new(DEFAULT_CERTDATA_OUTPUT))); assert_eq!(
resolved.file_name(),
Some(OsStr::new(DEFAULT_CERTDATA_OUTPUT))
);
let workspace = resolved.parent().unwrap().to_path_buf(); let workspace = resolved.parent().unwrap().to_path_buf();
assert!(workspace.exists()); assert!(workspace.exists());
@@ -2338,7 +2393,10 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
fn sync_certdata_output_dry_run_uses_tmp_path_without_creating_workspace() { fn sync_certdata_output_dry_run_uses_tmp_path_without_creating_workspace() {
let (resolved, cleanup) = resolve_sync_certdata_output(None, true).unwrap(); let (resolved, cleanup) = resolve_sync_certdata_output(None, true).unwrap();
assert!(resolved.starts_with(Path::new("/tmp"))); assert!(resolved.starts_with(Path::new("/tmp")));
assert_eq!(resolved.file_name(), Some(OsStr::new(DEFAULT_CERTDATA_OUTPUT))); assert_eq!(
resolved.file_name(),
Some(OsStr::new(DEFAULT_CERTDATA_OUTPUT))
);
assert!(cleanup.is_none()); assert!(cleanup.is_none());
} }