use cfg_if::cfg_if; use foreign_types::ForeignType; use foreign_types::ForeignTypeRef; #[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))] use libc::c_char; #[cfg(ossl111)] use libc::size_t; use libc::{c_int, c_uchar, c_uint, c_void}; #[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))] use std::ffi::CStr; use std::mem; use std::ptr; use std::slice; #[cfg(ossl111)] use std::str; use std::sync::Arc; use crate::dh::Dh; #[cfg(all(ossl101, not(ossl110)))] use crate::ec::EcKey; use crate::error::ErrorStack; use crate::pkey::Params; #[cfg(any(ossl102, libressl261))] use crate::ssl::AlpnError; use crate::ssl::{ try_get_session_ctx_index, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, SslSessionRef, }; #[cfg(ossl111)] use crate::ssl::{ClientHelloResponse, ExtensionContext}; #[cfg(ossl111)] use crate::util::ForeignTypeRefExt; #[cfg(ossl111)] use crate::x509::X509Ref; use crate::x509::{X509StoreContext, X509StoreContextRef}; pub extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, { unsafe { let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx); let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing"); let verify_idx = SslContext::cached_ex_index::(); // raw pointer shenanigans to break the borrow of ctx // the callback can't mess with its own ex_data slot so this is safe let verify = ctx .ex_data(ssl_idx) .expect("BUG: store context missing ssl") .ssl_context() .ex_data(verify_idx) .expect("BUG: verify callback missing") as *const F; (*verify)(preverify_ok != 0, ctx) as c_int } } #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] pub extern "C" fn raw_client_psk( ssl: *mut ffi::SSL, hint: *const c_char, identity: *mut c_char, max_identity_len: c_uint, psk: *mut c_uchar, max_psk_len: c_uint, ) -> c_uint where F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result + 'static + Sync + Send, { unsafe { let ssl = SslRef::from_ptr_mut(ssl); let callback_idx = SslContext::cached_ex_index::(); let callback = ssl .ssl_context() .ex_data(callback_idx) .expect("BUG: psk callback missing") as *const F; let hint = if !hint.is_null() { Some(CStr::from_ptr(hint).to_bytes()) } else { None }; // Give the callback mutable slices into which it can write the identity and psk. let identity_sl = slice::from_raw_parts_mut(identity as *mut u8, max_identity_len as usize); #[allow(clippy::unnecessary_cast)] let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize); match (*callback)(ssl, hint, identity_sl, psk_sl) { Ok(psk_len) => psk_len as u32, Err(e) => { e.put(); 0 } } } } #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] pub extern "C" fn raw_server_psk( ssl: *mut ffi::SSL, identity: *const c_char, psk: *mut c_uchar, max_psk_len: c_uint, ) -> c_uint where F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result + 'static + Sync + Send, { unsafe { let ssl = SslRef::from_ptr_mut(ssl); let callback_idx = SslContext::cached_ex_index::(); let callback = ssl .ssl_context() .ex_data(callback_idx) .expect("BUG: psk callback missing") as *const F; let identity = if identity.is_null() { None } else { Some(CStr::from_ptr(identity).to_bytes()) }; // Give the callback mutable slices into which it can write the psk. #[allow(clippy::unnecessary_cast)] let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize); match (*callback)(ssl, identity, psk_sl) { Ok(psk_len) => psk_len as u32, Err(e) => { e.put(); 0 } } } } pub extern "C" fn ssl_raw_verify( preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX, ) -> c_int where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, { unsafe { let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx); let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing"); let callback_idx = Ssl::cached_ex_index::>(); let callback = ctx .ex_data(ssl_idx) .expect("BUG: store context missing ssl") .ex_data(callback_idx) .expect("BUG: ssl verify callback missing") .clone(); callback(preverify_ok != 0, ctx) as c_int } } pub extern "C" fn raw_sni(ssl: *mut ffi::SSL, al: *mut c_int, arg: *mut c_void) -> c_int where F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send, { unsafe { let ssl = SslRef::from_ptr_mut(ssl); let callback = arg as *const F; let mut alert = SslAlert(*al); let r = (*callback)(ssl, &mut alert); *al = alert.0; match r { Ok(()) => ffi::SSL_TLSEXT_ERR_OK, Err(e) => e.0, } } } #[cfg(any(ossl102, libressl261))] pub extern "C" fn raw_alpn_select( ssl: *mut ffi::SSL, out: *mut *const c_uchar, outlen: *mut c_uchar, inbuf: *const c_uchar, inlen: c_uint, _arg: *mut c_void, ) -> c_int where F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, { unsafe { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: alpn callback missing") as *const F; #[allow(clippy::unnecessary_cast)] let protos = slice::from_raw_parts(inbuf as *const u8, inlen as usize); match (*callback)(ssl, protos) { Ok(proto) => { *out = proto.as_ptr() as *const c_uchar; *outlen = proto.len() as c_uchar; ffi::SSL_TLSEXT_ERR_OK } Err(e) => e.0, } } } pub unsafe extern "C" fn raw_tmp_dh( ssl: *mut ffi::SSL, is_export: c_int, keylength: c_int, ) -> *mut ffi::DH where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: tmp dh callback missing") as *const F; match (*callback)(ssl, is_export != 0, keylength as u32) { Ok(dh) => { let ptr = dh.as_ptr(); mem::forget(dh); ptr } Err(e) => { e.put(); ptr::null_mut() } } } #[cfg(all(ossl101, not(ossl110)))] pub unsafe extern "C" fn raw_tmp_ecdh( ssl: *mut ffi::SSL, is_export: c_int, keylength: c_int, ) -> *mut ffi::EC_KEY where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: tmp ecdh callback missing") as *const F; match (*callback)(ssl, is_export != 0, keylength as u32) { Ok(ec_key) => { let ptr = ec_key.as_ptr(); mem::forget(ec_key); ptr } Err(e) => { e.put(); ptr::null_mut() } } } pub unsafe extern "C" fn raw_tmp_dh_ssl( ssl: *mut ffi::SSL, is_export: c_int, keylength: c_int, ) -> *mut ffi::DH where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ex_data(Ssl::cached_ex_index::>()) .expect("BUG: ssl tmp dh callback missing") .clone(); match callback(ssl, is_export != 0, keylength as u32) { Ok(dh) => { let ptr = dh.as_ptr(); mem::forget(dh); ptr } Err(e) => { e.put(); ptr::null_mut() } } } #[cfg(all(ossl101, not(ossl110)))] pub unsafe extern "C" fn raw_tmp_ecdh_ssl( ssl: *mut ffi::SSL, is_export: c_int, keylength: c_int, ) -> *mut ffi::EC_KEY where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ex_data(Ssl::cached_ex_index::>()) .expect("BUG: ssl tmp ecdh callback missing") .clone(); match callback(ssl, is_export != 0, keylength as u32) { Ok(ec_key) => { let ptr = ec_key.as_ptr(); mem::forget(ec_key); ptr } Err(e) => { e.put(); ptr::null_mut() } } } pub unsafe extern "C" fn raw_tlsext_status(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int where F: Fn(&mut SslRef) -> Result + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: ocsp callback missing") as *const F; let ret = (*callback)(ssl); if ssl.is_server() { match ret { Ok(true) => ffi::SSL_TLSEXT_ERR_OK, Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK, Err(e) => { e.put(); ffi::SSL_TLSEXT_ERR_ALERT_FATAL } } } else { match ret { Ok(true) => 1, Ok(false) => 0, Err(e) => { e.put(); -1 } } } } pub unsafe extern "C" fn raw_new_session( ssl: *mut ffi::SSL, session: *mut ffi::SSL_SESSION, ) -> c_int where F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send, { let session_ctx_index = try_get_session_ctx_index().expect("BUG: session context index initialization failed"); let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ex_data(*session_ctx_index) .expect("BUG: session context missing") .ex_data(SslContext::cached_ex_index::()) .expect("BUG: new session callback missing") as *const F; let session = SslSession::from_ptr(session); (*callback)(ssl, session); // the return code doesn't indicate error vs success, but whether or not we consumed the session 1 } pub unsafe extern "C" fn raw_remove_session( ctx: *mut ffi::SSL_CTX, session: *mut ffi::SSL_SESSION, ) where F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send, { let ctx = SslContextRef::from_ptr(ctx); let callback = ctx .ex_data(SslContext::cached_ex_index::()) .expect("BUG: remove session callback missing"); let session = SslSessionRef::from_ptr(session); callback(ctx, session) } cfg_if! { if #[cfg(any(ossl110, libressl280, boringssl))] { type DataPtr = *const c_uchar; } else { type DataPtr = *mut c_uchar; } } pub unsafe extern "C" fn raw_get_session( ssl: *mut ffi::SSL, data: DataPtr, len: c_int, copy: *mut c_int, ) -> *mut ffi::SSL_SESSION where F: Fn(&mut SslRef, &[u8]) -> Option + 'static + Sync + Send, { let session_ctx_index = try_get_session_ctx_index().expect("BUG: session context index initialization failed"); let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ex_data(*session_ctx_index) .expect("BUG: session context missing") .ex_data(SslContext::cached_ex_index::()) .expect("BUG: get session callback missing") as *const F; #[allow(clippy::unnecessary_cast)] let data = slice::from_raw_parts(data as *const u8, len as usize); match (*callback)(ssl, data) { Some(session) => { let p = session.as_ptr(); mem::forget(session); *copy = 0; p } None => ptr::null_mut(), } } #[cfg(ossl111)] pub unsafe extern "C" fn raw_keylog(ssl: *const ffi::SSL, line: *const c_char) where F: Fn(&SslRef, &str) + 'static + Sync + Send, { let ssl = SslRef::from_const_ptr(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: get session callback missing"); let line = CStr::from_ptr(line).to_bytes(); let line = str::from_utf8_unchecked(line); callback(ssl, line); } #[cfg(ossl111)] pub unsafe extern "C" fn raw_stateless_cookie_generate( ssl: *mut ffi::SSL, cookie: *mut c_uchar, cookie_len: *mut size_t, ) -> c_int where F: Fn(&mut SslRef, &mut [u8]) -> Result + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: stateless cookie generate callback missing") as *const F; #[allow(clippy::unnecessary_cast)] let slice = slice::from_raw_parts_mut(cookie as *mut u8, ffi::SSL_COOKIE_LENGTH as usize); match (*callback)(ssl, slice) { Ok(len) => { *cookie_len = len as size_t; 1 } Err(e) => { e.put(); 0 } } } #[cfg(ossl111)] pub unsafe extern "C" fn raw_stateless_cookie_verify( ssl: *mut ffi::SSL, cookie: *const c_uchar, cookie_len: size_t, ) -> c_int where F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: stateless cookie verify callback missing") as *const F; #[allow(clippy::unnecessary_cast)] let slice = slice::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len); (*callback)(ssl, slice) as c_int } #[cfg(not(boringssl))] pub extern "C" fn raw_cookie_generate( ssl: *mut ffi::SSL, cookie: *mut c_uchar, cookie_len: *mut c_uint, ) -> c_int where F: Fn(&mut SslRef, &mut [u8]) -> Result + 'static + Sync + Send, { unsafe { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: cookie generate callback missing") as *const F; // We subtract 1 from DTLS1_COOKIE_LENGTH as the ostensible value, 256, is erroneous but retained for // compatibility. See comments in dtls1.h. #[allow(clippy::unnecessary_cast)] let slice = slice::from_raw_parts_mut(cookie as *mut u8, ffi::DTLS1_COOKIE_LENGTH as usize - 1); match (*callback)(ssl, slice) { Ok(len) => { *cookie_len = len as c_uint; 1 } Err(e) => { e.put(); 0 } } } } #[cfg(not(boringssl))] cfg_if! { if #[cfg(any(ossl110, libressl280))] { type CookiePtr = *const c_uchar; } else { type CookiePtr = *mut c_uchar; } } #[cfg(not(boringssl))] pub extern "C" fn raw_cookie_verify( ssl: *mut ffi::SSL, cookie: CookiePtr, cookie_len: c_uint, ) -> c_int where F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send, { unsafe { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: cookie verify callback missing") as *const F; #[allow(clippy::unnecessary_cast)] let slice = slice::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len as usize); (*callback)(ssl, slice) as c_int } } #[cfg(ossl111)] pub struct CustomExtAddState(Option); #[cfg(ossl111)] pub extern "C" fn raw_custom_ext_add( ssl: *mut ffi::SSL, _: c_uint, context: c_uint, out: *mut *const c_uchar, outlen: *mut size_t, x: *mut ffi::X509, chainidx: size_t, al: *mut c_int, _: *mut c_void, ) -> c_int where F: Fn(&mut SslRef, ExtensionContext, Option<(usize, &X509Ref)>) -> Result, SslAlert> + 'static + Sync + Send, T: AsRef<[u8]> + 'static + Sync + Send, { unsafe { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: custom ext add callback missing") as *const F; let ectx = ExtensionContext::from_bits_truncate(context); let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) { Some((chainidx, X509Ref::from_ptr(x))) } else { None }; match (*callback)(ssl, ectx, cert) { Ok(None) => 0, Ok(Some(buf)) => { *outlen = buf.as_ref().len(); *out = buf.as_ref().as_ptr(); let idx = Ssl::cached_ex_index::>(); let mut buf = Some(buf); let new = match ssl.ex_data_mut(idx) { Some(state) => { state.0 = buf.take(); false } None => true, }; if new { ssl.set_ex_data(idx, CustomExtAddState(buf)); } 1 } Err(alert) => { *al = alert.0; -1 } } } } #[cfg(ossl111)] pub extern "C" fn raw_custom_ext_free( ssl: *mut ffi::SSL, _: c_uint, _: c_uint, _: *const c_uchar, _: *mut c_void, ) where T: 'static + Sync + Send, { unsafe { let ssl = SslRef::from_ptr_mut(ssl); let idx = Ssl::cached_ex_index::>(); if let Some(state) = ssl.ex_data_mut(idx) { state.0 = None; } } } #[cfg(ossl111)] pub extern "C" fn raw_custom_ext_parse( ssl: *mut ffi::SSL, _: c_uint, context: c_uint, input: *const c_uchar, inlen: size_t, x: *mut ffi::X509, chainidx: size_t, al: *mut c_int, _: *mut c_void, ) -> c_int where F: Fn(&mut SslRef, ExtensionContext, &[u8], Option<(usize, &X509Ref)>) -> Result<(), SslAlert> + 'static + Sync + Send, { unsafe { let ssl = SslRef::from_ptr_mut(ssl); let callback = ssl .ssl_context() .ex_data(SslContext::cached_ex_index::()) .expect("BUG: custom ext parse callback missing") as *const F; let ectx = ExtensionContext::from_bits_truncate(context); #[allow(clippy::unnecessary_cast)] let slice = slice::from_raw_parts(input as *const u8, inlen); let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) { Some((chainidx, X509Ref::from_ptr(x))) } else { None }; match (*callback)(ssl, ectx, slice, cert) { Ok(()) => 1, Err(alert) => { *al = alert.0; 0 } } } } #[cfg(ossl111)] pub unsafe extern "C" fn raw_client_hello( ssl: *mut ffi::SSL, al: *mut c_int, arg: *mut c_void, ) -> c_int where F: Fn(&mut SslRef, &mut SslAlert) -> Result + 'static + Sync + Send, { let ssl = SslRef::from_ptr_mut(ssl); let callback = arg as *const F; let mut alert = SslAlert(*al); let r = (*callback)(ssl, &mut alert); *al = alert.0; match r { Ok(c) => c.0, Err(e) => { e.put(); ffi::SSL_CLIENT_HELLO_ERROR } } }