variant_ssl/x509/
store.rs

1//! Describe a context in which to verify an `X509` certificate.
2//!
3//! The `X509` certificate store holds trusted CA certificates used to verify
4//! peer certificates.
5//!
6//! # Example
7//!
8//! ```rust
9//! use openssl::x509::store::{X509StoreBuilder, X509Store};
10//! use openssl::x509::{X509, X509Name};
11//! use openssl::asn1::Asn1Time;
12//! use openssl::pkey::PKey;
13//! use openssl::hash::MessageDigest;
14//! use openssl::rsa::Rsa;
15//! use openssl::nid::Nid;
16//!
17//! let rsa = Rsa::generate(2048).unwrap();
18//! let pkey = PKey::from_rsa(rsa).unwrap();
19//!
20//! let mut name = X509Name::builder().unwrap();
21//! name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com").unwrap();
22//! let name = name.build();
23//!
24//! // Sep 27th, 2016
25//! let sample_time = Asn1Time::from_unix(1474934400).unwrap();
26//!
27//! let mut builder = X509::builder().unwrap();
28//! builder.set_version(2).unwrap();
29//! builder.set_subject_name(&name).unwrap();
30//! builder.set_issuer_name(&name).unwrap();
31//! builder.set_pubkey(&pkey).unwrap();
32//! builder.set_not_before(&sample_time);
33//! builder.set_not_after(&sample_time);
34//! builder.sign(&pkey, MessageDigest::sha256()).unwrap();
35//!
36//! let certificate: X509 = builder.build();
37//!
38//! let mut builder = X509StoreBuilder::new().unwrap();
39//! let _ = builder.add_cert(certificate);
40//!
41//! let store: X509Store = builder.build();
42//! ```
43
44use cfg_if::cfg_if;
45use foreign_types::{ForeignType, ForeignTypeRef};
46use std::mem;
47
48use crate::error::ErrorStack;
49#[cfg(not(any(boringssl, awslc)))]
50use crate::ssl::SslFiletype;
51#[cfg(any(ossl300, boringssl))]
52use crate::stack::Stack;
53use crate::util::ForeignTypeRefExt;
54use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef};
55#[cfg(any(ossl330, boringssl))]
56use crate::x509::X509Object;
57use crate::x509::{X509PurposeId, X509};
58use crate::{cvt, cvt_p};
59use openssl_macros::corresponds;
60#[cfg(not(any(boringssl, awslc)))]
61use std::ffi::CString;
62#[cfg(not(any(boringssl, awslc)))]
63use std::path::Path;
64
65foreign_type_and_impl_send_sync! {
66    type CType = ffi::X509_STORE;
67    fn drop = ffi::X509_STORE_free;
68
69    /// A builder type used to construct an `X509Store`.
70    pub struct X509StoreBuilder;
71    /// A reference to an [`X509StoreBuilder`].
72    pub struct X509StoreBuilderRef;
73}
74
75impl X509StoreBuilder {
76    /// Returns a builder for a certificate store.
77    ///
78    /// The store is initially empty.
79    #[corresponds(X509_STORE_new)]
80    pub fn new() -> Result<X509StoreBuilder, ErrorStack> {
81        unsafe {
82            ffi::init();
83
84            cvt_p(ffi::X509_STORE_new()).map(X509StoreBuilder)
85        }
86    }
87
88    /// Constructs the `X509Store`.
89    pub fn build(self) -> X509Store {
90        let store = X509Store(self.0);
91        mem::forget(self);
92        store
93    }
94}
95
96impl X509StoreBuilderRef {
97    /// Adds a certificate to the certificate store.
98    // FIXME should take an &X509Ref
99    #[corresponds(X509_STORE_add_cert)]
100    pub fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> {
101        unsafe { cvt(ffi::X509_STORE_add_cert(self.as_ptr(), cert.as_ptr())).map(|_| ()) }
102    }
103
104    /// Load certificates from their default locations.
105    ///
106    /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR`
107    /// environment variables if present, or defaults specified at OpenSSL
108    /// build time otherwise.
109    #[corresponds(X509_STORE_set_default_paths)]
110    pub fn set_default_paths(&mut self) -> Result<(), ErrorStack> {
111        unsafe { cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ()) }
112    }
113
114    /// Adds a lookup method to the store.
115    #[corresponds(X509_STORE_add_lookup)]
116    pub fn add_lookup<T>(
117        &mut self,
118        method: &'static X509LookupMethodRef<T>,
119    ) -> Result<&mut X509LookupRef<T>, ErrorStack> {
120        let lookup = unsafe { ffi::X509_STORE_add_lookup(self.as_ptr(), method.as_ptr()) };
121        cvt_p(lookup).map(|ptr| unsafe { X509LookupRef::from_ptr_mut(ptr) })
122    }
123
124    /// Sets certificate chain validation related flags.
125    #[corresponds(X509_STORE_set_flags)]
126    pub fn set_flags(&mut self, flags: X509VerifyFlags) -> Result<(), ErrorStack> {
127        unsafe { cvt(ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits())).map(|_| ()) }
128    }
129
130    /// Sets the certificate purpose.
131    /// The purpose value can be obtained by `X509PurposeRef::get_by_sname()`
132    #[corresponds(X509_STORE_set_purpose)]
133    pub fn set_purpose(&mut self, purpose: X509PurposeId) -> Result<(), ErrorStack> {
134        unsafe { cvt(ffi::X509_STORE_set_purpose(self.as_ptr(), purpose.as_raw())).map(|_| ()) }
135    }
136
137    /// Sets certificate chain validation related parameters.
138    #[corresponds[X509_STORE_set1_param]]
139    pub fn set_param(&mut self, param: &X509VerifyParamRef) -> Result<(), ErrorStack> {
140        unsafe { cvt(ffi::X509_STORE_set1_param(self.as_ptr(), param.as_ptr())).map(|_| ()) }
141    }
142}
143
144generic_foreign_type_and_impl_send_sync! {
145    type CType = ffi::X509_LOOKUP;
146    fn drop = ffi::X509_LOOKUP_free;
147
148    /// Information used by an `X509Store` to look up certificates and CRLs.
149    pub struct X509Lookup<T>;
150    /// A reference to an [`X509Lookup`].
151    pub struct X509LookupRef<T>;
152}
153
154/// Marker type corresponding to the [`X509_LOOKUP_hash_dir`] lookup method.
155///
156/// [`X509_LOOKUP_hash_dir`]: https://docs.openssl.org/master/man3/X509_LOOKUP_hash_dir/
157// FIXME should be an enum
158pub struct HashDir;
159
160impl X509Lookup<HashDir> {
161    /// Lookup method that loads certificates and CRLs on demand and caches
162    /// them in memory once they are loaded. It also checks for newer CRLs upon
163    /// each lookup, so that newer CRLs are used as soon as they appear in the
164    /// directory.
165    #[corresponds(X509_LOOKUP_hash_dir)]
166    pub fn hash_dir() -> &'static X509LookupMethodRef<HashDir> {
167        unsafe { X509LookupMethodRef::from_const_ptr(ffi::X509_LOOKUP_hash_dir()) }
168    }
169}
170
171#[cfg(not(any(boringssl, awslc)))]
172impl X509LookupRef<HashDir> {
173    /// Specifies a directory from which certificates and CRLs will be loaded
174    /// on-demand. Must be used with `X509Lookup::hash_dir`.
175    #[corresponds(X509_LOOKUP_add_dir)]
176    pub fn add_dir(&mut self, name: &str, file_type: SslFiletype) -> Result<(), ErrorStack> {
177        let name = CString::new(name).unwrap();
178        unsafe {
179            cvt(ffi::X509_LOOKUP_add_dir(
180                self.as_ptr(),
181                name.as_ptr(),
182                file_type.as_raw(),
183            ))
184            .map(|_| ())
185        }
186    }
187}
188
189/// Marker type corresponding to the [`X509_LOOKUP_file`] lookup method.
190///
191/// [`X509_LOOKUP_file`]: https://docs.openssl.org/master/man3/X509_LOOKUP_file/
192pub struct File;
193
194impl X509Lookup<File> {
195    /// Lookup method loads all the certificates or CRLs present in a file
196    /// into memory at the time the file is added as a lookup source.
197    #[corresponds(X509_LOOKUP_file)]
198    pub fn file() -> &'static X509LookupMethodRef<File> {
199        unsafe { X509LookupMethodRef::from_const_ptr(ffi::X509_LOOKUP_file()) }
200    }
201}
202
203#[cfg(not(any(boringssl, awslc)))]
204impl X509LookupRef<File> {
205    /// Specifies a file from which certificates will be loaded
206    #[corresponds(X509_load_cert_file)]
207    // FIXME should return 'Result<i32, ErrorStack' like load_crl_file
208    pub fn load_cert_file<P: AsRef<Path>>(
209        &mut self,
210        file: P,
211        file_type: SslFiletype,
212    ) -> Result<(), ErrorStack> {
213        let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
214        unsafe {
215            cvt(ffi::X509_load_cert_file(
216                self.as_ptr(),
217                file.as_ptr(),
218                file_type.as_raw(),
219            ))
220            .map(|_| ())
221        }
222    }
223
224    /// Specifies a file from which certificate revocation lists will be loaded
225    #[corresponds(X509_load_crl_file)]
226    pub fn load_crl_file<P: AsRef<Path>>(
227        &mut self,
228        file: P,
229        file_type: SslFiletype,
230    ) -> Result<i32, ErrorStack> {
231        let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
232        unsafe {
233            cvt(ffi::X509_load_crl_file(
234                self.as_ptr(),
235                file.as_ptr(),
236                file_type.as_raw(),
237            ))
238        }
239    }
240}
241
242generic_foreign_type_and_impl_send_sync! {
243    type CType = ffi::X509_LOOKUP_METHOD;
244    fn drop = X509_LOOKUP_meth_free;
245
246    /// Method used to look up certificates and CRLs.
247    pub struct X509LookupMethod<T>;
248    /// A reference to an [`X509LookupMethod`].
249    pub struct X509LookupMethodRef<T>;
250}
251
252foreign_type_and_impl_send_sync! {
253    type CType = ffi::X509_STORE;
254    fn drop = ffi::X509_STORE_free;
255
256    /// A certificate store to hold trusted `X509` certificates.
257    pub struct X509Store;
258    /// Reference to an `X509Store`.
259    pub struct X509StoreRef;
260}
261
262impl X509StoreRef {
263    /// Returns a snapshot of all objects in the store's X509 cache.
264    ///
265    /// The cache contains X509 and X509_CRL objects.
266    #[corresponds(X509_STORE_get1_objects)]
267    #[cfg(any(ossl330, boringssl))]
268    pub fn objects(&self) -> Stack<X509Object> {
269        unsafe { Stack::from_ptr(ffi::X509_STORE_get1_objects(self.as_ptr())) }
270    }
271
272    /// Returns a stack of all the certificates in this store.
273    #[corresponds(X509_STORE_get1_all_certs)]
274    #[cfg(ossl300)]
275    pub fn all_certificates(&self) -> Stack<X509> {
276        unsafe { Stack::from_ptr(ffi::X509_STORE_get1_all_certs(self.as_ptr())) }
277    }
278}
279
280cfg_if! {
281    if #[cfg(ossl110)] {
282        use ffi::X509_LOOKUP_meth_free;
283    } else {
284        #[allow(bad_style)]
285        unsafe fn X509_LOOKUP_meth_free(_x: *mut ffi::X509_LOOKUP_METHOD) {}
286    }
287}