boring2/quinn-boring/src/bffi_ext.rs

566 lines
17 KiB
Rust

use crate::error::{br, br_zero_is_success, BoringResult};
use boring::error::ErrorStack;
use boring::pkey::{HasPrivate, PKey};
use boring::ssl::{Ssl, SslContext, SslContextRef, SslSession};
use boring::x509::store::X509StoreBuilderRef;
use boring::x509::X509;
use boring_sys as bffi;
use bytes::{Buf, BufMut};
use foreign_types_shared::{ForeignType, ForeignTypeRef};
use std::ffi::{c_char, c_int, c_uint, c_void, CStr};
use std::fmt::{Display, Formatter};
use std::result::Result as StdResult;
use std::{ffi, fmt, mem, ptr, slice};
/// Provides additional methods to [SslContext] needed for QUIC.
pub trait QuicSslContext {
fn set_options(&mut self, options: u32) -> u32;
fn verify_peer(&mut self, verify: bool);
fn set_quic_method(&mut self, method: &bffi::SSL_QUIC_METHOD) -> BoringResult;
fn set_session_cache_mode(&mut self, mode: c_int) -> c_int;
fn set_new_session_callback(
&mut self,
cb: Option<
unsafe extern "C" fn(ssl: *mut bffi::SSL, session: *mut bffi::SSL_SESSION) -> c_int,
>,
);
fn set_info_callback(
&mut self,
cb: Option<unsafe extern "C" fn(ssl: *const bffi::SSL, type_: c_int, value: c_int)>,
);
fn set_keylog_callback(
&mut self,
cb: Option<unsafe extern "C" fn(ssl: *const bffi::SSL, line: *const c_char)>,
);
fn set_certificate(&mut self, cert: X509) -> BoringResult;
fn load_certificate_from_pem_file(&mut self, path: &str) -> BoringResult;
fn add_to_cert_chain(&mut self, cert: X509) -> BoringResult;
fn load_cert_chain_from_pem_file(&mut self, path: &str) -> BoringResult;
fn set_private_key<T: HasPrivate>(&mut self, key: PKey<T>) -> BoringResult;
fn load_private_key_from_pem_file(&mut self, path: &str) -> BoringResult;
fn check_private_key(&self) -> BoringResult;
fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef;
fn enable_early_data(&mut self, enable: bool);
fn set_alpn_protos(&mut self, protos: &[u8]) -> BoringResult;
fn set_alpn_select_cb(
&mut self,
cb: Option<
unsafe extern "C" fn(
ssl: *mut bffi::SSL,
out: *mut *const u8,
out_len: *mut u8,
in_: *const u8,
in_len: c_uint,
arg: *mut c_void,
) -> c_int,
>,
);
fn set_server_name_cb(
&mut self,
cb: Option<
unsafe extern "C" fn(
ssl: *mut bffi::SSL,
out_alert: *mut c_int,
arg: *mut c_void,
) -> c_int,
>,
);
fn set_select_certificate_cb(
&mut self,
cb: Option<
unsafe extern "C" fn(
arg1: *const bffi::SSL_CLIENT_HELLO,
) -> bffi::ssl_select_cert_result_t,
>,
);
}
impl QuicSslContext for SslContext {
fn set_options(&mut self, options: u32) -> u32 {
unsafe { bffi::SSL_CTX_set_options(self.as_ptr(), options) }
}
fn verify_peer(&mut self, verify: bool) {
let mode = if verify {
bffi::SSL_VERIFY_PEER | bffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT
} else {
bffi::SSL_VERIFY_NONE
};
unsafe { bffi::SSL_CTX_set_verify(self.as_ptr(), mode, None) }
}
fn set_quic_method(&mut self, method: &bffi::SSL_QUIC_METHOD) -> BoringResult {
unsafe { br(bffi::SSL_CTX_set_quic_method(self.as_ptr(), method)) }
}
fn set_session_cache_mode(&mut self, mode: c_int) -> c_int {
unsafe { bffi::SSL_CTX_set_session_cache_mode(self.as_ptr(), mode) }
}
fn set_new_session_callback(
&mut self,
cb: Option<
unsafe extern "C" fn(ssl: *mut bffi::SSL, session: *mut bffi::SSL_SESSION) -> c_int,
>,
) {
unsafe {
bffi::SSL_CTX_sess_set_new_cb(self.as_ptr(), cb);
}
}
fn set_info_callback(
&mut self,
cb: Option<unsafe extern "C" fn(ssl: *const bffi::SSL, type_: c_int, value: c_int)>,
) {
unsafe { bffi::SSL_CTX_set_info_callback(self.as_ptr(), cb) }
}
fn set_keylog_callback(
&mut self,
cb: Option<unsafe extern "C" fn(ssl: *const bffi::SSL, line: *const c_char)>,
) {
unsafe { bffi::SSL_CTX_set_keylog_callback(self.as_ptr(), cb) }
}
fn set_certificate(&mut self, cert: X509) -> BoringResult {
unsafe {
br(bffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr()))?;
mem::forget(cert);
Ok(())
}
}
fn load_certificate_from_pem_file(&mut self, path: &str) -> BoringResult {
let path = ffi::CString::new(path).unwrap();
unsafe {
br(bffi::SSL_CTX_use_certificate_file(
self.as_ptr(),
path.as_ptr(),
bffi::SSL_FILETYPE_PEM,
))
}
}
fn add_to_cert_chain(&mut self, cert: X509) -> BoringResult {
unsafe {
br(bffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?;
mem::forget(cert);
Ok(())
}
}
fn load_cert_chain_from_pem_file(&mut self, path: &str) -> BoringResult {
let path = ffi::CString::new(path).unwrap();
unsafe {
br(bffi::SSL_CTX_use_certificate_chain_file(
self.as_ptr(),
path.as_ptr(),
))
}
}
fn set_private_key<T: HasPrivate>(&mut self, key: PKey<T>) -> BoringResult {
unsafe {
br(bffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr()))?;
mem::forget(key);
Ok(())
}
}
fn load_private_key_from_pem_file(&mut self, path: &str) -> BoringResult {
let path = ffi::CString::new(path).unwrap();
unsafe {
br(bffi::SSL_CTX_use_PrivateKey_file(
self.as_ptr(),
path.as_ptr(),
bffi::SSL_FILETYPE_PEM,
))
}
}
fn check_private_key(&self) -> BoringResult {
unsafe { br(bffi::SSL_CTX_check_private_key(self.as_ptr())) }
}
fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef {
unsafe { X509StoreBuilderRef::from_ptr_mut(bffi::SSL_CTX_get_cert_store(self.as_ptr())) }
}
fn enable_early_data(&mut self, enable: bool) {
unsafe { bffi::SSL_CTX_set_early_data_enabled(self.as_ptr(), enable.into()) }
}
fn set_alpn_protos(&mut self, protos: &[u8]) -> BoringResult {
unsafe {
br_zero_is_success(bffi::SSL_CTX_set_alpn_protos(
self.as_ptr(),
protos.as_ptr(),
protos.len() as _,
))
}
}
fn set_alpn_select_cb(
&mut self,
cb: Option<
unsafe extern "C" fn(
*mut bffi::SSL,
*mut *const u8,
*mut u8,
*const u8,
c_uint,
*mut c_void,
) -> c_int,
>,
) {
unsafe { bffi::SSL_CTX_set_alpn_select_cb(self.as_ptr(), cb, ptr::null_mut()) }
}
fn set_server_name_cb(
&mut self,
cb: Option<
unsafe extern "C" fn(
ssl: *mut bffi::SSL,
out_alert: *mut c_int,
arg: *mut c_void,
) -> c_int,
>,
) {
// The function always returns 1.
unsafe {
let _ = bffi::SSL_CTX_set_tlsext_servername_callback(self.as_ptr(), cb);
}
}
fn set_select_certificate_cb(
&mut self,
cb: Option<
unsafe extern "C" fn(
arg1: *const bffi::SSL_CLIENT_HELLO,
) -> bffi::ssl_select_cert_result_t,
>,
) {
unsafe { bffi::SSL_CTX_set_select_certificate_cb(self.as_ptr(), cb) }
}
}
/// Provides additional methods to [Ssl] needed for QUIC.
pub trait QuicSsl {
fn set_connect_state(&mut self);
fn set_accept_state(&mut self);
fn state_string(&self) -> &'static str;
fn set_quic_transport_params(&mut self, params: &[u8]) -> BoringResult;
fn get_peer_quic_transport_params(&self) -> Option<&[u8]>;
fn get_error(&self, raw: c_int) -> SslError;
fn is_handshaking(&self) -> bool;
fn do_handshake(&mut self) -> SslError;
fn provide_quic_data(&mut self, level: Level, data: &[u8]) -> SslError;
fn quic_max_handshake_flight_len(&self, level: Level) -> usize;
fn quic_read_level(&self) -> Level;
fn quic_write_level(&self) -> Level;
fn process_post_handshake(&mut self) -> SslError;
fn set_verify_hostname(&mut self, domain: &str) -> BoringResult;
fn export_keyring_material(
&self,
output: &mut [u8],
label: &[u8],
context: &[u8],
) -> BoringResult;
fn in_early_data(&self) -> bool;
fn early_data_accepted(&self) -> bool;
fn set_quic_method(&mut self, method: &bffi::SSL_QUIC_METHOD) -> BoringResult;
fn set_quic_early_data_context(&mut self, value: &[u8]) -> BoringResult;
fn get_early_data_reason(&self) -> bffi::ssl_early_data_reason_t;
fn early_data_reason_string(reason: bffi::ssl_early_data_reason_t) -> &'static str;
fn reset_early_rejected_data(&mut self);
fn set_quic_use_legacy_codepoint(&mut self, use_legacy: bool);
}
impl QuicSsl for Ssl {
fn set_connect_state(&mut self) {
unsafe { bffi::SSL_set_connect_state(self.as_ptr()) }
}
fn set_accept_state(&mut self) {
unsafe { bffi::SSL_set_accept_state(self.as_ptr()) }
}
fn state_string(&self) -> &'static str {
unsafe {
CStr::from_ptr(bffi::SSL_state_string_long(self.as_ptr()))
.to_str()
.unwrap()
}
}
fn set_quic_transport_params(&mut self, params: &[u8]) -> BoringResult {
unsafe {
br(bffi::SSL_set_quic_transport_params(
self.as_ptr(),
params.as_ptr(),
params.len(),
))
}
}
fn get_peer_quic_transport_params(&self) -> Option<&[u8]> {
let mut ptr: *const u8 = ptr::null();
let mut len: usize = 0;
unsafe {
bffi::SSL_get_peer_quic_transport_params(self.as_ptr(), &mut ptr, &mut len);
if len == 0 {
None
} else {
Some(slice::from_raw_parts(ptr, len))
}
}
}
#[inline]
fn get_error(&self, raw: c_int) -> SslError {
unsafe { SslError(bffi::SSL_get_error(self.as_ptr(), raw)) }
}
#[inline]
fn is_handshaking(&self) -> bool {
unsafe { bffi::SSL_in_init(self.as_ptr()) == 1 }
}
#[inline]
fn do_handshake(&mut self) -> SslError {
self.get_error(unsafe { bffi::SSL_do_handshake(self.as_ptr()) })
}
#[inline]
fn provide_quic_data(&mut self, level: Level, plaintext: &[u8]) -> SslError {
unsafe {
self.get_error(bffi::SSL_provide_quic_data(
self.as_ptr(),
level.into(),
plaintext.as_ptr(),
plaintext.len(),
))
}
}
#[inline]
fn quic_max_handshake_flight_len(&self, level: Level) -> usize {
unsafe { bffi::SSL_quic_max_handshake_flight_len(self.as_ptr(), level.into()) }
}
#[inline]
fn quic_read_level(&self) -> Level {
unsafe { bffi::SSL_quic_read_level(self.as_ptr()).into() }
}
#[inline]
fn quic_write_level(&self) -> Level {
unsafe { bffi::SSL_quic_write_level(self.as_ptr()).into() }
}
#[inline]
fn process_post_handshake(&mut self) -> SslError {
self.get_error(unsafe { bffi::SSL_process_quic_post_handshake(self.as_ptr()) })
}
fn set_verify_hostname(&mut self, domain: &str) -> BoringResult {
let param = self.param_mut();
param.set_hostflags(boring::x509::verify::X509CheckFlags::NO_PARTIAL_WILDCARDS);
match domain.parse() {
Ok(ip) => param.set_ip(ip)?,
Err(_) => param.set_host(domain)?,
}
Ok(())
}
#[inline]
fn export_keyring_material(
&self,
output: &mut [u8],
label: &[u8],
context: &[u8],
) -> BoringResult {
unsafe {
br(bffi::SSL_export_keying_material(
self.as_ptr(),
output.as_mut_ptr(),
output.len(),
label.as_ptr() as *const c_char,
label.len(),
context.as_ptr(),
context.len(),
context.is_empty() as _,
))
}
}
#[inline]
fn in_early_data(&self) -> bool {
unsafe { bffi::SSL_in_early_data(self.as_ptr()) == 1 }
}
#[inline]
fn early_data_accepted(&self) -> bool {
unsafe { bffi::SSL_early_data_accepted(self.as_ptr()) == 1 }
}
fn set_quic_method(&mut self, method: &bffi::SSL_QUIC_METHOD) -> BoringResult {
unsafe { br(bffi::SSL_set_quic_method(self.as_ptr(), method)) }
}
fn set_quic_early_data_context(&mut self, value: &[u8]) -> BoringResult {
unsafe {
br(bffi::SSL_set_quic_early_data_context(
self.as_ptr(),
value.as_ptr(),
value.len(),
))
}
}
fn get_early_data_reason(&self) -> bffi::ssl_early_data_reason_t {
unsafe { bffi::SSL_get_early_data_reason(self.as_ptr()) }
}
fn early_data_reason_string(reason: bffi::ssl_early_data_reason_t) -> &'static str {
unsafe {
bffi::SSL_early_data_reason_string(reason)
.as_ref()
.map_or("unknown", |reason| CStr::from_ptr(reason).to_str().unwrap())
}
}
#[inline]
fn reset_early_rejected_data(&mut self) {
unsafe { bffi::SSL_reset_early_data_reject(self.as_ptr()) }
}
fn set_quic_use_legacy_codepoint(&mut self, use_legacy: bool) {
unsafe { bffi::SSL_set_quic_use_legacy_codepoint(self.as_ptr(), use_legacy as _) }
}
}
pub trait QuicSslSession {
fn early_data_capable(&self) -> bool;
fn copy_without_early_data(&mut self) -> SslSession;
fn encode<W: BufMut>(&self, out: &mut W) -> BoringResult;
fn decode<R: Buf>(ctx: &SslContextRef, r: &mut R) -> StdResult<SslSession, ErrorStack>;
}
impl QuicSslSession for SslSession {
fn early_data_capable(&self) -> bool {
unsafe { bffi::SSL_SESSION_early_data_capable(self.as_ptr()) == 1 }
}
fn copy_without_early_data(&mut self) -> SslSession {
unsafe { SslSession::from_ptr(bffi::SSL_SESSION_copy_without_early_data(self.as_ptr())) }
}
fn encode<W: BufMut>(&self, out: &mut W) -> BoringResult {
unsafe {
let mut buf: *mut u8 = ptr::null_mut();
let mut len = 0usize;
br(bffi::SSL_SESSION_to_bytes(
self.as_ptr(),
&mut buf,
&mut len,
))?;
out.put_slice(slice::from_raw_parts(buf, len));
bffi::OPENSSL_free(buf as _);
Ok(())
}
}
fn decode<R: Buf>(ctx: &SslContextRef, r: &mut R) -> StdResult<SslSession, ErrorStack> {
unsafe {
let in_len = r.remaining();
let in_ = r.chunk();
bffi::SSL_SESSION_from_bytes(in_.as_ptr(), in_len, ctx.as_ptr())
.as_mut()
.map_or_else(
|| Err(ErrorStack::get()),
|session| Ok(SslSession::from_ptr(session)),
)
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Level {
Initial = 0,
EarlyData = 1,
Handshake = 2,
Application = 3,
}
impl Level {
pub const NUM_LEVELS: usize = 4;
pub fn next(&self) -> Self {
match self {
Level::Initial => Level::Handshake,
Level::EarlyData => Level::Handshake,
_ => Level::Application,
}
}
}
impl From<bffi::ssl_encryption_level_t> for Level {
fn from(value: bffi::ssl_encryption_level_t) -> Self {
match value {
bffi::ssl_encryption_level_t::ssl_encryption_initial => Self::Initial,
bffi::ssl_encryption_level_t::ssl_encryption_early_data => Self::EarlyData,
bffi::ssl_encryption_level_t::ssl_encryption_handshake => Self::Handshake,
bffi::ssl_encryption_level_t::ssl_encryption_application => Self::Application,
_ => unreachable!(),
}
}
}
impl From<Level> for bffi::ssl_encryption_level_t {
fn from(value: Level) -> Self {
match value {
Level::Initial => bffi::ssl_encryption_level_t::ssl_encryption_initial,
Level::EarlyData => bffi::ssl_encryption_level_t::ssl_encryption_early_data,
Level::Handshake => bffi::ssl_encryption_level_t::ssl_encryption_handshake,
Level::Application => bffi::ssl_encryption_level_t::ssl_encryption_application,
}
}
}
#[derive(Copy, Clone)]
pub struct SslError(c_int);
impl SslError {
#[inline]
pub fn value(&self) -> c_int {
self.0
}
#[inline]
pub fn is_none(&self) -> bool {
self.0 == bffi::SSL_ERROR_NONE
}
#[inline]
pub fn get_description(&self) -> &'static str {
unsafe {
CStr::from_ptr(bffi::SSL_error_description(self.0))
.to_str()
.unwrap()
}
}
}
impl Display for SslError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "SSL_ERROR[{}]: {}", self.0, self.get_description())
}
}