From 9582cc88c35484760540f82a552d70cec66b93ab Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Tue, 5 Nov 2024 20:07:06 -0500 Subject: [PATCH] client: consolidate builder impl blocks Previously there were three. Let's reduce to one. --- src/client.rs | 320 +++++++++++++++++++++++++------------------------- 1 file changed, 158 insertions(+), 162 deletions(-) diff --git a/src/client.rs b/src/client.rs index c0a037db..65ff7a5c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -157,142 +157,7 @@ impl rustls_client_config_builder { rustls_result::Ok } } -} - -/// Input to a custom certificate verifier callback. -/// -/// See `rustls_client_config_builder_dangerous_set_certificate_verifier()`. -/// -/// server_name can contain a hostname, an IPv4 address in textual form, or an -/// IPv6 address in textual form. -#[allow(non_camel_case_types)] -#[repr(C)] -pub struct rustls_verify_server_cert_params<'a> { - pub end_entity_cert_der: rustls_slice_bytes<'a>, - pub intermediate_certs_der: &'a rustls_slice_slice_bytes<'a>, - pub server_name: rustls_str<'a>, - pub ocsp_response: rustls_slice_bytes<'a>, -} - -/// User-provided input to a custom certificate verifier callback. -/// -/// See `rustls_client_config_builder_dangerous_set_certificate_verifier()`. -#[allow(non_camel_case_types)] -pub type rustls_verify_server_cert_user_data = *mut libc::c_void; - -// According to the nomicon https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization): -// > Option c_int> is a correct way to represent a -// > nullable function pointer using the C ABI (corresponding to the C type int (*)(int)). -// So we use Option<...> here. This is the type that is passed from C code. -#[allow(non_camel_case_types)] -pub type rustls_verify_server_cert_callback = Option< - unsafe extern "C" fn( - userdata: rustls_verify_server_cert_user_data, - params: *const rustls_verify_server_cert_params, - ) -> u32, ->; - -// This is the same as a rustls_verify_server_cert_callback after unwrapping -// the Option (which is equivalent to checking for null). -type VerifyCallback = unsafe extern "C" fn( - userdata: rustls_verify_server_cert_user_data, - params: *const rustls_verify_server_cert_params, -) -> u32; - -// An implementation of rustls::ServerCertVerifier based on a C callback. -struct Verifier { - provider: Arc, - callback: VerifyCallback, -} - -/// Safety: Verifier is Send because we don't allocate or deallocate any of its -/// fields. -unsafe impl Send for Verifier {} - -/// Safety: Verifier is Sync if the C code that passes us a callback that -/// obeys the concurrency safety requirements documented in -/// rustls_client_config_builder_dangerous_set_certificate_verifier. -unsafe impl Sync for Verifier {} - -impl ServerCertVerifier for Verifier { - fn verify_server_cert( - &self, - end_entity: &CertificateDer, - intermediates: &[CertificateDer], - server_name: &pki_types::ServerName<'_>, - ocsp_response: &[u8], - _now: UnixTime, - ) -> Result { - let cb = self.callback; - let server_name = server_name.to_str(); - let server_name = match server_name.as_ref().try_into() { - Ok(r) => r, - Err(NulByte {}) => return Err(Error::General("NUL byte in SNI".to_string())), - }; - - let intermediates: Vec<_> = intermediates.iter().map(|cert| cert.as_ref()).collect(); - - let intermediates = rustls_slice_slice_bytes { - inner: &intermediates, - }; - - let params = rustls_verify_server_cert_params { - end_entity_cert_der: end_entity.as_ref().into(), - intermediate_certs_der: &intermediates, - server_name, - ocsp_response: ocsp_response.into(), - }; - let userdata = userdata_get() - .map_err(|_| Error::General("internal error with thread-local storage".to_string()))?; - let result = unsafe { cb(userdata, ¶ms) }; - match rustls_result::from(result) { - rustls_result::Ok => Ok(ServerCertVerified::assertion()), - r => Err(error::cert_result_to_error(r)), - } - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &CertificateDer, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls12_signature( - message, - cert, - dss, - &self.provider.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &CertificateDer, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls13_signature( - message, - cert, - dss, - &self.provider.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.provider - .signature_verification_algorithms - .supported_schemes() - } -} - -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 using the builder crypto provider. /// Returns rustls_result::NoDefaultCryptoProvider if no process default crypto /// provider has been set, and the builder was not constructed with an explicit @@ -501,34 +366,7 @@ impl rustls_client_config_builder { rustls_result::Ok } } -} - -/// Always send the same client certificate. -#[derive(Debug)] -struct ResolvesClientCertFromChoices { - keys: Vec>, -} - -impl ResolvesClientCert for ResolvesClientCertFromChoices { - fn resolve( - &self, - _acceptable_issuers: &[&[u8]], - sig_schemes: &[SignatureScheme], - ) -> Option> { - for key in self.keys.iter() { - if key.key.choose_scheme(sig_schemes).is_some() { - return Some(key.clone()); - } - } - None - } - - fn has_certs(&self) -> bool { - !self.keys.is_empty() - } -} -impl rustls_client_config_builder { /// Turn a *rustls_client_config_builder (mutable) into a const *rustls_client_config /// (read-only). #[no_mangle] @@ -596,6 +434,164 @@ impl rustls_client_config_builder { } } +/// Input to a custom certificate verifier callback. +/// +/// See `rustls_client_config_builder_dangerous_set_certificate_verifier()`. +/// +/// server_name can contain a hostname, an IPv4 address in textual form, or an +/// IPv6 address in textual form. +#[allow(non_camel_case_types)] +#[repr(C)] +pub struct rustls_verify_server_cert_params<'a> { + pub end_entity_cert_der: rustls_slice_bytes<'a>, + pub intermediate_certs_der: &'a rustls_slice_slice_bytes<'a>, + pub server_name: rustls_str<'a>, + pub ocsp_response: rustls_slice_bytes<'a>, +} + +/// User-provided input to a custom certificate verifier callback. +/// +/// See `rustls_client_config_builder_dangerous_set_certificate_verifier()`. +#[allow(non_camel_case_types)] +pub type rustls_verify_server_cert_user_data = *mut libc::c_void; + +// According to the nomicon https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization): +// > Option c_int> is a correct way to represent a +// > nullable function pointer using the C ABI (corresponding to the C type int (*)(int)). +// So we use Option<...> here. This is the type that is passed from C code. +#[allow(non_camel_case_types)] +pub type rustls_verify_server_cert_callback = Option< + unsafe extern "C" fn( + userdata: rustls_verify_server_cert_user_data, + params: *const rustls_verify_server_cert_params, + ) -> u32, +>; + +// This is the same as a rustls_verify_server_cert_callback after unwrapping +// the Option (which is equivalent to checking for null). +type VerifyCallback = unsafe extern "C" fn( + userdata: rustls_verify_server_cert_user_data, + params: *const rustls_verify_server_cert_params, +) -> u32; + +// An implementation of rustls::ServerCertVerifier based on a C callback. +struct Verifier { + provider: Arc, + callback: VerifyCallback, +} + +/// Safety: Verifier is Send because we don't allocate or deallocate any of its +/// fields. +unsafe impl Send for Verifier {} + +/// Safety: Verifier is Sync if the C code that passes us a callback that +/// obeys the concurrency safety requirements documented in +/// rustls_client_config_builder_dangerous_set_certificate_verifier. +unsafe impl Sync for Verifier {} + +impl ServerCertVerifier for Verifier { + fn verify_server_cert( + &self, + end_entity: &CertificateDer, + intermediates: &[CertificateDer], + server_name: &pki_types::ServerName<'_>, + ocsp_response: &[u8], + _now: UnixTime, + ) -> Result { + let cb = self.callback; + let server_name = server_name.to_str(); + let server_name = match server_name.as_ref().try_into() { + Ok(r) => r, + Err(NulByte {}) => return Err(Error::General("NUL byte in SNI".to_string())), + }; + + let intermediates: Vec<_> = intermediates.iter().map(|cert| cert.as_ref()).collect(); + + let intermediates = rustls_slice_slice_bytes { + inner: &intermediates, + }; + + let params = rustls_verify_server_cert_params { + end_entity_cert_der: end_entity.as_ref().into(), + intermediate_certs_der: &intermediates, + server_name, + ocsp_response: ocsp_response.into(), + }; + let userdata = userdata_get() + .map_err(|_| Error::General("internal error with thread-local storage".to_string()))?; + let result = unsafe { cb(userdata, ¶ms) }; + match rustls_result::from(result) { + rustls_result::Ok => Ok(ServerCertVerified::assertion()), + r => Err(error::cert_result_to_error(r)), + } + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls12_signature( + message, + cert, + dss, + &self.provider.signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature( + message, + cert, + dss, + &self.provider.signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + self.provider + .signature_verification_algorithms + .supported_schemes() + } +} + +impl Debug for Verifier { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Verifier").finish() + } +} + +/// Always send the same client certificate. +#[derive(Debug)] +struct ResolvesClientCertFromChoices { + keys: Vec>, +} + +impl ResolvesClientCert for ResolvesClientCertFromChoices { + fn resolve( + &self, + _acceptable_issuers: &[&[u8]], + sig_schemes: &[SignatureScheme], + ) -> Option> { + for key in self.keys.iter() { + if key.key.choose_scheme(sig_schemes).is_some() { + return Some(key.clone()); + } + } + None + } + + fn has_certs(&self) -> bool { + !self.keys.is_empty() + } +} + impl rustls_client_config { /// Returns true if a `rustls_connection` created from the `rustls_client_config` will /// operate in FIPS mode.