Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "openssl-probe"
version = "0.1.6"
version = "0.2.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/openssl-probe"
Expand Down
205 changes: 82 additions & 123 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,94 +1,6 @@
use std::env;
use std::path::{Path, PathBuf};

/// The OpenSSL environment variable to configure what certificate file to use.
pub const ENV_CERT_FILE: &'static str = "SSL_CERT_FILE";

/// The OpenSSL environment variable to configure what certificates directory to use.
pub const ENV_CERT_DIR: &'static str = "SSL_CERT_DIR";

pub struct ProbeResult {
pub cert_file: Option<PathBuf>,
pub cert_dir: Option<PathBuf>,
}

/// Probe the system for the directory in which CA certificates should likely be
/// found.
///
/// This will only search known system locations.
#[doc(hidden)]
#[deprecated(note = "use `candidate_cert_dirs` instead")]
pub fn find_certs_dirs() -> Vec<PathBuf> {
candidate_cert_dirs().map(Path::to_path_buf).collect()
}

/// Probe the system for the directory in which CA certificates should likely be
/// found.
///
/// This will only search known system locations.
pub fn candidate_cert_dirs() -> impl Iterator<Item = &'static Path> {
// see http://gagravarr.org/writing/openssl-certs/others.shtml
[
"/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
#[cfg(target_os = "android")]
"/data/data/com.termux/files/usr/etc/tls",
#[cfg(target_os = "haiku")]
"/boot/system/data/ssl",
]
.iter()
.map(Path::new)
.filter(|p| p.exists())
}

/// Deprecated as this isn't sound, use [`init_openssl_env_vars`] instead.
#[doc(hidden)]
#[deprecated(note = "this function is not safe, use `init_openssl_env_vars` instead")]
pub fn init_ssl_cert_env_vars() {
unsafe {
init_openssl_env_vars();
}
}

/// Probe for SSL certificates on the system, then configure the SSL certificate `SSL_CERT_FILE`
/// and `SSL_CERT_DIR` environment variables in this process for OpenSSL to use.
///
/// Preconfigured values in the environment variables will not be overwritten if the paths they
/// point to exist and are accessible.
///
/// # Safety
///
/// This function is not safe because it mutates the process's environment
/// variables which is generally not safe. See the [documentation in libstd][doc]
/// for information about why setting environment variables is not safe.
///
/// If possible use the [`probe`] function and directly configure OpenSSL
/// methods instead of relying on environment variables.
///
/// [doc]: https://doc.rust-lang.org/stable/std/env/fn.set_var.html#safety
pub unsafe fn init_openssl_env_vars() {
try_init_openssl_env_vars();
}

/// Deprecated as this isn't sound, use [`try_init_openssl_env_vars`] instead.
#[doc(hidden)]
#[deprecated(note = "use try_init_openssl_env_vars instead, this function is not safe")]
pub fn try_init_ssl_cert_env_vars() -> bool {
unsafe { try_init_openssl_env_vars() }
}

/// Probe for SSL certificates on the system, then configure the SSL certificate `SSL_CERT_FILE`
/// and `SSL_CERT_DIR` environment variables in this process for OpenSSL to use.
///
Expand Down Expand Up @@ -141,48 +53,15 @@ pub unsafe fn try_init_openssl_env_vars() -> bool {
cert_file.is_some() || cert_dir.is_some()
}

/// Check whether the OpenSSL `SSL_CERT_FILE` and/or `SSL_CERT_DIR` environment variable is
/// configured in this process with an existing file or directory.
///
/// That being the case would indicate that certificates will be found successfully by OpenSSL.
///
/// Returns `true` if either variable is set to an existing file or directory.
pub fn has_ssl_cert_env_vars() -> bool {
let probe = probe_from_env();
probe.cert_file.is_some() || probe.cert_dir.is_some()
}

fn probe_from_env() -> ProbeResult {
let var = |name| env::var_os(name).map(PathBuf::from).filter(|p| p.exists());
ProbeResult {
cert_file: var(ENV_CERT_FILE),
cert_dir: var(ENV_CERT_DIR),
}
}

/// Probe the current system for the "cert file" and "cert dir" variables that
/// OpenSSL typically requires.
///
/// The probe result is returned as a [`ProbeResult`] structure here.
pub fn probe() -> ProbeResult {
let mut result = probe_from_env();
let mut result = ProbeResult::from_env();
for certs_dir in candidate_cert_dirs() {
// 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 cert_filenames = [
"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",
];
if result.cert_file.is_none() {
result.cert_file = cert_filenames
result.cert_file = CERTIFICATE_FILE_NAMES
.iter()
.map(|fname| certs_dir.join(fname))
.find(|p| p.exists());
Expand All @@ -199,3 +78,83 @@ pub fn probe() -> ProbeResult {
}
result
}

/// Probe the system for the directory in which CA certificates should likely be
/// found.
///
/// This will only search known system locations.
pub fn candidate_cert_dirs() -> impl Iterator<Item = &'static Path> {
CERTIFICATE_DIRS
.iter()
.map(Path::new)
.filter(|p| p.exists())
}

/// Check whether the OpenSSL `SSL_CERT_FILE` and/or `SSL_CERT_DIR` environment variable is
/// configured in this process with an existing file or directory.
///
/// That being the case would indicate that certificates will be found successfully by OpenSSL.
///
/// Returns `true` if either variable is set to an existing file or directory.
pub fn has_ssl_cert_env_vars() -> bool {
let probe = ProbeResult::from_env();
probe.cert_file.is_some() || probe.cert_dir.is_some()
}

pub struct ProbeResult {
pub cert_file: Option<PathBuf>,
pub cert_dir: Option<PathBuf>,
}

impl ProbeResult {
fn from_env() -> ProbeResult {
let var = |name| env::var_os(name).map(PathBuf::from).filter(|p| p.exists());
ProbeResult {
cert_file: var(ENV_CERT_FILE),
cert_dir: var(ENV_CERT_DIR),
}
}
}

// see http://gagravarr.org/writing/openssl-certs/others.shtml
const CERTIFICATE_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
#[cfg(target_os = "android")]
"/data/data/com.termux/files/usr/etc/tls",
#[cfg(target_os = "haiku")]
"/boot/system/data/ssl",
];

// cert.pem looks to be an openssl 1.0.1 thing, while
// certs/ca-certificates.crt appears to be a 0.9.8 thing
const CERTIFICATE_FILE_NAMES: &[&str] = &[
"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",
];

/// The OpenSSL environment variable to configure what certificate file to use.
pub const ENV_CERT_FILE: &'static str = "SSL_CERT_FILE";

/// The OpenSSL environment variable to configure what certificates directory to use.
pub const ENV_CERT_DIR: &'static str = "SSL_CERT_DIR";