| 1 | use crate::error::ErrorStack; |
| 2 | use crate::util; |
| 3 | use foreign_types::{ForeignType, ForeignTypeRef}; |
| 4 | use libc::{c_char, c_int, c_void}; |
| 5 | use std::any::Any; |
| 6 | use std::panic::{self, AssertUnwindSafe}; |
| 7 | use std::slice; |
| 8 | |
| 9 | /// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI |
| 10 | /// frames are on the stack). |
| 11 | /// |
| 12 | /// When dropped, checks if the callback has panicked, and resumes unwinding if so. |
| 13 | pub struct CallbackState<F> { |
| 14 | /// The user callback. Taken out of the `Option` when called. |
| 15 | cb: Option<F>, |
| 16 | /// If the callback panics, we place the panic object here, to be re-thrown once OpenSSL |
| 17 | /// returns. |
| 18 | panic: Option<Box<dyn Any + Send + 'static>>, |
| 19 | } |
| 20 | |
| 21 | impl<F> CallbackState<F> { |
| 22 | pub fn new(callback: F) -> Self { |
| 23 | CallbackState { |
| 24 | cb: Some(callback), |
| 25 | panic: None, |
| 26 | } |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | impl<F> Drop for CallbackState<F> { |
| 31 | fn drop(&mut self) { |
| 32 | if let Some(panic: Box) = self.panic.take() { |
| 33 | panic::resume_unwind(payload:panic); |
| 34 | } |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | /// Password callback function, passed to private key loading functions. |
| 39 | /// |
| 40 | /// `cb_state` is expected to be a pointer to a `CallbackState`. |
| 41 | pub unsafe extern "C" fn invoke_passwd_cb<F>( |
| 42 | buf: *mut c_char, |
| 43 | size: c_int, |
| 44 | _rwflag: c_int, |
| 45 | cb_state: *mut c_void, |
| 46 | ) -> c_int |
| 47 | where |
| 48 | F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>, |
| 49 | { |
| 50 | let callback: &mut CallbackState = &mut *(cb_state as *mut CallbackState<F>); |
| 51 | |
| 52 | let result: Result, …> = panic::catch_unwind(AssertUnwindSafe(|| { |
| 53 | let pass_slice: &mut [u8] = util::from_raw_parts_mut(data:buf as *mut u8, len:size as usize); |
| 54 | callback.cb.take().unwrap()(pass_slice) |
| 55 | })); |
| 56 | |
| 57 | match result { |
| 58 | Ok(Ok(len: usize)) => len as c_int, |
| 59 | Ok(Err(_)) => { |
| 60 | // FIXME restore error stack |
| 61 | 0 |
| 62 | } |
| 63 | Err(err: Box) => { |
| 64 | callback.panic = Some(err); |
| 65 | 0 |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | pub trait ForeignTypeExt: ForeignType { |
| 71 | unsafe fn from_ptr_opt(ptr: *mut Self::CType) -> Option<Self> { |
| 72 | if ptr.is_null() { |
| 73 | None |
| 74 | } else { |
| 75 | Some(Self::from_ptr(ptr)) |
| 76 | } |
| 77 | } |
| 78 | } |
| 79 | impl<FT: ForeignType> ForeignTypeExt for FT {} |
| 80 | |
| 81 | pub trait ForeignTypeRefExt: ForeignTypeRef { |
| 82 | unsafe fn from_const_ptr<'a>(ptr: *const Self::CType) -> &'a Self { |
| 83 | Self::from_ptr(ptr as *mut Self::CType) |
| 84 | } |
| 85 | |
| 86 | unsafe fn from_const_ptr_opt<'a>(ptr: *const Self::CType) -> Option<&'a Self> { |
| 87 | if ptr.is_null() { |
| 88 | None |
| 89 | } else { |
| 90 | Some(Self::from_const_ptr(ptr as *mut Self::CType)) |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | impl<FT: ForeignTypeRef> ForeignTypeRefExt for FT {} |
| 95 | |
| 96 | /// The same as `slice::from_raw_parts`, except that `data` may be `NULL` if |
| 97 | /// `len` is 0. |
| 98 | pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { |
| 99 | if len == 0 { |
| 100 | &[] |
| 101 | } else { |
| 102 | // Using this to implement the preferred API |
| 103 | #[allow (clippy::disallowed_methods)] |
| 104 | slice::from_raw_parts(data, len) |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | /// The same as `slice::from_raw_parts_mut`, except that `data` may be `NULL` |
| 109 | /// if `len` is 0. |
| 110 | pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { |
| 111 | if len == 0 { |
| 112 | &mut [] |
| 113 | } else { |
| 114 | // Using this to implement the preferred API |
| 115 | #[allow (clippy::disallowed_methods)] |
| 116 | slice::from_raw_parts_mut(data, len) |
| 117 | } |
| 118 | } |
| 119 | |