diff --git a/openssl-sys/Cargo.toml b/openssl-sys/Cargo.toml index 99195a303..dfcf8764a 100644 --- a/openssl-sys/Cargo.toml +++ b/openssl-sys/Cargo.toml @@ -17,6 +17,7 @@ edition = "2018" [features] vendored = ['openssl-src'] unstable_boringssl = ['bssl-sys'] +probe = [] [dependencies] libc = "0.2" diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 0e23386fd..0856325de 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -32,6 +32,14 @@ mod boringssl { #[cfg(all(boringssl, not(feature = "unstable_boringssl")))] pub use boringssl::*; +#[cfg(all(feature = "probe", feature = "vendored"))] +#[path = "probe_vendored.rs"] +pub mod probe; + +#[cfg(all(feature = "probe", not(feature = "vendored")))] +#[path = "probe_system.rs"] +pub mod probe; + #[cfg(openssl)] #[path = "."] mod openssl { diff --git a/openssl-sys/src/probe_system.rs b/openssl-sys/src/probe_system.rs new file mode 100644 index 000000000..9b4a26ad7 --- /dev/null +++ b/openssl-sys/src/probe_system.rs @@ -0,0 +1,49 @@ +use std::path::{Path, PathBuf}; +use std::ffi::{CStr, OsStr}; +use std::os::unix::ffi::{OsStrExt, OsStringExt}; + +#[cfg(unix)] +fn cstr_to_path(p: &CStr) -> Option<&Path> { + Some(Path::new(OsStr::from_bytes(p.to_bytes()))) +} + +#[cfg(not(unix))] +fn cstr_to_path(p: &CStr) -> Option<&Path> { + p.to_str().ok().map(Path::new) +} + +fn system_cert_file() -> Option<&'static Path> { + let c_path: &'static CStr = unsafe { + let p = crate::X509_get_default_cert_file(); + CStr::from_ptr(p) + }; + cstr_to_path(c_path) +} + +fn system_cert_dir() -> Option<&'static Path> { + let c_path: &'static CStr = unsafe { + let p = crate::X509_get_default_cert_dir(); + CStr::from_ptr(p) + }; + cstr_to_path(c_path) +} + +/// Return the directories in which CA certificates should likely be found. +pub fn default_certs_dirs() -> Vec { + let Some(p) = system_cert_dir() else { + return vec![]; + }; + vec![p.to_path_buf()] +} + +/// Return the path to the file containing the default system CA certificates. +/// Any configuration provided via environment variables is ignored. +pub fn default_cert_file() -> Option { + Some(system_cert_file()?.to_path_buf()) +} + +/// Return the path to the directory containing the default system CA certificates. +/// Any configuration provided via environment variables is ignored. +pub fn default_cert_dir() -> Option { + Some(system_cert_file()?.join("certs")) +} diff --git a/openssl-sys/src/probe_vendored.rs b/openssl-sys/src/probe_vendored.rs new file mode 100644 index 000000000..d665a16cf --- /dev/null +++ b/openssl-sys/src/probe_vendored.rs @@ -0,0 +1,74 @@ +use std::path::{Path, PathBuf}; + +// see http://gagravarr.org/writing/openssl-certs/others.shtml +static CERT_DIRS: &[&str] = &[ + "/var/ssl", + "/usr/share/ssl", + "/usr/local/ssl", + "/usr/local/openssl", + "/usr/local/etc/openssl", + "/usr/local/share", + "/usr/lib/ssl", + "/usr/ssl", + "/etc/openssl", + "/etc/pki/ca-trust/extracted/pem", + "/etc/pki/tls", + "/etc/ssl", + "/etc/certs", + "/opt/etc/ssl", // Entware + "/data/data/com.termux/files/usr/etc/tls", + "/boot/system/data/ssl", +]; + +/// Return the directories in which CA certificates should likely be found. +pub fn default_certs_dirs() -> Vec { + CERT_DIRS.iter().filter_map(|p| { + let p: &Path = p.as_ref(); + if p.exists() { + Some(p.to_path_buf()) + } else { + None + } + }).collect() +} + +/// Return the path to the file containing the default system CA certificates. +/// Any configuration provided via environment variables is ignored. +pub fn default_cert_file() -> Option { + for certs_dir in CERT_DIRS.iter() { + // cert.pem looks to be an openssl 1.0.1 thing, while + // certs/ca-certificates.crt appears to be a 0.9.8 thing + let certs_dir: &'static Path = certs_dir.as_ref(); + for cert_filename in [ + "cert.pem", + "certs.pem", + "ca-bundle.pem", + "cacert.pem", + "ca-certificates.crt", + "certs/ca-certificates.crt", + "certs/ca-root-nss.crt", + "certs/ca-bundle.crt", + "CARootCertificates.pem", + "tls-ca-bundle.pem", + ].iter() { + let cert_file = certs_dir.join(cert_filename); + if cert_file.exists() { + return Some(cert_file); + } + } + } + None +} + +/// Return the path to the directory containing the default system CA certificates. +/// Any configuration provided via environment variables is ignored. +pub fn default_cert_dir() -> Option { + for certs_dir in CERT_DIRS.iter() { + let certs_dir: &'static Path = certs_dir.as_ref(); + let cert_dir = certs_dir.join("certs"); + if cert_dir.exists() { + return Some(cert_dir); + } + } + None +}