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