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(boringssl))]
50use crate::ssl::SslFiletype;
51#[cfg(ossl300)]
52use crate::stack::Stack;
53use crate::stack::StackRef;
54use crate::util::ForeignTypeRefExt;
55#[cfg(any(ossl102, libressl261))]
56use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef};
57use crate::x509::{X509Object, X509PurposeId, X509};
58use crate::{cvt, cvt_p};
59use openssl_macros::corresponds;
60#[cfg(not(boringssl))]
61use std::ffi::CString;
62#[cfg(not(boringssl))]
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(op:X509StoreBuilder)
85 }
86 }
87
88 /// Constructs the `X509Store`.
89 pub fn build(self) -> X509Store {
90 let store: X509Store = 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 #[cfg(any(ossl102, libressl261))]
127 pub fn set_flags(&mut self, flags: X509VerifyFlags) -> Result<(), ErrorStack> {
128 unsafe { cvt(ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits())).map(|_| ()) }
129 }
130
131 /// Sets the certificate purpose.
132 /// The purpose value can be obtained by `X509PurposeRef::get_by_sname()`
133 #[corresponds(X509_STORE_set_purpose)]
134 pub fn set_purpose(&mut self, purpose: X509PurposeId) -> Result<(), ErrorStack> {
135 unsafe { cvt(ffi::X509_STORE_set_purpose(self.as_ptr(), purpose.as_raw())).map(|_| ()) }
136 }
137
138 /// Sets certificate chain validation related parameters.
139 #[corresponds[X509_STORE_set1_param]]
140 #[cfg(any(ossl102, libressl261))]
141 pub fn set_param(&mut self, param: &X509VerifyParamRef) -> Result<(), ErrorStack> {
142 unsafe { cvt(ffi::X509_STORE_set1_param(self.as_ptr(), param.as_ptr())).map(|_| ()) }
143 }
144}
145
146generic_foreign_type_and_impl_send_sync! {
147 type CType = ffi::X509_LOOKUP;
148 fn drop = ffi::X509_LOOKUP_free;
149
150 /// Information used by an `X509Store` to look up certificates and CRLs.
151 pub struct X509Lookup<T>;
152 /// A reference to an [`X509Lookup`].
153 pub struct X509LookupRef<T>;
154}
155
156/// Marker type corresponding to the [`X509_LOOKUP_hash_dir`] lookup method.
157///
158/// [`X509_LOOKUP_hash_dir`]: https://www.openssl.org/docs/manmaster/crypto/X509_LOOKUP_hash_dir.html
159// FIXME should be an enum
160pub struct HashDir;
161
162impl X509Lookup<HashDir> {
163 /// Lookup method that loads certificates and CRLs on demand and caches
164 /// them in memory once they are loaded. It also checks for newer CRLs upon
165 /// each lookup, so that newer CRLs are used as soon as they appear in the
166 /// directory.
167 #[corresponds(X509_LOOKUP_hash_dir)]
168 pub fn hash_dir() -> &'static X509LookupMethodRef<HashDir> {
169 unsafe { X509LookupMethodRef::from_const_ptr(ffi::X509_LOOKUP_hash_dir()) }
170 }
171}
172
173#[cfg(not(boringssl))]
174impl X509LookupRef<HashDir> {
175 /// Specifies a directory from which certificates and CRLs will be loaded
176 /// on-demand. Must be used with `X509Lookup::hash_dir`.
177 #[corresponds(X509_LOOKUP_add_dir)]
178 pub fn add_dir(&mut self, name: &str, file_type: SslFiletype) -> Result<(), ErrorStack> {
179 let name: CString = CString::new(name).unwrap();
180 unsafe {
181 cvt(ffi::X509_LOOKUP_add_dir(
182 self.as_ptr(),
183 name.as_ptr(),
184 file_type.as_raw(),
185 ))
186 .map(|_| ())
187 }
188 }
189}
190
191/// Marker type corresponding to the [`X509_LOOKUP_file`] lookup method.
192///
193/// [`X509_LOOKUP_file`]: https://www.openssl.org/docs/man1.1.1/man3/X509_LOOKUP_file.html
194pub struct File;
195
196impl X509Lookup<File> {
197 /// Lookup method loads all the certificates or CRLs present in a file
198 /// into memory at the time the file is added as a lookup source.
199 #[corresponds(X509_LOOKUP_file)]
200 pub fn file() -> &'static X509LookupMethodRef<File> {
201 unsafe { X509LookupMethodRef::from_const_ptr(ffi::X509_LOOKUP_file()) }
202 }
203}
204
205#[cfg(not(boringssl))]
206impl X509LookupRef<File> {
207 /// Specifies a file from which certificates will be loaded
208 #[corresponds(X509_load_cert_file)]
209 // FIXME should return 'Result<i32, ErrorStack' like load_crl_file
210 pub fn load_cert_file<P: AsRef<Path>>(
211 &mut self,
212 file: P,
213 file_type: SslFiletype,
214 ) -> Result<(), ErrorStack> {
215 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
216 unsafe {
217 cvt(ffi::X509_load_cert_file(
218 self.as_ptr(),
219 file.as_ptr(),
220 file_type.as_raw(),
221 ))
222 .map(|_| ())
223 }
224 }
225
226 /// Specifies a file from which certificate revocation lists will be loaded
227 #[corresponds(X509_load_crl_file)]
228 pub fn load_crl_file<P: AsRef<Path>>(
229 &mut self,
230 file: P,
231 file_type: SslFiletype,
232 ) -> Result<i32, ErrorStack> {
233 let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
234 unsafe {
235 cvt(ffi::X509_load_crl_file(
236 self.as_ptr(),
237 file.as_ptr(),
238 file_type.as_raw(),
239 ))
240 }
241 }
242}
243
244generic_foreign_type_and_impl_send_sync! {
245 type CType = ffi::X509_LOOKUP_METHOD;
246 fn drop = X509_LOOKUP_meth_free;
247
248 /// Method used to look up certificates and CRLs.
249 pub struct X509LookupMethod<T>;
250 /// A reference to an [`X509LookupMethod`].
251 pub struct X509LookupMethodRef<T>;
252}
253
254foreign_type_and_impl_send_sync! {
255 type CType = ffi::X509_STORE;
256 fn drop = ffi::X509_STORE_free;
257
258 /// A certificate store to hold trusted `X509` certificates.
259 pub struct X509Store;
260 /// Reference to an `X509Store`.
261 pub struct X509StoreRef;
262}
263
264impl X509StoreRef {
265 /// Get a reference to the cache of certificates in this store.
266 ///
267 /// This method is deprecated. It is **unsound** and will be removed in a
268 /// future version of rust-openssl. `X509StoreRef::all_certificates`
269 /// should be used instead.
270 #[deprecated(
271 note = "This method is unsound, and will be removed in a future version of rust-openssl. X509StoreRef::all_certificates should be used instead."
272 )]
273 #[corresponds(X509_STORE_get0_objects)]
274 pub fn objects(&self) -> &StackRef<X509Object> {
275 unsafe { StackRef::from_ptr(X509_STORE_get0_objects(self.as_ptr())) }
276 }
277
278 /// Returns a stack of all the certificates in this store.
279 #[corresponds(X509_STORE_get1_all_certs)]
280 #[cfg(ossl300)]
281 pub fn all_certificates(&self) -> Stack<X509> {
282 unsafe { Stack::from_ptr(ffi::X509_STORE_get1_all_certs(self.as_ptr())) }
283 }
284}
285
286cfg_if! {
287 if #[cfg(any(boringssl, ossl110, libressl270))] {
288 use ffi::X509_STORE_get0_objects;
289 } else {
290 #[allow(bad_style)]
291 unsafe fn X509_STORE_get0_objects(x: *mut ffi::X509_STORE) -> *mut ffi::stack_st_X509_OBJECT {
292 (*x).objs
293 }
294 }
295}
296
297cfg_if! {
298 if #[cfg(ossl110)] {
299 use ffi::X509_LOOKUP_meth_free;
300 } else {
301 #[allow(bad_style)]
302 unsafe fn X509_LOOKUP_meth_free(_x: *mut ffi::X509_LOOKUP_METHOD) {}
303 }
304}
305