From e122674e6ff4098dc357e88741c227d73ca8c2ba Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 3 Apr 2024 13:12:21 -0400 Subject: [PATCH] impl SSL_CTX_set_default_verify_paths and friends Adds: * SSL_CTX_set_default_verify_paths * SSL_CTX_set_default_verify_dir * SSL_CTX_set_default_verify_file Stubs: * SSL_CTX_set_default_verify_store We take a dep on `openssl-probe` in order to get convenient handling of the `SSL_CERT_DIR` and `SSL_CERT_FILE` env vars, and default locations for the verify paths. There is likely some fine-tuning to do (e.g. with respect to the `X509_LOOKUP` API surface), but is a step in the right direction for the simple cases. --- rustls-libssl/Cargo.lock | 7 +++++ rustls-libssl/Cargo.toml | 1 + rustls-libssl/build.rs | 4 +++ rustls-libssl/src/entry.rs | 47 ++++++++++++++++++++++++++++++++ rustls-libssl/src/lib.rs | 56 +++++++++++++++++++++++++++++++++++++- 5 files changed, 114 insertions(+), 1 deletion(-) diff --git a/rustls-libssl/Cargo.lock b/rustls-libssl/Cargo.lock index 9b7c53c..6f4098c 100644 --- a/rustls-libssl/Cargo.lock +++ b/rustls-libssl/Cargo.lock @@ -94,6 +94,12 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "openssl-sys" version = "0.9.101" @@ -176,6 +182,7 @@ version = "0.1.0" dependencies = [ "env_logger", "log", + "openssl-probe", "openssl-sys", "rustls", "rustls-pemfile", diff --git a/rustls-libssl/Cargo.toml b/rustls-libssl/Cargo.toml index c81317d..cc7ab01 100644 --- a/rustls-libssl/Cargo.toml +++ b/rustls-libssl/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["cdylib"] [dependencies] env_logger = "0.10" log = "0.4" +openssl-probe = "0.1" openssl-sys = "0.9.98" rustls = "0.22" rustls-pemfile = "2" diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 3889cf6..885b596 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -65,6 +65,10 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_get_cert_store", "SSL_CTX_get_ex_data", "SSL_CTX_get_options", + "SSL_CTX_set_default_verify_paths", + "SSL_CTX_set_default_verify_dir", + "SSL_CTX_set_default_verify_file", + "SSL_CTX_set_default_verify_store", "SSL_CTX_load_verify_dir", "SSL_CTX_load_verify_file", "SSL_CTX_new", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index df36524..8f16f68 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -234,6 +234,48 @@ fn load_verify_files(ctx: &Mutex, file_names: impl Iterator c_int { + let ctx = try_clone_arc!(ctx); + match ctx + .lock() + .map_err(|_| Error::cannot_lock()) + .map(|mut ctx| ctx.set_default_verify_paths()) + { + Err(e) => e.raise().into(), + Ok(()) => C_INT_SUCCESS, + } + } +} + +entry! { + pub fn _SSL_CTX_set_default_verify_dir(ctx: *mut SSL_CTX) -> c_int { + let ctx = try_clone_arc!(ctx); + match ctx + .lock() + .map_err(|_| Error::cannot_lock()) + .map(|mut ctx| ctx.set_default_verify_dir()) + { + Err(e) => e.raise().into(), + Ok(()) => C_INT_SUCCESS, + } + } +} + +entry! { + pub fn _SSL_CTX_set_default_verify_file(ctx: *mut SSL_CTX) -> c_int { + let ctx = try_clone_arc!(ctx); + match ctx + .lock() + .map_err(|_| Error::cannot_lock()) + .map(|mut ctx| ctx.set_default_verify_file()) + { + Err(e) => e.raise().into(), + Ok(()) => C_INT_SUCCESS, + } + } +} + entry! { pub fn _SSL_CTX_load_verify_file(ctx: *mut SSL_CTX, ca_file: *const c_char) -> c_int { let ctx = try_clone_arc!(ctx); @@ -1007,6 +1049,11 @@ entry_stub! { ) -> c_int; } +// The SSL_CTX X509_STORE isn't being meaningfully used yet. +entry_stub! { + pub fn _SSL_CTX_set_default_verify_store(_ctx: *mut SSL_CTX) -> c_int; +} + pub struct SSL_SESSION; entry_stub! { diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 5b9d339..245bddf 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -1,7 +1,10 @@ use core::ffi::{c_int, CStr}; +use std::fs; use std::io::{ErrorKind, Read, Write}; +use std::path::PathBuf; use std::sync::{Arc, Mutex}; +use openssl_probe::ProbeResult; use openssl_sys::{ SSL_ERROR_NONE, SSL_ERROR_SSL, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, X509_STORE, X509_V_ERR_UNSPECIFIED, @@ -205,6 +208,8 @@ pub struct SslContext { verify_roots: RootCertStore, verify_x509_store: x509::OwnedX509Store, alpn: Vec>, + default_cert_file: Option, + default_cert_dir: Option, } impl SslContext { @@ -216,6 +221,8 @@ impl SslContext { verify_roots: RootCertStore::empty(), verify_x509_store: x509::OwnedX509Store::new(), alpn: vec![], + default_cert_file: None, + default_cert_dir: None, } } @@ -237,6 +244,25 @@ impl SslContext { self.verify_mode = mode; } + fn set_default_verify_paths(&mut self) { + let ProbeResult { + cert_file, + cert_dir, + } = openssl_probe::probe(); + self.default_cert_file = cert_file; + self.default_cert_dir = cert_dir; + } + + fn set_default_verify_dir(&mut self) { + let ProbeResult { cert_dir, .. } = openssl_probe::probe(); + self.default_cert_dir = cert_dir; + } + + fn set_default_verify_file(&mut self) { + let ProbeResult { cert_file, .. } = openssl_probe::probe(); + self.default_cert_file = cert_file; + } + fn add_trusted_certs( &mut self, certs: Vec>, @@ -303,7 +329,7 @@ impl Ssl { raw_options: inner.raw_options, mode: inner.method.mode(), verify_mode: inner.verify_mode, - verify_roots: inner.verify_roots.clone(), + verify_roots: Self::load_verify_certs(inner)?, verify_server_name: None, alpn: inner.alpn.clone(), sni_server_name: None, @@ -633,6 +659,34 @@ impl Ssl { None => SSL_ERROR_SSL, } } + + fn load_verify_certs(ctx: &SslContext) -> Result { + let mut verify_roots = ctx.verify_roots.clone(); + + // If verify_roots isn't empty then it was configured with `SSL_CTX_load_verify_file` + // or `SSL_CTX_load_verify_dir` and we should use it as-is. + if !ctx.verify_roots.is_empty() { + return Ok(verify_roots); + } + + // Otherwise, try to load the default cert file or cert dir. + if let Some(default_cert_file) = &ctx.default_cert_file { + verify_roots.add_parsable_certificates(x509::load_certs( + vec![default_cert_file.to_path_buf()].into_iter(), + )?); + } else if let Some(default_cert_dir) = &ctx.default_cert_dir { + let entries = match fs::read_dir(default_cert_dir) { + Ok(iter) => iter, + Err(err) => return Err(error::Error::from_io(err).raise()), + } + .filter_map(|entry| entry.ok()) + .map(|dir_entry| dir_entry.path()); + + verify_roots.add_parsable_certificates(x509::load_certs(entries)?); + } + + Ok(verify_roots) + } } #[derive(Default)]