pingora_boringssl/ext.rs
1// Copyright 2024 Cloudflare, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! the extended functionalities that are yet exposed via the [`boring`] APIs
16
17use boring::error::ErrorStack;
18use boring::pkey::{HasPrivate, PKeyRef};
19use boring::ssl::{Ssl, SslAcceptor, SslRef};
20use boring::x509::store::X509StoreRef;
21use boring::x509::verify::X509VerifyParamRef;
22use boring::x509::X509Ref;
23use foreign_types_shared::ForeignTypeRef;
24use libc::*;
25use std::ffi::CString;
26
27fn cvt(r: c_int) -> Result<c_int, ErrorStack> {
28 if r != 1 {
29 Err(ErrorStack::get())
30 } else {
31 Ok(r)
32 }
33}
34
35/// Add name as an additional reference identifier that can match the peer's certificate
36///
37/// See [X509_VERIFY_PARAM_set1_host](https://www.openssl.org/docs/man3.1/man3/X509_VERIFY_PARAM_set1_host.html).
38pub fn add_host(verify_param: &mut X509VerifyParamRef, host: &str) -> Result<(), ErrorStack> {
39 if host.is_empty() {
40 return Ok(());
41 }
42 unsafe {
43 cvt(boring_sys::X509_VERIFY_PARAM_add1_host(
44 verify_param.as_ptr(),
45 host.as_ptr() as *const _,
46 host.len(),
47 ))
48 .map(|_| ())
49 }
50}
51
52/// Set the verify cert store of `ssl`
53///
54/// See [SSL_set1_verify_cert_store](https://www.openssl.org/docs/man1.1.1/man3/SSL_set1_verify_cert_store.html).
55pub fn ssl_set_verify_cert_store(
56 ssl: &mut SslRef,
57 cert_store: &X509StoreRef,
58) -> Result<(), ErrorStack> {
59 unsafe {
60 cvt(boring_sys::SSL_set1_verify_cert_store(
61 ssl.as_ptr(),
62 cert_store.as_ptr(),
63 ))?;
64 }
65 Ok(())
66}
67
68/// Load the certificate into `ssl`
69///
70/// See [SSL_use_certificate](https://www.openssl.org/docs/man1.1.1/man3/SSL_use_certificate.html).
71pub fn ssl_use_certificate(ssl: &mut SslRef, cert: &X509Ref) -> Result<(), ErrorStack> {
72 unsafe {
73 cvt(boring_sys::SSL_use_certificate(ssl.as_ptr(), cert.as_ptr()))?;
74 }
75 Ok(())
76}
77
78/// Load the private key into `ssl`
79///
80/// See [SSL_use_certificate](https://www.openssl.org/docs/man1.1.1/man3/SSL_use_PrivateKey.html).
81pub fn ssl_use_private_key<T>(ssl: &mut SslRef, key: &PKeyRef<T>) -> Result<(), ErrorStack>
82where
83 T: HasPrivate,
84{
85 unsafe {
86 cvt(boring_sys::SSL_use_PrivateKey(ssl.as_ptr(), key.as_ptr()))?;
87 }
88 Ok(())
89}
90
91/// Add the certificate into the cert chain of `ssl`
92///
93/// See [SSL_add1_chain_cert](https://www.openssl.org/docs/man1.1.1/man3/SSL_add1_chain_cert.html)
94pub fn ssl_add_chain_cert(ssl: &mut SslRef, cert: &X509Ref) -> Result<(), ErrorStack> {
95 unsafe {
96 cvt(boring_sys::SSL_add1_chain_cert(ssl.as_ptr(), cert.as_ptr()))?;
97 }
98 Ok(())
99}
100
101/// Set renegotiation
102///
103/// This function is specific to BoringSSL
104/// See <https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_set_renegotiate_mode>
105pub fn ssl_set_renegotiate_mode_freely(ssl: &mut SslRef) {
106 unsafe {
107 boring_sys::SSL_set_renegotiate_mode(
108 ssl.as_ptr(),
109 boring_sys::ssl_renegotiate_mode_t::ssl_renegotiate_freely,
110 );
111 }
112}
113
114/// Set the curves/groups of `ssl`
115///
116/// See [set_groups_list](https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_curves.html).
117pub fn ssl_set_groups_list(ssl: &mut SslRef, groups: &str) -> Result<(), ErrorStack> {
118 let groups = CString::new(groups).unwrap();
119 unsafe {
120 // somehow SSL_set1_groups_list doesn't exist but SSL_set1_curves_list means the same anyways
121 cvt(boring_sys::SSL_set1_curves_list(
122 ssl.as_ptr(),
123 groups.as_ptr(),
124 ))?;
125 }
126 Ok(())
127}
128
129/// Set's whether a second keyshare to be sent in client hello when PQ is used.
130///
131/// Default is true. When `true`, the first PQ (if any) and none-PQ keyshares are sent.
132/// When `false`, only the first configured keyshares are sent.
133#[cfg(feature = "pq_use_second_keyshare")]
134pub fn ssl_use_second_key_share(ssl: &mut SslRef, enabled: bool) {
135 unsafe { boring_sys::SSL_use_second_keyshare(ssl.as_ptr(), enabled as _) }
136}
137#[cfg(not(feature = "pq_use_second_keyshare"))]
138pub fn ssl_use_second_key_share(_ssl: &mut SslRef, _enabled: bool) {}
139
140/// Clear the error stack
141///
142/// SSL calls should check and clear the BoringSSL error stack. But some calls fail to do so.
143/// This causes the next unrelated SSL call to fail due to the leftover errors. This function allows
144/// the caller to clear the error stack before performing SSL calls to avoid this issue.
145pub fn clear_error_stack() {
146 let _ = ErrorStack::get();
147}
148
149/// Create a new [Ssl] from &[SslAcceptor]
150///
151/// This function is needed because [Ssl::new()] doesn't take `&SslContextRef` like openssl-rs
152pub fn ssl_from_acceptor(acceptor: &SslAcceptor) -> Result<Ssl, ErrorStack> {
153 Ssl::new_from_ref(acceptor.context())
154}
155
156/// Suspend the TLS handshake when a certificate is needed.
157///
158/// This function will cause tls handshake to pause and return the error: SSL_ERROR_WANT_X509_LOOKUP.
159/// The caller should set the certificate and then call [unblock_ssl_cert()] before continue the
160/// handshake on the tls connection.
161pub fn suspend_when_need_ssl_cert(ssl: &mut SslRef) {
162 unsafe {
163 boring_sys::SSL_set_cert_cb(ssl.as_ptr(), Some(raw_cert_block), std::ptr::null_mut());
164 }
165}
166
167/// Unblock a TLS handshake after the certificate is set.
168///
169/// The user should continue to call tls handshake after this function is called.
170pub fn unblock_ssl_cert(ssl: &mut SslRef) {
171 unsafe {
172 boring_sys::SSL_set_cert_cb(ssl.as_ptr(), None, std::ptr::null_mut());
173 }
174}
175
176// Just block the handshake
177extern "C" fn raw_cert_block(_ssl: *mut boring_sys::SSL, _arg: *mut c_void) -> c_int {
178 -1
179}
180
181/// Whether the TLS error is SSL_ERROR_WANT_X509_LOOKUP
182pub fn is_suspended_for_cert(error: &boring::ssl::Error) -> bool {
183 error.code().as_raw() == boring_sys::SSL_ERROR_WANT_X509_LOOKUP
184}
185
186#[allow(clippy::mut_from_ref)]
187/// Get a mutable SslRef ouf of SslRef. which is a missing functionality for certain SslStream
188/// # Safety
189/// the caller needs to make sure that they hold a &mut SslRef
190pub unsafe fn ssl_mut(ssl: &SslRef) -> &mut SslRef {
191 unsafe { SslRef::from_ptr_mut(ssl.as_ptr()) }
192}