Skip to content

Commit

Permalink
openssl-sys: Provide information about the default certificate paths
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
hvenev committed Jul 7, 2024
1 parent 22ffa9a commit ad2d76c
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 0 deletions.
1 change: 1 addition & 0 deletions openssl-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ edition = "2018"
[features]
vendored = ['openssl-src']
unstable_boringssl = ['bssl-sys']
probe = []

[dependencies]
libc = "0.2"
Expand Down
8 changes: 8 additions & 0 deletions openssl-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
49 changes: 49 additions & 0 deletions openssl-sys/src/probe_system.rs
Original file line number Diff line number Diff line change
@@ -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<PathBuf> {
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<PathBuf> {
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<PathBuf> {
Some(system_cert_file()?.join("certs"))
}
74 changes: 74 additions & 0 deletions openssl-sys/src/probe_vendored.rs
Original file line number Diff line number Diff line change
@@ -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<PathBuf> {
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<PathBuf> {
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<PathBuf> {
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
}

0 comments on commit ad2d76c

Please sign in to comment.