openssl/x509/
mod.rs

1//! The standard defining the format of public key certificates.
2//!
3//! An `X509` certificate binds an identity to a public key, and is either
4//! signed by a certificate authority (CA) or self-signed. An entity that gets
5//! a hold of a certificate can both verify your identity (via a CA) and encrypt
6//! data with the included public key. `X509` certificates are used in many
7//! Internet protocols, including SSL/TLS, which is the basis for HTTPS,
8//! the secure protocol for browsing the web.
9
10use cfg_if::cfg_if;
11use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
12use libc::{c_int, c_long, c_uint, c_void};
13use std::cmp::{self, Ordering};
14use std::convert::{TryFrom, TryInto};
15use std::error::Error;
16use std::ffi::{CStr, CString};
17use std::fmt;
18use std::marker::PhantomData;
19use std::mem;
20use std::net::IpAddr;
21use std::path::Path;
22use std::ptr;
23use std::str;
24
25use crate::asn1::{
26    Asn1BitStringRef, Asn1Enumerated, Asn1IntegerRef, Asn1Object, Asn1ObjectRef,
27    Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
28};
29use crate::bio::MemBioSlice;
30use crate::conf::ConfRef;
31use crate::error::ErrorStack;
32use crate::ex_data::Index;
33use crate::hash::{DigestBytes, MessageDigest};
34use crate::nid::Nid;
35use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
36use crate::ssl::SslRef;
37use crate::stack::{Stack, StackRef, Stackable};
38use crate::string::OpensslString;
39use crate::util::{self, ForeignTypeExt, ForeignTypeRefExt};
40use crate::{cvt, cvt_n, cvt_p, cvt_p_const};
41use openssl_macros::corresponds;
42
43pub mod verify;
44
45pub mod extension;
46pub mod store;
47
48#[cfg(test)]
49mod tests;
50
51/// A type of X509 extension.
52///
53/// # Safety
54/// The value of NID and Output must match those in OpenSSL so that
55/// `Output::from_ptr_opt(*_get_ext_d2i(*, NID, ...))` is valid.
56pub unsafe trait ExtensionType {
57    const NID: Nid;
58    type Output: ForeignType;
59}
60
61foreign_type_and_impl_send_sync! {
62    type CType = ffi::X509_STORE_CTX;
63    fn drop = ffi::X509_STORE_CTX_free;
64
65    /// An `X509` certificate store context.
66    pub struct X509StoreContext;
67
68    /// A reference to an [`X509StoreContext`].
69    pub struct X509StoreContextRef;
70}
71
72impl X509StoreContext {
73    /// Returns the index which can be used to obtain a reference to the `Ssl` associated with a
74    /// context.
75    #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
76    pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
77        unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
78    }
79
80    /// Creates a new `X509StoreContext` instance.
81    #[corresponds(X509_STORE_CTX_new)]
82    pub fn new() -> Result<X509StoreContext, ErrorStack> {
83        unsafe {
84            ffi::init();
85            cvt_p(ffi::X509_STORE_CTX_new()).map(X509StoreContext)
86        }
87    }
88}
89
90impl X509StoreContextRef {
91    /// Returns application data pertaining to an `X509` store context.
92    #[corresponds(X509_STORE_CTX_get_ex_data)]
93    pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
94        unsafe {
95            let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw());
96            if data.is_null() {
97                None
98            } else {
99                Some(&*(data as *const T))
100            }
101        }
102    }
103
104    /// Returns the error code of the context.
105    #[corresponds(X509_STORE_CTX_get_error)]
106    pub fn error(&self) -> X509VerifyResult {
107        unsafe { X509VerifyResult::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
108    }
109
110    /// Initializes this context with the given certificate, certificates chain and certificate
111    /// store. After initializing the context, the `with_context` closure is called with the prepared
112    /// context. As long as the closure is running, the context stays initialized and can be used
113    /// to e.g. verify a certificate. The context will be cleaned up, after the closure finished.
114    ///
115    /// * `trust` - The certificate store with the trusted certificates.
116    /// * `cert` - The certificate that should be verified.
117    /// * `cert_chain` - The certificates chain.
118    /// * `with_context` - The closure that is called with the initialized context.
119    ///
120    /// This corresponds to [`X509_STORE_CTX_init`] before calling `with_context` and to
121    /// [`X509_STORE_CTX_cleanup`] after calling `with_context`.
122    ///
123    /// [`X509_STORE_CTX_init`]:  https://docs.openssl.org/master/man3/X509_STORE_CTX_init/
124    /// [`X509_STORE_CTX_cleanup`]:  https://docs.openssl.org/master/man3/X509_STORE_CTX_cleanup/
125    pub fn init<F, T>(
126        &mut self,
127        trust: &store::X509StoreRef,
128        cert: &X509Ref,
129        cert_chain: &StackRef<X509>,
130        with_context: F,
131    ) -> Result<T, ErrorStack>
132    where
133        F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
134    {
135        struct Cleanup<'a>(&'a mut X509StoreContextRef);
136
137        impl Drop for Cleanup<'_> {
138            fn drop(&mut self) {
139                unsafe {
140                    ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
141                }
142            }
143        }
144
145        unsafe {
146            cvt(ffi::X509_STORE_CTX_init(
147                self.as_ptr(),
148                trust.as_ptr(),
149                cert.as_ptr(),
150                cert_chain.as_ptr(),
151            ))?;
152
153            let cleanup = Cleanup(self);
154            with_context(cleanup.0)
155        }
156    }
157
158    /// Verifies the stored certificate.
159    ///
160    /// Returns `true` if verification succeeds. The `error` method will return the specific
161    /// validation error if the certificate was not valid.
162    ///
163    /// This will only work inside of a call to `init`.
164    #[corresponds(X509_verify_cert)]
165    pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
166        unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
167    }
168
169    /// Set the error code of the context.
170    #[corresponds(X509_STORE_CTX_set_error)]
171    pub fn set_error(&mut self, result: X509VerifyResult) {
172        unsafe {
173            ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw());
174        }
175    }
176
177    /// Returns a reference to the certificate which caused the error or None if
178    /// no certificate is relevant to the error.
179    #[corresponds(X509_STORE_CTX_get_current_cert)]
180    pub fn current_cert(&self) -> Option<&X509Ref> {
181        unsafe {
182            let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
183            X509Ref::from_const_ptr_opt(ptr)
184        }
185    }
186
187    /// Returns a non-negative integer representing the depth in the certificate
188    /// chain where the error occurred. If it is zero it occurred in the end
189    /// entity certificate, one if it is the certificate which signed the end
190    /// entity certificate and so on.
191    #[corresponds(X509_STORE_CTX_get_error_depth)]
192    pub fn error_depth(&self) -> u32 {
193        unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
194    }
195
196    /// Returns a reference to a complete valid `X509` certificate chain.
197    #[corresponds(X509_STORE_CTX_get0_chain)]
198    pub fn chain(&self) -> Option<&StackRef<X509>> {
199        unsafe {
200            let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
201
202            if chain.is_null() {
203                None
204            } else {
205                Some(StackRef::from_ptr(chain))
206            }
207        }
208    }
209}
210
211/// A builder used to construct an `X509`.
212pub struct X509Builder(X509);
213
214impl X509Builder {
215    /// Creates a new builder.
216    #[corresponds(X509_new)]
217    pub fn new() -> Result<X509Builder, ErrorStack> {
218        unsafe {
219            ffi::init();
220            cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p)))
221        }
222    }
223
224    /// Sets the notAfter constraint on the certificate.
225    #[corresponds(X509_set1_notAfter)]
226    pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
227        unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) }
228    }
229
230    /// Sets the notBefore constraint on the certificate.
231    #[corresponds(X509_set1_notBefore)]
232    pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
233        unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) }
234    }
235
236    /// Sets the version of the certificate.
237    ///
238    /// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of
239    /// the X.509 standard should pass `2` to this method.
240    #[corresponds(X509_set_version)]
241    #[allow(clippy::useless_conversion)]
242    pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
243        unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
244    }
245
246    /// Sets the serial number of the certificate.
247    #[corresponds(X509_set_serialNumber)]
248    pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
249        unsafe {
250            cvt(ffi::X509_set_serialNumber(
251                self.0.as_ptr(),
252                serial_number.as_ptr(),
253            ))
254            .map(|_| ())
255        }
256    }
257
258    /// Sets the issuer name of the certificate.
259    #[corresponds(X509_set_issuer_name)]
260    pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
261        unsafe {
262            cvt(ffi::X509_set_issuer_name(
263                self.0.as_ptr(),
264                issuer_name.as_ptr(),
265            ))
266            .map(|_| ())
267        }
268    }
269
270    /// Sets the subject name of the certificate.
271    ///
272    /// When building certificates, the `C`, `ST`, and `O` options are common when using the openssl command line tools.
273    /// The `CN` field is used for the common name, such as a DNS name.
274    ///
275    /// ```
276    /// use openssl::x509::{X509, X509NameBuilder};
277    ///
278    /// let mut x509_name = openssl::x509::X509NameBuilder::new().unwrap();
279    /// x509_name.append_entry_by_text("C", "US").unwrap();
280    /// x509_name.append_entry_by_text("ST", "CA").unwrap();
281    /// x509_name.append_entry_by_text("O", "Some organization").unwrap();
282    /// x509_name.append_entry_by_text("CN", "www.example.com").unwrap();
283    /// let x509_name = x509_name.build();
284    ///
285    /// let mut x509 = openssl::x509::X509::builder().unwrap();
286    /// x509.set_subject_name(&x509_name).unwrap();
287    /// ```
288    #[corresponds(X509_set_subject_name)]
289    pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
290        unsafe {
291            cvt(ffi::X509_set_subject_name(
292                self.0.as_ptr(),
293                subject_name.as_ptr(),
294            ))
295            .map(|_| ())
296        }
297    }
298
299    /// Sets the public key associated with the certificate.
300    #[corresponds(X509_set_pubkey)]
301    pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
302    where
303        T: HasPublic,
304    {
305        unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
306    }
307
308    /// Returns a context object which is needed to create certain X509 extension values.
309    ///
310    /// Set `issuer` to `None` if the certificate will be self-signed.
311    #[corresponds(X509V3_set_ctx)]
312    pub fn x509v3_context<'a>(
313        &'a self,
314        issuer: Option<&'a X509Ref>,
315        conf: Option<&'a ConfRef>,
316    ) -> X509v3Context<'a> {
317        unsafe {
318            let mut ctx = mem::zeroed();
319
320            let issuer = match issuer {
321                Some(issuer) => issuer.as_ptr(),
322                None => self.0.as_ptr(),
323            };
324            let subject = self.0.as_ptr();
325            ffi::X509V3_set_ctx(
326                &mut ctx,
327                issuer,
328                subject,
329                ptr::null_mut(),
330                ptr::null_mut(),
331                0,
332            );
333
334            // nodb case taken care of since we zeroed ctx above
335            if let Some(conf) = conf {
336                ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
337            }
338
339            X509v3Context(ctx, PhantomData)
340        }
341    }
342
343    /// Adds an X509 extension value to the certificate.
344    ///
345    /// This works just as `append_extension` except it takes ownership of the `X509Extension`.
346    pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
347        self.append_extension2(&extension)
348    }
349
350    /// Adds an X509 extension value to the certificate.
351    #[corresponds(X509_add_ext)]
352    pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
353        unsafe {
354            cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
355            Ok(())
356        }
357    }
358
359    /// Signs the certificate with a private key.
360    #[corresponds(X509_sign)]
361    pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
362    where
363        T: HasPrivate,
364    {
365        unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
366    }
367
368    /// Consumes the builder, returning the certificate.
369    pub fn build(self) -> X509 {
370        self.0
371    }
372}
373
374foreign_type_and_impl_send_sync! {
375    type CType = ffi::X509;
376    fn drop = ffi::X509_free;
377
378    /// An `X509` public key certificate.
379    pub struct X509;
380    /// Reference to `X509`.
381    pub struct X509Ref;
382}
383
384impl X509Ref {
385    /// Returns this certificate's subject name.
386    #[corresponds(X509_get_subject_name)]
387    pub fn subject_name(&self) -> &X509NameRef {
388        unsafe {
389            let name = ffi::X509_get_subject_name(self.as_ptr());
390            X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
391        }
392    }
393
394    /// Returns the hash of the certificates subject
395    #[corresponds(X509_subject_name_hash)]
396    pub fn subject_name_hash(&self) -> u32 {
397        #[allow(clippy::unnecessary_cast)]
398        unsafe {
399            ffi::X509_subject_name_hash(self.as_ptr()) as u32
400        }
401    }
402
403    /// Returns this certificate's issuer name.
404    #[corresponds(X509_get_issuer_name)]
405    pub fn issuer_name(&self) -> &X509NameRef {
406        unsafe {
407            let name = ffi::X509_get_issuer_name(self.as_ptr());
408            X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
409        }
410    }
411
412    /// Returns the hash of the certificates issuer
413    #[corresponds(X509_issuer_name_hash)]
414    pub fn issuer_name_hash(&self) -> u32 {
415        #[allow(clippy::unnecessary_cast)]
416        unsafe {
417            ffi::X509_issuer_name_hash(self.as_ptr()) as u32
418        }
419    }
420
421    /// Returns this certificate's subject alternative name entries, if they exist.
422    #[corresponds(X509_get_ext_d2i)]
423    pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
424        unsafe {
425            let stack = ffi::X509_get_ext_d2i(
426                self.as_ptr(),
427                ffi::NID_subject_alt_name,
428                ptr::null_mut(),
429                ptr::null_mut(),
430            );
431            Stack::from_ptr_opt(stack as *mut _)
432        }
433    }
434
435    /// Returns this certificate's CRL distribution points, if they exist.
436    #[corresponds(X509_get_ext_d2i)]
437    pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
438        unsafe {
439            let stack = ffi::X509_get_ext_d2i(
440                self.as_ptr(),
441                ffi::NID_crl_distribution_points,
442                ptr::null_mut(),
443                ptr::null_mut(),
444            );
445            Stack::from_ptr_opt(stack as *mut _)
446        }
447    }
448
449    /// Returns this certificate's issuer alternative name entries, if they exist.
450    #[corresponds(X509_get_ext_d2i)]
451    pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
452        unsafe {
453            let stack = ffi::X509_get_ext_d2i(
454                self.as_ptr(),
455                ffi::NID_issuer_alt_name,
456                ptr::null_mut(),
457                ptr::null_mut(),
458            );
459            Stack::from_ptr_opt(stack as *mut _)
460        }
461    }
462
463    /// Returns this certificate's [`authority information access`] entries, if they exist.
464    ///
465    /// [`authority information access`]: https://tools.ietf.org/html/rfc5280#section-4.2.2.1
466    #[corresponds(X509_get_ext_d2i)]
467    pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
468        unsafe {
469            let stack = ffi::X509_get_ext_d2i(
470                self.as_ptr(),
471                ffi::NID_info_access,
472                ptr::null_mut(),
473                ptr::null_mut(),
474            );
475            Stack::from_ptr_opt(stack as *mut _)
476        }
477    }
478
479    /// Retrieves the path length extension from a certificate, if it exists.
480    #[corresponds(X509_get_pathlen)]
481    #[cfg(any(ossl110, boringssl, awslc))]
482    pub fn pathlen(&self) -> Option<u32> {
483        let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
484        u32::try_from(v).ok()
485    }
486
487    /// Returns this certificate's subject key id, if it exists.
488    #[corresponds(X509_get0_subject_key_id)]
489    #[cfg(any(ossl110, boringssl, awslc))]
490    pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
491        unsafe {
492            let data = ffi::X509_get0_subject_key_id(self.as_ptr());
493            Asn1OctetStringRef::from_const_ptr_opt(data)
494        }
495    }
496
497    /// Returns this certificate's authority key id, if it exists.
498    #[corresponds(X509_get0_authority_key_id)]
499    #[cfg(any(ossl110, boringssl, awslc))]
500    pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
501        unsafe {
502            let data = ffi::X509_get0_authority_key_id(self.as_ptr());
503            Asn1OctetStringRef::from_const_ptr_opt(data)
504        }
505    }
506
507    /// Returns this certificate's authority issuer name entries, if they exist.
508    #[corresponds(X509_get0_authority_issuer)]
509    #[cfg(ossl111d)]
510    pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
511        unsafe {
512            let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
513            StackRef::from_const_ptr_opt(stack)
514        }
515    }
516
517    /// Returns this certificate's authority serial number, if it exists.
518    #[corresponds(X509_get0_authority_serial)]
519    #[cfg(ossl111d)]
520    pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
521        unsafe {
522            let r = ffi::X509_get0_authority_serial(self.as_ptr());
523            Asn1IntegerRef::from_const_ptr_opt(r)
524        }
525    }
526
527    #[corresponds(X509_get_pubkey)]
528    pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
529        unsafe {
530            let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
531            Ok(PKey::from_ptr(pkey))
532        }
533    }
534
535    /// Returns a digest of the DER representation of the certificate.
536    #[corresponds(X509_digest)]
537    pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
538        unsafe {
539            let mut digest = DigestBytes {
540                buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
541                len: ffi::EVP_MAX_MD_SIZE as usize,
542            };
543            let mut len = ffi::EVP_MAX_MD_SIZE as c_uint;
544            cvt(ffi::X509_digest(
545                self.as_ptr(),
546                hash_type.as_ptr(),
547                digest.buf.as_mut_ptr() as *mut _,
548                &mut len,
549            ))?;
550            digest.len = len as usize;
551
552            Ok(digest)
553        }
554    }
555
556    #[deprecated(since = "0.10.9", note = "renamed to digest")]
557    pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
558        self.digest(hash_type).map(|b| b.to_vec())
559    }
560
561    /// Returns the certificate's Not After validity period.
562    #[corresponds(X509_getm_notAfter)]
563    pub fn not_after(&self) -> &Asn1TimeRef {
564        unsafe {
565            let date = X509_getm_notAfter(self.as_ptr());
566            Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
567        }
568    }
569
570    /// Returns the certificate's Not Before validity period.
571    #[corresponds(X509_getm_notBefore)]
572    pub fn not_before(&self) -> &Asn1TimeRef {
573        unsafe {
574            let date = X509_getm_notBefore(self.as_ptr());
575            Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
576        }
577    }
578
579    /// Returns the certificate's signature
580    #[corresponds(X509_get0_signature)]
581    pub fn signature(&self) -> &Asn1BitStringRef {
582        unsafe {
583            let mut signature = ptr::null();
584            X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
585            Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
586        }
587    }
588
589    /// Returns the certificate's signature algorithm.
590    #[corresponds(X509_get0_signature)]
591    pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
592        unsafe {
593            let mut algor = ptr::null();
594            X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
595            X509AlgorithmRef::from_const_ptr_opt(algor)
596                .expect("signature algorithm must not be null")
597        }
598    }
599
600    /// Returns the list of OCSP responder URLs specified in the certificate's Authority Information
601    /// Access field.
602    #[corresponds(X509_get1_ocsp)]
603    pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
604        unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) }
605    }
606
607    /// Checks that this certificate issued `subject`.
608    #[corresponds(X509_check_issued)]
609    pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
610        unsafe {
611            let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
612            X509VerifyResult::from_raw(r)
613        }
614    }
615
616    /// Returns certificate version. If this certificate has no explicit version set, it defaults to
617    /// version 1.
618    ///
619    /// Note that `0` return value stands for version 1, `1` for version 2 and so on.
620    #[corresponds(X509_get_version)]
621    #[cfg(ossl110)]
622    #[allow(clippy::unnecessary_cast)]
623    pub fn version(&self) -> i32 {
624        unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
625    }
626
627    /// Check if the certificate is signed using the given public key.
628    ///
629    /// Only the signature is checked: no other checks (such as certificate chain validity)
630    /// are performed.
631    ///
632    /// Returns `true` if verification succeeds.
633    #[corresponds(X509_verify)]
634    pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
635    where
636        T: HasPublic,
637    {
638        unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
639    }
640
641    /// Returns this certificate's serial number.
642    #[corresponds(X509_get_serialNumber)]
643    pub fn serial_number(&self) -> &Asn1IntegerRef {
644        unsafe {
645            let r = ffi::X509_get_serialNumber(self.as_ptr());
646            Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
647        }
648    }
649
650    /// Returns this certificate's "alias". This field is populated by
651    /// OpenSSL in some situations -- specifically OpenSSL will store a
652    /// PKCS#12 `friendlyName` in this field. This is not a part of the X.509
653    /// certificate itself, OpenSSL merely attaches it to this structure in
654    /// memory.
655    #[corresponds(X509_alias_get0)]
656    pub fn alias(&self) -> Option<&[u8]> {
657        unsafe {
658            let mut len = 0;
659            let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
660            if ptr.is_null() {
661                None
662            } else {
663                Some(util::from_raw_parts(ptr, len as usize))
664            }
665        }
666    }
667
668    to_pem! {
669        /// Serializes the certificate into a PEM-encoded X509 structure.
670        ///
671        /// The output will have a header of `-----BEGIN CERTIFICATE-----`.
672        #[corresponds(PEM_write_bio_X509)]
673        to_pem,
674        ffi::PEM_write_bio_X509
675    }
676
677    to_der! {
678        /// Serializes the certificate into a DER-encoded X509 structure.
679        #[corresponds(i2d_X509)]
680        to_der,
681        ffi::i2d_X509
682    }
683
684    to_pem! {
685        /// Converts the certificate to human readable text.
686        #[corresponds(X509_print)]
687        to_text,
688        ffi::X509_print
689    }
690}
691
692impl ToOwned for X509Ref {
693    type Owned = X509;
694
695    fn to_owned(&self) -> X509 {
696        unsafe {
697            X509_up_ref(self.as_ptr());
698            X509::from_ptr(self.as_ptr())
699        }
700    }
701}
702
703impl Ord for X509Ref {
704    fn cmp(&self, other: &Self) -> cmp::Ordering {
705        // X509_cmp returns a number <0 for less than, 0 for equal and >0 for greater than.
706        // It can't fail if both pointers are valid, which we know is true.
707        let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
708        cmp.cmp(&0)
709    }
710}
711
712impl PartialOrd for X509Ref {
713    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
714        Some(self.cmp(other))
715    }
716}
717
718impl PartialOrd<X509> for X509Ref {
719    fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
720        <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
721    }
722}
723
724impl PartialEq for X509Ref {
725    fn eq(&self, other: &Self) -> bool {
726        self.cmp(other) == cmp::Ordering::Equal
727    }
728}
729
730impl PartialEq<X509> for X509Ref {
731    fn eq(&self, other: &X509) -> bool {
732        <X509Ref as PartialEq<X509Ref>>::eq(self, other)
733    }
734}
735
736impl Eq for X509Ref {}
737
738impl X509 {
739    /// Returns a new builder.
740    pub fn builder() -> Result<X509Builder, ErrorStack> {
741        X509Builder::new()
742    }
743
744    from_pem! {
745        /// Deserializes a PEM-encoded X509 structure.
746        ///
747        /// The input should have a header of `-----BEGIN CERTIFICATE-----`.
748        #[corresponds(PEM_read_bio_X509)]
749        from_pem,
750        X509,
751        ffi::PEM_read_bio_X509
752    }
753
754    from_der! {
755        /// Deserializes a DER-encoded X509 structure.
756        #[corresponds(d2i_X509)]
757        from_der,
758        X509,
759        ffi::d2i_X509
760    }
761
762    /// Deserializes a list of PEM-formatted certificates.
763    #[corresponds(PEM_read_bio_X509)]
764    pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
765        unsafe {
766            ffi::init();
767            let bio = MemBioSlice::new(pem)?;
768
769            let mut certs = vec![];
770            loop {
771                let r =
772                    ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
773                if r.is_null() {
774                    let e = ErrorStack::get();
775
776                    if let Some(err) = e.errors().last() {
777                        if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
778                            && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
779                        {
780                            break;
781                        }
782                    }
783
784                    return Err(e);
785                } else {
786                    certs.push(X509(r));
787                }
788            }
789
790            Ok(certs)
791        }
792    }
793}
794
795impl Clone for X509 {
796    fn clone(&self) -> X509 {
797        X509Ref::to_owned(self)
798    }
799}
800
801impl fmt::Debug for X509 {
802    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
803        let serial = match &self.serial_number().to_bn() {
804            Ok(bn) => match bn.to_hex_str() {
805                Ok(hex) => hex.to_string(),
806                Err(_) => "".to_string(),
807            },
808            Err(_) => "".to_string(),
809        };
810        let mut debug_struct = formatter.debug_struct("X509");
811        debug_struct.field("serial_number", &serial);
812        debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
813        debug_struct.field("issuer", &self.issuer_name());
814        debug_struct.field("subject", &self.subject_name());
815        if let Some(subject_alt_names) = &self.subject_alt_names() {
816            debug_struct.field("subject_alt_names", subject_alt_names);
817        }
818        debug_struct.field("not_before", &self.not_before());
819        debug_struct.field("not_after", &self.not_after());
820
821        if let Ok(public_key) = &self.public_key() {
822            debug_struct.field("public_key", public_key);
823        };
824        // TODO: Print extensions once they are supported on the X509 struct.
825
826        debug_struct.finish()
827    }
828}
829
830impl AsRef<X509Ref> for X509Ref {
831    fn as_ref(&self) -> &X509Ref {
832        self
833    }
834}
835
836impl Stackable for X509 {
837    type StackType = ffi::stack_st_X509;
838}
839
840impl Ord for X509 {
841    fn cmp(&self, other: &Self) -> cmp::Ordering {
842        X509Ref::cmp(self, other)
843    }
844}
845
846impl PartialOrd for X509 {
847    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
848        Some(self.cmp(other))
849    }
850}
851
852impl PartialOrd<X509Ref> for X509 {
853    fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
854        X509Ref::partial_cmp(self, other)
855    }
856}
857
858impl PartialEq for X509 {
859    fn eq(&self, other: &Self) -> bool {
860        X509Ref::eq(self, other)
861    }
862}
863
864impl PartialEq<X509Ref> for X509 {
865    fn eq(&self, other: &X509Ref) -> bool {
866        X509Ref::eq(self, other)
867    }
868}
869
870impl Eq for X509 {}
871
872/// A context object required to construct certain `X509` extension values.
873pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
874
875impl X509v3Context<'_> {
876    pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
877        &self.0 as *const _ as *mut _
878    }
879}
880
881foreign_type_and_impl_send_sync! {
882    type CType = ffi::X509_EXTENSION;
883    fn drop = ffi::X509_EXTENSION_free;
884
885    /// Permit additional fields to be added to an `X509` v3 certificate.
886    pub struct X509Extension;
887    /// Reference to `X509Extension`.
888    pub struct X509ExtensionRef;
889}
890
891impl Stackable for X509Extension {
892    type StackType = ffi::stack_st_X509_EXTENSION;
893}
894
895impl X509Extension {
896    /// Constructs an X509 extension value. See `man x509v3_config` for information on supported
897    /// names and their value formats.
898    ///
899    /// Some extension types, such as `subjectAlternativeName`, require an `X509v3Context` to be
900    /// provided.
901    ///
902    /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL
903    /// mini-language that can read arbitrary files.
904    ///
905    /// See the extension module for builder types which will construct certain common extensions.
906    ///
907    /// This function is deprecated, `X509Extension::new_from_der` or the
908    /// types in `x509::extension` should be used in its place.
909    #[deprecated(
910        note = "Use x509::extension types or new_from_der instead",
911        since = "0.10.51"
912    )]
913    pub fn new(
914        conf: Option<&ConfRef>,
915        context: Option<&X509v3Context<'_>>,
916        name: &str,
917        value: &str,
918    ) -> Result<X509Extension, ErrorStack> {
919        let name = CString::new(name).unwrap();
920        let value = CString::new(value).unwrap();
921        let mut ctx;
922        unsafe {
923            ffi::init();
924            let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
925            let context_ptr = match context {
926                Some(c) => c.as_ptr(),
927                None => {
928                    ctx = mem::zeroed();
929
930                    ffi::X509V3_set_ctx(
931                        &mut ctx,
932                        ptr::null_mut(),
933                        ptr::null_mut(),
934                        ptr::null_mut(),
935                        ptr::null_mut(),
936                        0,
937                    );
938                    &mut ctx
939                }
940            };
941            let name = name.as_ptr() as *mut _;
942            let value = value.as_ptr() as *mut _;
943
944            cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
945        }
946    }
947
948    /// Constructs an X509 extension value. See `man x509v3_config` for information on supported
949    /// extensions and their value formats.
950    ///
951    /// Some extension types, such as `nid::SUBJECT_ALTERNATIVE_NAME`, require an `X509v3Context` to
952    /// be provided.
953    ///
954    /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL
955    /// mini-language that can read arbitrary files.
956    ///
957    /// See the extension module for builder types which will construct certain common extensions.
958    ///
959    /// This function is deprecated, `X509Extension::new_from_der` or the
960    /// types in `x509::extension` should be used in its place.
961    #[deprecated(
962        note = "Use x509::extension types or new_from_der instead",
963        since = "0.10.51"
964    )]
965    pub fn new_nid(
966        conf: Option<&ConfRef>,
967        context: Option<&X509v3Context<'_>>,
968        name: Nid,
969        value: &str,
970    ) -> Result<X509Extension, ErrorStack> {
971        let value = CString::new(value).unwrap();
972        let mut ctx;
973        unsafe {
974            ffi::init();
975            let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
976            let context_ptr = match context {
977                Some(c) => c.as_ptr(),
978                None => {
979                    ctx = mem::zeroed();
980
981                    ffi::X509V3_set_ctx(
982                        &mut ctx,
983                        ptr::null_mut(),
984                        ptr::null_mut(),
985                        ptr::null_mut(),
986                        ptr::null_mut(),
987                        0,
988                    );
989                    &mut ctx
990                }
991            };
992            let name = name.as_raw();
993            let value = value.as_ptr() as *mut _;
994
995            cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
996        }
997    }
998
999    /// Constructs a new X509 extension value from its OID, whether it's
1000    /// critical, and its DER contents.
1001    ///
1002    /// The extent structure of the DER value will vary based on the
1003    /// extension type, and can generally be found in the RFC defining the
1004    /// extension.
1005    ///
1006    /// For common extension types, there are Rust APIs provided in
1007    /// `openssl::x509::extensions` which are more ergonomic.
1008    pub fn new_from_der(
1009        oid: &Asn1ObjectRef,
1010        critical: bool,
1011        der_contents: &Asn1OctetStringRef,
1012    ) -> Result<X509Extension, ErrorStack> {
1013        unsafe {
1014            cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
1015                ptr::null_mut(),
1016                oid.as_ptr(),
1017                critical as _,
1018                der_contents.as_ptr(),
1019            ))
1020            .map(X509Extension)
1021        }
1022    }
1023
1024    pub(crate) unsafe fn new_internal(
1025        nid: Nid,
1026        critical: bool,
1027        value: *mut c_void,
1028    ) -> Result<X509Extension, ErrorStack> {
1029        ffi::init();
1030        cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
1031    }
1032
1033    /// Adds an alias for an extension
1034    ///
1035    /// # Safety
1036    ///
1037    /// This method modifies global state without locking and therefore is not thread safe
1038    #[cfg(not(libressl390))]
1039    #[corresponds(X509V3_EXT_add_alias)]
1040    #[deprecated(
1041        note = "Use x509::extension types or new_from_der and then this is not necessary",
1042        since = "0.10.51"
1043    )]
1044    pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
1045        ffi::init();
1046        cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
1047    }
1048}
1049
1050impl X509ExtensionRef {
1051    to_der! {
1052        /// Serializes the Extension to its standard DER encoding.
1053        #[corresponds(i2d_X509_EXTENSION)]
1054        to_der,
1055        ffi::i2d_X509_EXTENSION
1056    }
1057}
1058
1059/// A builder used to construct an `X509Name`.
1060pub struct X509NameBuilder(X509Name);
1061
1062impl X509NameBuilder {
1063    /// Creates a new builder.
1064    pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1065        unsafe {
1066            ffi::init();
1067            cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
1068        }
1069    }
1070
1071    /// Add a name entry
1072    #[corresponds(X509_NAME_add_entry)]
1073    pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
1074        unsafe {
1075            cvt(ffi::X509_NAME_add_entry(
1076                self.0.as_ptr(),
1077                ne.as_ptr(),
1078                -1,
1079                0,
1080            ))
1081            .map(|_| ())
1082        }
1083    }
1084
1085    /// Add a field entry by str.
1086    #[corresponds(X509_NAME_add_entry_by_txt)]
1087    pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1088        unsafe {
1089            let field = CString::new(field).unwrap();
1090            assert!(value.len() <= crate::SLenType::MAX as usize);
1091            cvt(ffi::X509_NAME_add_entry_by_txt(
1092                self.0.as_ptr(),
1093                field.as_ptr() as *mut _,
1094                ffi::MBSTRING_UTF8,
1095                value.as_ptr(),
1096                value.len() as crate::SLenType,
1097                -1,
1098                0,
1099            ))
1100            .map(|_| ())
1101        }
1102    }
1103
1104    /// Add a field entry by str with a specific type.
1105    #[corresponds(X509_NAME_add_entry_by_txt)]
1106    pub fn append_entry_by_text_with_type(
1107        &mut self,
1108        field: &str,
1109        value: &str,
1110        ty: Asn1Type,
1111    ) -> Result<(), ErrorStack> {
1112        unsafe {
1113            let field = CString::new(field).unwrap();
1114            assert!(value.len() <= crate::SLenType::MAX as usize);
1115            cvt(ffi::X509_NAME_add_entry_by_txt(
1116                self.0.as_ptr(),
1117                field.as_ptr() as *mut _,
1118                ty.as_raw(),
1119                value.as_ptr(),
1120                value.len() as crate::SLenType,
1121                -1,
1122                0,
1123            ))
1124            .map(|_| ())
1125        }
1126    }
1127
1128    /// Add a field entry by NID.
1129    #[corresponds(X509_NAME_add_entry_by_NID)]
1130    pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1131        unsafe {
1132            assert!(value.len() <= crate::SLenType::MAX as usize);
1133            cvt(ffi::X509_NAME_add_entry_by_NID(
1134                self.0.as_ptr(),
1135                field.as_raw(),
1136                ffi::MBSTRING_UTF8,
1137                value.as_ptr() as *mut _,
1138                value.len() as crate::SLenType,
1139                -1,
1140                0,
1141            ))
1142            .map(|_| ())
1143        }
1144    }
1145
1146    /// Add a field entry by NID with a specific type.
1147    #[corresponds(X509_NAME_add_entry_by_NID)]
1148    pub fn append_entry_by_nid_with_type(
1149        &mut self,
1150        field: Nid,
1151        value: &str,
1152        ty: Asn1Type,
1153    ) -> Result<(), ErrorStack> {
1154        unsafe {
1155            assert!(value.len() <= crate::SLenType::MAX as usize);
1156            cvt(ffi::X509_NAME_add_entry_by_NID(
1157                self.0.as_ptr(),
1158                field.as_raw(),
1159                ty.as_raw(),
1160                value.as_ptr() as *mut _,
1161                value.len() as crate::SLenType,
1162                -1,
1163                0,
1164            ))
1165            .map(|_| ())
1166        }
1167    }
1168
1169    /// Return an `X509Name`.
1170    pub fn build(self) -> X509Name {
1171        // Round-trip through bytes because OpenSSL is not const correct and
1172        // names in a "modified" state compute various things lazily. This can
1173        // lead to data-races because OpenSSL doesn't have locks or anything.
1174        X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1175    }
1176}
1177
1178foreign_type_and_impl_send_sync! {
1179    type CType = ffi::X509_NAME;
1180    fn drop = ffi::X509_NAME_free;
1181
1182    /// The names of an `X509` certificate.
1183    pub struct X509Name;
1184    /// Reference to `X509Name`.
1185    pub struct X509NameRef;
1186}
1187
1188impl X509Name {
1189    /// Returns a new builder.
1190    pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1191        X509NameBuilder::new()
1192    }
1193
1194    /// Loads subject names from a file containing PEM-formatted certificates.
1195    ///
1196    /// This is commonly used in conjunction with `SslContextBuilder::set_client_ca_list`.
1197    pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1198        let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
1199        unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1200    }
1201
1202    from_der! {
1203        /// Deserializes a DER-encoded X509 name structure.
1204        ///
1205        /// This corresponds to [`d2i_X509_NAME`].
1206        ///
1207        /// [`d2i_X509_NAME`]: https://docs.openssl.org/master/man3/d2i_X509_NAME/
1208        from_der,
1209        X509Name,
1210        ffi::d2i_X509_NAME
1211    }
1212}
1213
1214impl Stackable for X509Name {
1215    type StackType = ffi::stack_st_X509_NAME;
1216}
1217
1218impl X509NameRef {
1219    /// Returns the name entries by the nid.
1220    pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1221        X509NameEntries {
1222            name: self,
1223            nid: Some(nid),
1224            loc: -1,
1225        }
1226    }
1227
1228    /// Returns an iterator over all `X509NameEntry` values
1229    pub fn entries(&self) -> X509NameEntries<'_> {
1230        X509NameEntries {
1231            name: self,
1232            nid: None,
1233            loc: -1,
1234        }
1235    }
1236
1237    /// Compare two names, like [`Ord`] but it may fail.
1238    ///
1239    /// With OpenSSL versions from 3.0.0 this may return an error if the underlying `X509_NAME_cmp`
1240    /// call fails.
1241    /// For OpenSSL versions before 3.0.0 it will never return an error, but due to a bug it may
1242    /// spuriously return `Ordering::Less` if the `X509_NAME_cmp` call fails.
1243    #[corresponds(X509_NAME_cmp)]
1244    pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
1245        let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
1246        if cfg!(ossl300) && cmp == -2 {
1247            return Err(ErrorStack::get());
1248        }
1249        Ok(cmp.cmp(&0))
1250    }
1251
1252    /// Copies the name to a new `X509Name`.
1253    #[corresponds(X509_NAME_dup)]
1254    #[cfg(any(boringssl, ossl110, libressl, awslc))]
1255    pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
1256        unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
1257    }
1258
1259    to_der! {
1260        /// Serializes the certificate into a DER-encoded X509 name structure.
1261        ///
1262        /// This corresponds to [`i2d_X509_NAME`].
1263        ///
1264        /// [`i2d_X509_NAME`]: https://docs.openssl.org/master/man3/i2d_X509_NAME/
1265        to_der,
1266        ffi::i2d_X509_NAME
1267    }
1268}
1269
1270impl fmt::Debug for X509NameRef {
1271    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1272        formatter.debug_list().entries(self.entries()).finish()
1273    }
1274}
1275
1276/// A type to destructure and examine an `X509Name`.
1277pub struct X509NameEntries<'a> {
1278    name: &'a X509NameRef,
1279    nid: Option<Nid>,
1280    loc: c_int,
1281}
1282
1283impl<'a> Iterator for X509NameEntries<'a> {
1284    type Item = &'a X509NameEntryRef;
1285
1286    fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1287        unsafe {
1288            match self.nid {
1289                Some(nid) => {
1290                    // There is a `Nid` specified to search for
1291                    self.loc =
1292                        ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1293                    if self.loc == -1 {
1294                        return None;
1295                    }
1296                }
1297                None => {
1298                    // Iterate over all `Nid`s
1299                    self.loc += 1;
1300                    if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1301                        return None;
1302                    }
1303                }
1304            }
1305
1306            let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1307
1308            Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
1309        }
1310    }
1311}
1312
1313foreign_type_and_impl_send_sync! {
1314    type CType = ffi::X509_NAME_ENTRY;
1315    fn drop = ffi::X509_NAME_ENTRY_free;
1316
1317    /// A name entry associated with a `X509Name`.
1318    pub struct X509NameEntry;
1319    /// Reference to `X509NameEntry`.
1320    pub struct X509NameEntryRef;
1321}
1322
1323impl X509NameEntryRef {
1324    /// Returns the field value of an `X509NameEntry`.
1325    #[corresponds(X509_NAME_ENTRY_get_data)]
1326    pub fn data(&self) -> &Asn1StringRef {
1327        unsafe {
1328            let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1329            Asn1StringRef::from_ptr(data)
1330        }
1331    }
1332
1333    /// Returns the `Asn1Object` value of an `X509NameEntry`.
1334    /// This is useful for finding out about the actual `Nid` when iterating over all `X509NameEntries`.
1335    #[corresponds(X509_NAME_ENTRY_get_object)]
1336    pub fn object(&self) -> &Asn1ObjectRef {
1337        unsafe {
1338            let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1339            Asn1ObjectRef::from_ptr(object)
1340        }
1341    }
1342}
1343
1344impl fmt::Debug for X509NameEntryRef {
1345    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1346        formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1347    }
1348}
1349
1350/// A builder used to construct an `X509Req`.
1351pub struct X509ReqBuilder(X509Req);
1352
1353impl X509ReqBuilder {
1354    /// Returns a builder for a certificate request.
1355    #[corresponds(X509_REQ_new)]
1356    pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1357        unsafe {
1358            ffi::init();
1359            cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
1360        }
1361    }
1362
1363    /// Set the numerical value of the version field.
1364    #[corresponds(X509_REQ_set_version)]
1365    #[allow(clippy::useless_conversion)]
1366    pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1367        unsafe {
1368            cvt(ffi::X509_REQ_set_version(
1369                self.0.as_ptr(),
1370                version as c_long,
1371            ))
1372            .map(|_| ())
1373        }
1374    }
1375
1376    /// Set the issuer name.
1377    #[corresponds(X509_REQ_set_subject_name)]
1378    pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1379        unsafe {
1380            cvt(ffi::X509_REQ_set_subject_name(
1381                self.0.as_ptr(),
1382                subject_name.as_ptr(),
1383            ))
1384            .map(|_| ())
1385        }
1386    }
1387
1388    /// Set the public key.
1389    #[corresponds(X509_REQ_set_pubkey)]
1390    pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1391    where
1392        T: HasPublic,
1393    {
1394        unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
1395    }
1396
1397    /// Return an `X509v3Context`. This context object can be used to construct
1398    /// certain `X509` extensions.
1399    pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1400        unsafe {
1401            let mut ctx = mem::zeroed();
1402
1403            ffi::X509V3_set_ctx(
1404                &mut ctx,
1405                ptr::null_mut(),
1406                ptr::null_mut(),
1407                self.0.as_ptr(),
1408                ptr::null_mut(),
1409                0,
1410            );
1411
1412            // nodb case taken care of since we zeroed ctx above
1413            if let Some(conf) = conf {
1414                ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1415            }
1416
1417            X509v3Context(ctx, PhantomData)
1418        }
1419    }
1420
1421    /// Permits any number of extension fields to be added to the certificate.
1422    pub fn add_extensions(
1423        &mut self,
1424        extensions: &StackRef<X509Extension>,
1425    ) -> Result<(), ErrorStack> {
1426        unsafe {
1427            cvt(ffi::X509_REQ_add_extensions(
1428                self.0.as_ptr(),
1429                extensions.as_ptr(),
1430            ))
1431            .map(|_| ())
1432        }
1433    }
1434
1435    /// Sign the request using a private key.
1436    #[corresponds(X509_REQ_sign)]
1437    pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1438    where
1439        T: HasPrivate,
1440    {
1441        unsafe {
1442            cvt(ffi::X509_REQ_sign(
1443                self.0.as_ptr(),
1444                key.as_ptr(),
1445                hash.as_ptr(),
1446            ))
1447            .map(|_| ())
1448        }
1449    }
1450
1451    /// Returns the `X509Req`.
1452    pub fn build(self) -> X509Req {
1453        self.0
1454    }
1455}
1456
1457foreign_type_and_impl_send_sync! {
1458    type CType = ffi::X509_REQ;
1459    fn drop = ffi::X509_REQ_free;
1460
1461    /// An `X509` certificate request.
1462    pub struct X509Req;
1463    /// Reference to `X509Req`.
1464    pub struct X509ReqRef;
1465}
1466
1467impl X509Req {
1468    /// A builder for `X509Req`.
1469    pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1470        X509ReqBuilder::new()
1471    }
1472
1473    from_pem! {
1474        /// Deserializes a PEM-encoded PKCS#10 certificate request structure.
1475        ///
1476        /// The input should have a header of `-----BEGIN CERTIFICATE REQUEST-----`.
1477        ///
1478        /// This corresponds to [`PEM_read_bio_X509_REQ`].
1479        ///
1480        /// [`PEM_read_bio_X509_REQ`]: https://docs.openssl.org/master/man3/PEM_read_bio_X509_REQ/
1481        from_pem,
1482        X509Req,
1483        ffi::PEM_read_bio_X509_REQ
1484    }
1485
1486    from_der! {
1487        /// Deserializes a DER-encoded PKCS#10 certificate request structure.
1488        ///
1489        /// This corresponds to [`d2i_X509_REQ`].
1490        ///
1491        /// [`d2i_X509_REQ`]: https://docs.openssl.org/master/man3/d2i_X509_REQ/
1492        from_der,
1493        X509Req,
1494        ffi::d2i_X509_REQ
1495    }
1496}
1497
1498impl X509ReqRef {
1499    to_pem! {
1500        /// Serializes the certificate request to a PEM-encoded PKCS#10 structure.
1501        ///
1502        /// The output will have a header of `-----BEGIN CERTIFICATE REQUEST-----`.
1503        ///
1504        /// This corresponds to [`PEM_write_bio_X509_REQ`].
1505        ///
1506        /// [`PEM_write_bio_X509_REQ`]: https://docs.openssl.org/master/man3/PEM_write_bio_X509_REQ/
1507        to_pem,
1508        ffi::PEM_write_bio_X509_REQ
1509    }
1510
1511    to_der! {
1512        /// Serializes the certificate request to a DER-encoded PKCS#10 structure.
1513        ///
1514        /// This corresponds to [`i2d_X509_REQ`].
1515        ///
1516        /// [`i2d_X509_REQ`]: https://docs.openssl.org/master/man3/i2d_X509_REQ/
1517        to_der,
1518        ffi::i2d_X509_REQ
1519    }
1520
1521    to_pem! {
1522        /// Converts the request to human readable text.
1523        #[corresponds(X509_Req_print)]
1524        to_text,
1525        ffi::X509_REQ_print
1526    }
1527
1528    /// Returns the numerical value of the version field of the certificate request.
1529    #[corresponds(X509_REQ_get_version)]
1530    #[allow(clippy::unnecessary_cast)]
1531    pub fn version(&self) -> i32 {
1532        unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1533    }
1534
1535    /// Returns the subject name of the certificate request.
1536    #[corresponds(X509_REQ_get_subject_name)]
1537    pub fn subject_name(&self) -> &X509NameRef {
1538        unsafe {
1539            let name = X509_REQ_get_subject_name(self.as_ptr());
1540            X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
1541        }
1542    }
1543
1544    /// Returns the public key of the certificate request.
1545    #[corresponds(X509_REQ_get_pubkey)]
1546    pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1547        unsafe {
1548            let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1549            Ok(PKey::from_ptr(key))
1550        }
1551    }
1552
1553    /// Check if the certificate request is signed using the given public key.
1554    ///
1555    /// Returns `true` if verification succeeds.
1556    #[corresponds(X509_REQ_verify)]
1557    pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1558    where
1559        T: HasPublic,
1560    {
1561        unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1562    }
1563
1564    /// Returns the extensions of the certificate request.
1565    #[corresponds(X509_REQ_get_extensions)]
1566    pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1567        unsafe {
1568            let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1569            Ok(Stack::from_ptr(extensions))
1570        }
1571    }
1572}
1573
1574/// The reason that a certificate was revoked.
1575#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1576pub struct CrlReason(c_int);
1577
1578#[allow(missing_docs)] // no need to document the constants
1579impl CrlReason {
1580    pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
1581    pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
1582    pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
1583    pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
1584    pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
1585    pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
1586    pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
1587    pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
1588    pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
1589    pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
1590
1591    /// Constructs an `CrlReason` from a raw OpenSSL value.
1592    pub const fn from_raw(value: c_int) -> Self {
1593        CrlReason(value)
1594    }
1595
1596    /// Returns the raw OpenSSL value represented by this type.
1597    pub const fn as_raw(&self) -> c_int {
1598        self.0
1599    }
1600}
1601
1602foreign_type_and_impl_send_sync! {
1603    type CType = ffi::X509_REVOKED;
1604    fn drop = ffi::X509_REVOKED_free;
1605
1606    /// An `X509` certificate revocation status.
1607    pub struct X509Revoked;
1608    /// Reference to `X509Revoked`.
1609    pub struct X509RevokedRef;
1610}
1611
1612impl Stackable for X509Revoked {
1613    type StackType = ffi::stack_st_X509_REVOKED;
1614}
1615
1616impl X509Revoked {
1617    from_der! {
1618        /// Deserializes a DER-encoded certificate revocation status
1619        #[corresponds(d2i_X509_REVOKED)]
1620        from_der,
1621        X509Revoked,
1622        ffi::d2i_X509_REVOKED
1623    }
1624}
1625
1626impl X509RevokedRef {
1627    to_der! {
1628        /// Serializes the certificate request to a DER-encoded certificate revocation status
1629        #[corresponds(d2i_X509_REVOKED)]
1630        to_der,
1631        ffi::i2d_X509_REVOKED
1632    }
1633
1634    /// Copies the entry to a new `X509Revoked`.
1635    #[corresponds(X509_NAME_dup)]
1636    #[cfg(any(boringssl, ossl110, libressl, awslc))]
1637    pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
1638        unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
1639    }
1640
1641    /// Get the date that the certificate was revoked
1642    #[corresponds(X509_REVOKED_get0_revocationDate)]
1643    pub fn revocation_date(&self) -> &Asn1TimeRef {
1644        unsafe {
1645            let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
1646            assert!(!r.is_null());
1647            Asn1TimeRef::from_ptr(r as *mut _)
1648        }
1649    }
1650
1651    /// Get the serial number of the revoked certificate
1652    #[corresponds(X509_REVOKED_get0_serialNumber)]
1653    pub fn serial_number(&self) -> &Asn1IntegerRef {
1654        unsafe {
1655            let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
1656            assert!(!r.is_null());
1657            Asn1IntegerRef::from_ptr(r as *mut _)
1658        }
1659    }
1660
1661    /// Get the criticality and value of an extension.
1662    ///
1663    /// This returns None if the extension is not present or occurs multiple times.
1664    #[corresponds(X509_REVOKED_get_ext_d2i)]
1665    pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1666        let mut critical = -1;
1667        let out = unsafe {
1668            // SAFETY: self.as_ptr() is a valid pointer to an X509_REVOKED.
1669            let ext = ffi::X509_REVOKED_get_ext_d2i(
1670                self.as_ptr(),
1671                T::NID.as_raw(),
1672                &mut critical as *mut _,
1673                ptr::null_mut(),
1674            );
1675            // SAFETY: Extensions's contract promises that the type returned by
1676            // OpenSSL here is T::Output.
1677            T::Output::from_ptr_opt(ext as *mut _)
1678        };
1679        match (critical, out) {
1680            (0, Some(out)) => Ok(Some((false, out))),
1681            (1, Some(out)) => Ok(Some((true, out))),
1682            // -1 means the extension wasn't found, -2 means multiple were found.
1683            (-1 | -2, _) => Ok(None),
1684            // A critical value of 0 or 1 suggests success, but a null pointer
1685            // was returned so something went wrong.
1686            (0 | 1, None) => Err(ErrorStack::get()),
1687            (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1688        }
1689    }
1690}
1691
1692/// The CRL entry extension identifying the reason for revocation see [`CrlReason`],
1693/// this is as defined in RFC 5280 Section 5.3.1.
1694pub enum ReasonCode {}
1695
1696// SAFETY: CertificateIssuer is defined to be a stack of GeneralName in the RFC
1697// and in OpenSSL.
1698unsafe impl ExtensionType for ReasonCode {
1699    const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
1700
1701    type Output = Asn1Enumerated;
1702}
1703
1704/// The CRL entry extension identifying the issuer of a certificate used in
1705/// indirect CRLs, as defined in RFC 5280 Section 5.3.3.
1706pub enum CertificateIssuer {}
1707
1708// SAFETY: CertificateIssuer is defined to be a stack of GeneralName in the RFC
1709// and in OpenSSL.
1710unsafe impl ExtensionType for CertificateIssuer {
1711    const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
1712
1713    type Output = Stack<GeneralName>;
1714}
1715
1716/// The CRL extension identifying how to access information and services for the issuer of the CRL
1717pub enum AuthorityInformationAccess {}
1718
1719// SAFETY: AuthorityInformationAccess is defined to be a stack of AccessDescription in the RFC
1720// and in OpenSSL.
1721unsafe impl ExtensionType for AuthorityInformationAccess {
1722    const NID: Nid = Nid::from_raw(ffi::NID_info_access);
1723
1724    type Output = Stack<AccessDescription>;
1725}
1726
1727foreign_type_and_impl_send_sync! {
1728    type CType = ffi::X509_CRL;
1729    fn drop = ffi::X509_CRL_free;
1730
1731    /// An `X509` certificate revocation list.
1732    pub struct X509Crl;
1733    /// Reference to `X509Crl`.
1734    pub struct X509CrlRef;
1735}
1736
1737/// The status of a certificate in a revoction list
1738///
1739/// Corresponds to the return value from the [`X509_CRL_get0_by_*`] methods.
1740///
1741/// [`X509_CRL_get0_by_*`]: https://docs.openssl.org/master/man3/X509_CRL_get0_by_serial/
1742pub enum CrlStatus<'a> {
1743    /// The certificate is not present in the list
1744    NotRevoked,
1745    /// The certificate is in the list and is revoked
1746    Revoked(&'a X509RevokedRef),
1747    /// The certificate is in the list, but has the "removeFromCrl" status.
1748    ///
1749    /// This can occur if the certificate was revoked with the "CertificateHold"
1750    /// reason, and has since been unrevoked.
1751    RemoveFromCrl(&'a X509RevokedRef),
1752}
1753
1754impl<'a> CrlStatus<'a> {
1755    // Helper used by the X509_CRL_get0_by_* methods to convert their return
1756    // value to the status enum.
1757    // Safety note: the returned CrlStatus must not outlive the owner of the
1758    // revoked_entry pointer.
1759    unsafe fn from_ffi_status(
1760        status: c_int,
1761        revoked_entry: *mut ffi::X509_REVOKED,
1762    ) -> CrlStatus<'a> {
1763        match status {
1764            0 => CrlStatus::NotRevoked,
1765            1 => {
1766                assert!(!revoked_entry.is_null());
1767                CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
1768            }
1769            2 => {
1770                assert!(!revoked_entry.is_null());
1771                CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
1772            }
1773            _ => unreachable!(
1774                "{}",
1775                "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
1776            ),
1777        }
1778    }
1779}
1780
1781impl X509Crl {
1782    from_pem! {
1783        /// Deserializes a PEM-encoded Certificate Revocation List
1784        ///
1785        /// The input should have a header of `-----BEGIN X509 CRL-----`.
1786        #[corresponds(PEM_read_bio_X509_CRL)]
1787        from_pem,
1788        X509Crl,
1789        ffi::PEM_read_bio_X509_CRL
1790    }
1791
1792    from_der! {
1793        /// Deserializes a DER-encoded Certificate Revocation List
1794        #[corresponds(d2i_X509_CRL)]
1795        from_der,
1796        X509Crl,
1797        ffi::d2i_X509_CRL
1798    }
1799}
1800
1801impl X509CrlRef {
1802    to_pem! {
1803        /// Serializes the certificate request to a PEM-encoded Certificate Revocation List.
1804        ///
1805        /// The output will have a header of `-----BEGIN X509 CRL-----`.
1806        #[corresponds(PEM_write_bio_X509_CRL)]
1807        to_pem,
1808        ffi::PEM_write_bio_X509_CRL
1809    }
1810
1811    to_der! {
1812        /// Serializes the certificate request to a DER-encoded Certificate Revocation List.
1813        #[corresponds(i2d_X509_CRL)]
1814        to_der,
1815        ffi::i2d_X509_CRL
1816    }
1817
1818    /// Get the stack of revocation entries
1819    pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
1820        unsafe {
1821            let revoked = X509_CRL_get_REVOKED(self.as_ptr());
1822            if revoked.is_null() {
1823                None
1824            } else {
1825                Some(StackRef::from_ptr(revoked))
1826            }
1827        }
1828    }
1829
1830    /// Returns the CRL's `lastUpdate` time.
1831    #[corresponds(X509_CRL_get0_lastUpdate)]
1832    pub fn last_update(&self) -> &Asn1TimeRef {
1833        unsafe {
1834            let date = X509_CRL_get0_lastUpdate(self.as_ptr());
1835            assert!(!date.is_null());
1836            Asn1TimeRef::from_ptr(date as *mut _)
1837        }
1838    }
1839
1840    /// Returns the CRL's `nextUpdate` time.
1841    ///
1842    /// If the `nextUpdate` field is missing, returns `None`.
1843    #[corresponds(X509_CRL_get0_nextUpdate)]
1844    pub fn next_update(&self) -> Option<&Asn1TimeRef> {
1845        unsafe {
1846            let date = X509_CRL_get0_nextUpdate(self.as_ptr());
1847            Asn1TimeRef::from_const_ptr_opt(date)
1848        }
1849    }
1850
1851    /// Get the revocation status of a certificate by its serial number
1852    #[corresponds(X509_CRL_get0_by_serial)]
1853    pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
1854        unsafe {
1855            let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
1856            let status =
1857                ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
1858            CrlStatus::from_ffi_status(status, ret)
1859        }
1860    }
1861
1862    /// Get the revocation status of a certificate
1863    #[corresponds(X509_CRL_get0_by_cert)]
1864    pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
1865        unsafe {
1866            let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
1867            let status =
1868                ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
1869            CrlStatus::from_ffi_status(status, ret)
1870        }
1871    }
1872
1873    /// Get the issuer name from the revocation list.
1874    #[corresponds(X509_CRL_get_issuer)]
1875    pub fn issuer_name(&self) -> &X509NameRef {
1876        unsafe {
1877            let name = X509_CRL_get_issuer(self.as_ptr());
1878            assert!(!name.is_null());
1879            X509NameRef::from_ptr(name)
1880        }
1881    }
1882
1883    /// Check if the CRL is signed using the given public key.
1884    ///
1885    /// Only the signature is checked: no other checks (such as certificate chain validity)
1886    /// are performed.
1887    ///
1888    /// Returns `true` if verification succeeds.
1889    #[corresponds(X509_CRL_verify)]
1890    pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1891    where
1892        T: HasPublic,
1893    {
1894        unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1895    }
1896
1897    /// Get the criticality and value of an extension.
1898    ///
1899    /// This returns None if the extension is not present or occurs multiple times.
1900    #[corresponds(X509_CRL_get_ext_d2i)]
1901    pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
1902        let mut critical = -1;
1903        let out = unsafe {
1904            // SAFETY: self.as_ptr() is a valid pointer to an X509_CRL.
1905            let ext = ffi::X509_CRL_get_ext_d2i(
1906                self.as_ptr(),
1907                T::NID.as_raw(),
1908                &mut critical as *mut _,
1909                ptr::null_mut(),
1910            );
1911            // SAFETY: Extensions's contract promises that the type returned by
1912            // OpenSSL here is T::Output.
1913            T::Output::from_ptr_opt(ext as *mut _)
1914        };
1915        match (critical, out) {
1916            (0, Some(out)) => Ok(Some((false, out))),
1917            (1, Some(out)) => Ok(Some((true, out))),
1918            // -1 means the extension wasn't found, -2 means multiple were found.
1919            (-1 | -2, _) => Ok(None),
1920            // A critical value of 0 or 1 suggests success, but a null pointer
1921            // was returned so something went wrong.
1922            (0 | 1, None) => Err(ErrorStack::get()),
1923            (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
1924        }
1925    }
1926}
1927
1928/// The result of peer certificate verification.
1929#[derive(Copy, Clone, PartialEq, Eq)]
1930pub struct X509VerifyResult(c_int);
1931
1932impl fmt::Debug for X509VerifyResult {
1933    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1934        fmt.debug_struct("X509VerifyResult")
1935            .field("code", &self.0)
1936            .field("error", &self.error_string())
1937            .finish()
1938    }
1939}
1940
1941impl fmt::Display for X509VerifyResult {
1942    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1943        fmt.write_str(self.error_string())
1944    }
1945}
1946
1947impl Error for X509VerifyResult {}
1948
1949impl X509VerifyResult {
1950    /// Creates an `X509VerifyResult` from a raw error number.
1951    ///
1952    /// # Safety
1953    ///
1954    /// Some methods on `X509VerifyResult` are not thread safe if the error
1955    /// number is invalid.
1956    pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
1957        X509VerifyResult(err)
1958    }
1959
1960    /// Return the integer representation of an `X509VerifyResult`.
1961    #[allow(clippy::trivially_copy_pass_by_ref)]
1962    pub fn as_raw(&self) -> c_int {
1963        self.0
1964    }
1965
1966    /// Return a human readable error string from the verification error.
1967    #[corresponds(X509_verify_cert_error_string)]
1968    #[allow(clippy::trivially_copy_pass_by_ref)]
1969    pub fn error_string(&self) -> &'static str {
1970        ffi::init();
1971
1972        unsafe {
1973            let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
1974            str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
1975        }
1976    }
1977
1978    /// Successful peer certificate verification.
1979    pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
1980    /// Application verification failure.
1981    pub const APPLICATION_VERIFICATION: X509VerifyResult =
1982        X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
1983}
1984
1985foreign_type_and_impl_send_sync! {
1986    type CType = ffi::GENERAL_NAME;
1987    fn drop = ffi::GENERAL_NAME_free;
1988
1989    /// An `X509` certificate alternative names.
1990    pub struct GeneralName;
1991    /// Reference to `GeneralName`.
1992    pub struct GeneralNameRef;
1993}
1994
1995impl GeneralName {
1996    unsafe fn new(
1997        type_: c_int,
1998        asn1_type: Asn1Type,
1999        value: &[u8],
2000    ) -> Result<GeneralName, ErrorStack> {
2001        ffi::init();
2002        let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
2003        (*gn.as_ptr()).type_ = type_;
2004        let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
2005        ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
2006
2007        #[cfg(any(boringssl, awslc))]
2008        {
2009            (*gn.as_ptr()).d.ptr = s.cast();
2010        }
2011        #[cfg(not(any(boringssl, awslc)))]
2012        {
2013            (*gn.as_ptr()).d = s.cast();
2014        }
2015
2016        Ok(gn)
2017    }
2018
2019    pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
2020        unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
2021    }
2022
2023    pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
2024        unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
2025    }
2026
2027    pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
2028        unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
2029    }
2030
2031    pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
2032        match ip {
2033            IpAddr::V4(addr) => unsafe {
2034                GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2035            },
2036            IpAddr::V6(addr) => unsafe {
2037                GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
2038            },
2039        }
2040    }
2041
2042    pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
2043        unsafe {
2044            ffi::init();
2045            let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2046            (*gn).type_ = ffi::GEN_RID;
2047
2048            #[cfg(any(boringssl, awslc))]
2049            {
2050                (*gn).d.registeredID = oid.as_ptr();
2051            }
2052            #[cfg(not(any(boringssl, awslc)))]
2053            {
2054                (*gn).d = oid.as_ptr().cast();
2055            }
2056
2057            mem::forget(oid);
2058
2059            Ok(GeneralName::from_ptr(gn))
2060        }
2061    }
2062
2063    pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
2064        unsafe {
2065            ffi::init();
2066
2067            let typ = cvt_p(ffi::d2i_ASN1_TYPE(
2068                ptr::null_mut(),
2069                &mut value.as_ptr().cast(),
2070                value.len().try_into().unwrap(),
2071            ))?;
2072
2073            let gn = cvt_p(ffi::GENERAL_NAME_new())?;
2074            (*gn).type_ = ffi::GEN_OTHERNAME;
2075
2076            if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
2077                gn,
2078                oid.as_ptr().cast(),
2079                typ,
2080            )) {
2081                ffi::GENERAL_NAME_free(gn);
2082                return Err(e);
2083            }
2084
2085            mem::forget(oid);
2086
2087            Ok(GeneralName::from_ptr(gn))
2088        }
2089    }
2090}
2091
2092impl GeneralNameRef {
2093    fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
2094        unsafe {
2095            if (*self.as_ptr()).type_ != ffi_type {
2096                return None;
2097            }
2098
2099            #[cfg(any(boringssl, awslc))]
2100            let d = (*self.as_ptr()).d.ptr;
2101            #[cfg(not(any(boringssl, awslc)))]
2102            let d = (*self.as_ptr()).d;
2103
2104            let ptr = ASN1_STRING_get0_data(d as *mut _);
2105            let len = ffi::ASN1_STRING_length(d as *mut _);
2106
2107            #[allow(clippy::unnecessary_cast)]
2108            let slice = util::from_raw_parts(ptr as *const u8, len as usize);
2109            // IA5Strings are stated to be ASCII (specifically IA5). Hopefully
2110            // OpenSSL checks that when loading a certificate but if not we'll
2111            // use this instead of from_utf8_unchecked just in case.
2112            str::from_utf8(slice).ok()
2113        }
2114    }
2115
2116    /// Returns the contents of this `GeneralName` if it is an `rfc822Name`.
2117    pub fn email(&self) -> Option<&str> {
2118        self.ia5_string(ffi::GEN_EMAIL)
2119    }
2120
2121    /// Returns the contents of this `GeneralName` if it is a `directoryName`.
2122    pub fn directory_name(&self) -> Option<&X509NameRef> {
2123        unsafe {
2124            if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
2125                return None;
2126            }
2127
2128            #[cfg(any(boringssl, awslc))]
2129            let d = (*self.as_ptr()).d.ptr;
2130            #[cfg(not(any(boringssl, awslc)))]
2131            let d = (*self.as_ptr()).d;
2132
2133            Some(X509NameRef::from_const_ptr(d as *const _))
2134        }
2135    }
2136
2137    /// Returns the contents of this `GeneralName` if it is a `dNSName`.
2138    pub fn dnsname(&self) -> Option<&str> {
2139        self.ia5_string(ffi::GEN_DNS)
2140    }
2141
2142    /// Returns the contents of this `GeneralName` if it is an `uniformResourceIdentifier`.
2143    pub fn uri(&self) -> Option<&str> {
2144        self.ia5_string(ffi::GEN_URI)
2145    }
2146
2147    /// Returns the contents of this `GeneralName` if it is an `iPAddress`.
2148    pub fn ipaddress(&self) -> Option<&[u8]> {
2149        unsafe {
2150            if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
2151                return None;
2152            }
2153            #[cfg(any(boringssl, awslc))]
2154            let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
2155            #[cfg(not(any(boringssl, awslc)))]
2156            let d = (*self.as_ptr()).d;
2157
2158            let ptr = ASN1_STRING_get0_data(d as *mut _);
2159            let len = ffi::ASN1_STRING_length(d as *mut _);
2160
2161            #[allow(clippy::unnecessary_cast)]
2162            Some(util::from_raw_parts(ptr as *const u8, len as usize))
2163        }
2164    }
2165}
2166
2167impl fmt::Debug for GeneralNameRef {
2168    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2169        if let Some(email) = self.email() {
2170            formatter.write_str(email)
2171        } else if let Some(dnsname) = self.dnsname() {
2172            formatter.write_str(dnsname)
2173        } else if let Some(uri) = self.uri() {
2174            formatter.write_str(uri)
2175        } else if let Some(ipaddress) = self.ipaddress() {
2176            let address = <[u8; 16]>::try_from(ipaddress)
2177                .map(IpAddr::from)
2178                .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
2179            match address {
2180                Ok(a) => fmt::Debug::fmt(&a, formatter),
2181                Err(_) => fmt::Debug::fmt(ipaddress, formatter),
2182            }
2183        } else {
2184            formatter.write_str("(empty)")
2185        }
2186    }
2187}
2188
2189impl Stackable for GeneralName {
2190    type StackType = ffi::stack_st_GENERAL_NAME;
2191}
2192
2193foreign_type_and_impl_send_sync! {
2194    type CType = ffi::DIST_POINT;
2195    fn drop = ffi::DIST_POINT_free;
2196
2197    /// A `X509` distribution point.
2198    pub struct DistPoint;
2199    /// Reference to `DistPoint`.
2200    pub struct DistPointRef;
2201}
2202
2203impl DistPointRef {
2204    /// Returns the name of this distribution point if it exists
2205    pub fn distpoint(&self) -> Option<&DistPointNameRef> {
2206        unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
2207    }
2208}
2209
2210foreign_type_and_impl_send_sync! {
2211    type CType = ffi::DIST_POINT_NAME;
2212    fn drop = ffi::DIST_POINT_NAME_free;
2213
2214    /// A `X509` distribution point.
2215    pub struct DistPointName;
2216    /// Reference to `DistPointName`.
2217    pub struct DistPointNameRef;
2218}
2219
2220impl DistPointNameRef {
2221    /// Returns the contents of this DistPointName if it is a fullname.
2222    pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
2223        unsafe {
2224            if (*self.as_ptr()).type_ != 0 {
2225                return None;
2226            }
2227            StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
2228        }
2229    }
2230}
2231
2232impl Stackable for DistPoint {
2233    type StackType = ffi::stack_st_DIST_POINT;
2234}
2235
2236foreign_type_and_impl_send_sync! {
2237    type CType = ffi::ACCESS_DESCRIPTION;
2238    fn drop = ffi::ACCESS_DESCRIPTION_free;
2239
2240    /// `AccessDescription` of certificate authority information.
2241    pub struct AccessDescription;
2242    /// Reference to `AccessDescription`.
2243    pub struct AccessDescriptionRef;
2244}
2245
2246impl AccessDescriptionRef {
2247    /// Returns the access method OID.
2248    pub fn method(&self) -> &Asn1ObjectRef {
2249        unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
2250    }
2251
2252    // Returns the access location.
2253    pub fn location(&self) -> &GeneralNameRef {
2254        unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
2255    }
2256}
2257
2258impl Stackable for AccessDescription {
2259    type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
2260}
2261
2262foreign_type_and_impl_send_sync! {
2263    type CType = ffi::X509_ALGOR;
2264    fn drop = ffi::X509_ALGOR_free;
2265
2266    /// An `X509` certificate signature algorithm.
2267    pub struct X509Algorithm;
2268    /// Reference to `X509Algorithm`.
2269    pub struct X509AlgorithmRef;
2270}
2271
2272impl X509AlgorithmRef {
2273    /// Returns the ASN.1 OID of this algorithm.
2274    pub fn object(&self) -> &Asn1ObjectRef {
2275        unsafe {
2276            let mut oid = ptr::null();
2277            X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
2278            Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
2279        }
2280    }
2281}
2282
2283foreign_type_and_impl_send_sync! {
2284    type CType = ffi::X509_OBJECT;
2285    fn drop = X509_OBJECT_free;
2286
2287    /// An `X509` or an X509 certificate revocation list.
2288    pub struct X509Object;
2289    /// Reference to `X509Object`
2290    pub struct X509ObjectRef;
2291}
2292
2293impl X509ObjectRef {
2294    pub fn x509(&self) -> Option<&X509Ref> {
2295        unsafe {
2296            let ptr = X509_OBJECT_get0_X509(self.as_ptr());
2297            X509Ref::from_const_ptr_opt(ptr)
2298        }
2299    }
2300}
2301
2302impl Stackable for X509Object {
2303    type StackType = ffi::stack_st_X509_OBJECT;
2304}
2305
2306cfg_if! {
2307    if #[cfg(any(boringssl, ossl110, libressl, awslc))] {
2308        use ffi::{X509_getm_notAfter, X509_getm_notBefore, X509_up_ref, X509_get0_signature};
2309    } else {
2310        #[allow(bad_style)]
2311        unsafe fn X509_getm_notAfter(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME {
2312            (*(*(*x).cert_info).validity).notAfter
2313        }
2314
2315        #[allow(bad_style)]
2316        unsafe fn X509_getm_notBefore(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME {
2317            (*(*(*x).cert_info).validity).notBefore
2318        }
2319
2320        #[allow(bad_style)]
2321        unsafe fn X509_up_ref(x: *mut ffi::X509) {
2322            ffi::CRYPTO_add_lock(
2323                &mut (*x).references,
2324                1,
2325                ffi::CRYPTO_LOCK_X509,
2326                "mod.rs\0".as_ptr() as *const _,
2327                line!() as c_int,
2328            );
2329        }
2330
2331        #[allow(bad_style)]
2332        unsafe fn X509_get0_signature(
2333            psig: *mut *const ffi::ASN1_BIT_STRING,
2334            palg: *mut *const ffi::X509_ALGOR,
2335            x: *const ffi::X509,
2336        ) {
2337            if !psig.is_null() {
2338                *psig = (*x).signature;
2339            }
2340            if !palg.is_null() {
2341                *palg = (*x).sig_alg;
2342            }
2343        }
2344    }
2345}
2346
2347cfg_if! {
2348    if #[cfg(any(boringssl, ossl110, libressl, awslc))] {
2349        use ffi::{
2350            X509_ALGOR_get0, ASN1_STRING_get0_data, X509_STORE_CTX_get0_chain, X509_set1_notAfter,
2351            X509_set1_notBefore, X509_REQ_get_version, X509_REQ_get_subject_name,
2352        };
2353    } else {
2354        use ffi::{
2355            ASN1_STRING_data as ASN1_STRING_get0_data,
2356            X509_STORE_CTX_get_chain as X509_STORE_CTX_get0_chain,
2357            X509_set_notAfter as X509_set1_notAfter,
2358            X509_set_notBefore as X509_set1_notBefore,
2359        };
2360
2361        #[allow(bad_style)]
2362        unsafe fn X509_REQ_get_version(x: *mut ffi::X509_REQ) -> ::libc::c_long {
2363            ffi::ASN1_INTEGER_get((*(*x).req_info).version)
2364        }
2365
2366        #[allow(bad_style)]
2367        unsafe fn X509_REQ_get_subject_name(x: *mut ffi::X509_REQ) -> *mut ::ffi::X509_NAME {
2368            (*(*x).req_info).subject
2369        }
2370
2371        #[allow(bad_style)]
2372        unsafe fn X509_ALGOR_get0(
2373            paobj: *mut *const ffi::ASN1_OBJECT,
2374            pptype: *mut c_int,
2375            pval: *mut *mut ::libc::c_void,
2376            alg: *const ffi::X509_ALGOR,
2377        ) {
2378            if !paobj.is_null() {
2379                *paobj = (*alg).algorithm;
2380            }
2381            assert!(pptype.is_null());
2382            assert!(pval.is_null());
2383        }
2384    }
2385}
2386
2387cfg_if! {
2388    if #[cfg(any(ossl110, boringssl, libressl, awslc))] {
2389        use ffi::X509_OBJECT_get0_X509;
2390    } else {
2391        #[allow(bad_style)]
2392        unsafe fn X509_OBJECT_get0_X509(x: *mut ffi::X509_OBJECT) -> *mut ffi::X509 {
2393            if (*x).type_ == ffi::X509_LU_X509 {
2394                (*x).data.x509
2395            } else {
2396                ptr::null_mut()
2397            }
2398        }
2399    }
2400}
2401
2402cfg_if! {
2403    if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
2404        use ffi::X509_OBJECT_free;
2405    } else {
2406        #[allow(bad_style)]
2407        unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) {
2408            ffi::X509_OBJECT_free_contents(x);
2409            ffi::CRYPTO_free(x as *mut libc::c_void);
2410        }
2411    }
2412}
2413
2414cfg_if! {
2415    if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
2416        use ffi::{
2417            X509_CRL_get_issuer, X509_CRL_get0_nextUpdate, X509_CRL_get0_lastUpdate,
2418            X509_CRL_get_REVOKED,
2419            X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
2420        };
2421    } else {
2422        #[allow(bad_style)]
2423        unsafe fn X509_CRL_get0_lastUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME {
2424            (*(*x).crl).lastUpdate
2425        }
2426        #[allow(bad_style)]
2427        unsafe fn X509_CRL_get0_nextUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME {
2428            (*(*x).crl).nextUpdate
2429        }
2430        #[allow(bad_style)]
2431        unsafe fn X509_CRL_get_issuer(x: *const ffi::X509_CRL) -> *mut ffi::X509_NAME {
2432            (*(*x).crl).issuer
2433        }
2434        #[allow(bad_style)]
2435        unsafe fn X509_CRL_get_REVOKED(x: *const ffi::X509_CRL) -> *mut ffi::stack_st_X509_REVOKED {
2436            (*(*x).crl).revoked
2437        }
2438        #[allow(bad_style)]
2439        unsafe fn X509_REVOKED_get0_serialNumber(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_INTEGER {
2440            (*x).serialNumber
2441        }
2442        #[allow(bad_style)]
2443        unsafe fn X509_REVOKED_get0_revocationDate(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_TIME {
2444            (*x).revocationDate
2445        }
2446    }
2447}
2448
2449#[derive(Copy, Clone, PartialEq, Eq)]
2450pub struct X509PurposeId(c_int);
2451
2452impl X509PurposeId {
2453    pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
2454    pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
2455    pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
2456    pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
2457    pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
2458    pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
2459    pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
2460    pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
2461    pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
2462    #[cfg(ossl320)]
2463    pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
2464
2465    /// Constructs an `X509PurposeId` from a raw OpenSSL value.
2466    pub fn from_raw(id: c_int) -> Self {
2467        X509PurposeId(id)
2468    }
2469
2470    /// Returns the raw OpenSSL value represented by this type.
2471    pub fn as_raw(&self) -> c_int {
2472        self.0
2473    }
2474}
2475
2476/// A reference to an [`X509_PURPOSE`].
2477pub struct X509PurposeRef(Opaque);
2478
2479/// Implements a wrapper type for the static `X509_PURPOSE` table in OpenSSL.
2480impl ForeignTypeRef for X509PurposeRef {
2481    type CType = ffi::X509_PURPOSE;
2482}
2483
2484impl X509PurposeRef {
2485    /// Get the internal table index of an X509_PURPOSE for a given short name. Valid short
2486    /// names include
2487    ///  - "sslclient",
2488    ///  - "sslserver",
2489    ///  - "nssslserver",
2490    ///  - "smimesign",
2491    ///  - "smimeencrypt",
2492    ///  - "crlsign",
2493    ///  - "any",
2494    ///  - "ocsphelper",
2495    ///  - "timestampsign"
2496    ///
2497    /// The index can be used with `X509PurposeRef::from_idx()` to get the purpose.
2498    #[allow(clippy::unnecessary_cast)]
2499    pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
2500        unsafe {
2501            let sname = CString::new(sname).unwrap();
2502            cfg_if! {
2503                if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
2504                    let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
2505                } else {
2506                    let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *mut _))?;
2507                }
2508            }
2509            Ok(purpose)
2510        }
2511    }
2512    /// Get an `X509PurposeRef` for a given index value. The index can be obtained from e.g.
2513    /// `X509PurposeRef::get_by_sname()`.
2514    #[corresponds(X509_PURPOSE_get0)]
2515    pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
2516        unsafe {
2517            let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
2518            Ok(X509PurposeRef::from_const_ptr(ptr))
2519        }
2520    }
2521
2522    /// Get the purpose value from an X509Purpose structure. This value is one of
2523    /// - `X509_PURPOSE_SSL_CLIENT`
2524    /// - `X509_PURPOSE_SSL_SERVER`
2525    /// - `X509_PURPOSE_NS_SSL_SERVER`
2526    /// - `X509_PURPOSE_SMIME_SIGN`
2527    /// - `X509_PURPOSE_SMIME_ENCRYPT`
2528    /// - `X509_PURPOSE_CRL_SIGN`
2529    /// - `X509_PURPOSE_ANY`
2530    /// - `X509_PURPOSE_OCSP_HELPER`
2531    /// - `X509_PURPOSE_TIMESTAMP_SIGN`
2532    pub fn purpose(&self) -> X509PurposeId {
2533        unsafe {
2534            cfg_if! {
2535                if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
2536                    let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
2537                } else {
2538                    let x509_purpose = self.as_ptr() as *mut ffi::X509_PURPOSE;
2539                }
2540            }
2541            X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
2542        }
2543    }
2544}