use foreign_types::ForeignTypeRef;
use libc::*;
use openssl::error::ErrorStack;
use openssl::pkey::{HasPrivate, PKeyRef};
use openssl::ssl::{Ssl, SslAcceptor, SslRef};
use openssl::x509::store::X509StoreRef;
use openssl::x509::verify::X509VerifyParamRef;
use openssl::x509::X509Ref;
use openssl_sys::{
SSL_ctrl, EVP_PKEY, SSL, SSL_CTRL_SET_GROUPS_LIST, SSL_CTRL_SET_VERIFY_CERT_STORE, X509,
X509_VERIFY_PARAM,
};
use std::ffi::CString;
use std::os::raw;
fn cvt(r: c_long) -> Result<c_long, ErrorStack> {
if r != 1 {
Err(ErrorStack::get())
} else {
Ok(r)
}
}
extern "C" {
pub fn X509_VERIFY_PARAM_add1_host(
param: *mut X509_VERIFY_PARAM,
name: *const c_char,
namelen: size_t,
) -> c_int;
pub fn SSL_use_certificate(ssl: *mut SSL, cert: *mut X509) -> c_int;
pub fn SSL_use_PrivateKey(ssl: *mut SSL, key: *mut EVP_PKEY) -> c_int;
pub fn SSL_set_cert_cb(
ssl: *mut SSL,
cb: ::std::option::Option<
unsafe extern "C" fn(ssl: *mut SSL, arg: *mut raw::c_void) -> raw::c_int,
>,
arg: *mut raw::c_void,
);
}
pub fn add_host(verify_param: &mut X509VerifyParamRef, host: &str) -> Result<(), ErrorStack> {
if host.is_empty() {
return Ok(());
}
unsafe {
cvt(X509_VERIFY_PARAM_add1_host(
verify_param.as_ptr(),
host.as_ptr() as *const c_char,
host.len(),
) as c_long)
.map(|_| ())
}
}
pub fn ssl_set_verify_cert_store(
ssl: &mut SslRef,
cert_store: &X509StoreRef,
) -> Result<(), ErrorStack> {
unsafe {
cvt(SSL_ctrl(
ssl.as_ptr(),
SSL_CTRL_SET_VERIFY_CERT_STORE,
1, cert_store.as_ptr() as *mut c_void,
))?;
}
Ok(())
}
pub fn ssl_use_certificate(ssl: &mut SslRef, cert: &X509Ref) -> Result<(), ErrorStack> {
unsafe {
cvt(SSL_use_certificate(ssl.as_ptr(), cert.as_ptr()) as c_long)?;
}
Ok(())
}
pub fn ssl_use_private_key<T>(ssl: &mut SslRef, key: &PKeyRef<T>) -> Result<(), ErrorStack>
where
T: HasPrivate,
{
unsafe {
cvt(SSL_use_PrivateKey(ssl.as_ptr(), key.as_ptr()) as c_long)?;
}
Ok(())
}
pub fn ssl_add_chain_cert(ssl: &mut SslRef, cert: &X509Ref) -> Result<(), ErrorStack> {
const SSL_CTRL_CHAIN_CERT: i32 = 89;
unsafe {
cvt(SSL_ctrl(
ssl.as_ptr(),
SSL_CTRL_CHAIN_CERT,
1, cert.as_ptr() as *mut c_void,
))?;
}
Ok(())
}
pub fn ssl_set_renegotiate_mode_freely(_ssl: &mut SslRef) {}
pub fn ssl_set_groups_list(ssl: &mut SslRef, groups: &str) -> Result<(), ErrorStack> {
if groups.contains('\0') {
return Err(ErrorStack::get());
}
let groups = CString::new(groups).map_err(|_| ErrorStack::get())?;
unsafe {
cvt(SSL_ctrl(
ssl.as_ptr(),
SSL_CTRL_SET_GROUPS_LIST,
0,
groups.as_ptr() as *mut c_void,
))?;
}
Ok(())
}
pub fn ssl_use_second_key_share(_ssl: &mut SslRef, _enabled: bool) {}
pub fn clear_error_stack() {
let _ = ErrorStack::get();
}
pub fn ssl_from_acceptor(acceptor: &SslAcceptor) -> Result<Ssl, ErrorStack> {
Ssl::new(acceptor.context())
}
pub fn suspend_when_need_ssl_cert(ssl: &mut SslRef) {
unsafe {
SSL_set_cert_cb(ssl.as_ptr(), Some(raw_cert_block), std::ptr::null_mut());
}
}
pub fn unblock_ssl_cert(ssl: &mut SslRef) {
unsafe {
SSL_set_cert_cb(ssl.as_ptr(), None, std::ptr::null_mut());
}
}
extern "C" fn raw_cert_block(_ssl: *mut openssl_sys::SSL, _arg: *mut c_void) -> c_int {
-1
}
pub fn is_suspended_for_cert(error: &openssl::ssl::Error) -> bool {
error.code().as_raw() == openssl_sys::SSL_ERROR_WANT_X509_LOOKUP
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn ssl_mut(ssl: &SslRef) -> &mut SslRef {
SslRef::from_ptr_mut(ssl.as_ptr())
}
#[cfg(test)]
mod tests {
use super::*;
use openssl::ssl::{SslContextBuilder, SslMethod};
#[test]
fn test_ssl_set_groups_list() {
let ctx_builder = SslContextBuilder::new(SslMethod::tls()).unwrap();
let ssl = Ssl::new(&ctx_builder.build()).unwrap();
let ssl_ref = unsafe { ssl_mut(&ssl) };
assert!(ssl_set_groups_list(ssl_ref, "P-256:P-384").is_ok());
assert!(ssl_set_groups_list(ssl_ref, "P-256\0P-384").is_err());
}
}