Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce async callbacks #134

Merged
merged 12 commits into from
Oct 10, 2023
120 changes: 102 additions & 18 deletions boring/src/ssl/callbacks.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#![forbid(unsafe_op_in_unsafe_fn)]

use super::{
AlpnError, ClientHello, PrivateKeyMethod, PrivateKeyMethodError, SelectCertError, SniError,
Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, SslSessionRef,
SslSignatureAlgorithm, SESSION_CTX_INDEX,
};
use crate::error::ErrorStack;
use crate::ffi;
use crate::x509::{X509StoreContext, X509StoreContextRef};
use foreign_types::ForeignType;
use foreign_types::ForeignTypeRef;
use libc::c_char;
Expand All @@ -12,19 +19,7 @@ use std::slice;
use std::str;
use std::sync::Arc;

use crate::error::ErrorStack;
use crate::ssl::AlpnError;
use crate::ssl::{ClientHello, SelectCertError};
use crate::ssl::{
SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, SslSessionRef,
SESSION_CTX_INDEX,
};
use crate::x509::{X509StoreContext, X509StoreContextRef};

pub(super) unsafe extern "C" fn raw_verify<F>(
preverify_ok: c_int,
x509_ctx: *mut ffi::X509_STORE_CTX,
) -> c_int
pub extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
where
F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
{
Expand Down Expand Up @@ -223,14 +218,13 @@ pub(super) unsafe extern "C" fn raw_select_cert<F>(
client_hello: *const ffi::SSL_CLIENT_HELLO,
) -> ffi::ssl_select_cert_result_t
where
F: Fn(&ClientHello) -> Result<(), SelectCertError> + Sync + Send + 'static,
F: Fn(ClientHello<'_>) -> Result<(), SelectCertError> + Sync + Send + 'static,
{
// SAFETY: boring provides valid inputs.
let client_hello = unsafe { &*(client_hello as *const ClientHello) };
let client_hello = ClientHello(unsafe { &*client_hello });

let callback = client_hello
.ssl()
.ssl_context()
let ssl_context = client_hello.ssl().ssl_context().to_owned();
let callback = ssl_context
.ex_data(SslContext::cached_ex_index::<F>())
.expect("BUG: select cert callback missing");

Expand Down Expand Up @@ -373,3 +367,93 @@ where

callback(ssl, line);
}

pub(super) unsafe extern "C" fn raw_sign<M>(
nox marked this conversation as resolved.
Show resolved Hide resolved
ssl: *mut ffi::SSL,
out: *mut u8,
out_len: *mut usize,
max_out: usize,
signature_algorithm: u16,
in_: *const u8,
in_len: usize,
) -> ffi::ssl_private_key_result_t
where
M: PrivateKeyMethod,
{
// SAFETY: boring provides valid inputs.
let input = unsafe { slice::from_raw_parts(in_, in_len) };

let signature_algorithm = SslSignatureAlgorithm(signature_algorithm);

let callback = |method: &M, ssl: &mut _, output: &mut _| {
method.sign(ssl, input, signature_algorithm, output)
};

// SAFETY: boring provides valid inputs.
unsafe { raw_private_key_callback(ssl, out, out_len, max_out, callback) }
}

pub(super) unsafe extern "C" fn raw_decrypt<M>(
ssl: *mut ffi::SSL,
out: *mut u8,
out_len: *mut usize,
max_out: usize,
in_: *const u8,
in_len: usize,
) -> ffi::ssl_private_key_result_t
where
M: PrivateKeyMethod,
{
// SAFETY: boring provides valid inputs.
let input = unsafe { slice::from_raw_parts(in_, in_len) };

let callback = |method: &M, ssl: &mut _, output: &mut _| method.decrypt(ssl, input, output);

// SAFETY: boring provides valid inputs.
unsafe { raw_private_key_callback(ssl, out, out_len, max_out, callback) }
}

pub(super) unsafe extern "C" fn raw_complete<M>(
ssl: *mut ffi::SSL,
out: *mut u8,
out_len: *mut usize,
max_out: usize,
) -> ffi::ssl_private_key_result_t
where
M: PrivateKeyMethod,
{
// SAFETY: boring provides valid inputs.
unsafe { raw_private_key_callback::<M>(ssl, out, out_len, max_out, M::complete) }
}

unsafe fn raw_private_key_callback<M>(
ssl: *mut ffi::SSL,
out: *mut u8,
out_len: *mut usize,
max_out: usize,
callback: impl FnOnce(&M, &mut SslRef, &mut [u8]) -> Result<usize, PrivateKeyMethodError>,
) -> ffi::ssl_private_key_result_t
where
M: PrivateKeyMethod,
{
// SAFETY: boring provides valid inputs.
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
let output = unsafe { slice::from_raw_parts_mut(out, max_out) };
let out_len = unsafe { &mut *out_len };

let ssl_context = ssl.ssl_context().to_owned();
let method = ssl_context
.ex_data(SslContext::cached_ex_index::<M>())
.expect("BUG: private key method missing");

match callback(method, ssl, output) {
Ok(written) => {
assert!(written <= max_out);

*out_len = written;

ffi::ssl_private_key_result_t::ssl_private_key_success
}
Err(err) => err.0,
}
}
71 changes: 65 additions & 6 deletions boring/src/ssl/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use crate::ssl::{
use crate::version;
use std::net::IpAddr;

use super::MidHandshakeSslStream;

const FFDHE_2048: &str = "
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
Expand Down Expand Up @@ -99,11 +101,30 @@ impl SslConnector {
/// Initiates a client-side TLS session on a stream.
///
/// The domain is used for SNI and hostname verification.
pub fn setup_connect<S>(
&self,
domain: &str,
stream: S,
) -> Result<MidHandshakeSslStream<S>, ErrorStack>
where
S: Read + Write,
{
self.configure()?.setup_connect(domain, stream)
}

/// Attempts a client-side TLS session on a stream.
///
/// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
///
/// This is a convenience method which combines [`Self::setup_connect`] and
/// [`MidHandshakeSslStream::handshake`].
pub fn connect<S>(&self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
where
S: Read + Write,
{
self.configure()?.connect(domain, stream)
self.setup_connect(domain, stream)
.map_err(HandshakeError::SetupFailure)?
.handshake()
}

/// Returns a structure allowing for configuration of a single TLS session before connection.
Expand Down Expand Up @@ -190,7 +211,7 @@ impl ConnectConfiguration {
self.verify_hostname = verify_hostname;
}

/// Returns an `Ssl` configured to connect to the provided domain.
/// Returns an [`Ssl`] configured to connect to the provided domain.
///
/// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
pub fn into_ssl(mut self, domain: &str) -> Result<Ssl, ErrorStack> {
Expand All @@ -214,11 +235,33 @@ impl ConnectConfiguration {
/// Initiates a client-side TLS session on a stream.
///
/// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
///
/// This is a convenience method which combines [`Self::into_ssl`] and
/// [`Ssl::setup_connect`].
pub fn setup_connect<S>(
self,
domain: &str,
stream: S,
) -> Result<MidHandshakeSslStream<S>, ErrorStack>
where
S: Read + Write,
{
Ok(self.into_ssl(domain)?.setup_connect(stream))
}

/// Attempts a client-side TLS session on a stream.
///
/// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
///
/// This is a convenience method which combines [`Self::setup_connect`] and
/// [`MidHandshakeSslStream::handshake`].
pub fn connect<S>(self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
where
S: Read + Write,
{
self.into_ssl(domain)?.connect(stream)
self.setup_connect(domain, stream)
.map_err(HandshakeError::SetupFailure)?
.handshake()
}
}

Expand Down Expand Up @@ -327,13 +370,29 @@ impl SslAcceptor {
Ok(SslAcceptorBuilder(ctx))
}

/// Initiates a server-side TLS session on a stream.
pub fn accept<S>(&self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
/// Initiates a server-side TLS handshake on a stream.
///
/// See [`Ssl::setup_accept`] for more details.
pub fn setup_accept<S>(&self, stream: S) -> Result<MidHandshakeSslStream<S>, ErrorStack>
where
S: Read + Write,
{
let ssl = Ssl::new(&self.0)?;
ssl.accept(stream)

Ok(ssl.setup_accept(stream))
}

/// Attempts a server-side TLS handshake on a stream.
///
/// This is a convenience method which combines [`Self::setup_accept`] and
/// [`MidHandshakeSslStream::handshake`].
pub fn accept<S>(&self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
where
S: Read + Write,
{
self.setup_accept(stream)
.map_err(HandshakeError::SetupFailure)?
.handshake()
}

/// Consumes the `SslAcceptor`, returning the inner raw `SslContext`.
Expand Down
24 changes: 24 additions & 0 deletions boring/src/ssl/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ impl ErrorCode {
/// Wait for write readiness and retry the operation.
pub const WANT_WRITE: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_WRITE);

pub const WANT_X509_LOOKUP: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_X509_LOOKUP);

pub const PENDING_SESSION: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_SESSION);

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

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

pub const PENDING_TICKET: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_TICKET);

/// A non-recoverable IO error occurred.
pub const SYSCALL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SYSCALL);

Expand Down Expand Up @@ -81,6 +92,19 @@ impl Error {
_ => None,
}
}

pub fn would_block(&self) -> bool {
matches!(
self.code,
ErrorCode::WANT_READ
| ErrorCode::WANT_WRITE
| ErrorCode::WANT_X509_LOOKUP
| ErrorCode::PENDING_SESSION
| ErrorCode::PENDING_CERTIFICATE
| ErrorCode::WANT_PRIVATE_KEY_OPERATION
| ErrorCode::PENDING_TICKET
)
}
}

impl From<ErrorStack> for Error {
Expand Down
Loading
Loading