rust: bssl-tls: Introduce custom certificate verification Bug: 479599893 Signed-off-by: Xiangfei Ding <xfding@google.com> Change-Id: Iedb81134808f540f7f2d34c1c20927416a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/91087 Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Adam Langley <agl@google.com>
diff --git a/rust/bssl-tls/src/connection.rs b/rust/bssl-tls/src/connection.rs index 0dbb7c9..e7a26ae 100644 --- a/rust/bssl-tls/src/connection.rs +++ b/rust/bssl-tls/src/connection.rs
@@ -107,6 +107,14 @@ } } + fn get_connection_methods(&mut self) -> &mut methods::RustConnectionMethods<M> { + unsafe { + // Safety: the validity of the handle `self.0` is witnessed by + // `self`. + get_connection_methods(self.ptr()) + } + } + /// Disable session creation. pub fn disable_session(&mut self) -> &mut Self { let ptr = self.ptr(); @@ -211,8 +219,7 @@ } else { *waker_data = Some(waker.clone()); } - let methods = self.get_connection_methods(); - methods.set_waker(waker); + self.get_connection_methods().set_waker(waker); } }
diff --git a/rust/bssl-tls/src/connection/credentials.rs b/rust/bssl-tls/src/connection/credentials.rs index a612bbc..08baed7 100644 --- a/rust/bssl-tls/src/connection/credentials.rs +++ b/rust/bssl-tls/src/connection/credentials.rs
@@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use alloc::ffi::CString; +use alloc::{ + boxed::Box, + ffi::CString, // +}; use core::{ ffi::CStr, ptr::null, // @@ -37,14 +40,19 @@ credentials::{ CertificateVerificationMode, SignatureAlgorithm, - TlsCredential, // + TlsCredential, + VerifyCertificate, + cert_cb, // }, errors::Error, ffi::slice_into_ffi_raw_parts, has_duplicates, // }; -impl<R, M> TlsConnectionBuilder<R, M> { +impl<R, M> TlsConnectionBuilder<R, M> +where + M: HasTlsConnectionMethod, +{ /// Configure the certificate verification mode. pub fn with_certificate_verification_mode( &mut self, @@ -52,10 +60,50 @@ ) -> &mut Self { let ctx = self.ptr(); unsafe { - // Safety: `ctx` is still valid here, `mode` has a correct value by construction and - // `NULL` is a valid callback handle. + // Safety: this method only updates the mode value. + bssl_sys::SSL_set_verify(ctx, mode as _, None); + } + self + } + + /// Configure the certificate verifier. + /// + /// See [`VerifyCertificate`] for how to implement a custom verifier. + /// + /// If raw public key authentication, per [RFC 7250], is configured, + /// the authentication through this mechanism will **fail** unless a certificate verifier + /// is configured. + /// + /// [RFC 7250]: <https://datatracker.ietf.org/doc/html/rfc7250> + pub fn with_certificate_verifier<V>( + &mut self, + mode: CertificateVerificationMode, + verifier: V, + ) -> &mut Self + where + V: VerifyCertificate + 'static, + { + let ctx = self.ptr(); + unsafe { + // Safety: we only install our own vtable. + bssl_sys::SSL_set_custom_verify( + ctx, + mode as _, + Some(cert_cb::<super::methods::RustConnectionMethods<M>>), + ); + } + self.get_connection_methods().verify_certificate_methods = Some(Box::new(verifier) as _); + self + } + + /// Remove custom certificate verifier. + pub fn without_certificate_verifier(&mut self, mode: CertificateVerificationMode) -> &mut Self { + let ctx = self.ptr(); + unsafe { + // Safety: we only uninstall the vtable. bssl_sys::SSL_set_custom_verify(ctx, mode as _, None); } + self.get_connection_methods().verify_certificate_methods = None; self } }
diff --git a/rust/bssl-tls/src/connection/lifecycle.rs b/rust/bssl-tls/src/connection/lifecycle.rs index 71e9e48..032cec2 100644 --- a/rust/bssl-tls/src/connection/lifecycle.rs +++ b/rust/bssl-tls/src/connection/lifecycle.rs
@@ -165,8 +165,7 @@ impl<R> TlsConnection<R, TlsMode> { /// Inspect if the connection is suspended for which reason, after invocation of I/O methods. pub fn take_pending_reason(&mut self) -> Option<TlsRetryReason> { - let methods = self.get_connection_methods(); - methods.take_pending_reason() + self.get_connection_methods().take_pending_reason() } }
diff --git a/rust/bssl-tls/src/connection/methods.rs b/rust/bssl-tls/src/connection/methods.rs index 7a05231..0d1745d 100644 --- a/rust/bssl-tls/src/connection/methods.rs +++ b/rust/bssl-tls/src/connection/methods.rs
@@ -31,12 +31,17 @@ use crate::{ Methods, + VerifyCertificateMethods, abort_on_panic, context::{ DtlsMode, QuicMode, TlsMode, // }, + credentials::{ + VerifyCertificate, + VerifyCertificateTask, // + }, errors::TlsRetryReason, io::RustBioHandle, methods::drop_box_rust_methods, // @@ -46,6 +51,8 @@ pub(super) struct RustConnectionMethods<Mode> { /// A handle to a `BIO` managed by this crate. pub bio: Option<RustBioHandle>, + /// Certificate verifier handle. + pub verify_certificate_methods: Option<Box<dyn VerifyCertificate>>, /// A mailbox to propagate IO retrying reasons. pub pending_reason: Option<TlsRetryReason>, _p: PhantomData<fn() -> Mode>, @@ -55,6 +62,7 @@ pub fn new() -> Self { Self { bio: None, + verify_certificate_methods: None, pending_reason: None, _p: PhantomData, } @@ -91,6 +99,12 @@ } } +impl<Mode: HasTlsConnectionMethod> VerifyCertificateMethods for RustConnectionMethods<Mode> { + fn verify_certificate_methods(&self) -> Option<&dyn VerifyCertificate> { + self.verify_certificate_methods.as_deref() + } +} + // NOTE(@xfding): the reason that we are not using the `register_ex_data` macro is because // declarative macros today cannot handle generics well enough. fn register_tls_connection_vtable<Mode: HasTlsConnectionMethod>() -> c_int { @@ -114,7 +128,6 @@ /// Safety: /// - `ssl` must be a `SSL` object constructed by [`crate::connection::TlsConnection`]. /// - `ssl` must be exclusively owned. -#[allow(unused)] // This will be used in the following patch to support async I/O. pub(crate) unsafe fn waker_data_from_ssl(ssl: NonNull<bssl_sys::SSL>) -> Option<Waker> { unsafe { // Safety: `ssl` outlives `'a` and is constructed by `TlsConnection`. @@ -135,6 +148,18 @@ } } +/// Safety: +/// - `ssl` must be constructed from `TlsConnection` and outlived by `'a`. +/// - `ssl` must be exclusively owned. +pub(crate) unsafe fn verify_cert_task_from_ssl<'a>( + ssl: NonNull<bssl_sys::SSL>, +) -> &'a mut Option<Box<dyn VerifyCertificateTask>> { + unsafe { + // Safety: `ssl` outlives `'a` and is constructed by `TlsConnection`. + <ExDataRegistration as ExData<Option<Box<dyn VerifyCertificateTask>>>>::get_mut(ssl) + } +} + pub(crate) struct ExDataRegistration; pub(crate) trait ExData<T: Default> { @@ -239,6 +264,7 @@ } register_ex_data!(Option<Waker>); +register_ex_data!(Option<Box<dyn VerifyCertificateTask>>); pub(crate) trait HasTlsConnectionMethod { fn registration() -> c_int;
diff --git a/rust/bssl-tls/src/connection/transport.rs b/rust/bssl-tls/src/connection/transport.rs index 71abcb5..67bc9ca 100644 --- a/rust/bssl-tls/src/connection/transport.rs +++ b/rust/bssl-tls/src/connection/transport.rs
@@ -52,8 +52,7 @@ // Safety: the `bio` pointer has been sanitised and `self.0` is still valid. bssl_sys::SSL_set_bio(self.ptr(), bio.ptr(), bio.ptr()); } - let methods = self.get_connection_methods(); - methods.bio = Some(bio); + self.get_connection_methods().bio = Some(bio); Ok(self) } @@ -74,8 +73,7 @@ // Safety: the `bio` pointer has been sanitised and `self.0` is still valid. bssl_sys::SSL_set_bio(self.ptr(), bio.ptr(), bio.ptr()); } - let methods = self.get_connection_methods(); - methods.bio = Some(bio); + self.get_connection_methods().bio = Some(bio); Ok(self) }
diff --git a/rust/bssl-tls/src/context.rs b/rust/bssl-tls/src/context.rs index f75ec37..0ddc865 100644 --- a/rust/bssl-tls/src/context.rs +++ b/rust/bssl-tls/src/context.rs
@@ -153,7 +153,6 @@ } } - #[allow(unused)] fn get_context_methods(&mut self) -> &mut methods::RustContextMethods<M> { let methods = unsafe { // Safety: the validity of the handle `self.0` is witnessed by `self`.
diff --git a/rust/bssl-tls/src/context/credentials.rs b/rust/bssl-tls/src/context/credentials.rs index 073dd59..6c27400 100644 --- a/rust/bssl-tls/src/context/credentials.rs +++ b/rust/bssl-tls/src/context/credentials.rs
@@ -30,7 +30,9 @@ credentials::{ CertificateVerificationMode, SignatureAlgorithm, - TlsCredential, // + TlsCredential, + VerifyCertificate, + cert_cb, // }, errors::Error, ffi::slice_into_ffi_raw_parts, @@ -79,9 +81,54 @@ ) -> &mut Self { let conn = self.ptr(); unsafe { + // Safety: we only install our own vtable. + bssl_sys::SSL_CTX_set_verify(conn, mode as _, None); + } + self + } + + /// Set certificate verification mode and custom verifier. + /// + /// # Setting custom certificate verifier + /// + /// See [`VerifyCertificate`] for how to implement a custom verifier. + /// + /// # Client certificate verification for servers, mutual TLS + /// + /// Server can choose to request a certificate from the client by setting `mode` to + /// - [`CertificateVerificationMode::PeerCertRequested`] which may still let handshake complete + /// if the certificate request by the server is not fulfilled. + /// - [`CertificateVerificationMode::PeerCertMandatory`] which will abort handshake if + /// the request is not fulfilled. + pub fn with_certificate_verifier<V>( + &mut self, + mode: CertificateVerificationMode, + verifier: V, + ) -> &mut Self + where + V: VerifyCertificate + 'static, + { + let conn = self.ptr(); + unsafe { + // Safety: we only install our own vtable. + bssl_sys::SSL_CTX_set_custom_verify( + conn, + mode as _, + Some(cert_cb::<super::methods::RustContextMethods<M>>), + ); + } + self.get_context_methods().verify_certificate_methods = Some(Box::new(verifier) as _); + self + } + + /// Remove custom certificate verifier. + pub fn without_certificate_verifier(&mut self, mode: CertificateVerificationMode) -> &mut Self { + let conn = self.ptr(); + unsafe { // Safety: we only uninstall the vtable. bssl_sys::SSL_CTX_set_custom_verify(conn, mode as _, None); } + self.get_context_methods().verify_certificate_methods = None; self }
diff --git a/rust/bssl-tls/src/context/methods.rs b/rust/bssl-tls/src/context/methods.rs index ae2d319..e701810 100644 --- a/rust/bssl-tls/src/context/methods.rs +++ b/rust/bssl-tls/src/context/methods.rs
@@ -22,21 +22,29 @@ use crate::{ Methods, + VerifyCertificateMethods, context::{ DtlsMode, QuicMode, TlsMode, // }, + credentials::VerifyCertificate, methods::drop_box_rust_methods, // }; -pub(crate) struct RustContextMethods<M>(PhantomData<fn() -> M>); +pub(crate) struct RustContextMethods<M> { + pub(crate) verify_certificate_methods: Option<Box<dyn VerifyCertificate>>, + _p: PhantomData<fn() -> M>, +} // NOTE(@xfding): the reason we do not use `register_ex_data` for this type is because we need to // look up the associated SSL_CTX first. impl<M> RustContextMethods<M> { pub fn new() -> Self { - Self(PhantomData) + Self { + verify_certificate_methods: None, + _p: PhantomData, + } } } @@ -56,6 +64,12 @@ } } +impl<M: HasTlsContextMethod> VerifyCertificateMethods for RustContextMethods<M> { + fn verify_certificate_methods(&self) -> Option<&dyn VerifyCertificate> { + self.verify_certificate_methods.as_deref() + } +} + fn register_tls_context_vtable<M: HasTlsContextMethod>() -> c_int { unsafe { // Safety: this a one-time registration uses only valid function pointers.
diff --git a/rust/bssl-tls/src/credentials.rs b/rust/bssl-tls/src/credentials.rs index 5f2e860..b7610ef 100644 --- a/rust/bssl-tls/src/credentials.rs +++ b/rust/bssl-tls/src/credentials.rs
@@ -25,11 +25,18 @@ c_int, // }, fmt::Debug, + future::Future, + iter::FusedIterator, marker::PhantomData, mem::forget, + pin::Pin, ptr::{ NonNull, null_mut, // + }, + task::{ + Context, + Poll, // }, // }; @@ -39,8 +46,13 @@ }; use crate::{ + VerifyCertificateMethods, + abort_on_panic, + alerts::AlertDescription, + call_slice_getter, check_lib_error, config::ConfigurationError, + connection::methods::{verify_cert_task_from_ssl, waker_data_from_ssl}, context::CertificateCache, crypto_buffer_wrapper, errors::{ @@ -517,6 +529,307 @@ } } +// NOTE: this context does not own the connection. +/// A verification context handle. +#[repr(transparent)] +pub struct VerifyCertificateContext(NonNull<bssl_sys::SSL>); + +impl VerifyCertificateContext { + fn ptr(&self) -> *mut bssl_sys::SSL { + self.0.as_ptr() + } + + /// Get Encrypted `ClientHello` name override, specifically a DNS name per + /// [RFC 5280], which a character set stipulated by [RFC 1034] §3.5. + /// + /// The returned name should be interpreted first as an opaque byte string. + /// + /// # Interaction with custom certificate verification + /// + /// If the return value is [`Some`], the end-entity certificate must be + /// verified against the name reported by this call. + /// + /// [RFC 5280]: <https://datatracker.ietf.org/doc/html/rfc5280> + /// [RFC 1034]: <https://datatracker.ietf.org/doc/html/rfc1034#section-3.5> + pub fn get_ech_name_override(&self) -> Option<&str> { + let name: &[u8] = unsafe { + // Safety: + // - `self` outlives the slice. + // - transmuting i8 to u8 preserve the character value. + core::mem::transmute(call_slice_getter!( + bssl_sys::SSL_get0_ech_name_override, + self.ptr() + )?) + }; + if name.is_empty() || !name.is_ascii() { + return None; + } + // A DNS name has to be an IA5String, specifically ASCII first. + str::from_utf8(name).ok() + } + + /// Get the stapled OCSP response, if any. + /// + /// The response may not be a valid OCSPResponse from the server as per + /// [RFC 2560]. + /// + /// [RFC 2560]: <https://datatracker.ietf.org/doc/html/rfc6960> + pub fn get_ocsp_response(&self) -> Option<&[u8]> { + // Safety: response, when it exists, is outlived by the connection. + let response = call_slice_getter!(bssl_sys::SSL_get0_ocsp_response, self.ptr())?; + (!response.is_empty()).then_some(response) + } + + /// Get the Signed Certificate Timestamp list, if any, as per [RFC 6962] §3.2. + /// + /// [RFC 6962]: <https://datatracker.ietf.org/doc/html/rfc6962#section-3.2> + pub fn get_signed_cert_timestamp_list(&self) -> Option<&[u8]> { + // Safety: list, when it exists, is outlived by the connection. + let list = call_slice_getter!(bssl_sys::SSL_get0_signed_cert_timestamp_list, self.ptr())?; + (!list.is_empty()).then_some(list) + } +} + +/// Custom certificate verification callback. +/// +/// It is recommended to avoid panicking in the trait implementation. +/// A panic in this callback will lead to abort. +pub trait VerifyCertificate: Send + Sync { + /// Decide whether a certificate chain is acceptable. + /// + /// The peer certificate chain is supplied in `certs`, in which the first certificate is + /// the End Entity certificate, if any. + /// + /// This method may be called more than once if the verification is asynchronous. + /// To signal suspension, this method should return [`VerifyResult::Pending`]. + fn verify<'a>( + &self, + ctx: &'a VerifyCertificateContext, + certs: CertificateChainIterator<'a>, + ) -> Box<dyn VerifyCertificateTask>; +} + +/// An outstanding certificate verification task. +pub trait VerifyCertificateTask: Send { + /// Try to complete the verification task. + fn complete(&mut self, async_ctx: Option<&mut Context<'_>>) -> VerifyResult; +} + +/// Custom certificate verification result. +pub enum VerifyResult { + /// The certificate chain is accepted. + Accept, + /// The certification chain is pending asynchronous result. + Pending, + /// The certificate chain is rejected possibly with an alert. + Reject(Option<AlertDescription>), +} + +/// Asynchronous custom certificate verification. +/// +/// This is the `async` analogue of [`VerifyCertificate`]. +pub trait AsyncVerifyCertificate: Send + Sync + Unpin { + /// The future type of the verification process. + type VerifyFuture: 'static + Unpin + Send + Sync + Future<Output = bool>; + + /// Decide whether a certificate chain is acceptable. + fn verify( + &self, + ctx: &VerifyCertificateContext, + certs: CertificateChainIterator<'_>, + ) -> Self::VerifyFuture; +} + +/// Adapter to run certificate verification asynchronously. +pub struct AsyncVerifyCertificateAdapter<T>(pub T); + +impl<T, Fut> VerifyCertificate for AsyncVerifyCertificateAdapter<T> +where + T: AsyncVerifyCertificate<VerifyFuture = Fut>, + Fut: 'static + Unpin + Send + Sync + Future<Output = bool>, +{ + fn verify<'a>( + &self, + ctx: &'a VerifyCertificateContext, + certs: CertificateChainIterator<'a>, + ) -> Box<dyn VerifyCertificateTask> { + Box::new(AsyncVerifyCertificateTask(self.0.verify(ctx, certs))) + } +} + +struct AsyncVerifyCertificateTask<Fut>(Fut); + +impl<Fut> VerifyCertificateTask for AsyncVerifyCertificateTask<Fut> +where + Fut: 'static + Unpin + Send + Sync + Future<Output = bool>, +{ + fn complete(&mut self, async_ctx: Option<&mut Context<'_>>) -> VerifyResult { + let Some(cx) = async_ctx else { + return VerifyResult::Reject(Some(AlertDescription::InternalError)); + }; + let outstanding_task = Pin::new(&mut self.0); + match outstanding_task.poll(cx) { + Poll::Ready(accept) => { + if accept { + VerifyResult::Accept + } else { + VerifyResult::Reject(Some(AlertDescription::BadCertificate)) + } + } + Poll::Pending => VerifyResult::Pending, + } + } +} + +/// Certificate chain iterator. +/// +/// This iterator will supply the peer leaf certificate as the first element in the chain, if any. +#[derive(Clone, Copy)] +pub struct CertificateChainIterator<'a> { + certs: *const bssl_sys::stack_st_CRYPTO_BUFFER, + len: usize, + curr: usize, + _p: PhantomData<&'a ()>, +} + +impl<'a> CertificateChainIterator<'a> { + /// Safety: caller must ensure that `certs` is outlived by, + /// or in other words stays alive as long as, `'a`. + pub(crate) unsafe fn new(certs: *const bssl_sys::stack_st_CRYPTO_BUFFER) -> Self { + let len = if certs.is_null() { + 0 + } else { + unsafe { + // Safety: `certs` is valid now. + bssl_sys::sk_CRYPTO_BUFFER_num(certs) + } + }; + Self { + certs, + len, + curr: 0, + _p: PhantomData, + } + } +} + +impl<'a> Iterator for CertificateChainIterator<'a> { + type Item = Certificate; + + fn next(&mut self) -> Option<Self::Item> { + if self.curr >= self.len { + return None; + } + let cert = unsafe { + // Safety: `self.certs` is still valid now and `self.curr` is within the bound. + bssl_sys::sk_CRYPTO_BUFFER_value(self.certs, self.curr) + }; + self.curr += 1; + let Some(cert) = NonNull::new(cert) else { + // Fuse the iterator. + self.curr = self.len; + return None; + }; + unsafe { + // Safety: `cert` is valid here. + bssl_sys::CRYPTO_BUFFER_up_ref(cert.as_ptr()); + } + Some(Certificate(cert)) + } +} + +impl ExactSizeIterator for CertificateChainIterator<'_> { + fn len(&self) -> usize { + self.len + } +} + +impl FusedIterator for CertificateChainIterator<'_> {} + +/// Safety: this callback stub must be installed with a context object allocated +/// as a `Box<dyn VerifyCertificate>`. +pub(crate) unsafe extern "C" fn cert_cb<M: VerifyCertificateMethods>( + ssl: *mut bssl_sys::SSL, + alert: *mut u8, +) -> bssl_sys::ssl_verify_result_t { + let Some(ssl) = NonNull::new(ssl) else { + return bssl_sys::ssl_verify_result_t_ssl_verify_invalid; + }; + let Some(methods) = (unsafe { + // Safety: `ssl` outlives `methods` + M::from_ssl(ssl.as_ptr()) + }) else { + return bssl_sys::ssl_verify_result_t_ssl_verify_invalid; + }; + let waker = unsafe { + // Safety: + // - this callback must be installed by `TlsContextBuilder` or `TlsConnection`, + // so the associated data must have been set up correctly. + // - the caller of this callback must own the connection exclusively. + waker_data_from_ssl(ssl) + }; + let mut context = waker.as_ref().map(Context::from_waker); + let Some(verify) = methods.verify_certificate_methods() else { + return bssl_sys::ssl_verify_result_t_ssl_verify_invalid; + }; + let cert_chain = unsafe { + // Safety: `ssl` is still alive in handshake mode and will outlive `cert_chain`. + bssl_sys::SSL_get0_peer_certificates(ssl.as_ptr()) + }; + let certs = unsafe { + // Safety: `cert_chain` is outlived by `ssl` whose lifetime is annotated as `'a`. + CertificateChainIterator::new(cert_chain) + }; + let ctx = &VerifyCertificateContext(ssl); + let async_ctx = context.as_mut(); + + let outstanding_task = unsafe { + // Safety: `ssl` outlives the in-flight task and exclusively owned by the caller of + // this callback. + verify_cert_task_from_ssl(ssl) + }; + + abort_on_panic(move || { + if let Some(task) = outstanding_task { + match task.complete(async_ctx) { + VerifyResult::Pending => bssl_sys::ssl_verify_result_t_ssl_verify_retry, + VerifyResult::Accept => { + let _ = outstanding_task.take(); + bssl_sys::ssl_verify_result_t_ssl_verify_ok + } + VerifyResult::Reject(ad) => { + let _ = outstanding_task.take(); + if let Some(ad) = ad { + unsafe { + // Safety: `alert` is valid per BoringSSL invariants. + alert.write(ad as _); + } + } + bssl_sys::ssl_verify_result_t_ssl_verify_invalid + } + } + } else { + let mut task = verify.verify(ctx, certs); + match task.complete(async_ctx) { + VerifyResult::Pending => { + *outstanding_task = Some(task); + bssl_sys::ssl_verify_result_t_ssl_verify_retry + } + VerifyResult::Accept => bssl_sys::ssl_verify_result_t_ssl_verify_ok, + VerifyResult::Reject(ad) => { + if let Some(ad) = ad { + unsafe { + // Safety: `alert` is valid per BoringSSL invariants. + alert.write(ad as _); + } + } + bssl_sys::ssl_verify_result_t_ssl_verify_invalid + } + } + } + }) +} + bssl_macros::bssl_enum! { /// Certificate verification mode pub enum CertificateVerificationMode: i8 {
diff --git a/rust/bssl-tls/src/lib.rs b/rust/bssl-tls/src/lib.rs index e993416..88dd1ef 100644 --- a/rust/bssl-tls/src/lib.rs +++ b/rust/bssl-tls/src/lib.rs
@@ -59,13 +59,16 @@ list.iter().any(|elem| !seen.insert(elem)) } -#[allow(unused)] pub(crate) trait Methods { /// Safety: `ssl` must outlive `'a` and it must be passed in from BoringSSL /// through vtable calls. unsafe extern "C" fn from_ssl<'a>(ssl: *mut bssl_sys::SSL) -> Option<&'a Self>; } +pub(crate) trait VerifyCertificateMethods: Methods { + fn verify_certificate_methods(&self) -> Option<&dyn credentials::VerifyCertificate>; +} + #[inline] fn abort_on_panic<T>(work: impl FnOnce() -> T) -> T { let assert_unwind_safe = AssertUnwindSafe(work);
diff --git a/rust/bssl-tls/src/macros.rs b/rust/bssl-tls/src/macros.rs index eea95e3..eac9622 100644 --- a/rust/bssl-tls/src/macros.rs +++ b/rust/bssl-tls/src/macros.rs
@@ -132,3 +132,23 @@ } }; } + +// Safety: `$obj` must outlive the returned slice. +#[doc(hidden)] +#[macro_export] +macro_rules! call_slice_getter { + ($fn:path, $obj:expr) => {{ + let mut data = ::core::ptr::null(); + let mut len = 0; + #[allow(unused_unsafe)] + unsafe { + // Safety: `obj`, `data` and `len` are all valid. + $fn($obj, &raw mut data, &raw mut len); + } + #[allow(unused_unsafe)] + unsafe { + // Safety: data and len are returned by BoringSSL and are valid. + $crate::ffi::sanitize_slice(data, len) + } + }}; +}
diff --git a/rust/bssl-tls/src/sessions.rs b/rust/bssl-tls/src/sessions.rs index 5a6676c..84cb7cc 100644 --- a/rust/bssl-tls/src/sessions.rs +++ b/rust/bssl-tls/src/sessions.rs
@@ -17,11 +17,11 @@ use alloc::vec::Vec; use core::ptr::{ NonNull, - null, null_mut, // }; use crate::{ + call_slice_getter, config::ProtocolVersion, context::TlsContext, errors::Error, @@ -78,21 +78,6 @@ } } -macro_rules! call_slice_getter { - ($fn:path, $obj:expr) => {{ - let mut data = null(); - let mut len = 0; - unsafe { - // Safety: `obj`, `data` and `len` are all valid. - $fn($obj, &raw mut data, &raw mut len); - } - unsafe { - // Safety: data and len are returned by BoringSSL and are valid. - sanitize_slice(data, len) - } - }}; -} - impl TlsSession { pub(crate) fn ptr(&self) -> *mut bssl_sys::SSL_SESSION { self.0.as_ptr()