use std::ffi;
use std::io::Write;
use std::ptr;
use std::slice;
use libc::c_char;
use libc::c_int;
use libc::c_long;
use libc::c_uint;
use libc::c_void;
use log::trace;
use crate::codec::Decoder;
use crate::tls;
use crate::tls::boringssl::crypto;
use crate::tls::key;
use crate::tls::TlsSessionData;
use crate::Error;
use crate::Result;
#[repr(transparent)]
struct SslMethod(c_void);
#[repr(transparent)]
pub struct SslCtx(c_void);
#[repr(transparent)]
struct Ssl(c_void);
#[repr(transparent)]
struct SslCipher(c_void);
#[repr(transparent)]
struct SslSession(c_void);
#[repr(transparent)]
struct X509Store(c_void);
#[repr(transparent)]
struct X509(c_void);
#[repr(transparent)]
struct X509VerifyParam(c_void);
#[repr(transparent)]
struct StackOf(c_void);
#[repr(transparent)]
struct CryptoBuffer(c_void);
#[repr(transparent)]
struct CryptoExData(c_void);
#[repr(C)]
struct SslQuicMethod {
set_read_secret: extern "C" fn(
ssl: *mut Ssl,
level: tls::Level,
cipher: *const SslCipher,
secret: *const u8,
secret_len: usize,
) -> c_int,
set_write_secret: extern "C" fn(
ssl: *mut Ssl,
level: tls::Level,
cipher: *const SslCipher,
secret: *const u8,
secret_len: usize,
) -> c_int,
add_handshake_data:
extern "C" fn(ssl: *mut Ssl, level: tls::Level, data: *const u8, len: usize) -> c_int,
flush_flight: extern "C" fn(ssl: *mut Ssl) -> c_int,
send_alert: extern "C" fn(ssl: *mut Ssl, level: tls::Level, alert: u8) -> c_int,
}
#[repr(C)]
enum SslEarlyDataReason {
Unknown = 0,
Disabled = 1,
Accepted = 2,
ProtocolVersion = 3,
PeerDeclined = 4,
NoSessionOffered = 5,
SessionNotResumed = 6,
UnsupportedForSession = 7,
HelloRetryRequest = 8,
AlpnMismatch = 9,
ChannelId = 10,
TicketAgeSkew = 12,
QuicParameterMismatch = 13,
AlpsMismatch = 14,
}
extern "C" fn context_data_free(
parent: *mut c_void,
ptr: *mut c_void,
_ad: *mut CryptoExData,
_index: c_int,
arg1: c_long,
_argp: *mut c_void,
) {
if parent.is_null() || ptr.is_null() || arg1 != 0 {
return;
}
unsafe {
let _ = Box::from_raw(ptr as *mut Vec<Vec<u8>>);
};
}
lazy_static::lazy_static! {
pub static ref CONTEXT_DATA_INDEX: c_int = unsafe {
SSL_CTX_get_ex_new_index(0, ptr::null(), ptr::null(), ptr::null(), context_data_free)
};
pub static ref SESSION_DATA_INDEX: c_int = unsafe {
SSL_get_ex_new_index(0, ptr::null(), ptr::null(), ptr::null(), ptr::null())
};
}
static SSL_QUIC_METHOD: SslQuicMethod = SslQuicMethod {
set_read_secret,
set_write_secret,
add_handshake_data,
flush_flight,
send_alert,
};
pub(crate) struct Context {
ctx_raw: *mut SslCtx,
owned: bool,
}
impl Drop for Context {
fn drop(&mut self) {
if self.owned {
unsafe { SSL_CTX_free(self.as_mut_ptr()) }
}
}
}
impl Context {
pub fn new() -> Result<Context> {
unsafe {
let ctx_raw = SSL_CTX_new(TLS_method());
let mut ctx = Context {
ctx_raw,
owned: true,
};
ctx.set_session_callback();
ctx.set_default_verify_paths()?;
Ok(ctx)
}
}
pub fn new_with_ssl_ctx(ssl_ctx: *mut SslCtx) -> Context {
Self {
ctx_raw: ssl_ctx,
owned: false,
}
}
pub fn as_mut_ptr(&mut self) -> *mut SslCtx {
self.ctx_raw
}
pub fn as_ptr(&self) -> *const SslCtx {
self.ctx_raw
}
pub fn new_session(&self) -> Result<Session> {
unsafe {
let ssl = SSL_new(self.as_ptr());
Ok(Session::new(ssl))
}
}
pub fn load_verify_locations_from_file(&mut self, file: &str) -> Result<()> {
let file = ffi::CString::new(file)
.map_err(|e| Error::TlsFail(format!("file name({:?}) format error: {:?}", file, e)))?;
match unsafe {
SSL_CTX_load_verify_locations(self.as_mut_ptr(), file.as_ptr(), std::ptr::null())
} {
1 => Ok(()),
_ => Err(Error::TlsFail(format!(
"load verify locations from file({:?}) failed",
file
))),
}
}
pub fn load_verify_locations_from_directory(&mut self, path: &str) -> Result<()> {
let path = ffi::CString::new(path)
.map_err(|e| Error::TlsFail(format!("path name({:?}) format error: {:?}", path, e)))?;
match unsafe {
SSL_CTX_load_verify_locations(self.as_mut_ptr(), std::ptr::null(), path.as_ptr())
} {
1 => Ok(()),
_ => Err(Error::TlsFail(format!(
"load verify locations from path({:?}) failed",
path
))),
}
}
pub fn use_certificate_chain_file(&mut self, file: &str) -> Result<()> {
let cstr = ffi::CString::new(file)
.map_err(|e| Error::TlsFail(format!("file name({:?}) format error: {:?}", file, e)))?;
match unsafe { SSL_CTX_use_certificate_chain_file(self.as_mut_ptr(), cstr.as_ptr()) } {
1 => Ok(()),
_ => Err(Error::TlsFail(format!(
"use certificate chain file({:?}) failed",
file
))),
}
}
pub fn use_private_key_file(&mut self, file: &str) -> Result<()> {
let cstr = ffi::CString::new(file)
.map_err(|e| Error::TlsFail(format!("file name({:?}) format error: {:?}", file, e)))?;
match unsafe { SSL_CTX_use_PrivateKey_file(self.as_mut_ptr(), cstr.as_ptr(), 1) } {
1 => Ok(()),
_ => Err(Error::TlsFail(format!(
"use private key file({:?}) failed",
file
))),
}
}
#[cfg(not(windows))]
fn set_default_verify_paths(&mut self) -> Result<()> {
match unsafe { SSL_CTX_set_default_verify_paths(self.as_mut_ptr()) } {
1 => Ok(()),
_ => Err(Error::TlsFail(
"set default verify paths failed".to_string(),
)),
}
}
#[cfg(windows)]
fn set_default_verify_paths(&mut self) -> Result<()> {
unsafe {
let cstr = ffi::CString::new("Root")
.map_err(|_| Error::TlsFail("CString::new".to_string()))?;
let sys_store = winapi::um::wincrypt::CertOpenSystemStoreA(
0,
cstr.as_ptr() as winapi::um::winnt::LPCSTR,
);
if sys_store.is_null() {
return Err(Error::TlsFail("open system store".to_string()));
}
let crt_store = SSL_CTX_get_cert_store(self.as_mut_ptr());
if crt_store.is_null() {
winapi::um::wincrypt::CertCloseStore(sys_store, 0);
return Err(Error::TlsFail("get cert store".to_string()));
}
let mut ctx_p =
winapi::um::wincrypt::CertEnumCertificatesInStore(sys_store, ptr::null());
while !ctx_p.is_null() {
let in_p = (*ctx_p).pbCertEncoded as *const u8;
let cert = d2i_X509(ptr::null_mut(), &in_p, (*ctx_p).cbCertEncoded as i32);
if !cert.is_null() {
X509_STORE_add_cert(crt_store, cert);
X509_free(cert);
}
ctx_p = winapi::um::wincrypt::CertEnumCertificatesInStore(sys_store, ctx_p);
}
winapi::um::wincrypt::CertFreeCertificateContext(ctx_p);
winapi::um::wincrypt::CertCloseStore(sys_store, 0);
}
Ok(())
}
pub fn set_session_callback(&mut self) {
unsafe {
SSL_CTX_set_session_cache_mode(
self.as_mut_ptr(),
0x0001, );
SSL_CTX_sess_set_new_cb(self.as_mut_ptr(), new_session);
};
}
pub fn set_verify(&mut self, verify: bool) {
let mode = i32::from(verify);
unsafe {
SSL_CTX_set_verify(self.as_mut_ptr(), mode, ptr::null());
}
}
pub fn enable_keylog(&mut self) {
unsafe {
SSL_CTX_set_keylog_callback(self.as_mut_ptr(), keylog);
}
}
pub fn set_alpn(&mut self, v: Vec<Vec<u8>>) -> Result<()> {
let mut protos: Vec<u8> = Vec::new();
for proto in &v {
protos.push(proto.len() as u8);
protos.extend_from_slice(proto);
}
let v = Box::new(v);
unsafe {
SSL_CTX_set_ex_data(
self.as_mut_ptr(),
*CONTEXT_DATA_INDEX,
Box::into_raw(v) as *const c_void,
);
}
unsafe {
SSL_CTX_set_alpn_select_cb(self.as_mut_ptr(), select_alpn, ptr::null_mut());
}
match unsafe { SSL_CTX_set_alpn_protos(self.as_mut_ptr(), protos.as_ptr(), protos.len()) } {
0 => Ok(()),
_ => Err(Error::TlsFail("SSL set alpn failed".to_string())),
}
}
pub fn set_ticket_key(&mut self, key: &[u8]) -> Result<()> {
match unsafe { SSL_CTX_set_tlsext_ticket_keys(self.as_mut_ptr(), key.as_ptr(), key.len()) }
{
1 => Ok(()),
_ => Err(Error::TlsFail("set ticket key failed".to_string())),
}
}
pub fn set_early_data_enabled(&mut self, enabled: bool) {
let enabled = i32::from(enabled);
unsafe {
SSL_CTX_set_early_data_enabled(self.as_mut_ptr(), enabled);
}
}
pub fn set_session_psk_dhe_timeout(&mut self, timeout: u32) {
unsafe {
SSL_CTX_set_session_psk_dhe_timeout(self.as_mut_ptr(), timeout);
}
}
}
fn get_ctx_data_from_ptr<'a, T>(ptr: *mut SslCtx, idx: c_int) -> Option<&'a mut T> {
unsafe {
let data = SSL_CTX_get_ex_data(ptr, idx) as *mut T;
data.as_mut()
}
}
unsafe impl std::marker::Send for Context {}
unsafe impl std::marker::Sync for Context {}
pub struct Session {
ptr: *mut Ssl,
provided_data_outstanding: bool,
}
impl Session {
fn new(ptr: *mut Ssl) -> Session {
Session {
ptr,
provided_data_outstanding: false,
}
}
pub fn get_error(&self, ret_code: c_int) -> c_int {
unsafe { SSL_get_error(self.as_ptr(), ret_code) }
}
pub fn init(&mut self, is_server: bool) -> Result<()> {
self.set_state(is_server);
const TLS1_3_VERSION: u16 = 0x0304;
self.set_min_proto_version(TLS1_3_VERSION);
self.set_max_proto_version(TLS1_3_VERSION);
self.set_quic_method()?;
self.set_quic_early_data_context(b"quic")?;
self.set_quiet_shutdown(true);
Ok(())
}
pub fn set_state(&mut self, is_server: bool) {
unsafe {
if is_server {
SSL_set_accept_state(self.as_mut_ptr());
} else {
SSL_set_connect_state(self.as_mut_ptr());
}
}
}
pub fn set_ex_data<T>(&mut self, idx: c_int, data: *const T) -> Result<()> {
match unsafe {
let ptr = data as *const c_void;
SSL_set_ex_data(self.as_mut_ptr(), idx, ptr)
} {
1 => Ok(()),
_ => Err(Error::TlsFail("SSL set extra data failed".to_string())),
}
}
pub fn set_quic_method(&mut self) -> Result<()> {
match unsafe { SSL_set_quic_method(self.as_mut_ptr(), &SSL_QUIC_METHOD) } {
1 => Ok(()),
_ => Err(Error::TlsFail("SSL set quic method failed".to_string())),
}
}
pub fn set_quic_early_data_context(&mut self, context: &[u8]) -> Result<()> {
match unsafe {
SSL_set_quic_early_data_context(self.as_mut_ptr(), context.as_ptr(), context.len())
} {
1 => Ok(()),
_ => Err(Error::TlsFail(
"SSL set quic early data context failed".to_string(),
)),
}
}
pub fn set_min_proto_version(&mut self, version: u16) {
unsafe { SSL_set_min_proto_version(self.as_mut_ptr(), version) }
}
pub fn set_max_proto_version(&mut self, version: u16) {
unsafe { SSL_set_max_proto_version(self.as_mut_ptr(), version) }
}
pub fn set_quiet_shutdown(&mut self, mode: bool) {
unsafe { SSL_set_quiet_shutdown(self.as_mut_ptr(), i32::from(mode)) }
}
pub fn set_host_name(&mut self, name: &str) -> Result<()> {
let cstr = ffi::CString::new(name)
.map_err(|_| Error::TlsFail("host name format error".to_string()))?;
let rc = unsafe { SSL_set_tlsext_host_name(self.as_mut_ptr(), cstr.as_ptr()) };
self.map_result_ssl(rc, None)?;
let param = unsafe { SSL_get0_param(self.as_mut_ptr()) };
match unsafe { X509_VERIFY_PARAM_set1_host(param, cstr.as_ptr(), name.len()) } {
1 => Ok(()),
_ => Err(Error::TlsFail(format!(
"SSL set host name({:?}) failed",
name
))),
}
}
pub fn set_cert_cb(&mut self) {
unsafe { SSL_set_cert_cb(self.as_mut_ptr(), select_cert, std::ptr::null_mut()) }
}
pub fn set_quic_transport_params(&mut self, buf: &[u8]) -> Result<()> {
let rc =
unsafe { SSL_set_quic_transport_params(self.as_mut_ptr(), buf.as_ptr(), buf.len()) };
self.map_result_ssl(rc, None)
}
pub fn quic_transport_params(&self) -> &[u8] {
let mut ptr: *const u8 = ptr::null();
let mut len: usize = 0;
unsafe {
SSL_get_peer_quic_transport_params(self.as_ptr(), &mut ptr, &mut len);
}
if len == 0 {
return &mut [];
}
unsafe { slice::from_raw_parts(ptr, len) }
}
pub fn alpn_protocol(&self) -> &[u8] {
let mut ptr: *const u8 = ptr::null();
let mut len: u32 = 0;
unsafe {
SSL_get0_alpn_selected(self.as_ptr(), &mut ptr, &mut len);
}
if len == 0 {
return &mut [];
}
unsafe { slice::from_raw_parts(ptr, len as usize) }
}
pub fn server_name(&self) -> Option<&str> {
let s = unsafe {
let ptr = SSL_get_servername(
self.as_ptr(),
0, );
if ptr.is_null() {
return None;
}
ffi::CStr::from_ptr(ptr)
};
s.to_str().ok()
}
pub fn set_session(&mut self, session: &[u8]) -> Result<()> {
let ctx = unsafe { SSL_get_SSL_CTX(self.as_ptr()) };
if ctx.is_null() {
return Err(Error::TlsFail("SSL context is null".to_string()));
}
let session = unsafe { SSL_SESSION_from_bytes(session.as_ptr(), session.len(), ctx) };
if session.is_null() {
return Err(Error::TlsFail("SSL session is null".to_string()));
}
match unsafe {
let rc = SSL_set_session(self.as_mut_ptr(), session);
SSL_SESSION_free(session);
rc
} {
1 => Ok(()),
_ => Err(Error::TlsFail("SSL set session failed".to_string())),
}
}
pub fn provide_data(&mut self, level: tls::Level, buf: &[u8]) -> Result<()> {
self.provided_data_outstanding = true;
let rc =
unsafe { SSL_provide_quic_data(self.as_mut_ptr(), level, buf.as_ptr(), buf.len()) };
self.map_result_ssl(rc, None)
}
pub fn do_handshake(&mut self, session_data: &mut tls::TlsSessionData) -> Result<()> {
self.set_ex_data(*SESSION_DATA_INDEX, session_data)?;
let rc = unsafe { SSL_do_handshake(self.as_mut_ptr()) };
self.set_ex_data::<tls::TlsSessionData>(*SESSION_DATA_INDEX, std::ptr::null())?;
self.set_transport_error(session_data, rc);
self.map_result_ssl(rc, Some(session_data))
}
pub fn process_post_handshake(&mut self, session_data: &mut tls::TlsSessionData) -> Result<()> {
if !self.provided_data_outstanding {
return Ok(());
}
self.provided_data_outstanding = false;
self.set_ex_data(*SESSION_DATA_INDEX, session_data)?;
let rc = unsafe { SSL_process_quic_post_handshake(self.as_mut_ptr()) };
self.set_ex_data::<tls::TlsSessionData>(*SESSION_DATA_INDEX, std::ptr::null())?;
self.set_transport_error(session_data, rc);
self.map_result_ssl(rc, Some(session_data))
}
pub fn reset_early_data_reject(&mut self) {
unsafe { SSL_reset_early_data_reject(self.as_mut_ptr()) };
}
pub fn write_level(&self) -> tls::Level {
unsafe { SSL_quic_write_level(self.as_ptr()) }
}
pub fn cipher(&self) -> Option<crypto::Algorithm> {
let cipher = map_result_ptr(unsafe { SSL_get_current_cipher(self.as_ptr()) });
get_cipher_from_ptr(cipher.ok()?).ok()
}
pub fn curve(&self) -> Option<String> {
let curve = unsafe {
let curve_id = SSL_get_curve_id(self.as_ptr());
if curve_id == 0 {
return None;
}
let curve_name = SSL_get_curve_name(curve_id);
match ffi::CStr::from_ptr(curve_name).to_str() {
Ok(v) => v,
Err(_) => return None,
}
};
Some(curve.to_string())
}
pub fn peer_sign_algor(&self) -> Option<String> {
let sigalg = unsafe {
let sigalg_id = SSL_get_peer_signature_algorithm(self.as_ptr());
if sigalg_id == 0 {
return None;
}
let sigalg_name = SSL_get_signature_algorithm_name(sigalg_id, 1);
match ffi::CStr::from_ptr(sigalg_name).to_str() {
Ok(v) => v,
Err(_) => return None,
}
};
Some(sigalg.to_string())
}
pub fn peer_cert_chain(&self) -> Option<Vec<&[u8]>> {
let cert_chain = unsafe {
let chain = map_result_ptr(SSL_get0_peer_certificates(self.as_ptr())).ok()?;
let num = sk_num(chain);
if num <= 0 {
return None;
}
let mut cert_chain = vec![];
for i in 0..num {
let buffer = map_result_ptr(sk_value(chain, i) as *const CryptoBuffer).ok()?;
let out_len = CRYPTO_BUFFER_len(buffer);
if out_len == 0 {
return None;
}
let out = CRYPTO_BUFFER_data(buffer);
let slice = slice::from_raw_parts(out, out_len);
cert_chain.push(slice);
}
cert_chain
};
Some(cert_chain)
}
pub fn peer_cert(&self) -> Option<&[u8]> {
let peer_cert = unsafe {
let chain = map_result_ptr(SSL_get0_peer_certificates(self.as_ptr())).ok()?;
if sk_num(chain) <= 0 {
return None;
}
let buffer = map_result_ptr(sk_value(chain, 0) as *const CryptoBuffer).ok()?;
let out_len = CRYPTO_BUFFER_len(buffer);
if out_len == 0 {
return None;
}
let out = CRYPTO_BUFFER_data(buffer);
slice::from_raw_parts(out, out_len)
};
Some(peer_cert)
}
pub fn early_data_reason(&self) -> Result<Option<&str>> {
let reason = unsafe {
let reason = SSL_early_data_reason_string(SSL_get_early_data_reason(self.as_ptr()));
match ffi::CStr::from_ptr(reason).to_str() {
Ok(v) => v,
Err(e) => {
return Err(Error::TlsFail(format!(
"early data reason format error {:?}",
e
)))
}
}
};
Ok(Some(reason))
}
pub fn is_completed(&self) -> bool {
unsafe { SSL_in_init(self.as_ptr()) == 0 }
}
pub fn is_resumed(&self) -> bool {
unsafe { SSL_session_reused(self.as_ptr()) == 1 }
}
pub fn is_in_early_data(&self) -> bool {
unsafe { SSL_in_early_data(self.as_ptr()) == 1 }
}
pub fn clear(&mut self) -> Result<()> {
let rc = unsafe { SSL_clear(self.as_mut_ptr()) };
self.map_result_ssl(rc, None)
}
fn as_ptr(&self) -> *const Ssl {
self.ptr
}
fn as_mut_ptr(&mut self) -> *mut Ssl {
self.ptr
}
fn map_result_ssl(
&mut self,
bssl_result: c_int,
session_data: Option<&mut tls::TlsSessionData>,
) -> Result<()> {
match bssl_result {
1 => Ok(()),
_ => {
let ssl_err = self.get_error(bssl_result);
match ssl_err {
1 => {
let ssl_err = get_ssl_error()?;
trace!("SSL error: {}", ssl_err);
Err(Error::TlsFail(format!("SSL error: {}", ssl_err)))
}
2 => Err(Error::Done),
3 => Err(Error::Done),
4 => Err(Error::Done),
5 => Err(Error::TlsFail("SSL error, syscall".to_string())),
11 => Err(Error::Done),
12 => Err(Error::Done),
13 => Err(Error::Done),
14 => Err(Error::Done),
15 => {
self.reset_early_data_reject();
if let Some(session_data) = session_data {
trace!("{} early data rejected", session_data.trace_id);
session_data.early_data_rejected = true;
}
Err(Error::Done)
}
16 => Err(Error::Done),
_ => Err(Error::TlsFail("SSL error, unknown".to_string())),
}
}
}
}
fn set_transport_error(&mut self, session_data: &mut tls::TlsSessionData, bssl_result: c_int) {
if self.get_error(bssl_result) == 1 {
if session_data.error.is_none() {
session_data.error = Some(tls::TlsError {
error_code: 0x01,
reason: Vec::new(),
})
}
}
}
}
unsafe impl std::marker::Send for Session {}
unsafe impl std::marker::Sync for Session {}
impl Drop for Session {
fn drop(&mut self) {
unsafe { SSL_free(self.as_mut_ptr()) }
}
}
fn get_sess_data_from_ptr<'a, T>(ptr: *mut Ssl, idx: c_int) -> Option<&'a mut T> {
unsafe {
let data = SSL_get_ex_data(ptr, idx) as *mut T;
data.as_mut()
}
}
fn get_cipher_from_ptr(cipher: *const SslCipher) -> Result<crypto::Algorithm> {
let cipher_id = unsafe { SSL_CIPHER_get_id(cipher) };
let algor = match cipher_id {
0x0300_1301 => crypto::Algorithm::Aes128Gcm,
0x0300_1302 => crypto::Algorithm::Aes256Gcm,
0x0300_1303 => crypto::Algorithm::ChaCha20Poly1305,
_ => return Err(Error::TlsFail("unsupported cipher".to_string())),
};
Ok(algor)
}
extern "C" fn set_read_secret(
ssl: *mut Ssl,
level: tls::Level,
cipher: *const SslCipher,
secret: *const u8,
secret_len: usize,
) -> c_int {
let session_data = match get_sess_data_from_ptr::<tls::TlsSessionData>(ssl, *SESSION_DATA_INDEX)
{
Some(v) => v,
None => return 0,
};
trace!(
"{} set read secret level {:?}",
session_data.trace_id,
level
);
let keys = &mut session_data.key_collection[level];
let aead = match get_cipher_from_ptr(cipher) {
Ok(v) => v,
Err(_) => return 0,
};
if level != tls::Level::ZeroRTT || session_data.is_server {
let secret = unsafe { slice::from_raw_parts(secret, secret_len) };
let open = match crypto::Open::new_with_secret(aead, secret.to_vec()) {
Ok(v) => v,
Err(_) => return 0,
};
keys.open = Some(open);
}
1
}
extern "C" fn set_write_secret(
ssl: *mut Ssl,
level: tls::Level,
cipher: *const SslCipher,
secret: *const u8,
secret_len: usize,
) -> c_int {
let session_data = match get_sess_data_from_ptr::<tls::TlsSessionData>(ssl, *SESSION_DATA_INDEX)
{
Some(v) => v,
None => return 0,
};
trace!(
"{} set write secret level {:?}",
session_data.trace_id,
level
);
let keys = &mut session_data.key_collection[level];
let aead = match get_cipher_from_ptr(cipher) {
Ok(v) => v,
Err(_) => return 0,
};
if level != tls::Level::ZeroRTT || !session_data.is_server {
let secret = unsafe { slice::from_raw_parts(secret, secret_len) };
let seal = match crypto::Seal::new_with_secret(aead, secret.to_vec()) {
Ok(v) => v,
Err(_) => return 0,
};
keys.seal = Some(seal);
}
1
}
extern "C" fn add_handshake_data(
ssl: *mut Ssl,
level: tls::Level,
data: *const u8,
len: usize,
) -> c_int {
let session_data = match get_sess_data_from_ptr::<tls::TlsSessionData>(ssl, *SESSION_DATA_INDEX)
{
Some(v) => v,
None => return 0,
};
trace!(
"{} write message level {:?} len {}",
session_data.trace_id,
level,
len
);
let buf = unsafe { slice::from_raw_parts(data, len) };
if session_data.write_method.is_none()
|| (session_data.write_method.as_mut().unwrap())(level, buf).is_err()
{
return 0;
}
1
}
extern "C" fn flush_flight(_ssl: *mut Ssl) -> c_int {
1
}
extern "C" fn send_alert(ssl: *mut Ssl, level: tls::Level, alert: u8) -> c_int {
let session_data = match get_sess_data_from_ptr::<tls::TlsSessionData>(ssl, *SESSION_DATA_INDEX)
{
Some(v) => v,
None => return 0,
};
trace!(
"{} send alert level {:?} alert {:x}",
session_data.trace_id,
level,
alert
);
const TLS_ALERT_ERROR: u64 = 0x100;
let error: u64 = TLS_ALERT_ERROR + u64::from(alert);
session_data.error = Some(tls::TlsError {
error_code: error,
reason: Vec::new(),
});
1
}
extern "C" fn keylog(ssl: *mut Ssl, line: *const c_char) {
let session_data = match get_sess_data_from_ptr::<tls::TlsSessionData>(ssl, *SESSION_DATA_INDEX)
{
Some(v) => v,
None => return,
};
if let Some(keylog) = &mut session_data.keylog {
let data = unsafe { ffi::CStr::from_ptr(line).to_bytes() };
let mut full_line = Vec::with_capacity(data.len() + 1);
full_line.extend_from_slice(data);
full_line.push(b'\n');
keylog.write_all(&full_line[..]).ok();
}
}
extern "C" fn select_alpn(
ssl: *mut Ssl,
out: *mut *const u8,
out_len: *mut u8,
inp: *mut u8,
in_len: c_uint,
_arg: *mut c_void,
) -> c_int {
let session_data = match get_sess_data_from_ptr::<tls::TlsSessionData>(ssl, *SESSION_DATA_INDEX)
{
Some(v) => v,
None => return 3, };
let ctx = unsafe { SSL_get_SSL_CTX(ssl) };
let application_protos = match get_ctx_data_from_ptr::<Vec<Vec<u8>>>(ctx, *CONTEXT_DATA_INDEX) {
Some(v) => v,
None => return 3, };
if application_protos.is_empty() {
return 3; }
let mut protos = unsafe { slice::from_raw_parts(inp, in_len as usize) };
while let Ok(proto) = protos.read_with_u8_length() {
let found = application_protos.iter().any(|expected| {
trace!(
"{} peer ALPN {:?} expected {:?}",
session_data.trace_id,
std::str::from_utf8(proto.as_ref()),
std::str::from_utf8(expected.as_slice())
);
if expected.len() == proto.len() && expected.as_slice() == proto.as_slice() {
unsafe {
*out = expected.as_slice().as_ptr();
*out_len = expected.len() as u8;
}
return true;
}
false
});
if found {
return 0; }
}
3 }
extern "C" fn select_cert(ssl: *mut Ssl, _arg: *mut c_void) -> c_int {
let session_data = match get_sess_data_from_ptr::<tls::TlsSessionData>(ssl, *SESSION_DATA_INDEX)
{
Some(v) => v,
None => return 0,
};
let server_name = unsafe {
let ptr = SSL_get_servername(
ssl, 0, );
if ptr.is_null() {
trace!("{} no server name", session_data.trace_id);
return 1;
}
ffi::CStr::from_ptr(ptr)
};
let server_name = server_name.to_str();
if server_name.is_err() {
trace!("{} server name invalid", session_data.trace_id);
return 1;
}
let server_name = server_name.unwrap();
trace!("{} select cert for {}", session_data.trace_id, server_name);
if let Some(config_selector) = &session_data.conf_selector {
let tls_config = config_selector.select(server_name);
if tls_config.is_none() {
trace!(
"{} select cert for {} failed.",
session_data.trace_id,
server_name
);
return 0;
}
let tls_ctx = &tls_config.unwrap().tls_ctx;
let ssl_ctx = unsafe { SSL_set_SSL_CTX(ssl, tls_ctx.as_ptr()) };
if ssl_ctx.is_null() {
trace!("{} set SSL_CTX failed", session_data.trace_id);
return 0;
}
}
1
}
extern "C" fn new_session(ssl: *mut Ssl, ssl_session: *mut SslSession) -> c_int {
let session_data = match get_sess_data_from_ptr::<tls::TlsSessionData>(ssl, *SESSION_DATA_INDEX)
{
Some(v) => v,
None => return 0,
};
let session = Session::new(ssl);
let peer_params = session.quic_transport_params();
let session_bytes = unsafe {
let mut out: *mut u8 = std::ptr::null_mut();
let mut out_len: usize = 0;
if SSL_SESSION_to_bytes(ssl_session, &mut out, &mut out_len) == 0 {
return 0;
}
let session_bytes = std::slice::from_raw_parts(out, out_len).to_vec();
OPENSSL_free(out as *mut c_void);
session_bytes
};
let mut buffer = Vec::with_capacity(8 + peer_params.len() + 8 + session_bytes.len());
let session_bytes_len = session_bytes.len() as u64;
if buffer.write(&session_bytes_len.to_be_bytes()).is_err() {
std::mem::forget(session);
return 0;
}
if buffer.write(&session_bytes).is_err() {
std::mem::forget(session);
return 0;
}
let peer_params_len = peer_params.len() as u64;
if buffer.write(&peer_params_len.to_be_bytes()).is_err() {
std::mem::forget(session);
return 0;
}
if buffer.write(peer_params).is_err() {
std::mem::forget(session);
return 0;
}
session_data.session = Some(buffer);
std::mem::forget(session);
0
}
fn map_result_ptr<'a, T>(bssl_result: *const T) -> Result<&'a T> {
match unsafe { bssl_result.as_ref() } {
Some(v) => Ok(v),
None => Err(Error::TlsFail("pointer as reference error".to_string())),
}
}
fn get_ssl_error() -> Result<String> {
let err = [0; 1024];
unsafe {
let e = ERR_peek_error();
ERR_error_string_n(e, err.as_ptr(), err.len());
}
let err = std::str::from_utf8(&err)
.map_err(|e| Error::TlsFail(format!("ssl error message format incorrect: {:?}", e)))?;
Ok(err.trim_end_matches('\0').to_string())
}
extern "C" {
fn TLS_method() -> *const SslMethod;
fn SSL_CTX_new(method: *const SslMethod) -> *mut SslCtx;
fn SSL_CTX_free(ctx: *mut SslCtx);
fn SSL_CTX_use_certificate_chain_file(ctx: *mut SslCtx, file: *const c_char) -> c_int;
fn SSL_CTX_use_PrivateKey_file(ctx: *mut SslCtx, file: *const c_char, ty: c_int) -> c_int;
fn SSL_CTX_load_verify_locations(
ctx: *mut SslCtx,
file: *const c_char,
path: *const c_char,
) -> c_int;
fn SSL_CTX_set_default_verify_paths(ctx: *mut SslCtx) -> c_int;
fn SSL_CTX_get_cert_store(ctx: *mut SslCtx) -> *mut X509Store;
fn SSL_CTX_set_verify(ctx: *mut SslCtx, mode: c_int, cb: *const c_void);
fn SSL_CTX_set_keylog_callback(
ctx: *mut SslCtx,
cb: extern "C" fn(ssl: *mut Ssl, line: *const c_char),
);
fn SSL_CTX_set_tlsext_ticket_keys(ctx: *mut SslCtx, key: *const u8, key_len: usize) -> c_int;
fn SSL_CTX_set_alpn_protos(ctx: *mut SslCtx, protos: *const u8, protos_len: usize) -> c_int;
fn SSL_CTX_set_alpn_select_cb(
ctx: *mut SslCtx,
cb: extern "C" fn(
ssl: *mut Ssl,
out: *mut *const u8,
out_len: *mut u8,
inp: *mut u8,
in_len: c_uint,
arg: *mut c_void,
) -> c_int,
arg: *mut c_void,
);
fn SSL_CTX_set_early_data_enabled(ctx: *mut SslCtx, enabled: i32);
fn SSL_CTX_set_session_psk_dhe_timeout(ctx: *mut SslCtx, timeout: u32);
fn SSL_CTX_set_session_cache_mode(ctx: *mut SslCtx, mode: c_int) -> c_int;
fn SSL_CTX_sess_set_new_cb(
ctx: *mut SslCtx,
cb: extern "C" fn(ssl: *mut Ssl, session: *mut SslSession) -> c_int,
);
fn SSL_CTX_get_ex_new_index(
argl: c_long,
argp: *const c_void,
unused: *const c_void,
dup_unused: *const c_void,
free_func: extern "C" fn(
parent: *mut c_void,
ptr: *mut c_void,
ad: *mut CryptoExData,
index: c_int,
arg1: c_long,
argp: *mut c_void,
),
) -> c_int;
fn SSL_CTX_set_ex_data(ctx: *mut SslCtx, idx: c_int, ptr: *const c_void) -> c_int;
fn SSL_CTX_get_ex_data(ctx: *mut SslCtx, idx: c_int) -> *mut c_void;
fn SSL_set_SSL_CTX(ssl: *mut Ssl, ssl_ctx: *const SslCtx) -> *mut SslCtx;
fn SSL_get_SSL_CTX(ssl: *const Ssl) -> *mut SslCtx;
fn SSL_get_ex_new_index(
argl: c_long,
argp: *const c_void,
unused: *const c_void,
dup_unused: *const c_void,
free_func: *const c_void,
) -> c_int;
fn SSL_new(ctx: *const SslCtx) -> *mut Ssl;
fn SSL_set_cert_cb(
ssl: *mut Ssl,
cb: extern "C" fn(ssl: *mut Ssl, arg: *mut c_void) -> c_int,
arg: *mut c_void,
);
fn SSL_set_accept_state(ssl: *mut Ssl);
fn SSL_set_connect_state(ssl: *mut Ssl);
fn SSL_set_ex_data(ssl: *mut Ssl, idx: c_int, ptr: *const c_void) -> c_int;
fn SSL_get_ex_data(ssl: *mut Ssl, idx: c_int) -> *mut c_void;
fn SSL_set_quic_transport_params(ssl: *mut Ssl, params: *const u8, params_len: usize) -> c_int;
fn SSL_get_peer_quic_transport_params(
ssl: *const Ssl,
out_params: *mut *const u8,
out_params_len: *mut usize,
);
fn SSL_set_session(ssl: *mut Ssl, session: *mut SslSession) -> c_int;
fn SSL_set_min_proto_version(ssl: *mut Ssl, version: u16);
fn SSL_set_max_proto_version(ssl: *mut Ssl, version: u16);
fn SSL_set_quiet_shutdown(ssl: *mut Ssl, mode: c_int);
fn SSL_set_tlsext_host_name(ssl: *mut Ssl, name: *const c_char) -> c_int;
fn SSL_set_quic_method(ssl: *mut Ssl, quic_method: *const SslQuicMethod) -> c_int;
fn SSL_set_quic_early_data_context(
ssl: *mut Ssl,
context: *const u8,
context_len: usize,
) -> c_int;
fn SSL_provide_quic_data(
ssl: *mut Ssl,
level: tls::Level,
data: *const u8,
len: usize,
) -> c_int;
fn SSL_process_quic_post_handshake(ssl: *mut Ssl) -> c_int;
fn SSL_reset_early_data_reject(ssl: *mut Ssl);
fn SSL_do_handshake(ssl: *mut Ssl) -> c_int;
fn SSL_quic_write_level(ssl: *const Ssl) -> tls::Level;
fn SSL_session_reused(ssl: *const Ssl) -> c_int;
fn SSL_in_init(ssl: *const Ssl) -> c_int;
fn SSL_in_early_data(ssl: *const Ssl) -> c_int;
fn SSL_get_error(ssl: *const Ssl, ret_code: c_int) -> c_int;
fn SSL_get_current_cipher(ssl: *const Ssl) -> *const SslCipher;
fn SSL_get_curve_id(ssl: *const Ssl) -> u16;
fn SSL_get_curve_name(curve: u16) -> *const c_char;
fn SSL_get_peer_signature_algorithm(ssl: *const Ssl) -> u16;
fn SSL_get_signature_algorithm_name(sigalg: u16, include_curve: i32) -> *const c_char;
fn SSL_get0_param(ssl: *mut Ssl) -> *mut X509VerifyParam;
fn SSL_get0_peer_certificates(ssl: *const Ssl) -> *const StackOf;
fn SSL_get0_alpn_selected(ssl: *const Ssl, out: *mut *const u8, out_len: *mut u32);
fn SSL_get_servername(ssl: *const Ssl, ty: c_int) -> *const c_char;
fn SSL_get_early_data_reason(ssl: *const Ssl) -> SslEarlyDataReason;
fn SSL_early_data_reason_string(reason: SslEarlyDataReason) -> *const c_char;
fn SSL_clear(ssl: *mut Ssl) -> c_int;
fn SSL_free(ssl: *mut Ssl);
fn SSL_CIPHER_get_id(cipher: *const SslCipher) -> c_uint;
fn SSL_SESSION_to_bytes(
session: *const SslSession,
out: *mut *mut u8,
out_len: *mut usize,
) -> c_int;
fn SSL_SESSION_from_bytes(
input: *const u8,
input_len: usize,
ctx: *const SslCtx,
) -> *mut SslSession;
fn SSL_SESSION_free(session: *mut SslSession);
fn X509_STORE_add_cert(ctx: *mut X509Store, x: *mut X509) -> c_int;
fn X509_free(x: *mut X509);
fn d2i_X509(px: *mut X509, input: *const *const u8, len: c_int) -> *mut X509;
fn X509_VERIFY_PARAM_set1_host(
param: *mut X509VerifyParam,
name: *const c_char,
namelen: usize,
) -> c_int;
fn sk_num(stack: *const StackOf) -> c_int;
fn sk_value(stack: *const StackOf, idx: c_int) -> *mut c_void;
fn CRYPTO_BUFFER_len(buffer: *const CryptoBuffer) -> usize;
fn CRYPTO_BUFFER_data(buffer: *const CryptoBuffer) -> *const u8;
fn ERR_peek_error() -> c_uint;
fn ERR_error_string_n(err: c_uint, buf: *const u8, len: usize);
fn OPENSSL_free(ptr: *mut c_void);
}