aboutsummaryrefslogtreecommitdiff
path: root/openssl/src
diff options
context:
space:
mode:
authorSteven Fackler <[email protected]>2016-05-05 13:32:41 -0700
committerSteven Fackler <[email protected]>2016-05-05 13:32:41 -0700
commit4294511f2ba9fe7ec6860f0c783498d93ea68fd9 (patch)
treedf813ae00076c59fd6cb4b7d61bc5f5e76b4f680 /openssl/src
parentMerge branch 'release-v0.7.10' into release (diff)
parentRelease v0.7.11 (diff)
downloadrust-openssl-0.7.11.tar.xz
rust-openssl-0.7.11.zip
Merge branch 'release-v0.7.11' into releasev0.7.11
Diffstat (limited to 'openssl/src')
-rw-r--r--openssl/src/crypto/symm.rs25
-rw-r--r--openssl/src/crypto/symm_internal.rs3
-rw-r--r--openssl/src/lib.rs2
-rw-r--r--openssl/src/ssl/mod.rs148
-rw-r--r--openssl/src/ssl/tests/mod.rs31
-rw-r--r--openssl/src/x509/extension.rs3
-rw-r--r--openssl/src/x509/mod.rs163
-rw-r--r--openssl/src/x509/tests.rs27
8 files changed, 345 insertions, 57 deletions
diff --git a/openssl/src/crypto/symm.rs b/openssl/src/crypto/symm.rs
index c0e845dc..935980f3 100644
--- a/openssl/src/crypto/symm.rs
+++ b/openssl/src/crypto/symm.rs
@@ -37,6 +37,9 @@ pub enum Type {
AES_256_CFB128,
AES_256_CFB8,
+ DES_CBC,
+ DES_ECB,
+
RC4_128,
}
@@ -362,4 +365,26 @@ mod tests {
cipher_test(super::Type::AES_256_CFB8, pt, ct, key, iv);
}
+
+ #[test]
+ fn test_des_cbc() {
+
+ let pt = "54686973206973206120746573742e";
+ let ct = "6f2867cfefda048a4046ef7e556c7132";
+ let key = "7cb66337f3d3c0fe";
+ let iv = "0001020304050607";
+
+ cipher_test(super::Type::DES_CBC, pt, ct, key, iv);
+ }
+
+ #[test]
+ fn test_des_ecb() {
+
+ let pt = "54686973206973206120746573742e";
+ let ct = "0050ab8aecec758843fe157b4dde938c";
+ let key = "7cb66337f3d3c0fe";
+ let iv = "0001020304050607";
+
+ cipher_test(super::Type::DES_ECB, pt, ct, key, iv);
+ }
}
diff --git a/openssl/src/crypto/symm_internal.rs b/openssl/src/crypto/symm_internal.rs
index 5c457f3f..ba01e1c1 100644
--- a/openssl/src/crypto/symm_internal.rs
+++ b/openssl/src/crypto/symm_internal.rs
@@ -26,6 +26,9 @@ pub fn evpc(t: symm::Type) -> (*const ffi::EVP_CIPHER, u32, u32) {
symm::Type::AES_256_CFB128 => (ffi::EVP_aes_256_cfb128(), 32, 16),
symm::Type::AES_256_CFB8 => (ffi::EVP_aes_256_cfb8(), 32, 16),
+ symm::Type::DES_CBC => (ffi::EVP_des_cbc(), 8, 8),
+ symm::Type::DES_ECB => (ffi::EVP_des_ecb(), 8, 8),
+
symm::Type::RC4_128 => (ffi::EVP_rc4(), 16, 0),
}
}
diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
index 63926615..f3be24b1 100644
--- a/openssl/src/lib.rs
+++ b/openssl/src/lib.rs
@@ -1,4 +1,4 @@
-#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.7.10")]
+#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.7.11")]
#![cfg_attr(feature = "nightly", feature(const_fn))]
#[macro_use]
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index 4b3a4385..aa785142 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -69,44 +69,44 @@ pub fn init() {
}
bitflags! {
- flags SslContextOptions: u64 {
- const SSL_OP_MICROSOFT_SESS_ID_BUG = ffi_extras::SSL_OP_MICROSOFT_SESS_ID_BUG,
- const SSL_OP_NETSCAPE_CHALLENGE_BUG = ffi_extras::SSL_OP_NETSCAPE_CHALLENGE_BUG,
- const SSL_OP_LEGACY_SERVER_CONNECT = ffi_extras::SSL_OP_LEGACY_SERVER_CONNECT,
- const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = ffi_extras::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
- const SSL_OP_TLSEXT_PADDING = ffi_extras::SSL_OP_TLSEXT_PADDING,
- const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ffi_extras::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER,
- const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = ffi_extras::SSL_OP_SAFARI_ECDHE_ECDSA_BUG,
- const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ffi_extras::SSL_OP_SSLEAY_080_CLIENT_DH_BUG,
- const SSL_OP_TLS_D5_BUG = ffi_extras::SSL_OP_TLS_D5_BUG,
- const SSL_OP_TLS_BLOCK_PADDING_BUG = ffi_extras::SSL_OP_TLS_BLOCK_PADDING_BUG,
- const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ffi_extras::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS,
- const SSL_OP_NO_QUERY_MTU = ffi_extras::SSL_OP_NO_QUERY_MTU,
- const SSL_OP_COOKIE_EXCHANGE = ffi_extras::SSL_OP_COOKIE_EXCHANGE,
- const SSL_OP_NO_TICKET = ffi_extras::SSL_OP_NO_TICKET,
- const SSL_OP_CISCO_ANYCONNECT = ffi_extras::SSL_OP_CISCO_ANYCONNECT,
- const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = ffi_extras::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION,
- const SSL_OP_NO_COMPRESSION = ffi_extras::SSL_OP_NO_COMPRESSION,
- const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ffi_extras::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION,
- const SSL_OP_SINGLE_ECDH_USE = ffi_extras::SSL_OP_SINGLE_ECDH_USE,
- const SSL_OP_SINGLE_DH_USE = ffi_extras::SSL_OP_SINGLE_DH_USE,
- const SSL_OP_CIPHER_SERVER_PREFERENCE = ffi_extras::SSL_OP_CIPHER_SERVER_PREFERENCE,
- const SSL_OP_TLS_ROLLBACK_BUG = ffi_extras::SSL_OP_TLS_ROLLBACK_BUG,
- const SSL_OP_NO_SSLV2 = ffi_extras::SSL_OP_NO_SSLv2,
- const SSL_OP_NO_SSLV3 = ffi_extras::SSL_OP_NO_SSLv3,
- const SSL_OP_NO_DTLSV1 = ffi_extras::SSL_OP_NO_DTLSv1,
- const SSL_OP_NO_TLSV1 = ffi_extras::SSL_OP_NO_TLSv1,
- const SSL_OP_NO_DTLSV1_2 = ffi_extras::SSL_OP_NO_DTLSv1_2,
- const SSL_OP_NO_TLSV1_2 = ffi_extras::SSL_OP_NO_TLSv1_2,
- const SSL_OP_NO_TLSV1_1 = ffi_extras::SSL_OP_NO_TLSv1_1,
- const SSL_OP_NETSCAPE_CA_DN_BUG = ffi_extras::SSL_OP_NETSCAPE_CA_DN_BUG,
- const SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = ffi_extras::SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG,
- const SSL_OP_CRYPTOPRO_TLSEXT_BUG = ffi_extras::SSL_OP_CRYPTOPRO_TLSEXT_BUG,
- const SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = ffi_extras::SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG,
- const SSL_OP_MSIE_SSLV2_RSA_PADDING = ffi_extras::SSL_OP_MSIE_SSLV2_RSA_PADDING,
- const SSL_OP_PKCS1_CHECK_1 = ffi_extras::SSL_OP_PKCS1_CHECK_1,
- const SSL_OP_PKCS1_CHECK_2 = ffi_extras::SSL_OP_PKCS1_CHECK_2,
- const SSL_OP_EPHEMERAL_RSA = ffi_extras::SSL_OP_EPHEMERAL_RSA,
+ pub flags SslContextOptions: u64 {
+ const SSL_OP_MICROSOFT_SESS_ID_BUG = ::ffi_extras::SSL_OP_MICROSOFT_SESS_ID_BUG,
+ const SSL_OP_NETSCAPE_CHALLENGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_CHALLENGE_BUG,
+ const SSL_OP_LEGACY_SERVER_CONNECT = ::ffi_extras::SSL_OP_LEGACY_SERVER_CONNECT,
+ const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
+ const SSL_OP_TLSEXT_PADDING = ::ffi_extras::SSL_OP_TLSEXT_PADDING,
+ const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ::ffi_extras::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER,
+ const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = ::ffi_extras::SSL_OP_SAFARI_ECDHE_ECDSA_BUG,
+ const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ::ffi_extras::SSL_OP_SSLEAY_080_CLIENT_DH_BUG,
+ const SSL_OP_TLS_D5_BUG = ::ffi_extras::SSL_OP_TLS_D5_BUG,
+ const SSL_OP_TLS_BLOCK_PADDING_BUG = ::ffi_extras::SSL_OP_TLS_BLOCK_PADDING_BUG,
+ const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ::ffi_extras::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS,
+ const SSL_OP_NO_QUERY_MTU = ::ffi_extras::SSL_OP_NO_QUERY_MTU,
+ const SSL_OP_COOKIE_EXCHANGE = ::ffi_extras::SSL_OP_COOKIE_EXCHANGE,
+ const SSL_OP_NO_TICKET = ::ffi_extras::SSL_OP_NO_TICKET,
+ const SSL_OP_CISCO_ANYCONNECT = ::ffi_extras::SSL_OP_CISCO_ANYCONNECT,
+ const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = ::ffi_extras::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION,
+ const SSL_OP_NO_COMPRESSION = ::ffi_extras::SSL_OP_NO_COMPRESSION,
+ const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ::ffi_extras::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION,
+ const SSL_OP_SINGLE_ECDH_USE = ::ffi_extras::SSL_OP_SINGLE_ECDH_USE,
+ const SSL_OP_SINGLE_DH_USE = ::ffi_extras::SSL_OP_SINGLE_DH_USE,
+ const SSL_OP_CIPHER_SERVER_PREFERENCE = ::ffi_extras::SSL_OP_CIPHER_SERVER_PREFERENCE,
+ const SSL_OP_TLS_ROLLBACK_BUG = ::ffi_extras::SSL_OP_TLS_ROLLBACK_BUG,
+ const SSL_OP_NO_SSLV2 = ::ffi_extras::SSL_OP_NO_SSLv2,
+ const SSL_OP_NO_SSLV3 = ::ffi_extras::SSL_OP_NO_SSLv3,
+ const SSL_OP_NO_DTLSV1 = ::ffi_extras::SSL_OP_NO_DTLSv1,
+ const SSL_OP_NO_TLSV1 = ::ffi_extras::SSL_OP_NO_TLSv1,
+ const SSL_OP_NO_DTLSV1_2 = ::ffi_extras::SSL_OP_NO_DTLSv1_2,
+ const SSL_OP_NO_TLSV1_2 = ::ffi_extras::SSL_OP_NO_TLSv1_2,
+ const SSL_OP_NO_TLSV1_1 = ::ffi_extras::SSL_OP_NO_TLSv1_1,
+ const SSL_OP_NETSCAPE_CA_DN_BUG = ::ffi_extras::SSL_OP_NETSCAPE_CA_DN_BUG,
+ const SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG,
+ const SSL_OP_CRYPTOPRO_TLSEXT_BUG = ::ffi_extras::SSL_OP_CRYPTOPRO_TLSEXT_BUG,
+ const SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = ::ffi_extras::SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG,
+ const SSL_OP_MSIE_SSLV2_RSA_PADDING = ::ffi_extras::SSL_OP_MSIE_SSLV2_RSA_PADDING,
+ const SSL_OP_PKCS1_CHECK_1 = ::ffi_extras::SSL_OP_PKCS1_CHECK_1,
+ const SSL_OP_PKCS1_CHECK_2 = ::ffi_extras::SSL_OP_PKCS1_CHECK_2,
+ const SSL_OP_EPHEMERAL_RSA = ::ffi_extras::SSL_OP_EPHEMERAL_RSA,
const SSL_OP_ALL = SSL_OP_MICROSOFT_SESS_ID_BUG.bits|SSL_OP_NETSCAPE_CHALLENGE_BUG.bits
|SSL_OP_LEGACY_SERVER_CONNECT.bits|SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG.bits
|SSL_OP_TLSEXT_PADDING.bits|SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER.bits
@@ -214,19 +214,20 @@ impl SslMethod {
/// Determines the type of certificate verification used
bitflags! {
- flags SslVerifyMode: i32 {
+ pub flags SslVerifyMode: i32 {
/// Verify that the server's certificate is trusted
- const SSL_VERIFY_PEER = ffi::SSL_VERIFY_PEER,
+ const SSL_VERIFY_PEER = ::ffi::SSL_VERIFY_PEER,
/// Do not verify the server's certificate
- const SSL_VERIFY_NONE = ffi::SSL_VERIFY_NONE,
+ const SSL_VERIFY_NONE = ::ffi::SSL_VERIFY_NONE,
/// Terminate handshake if client did not return a certificate.
/// Use together with SSL_VERIFY_PEER.
- const SSL_VERIFY_FAIL_IF_NO_PEER_CERT = ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ const SSL_VERIFY_FAIL_IF_NO_PEER_CERT = ::ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
}
}
lazy_static! {
static ref INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
+ static ref SSL_INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
}
// Creates a static index for user data of type T
@@ -236,6 +237,10 @@ fn get_verify_data_idx<T: Any + 'static>() -> c_int {
*INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_idx::<T>())
}
+fn get_ssl_verify_data_idx<T: Any + 'static>() -> c_int {
+ *SSL_INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_ssl_idx::<T>())
+}
+
#[cfg(feature = "npn")]
lazy_static! {
static ref NPN_PROTOS_IDX: c_int = get_new_idx::<Vec<u8>>();
@@ -267,6 +272,26 @@ fn get_new_idx<T>() -> c_int {
}
}
+fn get_new_ssl_idx<T>() -> c_int {
+ extern "C" fn free_data_box<T>(_parent: *mut c_void,
+ ptr: *mut c_void,
+ _ad: *mut ffi::CRYPTO_EX_DATA,
+ _idx: c_int,
+ _argl: c_long,
+ _argp: *mut c_void) {
+ if !ptr.is_null() {
+ let _: Box<T> = unsafe { mem::transmute(ptr) };
+ }
+ }
+
+ unsafe {
+ let f: ffi::CRYPTO_EX_free = free_data_box::<T>;
+ let idx = ffi::SSL_get_ex_new_index(0, ptr::null(), None, None, Some(f));
+ assert!(idx >= 0);
+ idx
+ }
+}
+
extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int {
unsafe {
let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx();
@@ -311,6 +336,21 @@ extern "C" fn raw_verify_with_data<T>(preverify_ok: c_int,
}
}
+extern "C" fn ssl_raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
+ where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send
+{
+ unsafe {
+ let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx();
+ let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx);
+ let verify = ffi::SSL_get_ex_data(ssl, get_ssl_verify_data_idx::<F>());
+ let verify: &F = mem::transmute(verify);
+
+ let ctx = X509StoreContext::new(x509_ctx);
+
+ verify(preverify_ok != 0, &ctx) as c_int
+ }
+}
+
extern "C" fn raw_sni(ssl: *mut ffi::SSL, ad: &mut c_int, _arg: *mut c_void) -> c_int {
unsafe {
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
@@ -928,6 +968,30 @@ impl Ssl {
}
}
+ /// Sets the verification mode to be used during the handshake process.
+ ///
+ /// Use `set_verify_callback` to additionally add a callback.
+ pub fn set_verify(&mut self, mode: SslVerifyMode) {
+ unsafe { ffi::SSL_set_verify(self.ssl, mode.bits as c_int, None) }
+ }
+
+ /// Sets the certificate verification callback to be used during the
+ /// handshake process.
+ ///
+ /// The callback is provided with a boolean indicating if the
+ /// preveification process was successful, and an object providing access
+ /// to the certificate chain. It should return `true` if the certificate
+ /// chain is valid and `false` otherwise.
+ pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
+ where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send
+ {
+ unsafe {
+ let verify = Box::new(verify);
+ ffi::SSL_set_ex_data(self.ssl, get_ssl_verify_data_idx::<F>(), mem::transmute(verify));
+ ffi::SSL_set_verify(self.ssl, mode.bits as c_int, Some(ssl_raw_verify::<F>));
+ }
+ }
+
pub fn get_current_cipher<'a>(&'a self) -> Option<SslCipher<'a>> {
unsafe {
let ptr = ffi::SSL_get_current_cipher(self.ssl);
diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs
index 15811d99..c3e7a363 100644
--- a/openssl/src/ssl/tests/mod.rs
+++ b/openssl/src/ssl/tests/mod.rs
@@ -381,6 +381,36 @@ run_test!(verify_callback_data, |method, stream| {
}
});
+run_test!(ssl_verify_callback, |method, stream| {
+ use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+ use ssl::IntoSsl;
+
+ static CHECKED: AtomicUsize = ATOMIC_USIZE_INIT;
+
+ let ctx = SslContext::new(method).unwrap();
+ let mut ssl = ctx.into_ssl().unwrap();
+
+ let node_hash_str = "db400bb62f1b1f29c3b8f323b8f7d9dea724fdcd67104ef549c772ae3749655b";
+ let node_id = node_hash_str.from_hex().unwrap();
+ ssl.set_verify_callback(SSL_VERIFY_PEER, move |_, x509| {
+ CHECKED.store(1, Ordering::SeqCst);
+ match x509.get_current_cert() {
+ None => false,
+ Some(cert) => {
+ let fingerprint = cert.fingerprint(SHA256).unwrap();
+ fingerprint == node_id
+ }
+ }
+ });
+
+ match SslStream::connect_generic(ssl, stream) {
+ Ok(_) => (),
+ Err(err) => panic!("Expected success, got {:?}", err)
+ }
+
+ assert_eq!(CHECKED.load(Ordering::SeqCst), 1);
+});
+
// Make sure every write call translates to a write call to the underlying socket.
#[test]
fn test_write_hits_stream() {
@@ -1061,6 +1091,7 @@ fn refcount_ssl_context() {
}
#[test]
+#[cfg_attr(windows, ignore)] // don't have a trusted CA list easily available :(
fn default_verify_paths() {
let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap();
ctx.set_default_verify_paths().unwrap();
diff --git a/openssl/src/x509/extension.rs b/openssl/src/x509/extension.rs
index 88cb64a2..99ef62c1 100644
--- a/openssl/src/x509/extension.rs
+++ b/openssl/src/x509/extension.rs
@@ -1,4 +1,5 @@
use std::fmt;
+
use nid::Nid;
/// Type-only version of the `Extension` enum.
@@ -79,7 +80,7 @@ impl ExtensionType {
}
}
- pub fn get_name<'a>(&'a self) -> Option<&'a str> {
+ pub fn get_name(&self) -> Option<&str> {
match self {
&ExtensionType::OtherStr(ref s) => Some(s),
_ => None,
diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs
index a69f61d5..0a242a15 100644
--- a/openssl/src/x509/mod.rs
+++ b/openssl/src/x509/mod.rs
@@ -2,14 +2,16 @@ use libc::{c_char, c_int, c_long, c_ulong, c_uint, c_void};
use std::io;
use std::io::prelude::*;
use std::cmp::Ordering;
-use std::ffi::{CString, CStr};
+use std::ffi::CString;
use std::iter::repeat;
use std::mem;
use std::ptr;
use std::ops::Deref;
use std::fmt;
use std::str;
+use std::slice;
use std::collections::HashMap;
+use std::marker::PhantomData;
use asn1::Asn1Time;
use bio::MemBio;
@@ -20,7 +22,7 @@ use crypto::rand::rand_bytes;
use ffi;
use ffi_extras;
use ssl::error::{SslError, StreamError};
-use nid;
+use nid::Nid;
pub mod extension;
@@ -29,14 +31,12 @@ use self::extension::{ExtensionType, Extension};
#[cfg(test)]
mod tests;
-pub struct SslString {
- s: &'static str,
-}
+pub struct SslString(&'static str);
impl<'s> Drop for SslString {
fn drop(&mut self) {
unsafe {
- ffi::CRYPTO_free(self.s.as_ptr() as *mut c_void);
+ ffi::CRYPTO_free(self.0.as_ptr() as *mut c_void);
}
}
}
@@ -45,25 +45,26 @@ impl Deref for SslString {
type Target = str;
fn deref(&self) -> &str {
- self.s
+ self.0
}
}
impl SslString {
- unsafe fn new(buf: *const c_char) -> SslString {
- SslString { s: str::from_utf8(CStr::from_ptr(buf as *const _).to_bytes()).unwrap() }
+ unsafe fn new(buf: *const c_char, len: c_int) -> SslString {
+ let slice = slice::from_raw_parts(buf as *const _, len as usize);
+ SslString(str::from_utf8_unchecked(slice))
}
}
impl fmt::Display for SslString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(self.s, f)
+ fmt::Display::fmt(self.0, f)
}
}
impl fmt::Debug for SslString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(self.s, f)
+ fmt::Debug::fmt(self.0, f)
}
}
@@ -103,6 +104,10 @@ impl X509StoreContext {
})
}
}
+
+ pub fn error_depth(&self) -> u32 {
+ unsafe { ffi::X509_STORE_CTX_get_error_depth(self.ctx) as u32 }
+ }
}
#[allow(non_snake_case)]
@@ -464,6 +469,24 @@ impl<'ctx> X509<'ctx> {
}
}
+ /// Returns this certificate's SAN entries, if they exist.
+ pub fn subject_alt_names<'a>(&'a self) -> Option<GeneralNames<'a>> {
+ unsafe {
+ let stack = ffi::X509_get_ext_d2i(self.handle,
+ Nid::SubjectAltName as c_int,
+ ptr::null_mut(),
+ ptr::null_mut());
+ if stack.is_null() {
+ return None;
+ }
+
+ Some(GeneralNames {
+ stack: stack as *const _,
+ m: PhantomData,
+ })
+ }
+ }
+
pub fn public_key(&self) -> PKey {
let pkey = unsafe { ffi::X509_get_pubkey(self.handle) };
assert!(!pkey.is_null());
@@ -544,7 +567,7 @@ pub struct X509NameEntry<'x> {
}
impl<'x> X509Name<'x> {
- pub fn text_by_nid(&self, nid: nid::Nid) -> Option<SslString> {
+ pub fn text_by_nid(&self, nid: Nid) -> Option<SslString> {
unsafe {
let loc = ffi::X509_NAME_get_index_by_NID(self.name, nid as c_int, -1);
if loc == -1 {
@@ -570,7 +593,7 @@ impl<'x> X509Name<'x> {
assert!(!str_from_asn1.is_null());
- Some(SslString::new(str_from_asn1))
+ Some(SslString::new(str_from_asn1, len))
}
}
}
@@ -766,6 +789,120 @@ make_validation_error!(X509_V_OK,
X509ApplicationVerification = X509_V_ERR_APPLICATION_VERIFICATION,
);
+/// A collection of OpenSSL `GENERAL_NAME`s.
+pub struct GeneralNames<'a> {
+ stack: *const ffi::stack_st_GENERAL_NAME,
+ m: PhantomData<&'a ()>,
+}
+
+impl<'a> GeneralNames<'a> {
+ /// Returns the number of `GeneralName`s in this structure.
+ pub fn len(&self) -> usize {
+ unsafe {
+ (*self.stack).stack.num as usize
+ }
+ }
+
+ /// Returns the specified `GeneralName`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `idx` is not less than `len()`.
+ pub fn get(&self, idx: usize) -> GeneralName<'a> {
+ unsafe {
+ assert!(idx < self.len());
+
+ GeneralName {
+ name: *(*self.stack).stack.data.offset(idx as isize) as *const ffi::GENERAL_NAME,
+ m: PhantomData,
+ }
+ }
+ }
+
+ /// Returns an iterator over the `GeneralName`s in this structure.
+ pub fn iter(&self) -> GeneralNamesIter {
+ GeneralNamesIter {
+ names: self,
+ idx: 0
+ }
+ }
+}
+
+impl<'a> IntoIterator for &'a GeneralNames<'a> {
+ type Item = GeneralName<'a>;
+ type IntoIter = GeneralNamesIter<'a>;
+
+ fn into_iter(self) -> GeneralNamesIter<'a> {
+ self.iter()
+ }
+}
+
+/// An iterator over OpenSSL `GENERAL_NAME`s.
+pub struct GeneralNamesIter<'a> {
+ names: &'a GeneralNames<'a>,
+ idx: usize,
+}
+
+impl<'a> Iterator for GeneralNamesIter<'a> {
+ type Item = GeneralName<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.idx < self.names.len() {
+ let name = self.names.get(self.idx);
+ self.idx += 1;
+ Some(name)
+ } else {
+ None
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let size = self.names.len() - self.idx;
+ (size, Some(size))
+ }
+}
+
+impl<'a> ExactSizeIterator for GeneralNamesIter<'a> {}
+
+/// An OpenSSL `GENERAL_NAME`.
+pub struct GeneralName<'a> {
+ name: *const ffi::GENERAL_NAME,
+ m: PhantomData<&'a ()>,
+}
+
+impl<'a> GeneralName<'a> {
+ /// Returns the contents of this `GeneralName` if it is a `dNSName`.
+ pub fn dnsname(&self) -> Option<&str> {
+ unsafe {
+ if (*self.name).type_ != ffi::GEN_DNS {
+ return None;
+ }
+
+ let ptr = ffi::ASN1_STRING_data((*self.name).d as *mut _);
+ let len = ffi::ASN1_STRING_length((*self.name).d as *mut _);
+
+ let slice = slice::from_raw_parts(ptr as *const u8, len as usize);
+ // dNSNames are stated to be ASCII (specifically IA5). Hopefully
+ // OpenSSL checks that when loading a certificate but if not we'll
+ // use this instead of from_utf8_unchecked just in case.
+ str::from_utf8(slice).ok()
+ }
+ }
+
+ /// Returns the contents of this `GeneralName` if it is an `iPAddress`.
+ pub fn ipaddress(&self) -> Option<&[u8]> {
+ unsafe {
+ if (*self.name).type_ != ffi::GEN_IPADD {
+ return None;
+ }
+
+ let ptr = ffi::ASN1_STRING_data((*self.name).d as *mut _);
+ let len = ffi::ASN1_STRING_length((*self.name).d as *mut _);
+
+ Some(slice::from_raw_parts(ptr as *const u8, len as usize))
+ }
+ }
+}
#[test]
fn test_negative_serial() {
diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs
index 69ad37f8..0032d108 100644
--- a/openssl/src/x509/tests.rs
+++ b/openssl/src/x509/tests.rs
@@ -157,3 +157,30 @@ fn test_nid_uid_value() {
};
assert_eq!(&cn as &str, "this is the userId");
}
+
+#[test]
+fn test_subject_alt_name() {
+ let mut file = File::open("test/alt_name_cert.pem").unwrap();
+ let cert = X509::from_pem(&mut file).unwrap();
+
+ let subject_alt_names = cert.subject_alt_names().unwrap();
+ assert_eq!(3, subject_alt_names.len());
+ assert_eq!(Some("foobar.com"), subject_alt_names.get(0).dnsname());
+ assert_eq!(subject_alt_names.get(1).ipaddress(), Some(&[127, 0, 0, 1][..]));
+ assert_eq!(subject_alt_names.get(2).ipaddress(),
+ Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]));
+}
+
+#[test]
+fn test_subject_alt_name_iter() {
+ let mut file = File::open("test/alt_name_cert.pem").unwrap();
+ let cert = X509::from_pem(&mut file).unwrap();
+
+ let subject_alt_names = cert.subject_alt_names().unwrap();
+ let mut subject_alt_names_iter = subject_alt_names.iter();
+ assert_eq!(subject_alt_names_iter.next().unwrap().dnsname(), Some("foobar.com"));
+ assert_eq!(subject_alt_names_iter.next().unwrap().ipaddress(), Some(&[127, 0, 0, 1][..]));
+ assert_eq!(subject_alt_names_iter.next().unwrap().ipaddress(),
+ Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]));
+ assert!(subject_alt_names_iter.next().is_none());
+}