Skip to main content

rama_boring/ssl/
credential.rs

1use crate::error::ErrorStack;
2use crate::ex_data::Index;
3use crate::pkey::{PKeyRef, Private};
4use crate::ssl::callbacks;
5use crate::ssl::PrivateKeyMethod;
6use crate::{cvt_0i, cvt_n};
7use crate::{ffi, free_data_box};
8use foreign_types::{ForeignType, ForeignTypeRef};
9use openssl_macros::corresponds;
10use std::any::TypeId;
11use std::collections::HashMap;
12use std::ffi::{c_int, c_void};
13use std::mem;
14use std::ptr;
15use std::sync::{LazyLock, Mutex};
16
17static SSL_CREDENTIAL_INDEXES: LazyLock<Mutex<HashMap<TypeId, c_int>>> =
18    LazyLock::new(|| Mutex::new(HashMap::new()));
19
20foreign_type_and_impl_send_sync! {
21    type CType = ffi::SSL_CREDENTIAL;
22    fn drop = ffi::SSL_CREDENTIAL_free;
23
24    /// A credential.
25    pub struct SslCredential;
26}
27
28impl SslCredential {
29    /// Returns a new extra data index.
30    ///
31    /// Each invocation of this function is guaranteed to return a distinct index. These can be used
32    /// to store data in the context that can be retrieved later by callbacks, for example.
33    #[corresponds(SSL_C_get_ex_new_index)]
34    pub fn new_ex_index<T>() -> Result<Index<Self, T>, ErrorStack>
35    where
36        T: 'static + Sync + Send,
37    {
38        unsafe {
39            ffi::init();
40            let idx = cvt_n(get_new_ssl_credential_idx(Some(free_data_box::<T>)))?;
41            Ok(Index::from_raw(idx))
42        }
43    }
44
45    // FIXME should return a result?
46    pub(crate) fn cached_ex_index<T>() -> Index<Self, T>
47    where
48        T: 'static + Sync + Send,
49    {
50        unsafe {
51            let idx = *SSL_CREDENTIAL_INDEXES
52                .lock()
53                .unwrap_or_else(|e| e.into_inner())
54                .entry(TypeId::of::<T>())
55                .or_insert_with(|| Self::new_ex_index::<T>().unwrap().as_raw());
56            Index::from_raw(idx)
57        }
58    }
59}
60
61impl SslCredentialRef {
62    /// Returns a reference to the extra data at the specified index.
63    #[corresponds(SSL_CREDENTIAL_get_ex_data)]
64    #[must_use]
65    pub fn ex_data<T>(&self, index: Index<SslCredential, T>) -> Option<&T> {
66        unsafe {
67            let data = ffi::SSL_CREDENTIAL_get_ex_data(self.as_ptr(), index.as_raw());
68            if data.is_null() {
69                None
70            } else {
71                Some(&*(data as *const T))
72            }
73        }
74    }
75
76    // Unsafe because SSL contexts are not guaranteed to be unique, we call
77    // this only from SslCredentialBuilder.
78    #[corresponds(SSL_CREDENTIAL_get_ex_data)]
79    pub(crate) unsafe fn ex_data_mut<T>(
80        &mut self,
81        index: Index<SslCredential, T>,
82    ) -> Option<&mut T> {
83        let data = ffi::SSL_CREDENTIAL_get_ex_data(self.as_ptr(), index.as_raw());
84        if data.is_null() {
85            None
86        } else {
87            Some(&mut *(data as *mut T))
88        }
89    }
90
91    // Unsafe because SSL contexts are not guaranteed to be unique, we call
92    // this only from SslCredentialBuilder.
93    #[corresponds(SSL_CREDENTIAL_set_ex_data)]
94    pub(crate) unsafe fn replace_ex_data<T>(
95        &mut self,
96        index: Index<SslCredential, T>,
97        data: T,
98    ) -> Option<T> {
99        if let Some(old) = self.ex_data_mut(index) {
100            return Some(mem::replace(old, data));
101        }
102
103        unsafe {
104            let data = Box::into_raw(Box::new(data)) as *mut c_void;
105            ffi::SSL_CREDENTIAL_set_ex_data(self.as_ptr(), index.as_raw(), data);
106        }
107
108        None
109    }
110}
111
112/// A builder for [`SslCredential`]
113pub struct SslCredentialBuilder(SslCredential);
114
115impl SslCredentialBuilder {
116    /// Sets or overwrites the extra data at the specified index.
117    ///
118    /// This can be used to provide data to callbacks registered with the context. Use the
119    /// `SslCredential::new_ex_index` method to create an `Index`.
120    ///
121    /// Any previous value will be returned and replaced by the new one.
122    #[corresponds(SSL_CREDENTIAL_set_ex_data)]
123    pub fn replace_ex_data<T>(&mut self, index: Index<SslCredential, T>, data: T) -> Option<T> {
124        unsafe { self.0.replace_ex_data(index, data) }
125    }
126
127    // Sets the private key of the credential.
128    #[corresponds(SSL_CREDENTIAL_set1_private_key)]
129    pub fn set_private_key(&mut self, private_key: &PKeyRef<Private>) -> Result<(), ErrorStack> {
130        unsafe {
131            cvt_0i(ffi::SSL_CREDENTIAL_set1_private_key(
132                self.0.as_ptr(),
133                private_key.as_ptr(),
134            ))
135            .map(|_| ())
136        }
137    }
138
139    /// Configures a custom private key method on the credential.
140    ///
141    /// See [`PrivateKeyMethod`] for more details.
142    #[corresponds(SSL_CREDENTIAL_set_private_key_method)]
143    pub fn set_private_key_method<M>(&mut self, method: M) -> Result<(), ErrorStack>
144    where
145        M: PrivateKeyMethod,
146    {
147        unsafe {
148            self.replace_ex_data(SslCredential::cached_ex_index::<M>(), method);
149
150            cvt_0i(ffi::SSL_CREDENTIAL_set_private_key_method(
151                self.0.as_ptr(),
152                &ffi::SSL_PRIVATE_KEY_METHOD {
153                    sign: Some(callbacks::raw_sign::<M>),
154                    decrypt: Some(callbacks::raw_decrypt::<M>),
155                    complete: Some(callbacks::raw_complete::<M>),
156                },
157            ))
158            .map(|_| ())
159        }
160    }
161
162    #[must_use]
163    pub fn build(self) -> SslCredential {
164        self.0
165    }
166}
167
168unsafe fn get_new_ssl_credential_idx(f: ffi::CRYPTO_EX_free) -> c_int {
169    ffi::SSL_CREDENTIAL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f)
170}