diff --git a/openssl-sys/src/handwritten/x509_vfy.rs b/openssl-sys/src/handwritten/x509_vfy.rs index d8768d7c8c..ef2a6aac94 100644 --- a/openssl-sys/src/handwritten/x509_vfy.rs +++ b/openssl-sys/src/handwritten/x509_vfy.rs @@ -47,6 +47,12 @@ extern "C" { pub fn X509_STORE_set_flags(store: *mut X509_STORE, flags: c_ulong) -> c_int; } +const_ptr_api! { + extern "C" { + pub fn X509_STORE_set1_param(store: *mut X509_STORE, pm: #[const_ptr_if(ossl300)] X509_VERIFY_PARAM) -> c_int; + } +} + const_ptr_api! { extern "C" { pub fn X509_STORE_CTX_get_ex_data(ctx: #[const_ptr_if(ossl300)] X509_STORE_CTX, idx: c_int) -> *mut c_void; @@ -73,6 +79,8 @@ cfg_if! { } extern "C" { + #[cfg(any(ossl102, libressl261))] + pub fn X509_VERIFY_PARAM_new() -> *mut X509_VERIFY_PARAM; #[cfg(any(ossl102, libressl261))] pub fn X509_VERIFY_PARAM_free(param: *mut X509_VERIFY_PARAM); @@ -80,6 +88,12 @@ extern "C" { pub fn X509_VERIFY_PARAM_set_flags(param: *mut X509_VERIFY_PARAM, flags: c_ulong) -> c_int; #[cfg(any(ossl102, libressl261))] pub fn X509_VERIFY_PARAM_clear_flags(param: *mut X509_VERIFY_PARAM, flags: c_ulong) -> c_int; + + #[cfg(any(ossl102, libressl261))] + pub fn X509_VERIFY_PARAM_set_time(param: *mut X509_VERIFY_PARAM, t: time_t); + + #[cfg(any(ossl102, libressl261))] + pub fn X509_VERIFY_PARAM_set_depth(param: *mut X509_VERIFY_PARAM, depth: c_int); } const_ptr_api! { extern "C" { diff --git a/openssl/src/x509/store.rs b/openssl/src/x509/store.rs index a08d1e2ef9..4154d92121 100644 --- a/openssl/src/x509/store.rs +++ b/openssl/src/x509/store.rs @@ -50,7 +50,7 @@ use crate::error::ErrorStack; use crate::ssl::SslFiletype; use crate::stack::StackRef; #[cfg(any(ossl102, libressl261))] -use crate::x509::verify::X509VerifyFlags; +use crate::x509::verify::{X509VerifyFlags, X509VerifyParam}; use crate::x509::{X509Object, X509}; use crate::{cvt, cvt_p}; use openssl_macros::corresponds; @@ -122,6 +122,13 @@ impl X509StoreBuilderRef { pub fn set_flags(&mut self, flags: X509VerifyFlags) -> Result<(), ErrorStack> { unsafe { cvt(ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits())).map(|_| ()) } } + + /// Sets certificate chain validation related parameters. + #[corresponds[X509_STORE_set1_param]] + #[cfg(any(ossl102, libressl261))] + pub fn set_param(&mut self, param: X509VerifyParam) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_STORE_set1_param(self.as_ptr(), param.as_ptr())).map(|_| ()) } + } } generic_foreign_type_and_impl_send_sync! { diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index fcca2c7bf1..6761bb3c90 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -13,11 +13,13 @@ use crate::x509::extension::{ }; use crate::x509::store::X509StoreBuilder; #[cfg(any(ossl102, libressl261))] -use crate::x509::verify::X509VerifyFlags; +use crate::x509::verify::{X509VerifyFlags, X509VerifyParam}; #[cfg(ossl110)] use crate::x509::X509Builder; use crate::x509::{X509Name, X509Req, X509StoreContext, X509VerifyResult, X509}; use hex::{self, FromHex}; +#[cfg(any(ossl102, libressl261))] +use libc::time_t; fn pkey() -> PKey { let rsa = Rsa::generate(2048).unwrap(); @@ -543,3 +545,92 @@ fn test_name_cmp() { assert_eq!(Ordering::Equal, subject.try_cmp(subject).unwrap()); assert_eq!(Ordering::Greater, subject.try_cmp(issuer).unwrap()); } + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_param_set_time_fails_verification() { + const TEST_T_2030: time_t = 1893456000; + + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params.set_time(TEST_T_2030); + store_bldr.set_param(verify_params).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .error_string(), + "certificate has expired" + ) +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_param_set_time() { + const TEST_T_2020: time_t = 1577836800; + + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params.set_time(TEST_T_2020); + store_bldr.set_param(verify_params).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_param_set_depth() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + // OpenSSL 1.1.0+ considers the root certificate to not be part of the chain, while 1.0.2 does + let expected_depth = if cfg!(any(ossl110, libressl261)) { + 0 + } else { + 1 + }; + verify_params.set_depth(expected_depth); + store_bldr.set_param(verify_params).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} diff --git a/openssl/src/x509/verify.rs b/openssl/src/x509/verify.rs index 4752133996..b04c2f65ce 100644 --- a/openssl/src/x509/verify.rs +++ b/openssl/src/x509/verify.rs @@ -1,10 +1,10 @@ use bitflags::bitflags; use foreign_types::ForeignTypeRef; -use libc::{c_uint, c_ulong}; +use libc::{c_int, c_uint, c_ulong, time_t}; use std::net::IpAddr; -use crate::cvt; use crate::error::ErrorStack; +use crate::{cvt, cvt_p}; use openssl_macros::corresponds; bitflags! { @@ -69,6 +69,17 @@ foreign_type_and_impl_send_sync! { pub struct X509VerifyParamRef; } +impl X509VerifyParam { + /// Create an X509VerifyParam + #[corresponds(X509_VERIFY_PARAM_new)] + pub fn new() -> Result { + unsafe { + ffi::init(); + cvt_p(ffi::X509_VERIFY_PARAM_new()).map(X509VerifyParam) + } + } +} + impl X509VerifyParamRef { /// Set the host flags. #[corresponds(X509_VERIFY_PARAM_set_hostflags)] @@ -139,4 +150,16 @@ impl X509VerifyParamRef { .map(|_| ()) } } + + /// Set the verification time + #[corresponds(X509_VERIFY_PARAM_set_time)] + pub fn set_time(&mut self, time: time_t) { + unsafe { ffi::X509_VERIFY_PARAM_set_time(self.as_ptr(), time) } + } + + /// Set the verification depth + #[corresponds(X509_VERIFY_PARAM_set_depth)] + pub fn set_depth(&mut self, depth: c_int) { + unsafe { ffi::X509_VERIFY_PARAM_set_depth(self.as_ptr(), depth) } + } }