diff --git a/openssl-sys/src/ossl10x.rs b/openssl-sys/src/ossl10x.rs index 241dc782..07fa7d46 100644 --- a/openssl-sys/src/ossl10x.rs +++ b/openssl-sys/src/ossl10x.rs @@ -583,6 +583,7 @@ extern { pub fn X509_get_ext_d2i(x: *mut ::X509, nid: c_int, crit: *mut c_int, idx: *mut c_int) -> *mut c_void; pub fn X509_NAME_get_entry(n: *mut ::X509_NAME, loc: c_int) -> *mut ::X509_NAME_ENTRY; pub fn X509_NAME_ENTRY_get_data(ne: *mut ::X509_NAME_ENTRY) -> *mut ::ASN1_STRING; + pub fn X509_STORE_CTX_get_chain(ctx: *mut ::X509_STORE_CTX) -> *mut stack_st_X509; pub fn ASN1_STRING_to_UTF8(out: *mut *mut c_uchar, s: *mut ::ASN1_STRING) -> c_int; pub fn ASN1_STRING_data(x: *mut ::ASN1_STRING) -> *mut c_uchar; pub fn CRYPTO_add_lock(pointer: *mut c_int, diff --git a/openssl-sys/src/ossl110.rs b/openssl-sys/src/ossl110.rs index 1a7c9e00..1b0d9f34 100644 --- a/openssl-sys/src/ossl110.rs +++ b/openssl-sys/src/ossl110.rs @@ -13,7 +13,6 @@ pub enum HMAC_CTX {} pub enum OPENSSL_STACK {} pub enum RSA {} pub enum SSL_CTX {} -pub enum _STACK {} pub enum stack_st_ASN1_OBJECT {} pub enum stack_st_GENERAL_NAME {} pub enum stack_st_OPENSSL_STRING {} @@ -142,12 +141,13 @@ extern { pub fn X509_up_ref(x: *mut X509) -> c_int; pub fn SSL_CTX_up_ref(x: *mut SSL_CTX) -> c_int; pub fn X509_get0_extensions(req: *const ::X509) -> *const stack_st_X509_EXTENSION; + pub fn X509_STORE_CTX_get0_chain(ctx: *mut ::X509_STORE_CTX) -> *mut stack_st_X509; pub fn EVP_MD_CTX_new() -> *mut EVP_MD_CTX; pub fn EVP_MD_CTX_free(ctx: *mut EVP_MD_CTX); pub fn OpenSSL_version_num() -> c_ulong; pub fn OpenSSL_version(key: c_int) -> *const c_char; - pub fn OPENSSL_sk_free(st: *mut _STACK); - pub fn OPENSSL_sk_pop_free(st: *mut _STACK, free: Option); - pub fn OPENSSL_sk_pop(st: *mut _STACK) -> *mut c_void; + pub fn OPENSSL_sk_free(st: *mut ::OPENSSL_STACK); + pub fn OPENSSL_sk_pop_free(st: *mut ::OPENSSL_STACK, free: Option); + pub fn OPENSSL_sk_pop(st: *mut ::OPENSSL_STACK) -> *mut c_void; } diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index d053606f..03f702b5 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -81,6 +81,7 @@ pub mod ssl; pub mod symm; pub mod version; pub mod x509; +pub mod stack; #[cfg(any(ossl102, ossl110))] mod verify; diff --git a/openssl/src/ssl/connector.rs b/openssl/src/ssl/connector.rs index b2e00a81..c95b0fa1 100644 --- a/openssl/src/ssl/connector.rs +++ b/openssl/src/ssl/connector.rs @@ -256,7 +256,8 @@ mod verify { use std::str; use nid; - use x509::{X509StoreContext, X509, GeneralNames, X509Name}; + use x509::{X509StoreContext, X509, X509Name, GeneralName}; + use stack::Stack; use types::Ref; pub fn verify_callback(domain: &str, @@ -275,15 +276,16 @@ mod verify { fn verify_hostname(domain: &str, cert: &Ref) -> bool { match cert.subject_alt_names() { - Some(names) => verify_subject_alt_names(domain, &names), + Some(names) => verify_subject_alt_names(domain, names), None => verify_subject_name(domain, &cert.subject_name()), } } - fn verify_subject_alt_names(domain: &str, names: &GeneralNames) -> bool { + fn verify_subject_alt_names(domain: &str, + names: Stack) -> bool { let ip = domain.parse(); - for name in names { + for name in &names { match ip { Ok(ip) => { if let Some(actual) = name.ipaddress() { diff --git a/openssl/src/stack.rs b/openssl/src/stack.rs new file mode 100644 index 00000000..2cc53b5f --- /dev/null +++ b/openssl/src/stack.rs @@ -0,0 +1,319 @@ +use std::ops::{Deref, DerefMut, Index, IndexMut}; +use std::iter; +use std::borrow::Borrow; +use std::convert::AsRef; + +#[cfg(ossl110)] +use libc::c_int; + +use ffi; +use types::{OpenSslType, Ref}; + +/// Trait implemented by stackable types. This must *only* be +/// implemented on opaque types that can be directly casted into their +/// `CType`. +pub trait Stackable: OpenSslType { + /// C stack type for this element. Generally called + /// `stack_st_{ELEMENT_TYPE}`, normally hidden by the + /// `STACK_OF(ELEMENT_TYPE)` macro in the OpenSSL API. + type StackType; +} + +/// An owned stack of `T`. +pub struct Stack(*mut T::StackType); + +impl Stack { + /// Return a new Stack, taking ownership of the handle + pub unsafe fn from_ptr(stack: *mut T::StackType) -> Stack { + Stack(stack) + } +} + +impl Drop for Stack { + #[cfg(ossl10x)] + fn drop(&mut self) { + unsafe { + loop { + let ptr = ffi::sk_pop(self.as_stack()); + + if ptr.is_null() { + break; + } + + // Build the owned version of the object just to run + // its `drop` implementation and delete the item. + T::from_ptr(ptr as *mut _); + } + + ffi::sk_free(self.0 as *mut _); + } + } + + #[cfg(ossl110)] + fn drop(&mut self) { + unsafe { + loop { + let ptr = ffi::OPENSSL_sk_pop(self.as_stack()); + + if ptr.is_null() { + break; + } + + // Build the owned version of the object just to run + // its `drop` implementation and delete the item. + T::from_ptr(ptr as *mut _); + } + + ffi::OPENSSL_sk_free(self.0 as *mut _); + } + } +} + +impl AsRef>> for Stack { + fn as_ref(&self) -> &Ref> { + &*self + } +} + +impl Borrow>> for Stack { + fn borrow(&self) -> &Ref> { + &*self + } +} + +unsafe impl OpenSslType for Stack { + type CType = T::StackType; + + unsafe fn from_ptr(ptr: *mut T::StackType) -> Stack { + Stack(ptr) + } + + fn as_ptr(&self) -> *mut T::StackType { + self.0 + } +} + +impl Deref for Stack { + type Target = Ref>; + + fn deref(&self) -> &Ref> { + unsafe { Ref::from_ptr(self.0) } + } +} + +impl DerefMut for Stack { + fn deref_mut(&mut self) -> &mut ::types::Ref> { + unsafe { Ref::from_ptr_mut(self.0) } + } +} + +impl Ref> { + /// OpenSSL stack types are just a (kinda) typesafe wrapper around + /// a `_STACK` object. We can therefore safely cast it and access + /// the `_STACK` members without having to worry about the real + /// layout of `T::StackType`. + /// + /// If that sounds unsafe then keep in mind that's exactly how the + /// OpenSSL 1.1.0 new C stack code works. + #[cfg(ossl10x)] + fn as_stack(&self) -> *mut ffi::_STACK { + self.as_ptr() as *mut _ + } + + /// OpenSSL 1.1.0 replaced the stack macros with a functions and + /// only exposes an opaque OPENSSL_STACK struct + /// publicly. + #[cfg(ossl110)] + fn as_stack(&self) -> *mut ffi::OPENSSL_STACK { + self.as_ptr() as *mut _ + } + + /// Returns the number of items in the stack + pub fn len(&self) -> usize { + self._len() + } + + #[cfg(ossl10x)] + fn _len(&self) -> usize { + unsafe { (*self.as_stack()).num as usize } + } + + #[cfg(ossl110)] + fn _len(&self) -> usize { + unsafe { ffi::OPENSSL_sk_num(self.as_stack()) as usize } + } + + pub fn iter(&self) -> Iter { + // Unfortunately we can't simply convert the stack into a + // slice and use that because OpenSSL 1.1.0 doesn't directly + // expose the stack data (we have to use `OPENSSL_sk_value` + // instead). We have to rewrite the entire iteration framework + // instead. + + Iter { + stack: self, + pos: 0, + } + } + + pub fn iter_mut(&mut self) -> IterMut { + IterMut { + stack: self, + pos: 0, + } + } + + /// Returns a reference to the element at the given index in the + /// stack or `None` if the index is out of bounds + pub fn get(&self, idx: usize) -> Option<&Ref> { + if idx >= self.len() { + return None; + } + + unsafe { + let r = Ref::from_ptr(self._get(idx)); + + Some(r) + } + } + + /// Returns a mutable reference to the element at the given index in the + /// stack or `None` if the index is out of bounds + pub fn get_mut(&mut self, idx: usize) -> Option<&mut Ref> { + if idx >= self.len() { + return None; + } + + unsafe { + Some(Ref::from_ptr_mut(self._get(idx))) + } + } + + #[cfg(ossl10x)] + unsafe fn _get(&self, idx: usize) -> *mut T::CType { + *(*self.as_stack()).data.offset(idx as isize) as *mut _ + } + + #[cfg(ossl110)] + unsafe fn _get(&self, idx: usize) -> *mut T::CType { + ffi::OPENSSL_sk_value(self.as_stack(), idx as c_int) as *mut _ + } +} + +impl Index for Ref> { + type Output = Ref; + + fn index(&self, index: usize) -> &Ref { + self.get(index).unwrap() + } +} + +impl IndexMut for Ref> { + fn index_mut(&mut self, index: usize) -> &mut Ref { + self.get_mut(index).unwrap() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a Ref> { + type Item = &'a Ref; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a mut Ref> { + type Item = &'a mut Ref; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a Stack { + type Item = &'a Ref; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a mut Stack { + type Item = &'a mut Ref; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +/// An iterator over the stack's contents. +pub struct Iter<'a, T: Stackable> + where T: 'a { + stack: &'a Ref>, + pos: usize, +} + +impl<'a, T: Stackable> iter::Iterator for Iter<'a, T> { + type Item = &'a Ref; + + fn next(&mut self) -> Option<&'a Ref> { + let n = self.stack.get(self.pos); + + if n.is_some() { + self.pos += 1; + } + + n + } + + fn size_hint(&self) -> (usize, Option) { + let rem = self.stack.len() - self.pos; + + (rem, Some(rem)) + } +} + +impl<'a, T: Stackable> iter::ExactSizeIterator for Iter<'a, T> { +} + +/// A mutable iterator over the stack's contents. +pub struct IterMut<'a, T: Stackable + 'a> { + stack: &'a mut Ref>, + pos: usize, +} + +impl<'a, T: Stackable> iter::Iterator for IterMut<'a, T> { + type Item = &'a mut Ref; + + fn next(&mut self) -> Option<&'a mut Ref> { + if self.pos >= self.stack.len() { + None + } else { + // Rust won't allow us to get a mutable reference into + // `stack` in this situation since it can't statically + // guarantee that we won't return several references to + // the same object, so we have to use unsafe code for + // mutable iterators. + let n = unsafe { + Some(Ref::from_ptr_mut(self.stack._get(self.pos))) + }; + + self.pos += 1; + + n + } + } + + fn size_hint(&self) -> (usize, Option) { + let rem = self.stack.len() - self.pos; + + (rem, Some(rem)) + } +} + +impl<'a, T: Stackable> iter::ExactSizeIterator for IterMut<'a, T> { +} diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index 28f2542f..e27b03d1 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -1,11 +1,10 @@ -use libc::{c_char, c_int, c_long, c_ulong, c_void}; +use libc::{c_char, c_int, c_long, c_ulong}; use std::borrow::Borrow; use std::cmp; use std::collections::HashMap; use std::error::Error; use std::ffi::{CStr, CString}; use std::fmt; -use std::marker::PhantomData; use std::mem; use std::ptr; use std::slice; @@ -21,6 +20,7 @@ use error::ErrorStack; use ffi; use nid::Nid; use types::{OpenSslType, Ref}; +use stack::{Stack, Stackable}; #[cfg(ossl10x)] use ffi::{X509_set_notBefore, X509_set_notAfter, ASN1_STRING_data}; @@ -67,6 +67,28 @@ impl Ref { pub fn error_depth(&self) -> u32 { unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 } } + + pub fn get_chain(&self) -> Option<&Ref>> { + unsafe { + let chain = self._get_chain(); + + if chain.is_null() { + return None; + } + + Some(Ref::from_ptr(chain)) + } + } + + #[cfg(ossl110)] + unsafe fn _get_chain(&self) -> *mut ffi::stack_st_X509 { + ffi::X509_STORE_CTX_get0_chain(self.as_ptr()) + } + + #[cfg(ossl10x)] + unsafe fn _get_chain(&self) -> *mut ffi::stack_st_X509 { + ffi::X509_STORE_CTX_get_chain(self.as_ptr()) + } } #[allow(non_snake_case)] @@ -346,7 +368,7 @@ impl Ref { } /// Returns this certificate's SAN entries, if they exist. - pub fn subject_alt_names(&self) -> Option { + pub fn subject_alt_names(&self) -> Option> { unsafe { let stack = ffi::X509_get_ext_d2i(self.as_ptr(), ffi::NID_subject_alt_name, @@ -356,7 +378,7 @@ impl Ref { return None; } - Some(GeneralNames { stack: stack as *mut _ }) + Some(Stack::from_ptr(stack as *mut _)) } } @@ -468,6 +490,10 @@ impl Borrow> for X509 { &*self } } + +impl Stackable for X509 { + type StackType = ffi::stack_st_X509; +} type_!(X509Name, ffi::X509_NAME, ffi::X509_NAME_free); @@ -678,135 +704,18 @@ impl X509VerifyError { } } -/// A collection of OpenSSL `GENERAL_NAME`s. -pub struct GeneralNames { - stack: *mut ffi::stack_st_GENERAL_NAME, -} +type_!(GeneralName, ffi::GENERAL_NAME, ffi::GENERAL_NAME_free); -impl Drop for GeneralNames { - #[cfg(ossl10x)] - fn drop(&mut self) { - unsafe { - // This transmute is dubious but it's what openssl itself does... - let free: unsafe extern "C" fn(*mut ffi::GENERAL_NAME) = ffi::GENERAL_NAME_free; - let free: unsafe extern "C" fn(*mut c_void) = mem::transmute(free); - ffi::sk_pop_free(&mut (*self.stack).stack, Some(free)); - } - } - - #[cfg(ossl110)] - fn drop(&mut self) { - unsafe { - // This transmute is dubious but it's what openssl itself does... - let free: unsafe extern "C" fn(*mut ffi::GENERAL_NAME) = ffi::GENERAL_NAME_free; - let free: unsafe extern "C" fn(*mut c_void) = mem::transmute(free); - ffi::OPENSSL_sk_pop_free(self.stack as *mut _, Some(free)); - } - } -} - -impl GeneralNames { - /// Returns the number of `GeneralName`s in this structure. - pub fn len(&self) -> usize { - self._len() - } - - #[cfg(ossl10x)] - fn _len(&self) -> usize { - unsafe { (*self.stack).stack.num as usize } - } - - #[cfg(ossl110)] - fn _len(&self) -> usize { - unsafe { ffi::OPENSSL_sk_num(self.stack as *const _) as usize } - } - - /// Returns the specified `GeneralName`. - /// - /// # Panics - /// - /// Panics if `idx` is not less than `len()`. - pub fn get<'a>(&'a self, idx: usize) -> GeneralName<'a> { - unsafe { - assert!(idx < self.len()); - GeneralName { - name: self._get(idx), - m: PhantomData, - } - } - } - - #[cfg(ossl10x)] - unsafe fn _get(&self, idx: usize) -> *const ffi::GENERAL_NAME { - *(*self.stack).stack.data.offset(idx as isize) as *const ffi::GENERAL_NAME - } - - #[cfg(ossl110)] - unsafe fn _get(&self, idx: usize) -> *const ffi::GENERAL_NAME { - ffi::OPENSSL_sk_value(self.stack as *const _, idx as c_int) as *mut _ - } - - /// 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 { - 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, - idx: usize, -} - -impl<'a> Iterator for GeneralNamesIter<'a> { - type Item = GeneralName<'a>; - - fn next(&mut self) -> Option { - 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) { - 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> { +impl Ref { /// 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 { + if (*self.as_ptr()).type_ != ffi::GEN_DNS { return None; } - let ptr = ASN1_STRING_data((*self.name).d as *mut _); - let len = ffi::ASN1_STRING_length((*self.name).d as *mut _); + let ptr = ASN1_STRING_data((*self.as_ptr()).d as *mut _); + let len = ffi::ASN1_STRING_length((*self.as_ptr()).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 @@ -819,18 +728,22 @@ impl<'a> GeneralName<'a> { /// 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 { + if (*self.as_ptr()).type_ != ffi::GEN_IPADD { return None; } - let ptr = ASN1_STRING_data((*self.name).d as *mut _); - let len = ffi::ASN1_STRING_length((*self.name).d as *mut _); + let ptr = ASN1_STRING_data((*self.as_ptr()).d as *mut _); + let len = ffi::ASN1_STRING_length((*self.as_ptr()).d as *mut _); Some(slice::from_raw_parts(ptr as *const u8, len as usize)) } } } +impl Stackable for GeneralName { + type StackType = ffi::stack_st_GENERAL_NAME; +} + #[test] fn test_negative_serial() { // I guess that's enough to get a random negative number diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index db93c3a8..2527d538 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -152,10 +152,10 @@ fn test_subject_alt_name() { 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(), + assert_eq!(Some("foobar.com"), subject_alt_names[0].dnsname()); + assert_eq!(subject_alt_names[1].ipaddress(), Some(&[127, 0, 0, 1][..])); - assert_eq!(subject_alt_names.get(2).ipaddress(), + assert_eq!(subject_alt_names[2].ipaddress(), Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..])); }