From ad2d76c16f80efffe0de410e6e21889219814376 Mon Sep 17 00:00:00 2001 From: Hristo Venev Date: Sun, 7 Jul 2024 17:34:35 +0300 Subject: [PATCH] openssl-sys: Provide information about the default certificate paths The goal of these functions is to contain the logic implemented by the `openssl-probe` crate. The `openssl-sys` crate knows whether we are linked against the system OpenSSL library, and if so, it can use it to get the correct certificate paths. If we are using the vendored version, we keep the old insecure behavior where we randomly guess paths and hope one works. --- openssl-sys/Cargo.toml | 1 + openssl-sys/src/lib.rs | 8 ++++ openssl-sys/src/probe_system.rs | 49 ++++++++++++++++++++ openssl-sys/src/probe_vendored.rs | 74 +++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+) create mode 100644 openssl-sys/src/probe_system.rs create mode 100644 openssl-sys/src/probe_vendored.rs diff --git a/openssl-sys/Cargo.toml b/openssl-sys/Cargo.toml index 99195a303a..dfcf8764ab 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 0e23386fd3..0856325de6 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 0000000000..9b4a26ad73 --- /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 0000000000..d665a16cff --- /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 +}