diff options
Diffstat (limited to 'openssl/src/x509/mod.rs')
| -rw-r--r-- | openssl/src/x509/mod.rs | 697 |
1 files changed, 293 insertions, 404 deletions
diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index f5369447..eb517f80 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -1,100 +1,66 @@ -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::ffi::CString; +use std::collections::HashMap; +use std::error::Error; +use std::ffi::{CStr, CString}; +use std::fmt; 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 HashTypeInternals; -use asn1::Asn1Time; -#[cfg(feature = "x509_expiry")] -use asn1::Asn1TimeRef; +use std::str; +use {cvt, cvt_p}; +use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef}; use bio::{MemBio, MemBioSlice}; -use crypto::hash; -use crypto::hash::Type as HashType; -use crypto::pkey::PKey; -use crypto::rand::rand_bytes; +use hash::MessageDigest; +use pkey::{PKey, PKeyRef}; +use rand::rand_bytes; +use error::ErrorStack; use ffi; use nid::Nid; -use error::ErrorStack; +use types::{OpenSslType, OpenSslTypeRef}; +use stack::{Stack, StackRef, Stackable}; -pub mod extension; +#[cfg(ossl10x)] +use ffi::{X509_set_notBefore, X509_set_notAfter, ASN1_STRING_data, X509_STORE_CTX_get_chain}; +#[cfg(ossl110)] +use ffi::{X509_set1_notBefore as X509_set_notBefore, X509_set1_notAfter as X509_set_notAfter, + ASN1_STRING_get0_data as ASN1_STRING_data, + X509_STORE_CTX_get0_chain as X509_STORE_CTX_get_chain}; -use self::extension::{ExtensionType, Extension}; +#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] +pub mod verify; -#[cfg(test)] -mod tests; +use x509::extension::{ExtensionType, Extension}; -pub struct SslString(&'static str); +pub mod extension; -impl<'s> Drop for SslString { - fn drop(&mut self) { - unsafe { - ffi::CRYPTO_free(self.0.as_ptr() as *mut c_void); - } - } -} +#[cfg(test)] +mod tests; -impl Deref for SslString { - type Target = str; +pub struct X509FileType(c_int); - fn deref(&self) -> &str { +impl X509FileType { + pub fn as_raw(&self) -> c_int { self.0 } } -impl SslString { - 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)) - } -} +pub const X509_FILETYPE_PEM: X509FileType = X509FileType(ffi::X509_FILETYPE_PEM); +pub const X509_FILETYPE_ASN1: X509FileType = X509FileType(ffi::X509_FILETYPE_ASN1); +pub const X509_FILETYPE_DEFAULT: X509FileType = X509FileType(ffi::X509_FILETYPE_DEFAULT); -impl fmt::Display for SslString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self.0, f) - } -} +type_!(X509StoreContext, X509StoreContextRef, ffi::X509_STORE_CTX, ffi::X509_STORE_CTX_free); -impl fmt::Debug for SslString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self.0, f) +impl X509StoreContextRef { + pub fn error(&self) -> Option<X509VerifyError> { + unsafe { X509VerifyError::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr()) as c_long) } } -} -#[derive(Copy, Clone)] -#[repr(i32)] -pub enum X509FileType { - PEM = ffi::X509_FILETYPE_PEM, - ASN1 = ffi::X509_FILETYPE_ASN1, - Default = ffi::X509_FILETYPE_DEFAULT, -} - -#[allow(missing_copy_implementations)] -pub struct X509StoreContext { - ctx: *mut ffi::X509_STORE_CTX, -} - -impl X509StoreContext { - pub fn new(ctx: *mut ffi::X509_STORE_CTX) -> X509StoreContext { - X509StoreContext { ctx: ctx } - } - - pub fn error(&self) -> Option<X509ValidationError> { - let err = unsafe { ffi::X509_STORE_CTX_get_error(self.ctx) }; - X509ValidationError::from_raw(err) - } - - pub fn current_cert<'a>(&'a self) -> Option<X509Ref<'a>> { + pub fn current_cert(&self) -> Option<&X509Ref> { unsafe { - let ptr = ffi::X509_STORE_CTX_get_current_cert(self.ctx); - + let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr()); if ptr.is_null() { None } else { @@ -104,7 +70,19 @@ impl X509StoreContext { } pub fn error_depth(&self) -> u32 { - unsafe { ffi::X509_STORE_CTX_get_error_depth(self.ctx) as u32 } + unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 } + } + + pub fn chain(&self) -> Option<&StackRef<X509>> { + unsafe { + let chain = X509_STORE_CTX_get_chain(self.as_ptr()); + + if chain.is_null() { + return None; + } + + Some(StackRef::from_ptr(chain)) + } } } @@ -114,19 +92,19 @@ impl X509StoreContext { /// # Example /// /// ``` -/// use openssl::crypto::hash::Type; -/// use openssl::crypto::pkey::PKey; -/// use openssl::crypto::rsa::RSA; +/// use openssl::hash::MessageDigest; +/// use openssl::pkey::PKey; +/// use openssl::rsa::Rsa; /// use openssl::x509::X509Generator; /// use openssl::x509::extension::{Extension, KeyUsageOption}; /// -/// let rsa = RSA::generate(2048).unwrap(); +/// let rsa = Rsa::generate(2048).unwrap(); /// let pkey = PKey::from_rsa(rsa).unwrap(); /// /// let gen = X509Generator::new() /// .set_valid_period(365*2) /// .add_name("CN".to_owned(), "SuperMegaCorp Inc.".to_owned()) -/// .set_sign_hash(Type::SHA256) +/// .set_sign_hash(MessageDigest::sha256()) /// .add_extension(Extension::KeyUsage(vec![KeyUsageOption::DigitalSignature])); /// /// let cert = gen.sign(&pkey).unwrap(); @@ -137,7 +115,7 @@ pub struct X509Generator { days: u32, names: Vec<(String, String)>, extensions: Extensions, - hash_type: HashType, + hash_type: MessageDigest, } impl X509Generator { @@ -153,7 +131,7 @@ impl X509Generator { days: 365, names: vec![], extensions: Extensions::new(), - hash_type: HashType::SHA1, + hash_type: MessageDigest::sha1(), } } @@ -224,7 +202,7 @@ impl X509Generator { self } - pub fn set_sign_hash(mut self, hash_type: hash::Type) -> X509Generator { + pub fn set_sign_hash(mut self, hash_type: MessageDigest) -> X509Generator { self.hash_type = hash_type; self } @@ -239,25 +217,25 @@ impl X509Generator { let value = CString::new(value.as_bytes()).unwrap(); let ext = match exttype.get_nid() { Some(nid) => { - ffi::X509V3_EXT_conf_nid(ptr::null_mut(), - mem::transmute(&ctx), - nid as c_int, - value.as_ptr() as *mut c_char) + try!(cvt_p(ffi::X509V3_EXT_nconf_nid(ptr::null_mut(), + &mut ctx, + nid.as_raw(), + value.as_ptr() as *mut c_char))) } None => { let name = CString::new(exttype.get_name().unwrap().as_bytes()).unwrap(); - ffi::X509V3_EXT_conf(ptr::null_mut(), - mem::transmute(&ctx), - name.as_ptr() as *mut c_char, - value.as_ptr() as *mut c_char) + try!(cvt_p(ffi::X509V3_EXT_nconf(ptr::null_mut(), + &mut ctx, + name.as_ptr() as *mut c_char, + value.as_ptr() as *mut c_char))) } }; - let mut success = false; - if ext != ptr::null_mut() { - success = ffi::X509_add_ext(x509, ext, -1) != 0; + if ffi::X509_add_ext(x509, ext, -1) != 1 { ffi::X509_EXTENSION_free(ext); + Err(ErrorStack::get()) + } else { + Ok(()) } - lift_ssl_if!(!success) } } @@ -266,17 +244,18 @@ impl X509Generator { value: &str) -> Result<(), ErrorStack> { let value_len = value.len() as c_int; - lift_ssl!(unsafe { + unsafe { let key = CString::new(key.as_bytes()).unwrap(); let value = CString::new(value.as_bytes()).unwrap(); - ffi::X509_NAME_add_entry_by_txt(name, - key.as_ptr() as *const _, - ffi::MBSTRING_UTF8, - value.as_ptr() as *const _, - value_len, - -1, - 0) - }) + cvt(ffi::X509_NAME_add_entry_by_txt(name, + key.as_ptr() as *const _, + ffi::MBSTRING_UTF8, + value.as_ptr() as *const _, + value_len, + -1, + 0)) + .map(|_| ()) + } } fn random_serial() -> Result<c_long, ErrorStack> { @@ -296,32 +275,30 @@ impl X509Generator { } /// Sets the certificate public-key, then self-sign and return it - /// Note: That the bit-length of the private key is used (set_bitlength is ignored) - pub fn sign(&self, p_key: &PKey) -> Result<X509, ErrorStack> { + pub fn sign(&self, p_key: &PKeyRef) -> Result<X509, ErrorStack> { ffi::init(); unsafe { - let x509 = try_ssl_null!(ffi::X509_new()); - let x509 = X509::from_ptr(x509); + let x509 = X509::from_ptr(try!(cvt_p(ffi::X509_new()))); - try_ssl!(ffi::X509_set_version(x509.as_ptr(), 2)); - try_ssl!(ffi::ASN1_INTEGER_set(ffi::X509_get_serialNumber(x509.as_ptr()), - try!(X509Generator::random_serial()))); + try!(cvt(ffi::X509_set_version(x509.as_ptr(), 2))); + try!(cvt(ffi::ASN1_INTEGER_set(ffi::X509_get_serialNumber(x509.as_ptr()), + try!(X509Generator::random_serial())))); let not_before = try!(Asn1Time::days_from_now(0)); let not_after = try!(Asn1Time::days_from_now(self.days)); - try_ssl!(ffi::X509_set_notBefore(x509.as_ptr(), not_before.as_ptr() as *const _)); + try!(cvt(X509_set_notBefore(x509.as_ptr(), not_before.as_ptr() as *const _))); // If prev line succeded - ownership should go to cert mem::forget(not_before); - try_ssl!(ffi::X509_set_notAfter(x509.as_ptr(), not_after.as_ptr() as *const _)); + try!(cvt(X509_set_notAfter(x509.as_ptr(), not_after.as_ptr() as *const _))); // If prev line succeded - ownership should go to cert mem::forget(not_after); - try_ssl!(ffi::X509_set_pubkey(x509.as_ptr(), p_key.as_ptr())); + try!(cvt(ffi::X509_set_pubkey(x509.as_ptr(), p_key.as_ptr()))); - let name = try_ssl_null!(ffi::X509_get_subject_name(x509.as_ptr())); + let name = try!(cvt_p(ffi::X509_get_subject_name(x509.as_ptr()))); let default = [("CN", "rust-openssl")]; let default_iter = &mut default.iter().map(|&(k, v)| (k, v)); @@ -335,7 +312,7 @@ impl X509Generator { for (key, val) in iter { try!(X509Generator::add_name_internal(name, &key, &val)); } - try_ssl!(ffi::X509_set_issuer_name(x509.as_ptr(), name)); + try!(cvt(ffi::X509_set_issuer_name(x509.as_ptr(), name))); for (exttype, ext) in self.extensions.iter() { try!(X509Generator::add_extension_internal(x509.as_ptr(), @@ -343,117 +320,95 @@ impl X509Generator { &ext.to_string())); } - let hash_fn = self.hash_type.evp_md(); - try_ssl!(ffi::X509_sign(x509.as_ptr(), p_key.as_ptr(), hash_fn)); + let hash_fn = self.hash_type.as_ptr(); + try!(cvt(ffi::X509_sign(x509.as_ptr(), p_key.as_ptr(), hash_fn))); Ok(x509) } } /// Obtain a certificate signing request (CSR) - /// - /// Requries the `x509_generator_request` feature. - #[cfg(feature = "x509_generator_request")] - pub fn request(&self, p_key: &PKey) -> Result<X509Req, ErrorStack> { + pub fn request(&self, p_key: &PKeyRef) -> Result<X509Req, ErrorStack> { let cert = match self.sign(p_key) { Ok(c) => c, Err(x) => return Err(x), }; unsafe { - let req = ffi::X509_to_X509_REQ(cert.as_ptr(), ptr::null_mut(), ptr::null()); - try_ssl_null!(req); + let req = try!(cvt_p(ffi::X509_to_X509_REQ(cert.as_ptr(), + ptr::null_mut(), + ptr::null()))); + let req = X509Req::from_ptr(req); - let exts = ::c_helpers::rust_0_8_X509_get_extensions(cert.as_ptr()); + let exts = compat::X509_get0_extensions(cert.as_ptr()); if exts != ptr::null_mut() { - try_ssl!(ffi::X509_REQ_add_extensions(req, exts)); + try!(cvt(ffi::X509_REQ_add_extensions(req.as_ptr(), exts as *mut _))); } - let hash_fn = self.hash_type.evp_md(); - try_ssl!(ffi::X509_REQ_sign(req, p_key.as_ptr(), hash_fn)); + let hash_fn = self.hash_type.as_ptr(); + try!(cvt(ffi::X509_REQ_sign(req.as_ptr(), p_key.as_ptr(), hash_fn))); - Ok(X509Req::new(req)) + Ok(req) } } } -/// A borrowed public key certificate. -pub struct X509Ref<'a>(*mut ffi::X509, PhantomData<&'a ()>); +type_!(X509, X509Ref, ffi::X509, ffi::X509_free); -impl<'a> X509Ref<'a> { - /// Creates a new `X509Ref` wrapping the provided handle. - pub unsafe fn from_ptr(x509: *mut ffi::X509) -> X509Ref<'a> { - X509Ref(x509, PhantomData) - } - - /// - #[deprecated(note = "renamed to `X509::from_ptr`", since = "0.8.1")] - pub unsafe fn new(x509: *mut ffi::X509) -> X509Ref<'a> { - X509Ref::from_ptr(x509) - } - - pub fn as_ptr(&self) -> *mut ffi::X509 { - self.0 - } - - pub fn subject_name<'b>(&'b self) -> X509Name<'b> { - let name = unsafe { ffi::X509_get_subject_name(self.0) }; - X509Name(name, PhantomData) +impl X509Ref { + pub fn subject_name(&self) -> &X509NameRef { + unsafe { + let name = ffi::X509_get_subject_name(self.as_ptr()); + X509NameRef::from_ptr(name) + } } /// Returns this certificate's SAN entries, if they exist. - pub fn subject_alt_names<'b>(&'b self) -> Option<GeneralNames<'b>> { + pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> { unsafe { - let stack = ffi::X509_get_ext_d2i(self.0, - Nid::SubjectAltName as c_int, + let stack = ffi::X509_get_ext_d2i(self.as_ptr(), + ffi::NID_subject_alt_name, ptr::null_mut(), ptr::null_mut()); if stack.is_null() { return None; } - Some(GeneralNames { - stack: stack as *mut _, - m: PhantomData, - }) + Some(Stack::from_ptr(stack as *mut _)) } } pub fn public_key(&self) -> Result<PKey, ErrorStack> { unsafe { - let pkey = try_ssl_null!(ffi::X509_get_pubkey(self.0)); + let pkey = try!(cvt_p(ffi::X509_get_pubkey(self.as_ptr()))); Ok(PKey::from_ptr(pkey)) } } /// Returns certificate fingerprint calculated using provided hash - pub fn fingerprint(&self, hash_type: hash::Type) -> Result<Vec<u8>, ErrorStack> { + pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> { unsafe { - let evp = hash_type.evp_md(); + let evp = hash_type.as_ptr(); let mut len = ffi::EVP_MAX_MD_SIZE; let mut buf = vec![0u8; len as usize]; - try_ssl!(ffi::X509_digest(self.0, evp, buf.as_mut_ptr() as *mut _, &mut len)); + try!(cvt(ffi::X509_digest(self.as_ptr(), evp, buf.as_mut_ptr() as *mut _, &mut len))); buf.truncate(len as usize); Ok(buf) } } /// Returns certificate Not After validity period. - /// Requires the `x509_expiry` feature. - #[cfg(feature = "x509_expiry")] - pub fn not_after<'b>(&'b self) -> Asn1TimeRef<'b> { + pub fn not_after<'a>(&'a self) -> &'a Asn1TimeRef { unsafe { - let date = ::c_helpers::rust_0_8_X509_get_notAfter(self.0); + let date = compat::X509_get_notAfter(self.as_ptr()); assert!(!date.is_null()); Asn1TimeRef::from_ptr(date) } } /// Returns certificate Not Before validity period. - /// Requires the `x509_expiry` feature. - #[cfg(feature = "x509_expiry")] - pub fn not_before<'b>(&'b self) -> Asn1TimeRef<'b> { + pub fn not_before<'a>(&'a self) -> &'a Asn1TimeRef { unsafe { - let date = ::c_helpers::rust_0_8_X509_get_notBefore(self.0); + let date = compat::X509_get_notBefore(self.as_ptr()); assert!(!date.is_null()); Asn1TimeRef::from_ptr(date) } @@ -463,7 +418,7 @@ impl<'a> X509Ref<'a> { pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> { let mem_bio = try!(MemBio::new()); unsafe { - try_ssl!(ffi::PEM_write_bio_X509(mem_bio.as_ptr(), self.0)); + try!(cvt(ffi::PEM_write_bio_X509(mem_bio.as_ptr(), self.as_ptr()))); } Ok(mem_bio.get_buf().to_owned()) } @@ -472,33 +427,30 @@ impl<'a> X509Ref<'a> { pub fn to_der(&self) -> Result<Vec<u8>, ErrorStack> { let mem_bio = try!(MemBio::new()); unsafe { - ffi::i2d_X509_bio(mem_bio.as_ptr(), self.0); + ffi::i2d_X509_bio(mem_bio.as_ptr(), self.as_ptr()); } Ok(mem_bio.get_buf().to_owned()) } } -/// An owned public key certificate. -pub struct X509(X509Ref<'static>); - -impl X509 { - /// Returns a new `X509`, taking ownership of the handle. - pub unsafe fn from_ptr(x509: *mut ffi::X509) -> X509 { - X509(X509Ref::from_ptr(x509)) - } +impl ToOwned for X509Ref { + type Owned = X509; - /// - #[deprecated(note = "renamed to `X509::from_ptr`", since = "0.8.1")] - pub unsafe fn new(x509: *mut ffi::X509) -> X509 { - X509::from_ptr(x509) + fn to_owned(&self) -> X509 { + unsafe { + compat::X509_up_ref(self.as_ptr()); + X509::from_ptr(self.as_ptr()) + } } +} +impl X509 { /// Reads a certificate from DER. pub fn from_der(buf: &[u8]) -> Result<X509, ErrorStack> { unsafe { - let mut ptr = buf.as_ptr() as *mut _; + let mut ptr = buf.as_ptr(); let len = cmp::min(buf.len(), c_long::max_value() as usize) as c_long; - let x509 = try_ssl_null!(ffi::d2i_X509(ptr::null_mut(), &mut ptr, len)); + let x509 = try!(cvt_p(ffi::d2i_X509(ptr::null_mut(), &mut ptr, len))); Ok(X509::from_ptr(x509)) } } @@ -507,103 +459,99 @@ impl X509 { pub fn from_pem(buf: &[u8]) -> Result<X509, ErrorStack> { let mem_bio = try!(MemBioSlice::new(buf)); unsafe { - let handle = try_ssl_null!(ffi::PEM_read_bio_X509(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut())); + let handle = try!(cvt_p(ffi::PEM_read_bio_X509(mem_bio.as_ptr(), + ptr::null_mut(), + None, + ptr::null_mut()))); Ok(X509::from_ptr(handle)) } } } -impl Deref for X509 { - type Target = X509Ref<'static>; +impl Clone for X509 { + fn clone(&self) -> X509 { + self.to_owned() + } +} - fn deref(&self) -> &X509Ref<'static> { - &self.0 +impl AsRef<X509Ref> for X509 { + fn as_ref(&self) -> &X509Ref { + &*self } } -#[cfg(feature = "x509_clone")] -impl Clone for X509 { - /// Requires the `x509_clone` feature. - fn clone(&self) -> X509 { - unsafe { - ::c_helpers::rust_0_8_X509_clone(self.as_ptr()); - X509::new(self.as_ptr()) - } +impl AsRef<X509Ref> for X509Ref { + fn as_ref(&self) -> &X509Ref { + self } } -impl Drop for X509 { - fn drop(&mut self) { - unsafe { ffi::X509_free(self.as_ptr()) }; +impl Borrow<X509Ref> for X509 { + fn borrow(&self) -> &X509Ref { + &*self } } -pub struct X509Name<'x>(*mut ffi::X509_NAME, PhantomData<&'x ()>); +impl Stackable for X509 { + type StackType = ffi::stack_st_X509; +} -impl<'x> X509Name<'x> { - pub fn text_by_nid(&self, nid: Nid) -> Option<SslString> { - unsafe { - let loc = ffi::X509_NAME_get_index_by_NID(self.0, nid as c_int, -1); - if loc == -1 { - return None; - } +type_!(X509Name, X509NameRef, ffi::X509_NAME, ffi::X509_NAME_free); - let ne = ffi::X509_NAME_get_entry(self.0, loc); - if ne.is_null() { - return None; - } +impl X509NameRef { + pub fn entries_by_nid<'a>(&'a self, nid: Nid) -> X509NameEntries<'a> { + X509NameEntries { + name: self, + nid: nid, + loc: -1, + } + } +} - let asn1_str = ffi::X509_NAME_ENTRY_get_data(ne); - if asn1_str.is_null() { - return None; - } +pub struct X509NameEntries<'a> { + name: &'a X509NameRef, + nid: Nid, + loc: c_int, +} - let mut str_from_asn1: *mut c_char = ptr::null_mut(); - let len = ffi::ASN1_STRING_to_UTF8(&mut str_from_asn1, asn1_str); +impl<'a> Iterator for X509NameEntries<'a> { + type Item = &'a X509NameEntryRef; + + fn next(&mut self) -> Option<&'a X509NameEntryRef> { + unsafe { + self.loc = + ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), self.nid.as_raw(), self.loc); - if len < 0 { + if self.loc == -1 { return None; } - assert!(!str_from_asn1.is_null()); + let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc); + assert!(!entry.is_null()); - Some(SslString::new(str_from_asn1, len)) + Some(X509NameEntryRef::from_ptr(entry)) } } } -/// A certificate signing request -pub struct X509Req(*mut ffi::X509_REQ); +type_!(X509NameEntry, X509NameEntryRef, ffi::X509_NAME_ENTRY, ffi::X509_NAME_ENTRY_free); -impl X509Req { - /// Creates new from handle - pub unsafe fn new(handle: *mut ffi::X509_REQ) -> X509Req { - X509Req(handle) - } - - pub fn as_ptr(&self) -> *mut ffi::X509_REQ { - self.0 - } - - /// Reads CSR from PEM - pub fn from_pem(buf: &[u8]) -> Result<X509Req, ErrorStack> { - let mem_bio = try!(MemBioSlice::new(buf)); +impl X509NameEntryRef { + pub fn data(&self) -> &Asn1StringRef { unsafe { - let handle = try_ssl_null!(ffi::PEM_read_bio_X509_REQ(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut())); - Ok(X509Req::new(handle)) + let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr()); + Asn1StringRef::from_ptr(data) } } +} + +type_!(X509Req, X509ReqRef, ffi::X509_REQ, ffi::X509_REQ_free); +impl X509ReqRef { /// Writes CSR as PEM pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> { let mem_bio = try!(MemBio::new()); - if unsafe { ffi::PEM_write_bio_X509_REQ(mem_bio.as_ptr(), self.0) } != 1 { + if unsafe { ffi::PEM_write_bio_X509_REQ(mem_bio.as_ptr(), self.as_ptr()) } != 1 { return Err(ErrorStack::get()); } Ok(mem_bio.get_buf().to_owned()) @@ -613,15 +561,23 @@ impl X509Req { pub fn to_der(&self) -> Result<Vec<u8>, ErrorStack> { let mem_bio = try!(MemBio::new()); unsafe { - ffi::i2d_X509_REQ_bio(mem_bio.as_ptr(), self.0); + ffi::i2d_X509_REQ_bio(mem_bio.as_ptr(), self.as_ptr()); } Ok(mem_bio.get_buf().to_owned()) } } -impl Drop for X509Req { - fn drop(&mut self) { - unsafe { ffi::X509_REQ_free(self.0) }; +impl X509Req { + /// Reads CSR from PEM + pub fn from_pem(buf: &[u8]) -> Result<X509Req, ErrorStack> { + let mem_bio = try!(MemBioSlice::new(buf)); + unsafe { + let handle = try!(cvt_p(ffi::PEM_read_bio_X509_REQ(mem_bio.as_ptr(), + ptr::null_mut(), + None, + ptr::null_mut()))); + Ok(X509Req::from_ptr(handle)) + } } } @@ -694,184 +650,72 @@ impl<'a> Iterator for ExtensionsIter<'a> { } } -macro_rules! make_validation_error( - ($ok_val:ident, $($name:ident = $val:ident,)+) => ( - #[derive(Copy, Clone)] - pub enum X509ValidationError { - $($name,)+ - X509UnknownError(c_int) - } - - impl X509ValidationError { - #[doc(hidden)] - pub fn from_raw(err: c_int) -> Option<X509ValidationError> { - match err { - ffi::$ok_val => None, - $(ffi::$val => Some(X509ValidationError::$name),)+ - err => Some(X509ValidationError::X509UnknownError(err)) - } - } - } - ) -); - -make_validation_error!(X509_V_OK, - X509UnableToGetIssuerCert = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, - X509UnableToGetCrl = X509_V_ERR_UNABLE_TO_GET_CRL, - X509UnableToDecryptCertSignature = X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE, - X509UnableToDecryptCrlSignature = X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE, - X509UnableToDecodeIssuerPublicKey = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, - X509CertSignatureFailure = X509_V_ERR_CERT_SIGNATURE_FAILURE, - X509CrlSignatureFailure = X509_V_ERR_CRL_SIGNATURE_FAILURE, - X509CertNotYetValid = X509_V_ERR_CERT_NOT_YET_VALID, - X509CertHasExpired = X509_V_ERR_CERT_HAS_EXPIRED, - X509CrlNotYetValid = X509_V_ERR_CRL_NOT_YET_VALID, - X509CrlHasExpired = X509_V_ERR_CRL_HAS_EXPIRED, - X509ErrorInCertNotBeforeField = X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD, - X509ErrorInCertNotAfterField = X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD, - X509ErrorInCrlLastUpdateField = X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD, - X509ErrorInCrlNextUpdateField = X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD, - X509OutOfMem = X509_V_ERR_OUT_OF_MEM, - X509DepthZeroSelfSignedCert = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, - X509SelfSignedCertInChain = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, - X509UnableToGetIssuerCertLocally = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, - X509UnableToVerifyLeafSignature = X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, - X509CertChainTooLong = X509_V_ERR_CERT_CHAIN_TOO_LONG, - X509CertRevoked = X509_V_ERR_CERT_REVOKED, - X509InvalidCA = X509_V_ERR_INVALID_CA, - X509PathLengthExceeded = X509_V_ERR_PATH_LENGTH_EXCEEDED, - X509InvalidPurpose = X509_V_ERR_INVALID_PURPOSE, - X509CertUntrusted = X509_V_ERR_CERT_UNTRUSTED, - X509CertRejected = X509_V_ERR_CERT_REJECTED, - X509SubjectIssuerMismatch = X509_V_ERR_SUBJECT_ISSUER_MISMATCH, - X509AkidSkidMismatch = X509_V_ERR_AKID_SKID_MISMATCH, - X509AkidIssuerSerialMismatch = X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH, - X509KeyusageNoCertsign = X509_V_ERR_KEYUSAGE_NO_CERTSIGN, - X509UnableToGetCrlIssuer = X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, - X509UnhandledCriticalExtension = X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, - X509KeyusageNoCrlSign = X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, - X509UnhandledCriticalCrlExtension = X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, - X509InvalidNonCA = X509_V_ERR_INVALID_NON_CA, - X509ProxyPathLengthExceeded = X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, - X509KeyusageNoDigitalSignature = X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, - X509ProxyCertificatesNotAllowed = X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, - X509InvalidExtension = X509_V_ERR_INVALID_EXTENSION, - X509InavlidPolicyExtension = X509_V_ERR_INVALID_POLICY_EXTENSION, - X509NoExplicitPolicy = X509_V_ERR_NO_EXPLICIT_POLICY, - X509DifferentCrlScope = X509_V_ERR_DIFFERENT_CRL_SCOPE, - X509UnsupportedExtensionFeature = X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, - X509UnnestedResource = X509_V_ERR_UNNESTED_RESOURCE, - X509PermittedVolation = X509_V_ERR_PERMITTED_VIOLATION, - X509ExcludedViolation = X509_V_ERR_EXCLUDED_VIOLATION, - X509SubtreeMinmax = X509_V_ERR_SUBTREE_MINMAX, - X509UnsupportedConstraintType = X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, - X509UnsupportedConstraintSyntax = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, - X509UnsupportedNameSyntax = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, - X509CrlPathValidationError= X509_V_ERR_CRL_PATH_VALIDATION_ERROR, - X509ApplicationVerification = X509_V_ERR_APPLICATION_VERIFICATION, -); - -// FIXME remove lifetime param for 0.9 -/// A collection of OpenSSL `GENERAL_NAME`s. -pub struct GeneralNames<'a> { - stack: *mut ffi::stack_st_GENERAL_NAME, - m: PhantomData<&'a ()>, +pub struct X509VerifyError(c_long); + +impl fmt::Debug for X509VerifyError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("X509VerifyError") + .field("code", &self.0) + .field("error", &self.error_string()) + .finish() + } } -impl<'a> Drop for GeneralNames<'a> { - 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)); - } +impl fmt::Display for X509VerifyError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(self.error_string()) } } -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 } +impl Error for X509VerifyError { + fn description(&self) -> &str { + "an X509 validation error" } +} - /// Returns the specified `GeneralName`. +impl X509VerifyError { + /// Creates an `X509VerifyError` from a raw error number. /// - /// # Panics + /// `None` will be returned if `err` is `X509_V_OK`. /// - /// 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, + /// # Safety + /// + /// Some methods on `X509VerifyError` are not thread safe if the error + /// number is invalid. + pub unsafe fn from_raw(err: c_long) -> Option<X509VerifyError> { + if err == ffi::X509_V_OK as c_long { + None + } else { + Some(X509VerifyError(err)) } } -} - -impl<'a> IntoIterator for &'a GeneralNames<'a> { - type Item = GeneralName<'a>; - type IntoIter = GeneralNamesIter<'a>; - fn into_iter(self) -> GeneralNamesIter<'a> { - self.iter() + pub fn as_raw(&self) -> c_long { + self.0 } -} -/// 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>; + pub fn error_string(&self) -> &'static str { + ffi::init(); - 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 + unsafe { + let s = ffi::X509_verify_cert_error_string(self.0); + str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap() } } - - fn size_hint(&self) -> (usize, Option<usize>) { - let size = self.names.len() - self.idx; - (size, Some(size)) - } } -impl<'a> ExactSizeIterator for GeneralNamesIter<'a> {} +type_!(GeneralName, GeneralNameRef, ffi::GENERAL_NAME, ffi::GENERAL_NAME_free); -/// An OpenSSL `GENERAL_NAME`. -pub struct GeneralName<'a> { - name: *const ffi::GENERAL_NAME, - m: PhantomData<&'a ()>, -} - -impl<'a> GeneralName<'a> { +impl GeneralNameRef { /// 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 = ffi::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 @@ -884,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 = ffi::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 @@ -904,3 +752,44 @@ fn test_negative_serial() { "All serials should be positive"); } } + +#[cfg(ossl110)] +mod compat { + pub use ffi::X509_getm_notAfter as X509_get_notAfter; + pub use ffi::X509_getm_notBefore as X509_get_notBefore; + pub use ffi::X509_up_ref; + pub use ffi::X509_get0_extensions; +} + +#[cfg(ossl10x)] +#[allow(bad_style)] +mod compat { + use libc::c_int; + use ffi; + + pub unsafe fn X509_get_notAfter(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME { + (*(*(*x).cert_info).validity).notAfter + } + + pub unsafe fn X509_get_notBefore(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME { + (*(*(*x).cert_info).validity).notBefore + } + + pub unsafe fn X509_up_ref(x: *mut ffi::X509) { + ffi::CRYPTO_add_lock(&mut (*x).references, + 1, + ffi::CRYPTO_LOCK_X509, + "mod.rs\0".as_ptr() as *const _, + line!() as c_int); + } + + pub unsafe fn X509_get0_extensions(cert: *const ffi::X509) + -> *const ffi::stack_st_X509_EXTENSION { + let info = (*cert).cert_info; + if info.is_null() { + 0 as *mut _ + } else { + (*info).extensions + } + } +} |