     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94

    use foreign_types::{ForeignType, ForeignTypeRef};
    use libc::{c_char, c_int, c_void};
    use std::any::Any;
    use std::panic::{self, AssertUnwindSafe};
    use std::slice;

    use error::ErrorStack;

    /// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI
    /// frames are on the stack).
    ///
    /// When dropped, checks if the callback has panicked, and resumes unwinding if so.
    pub struct CallbackState<F> {
        /// The user callback. Taken out of the `Option` when called.
        cb: Option<F>,
        /// If the callback panics, we place the panic object here, to be re-thrown once OpenSSL
        /// returns.
        panic: Option<Box<dyn Any + Send + 'static>>,
    }

    impl<F> CallbackState<F> {
        pub fn new(callback: F) -> Self {
            CallbackState {
                cb: Some(callback),
                panic: None,
            }
        }
    }

    impl<F> Drop for CallbackState<F> {
        fn drop(&mut self) {
            if let Some(panic) = self.panic.take() {
                panic::resume_unwind(panic);
            }
        }
    }

    /// Password callback function, passed to private key loading functions.
    ///
    /// `cb_state` is expected to be a pointer to a `CallbackState`.
    pub unsafe extern "C" fn invoke_passwd_cb<F>(
        buf: *mut c_char,
        size: c_int,
        _rwflag: c_int,
        cb_state: *mut c_void,
    ) -> c_int
    where
        F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>,
    {
        let callback = &mut *(cb_state as *mut CallbackState<F>);

        let result = panic::catch_unwind(AssertUnwindSafe(|| {
            let pass_slice = slice::from_raw_parts_mut(buf as *mut u8, size as usize);
            callback.cb.take().unwrap()(pass_slice)
        }));

        match result {
            Ok(Ok(len)) => len as c_int,
            Ok(Err(_)) => {
                // FIXME restore error stack
                0
            }
            Err(err) => {
                callback.panic = Some(err);
                0
            }
        }
    }

    pub trait ForeignTypeExt: ForeignType {
        unsafe fn from_ptr_opt(ptr: *mut Self::CType) -> Option<Self> {
            if ptr.is_null() {
                None
            } else {
                Some(Self::from_ptr(ptr))
            }
        }
    }
    impl<FT: ForeignType> ForeignTypeExt for FT {}

    pub trait ForeignTypeRefExt: ForeignTypeRef {
        unsafe fn from_const_ptr<'a>(ptr: *const Self::CType) -> &'a Self {
            Self::from_ptr(ptr as *mut Self::CType)
        }

        unsafe fn from_const_ptr_opt<'a>(ptr: *const Self::CType) -> Option<&'a Self> {
            if ptr.is_null() {
                None
            } else {
                Some(Self::from_const_ptr(ptr as *mut Self::CType))
            }
        }
    }
    impl<FT: ForeignTypeRef> ForeignTypeRefExt for FT {}