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
35 changes: 13 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use rustls::pki_types::{CertificateDer, ServerName};
use rustls::server::{Accepted, Acceptor, ProducesTickets};
use rustls::{
CipherSuite, ClientConfig, ClientConnection, Connection, HandshakeKind, ProtocolVersion,
RootCertStore, ServerConfig, SignatureScheme, SupportedProtocolVersion,
ServerConfig, SignatureScheme, SupportedProtocolVersion,
};

use not_thread_safe::NotThreadSafe;
Expand Down Expand Up @@ -416,7 +416,6 @@ pub struct SslContext {
raw_options: u64,
verify_mode: VerifyMode,
verify_depth: c_int,
verify_roots: RootCertStore,
verify_x509_store: x509::OwnedX509Store,
alpn: Vec<Vec<u8>>,
default_cert_file: Option<PathBuf>,
Expand Down Expand Up @@ -447,7 +446,6 @@ impl SslContext {
raw_options: 0,
verify_mode: VerifyMode::default(),
verify_depth: -1,
verify_roots: RootCertStore::empty(),
verify_x509_store: OwnedX509Store::default(),
alpn: vec![],
default_cert_file: None,
Expand Down Expand Up @@ -624,12 +622,7 @@ impl SslContext {
&mut self,
certs: Vec<CertificateDer<'static>>,
) -> Result<(), error::Error> {
for c in certs {
self.verify_roots
.add(c)
.map_err(error::Error::from_rustls)?;
}
Ok(())
self.verify_x509_store.add(certs)
}

fn get_x509_store(&self) -> *mut X509_STORE {
Expand Down Expand Up @@ -735,8 +728,8 @@ struct Ssl {
mode: ConnMode,
verify_mode: VerifyMode,
verify_depth: c_int,
verify_roots: RootCertStore,
verify_server_name: Option<ServerName<'static>>,
verify_x509_store: x509::OwnedX509Store,
alpn: Vec<Vec<u8>>,
alpn_callback: callbacks::AlpnCallbackConfig,
cert_callback: callbacks::CertCallbackConfig,
Expand Down Expand Up @@ -776,8 +769,8 @@ impl Ssl {
mode: inner.method.mode(),
verify_mode: inner.verify_mode,
verify_depth: inner.verify_depth,
verify_roots: Self::load_verify_certs(inner)?,
verify_server_name: None,
verify_x509_store: Self::load_verify_certs(inner)?,
alpn: inner.alpn.clone(),
alpn_callback: inner.alpn_callback.clone(),
cert_callback: inner.cert_callback.clone(),
Expand Down Expand Up @@ -1027,7 +1020,7 @@ impl Ssl {

let provider = Arc::new(provider::default_provider());
let verifier = Arc::new(verifier::ServerVerifier::new(
self.verify_roots.clone().into(),
self.verify_x509_store.clone(),
provider.clone(),
self.verify_mode,
&self.verify_server_name,
Expand Down Expand Up @@ -1112,7 +1105,7 @@ impl Ssl {
let provider = Arc::new(provider::default_provider());
let verifier = Arc::new(
verifier::ClientVerifier::new(
self.verify_roots.clone().into(),
&self.verify_x509_store,
provider.clone(),
self.verify_mode,
)
Expand Down Expand Up @@ -1435,20 +1428,18 @@ impl Ssl {
}
}

fn load_verify_certs(ctx: &SslContext) -> Result<RootCertStore, error::Error> {
let mut verify_roots = ctx.verify_roots.clone();

fn load_verify_certs(ctx: &SslContext) -> Result<x509::OwnedX509Store, error::Error> {
// 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);
if !ctx.verify_x509_store.is_empty() {
return Ok(ctx.verify_x509_store.clone());
}

// Otherwise, try to load the default cert file or cert dir.
let mut verify_roots = x509::OwnedX509Store::default();

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(),
)?);
verify_roots.add_from_files([default_cert_file.to_path_buf()])?;
} else if let Some(default_cert_dir) = &ctx.default_cert_dir {
let entries = match fs::read_dir(default_cert_dir) {
Ok(iter) => iter,
Expand All @@ -1457,7 +1448,7 @@ impl Ssl {
.filter_map(|entry| entry.ok())
.map(|dir_entry| dir_entry.path());

verify_roots.add_parsable_certificates(x509::load_certs(entries)?);
verify_roots.add_from_files(entries)?;
}

Ok(verify_roots)
Expand Down
10 changes: 10 additions & 0 deletions src/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ pub extern "C" fn X509_STORE_new() -> *mut X509_STORE {
Box::into_raw(Box::new(X509_STORE(())))
}

#[no_mangle]
pub extern "C" fn X509_STORE_get0_objects(s: *mut X509_STORE) -> *mut c_void {
ptr::null_mut()
}

#[no_mangle]
pub extern "C" fn OPENSSL_sk_num(sk: *mut c_void) -> c_int {
0
}

#[no_mangle]
pub extern "C" fn X509_STORE_free(ptr: *mut X509_STORE) {
if ptr.is_null() {
Expand Down
22 changes: 14 additions & 8 deletions src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ use rustls::{
pki_types::{CertificateDer, ServerName, UnixTime},
server::danger::{ClientCertVerified, ClientCertVerifier},
server::{ParsedCertificate, WebPkiClientVerifier},
CertificateError, DigitallySignedStruct, DistinguishedName, Error, RootCertStore,
SignatureScheme,
CertificateError, DigitallySignedStruct, DistinguishedName, Error, SignatureScheme,
};

use crate::x509::OwnedX509Store;
use crate::VerifyMode;

/// This is a verifier that implements the selection of bad ideas from OpenSSL:
Expand All @@ -29,7 +29,7 @@ use crate::VerifyMode;
/// - that the behaviour defaults to verifying nothing
#[derive(Debug)]
pub struct ServerVerifier {
root_store: Arc<RootCertStore>,
x509_store: OwnedX509Store,

provider: Arc<CryptoProvider>,

Expand All @@ -47,13 +47,13 @@ pub struct ServerVerifier {

impl ServerVerifier {
pub fn new(
root_store: Arc<RootCertStore>,
x509_store: OwnedX509Store,
provider: Arc<CryptoProvider>,
mode: VerifyMode,
hostname: &Option<ServerName<'static>>,
) -> Self {
Self {
root_store,
x509_store,
provider,
verify_hostname: hostname.clone(),
mode,
Expand Down Expand Up @@ -81,10 +81,14 @@ impl ServerVerifier {
now: UnixTime,
) -> Result<(), Error> {
let end_entity = ParsedCertificate::try_from(end_entity)?;
let root_store = self.x509_store.to_root_store()?;
if root_store.is_empty() {
log::warn!("X509_STORE has no cached certificates");
}

verify_server_cert_signed_by_trust_anchor(
&end_entity,
&self.root_store,
&root_store,
intermediates,
now,
self.provider.signature_verification_algorithms.all,
Expand Down Expand Up @@ -173,14 +177,16 @@ pub struct ClientVerifier {

impl ClientVerifier {
pub fn new(
root_store: Arc<RootCertStore>,
x509_store: &OwnedX509Store,
provider: Arc<CryptoProvider>,
mode: VerifyMode,
) -> Result<Self, Error> {
let root_store = x509_store.to_root_store()?;

let (parent, initial_result) = if !mode.server_must_attempt_client_auth() {
(Ok(WebPkiClientVerifier::no_client_auth()), X509_V_OK)
} else {
let builder = WebPkiClientVerifier::builder_with_provider(root_store, provider);
let builder = WebPkiClientVerifier::builder_with_provider(root_store.into(), provider);

if mode.server_must_verify_client() {
(builder.build(), X509_V_ERR_UNSPECIFIED)
Expand Down
73 changes: 72 additions & 1 deletion src/x509.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ use std::{fs, io};

use openssl_sys::{
d2i_X509, i2d_X509, stack_st_X509, OPENSSL_free, OPENSSL_sk_new_null, OPENSSL_sk_num,
OPENSSL_sk_push, OPENSSL_sk_value, X509_STORE_free, X509_STORE_new, X509_free, OPENSSL_STACK,
OPENSSL_sk_push, OPENSSL_sk_value, X509_STORE_add_cert, X509_STORE_free,
X509_STORE_get0_objects, X509_STORE_get1_all_certs, X509_STORE_new, X509_free, OPENSSL_STACK,
X509, X509_STORE,
};
use rustls::pki_types::pem::PemObject;
use rustls::pki_types::CertificateDer;
use rustls::RootCertStore;

use crate::error::Error;

Expand Down Expand Up @@ -233,6 +235,7 @@ impl Drop for OwnedX509 {
}
}

#[derive(Debug)]
pub struct OwnedX509Store {
raw: *mut X509_STORE,
}
Expand All @@ -243,9 +246,64 @@ impl OwnedX509Store {
Self { raw: store }
}

pub fn add(
&mut self,
cert_ders: impl IntoIterator<Item = CertificateDer<'static>>,
) -> Result<(), Error> {
for cert_der in cert_ders {
let item = OwnedX509::parse_der(cert_der.as_ref())
.ok_or_else(|| Error::bad_data("cannot parse certificate"))?;
match unsafe { X509_STORE_add_cert(self.raw, item.borrow_ref()) } {
1 => {}
_ => {
return Err(Error::bad_data("cannot X509_STORE_add_cert"));
}
}
}

Ok(())
}

pub fn add_from_files(
&mut self,
file_names: impl IntoIterator<Item = PathBuf>,
) -> Result<(), Error> {
self.add(load_certs(file_names.into_iter())?)
}

pub fn to_root_store(&self) -> Result<RootCertStore, rustls::Error> {
let ptr = unsafe { X509_STORE_get1_all_certs(self.raw) };

if ptr.is_null() {
return Err(rustls::Error::General(
"cannot retrieve trusted certs".to_string(),
));
}

let certs = OwnedX509Stack::new(ptr);
let mut ret = RootCertStore::empty();

for i in 0..certs.len() {
ret.add(certs.item(i).der_bytes().into())?;
}

Ok(ret)
}

pub fn pointer(&self) -> *mut X509_STORE {
self.raw
}

pub fn len(&self) -> usize {
match unsafe { OPENSSL_sk_num(X509_STORE_get0_objects(self.raw) as *const OPENSSL_STACK) } {
-1 | 0 => 0,
i => i as usize,
}
}

pub fn is_empty(&self) -> bool {
self.len() == 0
}
}

impl Default for OwnedX509Store {
Expand All @@ -256,6 +314,15 @@ impl Default for OwnedX509Store {
}
}

impl Clone for OwnedX509Store {
fn clone(&self) -> Self {
unsafe {
X509_STORE_up_ref(self.raw);
}
Self { raw: self.raw }
}
}

impl Drop for OwnedX509Store {
fn drop(&mut self) {
unsafe {
Expand All @@ -264,6 +331,9 @@ impl Drop for OwnedX509Store {
}
}

unsafe impl Send for OwnedX509Store {}
unsafe impl Sync for OwnedX509Store {}

pub(crate) fn load_certs<'a>(
file_names: impl Iterator<Item = PathBuf>,
) -> Result<Vec<CertificateDer<'a>>, Error> {
Expand Down Expand Up @@ -295,4 +365,5 @@ extern "C" {
);
fn OPENSSL_sk_dup(st: *const OPENSSL_STACK) -> *mut OPENSSL_STACK;
fn X509_up_ref(x: *mut X509) -> c_int;
fn X509_STORE_up_ref(xs: *mut X509_STORE) -> c_int;
}
4 changes: 4 additions & 0 deletions tests/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ int main(int argc, char **argv) {
dump_openssl_error_stack();
assert(SSL_CTX_get_verify_mode(ctx) == SSL_VERIFY_PEER);
assert(SSL_CTX_get_verify_callback(ctx) == NULL);
TRACE(sk_X509_OBJECT_num(
X509_STORE_get0_objects(SSL_CTX_get_cert_store(ctx))));
TRACE(SSL_CTX_load_verify_file(ctx, cacert));
dump_openssl_error_stack();
TRACE(sk_X509_OBJECT_num(
X509_STORE_get0_objects(SSL_CTX_get_cert_store(ctx))));
}
printf("SSL_CTX_get_verify_depth default %d\n",
SSL_CTX_get_verify_depth(ctx));
Expand Down
4 changes: 4 additions & 0 deletions tests/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,12 @@ int main(int argc, char **argv) {
if (strcmp(cacert, "unauth") != 0) {
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
dump_openssl_error_stack();
TRACE(sk_X509_OBJECT_num(
X509_STORE_get0_objects(SSL_CTX_get_cert_store(ctx))));
TRACE(SSL_CTX_load_verify_file(ctx, cacert));
dump_openssl_error_stack();
TRACE(sk_X509_OBJECT_num(
X509_STORE_get0_objects(SSL_CTX_get_cert_store(ctx))));
} else {
printf("client auth disabled\n");
}
Expand Down