Skip to content

Commit

Permalink
Expose SSL_CTX_set_info_callback (#266)
Browse files Browse the repository at this point in the history
Model callback arguments as structs
  • Loading branch information
evanrittenhouse authored Sep 11, 2024
1 parent 7324db2 commit b2525f2
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 2 deletions.
34 changes: 32 additions & 2 deletions boring/src/ssl/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

use super::{
AlpnError, ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError,
SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession,
SslSessionRef, SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX,
SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslInfoCallbackAlert,
SslInfoCallbackMode, SslInfoCallbackValue, SslRef, SslSession, SslSessionRef,
SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX,
};
use crate::error::ErrorStack;
use crate::ffi;
Expand Down Expand Up @@ -521,3 +522,32 @@ where
Err(err) => err.0,
}
}

pub(super) unsafe extern "C" fn raw_info_callback<F>(
ssl: *const ffi::SSL,
mode: c_int,
value: c_int,
) where
F: Fn(&SslRef, SslInfoCallbackMode, SslInfoCallbackValue) + Send + Sync + 'static,
{
// Due to FFI signature requirements we have to pass a *const SSL into this function, but
// foreign-types requires a *mut SSL to get the Rust SslRef
let mut_ref = ssl as *mut ffi::SSL;

// SAFETY: boring provides valid inputs.
let ssl = unsafe { SslRef::from_ptr(mut_ref) };
let ssl_context = ssl.ssl_context();

let callback = ssl_context
.ex_data(SslContext::cached_ex_index::<F>())
.expect("BUG: info callback missing");

let value = match mode {
ffi::SSL_CB_READ_ALERT | ffi::SSL_CB_WRITE_ALERT => {
SslInfoCallbackValue::Alert(SslInfoCallbackAlert(value))
}
_ => SslInfoCallbackValue::Unit,
};

callback(ssl, SslInfoCallbackMode(mode), value);
}
72 changes: 72 additions & 0 deletions boring/src/ssl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,66 @@ pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8]
}
}

/// Options controlling the behavior of the info callback.
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
pub struct SslInfoCallbackMode(i32);

impl SslInfoCallbackMode {
/// Signaled for each alert received, warning or fatal.
pub const READ_ALERT: Self = Self(ffi::SSL_CB_READ_ALERT);

/// Signaled for each alert sent, warning or fatal.
pub const WRITE_ALERT: Self = Self(ffi::SSL_CB_WRITE_ALERT);

/// Signaled when a handshake begins.
pub const HANDSHAKE_START: Self = Self(ffi::SSL_CB_HANDSHAKE_START);

/// Signaled when a handshake completes successfully.
pub const HANDSHAKE_DONE: Self = Self(ffi::SSL_CB_HANDSHAKE_DONE);

/// Signaled when a handshake progresses to a new state.
pub const ACCEPT_LOOP: Self = Self(ffi::SSL_CB_ACCEPT_LOOP);
}

/// The `value` argument to an info callback. The most-significant byte is the alert level, while
/// the least significant byte is the alert itself.
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
pub enum SslInfoCallbackValue {
/// The unit value (1). Some BoringSSL info callback modes, like ACCEPT_LOOP, always call the
/// callback with `value` set to the unit value. If the [`SslInfoCallbackValue`] is a
/// `Unit`, it can safely be disregarded.
Unit,
/// An alert. See [`SslInfoCallbackAlert`] for details on how to manipulate the alert. This
/// variant should only be present if the info callback was called with a `READ_ALERT` or
/// `WRITE_ALERT` mode.
Alert(SslInfoCallbackAlert),
}

#[derive(Hash, Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
pub struct SslInfoCallbackAlert(c_int);

impl SslInfoCallbackAlert {
/// The level of the SSL alert.
pub fn alert_level(&self) -> Ssl3AlertLevel {
let value = self.0 >> 8;
Ssl3AlertLevel(value)
}

/// The value of the SSL alert.
pub fn alert(&self) -> SslAlert {
let value = self.0 & (u8::MAX as i32);
SslAlert(value)
}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Ssl3AlertLevel(c_int);

impl Ssl3AlertLevel {
pub const WARNING: Ssl3AlertLevel = Self(ffi::SSL3_AL_WARNING);
pub const FATAL: Ssl3AlertLevel = Self(ffi::SSL3_AL_FATAL);
}

#[cfg(feature = "rpk")]
extern "C" fn rpk_verify_failure_callback(
_ssl: *mut ffi::SSL,
Expand Down Expand Up @@ -1818,6 +1878,18 @@ impl SslContextBuilder {
unsafe { cvt_0i(ffi::SSL_CTX_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) }
}

/// Sets the context's info callback.
#[corresponds(SSL_CTX_set_info_callback)]
pub fn set_info_callback<F>(&mut self, callback: F)
where
F: Fn(&SslRef, SslInfoCallbackMode, SslInfoCallbackValue) + Send + Sync + 'static,
{
unsafe {
self.replace_ex_data(SslContext::cached_ex_index::<F>(), callback);
ffi::SSL_CTX_set_info_callback(self.as_ptr(), Some(callbacks::raw_info_callback::<F>));
}
}

/// Consumes the builder, returning a new `SslContext`.
pub fn build(self) -> SslContext {
self.ctx
Expand Down
14 changes: 14 additions & 0 deletions boring/src/ssl/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1052,3 +1052,17 @@ fn drop_ex_data_in_ssl() {
assert_eq!(ssl.replace_ex_data(index, "camembert"), Some("comté"));
assert_eq!(ssl.replace_ex_data(index, "raclette"), Some("camembert"));
}

#[test]
fn test_info_callback() {
static CALLED_BACK: AtomicBool = AtomicBool::new(false);

let server = Server::builder().build();
let mut client = server.client();
client.ctx().set_info_callback(move |_, _, _| {
CALLED_BACK.store(true, Ordering::Relaxed);
});

client.connect();
assert!(CALLED_BACK.load(Ordering::Relaxed));
}

0 comments on commit b2525f2

Please sign in to comment.