Skip to content
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>(
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