From 1ea02b128b37d5bc1c1edf84b3a2a051ed259a0e Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Mon, 2 Oct 2023 12:30:05 -0400 Subject: [PATCH 01/19] deps: use rustls/webpki/rustls-pemfile alphas. rustls 0.21.5 -> 0.22.0-alpha.4 webpki 0.101.0 -> 0.102.0-alpha.6 rustls-pemfile 1.0.3 -> 2.0.0-alpha.1 adds rustls-pki-types 0.2.1 --- Cargo.lock | 37 ++++++++++++++++++++++++++++++------- Cargo.toml | 7 ++++--- build.rs | 2 +- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99bd4b61..fdd51abe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,15 +106,17 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.9" +version = "0.22.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "7c23376606de66c7b9249d091b59ee55b52df72063e1cae7bb44e0691c9e5150" dependencies = [ "log", "ring", + "rustls-pki-types", "rustls-webpki", "rustversion", - "sct", + "subtle", + "zeroize", ] [[package]] @@ -126,26 +128,35 @@ dependencies = [ "regex", "rustls", "rustls-pemfile", + "rustls-pki-types", "rustls-webpki", "sct", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.0.0-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "4aaa4fe93b39faddb6a8f99568c3e5880680156da0d46818e884a071381f67fe" dependencies = [ "base64", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47003264dea418db67060fa420ad16d0d2f8f0a0360d825c00e177ac52cb5d8" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "34d9ed3a8267782ba32d257ff5b197b63eef19a467dbd1be011caaae35ee416e" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -171,6 +182,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "untrusted" version = "0.9.0" @@ -248,3 +265,9 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index 17804270..84f96eb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,11 +23,12 @@ read_buf = ["rustls/read_buf"] [dependencies] # Keep in sync with RUSTLS_CRATE_VERSION in build.rs -rustls = { version = "=0.21.9", features = [ "dangerous_configuration" ] } -rustls-webpki = "0.101.0" +rustls = { version = "=0.22.0-alpha.4", features = [ "ring" ]} +rustls-webpki = "0.102.0-alpha.6" +pki-types = { package = "rustls-pki-types", version = "0.2.1", features = ["std"] } libc = "0.2" sct = "0.7" -rustls-pemfile = "1.0.3" +rustls-pemfile = { version = "2.0.0-alpha.1" } log = "0.4.17" [lib] diff --git a/build.rs b/build.rs index f259746e..a22bddc2 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,7 @@ use std::io::Write; use std::{env, fs, path::PathBuf}; // Keep in sync with Cargo.toml. -const RUSTLS_CRATE_VERSION: &str = "0.21.9"; +const RUSTLS_CRATE_VERSION: &str = "0.22.0-alpha.4"; fn main() { let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); From ff8bbfded71b9523687fa05d86264cb7f485676a Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Thu, 3 Aug 2023 13:54:58 -0400 Subject: [PATCH 02/19] error: track upstream removal of cert SCT support. This commit removes the error handling related to certificate SCTs. The upstream Rustls project removed embedded SCT support in 0.22.x. --- src/client.rs | 2 -- src/error.rs | 42 ++++++------------------------------------ src/rustls.h | 5 ----- 3 files changed, 6 insertions(+), 43 deletions(-) diff --git a/src/client.rs b/src/client.rs index 6fef5af5..52e6a7a3 100644 --- a/src/client.rs +++ b/src/client.rs @@ -76,7 +76,6 @@ impl ServerCertVerifier for NoneVerifier { _end_entity: &Certificate, _intermediates: &[Certificate], _server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, _ocsp_response: &[u8], _now: SystemTime, ) -> Result { @@ -231,7 +230,6 @@ impl rustls::client::ServerCertVerifier for Verifier { end_entity: &Certificate, intermediates: &[Certificate], server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, ocsp_response: &[u8], _now: SystemTime, ) -> Result { diff --git a/src/error.rs b/src/error.rs index 97a750f6..28075644 100644 --- a/src/error.rs +++ b/src/error.rs @@ -162,12 +162,12 @@ u32_enum_builder! { AlertNoApplicationProtocol => 7233, AlertUnknown => 7234, - // https://docs.rs/sct/latest/sct/enum.Error.html - CertSCTMalformed => 7319, - CertSCTInvalidSignature => 7320, - CertSCTTimestampInFuture => 7321, - CertSCTUnsupportedVersion => 7322, - CertSCTUnknownLog => 7323, + // Reserved from previous use pre rustls-ffi <0.22.0 + // CertSCTMalformed => 7319, + // CertSCTInvalidSignature => 7320, + // CertSCTTimestampInFuture => 7321, + // CertSCTUnsupportedVersion => 7322, + // CertSCTUnknownLog => 7323, // From InvalidCertRevocationList, with fields that get flattened. // https://docs.rs/rustls/0.21.6/rustls/enum.Error.html#variant.InvalidCertRevocationList @@ -229,11 +229,6 @@ impl rustls_result { | CertInvalidPurpose | CertApplicationVerificationFailure | CertOtherError - | CertSCTMalformed - | CertSCTInvalidSignature - | CertSCTTimestampInFuture - | CertSCTUnsupportedVersion - | CertSCTUnknownLog ) } } @@ -259,11 +254,6 @@ pub(crate) fn cert_result_to_error(result: rustls_result) -> rustls::Error { InvalidCertificate(CertificateError::ApplicationVerificationFailure) } CertOtherError => InvalidCertificate(CertificateError::Other(Arc::from(Box::from("")))), - CertSCTMalformed => InvalidSct(sct::Error::MalformedSct), - CertSCTInvalidSignature => InvalidSct(sct::Error::InvalidSignature), - CertSCTTimestampInFuture => InvalidSct(sct::Error::TimestampInFuture), - CertSCTUnsupportedVersion => InvalidSct(sct::Error::UnsupportedSctVersion), - CertSCTUnknownLog => InvalidSct(sct::Error::UnknownLog), _ => rustls::Error::General("".into()), } } @@ -294,17 +284,11 @@ fn test_rustls_result_is_cert_error() { for id in 7121..=7131 { assert!(rustls_result::rustls_result_is_cert_error(id)); } - - // Test SCTError range - for id in 7319..=7323 { - assert!(rustls_result::rustls_result_is_cert_error(id)); - } } pub(crate) fn map_error(input: rustls::Error) -> rustls_result { use rustls::AlertDescription as alert; use rustls_result::*; - use sct::Error as sct; match input { Error::InappropriateMessage { .. } => InappropriateMessage, @@ -402,13 +386,6 @@ pub(crate) fn map_error(input: rustls::Error) -> rustls_result { alert::Unknown(_) => AlertUnknown, _ => AlertUnknown, }, - Error::InvalidSct(e) => match e { - sct::MalformedSct => CertSCTMalformed, - sct::InvalidSignature => CertSCTInvalidSignature, - sct::TimestampInFuture => CertSCTTimestampInFuture, - sct::UnsupportedSctVersion => CertSCTUnsupportedVersion, - sct::UnknownLog => CertSCTUnknownLog, - }, Error::InvalidCertRevocationList(e) => match e { CertRevocationListError::BadSignature => CertRevocationListBadSignature, @@ -443,7 +420,6 @@ impl Display for rustls_result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use rustls::AlertDescription as alert; use rustls_result::*; - use sct::Error as sct; match self { // These variants are local to this glue layer. @@ -623,12 +599,6 @@ impl Display for rustls_result { AlertNoApplicationProtocol => Error::AlertReceived(alert::NoApplicationProtocol).fmt(f), AlertUnknown => Error::AlertReceived(alert::Unknown(0)).fmt(f), - CertSCTMalformed => Error::InvalidSct(sct::MalformedSct).fmt(f), - CertSCTInvalidSignature => Error::InvalidSct(sct::InvalidSignature).fmt(f), - CertSCTTimestampInFuture => Error::InvalidSct(sct::TimestampInFuture).fmt(f), - CertSCTUnsupportedVersion => Error::InvalidSct(sct::UnsupportedSctVersion).fmt(f), - CertSCTUnknownLog => Error::InvalidSct(sct::UnknownLog).fmt(f), - CertRevocationListBadSignature => { Error::InvalidCertRevocationList(CertRevocationListError::BadSignature).fmt(f) } diff --git a/src/rustls.h b/src/rustls.h index 65e3ee3c..d485e04a 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -105,11 +105,6 @@ enum rustls_result { RUSTLS_RESULT_ALERT_CERTIFICATE_REQUIRED = 7232, RUSTLS_RESULT_ALERT_NO_APPLICATION_PROTOCOL = 7233, RUSTLS_RESULT_ALERT_UNKNOWN = 7234, - RUSTLS_RESULT_CERT_SCT_MALFORMED = 7319, - RUSTLS_RESULT_CERT_SCT_INVALID_SIGNATURE = 7320, - RUSTLS_RESULT_CERT_SCT_TIMESTAMP_IN_FUTURE = 7321, - RUSTLS_RESULT_CERT_SCT_UNSUPPORTED_VERSION = 7322, - RUSTLS_RESULT_CERT_SCT_UNKNOWN_LOG = 7323, RUSTLS_RESULT_CERT_REVOCATION_LIST_BAD_SIGNATURE = 7400, RUSTLS_RESULT_CERT_REVOCATION_LIST_INVALID_CRL_NUMBER = 7401, RUSTLS_RESULT_CERT_REVOCATION_LIST_INVALID_REVOKED_CERT_SERIAL_NUMBER = 7402, From 3be81172bd623c2542f73e150ed67fed02b041f0 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Thu, 3 Aug 2023 15:57:41 -0400 Subject: [PATCH 03/19] client: WebPkiVerifier -> WebPkiServerVerifier. --- src/client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index 52e6a7a3..af386434 100644 --- a/src/client.rs +++ b/src/client.rs @@ -324,7 +324,7 @@ impl rustls_client_config_builder { ffi_panic_boundary! { let builder = try_mut_from_ptr!(config_builder); let root_store: &RootCertStore = try_ref_from_ptr!(roots); - builder.verifier = Arc::new(rustls::client::WebPkiVerifier::new(root_store.clone(), None)); + builder.verifier = Arc::new(rustls::client::WebPkiServerVerifier::new(root_store.clone())); rustls_result::Ok } } @@ -368,7 +368,7 @@ impl rustls_client_config_builder { return rustls_result::CertificateParseError; } - config_builder.verifier = Arc::new(rustls::client::WebPkiVerifier::new(roots, None)); + config_builder.verifier = Arc::new(rustls::client::WebPkiServerVerifier::new(roots)); rustls_result::Ok } } From b556bf3417c61a30019d037609d41a0b513f5e5c Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Thu, 10 Aug 2023 15:14:44 -0400 Subject: [PATCH 04/19] client: fixes for updated verifier traits. The upstream traits no longer have any default fn implementations, because they relied on webpki/*ring* and Rustls is making that optional. In this branch we're continuing to keep a webpki/*ring* dep. and so can reconstitute the default fns by deferring to the webpki impls as appropriate. --- src/client.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index af386434..a1248d71 100644 --- a/src/client.rs +++ b/src/client.rs @@ -8,10 +8,14 @@ use std::sync::Arc; use std::time::SystemTime; use libc::{c_char, size_t}; -use rustls::client::{ResolvesClientCert, ServerCertVerified, ServerCertVerifier}; +use rustls::client::{ + HandshakeSignatureValid, ResolvesClientCert, ServerCertVerified, ServerCertVerifier, + WebPkiServerVerifier, +}; use rustls::{ sign::CertifiedKey, Certificate, CertificateError, ClientConfig, ClientConnection, - ProtocolVersion, RootCertStore, SupportedCipherSuite, WantsVerifier, ALL_CIPHER_SUITES, + DigitallySignedStruct, Error, ProtocolVersion, RootCertStore, SignatureScheme, + SupportedCipherSuite, WantsVerifier, ALL_CIPHER_SUITES, }; use crate::cipher::{rustls_certified_key, rustls_root_cert_store, rustls_supported_ciphersuite}; @@ -83,6 +87,28 @@ impl ServerCertVerifier for NoneVerifier { CertificateError::BadSignature, )) } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &Certificate, + _dss: &DigitallySignedStruct, + ) -> Result { + Err(Error::InvalidCertificate(CertificateError::BadSignature)) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &Certificate, + _dss: &DigitallySignedStruct, + ) -> Result { + Err(Error::InvalidCertificate(CertificateError::BadSignature)) + } + + fn supported_verify_schemes(&self) -> Vec { + WebPkiServerVerifier::default_supported_verify_schemes() + } } impl rustls_client_config_builder { @@ -265,6 +291,28 @@ impl rustls::client::ServerCertVerifier for Verifier { r => Err(error::cert_result_to_error(r)), } } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &Certificate, + dss: &DigitallySignedStruct, + ) -> Result { + WebPkiServerVerifier::default_verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &Certificate, + dss: &DigitallySignedStruct, + ) -> Result { + WebPkiServerVerifier::default_verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + WebPkiServerVerifier::default_supported_verify_schemes() + } } impl rustls_client_config_builder { From 17accd8717adc26b0411876ef2e51c2d9d0d45db Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 6 Oct 2023 13:25:02 -0400 Subject: [PATCH 05/19] use danger modules for dangerous bits This commit updates several imports that were once provided when the `dangerous_configuration` feature was enabled to use their new homes in specific `danger` modules. The upstream feature flag was removed and these new `danger` modules are always available. --- src/client.rs | 10 ++++------ src/server.rs | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/client.rs b/src/client.rs index a1248d71..cb2ccdd5 100644 --- a/src/client.rs +++ b/src/client.rs @@ -8,10 +8,8 @@ use std::sync::Arc; use std::time::SystemTime; use libc::{c_char, size_t}; -use rustls::client::{ - HandshakeSignatureValid, ResolvesClientCert, ServerCertVerified, ServerCertVerifier, - WebPkiServerVerifier, -}; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; +use rustls::client::{ResolvesClientCert, WebPkiServerVerifier}; use rustls::{ sign::CertifiedKey, Certificate, CertificateError, ClientConfig, ClientConnection, DigitallySignedStruct, Error, ProtocolVersion, RootCertStore, SignatureScheme, @@ -250,7 +248,7 @@ unsafe impl Send for Verifier {} /// rustls_client_config_builder_dangerous_set_certificate_verifier. unsafe impl Sync for Verifier {} -impl rustls::client::ServerCertVerifier for Verifier { +impl rustls::client::danger::ServerCertVerifier for Verifier { fn verify_server_cert( &self, end_entity: &Certificate, @@ -530,7 +528,7 @@ impl rustls_client_config_builder { ) -> *const rustls_client_config { ffi_panic_boundary! { let builder: Box = try_box_from_ptr!(builder); - let config = builder.base.with_custom_certificate_verifier(builder.verifier); + let config = builder.base.dangerous().with_custom_certificate_verifier(builder.verifier); let mut config = match builder.cert_resolver { Some(r) => config.with_client_cert_resolver(r), None => config.with_no_client_auth(), diff --git a/src/server.rs b/src/server.rs index 2234af10..4758865a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -5,10 +5,10 @@ use std::slice; use std::sync::Arc; use libc::size_t; +use rustls::server::danger::ClientCertVerifier; use rustls::server::{ - AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientCertVerifier, - ClientHello, NoClientAuth, ResolvesServerCert, ServerConfig, ServerConnection, - StoresServerSessions, + AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientHello, NoClientAuth, + ResolvesServerCert, ServerConfig, ServerConnection, StoresServerSessions, }; use rustls::sign::CertifiedKey; use rustls::{ From fb67a90047bd1a403ab78dc85ca40994331a2436 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 6 Oct 2023 13:27:46 -0400 Subject: [PATCH 06/19] fix ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES imports Both the `ALL_CIPHER_SUITES` and `DEFAULT_CIPHER_SUITES` symbols are now specific to a crypto provider. Since for the time being rustls-ffi will stick with using *ring* for the crypto provider this commit updates the imports to use the symbols provided by `rustls::crypto::ring` instead of the crate root. --- src/cipher.rs | 6 ++---- src/client.rs | 3 ++- src/connection.rs | 5 ++--- src/server.rs | 5 ++--- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/cipher.rs b/src/cipher.rs index 463f0726..9cbdded1 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -5,14 +5,12 @@ use std::ptr::null; use std::slice; use std::sync::Arc; +use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES}; use rustls::server::{ AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, UnparsedCertRevocationList, }; use rustls::sign::CertifiedKey; -use rustls::{ - Certificate, PrivateKey, RootCertStore, SupportedCipherSuite, ALL_CIPHER_SUITES, - DEFAULT_CIPHER_SUITES, -}; +use rustls::{Certificate, PrivateKey, RootCertStore, SupportedCipherSuite}; use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys}; use crate::error::{map_error, rustls_result}; diff --git a/src/client.rs b/src/client.rs index cb2ccdd5..0562a8fc 100644 --- a/src/client.rs +++ b/src/client.rs @@ -10,10 +10,11 @@ use std::time::SystemTime; use libc::{c_char, size_t}; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; use rustls::client::{ResolvesClientCert, WebPkiServerVerifier}; +use rustls::crypto::ring::ALL_CIPHER_SUITES; use rustls::{ sign::CertifiedKey, Certificate, CertificateError, ClientConfig, ClientConnection, DigitallySignedStruct, Error, ProtocolVersion, RootCertStore, SignatureScheme, - SupportedCipherSuite, WantsVerifier, ALL_CIPHER_SUITES, + SupportedCipherSuite, WantsVerifier, }; use crate::cipher::{rustls_certified_key, rustls_root_cert_store, rustls_supported_ciphersuite}; diff --git a/src/connection.rs b/src/connection.rs index 49e93b00..0b5f9c9c 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -3,9 +3,8 @@ use std::{ffi::c_void, ptr::null}; use std::{ptr::null_mut, slice}; use libc::{size_t, EINVAL, EIO}; -use rustls::{ - Certificate, ClientConnection, ServerConnection, SupportedCipherSuite, ALL_CIPHER_SUITES, -}; +use rustls::crypto::ring::ALL_CIPHER_SUITES; +use rustls::{Certificate, ClientConnection, ServerConnection, SupportedCipherSuite}; use crate::io::{ rustls_write_vectored_callback, CallbackReader, CallbackWriter, ReadCallback, diff --git a/src/server.rs b/src/server.rs index 4758865a..792e41ec 100644 --- a/src/server.rs +++ b/src/server.rs @@ -5,15 +5,14 @@ use std::slice; use std::sync::Arc; use libc::size_t; +use rustls::crypto::ring::ALL_CIPHER_SUITES; use rustls::server::danger::ClientCertVerifier; use rustls::server::{ AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientHello, NoClientAuth, ResolvesServerCert, ServerConfig, ServerConnection, StoresServerSessions, }; use rustls::sign::CertifiedKey; -use rustls::{ - ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier, ALL_CIPHER_SUITES, -}; +use rustls::{ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier}; use crate::cipher::{ rustls_allow_any_anonymous_or_authenticated_client_verifier, From fbba68dc549c83ce16adf1814d5e2ea730bd6268 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 6 Oct 2023 13:36:27 -0400 Subject: [PATCH 07/19] switch to pki-types This commit updates rustls-ffi to use the shared pki-types crate, similar to the upstream rustls projects. --- src/cipher.rs | 76 +++++++++++++++++++++++------------------------ src/client.rs | 32 ++++++++++---------- src/connection.rs | 9 +++--- src/rustls.h | 4 +-- 4 files changed, 60 insertions(+), 61 deletions(-) diff --git a/src/cipher.rs b/src/cipher.rs index 9cbdded1..ec288db9 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -1,16 +1,16 @@ use libc::size_t; use std::convert::TryFrom; use std::io::Cursor; +use std::marker::PhantomData; use std::ptr::null; use std::slice; use std::sync::Arc; +use pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer}; use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES}; -use rustls::server::{ - AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, UnparsedCertRevocationList, -}; +use rustls::server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient}; use rustls::sign::CertifiedKey; -use rustls::{Certificate, PrivateKey, RootCertStore, SupportedCipherSuite}; +use rustls::{RootCertStore, SupportedCipherSuite}; use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys}; use crate::error::{map_error, rustls_result}; @@ -23,21 +23,22 @@ use crate::{ use rustls_result::NullParameter; /// An X.509 certificate, as used in rustls. -/// Corresponds to `Certificate` in the Rust API. -/// -pub struct rustls_certificate { +/// Corresponds to `CertificateDer` in the Rust pki-types API. +/// +pub struct rustls_certificate<'a> { // We use the opaque struct pattern to tell C about our types without // telling them what's inside. // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs _private: [u8; 0], + _marker: PhantomData<&'a ()>, } -impl Castable for rustls_certificate { +impl<'a> Castable for rustls_certificate<'a> { type Ownership = OwnershipRef; - type RustType = Certificate; + type RustType = CertificateDer<'a>; } -impl rustls_certificate { +impl<'a> rustls_certificate<'a> { /// Get the DER data of the certificate itself. /// The data is owned by the certificate and has the same lifetime. #[no_mangle] @@ -327,14 +328,14 @@ impl rustls_certified_key { /// /// The returned certificate is valid until the rustls_certified_key is freed. #[no_mangle] - pub extern "C" fn rustls_certified_key_get_certificate( + pub extern "C" fn rustls_certified_key_get_certificate<'a>( certified_key: *const rustls_certified_key, i: size_t, - ) -> *const rustls_certificate { + ) -> *const rustls_certificate<'a> { ffi_panic_boundary! { let certified_key: &CertifiedKey = try_ref_from_ptr!(certified_key); match certified_key.cert.get(i) { - Some(cert) => cert as *const Certificate as *const _, + Some(cert) => cert as *const CertificateDer as *const _, None => null() } } @@ -396,37 +397,32 @@ impl rustls_certified_key { } slice::from_raw_parts(cert_chain, cert_chain_len) }; - let private_key: &[u8] = unsafe { + let private_key_der: &[u8] = unsafe { if private_key.is_null() { return Err(NullParameter); } slice::from_raw_parts(private_key, private_key_len) }; - let mut private_keys: Vec> = match pkcs8_private_keys(&mut Cursor::new(private_key)) - { - Ok(v) => v, - Err(_) => return Err(rustls_result::PrivateKeyParseError), - }; - let private_key: PrivateKey = match private_keys.pop() { - Some(p) => PrivateKey(p), - None => { - private_keys = match rsa_private_keys(&mut Cursor::new(private_key)) { - Ok(v) => v, - Err(_) => return Err(rustls_result::PrivateKeyParseError), - }; - let rsa_private_key: PrivateKey = match private_keys.pop() { - Some(p) => PrivateKey(p), - None => return Err(rustls_result::PrivateKeyParseError), - }; - rsa_private_key - } - }; + let private_key: PrivateKeyDer = + match pkcs8_private_keys(&mut Cursor::new(private_key_der)).next() { + Some(Ok(p)) => p.into(), + Some(Err(_)) => return Err(rustls_result::PrivateKeyParseError), + None => { + let rsa_private_key: PrivateKeyDer = + match rsa_private_keys(&mut Cursor::new(private_key_der)).next() { + Some(Ok(p)) => p.into(), + _ => return Err(rustls_result::PrivateKeyParseError), + }; + rsa_private_key + } + }; let signing_key = match rustls::sign::any_supported_type(&private_key) { Ok(key) => key, Err(_) => return Err(rustls_result::PrivateKeyParseError), }; - let parsed_chain: Vec = match certs(&mut cert_chain) { - Ok(v) => v.into_iter().map(Certificate).collect(), + let parsed_chain: Result, _> = certs(&mut cert_chain).collect(); + let parsed_chain = match parsed_chain { + Ok(v) => v, Err(_) => return Err(rustls_result::CertificateParseError), }; @@ -480,7 +476,8 @@ impl rustls_root_cert_store { let certs_pem: &[u8] = try_slice!(pem, pem_len); let store: &mut RootCertStore = try_mut_from_ptr!(store); - let certs_der = match rustls_pemfile::certs(&mut Cursor::new(certs_pem)) { + let certs_der: Result, _> = rustls_pemfile::certs(&mut Cursor::new(certs_pem)).collect(); + let certs_der = match certs_der { Ok(vv) => vv, Err(_) => return rustls_result::CertificateParseError, }; @@ -488,7 +485,7 @@ impl rustls_root_cert_store { // API guideline that there are no partial failures or partial // successes. let mut new_store = RootCertStore::empty(); - let (parsed, rejected) = new_store.add_parsable_certificates(&certs_der); + let (parsed, rejected) = new_store.add_parsable_certificates(certs_der); if strict && (rejected > 0 || parsed == 0) { return rustls_result::CertificateParseError; } @@ -559,8 +556,9 @@ impl rustls_allow_any_authenticated_client_builder { let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); let crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len); - let crls_der: Vec = match crls(&mut Cursor::new(crl_pem)) { - Ok(vv) => vv.into_iter().map(UnparsedCertRevocationList).collect(), + let crls_der: Result, _> = crls(&mut Cursor::new(crl_pem)).collect(); + let crls_der = match crls_der{ + Ok(vv) => vv, Err(_) => return rustls_result::CertificateRevocationListParseError, }; diff --git a/src/client.rs b/src/client.rs index 0562a8fc..11f36c8b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -5,16 +5,15 @@ use std::fs::File; use std::io::BufReader; use std::slice; use std::sync::Arc; -use std::time::SystemTime; use libc::{c_char, size_t}; +use pki_types::{CertificateDer, UnixTime}; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; use rustls::client::{ResolvesClientCert, WebPkiServerVerifier}; use rustls::crypto::ring::ALL_CIPHER_SUITES; use rustls::{ - sign::CertifiedKey, Certificate, CertificateError, ClientConfig, ClientConnection, - DigitallySignedStruct, Error, ProtocolVersion, RootCertStore, SignatureScheme, - SupportedCipherSuite, WantsVerifier, + sign::CertifiedKey, CertificateError, ClientConfig, ClientConnection, DigitallySignedStruct, + Error, ProtocolVersion, RootCertStore, SignatureScheme, SupportedCipherSuite, WantsVerifier, }; use crate::cipher::{rustls_certified_key, rustls_root_cert_store, rustls_supported_ciphersuite}; @@ -76,11 +75,11 @@ struct NoneVerifier; impl ServerCertVerifier for NoneVerifier { fn verify_server_cert( &self, - _end_entity: &Certificate, - _intermediates: &[Certificate], + _end_entity: &CertificateDer, + _intermediates: &[CertificateDer], _server_name: &rustls::ServerName, _ocsp_response: &[u8], - _now: SystemTime, + _now: UnixTime, ) -> Result { Err(rustls::Error::InvalidCertificate( CertificateError::BadSignature, @@ -90,7 +89,7 @@ impl ServerCertVerifier for NoneVerifier { fn verify_tls12_signature( &self, _message: &[u8], - _cert: &Certificate, + _cert: &CertificateDer, _dss: &DigitallySignedStruct, ) -> Result { Err(Error::InvalidCertificate(CertificateError::BadSignature)) @@ -99,7 +98,7 @@ impl ServerCertVerifier for NoneVerifier { fn verify_tls13_signature( &self, _message: &[u8], - _cert: &Certificate, + _cert: &CertificateDer, _dss: &DigitallySignedStruct, ) -> Result { Err(Error::InvalidCertificate(CertificateError::BadSignature)) @@ -252,11 +251,11 @@ unsafe impl Sync for Verifier {} impl rustls::client::danger::ServerCertVerifier for Verifier { fn verify_server_cert( &self, - end_entity: &Certificate, - intermediates: &[Certificate], + end_entity: &CertificateDer, + intermediates: &[CertificateDer], server_name: &rustls::ServerName, ocsp_response: &[u8], - _now: SystemTime, + _now: UnixTime, ) -> Result { let cb = self.callback; let server_name: Cow<'_, str> = match server_name { @@ -294,7 +293,7 @@ impl rustls::client::danger::ServerCertVerifier for Verifier { fn verify_tls12_signature( &self, message: &[u8], - cert: &Certificate, + cert: &CertificateDer, dss: &DigitallySignedStruct, ) -> Result { WebPkiServerVerifier::default_verify_tls12_signature(message, cert, dss) @@ -303,7 +302,7 @@ impl rustls::client::danger::ServerCertVerifier for Verifier { fn verify_tls13_signature( &self, message: &[u8], - cert: &Certificate, + cert: &CertificateDer, dss: &DigitallySignedStruct, ) -> Result { WebPkiServerVerifier::default_verify_tls13_signature(message, cert, dss) @@ -404,13 +403,14 @@ impl rustls_client_config_builder { }; let mut bufreader = BufReader::new(&mut cafile); - let certs = match rustls_pemfile::certs(&mut bufreader) { + let certs: Result, _> = rustls_pemfile::certs(&mut bufreader).collect(); + let certs = match certs { Ok(certs) => certs, Err(_) => return rustls_result::Io, }; let mut roots = RootCertStore::empty(); - let (_, failed) = roots.add_parsable_certificates(&certs); + let (_, failed) = roots.add_parsable_certificates(certs); if failed > 0 { return rustls_result::CertificateParseError; } diff --git a/src/connection.rs b/src/connection.rs index 0b5f9c9c..78d25f9f 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -3,8 +3,9 @@ use std::{ffi::c_void, ptr::null}; use std::{ptr::null_mut, slice}; use libc::{size_t, EINVAL, EIO}; +use pki_types::CertificateDer; use rustls::crypto::ring::ALL_CIPHER_SUITES; -use rustls::{Certificate, ClientConnection, ServerConnection, SupportedCipherSuite}; +use rustls::{ClientConnection, ServerConnection, SupportedCipherSuite}; use crate::io::{ rustls_write_vectored_callback, CallbackReader, CallbackWriter, ReadCallback, @@ -323,14 +324,14 @@ impl rustls_connection { /// `const struct rustls_connection *`). /// #[no_mangle] - pub extern "C" fn rustls_connection_get_peer_certificate( + pub extern "C" fn rustls_connection_get_peer_certificate<'a>( conn: *const rustls_connection, i: size_t, - ) -> *const rustls_certificate { + ) -> *const rustls_certificate<'a> { ffi_panic_boundary! { let conn: &Connection = try_ref_from_ptr!(conn); match conn.peer_certificates().and_then(|c| c.get(i)) { - Some(cert) => cert as *const Certificate as *const _, + Some(cert) => cert as *const CertificateDer as *const _, None => null() } } diff --git a/src/rustls.h b/src/rustls.h index d485e04a..24ee75b7 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -200,8 +200,8 @@ typedef struct rustls_allow_any_authenticated_client_verifier rustls_allow_any_a /** * An X.509 certificate, as used in rustls. - * Corresponds to `Certificate` in the Rust API. - * + * Corresponds to `CertificateDer` in the Rust pki-types API. + * */ typedef struct rustls_certificate rustls_certificate; From c29479b5d61c72755932a33060b2a52e3f70acbb Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 6 Oct 2023 13:48:47 -0400 Subject: [PATCH 08/19] builder for root_cert_store This commit implements a builder pattern for `root_cert_store` so that we can have a path to both a mutable root cert store while trust anchors are being added, and a const root cert store suitable for an `Arc` once completed. --- src/cipher.rs | 106 +++++++++++++++++++++++++++++++++++++++---------- src/rustls.h | 61 +++++++++++++++++++--------- tests/server.c | 15 +++++-- 3 files changed, 139 insertions(+), 43 deletions(-) diff --git a/src/cipher.rs b/src/cipher.rs index ec288db9..2300f15d 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -20,7 +20,7 @@ use crate::{ try_mut_from_ptr, try_ref_from_ptr, try_slice, try_take, Castable, OwnershipArc, OwnershipBox, OwnershipRef, }; -use rustls_result::NullParameter; +use rustls_result::{AlreadyUsed, NullParameter}; /// An X.509 certificate, as used in rustls. /// Corresponds to `CertificateDer` in the Rust pki-types API. @@ -430,34 +430,46 @@ impl rustls_certified_key { } } -/// A root certificate store. -/// -pub struct rustls_root_cert_store { +/// A `rustls_root_cert_store` being constructed. +/// +/// A builder can be modified by adding trust anchor root certificates with +/// `rustls_root_cert_store_builder_add_pem`. Once you're done adding root certificates, +/// call `rustls_root_cert_store_builder_build` to turn it into a `rustls_root_cert_store`. +/// This object is not safe for concurrent mutation. +pub struct rustls_root_cert_store_builder { // We use the opaque struct pattern to tell C about our types without // telling them what's inside. // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs _private: [u8; 0], } -impl Castable for rustls_root_cert_store { +pub(crate) struct RootCertStoreBuilder { + roots: RootCertStore, +} + +impl Castable for rustls_root_cert_store_builder { type Ownership = OwnershipBox; - type RustType = RootCertStore; + type RustType = Option; } -impl rustls_root_cert_store { - /// Create a rustls_root_cert_store. Caller owns the memory and must - /// eventually call rustls_root_cert_store_free. The store starts out empty. - /// Caller must add root certificates with rustls_root_cert_store_add_pem. - /// +impl rustls_root_cert_store_builder { + /// Create a `rustls_root_cert_store_builder`. + /// + /// Caller owns the memory and may free it with `rustls_root_cert_store_free`, regardless of + /// whether `rustls_root_cert_store_builder_build` was called. + /// + /// If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`, + /// it must be freed with `rustls_root_cert_store_builder_free`. #[no_mangle] - pub extern "C" fn rustls_root_cert_store_new() -> *mut rustls_root_cert_store { + pub extern "C" fn rustls_root_cert_store_builder_new() -> *mut rustls_root_cert_store_builder { ffi_panic_boundary! { let store = rustls::RootCertStore::empty(); to_boxed_mut_ptr(store) } } - /// Add one or more certificates to the root cert store using PEM encoded data. + /// Add one or more certificates to the root cert store builder using PEM + /// encoded data. /// /// When `strict` is true an error will return a `CertificateParseError` /// result. So will an attempt to parse data that has zero certificates. @@ -466,15 +478,19 @@ impl rustls_root_cert_store { /// This may be useful on systems that have syntactically invalid root /// certificates. #[no_mangle] - pub extern "C" fn rustls_root_cert_store_add_pem( - store: *mut rustls_root_cert_store, + pub extern "C" fn rustls_root_cert_store_builder_add_pem( + builder: *mut rustls_root_cert_store_builder, pem: *const u8, pem_len: size_t, strict: bool, ) -> rustls_result { ffi_panic_boundary! { let certs_pem: &[u8] = try_slice!(pem, pem_len); - let store: &mut RootCertStore = try_mut_from_ptr!(store); + let builder: &mut Option = try_mut_from_ptr!(builder); + let builder = match builder { + None => return AlreadyUsed, + Some(b) => b, + }; let certs_der: Result, _> = rustls_pemfile::certs(&mut Cursor::new(certs_pem)).collect(); let certs_der = match certs_der { @@ -490,18 +506,68 @@ impl rustls_root_cert_store { return rustls_result::CertificateParseError; } - store.roots.append(&mut new_store.roots); + builder.roots.roots.append(&mut new_store.roots); + + rustls_result::Ok + } + } + + /// Create a new `rustls_root_cert_store` from the builder. + /// + /// The builder is consumed and cannot be used again, but must still be freed. + /// + /// The root cert store can be used in several `rustls_web_pki_client_cert_verifier_builder_new` + /// instances and must be freed by the application when no longer needed. See the documentation of + /// `rustls_root_cert_store_free` for details about lifetime. + #[no_mangle] + pub extern "C" fn rustls_root_cert_store_builder_build( + builder: *mut rustls_root_cert_store_builder, + root_cert_store_out: *mut *const rustls_root_cert_store, + ) -> rustls_result { + ffi_panic_boundary! { + let builder: &mut Option = try_mut_from_ptr!(builder); + let builder = try_take!(builder); + + ArcCastPtr::set_mut_ptr(root_cert_store_out, builder.roots.clone()); + rustls_result::Ok } } + /// Free a `rustls_root_cert_store_builder` previously returned from + /// `rustls_root_cert_store_builder_new`. Calling with NULL is fine. Must not be + /// called twice with the same value. + #[no_mangle] + pub extern "C" fn rustls_root_cert_store_builder_free( + builder: *mut rustls_root_cert_store_builder, + ) { + ffi_panic_boundary! { + BoxCastPtr::to_box(builder); + } + } +} + +/// A root certificate store. +/// +pub struct rustls_root_cert_store { + // We use the opaque struct pattern to tell C about our types without + // telling them what's inside. + // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs + _private: [u8; 0], +} + +impl Castable for rustls_root_cert_store { + type Ownership = OwnershipArc; + type RustType = RootCertStore; +} + +impl rustls_root_cert_store { /// Free a rustls_root_cert_store previously returned from rustls_root_cert_store_builder_build. /// Calling with NULL is fine. Must not be called twice with the same value. #[no_mangle] - pub extern "C" fn rustls_root_cert_store_free(store: *mut rustls_root_cert_store) { + pub extern "C" fn rustls_root_cert_store_free(store: *const rustls_root_cert_store) { ffi_panic_boundary! { - let store = try_box_from_ptr!(store); - drop(store) + ArcCastPtr::free(store); } } } diff --git a/src/rustls.h b/src/rustls.h index 24ee75b7..199a1340 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -247,6 +247,15 @@ typedef struct rustls_iovec rustls_iovec; */ typedef struct rustls_root_cert_store rustls_root_cert_store; +/** + * A `rustls_root_cert_store` being constructed. A builder can be modified by + * adding trust anchor root certificates with `rustls_root_cert_store_builder_add_pem`. + * Once you're done adding root certificates, call `rustls_root_cert_store_builder_build` + * to turn it into a `rustls_root_cert_store`. This object is not safe + * for concurrent mutation. + */ +typedef struct rustls_root_cert_store_builder rustls_root_cert_store_builder; + /** * A server config that is done being constructed and is now read-only. * Under the hood, this object corresponds to an `Arc`. @@ -822,14 +831,6 @@ rustls_result rustls_accepted_into_connection(struct rustls_accepted *accepted, */ void rustls_accepted_free(struct rustls_accepted *accepted); -/** - * Get the DER data of the certificate itself. - * The data is owned by the certificate and has the same lifetime. - */ -rustls_result rustls_certificate_get_der(const struct rustls_certificate *cert, - const uint8_t **out_der_data, - size_t *out_der_len); - /** * Return a 16-bit unsigned integer corresponding to this cipher suite's assignment from * . @@ -937,15 +938,18 @@ rustls_result rustls_certified_key_clone_with_ocsp(const struct rustls_certified void rustls_certified_key_free(const struct rustls_certified_key *key); /** - * Create a rustls_root_cert_store. Caller owns the memory and must - * eventually call rustls_root_cert_store_free. The store starts out empty. - * Caller must add root certificates with rustls_root_cert_store_add_pem. - * + * Create a `rustls_root_cert_store_builder`. Caller owns the memory and may + * free it with `rustls_root_cert_store_free`, regardless of whether + * `rustls_root_cert_store_builder_build` was called. + * + * If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`, + * it must be freed with `rustls_root_cert_store_builder_free`. */ -struct rustls_root_cert_store *rustls_root_cert_store_new(void); +struct rustls_root_cert_store_builder *rustls_root_cert_store_builder_new(void); /** - * Add one or more certificates to the root cert store using PEM encoded data. + * Add one or more certificates to the root cert store builder using PEM + * encoded data. * * When `strict` is true an error will return a `CertificateParseError` * result. So will an attempt to parse data that has zero certificates. @@ -954,16 +958,35 @@ struct rustls_root_cert_store *rustls_root_cert_store_new(void); * This may be useful on systems that have syntactically invalid root * certificates. */ -rustls_result rustls_root_cert_store_add_pem(struct rustls_root_cert_store *store, - const uint8_t *pem, - size_t pem_len, - bool strict); +rustls_result rustls_root_cert_store_builder_add_pem(struct rustls_root_cert_store_builder *builder, + const uint8_t *pem, + size_t pem_len, + bool strict); + +/** + * Create a new `rustls_root_cert_store` from the builder. + * + * The builder is consumed and cannot be used again, but must still be freed. + * + * The root cert store can be used in several `rustls_web_pki_client_cert_verifier_builder_new` + * instances and must be freed by the application when no longer needed. See the documentation of + * `rustls_root_cert_store_free` for details about lifetime. + */ +rustls_result rustls_root_cert_store_builder_build(struct rustls_root_cert_store_builder *builder, + const struct rustls_root_cert_store **root_cert_store_out); + +/** + * Free a `rustls_root_cert_store_builder` previously returned from + * `rustls_root_cert_store_builder_new`. Calling with NULL is fine. Must not be + * called twice with the same value. + */ +void rustls_root_cert_store_builder_free(struct rustls_root_cert_store_builder *builder); /** * Free a rustls_root_cert_store previously returned from rustls_root_cert_store_builder_build. * Calling with NULL is fine. Must not be called twice with the same value. */ -void rustls_root_cert_store_free(struct rustls_root_cert_store *store); +void rustls_root_cert_store_free(const struct rustls_root_cert_store *store); /** * Create a new allow any authenticated client certificate verifier builder using the root store. diff --git a/tests/server.c b/tests/server.c index 86cb354a..dbed3eb6 100644 --- a/tests/server.c +++ b/tests/server.c @@ -238,7 +238,8 @@ main(int argc, const char **argv) struct rustls_connection *rconn = NULL; const struct rustls_certified_key *certified_key = NULL; struct rustls_slice_bytes alpn_http11; - struct rustls_root_cert_store *client_cert_root_store = NULL; + struct rustls_root_cert_store_builder *client_cert_root_store_builder = NULL; + const struct rustls_root_cert_store *client_cert_root_store = NULL; struct rustls_allow_any_authenticated_client_builder *client_cert_verifier_builder = NULL; const struct rustls_allow_any_authenticated_client_verifier @@ -288,9 +289,14 @@ main(int argc, const char **argv) goto cleanup; } - client_cert_root_store = rustls_root_cert_store_new(); - result = rustls_root_cert_store_add_pem( - client_cert_root_store, (uint8_t *)certbuf, certbuf_len, true); + client_cert_root_store_builder = rustls_root_cert_store_builder_new(); + result = rustls_root_cert_store_builder_add_pem( + client_cert_root_store_builder, (uint8_t *)certbuf, certbuf_len, true); + if(result != RUSTLS_RESULT_OK) { + goto cleanup; + } + result = rustls_root_cert_store_builder_build( + client_cert_root_store_builder, &client_cert_root_store); if(result != RUSTLS_RESULT_OK) { goto cleanup; } @@ -396,6 +402,7 @@ main(int argc, const char **argv) cleanup: rustls_certified_key_free(certified_key); + rustls_root_cert_store_builder_free(client_cert_root_store_builder); rustls_root_cert_store_free(client_cert_root_store); rustls_allow_any_authenticated_client_builder_free( client_cert_verifier_builder); From 05a35d8c6e4f1c08a5017f2b96b704fd2c4b5078 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 6 Oct 2023 14:10:25 -0400 Subject: [PATCH 09/19] rework client cert verifiers w/ builder API This commit reworks the rustls-ffi API for client certificate validation to track the new builder based API that landed in Rustls rustls/rustls#1368 --- src/cipher.rs | 343 ++++++++++++++++++------------------------------- src/error.rs | 68 ++++++---- src/lib.rs | 28 ++-- src/rustls.h | 188 ++++++++------------------- src/server.rs | 41 ++---- tests/server.c | 21 +-- 6 files changed, 258 insertions(+), 431 deletions(-) diff --git a/src/cipher.rs b/src/cipher.rs index 2300f15d..638c790d 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -8,17 +8,18 @@ use std::sync::Arc; use pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer}; use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES}; -use rustls::server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient}; +use rustls::server::danger::ClientCertVerifier; +use rustls::server::WebPkiClientVerifier; use rustls::sign::CertifiedKey; use rustls::{RootCertStore, SupportedCipherSuite}; use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys}; -use crate::error::{map_error, rustls_result}; +use crate::error::{self, rustls_result}; use crate::rslice::{rustls_slice_bytes, rustls_str}; use crate::{ - ffi_panic_boundary, free_arc, to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr, - try_mut_from_ptr, try_ref_from_ptr, try_slice, try_take, Castable, OwnershipArc, OwnershipBox, - OwnershipRef, + ffi_panic_boundary, free_arc, free_box, set_arc_mut_ptr, set_boxed_mut_ptr, to_arc_const_ptr, + to_boxed_mut_ptr, try_mut_from_ptr, try_ref_from_ptr, try_slice, try_take, Castable, + OwnershipArc, OwnershipBox, OwnershipRef, }; use rustls_result::{AlreadyUsed, NullParameter}; @@ -464,7 +465,7 @@ impl rustls_root_cert_store_builder { pub extern "C" fn rustls_root_cert_store_builder_new() -> *mut rustls_root_cert_store_builder { ffi_panic_boundary! { let store = rustls::RootCertStore::empty(); - to_boxed_mut_ptr(store) + to_boxed_mut_ptr(Some(RootCertStoreBuilder { roots: store })) } } @@ -528,7 +529,7 @@ impl rustls_root_cert_store_builder { let builder: &mut Option = try_mut_from_ptr!(builder); let builder = try_take!(builder); - ArcCastPtr::set_mut_ptr(root_cert_store_out, builder.roots.clone()); + set_arc_mut_ptr(root_cert_store_out, builder.roots); rustls_result::Ok } @@ -542,7 +543,7 @@ impl rustls_root_cert_store_builder { builder: *mut rustls_root_cert_store_builder, ) { ffi_panic_boundary! { - BoxCastPtr::to_box(builder); + free_box(builder); } } } @@ -567,286 +568,188 @@ impl rustls_root_cert_store { #[no_mangle] pub extern "C" fn rustls_root_cert_store_free(store: *const rustls_root_cert_store) { ffi_panic_boundary! { - ArcCastPtr::free(store); + free_arc(store); } } } -/// A builder for a `rustls_allow_any_authenticated_client_verifier`. This builder object can be -/// used to configure certificate revocation lists, and then turned into a -/// `rustls_allow_any_authenticated_client_verifier` once ready. -pub struct rustls_allow_any_authenticated_client_builder { +/// A built client certificate verifier that can be provided to a `rustls_server_config_builder` +/// with `rustls_server_config_builder_set_client_verifier`. +pub struct rustls_client_cert_verifier { _private: [u8; 0], } -impl Castable for rustls_allow_any_authenticated_client_builder { +/// Rustls' ConfigBuilder requires an `Arc` here, meaning we +/// must follow the pattern described in CONTRIBUTING.md[0] for handling dynamically sized +/// types (DSTs) across the FFI boundary. +/// [0] +impl Castable for rustls_client_cert_verifier { type Ownership = OwnershipBox; - // NOTE: contained value is consumed even on error, so this can contain None, but the caller - // still needs to free it - type RustType = Option; + type RustType = Arc; } -impl rustls_allow_any_authenticated_client_builder { - /// Create a new allow any authenticated client certificate verifier builder using the root store. - /// - /// This copies the contents of the rustls_root_cert_store. It does not take - /// ownership of the pointed-to memory. - /// - /// This object can then be used to load any CRLs. - /// - /// Once that is complete, convert it into a real `rustls_allow_any_authenticated_client_verifier` - /// by calling `rustls_allow_any_authenticated_client_verifier_new()`. - #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_builder_new( - store: *const rustls_root_cert_store, - ) -> *mut rustls_allow_any_authenticated_client_builder { - ffi_panic_boundary! { - let store: &RootCertStore = try_ref_from_ptr!(store); - let client_cert_verifier = Some(AllowAnyAuthenticatedClient::new(store.clone())); - to_boxed_mut_ptr(client_cert_verifier) - } - } - - /// Add one or more certificate revocation lists (CRLs) to the client certificate verifier by - /// reading the CRL content from the provided buffer of PEM encoded content. - /// - /// This function returns an error if the provided buffer is not valid PEM encoded content, - /// or if the CRL content is invalid or unsupported. - #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_builder_add_crl( - builder: *mut rustls_allow_any_authenticated_client_builder, - crl_pem: *const u8, - crl_pem_len: size_t, - ) -> rustls_result { - ffi_panic_boundary! { - let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); - - let crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len); - let crls_der: Result, _> = crls(&mut Cursor::new(crl_pem)).collect(); - let crls_der = match crls_der{ - Ok(vv) => vv, - Err(_) => return rustls_result::CertificateRevocationListParseError, - }; - - let client_cert_verifier = try_take!(client_cert_verifier_builder); - match client_cert_verifier.with_crls(crls_der) { - Ok(v) => client_cert_verifier_builder.replace(v), - Err(e) => return map_error(rustls::Error::InvalidCertRevocationList(e)), - }; - - rustls_result::Ok - } - } - - /// Free a `rustls_allow_any_authenticated_client_builder` previously returned from - /// `rustls_allow_any_authenticated_client_builder_new`. - /// Calling with NULL is fine. Must not be called twice with the same value. +impl rustls_client_cert_verifier { + /// Free a `rustls_client_cert_verifier` previously returned from + /// `rustls_client_cert_verifier_builder_build`. Calling with NULL is fine. Must not be + /// called twice with the same value. #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_builder_free( - builder: *mut rustls_allow_any_authenticated_client_builder, - ) { + pub extern "C" fn rustls_client_cert_verifier_free(verifier: *mut rustls_client_cert_verifier) { ffi_panic_boundary! { - let store = try_box_from_ptr!(builder); - drop(store) + free_box(verifier); } } } -/// A verifier of client certificates that requires all certificates to be -/// trusted based on a given `rustls_root_cert_store`. Usable in building server -/// configurations. Connections without such a client certificate will not -/// be accepted. -pub struct rustls_allow_any_authenticated_client_verifier { +/// A client certificate verifier being constructed. A builder can be modified by, +/// e.g. `rustls_web_pki_client_cert_verifier_builder_add_crl`. Once you're +/// done configuring settings, call `rustls_web_pki_client_cert_verifier_builder_build` +/// to turn it into a `rustls_client_cert_verifier`. This object is not safe +/// for concurrent mutation. +// TODO(@cpu): Add rustdoc link once available. +pub struct rustls_web_pki_client_cert_verifier_builder { + // We use the opaque struct pattern to tell C about our types without + // telling them what's inside. + // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs _private: [u8; 0], } -impl Castable for rustls_allow_any_authenticated_client_verifier { - type Ownership = OwnershipArc; - type RustType = AllowAnyAuthenticatedClient; -} - -impl rustls_allow_any_authenticated_client_verifier { - /// Create a new allow any authenticated client certificate verifier from a builder. - /// - /// The builder is consumed and cannot be used again, but must still be freed. - /// - /// The verifier can be used in several `rustls_server_config` instances. Must be freed by - /// the application when no longer needed. See the documentation of - /// `rustls_allow_any_authenticated_client_verifier_free` for details about lifetime. - /// This copies the contents of the `rustls_root_cert_store`. It does not take - /// ownership of the pointed-to memory. - #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_verifier_new( - builder: *mut rustls_allow_any_authenticated_client_builder, - ) -> *const rustls_allow_any_authenticated_client_verifier { - ffi_panic_boundary! { - let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); - - let client_cert_verifier = match client_cert_verifier_builder.take() { - None => { - return null() as *const _; - }, - Some(x) => x, - }; - Arc::into_raw(client_cert_verifier.boxed()) as *const _ - } - } - - /// "Free" a verifier previously returned from - /// `rustls_allow_any_authenticated_client_verifier_new`. Since - /// `rustls_allow_any_authenticated_client_verifier` is actually an - /// atomically reference-counted pointer, extant server_configs may still - /// hold an internal reference to the Rust object. However, C code must - /// consider this pointer unusable after "free"ing it. - /// Calling with NULL is fine. Must not be called twice with the same value. - #[no_mangle] - pub extern "C" fn rustls_allow_any_authenticated_client_verifier_free( - verifier: *const rustls_allow_any_authenticated_client_verifier, - ) { - ffi_panic_boundary! { - free_arc(verifier); - } - } -} - -/// A builder for a `rustls_allow_any_anonymous_or_authenticated_client_verifier`. This builder -/// object can be used to configure certificate revocation lists, and then turned into a -/// `rustls_allow_any_anonymous_or_authenticated_client_verifier` once ready. -pub struct rustls_allow_any_anonymous_or_authenticated_client_builder { - _private: [u8; 0], +pub(crate) struct ClientCertVerifierBuilder { + roots: Arc, + crls: Vec>, + allow_unauthenticated: bool, } -impl Castable for rustls_allow_any_anonymous_or_authenticated_client_builder { +impl Castable for rustls_web_pki_client_cert_verifier_builder { type Ownership = OwnershipBox; - // NOTE: contained value is consumed even on error, so this can contain None, but the caller - // still needs to free it - type RustType = Option; + type RustType = Option; } -impl rustls_allow_any_anonymous_or_authenticated_client_builder { - /// Create a new allow any anonymous or authenticated client certificate verifier builder - /// using the root store. +impl rustls_web_pki_client_cert_verifier_builder { + /// Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and may + /// eventually call `rustls_web_pki_client_cert_verifier_builder_free` to free it, whether or + /// not `rustls_web_pki_client_cert_verifier_builder_build` was called. + /// + /// Without further modification the builder will produce a client certificate verifier that + /// will require a client present a client certificate that chains to one of the trust anchors + /// in the provided `rustls_root_cert_store`. The root cert store must not be empty. /// - /// This copies the contents of the rustls_root_cert_store. It does not take - /// ownership of the pointed-to memory. + /// Revocation checking will not be performed unless + /// `rustls_web_pki_client_cert_verifier_builder_add_crl` is used to add certificate revocation + /// lists (CRLs) to the builder. /// - /// This object can then be used to load any CRLs. + /// Unauthenticated clients will not be permitted unless + /// `rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated` is used. /// - /// Once that is complete, convert it into a real - /// `rustls_allow_any_anonymous_or_authenticated_client_verifier` - /// by calling `rustls_allow_any_anonymous_or_authenticated_client_verifier_new()`. + /// This copies the contents of the `rustls_root_cert_store`. It does not take + /// ownership of the pointed-to data. #[no_mangle] - pub extern "C" fn rustls_client_cert_verifier_optional_builder_new( + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_new( store: *const rustls_root_cert_store, - ) -> *mut rustls_allow_any_anonymous_or_authenticated_client_builder { + ) -> *mut rustls_web_pki_client_cert_verifier_builder { ffi_panic_boundary! { - let store: &RootCertStore = try_ref_from_ptr!(store); - let client_cert_verifier = Some(AllowAnyAnonymousOrAuthenticatedClient::new(store.clone())); - to_boxed_mut_ptr(client_cert_verifier) + let store = try_clone_arc!(store); + let builder = ClientCertVerifierBuilder { + roots: store, + crls: Vec::default(), + allow_unauthenticated: false, + }; + to_boxed_mut_ptr(Some(builder)) } } - /// Add one or more certificate revocation lists (CRLs) to the client certificate verifier by - /// reading the CRL content from the provided buffer of PEM encoded content. + /// Add one or more certificate revocation lists (CRLs) to the client certificate verifier + /// builder by reading the CRL content from the provided buffer of PEM encoded content. /// - /// This function returns an error if the provided buffer is not valid PEM encoded content, - /// or if the CRL content is invalid or unsupported. + /// This function returns an error if the provided buffer is not valid PEM encoded content. #[no_mangle] - pub extern "C" fn rustls_client_cert_verifier_optional_builder_add_crl( - builder: *mut rustls_allow_any_anonymous_or_authenticated_client_builder, + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_add_crl( + builder: *mut rustls_web_pki_client_cert_verifier_builder, crl_pem: *const u8, crl_pem_len: size_t, ) -> rustls_result { ffi_panic_boundary! { - let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; let crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len); - let crls_der: Vec = match crls(&mut Cursor::new(crl_pem)) { - Ok(vv) => vv.into_iter().map(UnparsedCertRevocationList).collect(), + let crls_der: Result, _> = crls(&mut Cursor::new(crl_pem)).collect(); + let crls_der = match crls_der{ + Ok(vv) => vv, Err(_) => return rustls_result::CertificateRevocationListParseError, }; + if crls_der.is_empty() { + return rustls_result::CertificateRevocationListParseError; + } - let client_cert_verifier = try_take!(client_cert_verifier_builder); - match client_cert_verifier.with_crls(crls_der) { - Ok(v) => client_cert_verifier_builder.replace(v), - Err(e) => return map_error(rustls::Error::InvalidCertRevocationList(e)), - }; + client_verifier_builder.crls.extend(crls_der); rustls_result::Ok } } - /// Free a `rustls_allow_any_anonymous_or_authenticated_client_builder` previously returned from - /// `rustls_client_cert_verifier_optional_builder_new`. - /// Calling with NULL is fine. Must not be called twice with the same value. + /// Allow unauthenticated anonymous clients in addition to those that present a client + /// certificate that chains to one of the verifier's configured trust anchors. #[no_mangle] - pub extern "C" fn rustls_client_cert_verifier_optional_builder_free( - builder: *mut rustls_allow_any_anonymous_or_authenticated_client_builder, - ) { + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + ) -> rustls_result { ffi_panic_boundary! { - let store = try_box_from_ptr!(builder); - drop(store) + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + client_verifier_builder.allow_unauthenticated = true; + rustls_result::Ok } } -} -/// Alternative to `rustls_allow_any_authenticated_client_verifier` that allows connections -/// with or without a client certificate. If the client offers a certificate, -/// it will be verified (and rejected if it is not valid). If the client -/// does not offer a certificate, the connection will succeed. -/// -/// The application can retrieve the certificate, if any, with -/// `rustls_connection_get_peer_certificate`. -pub struct rustls_allow_any_anonymous_or_authenticated_client_verifier { - _private: [u8; 0], -} - -impl Castable for rustls_allow_any_anonymous_or_authenticated_client_verifier { - type Ownership = OwnershipArc; - type RustType = AllowAnyAnonymousOrAuthenticatedClient; -} - -impl rustls_allow_any_anonymous_or_authenticated_client_verifier { - /// Create a new allow any anonymous or authenticated client certificate verifier builder - /// from the builder. + /// Create a new client certificate verifier from the builder. /// /// The builder is consumed and cannot be used again, but must still be freed. /// - /// The verifier can be used in several `rustls_server_config` instances. Must be + /// The verifier can be used in several `rustls_server_config` instances and must be /// freed by the application when no longer needed. See the documentation of - /// `rustls_allow_any_anonymous_or_authenticated_client_verifier_free` for details about lifetime. - /// This copies the contents of the `rustls_root_cert_store`. It does not take - /// ownership of the pointed-to data. + /// `rustls_web_pki_client_cert_verifier_builder_free` for details about lifetime. #[no_mangle] - pub extern "C" fn rustls_allow_any_anonymous_or_authenticated_client_verifier_new( - builder: *mut rustls_allow_any_anonymous_or_authenticated_client_builder, - ) -> *const rustls_allow_any_anonymous_or_authenticated_client_verifier { + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_build( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + verifier_out: *mut *mut rustls_client_cert_verifier, + ) -> rustls_result { ffi_panic_boundary! { - let client_cert_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = try_take!(client_verifier_builder); - let client_cert_verifier = match client_cert_verifier_builder.take() { - None => { - return null() as *const _; - }, - Some(x) => x, + let mut builder = WebPkiClientVerifier::builder(client_verifier_builder.roots) + .with_crls(client_verifier_builder.crls); + if client_verifier_builder.allow_unauthenticated { + builder = builder.allow_unauthenticated(); + } + + let verifier = match builder.build() { + Ok(v) => v, + Err(e) => return error::map_verifier_builder_error(e), }; - Arc::into_raw(client_cert_verifier.boxed()) as *const _ + + set_boxed_mut_ptr(verifier_out, verifier); + + rustls_result::Ok } } - /// "Free" a verifier previously returned from - /// `rustls_allow_any_anonymous_or_authenticated_client_verifier_new`. Since - /// `rustls_allow_any_anonymous_or_authenticated_client_verifier` - /// is actually an atomically reference-counted pointer, extant `server_configs` may still - /// hold an internal reference to the Rust object. However, C code must - /// consider this pointer unusable after "free"ing it. - /// Calling with NULL is fine. Must not be called twice with the same value. + /// Free a `rustls_client_cert_verifier_builder` previously returned from + /// `rustls_client_cert_verifier_builder_new`. Calling with NULL is fine. Must not be + /// called twice with the same value. #[no_mangle] - pub extern "C" fn rustls_allow_any_anonymous_or_authenticated_client_verifier_free( - verifier: *const rustls_allow_any_anonymous_or_authenticated_client_verifier, + pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_free( + builder: *mut rustls_web_pki_client_cert_verifier_builder, ) { ffi_panic_boundary! { - free_arc(verifier); + free_box(builder); } } } diff --git a/src/error.rs b/src/error.rs index 28075644..56d0f0e5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use crate::ffi_panic_boundary; use libc::{c_char, c_uint, size_t}; +use rustls::server::ClientCertVerifierBuilderError; use rustls::{CertRevocationListError, CertificateError, Error, InvalidMessage}; /// A return value for a function that may return either success (0) or a @@ -181,7 +182,10 @@ u32_enum_builder! { CertRevocationListUnsupportedCriticalExtension => 7407, CertRevocationListUnsupportedDeltaCrl => 7408, CertRevocationListUnsupportedIndirectCrl => 7409, - CertRevocationListUnsupportedRevocationReason => 7410 + CertRevocationListUnsupportedRevocationReason => 7410, + + // From ClientCertVerifierBuilderError, with fields that get flattened. + ClientCertVerifierBuilderNoRootAnchors => 7500 } } @@ -387,35 +391,47 @@ pub(crate) fn map_error(input: rustls::Error) -> rustls_result { _ => AlertUnknown, }, - Error::InvalidCertRevocationList(e) => match e { - CertRevocationListError::BadSignature => CertRevocationListBadSignature, - CertRevocationListError::InvalidCrlNumber => CertRevocationListInvalidCrlNumber, - CertRevocationListError::InvalidRevokedCertSerialNumber => { - CertRevocationListInvalidRevokedCertSerialNumber - } - CertRevocationListError::IssuerInvalidForCrl => CertRevocationListIssuerInvalidForCrl, - CertRevocationListError::Other(_) => CertRevocationListOtherError, - CertRevocationListError::ParseError => CertRevocationListParseError, - CertRevocationListError::UnsupportedCrlVersion => { - CertRevocationListUnsupportedCrlVersion - } - CertRevocationListError::UnsupportedCriticalExtension => { - CertRevocationListUnsupportedCriticalExtension - } - CertRevocationListError::UnsupportedDeltaCrl => CertRevocationListUnsupportedDeltaCrl, - CertRevocationListError::UnsupportedIndirectCrl => { - CertRevocationListUnsupportedIndirectCrl - } - CertRevocationListError::UnsupportedRevocationReason => { - CertRevocationListUnsupportedRevocationReason - } - _ => CertRevocationListOtherError, - }, + Error::InvalidCertRevocationList(e) => map_crl_error(e), _ => General, } } +pub(crate) fn map_crl_error(err: CertRevocationListError) -> rustls_result { + use rustls_result::*; + + match err { + CertRevocationListError::BadSignature => CertRevocationListBadSignature, + CertRevocationListError::InvalidCrlNumber => CertRevocationListInvalidCrlNumber, + CertRevocationListError::InvalidRevokedCertSerialNumber => { + CertRevocationListInvalidRevokedCertSerialNumber + } + CertRevocationListError::IssuerInvalidForCrl => CertRevocationListIssuerInvalidForCrl, + CertRevocationListError::Other(_) => CertRevocationListOtherError, + CertRevocationListError::ParseError => CertRevocationListParseError, + CertRevocationListError::UnsupportedCrlVersion => CertRevocationListUnsupportedCrlVersion, + CertRevocationListError::UnsupportedCriticalExtension => { + CertRevocationListUnsupportedCriticalExtension + } + CertRevocationListError::UnsupportedDeltaCrl => CertRevocationListUnsupportedDeltaCrl, + CertRevocationListError::UnsupportedIndirectCrl => CertRevocationListUnsupportedIndirectCrl, + CertRevocationListError::UnsupportedRevocationReason => { + CertRevocationListUnsupportedRevocationReason + } + _ => CertRevocationListOtherError, + } +} + +pub(crate) fn map_verifier_builder_error(err: ClientCertVerifierBuilderError) -> rustls_result { + match err { + ClientCertVerifierBuilderError::NoRootAnchors => { + rustls_result::ClientCertVerifierBuilderNoRootAnchors + } + ClientCertVerifierBuilderError::InvalidCrl(crl_err) => map_crl_error(crl_err), + _ => rustls_result::General, + } +} + impl Display for rustls_result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use rustls::AlertDescription as alert; @@ -639,6 +655,8 @@ impl Display for rustls_result { CertRevocationListError::UnsupportedRevocationReason, ) .fmt(f), + + ClientCertVerifierBuilderNoRootAnchors => write!(f, "no root trust anchors provided"), } } } diff --git a/src/lib.rs b/src/lib.rs index f090e912..b7e43106 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -496,6 +496,22 @@ where } } +/// Converts a [`Castable`]'s underlying [`Castable::RustType`] to a const pointer +/// to an `Arc` over the rust type and sets the `dst` out pointer to the resulting const `Arc` +/// pointer. See [`to_arc_const_ptr`] for more information. +/// +/// ## Unsafety: +/// +/// `dst` must not be `NULL`. +pub(crate) fn set_arc_mut_ptr(dst: *mut *const C, src: C::RustType) +where + C: Castable, +{ + unsafe { + *dst = to_arc_const_ptr(src); + } +} + /// Converts a mutable pointer to a [`Castable`] to an optional ref to the underlying /// [`Castable::RustType`]. See [`cast_mut_ptr`] for more information. /// @@ -511,12 +527,6 @@ where /// If the provided pointer to a [`Castable`] is non-null, convert it to a mutable reference using /// [`try_from_mut`]. Otherwise, return [`rustls_result::NullParameter`], or an appropriate default /// (`false`, `0`, `NULL`) based on the context. See [`try_from_mut`] for more information. -/// -/// ## Example: -/// -/// ```rust,ignore -/// let config: &mut ClientConfig = try_mut_from_ptr!(builder); -/// ``` macro_rules! try_mut_from_ptr { ( $var:ident ) => { match $crate::try_from_mut($var) { @@ -546,12 +556,6 @@ where /// (`false`, `0`, `NULL`) based on the context; /// /// See [`try_from`] for more information. -/// -/// ## Example: -/// -/// ```rust, ignore -/// let config: &ClientConfig = try_ref_from_ptr!(builder); -/// ``` macro_rules! try_ref_from_ptr { ( $var:ident ) => { match $crate::try_from($var) { diff --git a/src/rustls.h b/src/rustls.h index 199a1340..32411478 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -116,6 +116,7 @@ enum rustls_result { RUSTLS_RESULT_CERT_REVOCATION_LIST_UNSUPPORTED_DELTA_CRL = 7408, RUSTLS_RESULT_CERT_REVOCATION_LIST_UNSUPPORTED_INDIRECT_CRL = 7409, RUSTLS_RESULT_CERT_REVOCATION_LIST_UNSUPPORTED_REVOCATION_REASON = 7410, + RUSTLS_RESULT_CLIENT_CERT_VERIFIER_BUILDER_NO_ROOT_ANCHORS = 7500, }; typedef uint32_t rustls_result; @@ -165,39 +166,6 @@ typedef struct rustls_accepted rustls_accepted; */ typedef struct rustls_acceptor rustls_acceptor; -/** - * A builder for a `rustls_allow_any_anonymous_or_authenticated_client_verifier`. This builder - * object can be used to configure certificate revocation lists, and then turned into a - * `rustls_allow_any_anonymous_or_authenticated_client_verifier` once ready. - */ -typedef struct rustls_allow_any_anonymous_or_authenticated_client_builder rustls_allow_any_anonymous_or_authenticated_client_builder; - -/** - * Alternative to `rustls_allow_any_authenticated_client_verifier` that allows connections - * with or without a client certificate. If the client offers a certificate, - * it will be verified (and rejected if it is not valid). If the client - * does not offer a certificate, the connection will succeed. - * - * The application can retrieve the certificate, if any, with - * `rustls_connection_get_peer_certificate`. - */ -typedef struct rustls_allow_any_anonymous_or_authenticated_client_verifier rustls_allow_any_anonymous_or_authenticated_client_verifier; - -/** - * A builder for a `rustls_allow_any_authenticated_client_verifier`. This builder object can be - * used to configure certificate revocation lists, and then turned into a - * `rustls_allow_any_authenticated_client_verifier` once ready. - */ -typedef struct rustls_allow_any_authenticated_client_builder rustls_allow_any_authenticated_client_builder; - -/** - * A verifier of client certificates that requires all certificates to be - * trusted based on a given `rustls_root_cert_store`. Usable in building server - * configurations. Connections without such a client certificate will not - * be accepted. - */ -typedef struct rustls_allow_any_authenticated_client_verifier rustls_allow_any_authenticated_client_verifier; - /** * An X.509 certificate, as used in rustls. * Corresponds to `CertificateDer` in the Rust pki-types API. @@ -213,6 +181,12 @@ typedef struct rustls_certificate rustls_certificate; */ typedef struct rustls_certified_key rustls_certified_key; +/** + * A built client certificate verifier that can be provided to a `rustls_server_config_builder` + * with `rustls_server_config_builder_set_client_verifier`. + */ +typedef struct rustls_client_cert_verifier rustls_client_cert_verifier; + /** * A client config that is done being constructed and is now read-only. * Under the hood, this object corresponds to an `Arc`. @@ -314,6 +288,15 @@ typedef struct rustls_slice_str rustls_slice_str; */ typedef struct rustls_supported_ciphersuite rustls_supported_ciphersuite; +/** + * A client certificate verifier being constructed. A builder can be modified by, + * e.g. `rustls_web_pki_client_cert_verifier_builder_add_crl`. Once you're + * done configuring settings, call `rustls_web_pki_client_cert_verifier_builder_build` + * to turn it into a `rustls_client_cert_verifier`. This object is not safe + * for concurrent mutation. + */ +typedef struct rustls_web_pki_client_cert_verifier_builder rustls_web_pki_client_cert_verifier_builder; + /** * A read-only view on a Rust `&str`. The contents are guaranteed to be valid * UTF-8. As an additional guarantee on top of Rust's normal UTF-8 guarantee, @@ -989,117 +972,67 @@ void rustls_root_cert_store_builder_free(struct rustls_root_cert_store_builder * void rustls_root_cert_store_free(const struct rustls_root_cert_store *store); /** - * Create a new allow any authenticated client certificate verifier builder using the root store. - * - * This copies the contents of the rustls_root_cert_store. It does not take - * ownership of the pointed-to memory. - * - * This object can then be used to load any CRLs. - * - * Once that is complete, convert it into a real `rustls_allow_any_authenticated_client_verifier` - * by calling `rustls_allow_any_authenticated_client_verifier_new()`. - */ -struct rustls_allow_any_authenticated_client_builder *rustls_allow_any_authenticated_client_builder_new(const struct rustls_root_cert_store *store); - -/** - * Add one or more certificate revocation lists (CRLs) to the client certificate verifier by - * reading the CRL content from the provided buffer of PEM encoded content. - * - * This function returns an error if the provided buffer is not valid PEM encoded content, - * or if the CRL content is invalid or unsupported. - */ -rustls_result rustls_allow_any_authenticated_client_builder_add_crl(struct rustls_allow_any_authenticated_client_builder *builder, - const uint8_t *crl_pem, - size_t crl_pem_len); - -/** - * Free a `rustls_allow_any_authenticated_client_builder` previously returned from - * `rustls_allow_any_authenticated_client_builder_new`. - * Calling with NULL is fine. Must not be called twice with the same value. + * Free a `rustls_client_cert_verifier` previously returned from + * `rustls_client_cert_verifier_builder_build`. Calling with NULL is fine. Must not be + * called twice with the same value. */ -void rustls_allow_any_authenticated_client_builder_free(struct rustls_allow_any_authenticated_client_builder *builder); +void rustls_client_cert_verifier_free(struct rustls_client_cert_verifier *verifier); /** - * Create a new allow any authenticated client certificate verifier from a builder. + * Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and must + * eventually call `rustls_web_pki_client_cert_verifier_builder_build`, then free the + * resulting `rustls_client_cert_verifier`. * - * The builder is consumed and cannot be used again, but must still be freed. - * - * The verifier can be used in several `rustls_server_config` instances. Must be freed by - * the application when no longer needed. See the documentation of - * `rustls_allow_any_authenticated_client_verifier_free` for details about lifetime. - * This copies the contents of the `rustls_root_cert_store`. It does not take - * ownership of the pointed-to memory. - */ -const struct rustls_allow_any_authenticated_client_verifier *rustls_allow_any_authenticated_client_verifier_new(struct rustls_allow_any_authenticated_client_builder *builder); - -/** - * "Free" a verifier previously returned from - * `rustls_allow_any_authenticated_client_verifier_new`. Since - * `rustls_allow_any_authenticated_client_verifier` is actually an - * atomically reference-counted pointer, extant server_configs may still - * hold an internal reference to the Rust object. However, C code must - * consider this pointer unusable after "free"ing it. - * Calling with NULL is fine. Must not be called twice with the same value. - */ -void rustls_allow_any_authenticated_client_verifier_free(const struct rustls_allow_any_authenticated_client_verifier *verifier); - -/** - * Create a new allow any anonymous or authenticated client certificate verifier builder - * using the root store. + * Without further modification the builder will produce a client certificate verifier that + * will require a client present a client certificate that chains to one of the trust anchors + * in the provided `rustls_root_cert_store`. The root cert store must not be empty. * - * This copies the contents of the rustls_root_cert_store. It does not take - * ownership of the pointed-to memory. + * Revocation checking will not be performed unless + * `rustls_web_pki_client_cert_verifier_builder_add_crl` is used to add certificate revocation + * lists (CRLs) to the builder. * - * This object can then be used to load any CRLs. + * Unauthenticated clients will not be permitted unless + * `rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated` is used. * - * Once that is complete, convert it into a real - * `rustls_allow_any_anonymous_or_authenticated_client_verifier` - * by calling `rustls_allow_any_anonymous_or_authenticated_client_verifier_new()`. + * This copies the contents of the `rustls_root_cert_store`. It does not take + * ownership of the pointed-to data. */ -struct rustls_allow_any_anonymous_or_authenticated_client_builder *rustls_client_cert_verifier_optional_builder_new(const struct rustls_root_cert_store *store); +struct rustls_web_pki_client_cert_verifier_builder *rustls_web_pki_client_cert_verifier_builder_new(const struct rustls_root_cert_store *store); /** - * Add one or more certificate revocation lists (CRLs) to the client certificate verifier by - * reading the CRL content from the provided buffer of PEM encoded content. + * Add one or more certificate revocation lists (CRLs) to the client certificate verifier + * builder by reading the CRL content from the provided buffer of PEM encoded content. * - * This function returns an error if the provided buffer is not valid PEM encoded content, - * or if the CRL content is invalid or unsupported. + * This function returns an error if the provided buffer is not valid PEM encoded content. */ -rustls_result rustls_client_cert_verifier_optional_builder_add_crl(struct rustls_allow_any_anonymous_or_authenticated_client_builder *builder, - const uint8_t *crl_pem, - size_t crl_pem_len); +rustls_result rustls_web_pki_client_cert_verifier_builder_add_crl(struct rustls_web_pki_client_cert_verifier_builder *builder, + const uint8_t *crl_pem, + size_t crl_pem_len); /** - * Free a `rustls_allow_any_anonymous_or_authenticated_client_builder` previously returned from - * `rustls_client_cert_verifier_optional_builder_new`. - * Calling with NULL is fine. Must not be called twice with the same value. + * Allow unauthenticated anonymous clients in addition to those that present a client + * certificate that chains to one of the verifier's configured trust anchors. */ -void rustls_client_cert_verifier_optional_builder_free(struct rustls_allow_any_anonymous_or_authenticated_client_builder *builder); +rustls_result rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated(struct rustls_web_pki_client_cert_verifier_builder *builder); /** - * Create a new allow any anonymous or authenticated client certificate verifier builder - * from the builder. + * Create a new client certificate verifier from the builder. * * The builder is consumed and cannot be used again, but must still be freed. * - * The verifier can be used in several `rustls_server_config` instances. Must be + * The verifier can be used in several `rustls_server_config` instances and must be * freed by the application when no longer needed. See the documentation of - * `rustls_allow_any_anonymous_or_authenticated_client_verifier_free` for details about lifetime. - * This copies the contents of the `rustls_root_cert_store`. It does not take - * ownership of the pointed-to data. + * `rustls_web_pki_client_cert_verifier_builder_free` for details about lifetime. */ -const struct rustls_allow_any_anonymous_or_authenticated_client_verifier *rustls_allow_any_anonymous_or_authenticated_client_verifier_new(struct rustls_allow_any_anonymous_or_authenticated_client_builder *builder); +rustls_result rustls_web_pki_client_cert_verifier_builder_build(struct rustls_web_pki_client_cert_verifier_builder *builder, + struct rustls_client_cert_verifier **verifier_out); /** - * "Free" a verifier previously returned from - * `rustls_allow_any_anonymous_or_authenticated_client_verifier_new`. Since - * `rustls_allow_any_anonymous_or_authenticated_client_verifier` - * is actually an atomically reference-counted pointer, extant `server_configs` may still - * hold an internal reference to the Rust object. However, C code must - * consider this pointer unusable after "free"ing it. - * Calling with NULL is fine. Must not be called twice with the same value. + * Free a `rustls_client_cert_verifier_builder` previously returned from + * `rustls_client_cert_verifier_builder_new`. Calling with NULL is fine. Must not be + * called twice with the same value. */ -void rustls_allow_any_anonymous_or_authenticated_client_verifier_free(const struct rustls_allow_any_anonymous_or_authenticated_client_verifier *verifier); +void rustls_web_pki_client_cert_verifier_builder_free(struct rustls_web_pki_client_cert_verifier_builder *builder); /** * Create a rustls_client_config_builder. Caller owns the memory and must @@ -1551,22 +1484,11 @@ rustls_result rustls_server_config_builder_new_custom(const struct rustls_suppor struct rustls_server_config_builder **builder_out); /** - * Create a rustls_server_config_builder for TLS sessions that require - * valid client certificates. The passed rustls_client_cert_verifier may - * be used in several builders. - * For memory lifetime, see rustls_server_config_builder_new. + * Create a rustls_server_config_builder for TLS sessions that may verify client + * certificates. */ void rustls_server_config_builder_set_client_verifier(struct rustls_server_config_builder *builder, - const struct rustls_allow_any_authenticated_client_verifier *verifier); - -/** - * Create a rustls_server_config_builder for TLS sessions that accept - * valid client certificates, but do not require them. The passed - * rustls_client_cert_verifier_optional may be used in several builders. - * For memory lifetime, see rustls_server_config_builder_new. - */ -void rustls_server_config_builder_set_client_verifier_optional(struct rustls_server_config_builder *builder, - const struct rustls_allow_any_anonymous_or_authenticated_client_verifier *verifier); + const struct rustls_client_cert_verifier *verifier); /** * "Free" a server_config_builder without building it into a rustls_server_config. diff --git a/src/server.rs b/src/server.rs index 792e41ec..83ceae1c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -8,16 +8,14 @@ use libc::size_t; use rustls::crypto::ring::ALL_CIPHER_SUITES; use rustls::server::danger::ClientCertVerifier; use rustls::server::{ - AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientHello, NoClientAuth, - ResolvesServerCert, ServerConfig, ServerConnection, StoresServerSessions, + ClientHello, ResolvesServerCert, ServerConfig, ServerConnection, StoresServerSessions, + WebPkiClientVerifier, }; use rustls::sign::CertifiedKey; use rustls::{ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier}; use crate::cipher::{ - rustls_allow_any_anonymous_or_authenticated_client_verifier, - rustls_allow_any_authenticated_client_verifier, rustls_certified_key, - rustls_supported_ciphersuite, + rustls_certified_key, rustls_client_cert_verifier, rustls_supported_ciphersuite, }; use crate::connection::{rustls_connection, Connection}; use crate::error::rustls_result::{InvalidParameter, NullParameter}; @@ -85,7 +83,7 @@ impl rustls_server_config_builder { ffi_panic_boundary! { let builder = ServerConfigBuilder { base: rustls::ServerConfig::builder().with_safe_defaults(), - verifier: NoClientAuth::boxed(), + verifier: WebPkiClientVerifier::no_client_auth(), cert_resolver: None, session_storage: None, alpn_protocols: vec![], @@ -147,7 +145,7 @@ impl rustls_server_config_builder { let builder = ServerConfigBuilder { base, - verifier: NoClientAuth::boxed(), + verifier: WebPkiClientVerifier::no_client_auth(), cert_resolver: None, session_storage: None, alpn_protocols: vec![], @@ -158,36 +156,17 @@ impl rustls_server_config_builder { } } - /// Create a rustls_server_config_builder for TLS sessions that require - /// valid client certificates. The passed rustls_client_cert_verifier may - /// be used in several builders. - /// For memory lifetime, see rustls_server_config_builder_new. + /// Create a rustls_server_config_builder for TLS sessions that may verify client + /// certificates. This increases the refcount of `verifier` and doesn't take ownership. #[no_mangle] pub extern "C" fn rustls_server_config_builder_set_client_verifier( builder: *mut rustls_server_config_builder, - verifier: *const rustls_allow_any_authenticated_client_verifier, - ) { - ffi_panic_boundary! { - let builder: &mut ServerConfigBuilder = try_mut_from_ptr!(builder); - let verifier: Arc = try_clone_arc!(verifier); - builder.verifier = verifier; - } - } - - /// Create a rustls_server_config_builder for TLS sessions that accept - /// valid client certificates, but do not require them. The passed - /// rustls_client_cert_verifier_optional may be used in several builders. - /// For memory lifetime, see rustls_server_config_builder_new. - #[no_mangle] - pub extern "C" fn rustls_server_config_builder_set_client_verifier_optional( - builder: *mut rustls_server_config_builder, - verifier: *const rustls_allow_any_anonymous_or_authenticated_client_verifier, + verifier: *const rustls_client_cert_verifier, ) { ffi_panic_boundary! { let builder: &mut ServerConfigBuilder = try_mut_from_ptr!(builder); - let verifier: Arc = try_clone_arc!(verifier); - - builder.verifier = verifier; + let verifier = try_ref_from_ptr!(verifier); + builder.verifier = verifier.clone(); } } diff --git a/tests/server.c b/tests/server.c index dbed3eb6..013c624a 100644 --- a/tests/server.c +++ b/tests/server.c @@ -240,10 +240,9 @@ main(int argc, const char **argv) struct rustls_slice_bytes alpn_http11; struct rustls_root_cert_store_builder *client_cert_root_store_builder = NULL; const struct rustls_root_cert_store *client_cert_root_store = NULL; - struct rustls_allow_any_authenticated_client_builder + struct rustls_web_pki_client_cert_verifier_builder *client_cert_verifier_builder = NULL; - const struct rustls_allow_any_authenticated_client_verifier - *client_cert_verifier = NULL; + struct rustls_client_cert_verifier *client_cert_verifier = NULL; /* Set this global variable for logging purposes. */ programname = "server"; @@ -301,8 +300,7 @@ main(int argc, const char **argv) goto cleanup; } client_cert_verifier_builder = - rustls_allow_any_authenticated_client_builder_new( - client_cert_root_store); + rustls_web_pki_client_cert_verifier_builder_new(client_cert_root_store); char crlbuf[10000]; size_t crlbuf_len; @@ -312,15 +310,18 @@ main(int argc, const char **argv) goto cleanup; } - result = rustls_allow_any_authenticated_client_builder_add_crl( + result = rustls_web_pki_client_cert_verifier_builder_add_crl( client_cert_verifier_builder, (uint8_t *)crlbuf, certbuf_len); if(result != RUSTLS_RESULT_OK) { goto cleanup; } } - client_cert_verifier = rustls_allow_any_authenticated_client_verifier_new( - client_cert_verifier_builder); + result = rustls_web_pki_client_cert_verifier_builder_build( + client_cert_verifier_builder, &client_cert_verifier); + if(result != RUSTLS_RESULT_OK) { + goto cleanup; + } rustls_server_config_builder_set_client_verifier(config_builder, client_cert_verifier); } @@ -404,9 +405,9 @@ main(int argc, const char **argv) rustls_certified_key_free(certified_key); rustls_root_cert_store_builder_free(client_cert_root_store_builder); rustls_root_cert_store_free(client_cert_root_store); - rustls_allow_any_authenticated_client_builder_free( + rustls_web_pki_client_cert_verifier_builder_free( client_cert_verifier_builder); - rustls_allow_any_authenticated_client_verifier_free(client_cert_verifier); + rustls_client_cert_verifier_free(client_cert_verifier); rustls_server_config_free(server_config); rustls_connection_free(rconn); if(sockfd > 0) { From 20b1081a079a5a63a51bae6950566556caa8f48b Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 15 Nov 2023 13:14:06 -0500 Subject: [PATCH 10/19] implement Debug where required by upstream bounds The upstream Rustls project has added `Debug` bounds to many traits. This commit updates rustls-ffi implementations to derive `Debug`, or implement it by hand, as required. --- src/client.rs | 11 ++++++++++- src/server.rs | 8 ++++++++ src/session.rs | 7 +++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index 11f36c8b..0643f7db 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::convert::TryInto; use std::ffi::{CStr, OsStr}; +use std::fmt::{Debug, Formatter}; use std::fs::File; use std::io::BufReader; use std::slice; @@ -70,6 +71,7 @@ impl Castable for rustls_client_config { type RustType = ClientConfig; } +#[derive(Debug)] struct NoneVerifier; impl ServerCertVerifier for NoneVerifier { @@ -248,7 +250,7 @@ unsafe impl Send for Verifier {} /// rustls_client_config_builder_dangerous_set_certificate_verifier. unsafe impl Sync for Verifier {} -impl rustls::client::danger::ServerCertVerifier for Verifier { +impl ServerCertVerifier for Verifier { fn verify_server_cert( &self, end_entity: &CertificateDer, @@ -313,6 +315,12 @@ impl rustls::client::danger::ServerCertVerifier for Verifier { } } +impl Debug for Verifier { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Verifier").finish() + } +} + impl rustls_client_config_builder { /// Set a custom server certificate verifier. /// @@ -497,6 +505,7 @@ impl rustls_client_config_builder { } /// Always send the same client certificate. +#[derive(Debug)] struct ResolvesClientCertFromChoices { keys: Vec>, } diff --git a/src/server.rs b/src/server.rs index 83ceae1c..cd2d5507 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,5 +1,6 @@ use std::convert::TryInto; use std::ffi::c_void; +use std::fmt::{Debug, Formatter}; use std::ptr::null; use std::slice; use std::sync::Arc; @@ -380,6 +381,7 @@ pub extern "C" fn rustls_server_connection_get_server_name( /// Choose the server certificate to be used for a connection based on certificate /// type. Will pick the first CertfiedKey available that is suitable for /// the SignatureSchemes supported by the client. +#[derive(Debug)] struct ResolvesServerCertFromChoices { choices: Vec>, } @@ -528,6 +530,12 @@ impl ResolvesServerCert for ClientHelloResolver { unsafe impl Sync for ClientHelloResolver {} unsafe impl Send for ClientHelloResolver {} +impl Debug for ClientHelloResolver { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ClientHelloResolver").finish() + } +} + impl rustls_server_config_builder { /// Register a callback to be invoked when a connection created from this config /// sees a TLS ClientHello message. If `userdata` has been set with diff --git a/src/session.rs b/src/session.rs index 6f2594da..d8af719f 100644 --- a/src/session.rs +++ b/src/session.rs @@ -2,6 +2,7 @@ use crate::error::rustls_result; use crate::rslice::rustls_slice_bytes; use crate::userdata_get; use libc::{c_int, c_void, size_t}; +use std::fmt::{Debug, Formatter}; /// Any context information the callback will receive when invoked. pub type rustls_session_store_userdata = *mut c_void; @@ -148,6 +149,12 @@ impl rustls::server::StoresServerSessions for SessionStoreBroker { } } +impl Debug for SessionStoreBroker { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SessionStoreBroker").finish() + } +} + /// This struct can be considered thread safe, as long /// as the registered callbacks are thread safe. This is /// documented as a requirement in the API. From 833c6de9c973c55039d973fd14406a7c5b2312a7 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 15 Nov 2023 13:38:47 -0500 Subject: [PATCH 11/19] cipher: adjust to provider-specific cipher suite imports The upstream rustls crate moved the `cipher_suite` module and defines into provider specific packages. Since rustls-ffi is presently hardcoded to use the *ring*-based crypto provider this commit updates the cipher suite references to use `rustls::crypto::ring::cipher_suite` in place of `rustls::cipher_suite`. --- src/cipher.rs | 52 +++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/cipher.rs b/src/cipher.rs index 638c790d..63594730 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -153,21 +153,23 @@ pub extern "C" fn rustls_default_ciphersuites_get_entry( /// releases. #[no_mangle] pub static mut RUSTLS_ALL_CIPHER_SUITES: [*const rustls_supported_ciphersuite; 9] = [ - &rustls::cipher_suite::TLS13_AES_256_GCM_SHA384 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS13_AES_128_GCM_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite + &rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 as *const SupportedCipherSuite + &rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + &rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite as *const _, ]; @@ -181,21 +183,23 @@ pub static RUSTLS_ALL_CIPHER_SUITES_LEN: usize = unsafe { RUSTLS_ALL_CIPHER_SUIT /// between releases. #[no_mangle] pub static mut RUSTLS_DEFAULT_CIPHER_SUITES: [*const rustls_supported_ciphersuite; 9] = [ - &rustls::cipher_suite::TLS13_AES_256_GCM_SHA384 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS13_AES_128_GCM_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite + &rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 as *const SupportedCipherSuite + &rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + &rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 as *const SupportedCipherSuite - as *const _, - &rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + as *const SupportedCipherSuite as *const _, + &rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 as *const SupportedCipherSuite as *const _, ]; From 12798cc0dac004c9e4c3c28161d513eb8fff5d66 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 15 Nov 2023 13:41:54 -0500 Subject: [PATCH 12/19] error: ClientCertVerifierBuilderError -> VerifierBuilderError This commit updates references to `ClientCertVerifierBuilderError` to track the upstream rename to `VerifierBuilderError`. --- src/error.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index 56d0f0e5..3cb6d344 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use crate::ffi_panic_boundary; use libc::{c_char, c_uint, size_t}; -use rustls::server::ClientCertVerifierBuilderError; +use rustls::server::VerifierBuilderError; use rustls::{CertRevocationListError, CertificateError, Error, InvalidMessage}; /// A return value for a function that may return either success (0) or a @@ -422,12 +422,12 @@ pub(crate) fn map_crl_error(err: CertRevocationListError) -> rustls_result { } } -pub(crate) fn map_verifier_builder_error(err: ClientCertVerifierBuilderError) -> rustls_result { +pub(crate) fn map_verifier_builder_error(err: VerifierBuilderError) -> rustls_result { match err { - ClientCertVerifierBuilderError::NoRootAnchors => { + VerifierBuilderError::NoRootAnchors => { rustls_result::ClientCertVerifierBuilderNoRootAnchors } - ClientCertVerifierBuilderError::InvalidCrl(crl_err) => map_crl_error(crl_err), + VerifierBuilderError::InvalidCrl(crl_err) => map_crl_error(crl_err), _ => rustls_result::General, } } From 95ecbd056869b061db135897c0ea77ac149c94e9 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 15 Nov 2023 13:46:18 -0500 Subject: [PATCH 13/19] update import of rustls::sign::any_supported_type This re-export was removed and instead we need to use `rustls::crypto::ring::sign::any_supported_type` since this is a property of the *ring* specific crypto provider. --- src/cipher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cipher.rs b/src/cipher.rs index 63594730..4adf9139 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -421,7 +421,7 @@ impl rustls_certified_key { rsa_private_key } }; - let signing_key = match rustls::sign::any_supported_type(&private_key) { + let signing_key = match rustls::crypto::ring::sign::any_supported_type(&private_key) { Ok(key) => key, Err(_) => return Err(rustls_result::PrivateKeyParseError), }; From bc0e3ac3c39eba760e86506138efe736a71f4bbe Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 15 Nov 2023 14:39:23 -0500 Subject: [PATCH 14/19] add builder for server cert verifier, root builder from file * Implement a builder pattern and built representation for the webpki server cert verifier. * Update the client config builder to consume a built server cert verifier. * Update the roots builder to support loading roots from a file in addition to pem buffer. --- src/cipher.rs | 231 +++++++++++++++++++++++++++++++++++++++++++++++++- src/client.rs | 77 +++-------------- 2 files changed, 241 insertions(+), 67 deletions(-) diff --git a/src/cipher.rs b/src/cipher.rs index 4adf9139..7a184f5e 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -1,12 +1,16 @@ -use libc::size_t; +use libc::{c_char, size_t}; use std::convert::TryFrom; -use std::io::Cursor; +use std::ffi::{CStr, OsStr}; +use std::fs::File; +use std::io::{BufReader, Cursor}; use std::marker::PhantomData; use std::ptr::null; use std::slice; use std::sync::Arc; use pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer}; +use rustls::client::danger::ServerCertVerifier; +use rustls::client::WebPkiServerVerifier; use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES}; use rustls::server::danger::ClientCertVerifier; use rustls::server::WebPkiClientVerifier; @@ -18,8 +22,8 @@ use crate::error::{self, rustls_result}; use crate::rslice::{rustls_slice_bytes, rustls_str}; use crate::{ ffi_panic_boundary, free_arc, free_box, set_arc_mut_ptr, set_boxed_mut_ptr, to_arc_const_ptr, - to_boxed_mut_ptr, try_mut_from_ptr, try_ref_from_ptr, try_slice, try_take, Castable, - OwnershipArc, OwnershipBox, OwnershipRef, + to_boxed_mut_ptr, try_clone_arc, try_mut_from_ptr, try_ref_from_ptr, try_slice, try_take, + Castable, OwnershipArc, OwnershipBox, OwnershipRef, }; use rustls_result::{AlreadyUsed, NullParameter}; @@ -517,6 +521,67 @@ impl rustls_root_cert_store_builder { } } + /// Add one or more certificates to the root cert store builder using PEM + /// encoded data read from the named file. + /// + /// When `strict` is true an error will return a `CertificateParseError` + /// result. So will an attempt to parse data that has zero certificates. + /// + /// When `strict` is false, unparseable root certificates will be ignored. + /// This may be useful on systems that have syntactically invalid root + /// certificates. + #[no_mangle] + pub extern "C" fn rustls_client_config_builder_load_roots_from_file( + builder: *mut rustls_root_cert_store_builder, + filename: *const c_char, + strict: bool, + ) -> rustls_result { + ffi_panic_boundary! { + let builder: &mut Option = try_mut_from_ptr!(builder); + let builder = match builder { + None => return AlreadyUsed, + Some(b) => b, + }; + + let filename: &CStr = unsafe { + if filename.is_null() { + return rustls_result::NullParameter; + } + CStr::from_ptr(filename) + }; + + let filename: &[u8] = filename.to_bytes(); + let filename: &str = match std::str::from_utf8(filename) { + Ok(s) => s, + Err(_) => return rustls_result::Io, + }; + let filename: &OsStr = OsStr::new(filename); + let mut cafile = match File::open(filename) { + Ok(f) => f, + Err(_) => return rustls_result::Io, + }; + + let mut bufreader = BufReader::new(&mut cafile); + let certs: Result, _> = rustls_pemfile::certs(&mut bufreader).collect(); + let certs = match certs { + Ok(certs) => certs, + Err(_) => return rustls_result::Io, + }; + + // We first copy into a temporary root store so we can uphold our + // API guideline that there are no partial failures or partial + // successes. + let mut roots = RootCertStore::empty(); + let (parsed, rejected) = roots.add_parsable_certificates(certs); + if strict && (rejected > 0 || parsed == 0) { + return rustls_result::CertificateParseError; + } + + builder.roots.roots.append(&mut roots.roots); + rustls_result::Ok + } + } + /// Create a new `rustls_root_cert_store` from the builder. /// /// The builder is consumed and cannot be used again, but must still be freed. @@ -757,3 +822,161 @@ impl rustls_web_pki_client_cert_verifier_builder { } } } + +/// A server certificate verifier being constructed. A builder can be modified by, +/// e.g. `rustls_web_pki_server_cert_verifier_builder_add_crl`. Once you're +/// done configuring settings, call `rustls_web_pki_server_cert_verifier_builder_build` +/// to turn it into a `rustls_server_cert_verifier`. This object is not safe +/// for concurrent mutation. +// TODO(@cpu): Add rustdoc link once available. +pub struct rustls_web_pki_server_cert_verifier_builder { + // We use the opaque struct pattern to tell C about our types without + // telling them what's inside. + // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs + _private: [u8; 0], +} + +pub(crate) struct ServerCertVerifierBuilder { + roots: Arc, + crls: Vec>, + // TODO(@cpu): revocation checking depth + // TODO(@cpu): unknown revocation status policy + // TODO(@cpu): supported algs? +} + +impl Castable for rustls_web_pki_server_cert_verifier_builder { + type Ownership = OwnershipBox; + type RustType = Option; +} + +impl ServerCertVerifierBuilder { + /// Create a `rustls_web_pki_server_cert_verifier_builder`. Caller owns the memory and may + /// free it with `rustls_web_pki_server_cert_verifier_builder_free`, regardless of whether + /// `rustls_web_pki_server_cert_verifier_builder_build` was called. + /// + /// Without further modification the builder will produce a server certificate verifier that + /// will require a server present a certificate that chains to one of the trust anchors + /// in the provided `rustls_root_cert_store`. The root cert store must not be empty. + /// + /// Revocation checking will not be performed unless + /// `rustls_web_pki_server_cert_verifier_builder_add_crl` is used to add certificate revocation + /// lists (CRLs) to the builder. + /// + /// This copies the contents of the `rustls_root_cert_store`. It does not take + /// ownership of the pointed-to data. + #[no_mangle] + pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_new( + store: *const rustls_root_cert_store, + ) -> *mut rustls_web_pki_server_cert_verifier_builder { + ffi_panic_boundary! { + let store = try_clone_arc!(store); + let builder = ServerCertVerifierBuilder { + roots: store, + crls: Vec::default(), + }; + to_boxed_mut_ptr(Some(builder)) + } + } + + /// Add one or more certificate revocation lists (CRLs) to the server certificate verifier + /// builder by reading the CRL content from the provided buffer of PEM encoded content. + /// + /// This function returns an error if the provided buffer is not valid PEM encoded content. + #[no_mangle] + pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_add_crl( + builder: *mut rustls_web_pki_server_cert_verifier_builder, + crl_pem: *const u8, + crl_pem_len: size_t, + ) -> rustls_result { + ffi_panic_boundary! { + let server_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder = match server_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + let crl_pem: &[u8] = try_slice!(crl_pem, crl_pem_len); + let crls_der: Result, _> = crls(&mut Cursor::new(crl_pem)).collect(); + let crls_der = match crls_der{ + Ok(vv) => vv, + Err(_) => return rustls_result::CertificateRevocationListParseError, + }; + if crls_der.is_empty() { + return rustls_result::CertificateRevocationListParseError; + } + + server_verifier_builder.crls.extend(crls_der); + + rustls_result::Ok + } + } + + /// Create a new server certificate verifier from the builder. + /// + /// The builder is consumed and cannot be used again, but must still be freed. + /// + /// The verifier can be used in several `rustls_client_config` instances and must be + /// freed by the application when no longer needed. See the documentation of + /// `rustls_web_pki_server_cert_verifier_builder_free` for details about lifetime. + #[no_mangle] + pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_build( + builder: *mut rustls_web_pki_server_cert_verifier_builder, + verifier_out: *mut *mut rustls_server_cert_verifier, + ) -> rustls_result { + ffi_panic_boundary! { + let server_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder = try_take!(server_verifier_builder); + + let builder = WebPkiServerVerifier::builder(server_verifier_builder.roots) + .with_crls(server_verifier_builder.crls); + + let verifier = match builder.build() { + Ok(v) => v, + Err(e) => return error::map_verifier_builder_error(e), + }; + + set_boxed_mut_ptr(verifier_out, verifier); + + rustls_result::Ok + } + } + + /// Free a `rustls_server_cert_verifier_builder` previously returned from + /// `rustls_server_cert_verifier_builder_new`. Calling with NULL is fine. Must not be + /// called twice with the same value. + #[no_mangle] + pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_free( + builder: *mut rustls_web_pki_server_cert_verifier_builder, + ) { + ffi_panic_boundary! { + free_box(builder); + } + } +} + +/// A built server certificate verifier that can be provided to a `rustls_client_config_builder` +/// with `rustls_client_config_builder_set_server_verifier`. +pub struct rustls_server_cert_verifier { + _private: [u8; 0], +} + +/// Rustls' ConfigBuilder requires an `Arc` here, meaning we +/// must follow the pattern described in CONTRIBUTING.md[0] for handling dynamically sized +/// types (DSTs) across the FFI boundary. +/// [0] +impl Castable for rustls_server_cert_verifier { + type Ownership = OwnershipBox; + type RustType = Arc; +} + +impl rustls_server_cert_verifier { + /// Free a `rustls_server_cert_verifier` previously returned from + /// `rustls_server_cert_verifier_builder_build`. Calling with NULL is fine. Must not be + /// called twice with the same value. + #[no_mangle] + pub extern "C" fn rustls_server_cert_verifier_free(verifier: *mut rustls_server_cert_verifier) { + ffi_panic_boundary! { + free_box(verifier); + } + } +} diff --git a/src/client.rs b/src/client.rs index 0643f7db..28075343 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,9 +1,7 @@ use std::borrow::Cow; use std::convert::TryInto; -use std::ffi::{CStr, OsStr}; +use std::ffi::CStr; use std::fmt::{Debug, Formatter}; -use std::fs::File; -use std::io::BufReader; use std::slice; use std::sync::Arc; @@ -14,10 +12,12 @@ use rustls::client::{ResolvesClientCert, WebPkiServerVerifier}; use rustls::crypto::ring::ALL_CIPHER_SUITES; use rustls::{ sign::CertifiedKey, CertificateError, ClientConfig, ClientConnection, DigitallySignedStruct, - Error, ProtocolVersion, RootCertStore, SignatureScheme, SupportedCipherSuite, WantsVerifier, + Error, ProtocolVersion, SignatureScheme, SupportedCipherSuite, WantsVerifier, }; -use crate::cipher::{rustls_certified_key, rustls_root_cert_store, rustls_supported_ciphersuite}; +use crate::cipher::{ + rustls_certified_key, rustls_server_cert_verifier, rustls_supported_ciphersuite, +}; use crate::connection::{rustls_connection, Connection}; use crate::error::rustls_result::{InvalidParameter, NullParameter}; use crate::error::{self, rustls_result}; @@ -364,67 +364,18 @@ impl rustls_client_config_builder { } } - /// Use the trusted root certificates from the provided store. + /// Configure the server certificate verifier. /// - /// This replaces any trusted roots already configured with copies - /// from `roots`. This adds 1 to the refcount for `roots`. When you - /// call rustls_client_config_free or rustls_client_config_builder_free, - /// those will subtract 1 from the refcount for `roots`. + /// This increases the reference count of `verifier` and does not take ownership. #[no_mangle] - pub extern "C" fn rustls_client_config_builder_use_roots( - config_builder: *mut rustls_client_config_builder, - roots: *const rustls_root_cert_store, - ) -> rustls_result { - ffi_panic_boundary! { - let builder = try_mut_from_ptr!(config_builder); - let root_store: &RootCertStore = try_ref_from_ptr!(roots); - builder.verifier = Arc::new(rustls::client::WebPkiServerVerifier::new(root_store.clone())); - rustls_result::Ok - } - } - - /// Add trusted root certificates from the named file, which should contain - /// PEM-formatted certificates. - #[no_mangle] - pub extern "C" fn rustls_client_config_builder_load_roots_from_file( - config_builder: *mut rustls_client_config_builder, - filename: *const c_char, - ) -> rustls_result { + pub extern "C" fn rustls_client_config_builder_set_server_verifier( + builder: *mut rustls_client_config_builder, + verifier: *const rustls_server_cert_verifier, + ) { ffi_panic_boundary! { - let config_builder = try_mut_from_ptr!(config_builder); - let filename: &CStr = unsafe { - if filename.is_null() { - return rustls_result::NullParameter; - } - CStr::from_ptr(filename) - }; - - let filename: &[u8] = filename.to_bytes(); - let filename: &str = match std::str::from_utf8(filename) { - Ok(s) => s, - Err(_) => return rustls_result::Io, - }; - let filename: &OsStr = OsStr::new(filename); - let mut cafile = match File::open(filename) { - Ok(f) => f, - Err(_) => return rustls_result::Io, - }; - - let mut bufreader = BufReader::new(&mut cafile); - let certs: Result, _> = rustls_pemfile::certs(&mut bufreader).collect(); - let certs = match certs { - Ok(certs) => certs, - Err(_) => return rustls_result::Io, - }; - - let mut roots = RootCertStore::empty(); - let (_, failed) = roots.add_parsable_certificates(certs); - if failed > 0 { - return rustls_result::CertificateParseError; - } - - config_builder.verifier = Arc::new(rustls::client::WebPkiServerVerifier::new(roots)); - rustls_result::Ok + let builder: &mut ClientConfigBuilder = try_mut_from_ptr!(builder); + let verifier = try_ref_from_ptr!(verifier); + builder.verifier = verifier.clone(); } } From 27049a23391a4e268718abcb7f9fddf323027461 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Fri, 17 Nov 2023 14:16:32 -0500 Subject: [PATCH 15/19] regenerate rustls.h --- src/rustls.h | 110 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 92 insertions(+), 18 deletions(-) diff --git a/src/rustls.h b/src/rustls.h index 32411478..e405f7a1 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -230,6 +230,12 @@ typedef struct rustls_root_cert_store rustls_root_cert_store; */ typedef struct rustls_root_cert_store_builder rustls_root_cert_store_builder; +/** + * A built server certificate verifier that can be provided to a `rustls_client_config_builder` + * with `rustls_client_config_builder_set_server_verifier`. + */ +typedef struct rustls_server_cert_verifier rustls_server_cert_verifier; + /** * A server config that is done being constructed and is now read-only. * Under the hood, this object corresponds to an `Arc`. @@ -297,6 +303,15 @@ typedef struct rustls_supported_ciphersuite rustls_supported_ciphersuite; */ typedef struct rustls_web_pki_client_cert_verifier_builder rustls_web_pki_client_cert_verifier_builder; +/** + * A server certificate verifier being constructed. A builder can be modified by, + * e.g. `rustls_web_pki_server_cert_verifier_builder_add_crl`. Once you're + * done configuring settings, call `rustls_web_pki_server_cert_verifier_builder_build` + * to turn it into a `rustls_server_cert_verifier`. This object is not safe + * for concurrent mutation. + */ +typedef struct rustls_web_pki_server_cert_verifier_builder rustls_web_pki_server_cert_verifier_builder; + /** * A read-only view on a Rust `&str`. The contents are guaranteed to be valid * UTF-8. As an additional guarantee on top of Rust's normal UTF-8 guarantee, @@ -946,6 +961,21 @@ rustls_result rustls_root_cert_store_builder_add_pem(struct rustls_root_cert_sto size_t pem_len, bool strict); +/** + * Add one or more certificates to the root cert store builder using PEM + * encoded data read from the named file. + * + * When `strict` is true an error will return a `CertificateParseError` + * result. So will an attempt to parse data that has zero certificates. + * + * When `strict` is false, unparseable root certificates will be ignored. + * This may be useful on systems that have syntactically invalid root + * certificates. + */ +rustls_result rustls_client_config_builder_load_roots_from_file(struct rustls_root_cert_store_builder *builder, + const char *filename, + bool strict); + /** * Create a new `rustls_root_cert_store` from the builder. * @@ -979,9 +1009,9 @@ void rustls_root_cert_store_free(const struct rustls_root_cert_store *store); void rustls_client_cert_verifier_free(struct rustls_client_cert_verifier *verifier); /** - * Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and must - * eventually call `rustls_web_pki_client_cert_verifier_builder_build`, then free the - * resulting `rustls_client_cert_verifier`. + * Create a `rustls_web_pki_client_cert_verifier_builder`. Caller owns the memory and may + * eventually call `rustls_web_pki_client_cert_verifier_builder_free` to free it, whether or + * not `rustls_web_pki_client_cert_verifier_builder_build` was called. * * Without further modification the builder will produce a client certificate verifier that * will require a client present a client certificate that chains to one of the trust anchors @@ -1034,6 +1064,60 @@ rustls_result rustls_web_pki_client_cert_verifier_builder_build(struct rustls_we */ void rustls_web_pki_client_cert_verifier_builder_free(struct rustls_web_pki_client_cert_verifier_builder *builder); +/** + * Create a `rustls_web_pki_server_cert_verifier_builder`. Caller owns the memory and may + * free it with `rustls_web_pki_server_cert_verifier_builder_free`, regardless of whether + * `rustls_web_pki_server_cert_verifier_builder_build` was called. + * + * Without further modification the builder will produce a server certificate verifier that + * will require a server present a certificate that chains to one of the trust anchors + * in the provided `rustls_root_cert_store`. The root cert store must not be empty. + * + * Revocation checking will not be performed unless + * `rustls_web_pki_server_cert_verifier_builder_add_crl` is used to add certificate revocation + * lists (CRLs) to the builder. + * + * This copies the contents of the `rustls_root_cert_store`. It does not take + * ownership of the pointed-to data. + */ +struct rustls_web_pki_server_cert_verifier_builder *rustls_web_pki_server_cert_verifier_builder_new(const struct rustls_root_cert_store *store); + +/** + * Add one or more certificate revocation lists (CRLs) to the server certificate verifier + * builder by reading the CRL content from the provided buffer of PEM encoded content. + * + * This function returns an error if the provided buffer is not valid PEM encoded content. + */ +rustls_result rustls_web_pki_server_cert_verifier_builder_add_crl(struct rustls_web_pki_server_cert_verifier_builder *builder, + const uint8_t *crl_pem, + size_t crl_pem_len); + +/** + * Create a new server certificate verifier from the builder. + * + * The builder is consumed and cannot be used again, but must still be freed. + * + * The verifier can be used in several `rustls_client_config` instances and must be + * freed by the application when no longer needed. See the documentation of + * `rustls_web_pki_server_cert_verifier_builder_free` for details about lifetime. + */ +rustls_result rustls_web_pki_server_cert_verifier_builder_build(struct rustls_web_pki_server_cert_verifier_builder *builder, + struct rustls_server_cert_verifier **verifier_out); + +/** + * Free a `rustls_server_cert_verifier_builder` previously returned from + * `rustls_server_cert_verifier_builder_new`. Calling with NULL is fine. Must not be + * called twice with the same value. + */ +void rustls_web_pki_server_cert_verifier_builder_free(struct rustls_web_pki_server_cert_verifier_builder *builder); + +/** + * Free a `rustls_server_cert_verifier` previously returned from + * `rustls_server_cert_verifier_builder_build`. Calling with NULL is fine. Must not be + * called twice with the same value. + */ +void rustls_server_cert_verifier_free(struct rustls_server_cert_verifier *verifier); + /** * Create a rustls_client_config_builder. Caller owns the memory and must * eventually call rustls_client_config_builder_build, then free the @@ -1100,22 +1184,12 @@ rustls_result rustls_client_config_builder_dangerous_set_certificate_verifier(st rustls_verify_server_cert_callback callback); /** - * Use the trusted root certificates from the provided store. + * Configure the server certificate verifier. * - * This replaces any trusted roots already configured with copies - * from `roots`. This adds 1 to the refcount for `roots`. When you - * call rustls_client_config_free or rustls_client_config_builder_free, - * those will subtract 1 from the refcount for `roots`. + * This increases the reference count of `verifier` and does not take ownership. */ -rustls_result rustls_client_config_builder_use_roots(struct rustls_client_config_builder *config_builder, - const struct rustls_root_cert_store *roots); - -/** - * Add trusted root certificates from the named file, which should contain - * PEM-formatted certificates. - */ -rustls_result rustls_client_config_builder_load_roots_from_file(struct rustls_client_config_builder *config_builder, - const char *filename); +void rustls_client_config_builder_set_server_verifier(struct rustls_client_config_builder *builder, + const struct rustls_server_cert_verifier *verifier); /** * Set the ALPN protocol list to the given protocols. `protocols` must point @@ -1485,7 +1559,7 @@ rustls_result rustls_server_config_builder_new_custom(const struct rustls_suppor /** * Create a rustls_server_config_builder for TLS sessions that may verify client - * certificates. + * certificates. This increases the refcount of `verifier` and doesn't take ownership. */ void rustls_server_config_builder_set_client_verifier(struct rustls_server_config_builder *builder, const struct rustls_client_cert_verifier *verifier); From 057aa4857a71406d479ade3b46d6dd04147b357c Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 15 Nov 2023 14:58:35 -0500 Subject: [PATCH 16/19] update client for server cert verifier builder --- tests/client.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/client.c b/tests/client.c index a406595e..5f29a3e8 100644 --- a/tests/client.c +++ b/tests/client.c @@ -413,7 +413,12 @@ main(int argc, const char **argv) struct rustls_client_config_builder *config_builder = rustls_client_config_builder_new(); + struct rustls_root_cert_store_builder *server_cert_root_store_builder = NULL; + const struct rustls_root_cert_store *server_cert_root_store = NULL; const struct rustls_client_config *client_config = NULL; + struct rustls_web_pki_server_cert_verifier_builder + *server_cert_verifier_builder = NULL; + struct rustls_server_cert_verifier *server_cert_verifier = NULL; struct rustls_slice_bytes alpn_http11; const struct rustls_certified_key *certified_key = NULL; @@ -427,12 +432,29 @@ main(int argc, const char **argv) #endif if(getenv("CA_FILE")) { + server_cert_root_store_builder = rustls_root_cert_store_builder_new(); result = rustls_client_config_builder_load_roots_from_file( - config_builder, getenv("CA_FILE")); + server_cert_root_store_builder, getenv("CA_FILE"), true); if(result != RUSTLS_RESULT_OK) { print_error("loading trusted certificates", result); goto cleanup; } + result = rustls_root_cert_store_builder_build( + server_cert_root_store_builder, &server_cert_root_store); + if(result != RUSTLS_RESULT_OK) { + goto cleanup; + } + + server_cert_verifier_builder = + rustls_web_pki_server_cert_verifier_builder_new(server_cert_root_store); + + result = rustls_web_pki_server_cert_verifier_builder_build( + server_cert_verifier_builder, &server_cert_verifier); + if(result != RUSTLS_RESULT_OK) { + goto cleanup; + } + rustls_client_config_builder_set_server_verifier(config_builder, + server_cert_verifier); } else if(getenv("NO_CHECK_CERTIFICATE")) { rustls_client_config_builder_dangerous_set_certificate_verifier( @@ -479,6 +501,11 @@ main(int argc, const char **argv) ret = 0; cleanup: + rustls_root_cert_store_builder_free(server_cert_root_store_builder); + rustls_root_cert_store_free(server_cert_root_store); + rustls_web_pki_server_cert_verifier_builder_free( + server_cert_verifier_builder); + rustls_server_cert_verifier_free(server_cert_verifier); rustls_certified_key_free(certified_key); rustls_client_config_free(client_config); From b9af9f8b7ffa0535a4c49edc1884f0f44a65cab6 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 15 Nov 2023 16:46:42 -0500 Subject: [PATCH 17/19] revocation status and unknown status control w/ verifier builders --- src/cipher.rs | 132 +++++++++++++++++++++++++++++++++++++++++++++++--- src/rustls.h | 54 ++++++++++++++++++++- 2 files changed, 178 insertions(+), 8 deletions(-) diff --git a/src/cipher.rs b/src/cipher.rs index 7a184f5e..3efa726c 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -17,6 +17,7 @@ use rustls::server::WebPkiClientVerifier; use rustls::sign::CertifiedKey; use rustls::{RootCertStore, SupportedCipherSuite}; use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys}; +use webpki::{RevocationCheckDepth, UnknownStatusPolicy}; use crate::error::{self, rustls_result}; use crate::rslice::{rustls_slice_bytes, rustls_str}; @@ -685,6 +686,8 @@ pub struct rustls_web_pki_client_cert_verifier_builder { pub(crate) struct ClientCertVerifierBuilder { roots: Arc, crls: Vec>, + revocation_depth: RevocationCheckDepth, + revocation_policy: UnknownStatusPolicy, allow_unauthenticated: bool, } @@ -704,7 +707,12 @@ impl rustls_web_pki_client_cert_verifier_builder { /// /// Revocation checking will not be performed unless /// `rustls_web_pki_client_cert_verifier_builder_add_crl` is used to add certificate revocation - /// lists (CRLs) to the builder. + /// lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed + /// for the entire certificate chain unless + /// `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation` is used. Unknown + /// revocation status for certificates considered for revocation status will be treated as + /// an error unless `rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status` is + /// used. /// /// Unauthenticated clients will not be permitted unless /// `rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated` is used. @@ -720,6 +728,8 @@ impl rustls_web_pki_client_cert_verifier_builder { let builder = ClientCertVerifierBuilder { roots: store, crls: Vec::default(), + revocation_depth: RevocationCheckDepth::Chain, + revocation_policy: UnknownStatusPolicy::Deny, allow_unauthenticated: false, }; to_boxed_mut_ptr(Some(builder)) @@ -729,6 +739,10 @@ impl rustls_web_pki_client_cert_verifier_builder { /// Add one or more certificate revocation lists (CRLs) to the client certificate verifier /// builder by reading the CRL content from the provided buffer of PEM encoded content. /// + /// By default revocation checking will be performed on the entire certificate chain. To only + /// check the revocation status of the end entity certificate, use + /// `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation`. + /// /// This function returns an error if the provided buffer is not valid PEM encoded content. #[no_mangle] pub extern "C" fn rustls_web_pki_client_cert_verifier_builder_add_crl( @@ -759,6 +773,46 @@ impl rustls_web_pki_client_cert_verifier_builder { } } + /// When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, only + /// check the revocation status of end entity certificates, ignoring any intermediate certificates + /// in the chain. + #[no_mangle] + pub extern "C" fn rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + ) -> rustls_result { + ffi_panic_boundary! { + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + client_verifier_builder.revocation_depth = RevocationCheckDepth::EndEntity; + rustls_result::Ok + } + } + + /// When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, and it + /// isn't possible to determine the revocation status of a considered certificate, do not treat + /// it as an error condition. + /// + /// Overrides the default behavior where unknown revocation status is considered an error. + #[no_mangle] + pub extern "C" fn rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + ) -> rustls_result { + ffi_panic_boundary! { + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + client_verifier_builder.revocation_policy = UnknownStatusPolicy::Allow; + rustls_result::Ok + } + } + /// Allow unauthenticated anonymous clients in addition to those that present a client /// certificate that chains to one of the verifier's configured trust anchors. #[no_mangle] @@ -795,6 +849,14 @@ impl rustls_web_pki_client_cert_verifier_builder { let mut builder = WebPkiClientVerifier::builder(client_verifier_builder.roots) .with_crls(client_verifier_builder.crls); + match client_verifier_builder.revocation_depth { + RevocationCheckDepth::EndEntity => builder = builder.only_check_end_entity_revocation(), + RevocationCheckDepth::Chain => {}, + } + match client_verifier_builder.revocation_policy { + UnknownStatusPolicy::Allow => builder = builder.allow_unknown_revocation_status(), + UnknownStatusPolicy::Deny => {}, + } if client_verifier_builder.allow_unauthenticated { builder = builder.allow_unauthenticated(); } @@ -839,9 +901,8 @@ pub struct rustls_web_pki_server_cert_verifier_builder { pub(crate) struct ServerCertVerifierBuilder { roots: Arc, crls: Vec>, - // TODO(@cpu): revocation checking depth - // TODO(@cpu): unknown revocation status policy - // TODO(@cpu): supported algs? + revocation_depth: RevocationCheckDepth, + revocation_policy: UnknownStatusPolicy, } impl Castable for rustls_web_pki_server_cert_verifier_builder { @@ -860,7 +921,12 @@ impl ServerCertVerifierBuilder { /// /// Revocation checking will not be performed unless /// `rustls_web_pki_server_cert_verifier_builder_add_crl` is used to add certificate revocation - /// lists (CRLs) to the builder. + /// lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed + /// for the entire certificate chain unless + /// `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation` is used. Unknown + /// revocation status for certificates considered for revocation status will be treated as + /// an error unless `rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status` is + /// used. /// /// This copies the contents of the `rustls_root_cert_store`. It does not take /// ownership of the pointed-to data. @@ -873,6 +939,8 @@ impl ServerCertVerifierBuilder { let builder = ServerCertVerifierBuilder { roots: store, crls: Vec::default(), + revocation_depth: RevocationCheckDepth::Chain, + revocation_policy: UnknownStatusPolicy::Deny }; to_boxed_mut_ptr(Some(builder)) } @@ -881,6 +949,10 @@ impl ServerCertVerifierBuilder { /// Add one or more certificate revocation lists (CRLs) to the server certificate verifier /// builder by reading the CRL content from the provided buffer of PEM encoded content. /// + /// By default revocation checking will be performed on the entire certificate chain. To only + /// check the revocation status of the end entity certificate, use + /// `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation`. + /// /// This function returns an error if the provided buffer is not valid PEM encoded content. #[no_mangle] pub extern "C" fn rustls_web_pki_server_cert_verifier_builder_add_crl( @@ -911,6 +983,46 @@ impl ServerCertVerifierBuilder { } } + /// When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, only + /// check the revocation status of end entity certificates, ignoring any intermediate certificates + /// in the chain. + #[no_mangle] + pub extern "C" fn rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation( + builder: *mut rustls_web_pki_server_cert_verifier_builder, + ) -> rustls_result { + ffi_panic_boundary! { + let server_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder = match server_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + server_verifier_builder.revocation_depth = RevocationCheckDepth::EndEntity; + rustls_result::Ok + } + } + + /// When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, and it + /// isn't possible to determine the revocation status of a considered certificate, do not treat + /// it as an error condition. + /// + /// Overrides the default behavior where unknown revocation status is considered an error. + #[no_mangle] + pub extern "C" fn rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status( + builder: *mut rustls_web_pki_server_cert_verifier_builder, + ) -> rustls_result { + ffi_panic_boundary! { + let server_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let server_verifier_builder = match server_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + server_verifier_builder.revocation_policy = UnknownStatusPolicy::Allow; + rustls_result::Ok + } + } + /// Create a new server certificate verifier from the builder. /// /// The builder is consumed and cannot be used again, but must still be freed. @@ -927,8 +1039,16 @@ impl ServerCertVerifierBuilder { let server_verifier_builder: &mut Option = try_mut_from_ptr!(builder); let server_verifier_builder = try_take!(server_verifier_builder); - let builder = WebPkiServerVerifier::builder(server_verifier_builder.roots) + let mut builder = WebPkiServerVerifier::builder(server_verifier_builder.roots) .with_crls(server_verifier_builder.crls); + match server_verifier_builder.revocation_depth { + RevocationCheckDepth::EndEntity => builder = builder.only_check_end_entity_revocation(), + RevocationCheckDepth::Chain => {}, + } + match server_verifier_builder.revocation_policy { + UnknownStatusPolicy::Allow => builder = builder.allow_unknown_revocation_status(), + UnknownStatusPolicy::Deny => {}, + } let verifier = match builder.build() { Ok(v) => v, diff --git a/src/rustls.h b/src/rustls.h index e405f7a1..dd570847 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -1019,7 +1019,12 @@ void rustls_client_cert_verifier_free(struct rustls_client_cert_verifier *verifi * * Revocation checking will not be performed unless * `rustls_web_pki_client_cert_verifier_builder_add_crl` is used to add certificate revocation - * lists (CRLs) to the builder. + * lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed + * for the entire certificate chain unless + * `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation` is used. Unknown + * revocation status for certificates considered for revocation status will be treated as + * an error unless `rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status` is + * used. * * Unauthenticated clients will not be permitted unless * `rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated` is used. @@ -1033,12 +1038,32 @@ struct rustls_web_pki_client_cert_verifier_builder *rustls_web_pki_client_cert_v * Add one or more certificate revocation lists (CRLs) to the client certificate verifier * builder by reading the CRL content from the provided buffer of PEM encoded content. * + * By default revocation checking will be performed on the entire certificate chain. To only + * check the revocation status of the end entity certificate, use + * `rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation`. + * * This function returns an error if the provided buffer is not valid PEM encoded content. */ rustls_result rustls_web_pki_client_cert_verifier_builder_add_crl(struct rustls_web_pki_client_cert_verifier_builder *builder, const uint8_t *crl_pem, size_t crl_pem_len); +/** + * When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, only + * check the revocation status of end entity certificates, ignoring any intermediate certificates + * in the chain. + */ +rustls_result rustls_web_pki_client_cert_verifier_only_check_end_entity_revocation(struct rustls_web_pki_client_cert_verifier_builder *builder); + +/** + * When CRLs are provided with `rustls_web_pki_client_cert_verifier_builder_add_crl`, and it + * isn't possible to determine the revocation status of a considered certificate, do not treat + * it as an error condition. + * + * Overrides the default behavior where unknown revocation status is considered an error. + */ +rustls_result rustls_web_pki_client_cert_verifier_allow_unknown_revocation_status(struct rustls_web_pki_client_cert_verifier_builder *builder); + /** * Allow unauthenticated anonymous clients in addition to those that present a client * certificate that chains to one of the verifier's configured trust anchors. @@ -1075,7 +1100,12 @@ void rustls_web_pki_client_cert_verifier_builder_free(struct rustls_web_pki_clie * * Revocation checking will not be performed unless * `rustls_web_pki_server_cert_verifier_builder_add_crl` is used to add certificate revocation - * lists (CRLs) to the builder. + * lists (CRLs) to the builder. If CRLs are added, revocation checking will be performed + * for the entire certificate chain unless + * `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation` is used. Unknown + * revocation status for certificates considered for revocation status will be treated as + * an error unless `rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status` is + * used. * * This copies the contents of the `rustls_root_cert_store`. It does not take * ownership of the pointed-to data. @@ -1086,12 +1116,32 @@ struct rustls_web_pki_server_cert_verifier_builder *rustls_web_pki_server_cert_v * Add one or more certificate revocation lists (CRLs) to the server certificate verifier * builder by reading the CRL content from the provided buffer of PEM encoded content. * + * By default revocation checking will be performed on the entire certificate chain. To only + * check the revocation status of the end entity certificate, use + * `rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation`. + * * This function returns an error if the provided buffer is not valid PEM encoded content. */ rustls_result rustls_web_pki_server_cert_verifier_builder_add_crl(struct rustls_web_pki_server_cert_verifier_builder *builder, const uint8_t *crl_pem, size_t crl_pem_len); +/** + * When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, only + * check the revocation status of end entity certificates, ignoring any intermediate certificates + * in the chain. + */ +rustls_result rustls_web_pki_server_cert_verifier_only_check_end_entity_revocation(struct rustls_web_pki_server_cert_verifier_builder *builder); + +/** + * When CRLs are provided with `rustls_web_pki_server_cert_verifier_builder_add_crl`, and it + * isn't possible to determine the revocation status of a considered certificate, do not treat + * it as an error condition. + * + * Overrides the default behavior where unknown revocation status is considered an error. + */ +rustls_result rustls_web_pki_server_cert_verifier_allow_unknown_revocation_status(struct rustls_web_pki_server_cert_verifier_builder *builder); + /** * Create a new server certificate verifier from the builder. * From 0700a45190d32e8894c24fba92749896d6f2476d Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 15 Nov 2023 17:11:26 -0500 Subject: [PATCH 18/19] control root hint subjects w/ client verifier builder --- src/cipher.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/rustls.h | 22 +++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/cipher.rs b/src/cipher.rs index 3efa726c..e52da198 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -15,7 +15,7 @@ use rustls::crypto::ring::{ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES}; use rustls::server::danger::ClientCertVerifier; use rustls::server::WebPkiClientVerifier; use rustls::sign::CertifiedKey; -use rustls::{RootCertStore, SupportedCipherSuite}; +use rustls::{DistinguishedName, RootCertStore, SupportedCipherSuite}; use rustls_pemfile::{certs, crls, pkcs8_private_keys, rsa_private_keys}; use webpki::{RevocationCheckDepth, UnknownStatusPolicy}; @@ -685,6 +685,7 @@ pub struct rustls_web_pki_client_cert_verifier_builder { pub(crate) struct ClientCertVerifierBuilder { roots: Arc, + root_hint_subjects: Vec, crls: Vec>, revocation_depth: RevocationCheckDepth, revocation_policy: UnknownStatusPolicy, @@ -726,6 +727,7 @@ impl rustls_web_pki_client_cert_verifier_builder { ffi_panic_boundary! { let store = try_clone_arc!(store); let builder = ClientCertVerifierBuilder { + root_hint_subjects: store.subjects(), roots: store, crls: Vec::default(), revocation_depth: RevocationCheckDepth::Chain, @@ -831,6 +833,52 @@ impl rustls_web_pki_client_cert_verifier_builder { } } + /// Clear the list of trust anchor hint subjects. + /// + /// By default, the client cert verifier will use the subjects provided by the root cert + /// store configured for client authentication. Calling this function will remove these + /// hint subjects, indicating the client should make a free choice of which certificate + /// to send. + #[no_mangle] + pub extern "C" fn rustls_web_pki_client_cert_verifier_clear_root_hint_subjects( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + ) -> rustls_result { + ffi_panic_boundary! { + let client_verifier_builder: &mut Option = try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + client_verifier_builder.root_hint_subjects.clear(); + rustls_result::Ok + } + } + + /// Add additional distinguished names to the list of trust anchor hint subjects. + /// + /// By default, the client cert verifier will use the subjects provided by the root cert + /// store configured for client authentication. Calling this function will add to these + /// existing hint subjects. Calling this function with an empty `store` will have no + /// effect, use `rustls_web_pki_client_cert_verifier_clear_root_hint_subjects` to clear + /// the subject hints. + #[no_mangle] + pub extern "C" fn rustls_web_pki_client_cert_verifier_add_root_hint_subjects( + builder: *mut rustls_web_pki_client_cert_verifier_builder, + store: *const rustls_root_cert_store, + ) -> rustls_result { + let client_verifier_builder: &mut Option = + try_mut_from_ptr!(builder); + let client_verifier_builder = match client_verifier_builder { + None => return AlreadyUsed, + Some(v) => v, + }; + + let store = try_clone_arc!(store); + client_verifier_builder.root_hint_subjects = store.subjects(); + rustls_result::Ok + } + /// Create a new client certificate verifier from the builder. /// /// The builder is consumed and cannot be used again, but must still be freed. @@ -860,6 +908,11 @@ impl rustls_web_pki_client_cert_verifier_builder { if client_verifier_builder.allow_unauthenticated { builder = builder.allow_unauthenticated(); } + if client_verifier_builder.root_hint_subjects.is_empty() { + builder = builder.clear_root_hint_subjects(); + } else { + builder = builder.add_root_hint_subjects(client_verifier_builder.root_hint_subjects); + } let verifier = match builder.build() { Ok(v) => v, diff --git a/src/rustls.h b/src/rustls.h index dd570847..37756135 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -1070,6 +1070,28 @@ rustls_result rustls_web_pki_client_cert_verifier_allow_unknown_revocation_statu */ rustls_result rustls_web_pki_client_cert_verifier_builder_allow_unauthenticated(struct rustls_web_pki_client_cert_verifier_builder *builder); +/** + * Clear the list of trust anchor hint subjects. + * + * By default, the client cert verifier will use the subjects provided by the root cert + * store configured for client authentication. Calling this function will remove these + * hint subjects, indicating the client should make a free choice of which certificate + * to send. + */ +rustls_result rustls_web_pki_client_cert_verifier_clear_root_hint_subjects(struct rustls_web_pki_client_cert_verifier_builder *builder); + +/** + * Add additional distinguished names to the list of trust anchor hint subjects. + * + * By default, the client cert verifier will use the subjects provided by the root cert + * store configured for client authentication. Calling this function will add to these + * existing hint subjects. Calling this function with an empty `store` will have no + * effect, use `rustls_web_pki_client_cert_verifier_clear_root_hint_subjects` to clear + * the subject hints. + */ +rustls_result rustls_web_pki_client_cert_verifier_add_root_hint_subjects(struct rustls_web_pki_client_cert_verifier_builder *builder, + const struct rustls_root_cert_store *store); + /** * Create a new client certificate verifier from the builder. * From 2d6c77cc3b0a2e88a820aea1bb6dd2e9bd23c945 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Mon, 20 Nov 2023 10:15:40 -0500 Subject: [PATCH 19/19] cbindgen update for comment tweaks --- src/rustls.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/rustls.h b/src/rustls.h index 37756135..aa870fbc 100644 --- a/src/rustls.h +++ b/src/rustls.h @@ -222,11 +222,12 @@ typedef struct rustls_iovec rustls_iovec; typedef struct rustls_root_cert_store rustls_root_cert_store; /** - * A `rustls_root_cert_store` being constructed. A builder can be modified by - * adding trust anchor root certificates with `rustls_root_cert_store_builder_add_pem`. - * Once you're done adding root certificates, call `rustls_root_cert_store_builder_build` - * to turn it into a `rustls_root_cert_store`. This object is not safe - * for concurrent mutation. + * A `rustls_root_cert_store` being constructed. + * + * A builder can be modified by adding trust anchor root certificates with + * `rustls_root_cert_store_builder_add_pem`. Once you're done adding root certificates, + * call `rustls_root_cert_store_builder_build` to turn it into a `rustls_root_cert_store`. + * This object is not safe for concurrent mutation. */ typedef struct rustls_root_cert_store_builder rustls_root_cert_store_builder; @@ -936,9 +937,10 @@ rustls_result rustls_certified_key_clone_with_ocsp(const struct rustls_certified void rustls_certified_key_free(const struct rustls_certified_key *key); /** - * Create a `rustls_root_cert_store_builder`. Caller owns the memory and may - * free it with `rustls_root_cert_store_free`, regardless of whether - * `rustls_root_cert_store_builder_build` was called. + * Create a `rustls_root_cert_store_builder`. + * + * Caller owns the memory and may free it with `rustls_root_cert_store_free`, regardless of + * whether `rustls_root_cert_store_builder_build` was called. * * If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`, * it must be freed with `rustls_root_cert_store_builder_free`.