aboutsummaryrefslogtreecommitdiff
path: root/openssl/src/x509/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openssl/src/x509/mod.rs')
-rw-r--r--openssl/src/x509/mod.rs163
1 files changed, 150 insertions, 13 deletions
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() {