-
-
Notifications
You must be signed in to change notification settings - Fork 764
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Password callbacks #410
Password callbacks #410
Changes from all commits
311af7c
f0b4a03
8119f06
c399c24
c1b7cd2
41b7854
d176ea1
351bc56
f24ab26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,11 @@ use bio::MemBio; | |
use crypto::HashTypeInternals; | ||
use crypto::hash; | ||
|
||
#[cfg(feature = "catch_unwind")] | ||
use libc::{c_void, c_char}; | ||
#[cfg(feature = "catch_unwind")] | ||
use crypto::util::{CallbackState, invoke_passwd_cb}; | ||
|
||
pub struct RSA(*mut ffi::RSA); | ||
|
||
impl Drop for RSA { | ||
|
@@ -76,6 +81,29 @@ impl RSA { | |
} | ||
} | ||
|
||
/// Reads an RSA private key from PEM formatted data and supplies a password callback. | ||
/// | ||
/// Requires the `catch_unwind` feature. | ||
#[cfg(feature = "catch_unwind")] | ||
pub fn private_key_from_pem_cb<R, F>(reader: &mut R, pass_cb: F) -> Result<RSA, SslError> | ||
where R: Read, F: FnOnce(&mut [c_char]) -> usize | ||
{ | ||
let mut cb = CallbackState::new(pass_cb); | ||
|
||
let mut mem_bio = try!(MemBio::new()); | ||
try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); | ||
|
||
unsafe { | ||
let cb_ptr = &mut cb as *mut _ as *mut c_void; | ||
let rsa = try_ssl_null!(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.get_handle(), | ||
ptr::null_mut(), | ||
Some(invoke_passwd_cb::<F>), | ||
cb_ptr)); | ||
|
||
Ok(RSA(rsa)) | ||
} | ||
} | ||
|
||
/// Writes an RSA private key as unencrypted PEM formatted data | ||
pub fn private_key_to_pem<W>(&self, writer: &mut W) -> Result<(), SslError> | ||
where W: Write | ||
|
@@ -277,4 +305,23 @@ mod test { | |
|
||
assert!(result); | ||
} | ||
|
||
#[test] | ||
#[cfg(feature = "catch_unwind")] | ||
pub fn test_password() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will need to be flagged under the catch_unwind feature - I fixed the travis builds so if you rebase onto master it should be ok. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
let mut password_queried = false; | ||
let mut buffer = File::open("test/rsa-encrypted.pem").unwrap(); | ||
RSA::private_key_from_pem_cb(&mut buffer, |password| { | ||
password_queried = true; | ||
password[0] = b'm' as _; | ||
password[1] = b'y' as _; | ||
password[2] = b'p' as _; | ||
password[3] = b'a' as _; | ||
password[4] = b's' as _; | ||
password[5] = b's' as _; | ||
6 | ||
}).unwrap(); | ||
|
||
assert!(password_queried); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
use libc::{c_int, c_char, c_void}; | ||
|
||
use std::any::Any; | ||
use std::panic; | ||
use std::slice; | ||
|
||
/// 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<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 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 [i8]) -> usize { | ||
let result = panic::catch_unwind(|| { | ||
// build a `i8` slice to pass to the user callback | ||
let pass_slice = unsafe { slice::from_raw_parts_mut(buf, size as usize) }; | ||
let callback = unsafe { &mut *(cb_state as *mut CallbackState<F>) }; | ||
|
||
callback.cb.take().unwrap()(pass_slice) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't seem like this option dance is necessary if we're only using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aaah I think |
||
}); | ||
|
||
if let Ok(len) = result { | ||
return len as c_int; | ||
} else { | ||
return 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
-----BEGIN RSA PRIVATE KEY----- | ||
Proc-Type: 4,ENCRYPTED | ||
DEK-Info: AES-128-CBC,E2F16153E2BA3D617285A68C896BA6AF | ||
|
||
vO9SnhtGjGe8pG1pN//vsONnvJr+DjU+lFCiSqGMPT7tezDnbehLfS+9kus2HV7r | ||
HmI14JvVG9O7NpF7zMyBRlHYdWcCCWED9Yar0NsWN9419e5pMe/bqIXAzAiJbtT4 | ||
OB9U5XF3m+349zjN1dVXPPLGRmMC1pcHAlofeb5nIUFTvUi5xcsbe1itGjgkkvHb | ||
Bt8NioHTBun8kKrlsFQOuB55ylBU/eWG8DQBtvFOmQ7iWp0RnGQfh8k5e5rcZNpQ | ||
fD9ygc7UVISl0xTrIG4IH15g34H+nrBauKtIPOpNPuXQPOMHCZv3XH8wnhrWHHwT | ||
ZFnQBdXbSpQtMsRh0phG2G+VIlyCgSn4+CxjCJ+TgFtsoK/tU0unmRYc59QnTxxb | ||
qkHYsPs3E0NApQAgH1ENEGl1M+FGLYQH7gftjc3ophBTeRA17sRmD7Y4QBInggsq | ||
Gv6tImPVBdekAjz/Ls/EyMwjAvvrL5eAokqrIsAarGo+zmbJKHzknw2KUz2En0+k | ||
YYaxB4oy9u7bzuQlvio6xYHJEb4K197bby4Dldmqv7YCCJBJwhOBAInMD687viKv | ||
vcUwL8YuS6cW5E8MbvEENlY4+lvKKj3M8Bnyb79cYIPQe92EuCwXU9DZXPRMLwwM | ||
oFEJpF5E/PmNJzu+B52ahHtDrh83WSx71fWqjdTqwkPZhAYo3ztsfFkb/UqUcq8u | ||
rBSebeUjZh0XZ9B04eshZQ5vJUcXGtYIe/77beV3Pv89/fw+zTZjpiP9Q3sZALzf | ||
Qt0YGp0/6qBuqR1tcqdu65AS2hun7yFw7uRavqYKvww4axRiz2do+xWmZFuoCAwD | ||
EWktaUujltpvAc1lo7lg4C6nByefJB9Xqk22N/vpqOsWr1NbAntT42Qj/HF9BVWR | ||
osvN3yMnKYWYe6oSTVnNBDM5obWAIHd3I9gcxTOTb1KsEwt2RrDs5EpB5ptS3Fjo | ||
JfBRhNZQ3cXttrIIhsHgDn9BDNg865/xpIgktKj0gEd60Abx0PqkAIm6IZTh4Efg | ||
7uZwfzxB+saOcddbrW2gNdzVZMC0s2Ye3sqHhtLbAJ3BlXYTxE4CAvTg54Ny+5hF | ||
IjvjlOKgXceSG1cSfk21/wyp9RY3Ft0AEYvvp0kZScWZaoA2aSFDUrchXVhgrEbn | ||
lJ7UptjefwRFIreAlwbKSbIDDNWnyzvIWyHfQ2aYqgnb7W7XqNPSgH9cALCfzirI | ||
dlRHjha0bMUtrjPCC/YfMXzJBVniy0gG6Pd5uC7vz/Awn6/6HRQVNaTQASphPBQ7 | ||
bJuz+JTfzI9OUVCMRMdnb6b35U4P9tibFmnPvzTIPe+3WUmf8aRsLS3NN3G1Webd | ||
PMYVZpMycPaAI0Ht87axhsOzlxCWHYWjdHa+WoNNc1J90TxLCmAHquh5BDaWvjMK | ||
0DySftJZjV7Tf1p2KosmU83LRl39B5NHMbZb1xOEZl9IWwhT/PVKTVZ25xdxWLfb | ||
hF4l8rfvKehIp5r4t8zW1bvI2Hl6vrUvmcUVWt3BfKjxlgwRVD0vvwonMt1INesF | ||
204vUBeXbDsUUicLwOyUgaFvJ3XU3dOyvL9MhOgM5OgoFRRhG+4AS8a5JCD8iLtq | ||
-----END RSA PRIVATE KEY----- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs for these methods should mention that they require the
catch_unwind
feature.