From fcf69a56f4249b2e484495a94099d9cfb772f987 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Mon, 8 Apr 2024 14:05:45 +0100 Subject: [PATCH] Implement `SSL_set0_chain`, `SSL_set1_chain` & `SSL_clear_chain_certs` --- rustls-libssl/src/entry.rs | 94 ++++++++++++++++++++++++++------------ rustls-libssl/src/lib.rs | 4 ++ rustls-libssl/src/sign.rs | 19 ++++++-- rustls-libssl/src/x509.rs | 29 ++++++++++++ 4 files changed, 114 insertions(+), 32 deletions(-) diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index acf0ddc..d28ebc7 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -26,7 +26,7 @@ use crate::ffi::{ try_mut_slice_int, try_ref_from_ptr, try_slice, try_slice_int, try_str, Castable, OwnershipArc, OwnershipRef, }; -use crate::x509::{load_certs, OwnedX509}; +use crate::x509::{load_certs, OwnedX509, OwnedX509Stack}; use crate::ShutdownResult; /// Makes a entry function definition. @@ -230,6 +230,23 @@ entry! { inner.set_servername_callback_context(parg); C_INT_SUCCESS as c_long } + Ok(SslCtrl::SetChain) => { + let chain = if parg.is_null() { + // this is `SSL_CTX_clear_chain_certs` + vec![] + } else { + match larg { + // this is `SSL_CTX_set1_chain` (incs ref) + 1 => OwnedX509Stack::new_copy(parg as *mut stack_st_X509).to_rustls(), + // this is `SSL_CTX_set0_chain` (retain ref) + _ => OwnedX509Stack::new(parg as *mut stack_st_X509).to_rustls(), + } + }; + + inner.stage_certificate_chain(chain); + C_INT_SUCCESS as i64 + } + Err(()) => { log::warn!("unimplemented _SSL_CTX_ctrl(..., {cmd}, {larg}, ...)"); 0 @@ -499,12 +516,12 @@ entry! { return Error::null_pointer().raise().into(); } - let chain = vec![CertificateDer::from(OwnedX509::new(x).der_bytes())]; + let ee = CertificateDer::from(OwnedX509::new(x).der_bytes()); match ctx .lock() .map_err(|_| Error::cannot_lock()) - .map(|mut ctx| ctx.stage_certificate_chain(chain)) + .map(|mut ctx| ctx.stage_certificate_end(ee)) { Err(e) => e.raise().into(), Ok(()) => C_INT_SUCCESS, @@ -802,33 +819,51 @@ entry! { pub fn _SSL_ctrl(ssl: *mut SSL, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long { let ssl = try_clone_arc!(ssl); - match SslCtrl::try_from(cmd) { - Ok(SslCtrl::Mode) => { - log::warn!("unimplemented SSL_set_mode()"); - 0 - } - Ok(SslCtrl::SetMsgCallbackArg) => { - log::warn!("unimplemented SSL_set_msg_callback_arg()"); - 0 - } - Ok(SslCtrl::SetMaxProtoVersion) => { - log::warn!("unimplemented SSL_set_max_proto_version()"); - 1 - } - Ok(SslCtrl::SetTlsExtHostname) => { - let hostname = try_str!(parg as *const c_char); - ssl.lock() - .ok() - .map(|mut ssl| ssl.set_sni_hostname(hostname)) - .unwrap_or_default() as c_long - } - // not a defined operation in the OpenSSL API - Ok(SslCtrl::SetTlsExtServerNameCallback) | Ok(SslCtrl::SetTlsExtServerNameArg) => 0, - Err(()) => { - log::warn!("unimplemented _SSL_ctrl(..., {cmd}, {larg}, ...)"); - 0 + let result = if let Ok(mut inner) = ssl.lock() { + match SslCtrl::try_from(cmd) { + Ok(SslCtrl::Mode) => { + log::warn!("unimplemented SSL_set_mode()"); + 0 + } + Ok(SslCtrl::SetMsgCallbackArg) => { + log::warn!("unimplemented SSL_set_msg_callback_arg()"); + 0 + } + Ok(SslCtrl::SetMaxProtoVersion) => { + log::warn!("unimplemented SSL_set_max_proto_version()"); + 1 + } + Ok(SslCtrl::SetTlsExtHostname) => { + let hostname = try_str!(parg as *const c_char); + inner.set_sni_hostname(hostname) as c_long + } + Ok(SslCtrl::SetChain) => { + let chain = if parg.is_null() { + // this is `SSL_clear_chain_certs` + vec![] + } else { + match larg { + // this is `SSL_set1_chain` (incs ref) + 1 => OwnedX509Stack::new_copy(parg as *mut stack_st_X509).to_rustls(), + // this is `SSL_set0_chain` (retain ref) + _ => OwnedX509Stack::new(parg as *mut stack_st_X509).to_rustls(), + } + }; + + inner.stage_certificate_chain(chain); + C_INT_SUCCESS as i64 + } + // not a defined operation in the OpenSSL API + Ok(SslCtrl::SetTlsExtServerNameCallback) | Ok(SslCtrl::SetTlsExtServerNameArg) => 0, + Err(()) => { + log::warn!("unimplemented _SSL_ctrl(..., {cmd}, {larg}, ...)"); + 0 + } } - } + } else { + 0 + }; + result } } @@ -1686,6 +1721,7 @@ num_enum! { SetTlsExtServerNameCallback = 53, SetTlsExtServerNameArg = 54, SetTlsExtHostname = 55, + SetChain = 88, SetMaxProtoVersion = 124, } } diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index b11458f..a5fae04 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -351,6 +351,10 @@ impl SslContext { self.cert_callback = callbacks::CertCallbackConfig { cb, context }; } + fn stage_certificate_end(&mut self, end: CertificateDer<'static>) { + self.auth_keys.stage_certificate_end(end) + } + fn stage_certificate_chain(&mut self, chain: Vec>) { self.auth_keys.stage_certificate_chain(chain) } diff --git a/rustls-libssl/src/sign.rs b/rustls-libssl/src/sign.rs index a0f9f20..c7390c7 100644 --- a/rustls-libssl/src/sign.rs +++ b/rustls-libssl/src/sign.rs @@ -23,6 +23,10 @@ pub struct CertifiedKeySet { /// `SSL_CTX_use_PrivateKey_file`. pending_cert_chain: Option>>, + /// Last `SSL_CTX_use_certificate` result, prepended to chain during commit. + /// May be absent. + pending_cert_end: Option>, + /// The key and certificate we're currently using. /// /// TODO: support multiple key types, and demultiplex them by type. @@ -34,10 +38,19 @@ impl CertifiedKeySet { self.pending_cert_chain = Some(chain); } + pub fn stage_certificate_end(&mut self, end: CertificateDer<'static>) { + self.pending_cert_end = Some(end); + } + pub fn commit_private_key(&mut self, key: EvpPkey) -> Result<(), error::Error> { - let chain = match self.pending_cert_chain.take() { - Some(chain) => chain, - None => { + let chain = match (self.pending_cert_end.take(), self.pending_cert_chain.take()) { + (Some(end), Some(mut chain)) => { + chain.insert(0, end); + chain + } + (None, Some(chain)) => chain, + (Some(end), None) => vec![end], + (None, None) => { return Err(error::Error::bad_data("no certificate found for key")); } }; diff --git a/rustls-libssl/src/x509.rs b/rustls-libssl/src/x509.rs index 4364ad8..b093cc2 100644 --- a/rustls-libssl/src/x509.rs +++ b/rustls-libssl/src/x509.rs @@ -27,6 +27,35 @@ impl OwnedX509Stack { } } + /// Make one by adopting ownership of an existing pointer. + pub fn new(raw: *mut stack_st_X509) -> Self { + Self { raw } + } + + /// Make one by copying existing stack. + pub fn new_copy(other: *const stack_st_X509) -> Self { + let mut ret = Self::empty(); + + let len = match unsafe { OPENSSL_sk_num(other as *const OPENSSL_STACK) } { + -1 => 0, + x => x as usize, + }; + + for index in 0..len { + let item_ptr = unsafe { + OPENSSL_sk_value(other as *const OPENSSL_STACK, index as c_int) as *mut X509 + }; + // item_ptr belongs to caller. + let item = OwnedX509::new(item_ptr); + // item belongs to `OwnedX509` -- ensure caller's ref is not stolen + item.up_ref(); + // `ret` takes its own ref + ret.push(&item); + } + + ret + } + pub fn from_rustls(certs: &Vec>) -> Self { let mut r = Self::empty(); for c in certs {