Skip to content
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

Merged
merged 9 commits into from
Jul 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions openssl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ ecdh_auto = ["openssl-sys-extras/ecdh_auto"]
pkcs5_pbkdf2_hmac = ["openssl-sys/pkcs5_pbkdf2_hmac"]

nightly = []
catch_unwind = []

[dependencies]
bitflags = ">= 0.5.0, < 0.8.0"
Expand Down
2 changes: 2 additions & 0 deletions openssl/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub mod rand;
pub mod symm;
pub mod memcmp;
pub mod rsa;
#[cfg(feature = "catch_unwind")]
mod util;

mod symm_internal;

Expand Down
34 changes: 34 additions & 0 deletions openssl/src/crypto/pkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ use ffi;
use ssl::error::{SslError, StreamError};
use crypto::rsa::RSA;

#[cfg(feature = "catch_unwind")]
use libc::{c_void, c_char};
#[cfg(feature = "catch_unwind")]
use crypto::util::{CallbackState, invoke_passwd_cb};

#[derive(Copy, Clone)]
pub enum Parts {
Neither,
Expand Down Expand Up @@ -93,6 +98,35 @@ impl PKey {
}
}

/// Read a private key from PEM, supplying a password callback to be invoked if the private key
/// is encrypted.
///
/// The callback will be passed the password buffer and should return the number of characters
/// placed into the buffer.
Copy link
Owner

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.

///
/// 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<PKey, 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 evp = try_ssl_null!(ffi::PEM_read_bio_PrivateKey(mem_bio.get_handle(),
ptr::null_mut(),
Some(invoke_passwd_cb::<F>),
&mut cb as *mut _ as *mut c_void));

Ok(PKey {
evp: evp as *mut ffi::EVP_PKEY,
parts: Parts::Both,
})
}
}

/// Reads public key from PEM, takes ownership of handle
pub fn public_key_from_pem<R>(reader: &mut R) -> Result<PKey, SslError>
where R: Read
Expand Down
47 changes: 47 additions & 0 deletions openssl/src/crypto/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -277,4 +305,23 @@ mod test {

assert!(result);
}

#[test]
#[cfg(feature = "catch_unwind")]
pub fn test_password() {
Copy link
Owner

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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);
}
}
58 changes: 58 additions & 0 deletions openssl/src/crypto/util.rs
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)
Copy link
Owner

Choose a reason for hiding this comment

The 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 FnMut.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah I think FnMut was easier to work with before I had CallbackState. I'll switch to FnOnce, since that seems like the better thing to use there.

});

if let Ok(len) = result {
return len as c_int;
} else {
return 0;
}
}
30 changes: 30 additions & 0 deletions openssl/test/rsa-encrypted.pem
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-----
2 changes: 1 addition & 1 deletion openssl/test/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ if [ "$TEST_FEATURES" == "true" ]; then
fi

if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then
FEATURES="$FEATURES nightly"
FEATURES="$FEATURES nightly catch_unwind"
fi

if [ "$TRAVIS_OS_NAME" != "osx" ]; then
Expand Down