use product_os_security::certificates::ManagedCa;
use super::trust_native;
pub use trust_native::check_system_native;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TrustStatus {
Trusted,
InstalledNotTrusted,
NotInstalled,
FingerprintMismatch,
BypassedViaFlag,
Unknown(String),
}
#[must_use]
pub fn needs_reinstall(status: &TrustStatus) -> bool {
matches!(
status,
TrustStatus::NotInstalled
| TrustStatus::InstalledNotTrusted
| TrustStatus::FingerprintMismatch
)
}
#[must_use]
pub fn needs_uninstall_before_install(status: &TrustStatus) -> bool {
matches!(
status,
TrustStatus::InstalledNotTrusted | TrustStatus::FingerprintMismatch
)
}
pub struct TrustProbe;
impl TrustProbe {
pub fn check_target(managed: &ManagedCa, target: &str) -> TrustStatus {
match target.to_ascii_lowercase().as_str() {
"system" => Self::check_system(managed),
"firefox" => trust_native::check_nss_script(managed, "install-linux-nss.sh"),
"chromium-profile" | "chromium" => {
trust_native::check_nss_script(managed, "install-chromium-nss.sh")
}
"macos-user" | "user-keychain" => {
if let Some(status) = trust_native::check_macos_user_native(managed) {
return status;
}
TrustStatus::Unknown("macos-user probe unavailable on this platform".into())
}
other => TrustStatus::Unknown(format!("unknown trust target: {other}")),
}
}
pub fn all_trusted(managed: &ManagedCa, targets: &[String]) -> bool {
if targets.is_empty() {
return Self::check_target(managed, "system") == TrustStatus::Trusted;
}
targets
.iter()
.all(|t| Self::check_target(managed, t) == TrustStatus::Trusted)
}
fn check_system(managed: &ManagedCa) -> TrustStatus {
if let Some(status) = check_system_native(managed) {
return status;
}
#[cfg(target_os = "macos")]
{
return Self::check_macos_system_shell(managed);
}
#[cfg(target_os = "windows")]
{
return Self::check_windows_shell(managed);
}
#[cfg(target_os = "linux")]
{
return Self::check_linux_shell(managed);
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
{
TrustStatus::Unknown("unsupported platform for system trust probe".into())
}
}
#[cfg(target_os = "macos")]
fn check_macos_system_shell(managed: &ManagedCa) -> TrustStatus {
let Ok(fp) = managed.fingerprint_sha256_compact() else {
return TrustStatus::Unknown("fingerprint unavailable".into());
};
shell_fingerprint_probe(
"security",
&[
"find-certificate",
"-a",
"-Z",
"/Library/Keychains/System.keychain",
],
&fp,
managed,
Some("/Library/Keychains/System.keychain"),
)
}
#[cfg(target_os = "windows")]
fn check_windows_shell(managed: &ManagedCa) -> TrustStatus {
let Ok(fp) = managed.fingerprint_sha256_compact() else {
return TrustStatus::Unknown("fingerprint unavailable".into());
};
shell_fingerprint_probe("certutil", &["-store", "Root"], &fp, managed, None)
}
#[cfg(target_os = "linux")]
fn check_linux_shell(managed: &ManagedCa) -> TrustStatus {
let Ok(fp) = managed.fingerprint_sha256_compact() else {
return TrustStatus::Unknown("fingerprint unavailable".into());
};
shell_fingerprint_probe(
"sh",
&["-c", "openssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt 2>/dev/null | openssl pkcs7 -print_certs -noout 2>/dev/null || true"],
&fp,
managed,
None,
)
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
fn shell_fingerprint_probe(
cmd: &str,
args: &[&str],
fingerprint_compact: &str,
managed: &ManagedCa,
keychain: Option<&str>,
) -> TrustStatus {
use std::process::Command;
let output = Command::new(cmd).args(args).output();
match output {
Ok(out) if out.status.success() || cmd == "sh" => {
let text = String::from_utf8_lossy(&out.stdout).to_uppercase();
let needle = fingerprint_compact.to_ascii_uppercase();
if text.contains(&needle) {
#[cfg(target_os = "macos")]
if let Some(kc) = keychain {
return match trust_native::verify_macos_ssl_trust(managed, Some(kc)) {
Ok(true) => TrustStatus::Trusted,
Ok(false) => TrustStatus::InstalledNotTrusted,
Err(e) => TrustStatus::Unknown(e),
};
}
TrustStatus::Trusted
} else {
TrustStatus::NotInstalled
}
}
Ok(out) => TrustStatus::Unknown(format!(
"{cmd} probe failed: {}",
String::from_utf8_lossy(&out.stderr)
)),
Err(e) => TrustStatus::Unknown(format!("failed to run {cmd}: {e}")),
}
}
impl std::fmt::Display for TrustStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Trusted => f.write_str("trusted"),
Self::InstalledNotTrusted => f.write_str("installed-not-trusted"),
Self::NotInstalled => f.write_str("not-installed"),
Self::FingerprintMismatch => f.write_str("fingerprint-mismatch"),
Self::BypassedViaFlag => f.write_str("bypassed-via-flag"),
Self::Unknown(msg) => write!(f, "unknown({msg})"),
}
}
}