From 72cb97249d6f78b317987be8d5e00e75aca2acf4 Mon Sep 17 00:00:00 2001 From: SFG545 Date: Sat, 28 Feb 2026 19:12:03 -0600 Subject: [PATCH] Add fallback trust source installation and improve code formatting --- src/main.rs | 120 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 31 deletions(-) diff --git a/src/main.rs b/src/main.rs index 85c52f1..7fc0614 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,6 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; 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_ANCHORS_DIR: &str = "/etc/ca-certificates/trust-source/anchors"; 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_BUNDLE_CRT_LINK: &str = "/etc/ssl/certs/ca-bundle.crt"; 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_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"; @@ -29,9 +27,11 @@ const DEFAULT_CERTDATA_URL: &str = const DEFAULT_CERTDATA_OUTPUT: &str = "certdata.txt"; const DEFAULT_MOZILLA_TRUST_P11KIT: &str = "/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"; -// make-ca uses a pinned ISRG Root X1 to bootstrap downloads from hg.mozilla.org -// before the local trust store exists. We do the same for certdata fetch/sync. +const DEFAULT_PKI_CA_TRUST_SOURCE_MOZILLA_TRUST_P11KIT: &str = + "/etc/pki/ca-trust/source/mozilla.trust.p11-kit"; const MOZILLA_HG_BOOTSTRAP_ROOT_PEM: &str = r#"-----BEGIN CERTIFICATE----- MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh @@ -517,11 +517,17 @@ fn resolve_sync_certdata_output( } 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()?; - println!("Using temporary certdata workspace: {}", workspace.display()); + println!( + "Using temporary certdata workspace: {}", + workspace.display() + ); Ok(( workspace.join(DEFAULT_CERTDATA_OUTPUT), Some(TempWorkspaceCleanup { path: workspace }), @@ -760,8 +766,8 @@ fn http_get_text(client: &reqwest::blocking::Client, url: &str) -> Result return Err(err).with_context(|| format!("failed to request {url}")), } - .error_for_status() - .with_context(|| format!("server returned an error for {url}"))?; + .error_for_status() + .with_context(|| format!("server returned an error for {url}"))?; response.text().context("failed to read response body") } @@ -858,8 +864,7 @@ fn write_bootstrap_ca_tempfile() -> Result { "{}{}", MOZILLA_HG_BOOTSTRAP_ROOT_PEM, MOZILLA_HG_BOOTSTRAP_DIGICERT_G2_ROOT_PEM ); - fs::write(&path, bundle) - .with_context(|| format!("failed to write {}", path.display()))?; + fs::write(&path, bundle).with_context(|| format!("failed to write {}", path.display()))?; #[cfg(unix)] { 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 = None; if args.dry_run { if let Some(parsed) = &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()); - 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!( - "[dry-run] Would also write BLFS make-ca compatibility source {}", - path_in_root(&args.root, Path::new(DEFAULT_PKI_MOZILLA_TRUST_P11KIT)).display() + "[dry-run] If extraction is empty, would retry after installing compatibility trust source(s):" ); + for target in fallback_targets { + println!(" - {}", path_in_root(&args.root, &target).display()); + } } } else { let parsed = parsed.context("certdata parse result missing")?; let p11kit = convert_certdata_to_p11kit_bundle(&parsed)?; 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 { @@ -1123,33 +1132,75 @@ fn run_certdata_convert(args: CertdataConvertArgs) -> Result<()> { .unwrap_or_else(|| PathBuf::from(DEFAULT_EXTRACTED_DIR)); extract_trust(&args.root, &extract_output, args.dry_run)?; if !args.dry_run { - ensure_nonempty_extraction_outputs(&args.root, &extract_output)?; + 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)?; + } } println!("certdata conversion and extraction complete."); Ok(()) } -fn should_write_make_ca_mirror(root: &Path, output_target: &Path) -> bool { - output_target == Path::new(DEFAULT_MOZILLA_TRUST_P11KIT) - && path_in_root(root, Path::new("/etc/pki")).exists() +fn detect_trust_source_fallback_targets(_root: &Path, output_target: &Path) -> Vec { + if output_target != Path::new(DEFAULT_MOZILLA_TRUST_P11KIT) { + 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, output_target: &Path, p11kit: &str, force: bool, -) -> Result<()> { - if !should_write_make_ca_mirror(root, output_target) { - return Ok(()); +) -> Result { + let mut installed = 0usize; + 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)); - println!( - "Installing BLFS make-ca compatibility trust source: {}", - mirror_host.display() - ); - install_file(&mirror_host, p11kit.as_bytes(), force, false) + for target in targets { + let mirror_host = path_in_root(root, &target); + println!( + "Installing compatibility trust source: {}", + mirror_host.display() + ); + 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<()> { @@ -2317,7 +2368,8 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR #[test] fn sync_certdata_output_respects_explicit_path() { 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!(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() { let (resolved, cleanup) = resolve_sync_certdata_output(None, false).unwrap(); 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(); 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() { let (resolved, cleanup) = resolve_sync_certdata_output(None, true).unwrap(); 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()); }