Skip to content
Open
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
7 changes: 7 additions & 0 deletions openssl-sys/src/handwritten/x509_vfy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ extern "C" {
pub fn X509_STORE_CTX_cleanup(ctx: *mut X509_STORE_CTX);

pub fn X509_STORE_add_cert(store: *mut X509_STORE, x: *mut X509) -> c_int;
pub fn X509_STORE_add_crl(xs: *mut X509_STORE, x: *mut X509_CRL) -> c_int;

pub fn X509_STORE_set_default_paths(store: *mut X509_STORE) -> c_int;
pub fn X509_STORE_set_flags(store: *mut X509_STORE, flags: c_ulong) -> c_int;
Expand All @@ -68,6 +69,12 @@ const_ptr_api! {
pub fn X509_STORE_CTX_get_error(ctx: #[const_ptr_if(ossl300)] X509_STORE_CTX) -> c_int;
pub fn X509_STORE_CTX_get_error_depth(ctx: #[const_ptr_if(ossl300)] X509_STORE_CTX) -> c_int;
pub fn X509_STORE_CTX_get_current_cert(ctx: #[const_ptr_if(ossl300)] X509_STORE_CTX) -> *mut X509;
#[cfg(ossl110)]
pub fn X509_STORE_CTX_get1_crls(
ctx: #[const_ptr_if(ossl300)] X509_STORE_CTX,
nm: #[const_ptr_if(ossl300)] X509_NAME,
) -> *mut stack_st_X509_CRL;

}
}
extern "C" {
Expand Down
8 changes: 8 additions & 0 deletions openssl/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ pub trait ForeignTypeExt: ForeignType {
}
}
}

pub fn opt_to_ptr<T: ForeignTypeRef>(o: Option<&T>) -> *mut T::CType {
match o {
None => std::ptr::null_mut(),
Some(p) => p.as_ptr(),
}
}

impl<FT: ForeignType> ForeignTypeExt for FT {}

pub trait ForeignTypeRefExt: ForeignTypeRef {
Expand Down
160 changes: 143 additions & 17 deletions openssl/src/x509/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
use crate::ssl::SslRef;
use crate::stack::{Stack, StackRef, Stackable};
use crate::string::OpensslString;
use crate::util::{self, ForeignTypeExt, ForeignTypeRefExt};
use crate::util::{self, opt_to_ptr, ForeignTypeExt, ForeignTypeRefExt};
use crate::{cvt, cvt_n, cvt_p, cvt_p_const};
use openssl_macros::corresponds;

Expand Down Expand Up @@ -156,6 +156,65 @@ impl X509StoreContextRef {
}
}

/// Initializes this context with the given certificate, certificates chain and certificate
/// store. After initializing the context, the `with_context` closure is called with the prepared
/// context. As long as the closure is running, the context stays initialized and can be used
/// to e.g. verify a certificate. The context will be cleaned up, after the closure finished.
///
/// * `trust` - The certificate store with the trusted certificates.
/// * `cert` - The certificate that should be verified.
/// * `cert_chain` - The certificates chain.
/// * `with_context` - The closure that is called with the initialized context.
///
/// This corresponds to [`X509_STORE_CTX_init`] before calling `with_context` and to
/// [`X509_STORE_CTX_cleanup`] after calling `with_context`.
///
/// [`X509_STORE_CTX_init`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_init.html
/// [`X509_STORE_CTX_cleanup`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_cleanup.html
/// similar to [`X509StoreContextRef::init()`] but with optional cert and cert_chain.
/// Useful to call [`X509StoreContextRef::crls()`] on it.
pub fn init_opt<F, T>(
&mut self,
trust: &store::X509StoreRef,
cert: Option<&X509Ref>,
cert_chain: Option<&StackRef<X509>>,
with_context: F,
) -> Result<T, ErrorStack>
where
F: FnOnce(&mut X509StoreContextRef) -> std::result::Result<T, ErrorStack>,
{
struct Cleanup<'a>(&'a mut X509StoreContextRef);

impl<'a> Drop for Cleanup<'a> {
fn drop(&mut self) {
unsafe {
ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
}
}
}

unsafe {
cvt(ffi::X509_STORE_CTX_init(
self.as_ptr(),
trust.as_ptr(),
opt_to_ptr(cert),
opt_to_ptr(cert_chain),
))?;
}
let cleanup = Cleanup(self);
with_context(cleanup.0)
}

/// Get all Certificate Revocation Lists with the subject currently stored
#[cfg(ossl110)]
#[corresponds(X509_STORE_CTX_get1_crls)]
pub fn crls(&mut self, subj: &X509NameRef) -> std::result::Result<Stack<X509Crl>, ErrorStack> {
unsafe {
let crls = cvt_p(ffi::X509_STORE_CTX_get1_crls(self.as_ptr(), subj.as_ptr()))?;
Ok(Stack::from_ptr(crls))
}
}

/// Verifies the stored certificate.
///
/// Returns `true` if verification succeeds. The `error` method will return the specific
Expand Down Expand Up @@ -1645,8 +1704,7 @@ impl X509RevokedRef {
pub fn revocation_date(&self) -> &Asn1TimeRef {
unsafe {
let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
assert!(!r.is_null());
Asn1TimeRef::from_ptr(r as *mut _)
Asn1TimeRef::from_const_ptr_opt(r).expect("revocation date must not be null")
}
}

Expand All @@ -1655,8 +1713,7 @@ impl X509RevokedRef {
pub fn serial_number(&self) -> &Asn1IntegerRef {
unsafe {
let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
assert!(!r.is_null());
Asn1IntegerRef::from_ptr(r as *mut _)
Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
}
}

Expand Down Expand Up @@ -1736,6 +1793,10 @@ foreign_type_and_impl_send_sync! {
pub struct X509CrlRef;
}

impl Stackable for X509Crl {
type StackType = ffi::stack_st_X509_CRL;
}

/// The status of a certificate in a revoction list
///
/// Corresponds to the return value from the [`X509_CRL_get0_by_*`] methods.
Expand Down Expand Up @@ -1764,14 +1825,14 @@ impl<'a> CrlStatus<'a> {
) -> CrlStatus<'a> {
match status {
0 => CrlStatus::NotRevoked,
1 => {
assert!(!revoked_entry.is_null());
CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
}
2 => {
assert!(!revoked_entry.is_null());
CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
}
1 => CrlStatus::Revoked(
X509RevokedRef::from_const_ptr_opt(revoked_entry)
.expect("revoked entry must no be null"),
),
2 => CrlStatus::RemoveFromCrl(
X509RevokedRef::from_const_ptr_opt(revoked_entry)
.expect("revoked entry must no be null"),
),
_ => unreachable!(
"{}",
"X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
Expand All @@ -1798,6 +1859,39 @@ impl X509Crl {
X509Crl,
ffi::d2i_X509_CRL
}

#[corresponds(PEM_read_bio_X509_CRL)]
pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509Crl>, ErrorStack> {
unsafe {
ffi::init();
let bio = MemBioSlice::new(pem)?;

let mut crls = vec![];
loop {
let r = ffi::PEM_read_bio_X509_CRL(
bio.as_ptr(),
ptr::null_mut(),
None,
ptr::null_mut(),
);
if r.is_null() {
let err = ffi::ERR_peek_last_error();
if ffi::ERR_GET_LIB(err) as X509LenTy == ffi::ERR_LIB_PEM
&& ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE
{
ffi::ERR_clear_error();
break;
}

return Err(ErrorStack::get());
} else {
crls.push(X509Crl(r));
}
}

Ok(crls)
}
}
}

impl X509CrlRef {
Expand Down Expand Up @@ -1834,8 +1928,7 @@ impl X509CrlRef {
pub fn last_update(&self) -> &Asn1TimeRef {
unsafe {
let date = X509_CRL_get0_lastUpdate(self.as_ptr());
assert!(!date.is_null());
Asn1TimeRef::from_ptr(date as *mut _)
Asn1TimeRef::from_const_ptr_opt(date).expect("last update must not be null")
}
}

Expand Down Expand Up @@ -1877,8 +1970,7 @@ impl X509CrlRef {
pub fn issuer_name(&self) -> &X509NameRef {
unsafe {
let name = X509_CRL_get_issuer(self.as_ptr());
assert!(!name.is_null());
X509NameRef::from_ptr(name)
X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
}
}

Expand Down Expand Up @@ -1927,6 +2019,40 @@ impl X509CrlRef {
}
}

impl Ord for X509CrlRef {
fn cmp(&self, other: &Self) -> cmp::Ordering {
// X509_cmp returns a number <0 for less than, 0 for equal and >0 for greater than.
// It can't fail if both pointers are valid, which we know is true.
let cmp = unsafe { ffi::X509_CRL_cmp(self.as_ptr(), other.as_ptr()) };
cmp.cmp(&0)
}
}

impl PartialOrd for X509CrlRef {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}

impl PartialOrd<X509Crl> for X509CrlRef {
fn partial_cmp(&self, other: &X509Crl) -> Option<cmp::Ordering> {
<X509CrlRef as PartialOrd<X509CrlRef>>::partial_cmp(self, other)
}
}

impl PartialEq for X509CrlRef {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == cmp::Ordering::Equal
}
}

impl PartialEq<X509Crl> for X509CrlRef {
fn eq(&self, other: &X509Crl) -> bool {
<X509CrlRef as PartialEq<X509CrlRef>>::eq(self, other)
}
}
impl Eq for X509CrlRef {}

/// The result of peer certificate verification.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct X509VerifyResult(c_int);
Expand Down
8 changes: 8 additions & 0 deletions openssl/src/x509/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ use std::ffi::CString;
#[cfg(not(any(boringssl, awslc)))]
use std::path::Path;

use super::X509CrlRef;

foreign_type_and_impl_send_sync! {
type CType = ffi::X509_STORE;
fn drop = ffi::X509_STORE_free;
Expand Down Expand Up @@ -141,6 +143,12 @@ impl X509StoreBuilderRef {
pub fn set_param(&mut self, param: &X509VerifyParamRef) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::X509_STORE_set1_param(self.as_ptr(), param.as_ptr())).map(|_| ()) }
}

/// Adds the CRL to the store
#[corresponds(X509_STORE_add_crl)]
pub fn add_crl(&mut self, crl: &X509CrlRef) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::X509_STORE_add_crl(self.as_ptr(), crl.as_ptr())).map(|_| ()) }
}
}

generic_foreign_type_and_impl_send_sync! {
Expand Down
28 changes: 28 additions & 0 deletions openssl/src/x509/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1196,3 +1196,31 @@ fn test_store_all_certificates() {

assert_eq!(store.all_certificates().len(), 1);
}

#[test]
#[cfg(ossl110)]
fn test_store_ctx_crls() {
let ca = include_bytes!("../../test/crl-ca.crt");
let ca = X509::from_pem(ca).unwrap();
let crl = include_bytes!("../../test/test.crl");
let crl = X509Crl::from_der(crl).unwrap();
let cert = include_bytes!("../../test/leaf.pem");
let cert = X509::from_pem(cert).unwrap();
assert!(crl.verify(&ca.public_key().unwrap()).unwrap());

let mut store_bldr = X509StoreBuilder::new().unwrap();
store_bldr.add_crl(&crl).unwrap();
let store = store_bldr.build();

let mut context = X509StoreContext::new().unwrap();
assert_eq!(
context
.init_opt(&store, None, None, |c| c.crls(ca.subject_name()))
.unwrap()
.len(),
1
);
assert!(context
.init_opt(&store, None, None, |c| c.crls(cert.subject_name()))
.is_err());
}
Loading