Skip to content

Commit

Permalink
Introduce set_custom_verify_callback and set_async_custom_verify_call…
Browse files Browse the repository at this point in the history
…back
  • Loading branch information
nox committed Dec 14, 2023
1 parent af0c36a commit 2e4f8fb
Show file tree
Hide file tree
Showing 9 changed files with 791 additions and 217 deletions.
65 changes: 64 additions & 1 deletion boring/src/ssl/async_callbacks.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::mut_only::MutOnly;
use super::{
ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError, SelectCertError,
Ssl, SslContextBuilder, SslRef, SslSession, SslSignatureAlgorithm,
Ssl, SslAlert, SslContextBuilder, SslRef, SslSession, SslSignatureAlgorithm, SslVerifyError,
SslVerifyMode,
};
use crate::ex_data::Index;
use once_cell::sync::Lazy;
Expand Down Expand Up @@ -30,6 +31,12 @@ pub type BoxGetSessionFuture = ExDataFuture<Option<BoxGetSessionFinish>>;
/// The type of callbacks returned by [`BoxSelectCertFuture`] methods.
pub type BoxGetSessionFinish = Box<dyn FnOnce(&mut SslRef, &[u8]) -> Option<SslSession>>;

/// The type of futures to pass to [`SslContextBuilderExt::set_async_custom_verify_callback`].
pub type BoxCustomVerifyFuture = ExDataFuture<Result<BoxCustomVerifyFinish, SslAlert>>;

/// The type of callbacks returned by [`BoxCustomVerifyFuture`] methods.
pub type BoxCustomVerifyFinish = Box<dyn FnOnce(&mut SslRef) -> Result<(), SslAlert>>;

/// Convenience alias for futures stored in [`Ssl`] ex data by [`SslContextBuilderExt`] methods.
///
/// Public for documentation purposes.
Expand All @@ -45,6 +52,9 @@ pub(crate) static SELECT_PRIVATE_KEY_METHOD_FUTURE_INDEX: Lazy<
pub(crate) static SELECT_GET_SESSION_FUTURE_INDEX: Lazy<
Index<Ssl, MutOnly<Option<BoxGetSessionFuture>>>,
> = Lazy::new(|| Ssl::new_ex_index().unwrap());
pub(crate) static SELECT_CUSTOM_VERIFY_FUTURE_INDEX: Lazy<
Index<Ssl, MutOnly<Option<BoxCustomVerifyFuture>>>,
> = Lazy::new(|| Ssl::new_ex_index().unwrap());

impl SslContextBuilder {
/// Sets a callback that is called before most [`ClientHello`] processing
Expand Down Expand Up @@ -135,15 +145,68 @@ impl SslContextBuilder {

self.set_get_session_callback(async_callback)
}

/// Configures certificate verification.
///
/// The callback should return `Ok(())` if the certificate is valid.
/// If the certificate is invalid, the callback should return `SslVerifyError::Invalid(alert)`.
/// Some useful alerts include [`SslAlert::CERTIFICATE_EXPIRED`], [`SslAlert::CERTIFICATE_REVOKED`],
/// [`SslAlert::UNKNOWN_CA`], [`SslAlert::BAD_CERTIFICATE`], [`SslAlert::CERTIFICATE_UNKNOWN`],
/// and [`SslAlert::INTERNAL_ERROR`]. See RFC 5246 section 7.2.2 for their precise meanings.
///
/// A task waker must be set on `Ssl` values associated with the resulting
/// `SslContext` with [`SslRef::set_task_waker`].
///
/// See [`SslContextBuilder::set_custom_verify_callback`] for the sync version of this method.
///
/// # Panics
///
/// This method panics if this `Ssl` is associated with a RPK context.
pub fn set_async_custom_verify_callback<F>(&mut self, mode: SslVerifyMode, callback: F)
where
F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
{
self.set_custom_verify_callback(mode, async_custom_verify_callback(callback))
}
}

impl SslRef {
pub fn set_async_custom_verify_callback<F>(&mut self, mode: SslVerifyMode, callback: F)
where
F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
{
self.set_custom_verify_callback(mode, async_custom_verify_callback(callback))
}

/// Sets the task waker to be used in async callbacks installed on this `Ssl`.
pub fn set_task_waker(&mut self, waker: Option<Waker>) {
self.replace_ex_data(*TASK_WAKER_INDEX, waker);
}
}

fn async_custom_verify_callback<F>(
callback: F,
) -> impl Fn(&mut SslRef) -> Result<(), SslVerifyError>
where
F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
{
move |ssl| {
let fut_poll_result = with_ex_data_future(
&mut *ssl,
*SELECT_CUSTOM_VERIFY_FUTURE_INDEX,
|ssl| ssl,
&callback,
identity,
);

match fut_poll_result {
Poll::Ready(Err(alert)) => Err(SslVerifyError::Invalid(alert)),
Poll::Ready(Ok(finish)) => Ok(finish(ssl).map_err(SslVerifyError::Invalid)?),
Poll::Pending => Err(SslVerifyError::Retry),
}
}
}

/// A fatal error to be returned from async select certificate callbacks.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct AsyncSelectCertError;
Expand Down
62 changes: 61 additions & 1 deletion boring/src/ssl/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use super::{
AlpnError, ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError,
SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession,
SslSessionRef, SslSignatureAlgorithm, SESSION_CTX_INDEX,
SslSessionRef, SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX,
};
use crate::error::ErrorStack;
use crate::ffi;
Expand Down Expand Up @@ -42,6 +42,66 @@ where
verify(preverify_ok != 0, ctx) as c_int
}

pub(super) unsafe extern "C" fn raw_custom_verify<F>(
ssl: *mut ffi::SSL,
out_alert: *mut u8,
) -> ffi::ssl_verify_result_t
where
F: Fn(&mut SslRef) -> Result<(), SslVerifyError> + 'static + Sync + Send,
{
let callback = |ssl: &mut SslRef| {
let custom_verify_idx = SslContext::cached_ex_index::<F>();

let ssl_context = ssl.ssl_context().to_owned();
let callback = ssl_context
.ex_data(custom_verify_idx)
.expect("BUG: custom verify callback missing");

callback(ssl)
};

unsafe { raw_custom_verify_callback(ssl, out_alert, callback) }
}

pub(super) unsafe extern "C" fn ssl_raw_custom_verify<F>(
ssl: *mut ffi::SSL,
out_alert: *mut u8,
) -> ffi::ssl_verify_result_t
where
F: Fn(&mut SslRef) -> Result<(), SslVerifyError> + 'static + Sync + Send,
{
let callback = |ssl: &mut SslRef| {
let callback = ssl
.ex_data(Ssl::cached_ex_index::<Arc<F>>())
.expect("BUG: ssl verify callback missing")
.clone();

callback(ssl)
};

unsafe { raw_custom_verify_callback(ssl, out_alert, callback) }
}

unsafe fn raw_custom_verify_callback(
ssl: *mut ffi::SSL,
out_alert: *mut u8,
callback: impl FnOnce(&mut SslRef) -> Result<(), SslVerifyError>,
) -> ffi::ssl_verify_result_t {
// SAFETY: boring provides valid inputs.
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
let out_alert = unsafe { &mut *out_alert };

match callback(ssl) {
Ok(()) => ffi::ssl_verify_result_t::ssl_verify_ok,
Err(SslVerifyError::Invalid(alert)) => {
*out_alert = alert.0 as u8;

ffi::ssl_verify_result_t::ssl_verify_invalid
}
Err(SslVerifyError::Retry) => ffi::ssl_verify_result_t::ssl_verify_retry,
}
}

pub(super) unsafe extern "C" fn raw_client_psk<F>(
ssl_ptr: *mut ffi::SSL,
hint: *const c_char,
Expand Down
4 changes: 4 additions & 0 deletions boring/src/ssl/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ impl ErrorCode {

pub const PENDING_CERTIFICATE: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_CERTIFICATE);

pub const WANT_CERTIFICATE_VERIFY: ErrorCode =
ErrorCode(ffi::SSL_ERROR_WANT_CERTIFICATE_VERIFY);

pub const WANT_PRIVATE_KEY_OPERATION: ErrorCode =
ErrorCode(ffi::SSL_ERROR_WANT_PRIVATE_KEY_OPERATION);

Expand Down Expand Up @@ -101,6 +104,7 @@ impl Error {
| ErrorCode::PENDING_SESSION
| ErrorCode::PENDING_CERTIFICATE
| ErrorCode::WANT_PRIVATE_KEY_OPERATION
| ErrorCode::WANT_CERTIFICATE_VERIFY
| ErrorCode::PENDING_TICKET
)
}
Expand Down
Loading

0 comments on commit 2e4f8fb

Please sign in to comment.