aboutsummaryrefslogtreecommitdiffhomepage
path: root/pingora-openssl/src/ext.rs
diff options
context:
space:
mode:
Diffstat (limited to 'pingora-openssl/src/ext.rs')
-rw-r--r--pingora-openssl/src/ext.rs209
1 files changed, 209 insertions, 0 deletions
diff --git a/pingora-openssl/src/ext.rs b/pingora-openssl/src/ext.rs
new file mode 100644
index 0000000..f8cebb2
--- /dev/null
+++ b/pingora-openssl/src/ext.rs
@@ -0,0 +1,209 @@
+// Copyright 2024 Cloudflare, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+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_int) -> Result<c_int, 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: *const SSL, cert: *mut X509) -> c_int;
+ pub fn SSL_use_PrivateKey(ctx: *const 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,
+ );
+}
+
+/// Add name as an additional reference identifier that can match the peer's certificate
+///
+/// See [X509_VERIFY_PARAM_set1_host](https://www.openssl.org/docs/man3.1/man3/X509_VERIFY_PARAM_set1_host.html).
+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 _,
+ host.len(),
+ ))
+ .map(|_| ())
+ }
+}
+
+/// Set the verify cert store of `ssl`
+///
+/// See [SSL_set1_verify_cert_store](https://www.openssl.org/docs/man1.1.1/man3/SSL_set1_verify_cert_store.html).
+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, // increase the ref count of X509Store so that ssl_ctx can outlive X509StoreRef
+ cert_store.as_ptr() as *mut c_void,
+ ) as i32)?;
+ }
+ Ok(())
+}
+
+/// Load the certificate into `ssl`
+///
+/// See [SSL_use_certificate](https://www.openssl.org/docs/man1.1.1/man3/SSL_use_certificate.html).
+pub fn ssl_use_certificate(ssl: &mut SslRef, cert: &X509Ref) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(SSL_use_certificate(ssl.as_ptr(), cert.as_ptr()))?;
+ }
+ Ok(())
+}
+
+/// Load the private key into `ssl`
+///
+/// See [SSL_use_certificate](https://www.openssl.org/docs/man1.1.1/man3/SSL_use_PrivateKey.html).
+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()))?;
+ }
+ Ok(())
+}
+
+/// Add the certificate into the cert chain of `ssl`
+///
+/// See [SSL_add1_chain_cert](https://www.openssl.org/docs/man1.1.1/man3/SSL_add1_chain_cert.html)
+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, // increase the ref count of X509 so that ssl can outlive X509StoreRef
+ cert.as_ptr() as *mut c_void,
+ ) as i32)?;
+ }
+ Ok(())
+}
+
+/// Set renegotiation
+///
+/// This function is specific to BoringSSL. This function is noop for OpenSSL.
+pub fn ssl_set_renegotiate_mode_freely(_ssl: &mut SslRef) {}
+
+/// Set the curves/groups of `ssl`
+///
+/// See [set_groups_list](https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_curves.html).
+pub fn ssl_set_groups_list(ssl: &mut SslRef, groups: &str) -> Result<(), ErrorStack> {
+ let groups = CString::new(groups).unwrap();
+ unsafe {
+ cvt(SSL_ctrl(
+ ssl.as_ptr(),
+ SSL_CTRL_SET_GROUPS_LIST,
+ 0,
+ groups.as_ptr() as *mut c_void,
+ ) as i32)?;
+ }
+ Ok(())
+}
+
+/// Set's whether a second keyshare to be sent in client hello when PQ is used.
+///
+/// This function is specific to BoringSSL. This function is noop for OpenSSL.
+pub fn ssl_use_second_key_share(_ssl: &mut SslRef, _enabled: bool) {}
+
+/// Clear the error stack
+///
+/// SSL calls should check and clear the OpenSSL error stack. But some calls fail to do so.
+/// This causes the next unrelated SSL call to fail due to the leftover errors. This function allow
+/// caller to clear the error stack before performing SSL calls to avoid this issue.
+pub fn clear_error_stack() {
+ let _ = ErrorStack::get();
+}
+
+/// Create a new [Ssl] from &[SslAcceptor]
+///
+/// this function is to unify the interface between this crate and `pingora-boringssl`
+pub fn ssl_from_acceptor(acceptor: &SslAcceptor) -> Result<Ssl, ErrorStack> {
+ Ssl::new(acceptor.context())
+}
+
+/// Suspend the TLS handshake when a certificate is needed.
+///
+/// This function will cause tls handshake to pause and return the error: SSL_ERROR_WANT_X509_LOOKUP.
+/// The caller should set the certificate and then call [unblock_ssl_cert()] before continue the
+/// handshake on the tls connection.
+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());
+ }
+}
+
+/// Unblock a TLS handshake after the certificate is set.
+///
+/// The user should continue to call tls handshake after this function is called.
+pub fn unblock_ssl_cert(ssl: &mut SslRef) {
+ unsafe {
+ SSL_set_cert_cb(ssl.as_ptr(), None, std::ptr::null_mut());
+ }
+}
+
+// Just block the handshake
+extern "C" fn raw_cert_block(_ssl: *mut openssl_sys::SSL, _arg: *mut c_void) -> c_int {
+ -1
+}
+
+/// Whether the TLS error is SSL_ERROR_WANT_X509_LOOKUP
+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)]
+/// Get a mutable SslRef ouf of SslRef, which is a missing functionality even when holding &mut SslStream
+/// # Safety
+/// the caller need to make sure that they hold a &mut SslStream (or other mutable ref to the Ssl)
+pub unsafe fn ssl_mut(ssl: &SslRef) -> &mut SslRef {
+ SslRef::from_ptr_mut(ssl.as_ptr())
+}