1use foreign_types::{ForeignType, ForeignTypeRef};
11use libc::{c_int, c_long, c_void};
12use openssl_macros::corresponds;
13use std::convert::TryInto;
14use std::error::Error;
15use std::ffi::{CStr, CString};
16use std::fmt;
17use std::marker::PhantomData;
18use std::mem;
19use std::net::IpAddr;
20use std::path::Path;
21use std::ptr;
22use std::str;
23use std::sync::LazyLock;
24
25use crate::asn1::{
26 Asn1BitStringRef, Asn1IntegerRef, Asn1Object, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef,
27 Asn1Type,
28};
29use crate::bio::{MemBio, 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::try_int;
40use crate::util::ForeignTypeRefExt;
41use crate::x509::verify::{X509VerifyParam, X509VerifyParamRef};
42use crate::{cvt, cvt_n, cvt_p};
43use crate::{ffi, free_data_box};
44
45pub mod extension;
46pub mod store;
47pub mod verify;
48
49#[cfg(test)]
50mod tests;
51
52static STORE_INDEX: LazyLock<Index<X509StoreContext, store::X509Store>> =
53 LazyLock::new(|| X509StoreContext::new_ex_index().unwrap());
54
55static CERT_INDEX: LazyLock<Index<X509StoreContext, X509>> =
56 LazyLock::new(|| X509StoreContext::new_ex_index().unwrap());
57
58static CERT_CHAIN_INDEX: LazyLock<Index<X509StoreContext, Stack<X509>>> =
59 LazyLock::new(|| X509StoreContext::new_ex_index().unwrap());
60
61foreign_type_and_impl_send_sync! {
62 type CType = ffi::X509_STORE_CTX;
63 fn drop = ffi::X509_STORE_CTX_free;
64
65 pub struct X509StoreContext;
67}
68
69impl X509StoreContext {
70 #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
73 pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
74 unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
75 }
76
77 #[corresponds(X509_STORE_CTX_new)]
79 pub fn new() -> Result<X509StoreContext, ErrorStack> {
80 unsafe {
81 ffi::init();
82 cvt_p(ffi::X509_STORE_CTX_new()).map(|p| X509StoreContext::from_ptr(p))
83 }
84 }
85
86 #[corresponds(SSL_CTX_get_ex_new_index)]
91 pub fn new_ex_index<T>() -> Result<Index<X509StoreContext, T>, ErrorStack>
92 where
93 T: 'static + Sync + Send,
94 {
95 unsafe {
96 ffi::init();
97 let idx = cvt_n(get_new_x509_store_ctx_idx(Some(free_data_box::<T>)))?;
98 Ok(Index::from_raw(idx))
99 }
100 }
101}
102
103impl X509StoreContextRef {
104 #[corresponds(X509_STORE_CTX_get_ex_data)]
106 #[must_use]
107 pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
108 unsafe {
109 ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw())
110 .cast::<T>()
111 .as_ref()
112 }
113 }
114
115 #[corresponds(X509_STORE_CTX_get_ex_data)]
117 pub fn ex_data_mut<T>(&mut self, index: Index<X509StoreContext, T>) -> Option<&mut T> {
118 unsafe {
119 ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw())
120 .cast::<T>()
121 .as_mut()
122 }
123 }
124
125 #[corresponds(X509_STORE_CTX_set_ex_data)]
130 pub fn set_ex_data<T>(&mut self, index: Index<X509StoreContext, T>, data: T) {
131 if let Some(old) = self.ex_data_mut(index) {
132 *old = data;
133
134 return;
135 }
136
137 unsafe {
138 let data = Box::new(data);
139
140 ffi::X509_STORE_CTX_set_ex_data(
141 self.as_ptr(),
142 index.as_raw(),
143 Box::into_raw(data).cast(),
144 );
145 }
146 }
147
148 #[corresponds(X509_STORE_CTX_get_error)]
150 pub fn verify_result(&self) -> X509VerifyResult {
151 unsafe { X509VerifyError::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
152 }
153
154 #[corresponds(X509_STORE_CTX_init)]
168 pub fn init<F, T>(
169 &mut self,
170 trust: &store::X509StoreRef,
171 cert: &X509Ref,
172 cert_chain: &StackRef<X509>,
173 with_context: F,
174 ) -> Result<T, ErrorStack>
175 where
176 F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
177 {
178 struct Cleanup<'a>(&'a mut X509StoreContextRef);
179
180 impl Drop for Cleanup<'_> {
181 fn drop(&mut self) {
182 unsafe {
183 ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
184 }
185 }
186 }
187
188 unsafe {
189 let cleanup = Cleanup(self);
190
191 cvt(ffi::X509_STORE_CTX_init(
192 cleanup.0.as_ptr(),
193 trust.as_ptr(),
194 cert.as_ptr(),
195 cert_chain.as_ptr(),
196 ))?;
197
198 with_context(cleanup.0)
199 }
200 }
201
202 #[corresponds(X509_STORE_CTX_init)]
209 pub fn reset_with_context_data(
210 &mut self,
211 trust: store::X509Store,
212 cert: X509,
213 cert_chain: Stack<X509>,
214 ) -> Result<(), ErrorStack> {
215 unsafe {
216 if let Err(e) = cvt(ffi::X509_STORE_CTX_init(
217 self.as_ptr(),
218 trust.as_ptr(),
219 cert.as_ptr(),
220 cert_chain.as_ptr(),
221 )) {
222 ffi::X509_STORE_CTX_cleanup(self.as_ptr());
223
224 return Err(e);
225 }
226 }
227
228 self.set_ex_data(*STORE_INDEX, trust);
229 self.set_ex_data(*CERT_INDEX, cert);
230 self.set_ex_data(*CERT_CHAIN_INDEX, cert_chain);
231
232 Ok(())
233 }
234
235 #[corresponds(X509_STORE_CTX_get0_param)]
237 pub fn verify_param(&mut self) -> &X509VerifyParamRef {
238 unsafe { X509VerifyParamRef::from_ptr(ffi::X509_STORE_CTX_get0_param(self.as_ptr())) }
239 }
240
241 #[corresponds(X509_STORE_CTX_get0_param)]
243 pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef {
244 unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_CTX_get0_param(self.as_ptr())) }
245 }
246
247 #[corresponds(X509_STORE_CTX_set0_param)]
249 pub fn set_verify_param(&mut self, param: X509VerifyParam) {
250 unsafe { ffi::X509_STORE_CTX_set0_param(self.as_ptr(), param.into_ptr()) }
251 }
252
253 #[corresponds(X509_verify_cert)]
260 pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
261 unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
262 }
263
264 #[corresponds(X509_STORE_CTX_set_error)]
266 pub fn set_error(&mut self, result: X509VerifyResult) {
267 unsafe {
268 ffi::X509_STORE_CTX_set_error(
269 self.as_ptr(),
270 result
271 .err()
272 .as_ref()
273 .map_or(ffi::X509_V_OK, X509VerifyError::as_raw),
274 );
275 }
276 }
277
278 #[corresponds(X509_STORE_CTX_get_current_cert)]
281 #[must_use]
282 pub fn current_cert(&self) -> Option<&X509Ref> {
283 unsafe {
284 let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
285 if ptr.is_null() {
286 None
287 } else {
288 Some(X509Ref::from_ptr(ptr))
289 }
290 }
291 }
292
293 #[corresponds(X509_STORE_CTX_get_error_depth)]
298 #[must_use]
299 pub fn error_depth(&self) -> u32 {
300 unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
301 }
302
303 #[corresponds(X509_STORE_CTX_get0_chain)]
305 #[must_use]
306 pub fn chain(&self) -> Option<&StackRef<X509>> {
307 unsafe {
308 let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
309
310 if chain.is_null() {
311 None
312 } else {
313 Some(StackRef::from_ptr(chain))
314 }
315 }
316 }
317
318 #[corresponds(X509_STORE_CTX_get0_untrusted)]
321 #[must_use]
322 pub fn untrusted(&self) -> Option<&StackRef<X509>> {
323 unsafe {
324 let certs = ffi::X509_STORE_CTX_get0_untrusted(self.as_ptr());
325
326 if certs.is_null() {
327 None
328 } else {
329 Some(StackRef::from_ptr(certs))
330 }
331 }
332 }
333
334 #[corresponds(X509_STORE_CTX_get0_cert)]
337 #[must_use]
338 pub fn cert(&self) -> Option<&X509Ref> {
339 unsafe {
340 let ptr = ffi::X509_STORE_CTX_get0_cert(self.as_ptr());
341 if ptr.is_null() {
342 None
343 } else {
344 Some(X509Ref::from_ptr(ptr))
345 }
346 }
347 }
348}
349
350pub struct X509Builder(X509);
352
353impl X509Builder {
354 #[corresponds(X509_new)]
356 pub fn new() -> Result<X509Builder, ErrorStack> {
357 unsafe {
358 ffi::init();
359 cvt_p(ffi::X509_new()).map(|p| X509Builder(X509::from_ptr(p)))
360 }
361 }
362
363 #[corresponds(X509_set1_notAfter)]
365 pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
366 unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())) }
367 }
368
369 #[corresponds(X509_set1_notBefore)]
371 pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
372 unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())) }
373 }
374
375 #[corresponds(X509_set_version)]
380 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
381 unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version.into())) }
382 }
383
384 #[corresponds(X509_set_serialNumber)]
386 pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
387 unsafe {
388 cvt(ffi::X509_set_serialNumber(
389 self.0.as_ptr(),
390 serial_number.as_ptr(),
391 ))
392 }
393 }
394
395 #[corresponds(X509_set_issuer_name)]
397 pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
398 unsafe {
399 cvt(ffi::X509_set_issuer_name(
400 self.0.as_ptr(),
401 issuer_name.as_ptr(),
402 ))
403 }
404 }
405
406 #[corresponds(X509_set_subject_name)]
425 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
426 unsafe {
427 cvt(ffi::X509_set_subject_name(
428 self.0.as_ptr(),
429 subject_name.as_ptr(),
430 ))
431 }
432 }
433
434 #[corresponds(X509_set_pubkey)]
436 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
437 where
438 T: HasPublic,
439 {
440 unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())) }
441 }
442
443 #[corresponds(X509V3_set_ctx)]
447 #[must_use]
448 pub fn x509v3_context<'a>(
449 &'a self,
450 issuer: Option<&'a X509Ref>,
451 conf: Option<&'a ConfRef>,
452 ) -> X509v3Context<'a> {
453 unsafe {
454 let mut ctx = mem::zeroed();
455
456 let issuer = match issuer {
457 Some(issuer) => issuer.as_ptr(),
458 None => self.0.as_ptr(),
459 };
460 let subject = self.0.as_ptr();
461 ffi::X509V3_set_ctx(
462 &mut ctx,
463 issuer,
464 subject,
465 ptr::null_mut(),
466 ptr::null_mut(),
467 0,
468 );
469
470 if let Some(conf) = conf {
472 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
473 }
474
475 X509v3Context(ctx, PhantomData)
476 }
477 }
478
479 #[corresponds(X509_add_ext)]
481 pub fn append_extension(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
482 unsafe {
483 cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
484 Ok(())
485 }
486 }
487
488 #[corresponds(X509_sign)]
490 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
491 where
492 T: HasPrivate,
493 {
494 unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())) }
495 }
496
497 #[must_use]
499 pub fn build(self) -> X509 {
500 self.0
501 }
502}
503
504foreign_type_and_impl_send_sync! {
505 type CType = ffi::X509;
506 fn drop = ffi::X509_free;
507
508 pub struct X509;
510}
511
512impl X509Ref {
513 #[corresponds(X509_get_subject_name)]
515 #[must_use]
516 pub fn subject_name(&self) -> &X509NameRef {
517 unsafe {
518 let name = ffi::X509_get_subject_name(self.as_ptr());
519 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
520 }
521 }
522
523 #[corresponds(X509_subject_name_hash)]
525 #[must_use]
526 pub fn subject_name_hash(&self) -> u32 {
527 unsafe { ffi::X509_subject_name_hash(self.as_ptr()) as u32 }
528 }
529
530 #[corresponds(X509_get_ext_d2i)]
532 #[must_use]
533 pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
534 unsafe {
535 let stack = ffi::X509_get_ext_d2i(
536 self.as_ptr(),
537 ffi::NID_subject_alt_name,
538 ptr::null_mut(),
539 ptr::null_mut(),
540 );
541 if stack.is_null() {
542 None
543 } else {
544 Some(Stack::from_ptr(stack.cast()))
545 }
546 }
547 }
548
549 #[corresponds(X509_get_issuer_name)]
551 #[must_use]
552 pub fn issuer_name(&self) -> &X509NameRef {
553 unsafe {
554 let name = ffi::X509_get_issuer_name(self.as_ptr());
555 X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
556 }
557 }
558
559 #[corresponds(X509_get_ext_d2i)]
561 #[must_use]
562 pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
563 unsafe {
564 let stack = ffi::X509_get_ext_d2i(
565 self.as_ptr(),
566 ffi::NID_issuer_alt_name,
567 ptr::null_mut(),
568 ptr::null_mut(),
569 );
570 if stack.is_null() {
571 None
572 } else {
573 Some(Stack::from_ptr(stack.cast()))
574 }
575 }
576 }
577
578 #[corresponds(X509_get0_subject_key_id)]
580 #[must_use]
581 pub fn subject_key_id(&self) -> Option<&Asn1StringRef> {
582 unsafe {
583 let data = ffi::X509_get0_subject_key_id(self.as_ptr());
584 Asn1StringRef::from_const_ptr_opt(data)
585 }
586 }
587
588 #[corresponds(X509_get0_authority_key_id)]
590 #[must_use]
591 pub fn authority_key_id(&self) -> Option<&Asn1StringRef> {
592 unsafe {
593 let data = ffi::X509_get0_authority_key_id(self.as_ptr());
594 Asn1StringRef::from_const_ptr_opt(data)
595 }
596 }
597
598 #[corresponds(X509_get_pubkey)]
599 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
600 unsafe {
601 let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
602 Ok(PKey::from_ptr(pkey))
603 }
604 }
605
606 #[corresponds(X509_digest)]
608 pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
609 unsafe {
610 let mut digest = DigestBytes {
611 buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
612 len: ffi::EVP_MAX_MD_SIZE as usize,
613 };
614 let mut len = try_int(ffi::EVP_MAX_MD_SIZE)?;
615 cvt(ffi::X509_digest(
616 self.as_ptr(),
617 hash_type.as_ptr(),
618 digest.buf.as_mut_ptr(),
619 &mut len,
620 ))?;
621 digest.len = try_int(len)?;
622
623 Ok(digest)
624 }
625 }
626
627 #[deprecated(since = "0.10.9", note = "renamed to digest")]
628 pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
629 self.digest(hash_type).map(|b| b.to_vec())
630 }
631
632 #[corresponds(X509_getm_notAfter)]
634 #[must_use]
635 pub fn not_after(&self) -> &Asn1TimeRef {
636 unsafe {
637 let date = X509_getm_notAfter(self.as_ptr());
638 assert!(!date.is_null());
639 Asn1TimeRef::from_ptr(date)
640 }
641 }
642
643 #[corresponds(X509_getm_notBefore)]
645 #[must_use]
646 pub fn not_before(&self) -> &Asn1TimeRef {
647 unsafe {
648 let date = X509_getm_notBefore(self.as_ptr());
649 assert!(!date.is_null());
650 Asn1TimeRef::from_ptr(date)
651 }
652 }
653
654 #[corresponds(X509_get0_signature)]
656 #[must_use]
657 pub fn signature(&self) -> &Asn1BitStringRef {
658 unsafe {
659 let mut signature = ptr::null();
660 X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
661 assert!(!signature.is_null());
662 Asn1BitStringRef::from_ptr(signature.cast_mut())
663 }
664 }
665
666 #[corresponds(X509_get0_signature)]
668 #[must_use]
669 pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
670 unsafe {
671 let mut algor = ptr::null();
672 X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
673 assert!(!algor.is_null());
674 X509AlgorithmRef::from_ptr(algor.cast_mut())
675 }
676 }
677
678 #[corresponds(X509_get1_ocsp)]
681 pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
682 unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) }
683 }
684
685 #[corresponds(X509_check_issued)]
687 pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
688 unsafe {
689 let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
690 X509VerifyError::from_raw(r)
691 }
692 }
693
694 #[corresponds(X509_verify)]
701 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
702 where
703 T: HasPublic,
704 {
705 unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
706 }
707
708 #[corresponds(X509_get_serialNumber)]
710 #[must_use]
711 pub fn serial_number(&self) -> &Asn1IntegerRef {
712 unsafe {
713 let r = ffi::X509_get_serialNumber(self.as_ptr());
714 assert!(!r.is_null());
715 Asn1IntegerRef::from_ptr(r)
716 }
717 }
718
719 pub fn check_host(&self, host: &str) -> Result<bool, ErrorStack> {
720 unsafe {
721 cvt_n(ffi::X509_check_host(
722 self.as_ptr(),
723 host.as_ptr().cast(),
724 host.len(),
725 0,
726 std::ptr::null_mut(),
727 ))
728 .map(|n| n == 1)
729 }
730 }
731
732 #[corresponds(X509_check_ip_asc)]
733 pub fn check_ip_asc(&self, address: &str) -> Result<bool, ErrorStack> {
734 let c_str = CString::new(address).map_err(ErrorStack::internal_error)?;
735
736 unsafe { cvt_n(ffi::X509_check_ip_asc(self.as_ptr(), c_str.as_ptr(), 0)).map(|n| n == 1) }
737 }
738
739 to_pem! {
740 #[corresponds(PEM_write_bio_X509)]
744 to_pem,
745 ffi::PEM_write_bio_X509
746 }
747
748 to_der! {
749 #[corresponds(i2d_X509)]
751 to_der,
752 ffi::i2d_X509
753 }
754}
755
756impl ToOwned for X509Ref {
757 type Owned = X509;
758
759 fn to_owned(&self) -> X509 {
760 unsafe {
761 X509_up_ref(self.as_ptr());
762 X509::from_ptr(self.as_ptr())
763 }
764 }
765}
766
767impl X509 {
768 pub fn builder() -> Result<X509Builder, ErrorStack> {
770 X509Builder::new()
771 }
772
773 from_pem! {
774 #[corresponds(PEM_read_bio_X509)]
778 from_pem,
779 X509,
780 ffi::PEM_read_bio_X509
781 }
782
783 from_der! {
784 #[corresponds(d2i_X509)]
786 from_der,
787 X509,
788 ffi::d2i_X509,
789 ::libc::c_long
790 }
791
792 #[corresponds(PEM_read_bio_X509)]
794 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
795 unsafe {
796 ffi::init();
797 let bio = MemBioSlice::new(pem)?;
798
799 let mut certs = vec![];
800 loop {
801 let r =
802 ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
803 if r.is_null() {
804 let err = ffi::ERR_peek_last_error();
805
806 if ffi::ERR_GET_LIB(err) == ffi::ERR_LIB_PEM.0.try_into().unwrap()
807 && ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE
808 {
809 ErrorStack::clear();
810 break;
811 }
812
813 return Err(ErrorStack::get());
814 } else {
815 certs.push(X509::from_ptr(r));
816 }
817 }
818
819 Ok(certs)
820 }
821 }
822}
823
824impl Clone for X509 {
825 fn clone(&self) -> X509 {
826 X509Ref::to_owned(self)
827 }
828}
829
830impl fmt::Debug for X509 {
831 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
832 let serial = match &self.serial_number().to_bn() {
833 Ok(bn) => match bn.to_hex_str() {
834 Ok(hex) => hex.to_string(),
835 Err(_) => "".to_string(),
836 },
837 Err(_) => "".to_string(),
838 };
839 let mut debug_struct = formatter.debug_struct("X509");
840 debug_struct.field("serial_number", &serial);
841 debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
842 debug_struct.field("issuer", &self.issuer_name());
843 debug_struct.field("subject", &self.subject_name());
844 if let Some(subject_alt_names) = &self.subject_alt_names() {
845 debug_struct.field("subject_alt_names", subject_alt_names);
846 }
847 debug_struct.field("not_before", &self.not_before());
848 debug_struct.field("not_after", &self.not_after());
849
850 if let Ok(public_key) = &self.public_key() {
851 debug_struct.field("public_key", public_key);
852 }
853 debug_struct.finish()
856 }
857}
858
859impl AsRef<X509Ref> for X509Ref {
860 fn as_ref(&self) -> &X509Ref {
861 self
862 }
863}
864
865impl Stackable for X509 {
866 type StackType = ffi::stack_st_X509;
867}
868
869pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
871
872impl X509v3Context<'_> {
873 #[must_use]
874 pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
875 std::ptr::addr_of!(self.0).cast_mut()
876 }
877}
878
879foreign_type_and_impl_send_sync! {
880 type CType = ffi::X509_EXTENSION;
881 fn drop = ffi::X509_EXTENSION_free;
882
883 pub struct X509Extension;
885}
886
887impl Stackable for X509Extension {
888 type StackType = ffi::stack_st_X509_EXTENSION;
889}
890
891impl X509Extension {
892 pub fn new(
903 conf: Option<&ConfRef>,
904 context: Option<&X509v3Context>,
905 name: &str,
906 value: &str,
907 ) -> Result<X509Extension, ErrorStack> {
908 let name = CString::new(name).map_err(ErrorStack::internal_error)?;
909 let value = CString::new(value).map_err(ErrorStack::internal_error)?;
910 let mut ctx;
911 unsafe {
912 ffi::init();
913 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
914 let context_ptr = match context {
915 Some(c) => c.as_ptr(),
916 None => {
917 ctx = mem::zeroed();
918
919 ffi::X509V3_set_ctx(
920 &mut ctx,
921 ptr::null_mut(),
922 ptr::null_mut(),
923 ptr::null_mut(),
924 ptr::null_mut(),
925 0,
926 );
927 &mut ctx
928 }
929 };
930 let name = name.as_ptr().cast_mut();
931 let value = value.as_ptr().cast_mut();
932
933 cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value))
934 .map(|p| X509Extension::from_ptr(p))
935 }
936 }
937
938 pub fn new_nid(
949 conf: Option<&ConfRef>,
950 context: Option<&X509v3Context>,
951 name: Nid,
952 value: &str,
953 ) -> Result<X509Extension, ErrorStack> {
954 let value = CString::new(value).map_err(ErrorStack::internal_error)?;
955 let mut ctx;
956 unsafe {
957 ffi::init();
958 let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
959 let context_ptr = match context {
960 Some(c) => c.as_ptr(),
961 None => {
962 ctx = mem::zeroed();
963
964 ffi::X509V3_set_ctx(
965 &mut ctx,
966 ptr::null_mut(),
967 ptr::null_mut(),
968 ptr::null_mut(),
969 ptr::null_mut(),
970 0,
971 );
972 &mut ctx
973 }
974 };
975 let name = name.as_raw();
976 let value = value.as_ptr().cast_mut();
977
978 cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value))
979 .map(|p| X509Extension::from_ptr(p))
980 }
981 }
982
983 pub(crate) unsafe fn new_internal(
984 nid: Nid,
985 critical: bool,
986 value: *mut c_void,
987 ) -> Result<X509Extension, ErrorStack> {
988 ffi::init();
989 unsafe {
990 cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value))
991 .map(|p| X509Extension::from_ptr(p))
992 }
993 }
994}
995
996impl X509ExtensionRef {
997 to_der! {
998 to_der,
1000 ffi::i2d_X509_EXTENSION
1001 }
1002}
1003
1004pub struct X509NameBuilder(X509Name);
1006
1007impl X509NameBuilder {
1008 pub fn new() -> Result<X509NameBuilder, ErrorStack> {
1010 unsafe {
1011 ffi::init();
1012 cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name::from_ptr(p)))
1013 }
1014 }
1015
1016 #[corresponds(X509_NAME_add_entry_by_txt)]
1018 pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
1019 unsafe {
1020 let field = CString::new(field).map_err(ErrorStack::internal_error)?;
1021 cvt(ffi::X509_NAME_add_entry_by_txt(
1022 self.0.as_ptr(),
1023 field.as_ptr().cast_mut(),
1024 ffi::MBSTRING_UTF8,
1025 value.as_ptr(),
1026 try_int(value.len())?,
1027 -1,
1028 0,
1029 ))
1030 }
1031 }
1032
1033 #[corresponds(X509_NAME_add_entry_by_txt)]
1035 pub fn append_entry_by_text_with_type(
1036 &mut self,
1037 field: &str,
1038 value: &str,
1039 ty: Asn1Type,
1040 ) -> Result<(), ErrorStack> {
1041 unsafe {
1042 let field = CString::new(field).map_err(ErrorStack::internal_error)?;
1043 cvt(ffi::X509_NAME_add_entry_by_txt(
1044 self.0.as_ptr(),
1045 field.as_ptr().cast_mut(),
1046 ty.as_raw(),
1047 value.as_ptr(),
1048 try_int(value.len())?,
1049 -1,
1050 0,
1051 ))
1052 }
1053 }
1054
1055 #[corresponds(X509_NAME_add_entry_by_NID)]
1057 pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
1058 unsafe {
1059 cvt(ffi::X509_NAME_add_entry_by_NID(
1060 self.0.as_ptr(),
1061 field.as_raw(),
1062 ffi::MBSTRING_UTF8,
1063 value.as_ptr().cast_mut(),
1064 try_int(value.len())?,
1065 -1,
1066 0,
1067 ))
1068 }
1069 }
1070
1071 #[corresponds(X509_NAME_add_entry_by_NID)]
1073 pub fn append_entry_by_nid_with_type(
1074 &mut self,
1075 field: Nid,
1076 value: &str,
1077 ty: Asn1Type,
1078 ) -> Result<(), ErrorStack> {
1079 unsafe {
1080 cvt(ffi::X509_NAME_add_entry_by_NID(
1081 self.0.as_ptr(),
1082 field.as_raw(),
1083 ty.as_raw(),
1084 value.as_ptr().cast_mut(),
1085 try_int(value.len())?,
1086 -1,
1087 0,
1088 ))
1089 }
1090 }
1091
1092 #[must_use]
1094 pub fn build(self) -> X509Name {
1095 X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
1099 }
1100}
1101
1102foreign_type_and_impl_send_sync! {
1103 type CType = ffi::X509_NAME;
1104 fn drop = ffi::X509_NAME_free;
1105
1106 pub struct X509Name;
1108}
1109
1110impl X509Name {
1111 pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
1113 X509NameBuilder::new()
1114 }
1115
1116 pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
1120 let file = CString::new(file.as_ref().as_os_str().as_encoded_bytes())
1121 .map_err(ErrorStack::internal_error)?;
1122 unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
1123 }
1124
1125 from_der! {
1126 #[corresponds(d2i_X509_NAME)]
1128 from_der,
1129 X509Name,
1130 ffi::d2i_X509_NAME,
1131 ::libc::c_long
1132 }
1133}
1134
1135impl Stackable for X509Name {
1136 type StackType = ffi::stack_st_X509_NAME;
1137}
1138
1139impl X509NameRef {
1140 #[must_use]
1142 pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
1143 X509NameEntries {
1144 name: self,
1145 nid: Some(nid),
1146 loc: -1,
1147 }
1148 }
1149
1150 #[must_use]
1152 pub fn entries(&self) -> X509NameEntries<'_> {
1153 X509NameEntries {
1154 name: self,
1155 nid: None,
1156 loc: -1,
1157 }
1158 }
1159
1160 #[corresponds(X509_NAME_print_ex)]
1164 #[must_use]
1165 pub fn print_ex(&self, flags: i32) -> Option<String> {
1166 unsafe {
1167 let bio = MemBio::new().ok()?;
1168 ffi::X509_NAME_print_ex(bio.as_ptr(), self.as_ptr(), 0, flags as _);
1169 let buf = bio.get_buf().to_vec();
1170 let res = String::from_utf8(buf);
1171 res.ok()
1172 }
1173 }
1174
1175 to_der! {
1176 #[corresponds(i2d_X509_NAME)]
1178 to_der,
1179 ffi::i2d_X509_NAME
1180 }
1181}
1182
1183impl fmt::Debug for X509NameRef {
1184 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1185 formatter.debug_list().entries(self.entries()).finish()
1186 }
1187}
1188
1189pub struct X509NameEntries<'a> {
1191 name: &'a X509NameRef,
1192 nid: Option<Nid>,
1193 loc: c_int,
1194}
1195
1196impl<'a> Iterator for X509NameEntries<'a> {
1197 type Item = &'a X509NameEntryRef;
1198
1199 fn next(&mut self) -> Option<&'a X509NameEntryRef> {
1200 unsafe {
1201 match self.nid {
1202 Some(nid) => {
1203 self.loc =
1205 ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
1206 if self.loc == -1 {
1207 return None;
1208 }
1209 }
1210 None => {
1211 self.loc += 1;
1213 if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
1214 return None;
1215 }
1216 }
1217 }
1218
1219 let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
1220 assert!(!entry.is_null());
1221
1222 Some(X509NameEntryRef::from_ptr(entry))
1223 }
1224 }
1225}
1226
1227foreign_type_and_impl_send_sync! {
1228 type CType = ffi::X509_NAME_ENTRY;
1229 fn drop = ffi::X509_NAME_ENTRY_free;
1230
1231 pub struct X509NameEntry;
1233}
1234
1235impl X509NameEntryRef {
1236 #[corresponds(X509_NAME_ENTRY_get_data)]
1238 #[must_use]
1239 pub fn data(&self) -> &Asn1StringRef {
1240 unsafe {
1241 let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
1242 Asn1StringRef::from_ptr(data)
1243 }
1244 }
1245
1246 #[corresponds(X509_NAME_ENTRY_get_object)]
1249 #[must_use]
1250 pub fn object(&self) -> &Asn1ObjectRef {
1251 unsafe {
1252 let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
1253 Asn1ObjectRef::from_ptr(object)
1254 }
1255 }
1256}
1257
1258impl fmt::Debug for X509NameEntryRef {
1259 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1260 formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
1261 }
1262}
1263
1264pub struct X509ReqBuilder(X509Req);
1266
1267impl X509ReqBuilder {
1268 #[corresponds(X509_REQ_new)]
1270 pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
1271 unsafe {
1272 ffi::init();
1273 cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req::from_ptr(p)))
1274 }
1275 }
1276
1277 #[corresponds(X509_REQ_set_version)]
1279 pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
1280 unsafe { cvt(ffi::X509_REQ_set_version(self.0.as_ptr(), version.into())) }
1281 }
1282
1283 #[corresponds(X509_REQ_set_subject_name)]
1285 pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
1286 unsafe {
1287 cvt(ffi::X509_REQ_set_subject_name(
1288 self.0.as_ptr(),
1289 subject_name.as_ptr(),
1290 ))
1291 }
1292 }
1293
1294 #[corresponds(X509_REQ_set_pubkey)]
1296 pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
1297 where
1298 T: HasPublic,
1299 {
1300 unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())) }
1301 }
1302
1303 #[must_use]
1306 pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
1307 unsafe {
1308 let mut ctx = mem::zeroed();
1309
1310 ffi::X509V3_set_ctx(
1311 &mut ctx,
1312 ptr::null_mut(),
1313 ptr::null_mut(),
1314 self.0.as_ptr(),
1315 ptr::null_mut(),
1316 0,
1317 );
1318
1319 if let Some(conf) = conf {
1321 ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
1322 }
1323
1324 X509v3Context(ctx, PhantomData)
1325 }
1326 }
1327
1328 pub fn add_extensions(
1330 &mut self,
1331 extensions: &StackRef<X509Extension>,
1332 ) -> Result<(), ErrorStack> {
1333 unsafe {
1334 cvt(ffi::X509_REQ_add_extensions(
1335 self.0.as_ptr(),
1336 extensions.as_ptr(),
1337 ))
1338 }
1339 }
1340
1341 #[corresponds(X509_REQ_sign)]
1343 pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
1344 where
1345 T: HasPrivate,
1346 {
1347 unsafe {
1348 cvt(ffi::X509_REQ_sign(
1349 self.0.as_ptr(),
1350 key.as_ptr(),
1351 hash.as_ptr(),
1352 ))
1353 }
1354 }
1355
1356 #[must_use]
1358 pub fn build(self) -> X509Req {
1359 self.0
1360 }
1361}
1362
1363foreign_type_and_impl_send_sync! {
1364 type CType = ffi::X509_REQ;
1365 fn drop = ffi::X509_REQ_free;
1366
1367 pub struct X509Req;
1369}
1370
1371impl X509Req {
1372 pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
1374 X509ReqBuilder::new()
1375 }
1376
1377 from_pem! {
1378 #[corresponds(PEM_read_bio_X509_REQ)]
1382 from_pem,
1383 X509Req,
1384 ffi::PEM_read_bio_X509_REQ
1385 }
1386
1387 from_der! {
1388 #[corresponds(d2i_X509_REQ)]
1390 from_der,
1391 X509Req,
1392 ffi::d2i_X509_REQ,
1393 ::libc::c_long
1394 }
1395}
1396
1397impl X509ReqRef {
1398 to_pem! {
1399 #[corresponds(PEM_write_bio_X509_REQ)]
1403 to_pem,
1404 ffi::PEM_write_bio_X509_REQ
1405 }
1406
1407 to_der! {
1408 #[corresponds(i2d_X509_REQ)]
1410 to_der,
1411 ffi::i2d_X509_REQ
1412 }
1413
1414 #[corresponds(X509_REQ_get_version)]
1416 #[must_use]
1417 pub fn version(&self) -> i32 {
1418 unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
1419 }
1420
1421 #[corresponds(X509_REQ_get_subject_name)]
1423 #[must_use]
1424 pub fn subject_name(&self) -> &X509NameRef {
1425 unsafe {
1426 let name = X509_REQ_get_subject_name(self.as_ptr());
1427 assert!(!name.is_null());
1428 X509NameRef::from_ptr(name)
1429 }
1430 }
1431
1432 #[corresponds(X509_REQ_get_pubkey)]
1434 pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
1435 unsafe {
1436 let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
1437 Ok(PKey::from_ptr(key))
1438 }
1439 }
1440
1441 #[corresponds(X509_REQ_verify)]
1445 pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
1446 where
1447 T: HasPublic,
1448 {
1449 unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
1450 }
1451
1452 #[corresponds(X509_REQ_get_extensions)]
1454 pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
1455 unsafe {
1456 let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
1457 Ok(Stack::from_ptr(extensions))
1458 }
1459 }
1460}
1461
1462pub type X509VerifyResult = Result<(), X509VerifyError>;
1464
1465#[derive(Copy, Clone, PartialEq, Eq)]
1466pub struct X509VerifyError(c_int);
1467
1468impl fmt::Debug for X509VerifyError {
1469 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1470 fmt.debug_struct("X509VerifyError")
1471 .field("code", &self.0)
1472 .field("error", &self.error_string())
1473 .finish()
1474 }
1475}
1476
1477impl fmt::Display for X509VerifyError {
1478 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1479 fmt.write_str(self.error_string())
1480 }
1481}
1482
1483impl Error for X509VerifyError {}
1484
1485impl X509VerifyError {
1486 pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
1493 if err == ffi::X509_V_OK {
1494 Ok(())
1495 } else {
1496 Err(X509VerifyError(err))
1497 }
1498 }
1499
1500 #[allow(clippy::trivially_copy_pass_by_ref)]
1502 #[must_use]
1503 pub fn as_raw(&self) -> c_int {
1504 self.0
1505 }
1506
1507 #[corresponds(X509_verify_cert_error_string)]
1511 #[allow(clippy::trivially_copy_pass_by_ref)]
1512 #[must_use]
1513 pub fn error_string(&self) -> &'static str {
1514 ffi::init();
1515
1516 unsafe {
1517 let s = ffi::X509_verify_cert_error_string(c_long::from(self.0));
1518 CStr::from_ptr(s).to_str().unwrap_or_default()
1519 }
1520 }
1521}
1522
1523#[allow(missing_docs)] impl X509VerifyError {
1525 pub const UNSPECIFIED: Self = Self(ffi::X509_V_ERR_UNSPECIFIED);
1526 pub const UNABLE_TO_GET_ISSUER_CERT: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT);
1527 pub const UNABLE_TO_GET_CRL: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_CRL);
1528 pub const UNABLE_TO_DECRYPT_CERT_SIGNATURE: Self =
1529 Self(ffi::X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE);
1530 pub const UNABLE_TO_DECRYPT_CRL_SIGNATURE: Self =
1531 Self(ffi::X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE);
1532 pub const UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: Self =
1533 Self(ffi::X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY);
1534 pub const CERT_SIGNATURE_FAILURE: Self = Self(ffi::X509_V_ERR_CERT_SIGNATURE_FAILURE);
1535 pub const CRL_SIGNATURE_FAILURE: Self = Self(ffi::X509_V_ERR_CRL_SIGNATURE_FAILURE);
1536 pub const CERT_NOT_YET_VALID: Self = Self(ffi::X509_V_ERR_CERT_NOT_YET_VALID);
1537 pub const CERT_HAS_EXPIRED: Self = Self(ffi::X509_V_ERR_CERT_HAS_EXPIRED);
1538 pub const CRL_NOT_YET_VALID: Self = Self(ffi::X509_V_ERR_CRL_NOT_YET_VALID);
1539 pub const CRL_HAS_EXPIRED: Self = Self(ffi::X509_V_ERR_CRL_HAS_EXPIRED);
1540 pub const ERROR_IN_CERT_NOT_BEFORE_FIELD: Self =
1541 Self(ffi::X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD);
1542 pub const ERROR_IN_CERT_NOT_AFTER_FIELD: Self =
1543 Self(ffi::X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD);
1544 pub const ERROR_IN_CRL_LAST_UPDATE_FIELD: Self =
1545 Self(ffi::X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD);
1546 pub const ERROR_IN_CRL_NEXT_UPDATE_FIELD: Self =
1547 Self(ffi::X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
1548 pub const OUT_OF_MEM: Self = Self(ffi::X509_V_ERR_OUT_OF_MEM);
1549 pub const DEPTH_ZERO_SELF_SIGNED_CERT: Self = Self(ffi::X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
1550 pub const SELF_SIGNED_CERT_IN_CHAIN: Self = Self(ffi::X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN);
1551 pub const UNABLE_TO_GET_ISSUER_CERT_LOCALLY: Self =
1552 Self(ffi::X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY);
1553 pub const UNABLE_TO_VERIFY_LEAF_SIGNATURE: Self =
1554 Self(ffi::X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
1555 pub const CERT_CHAIN_TOO_LONG: Self = Self(ffi::X509_V_ERR_CERT_CHAIN_TOO_LONG);
1556 pub const CERT_REVOKED: Self = Self(ffi::X509_V_ERR_CERT_REVOKED);
1557 pub const INVALID_CA: Self = Self(ffi::X509_V_ERR_INVALID_CA);
1558 pub const PATH_LENGTH_EXCEEDED: Self = Self(ffi::X509_V_ERR_PATH_LENGTH_EXCEEDED);
1559 pub const INVALID_PURPOSE: Self = Self(ffi::X509_V_ERR_INVALID_PURPOSE);
1560 pub const CERT_UNTRUSTED: Self = Self(ffi::X509_V_ERR_CERT_UNTRUSTED);
1561 pub const CERT_REJECTED: Self = Self(ffi::X509_V_ERR_CERT_REJECTED);
1562 pub const SUBJECT_ISSUER_MISMATCH: Self = Self(ffi::X509_V_ERR_SUBJECT_ISSUER_MISMATCH);
1563 pub const AKID_SKID_MISMATCH: Self = Self(ffi::X509_V_ERR_AKID_SKID_MISMATCH);
1564 pub const AKID_ISSUER_SERIAL_MISMATCH: Self = Self(ffi::X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH);
1565 pub const KEYUSAGE_NO_CERTSIGN: Self = Self(ffi::X509_V_ERR_KEYUSAGE_NO_CERTSIGN);
1566 pub const UNABLE_TO_GET_CRL_ISSUER: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER);
1567 pub const UNHANDLED_CRITICAL_EXTENSION: Self =
1568 Self(ffi::X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION);
1569 pub const KEYUSAGE_NO_CRL_SIGN: Self = Self(ffi::X509_V_ERR_KEYUSAGE_NO_CRL_SIGN);
1570 pub const UNHANDLED_CRITICAL_CRL_EXTENSION: Self =
1571 Self(ffi::X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION);
1572 pub const INVALID_NON_CA: Self = Self(ffi::X509_V_ERR_INVALID_NON_CA);
1573 pub const PROXY_PATH_LENGTH_EXCEEDED: Self = Self(ffi::X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED);
1574 pub const KEYUSAGE_NO_DIGITAL_SIGNATURE: Self =
1575 Self(ffi::X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE);
1576 pub const PROXY_CERTIFICATES_NOT_ALLOWED: Self =
1577 Self(ffi::X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED);
1578 pub const INVALID_EXTENSION: Self = Self(ffi::X509_V_ERR_INVALID_EXTENSION);
1579 pub const INVALID_POLICY_EXTENSION: Self = Self(ffi::X509_V_ERR_INVALID_POLICY_EXTENSION);
1580 pub const NO_EXPLICIT_POLICY: Self = Self(ffi::X509_V_ERR_NO_EXPLICIT_POLICY);
1581 pub const DIFFERENT_CRL_SCOPE: Self = Self(ffi::X509_V_ERR_DIFFERENT_CRL_SCOPE);
1582 pub const UNSUPPORTED_EXTENSION_FEATURE: Self =
1583 Self(ffi::X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE);
1584 pub const UNNESTED_RESOURCE: Self = Self(ffi::X509_V_ERR_UNNESTED_RESOURCE);
1585 pub const PERMITTED_VIOLATION: Self = Self(ffi::X509_V_ERR_PERMITTED_VIOLATION);
1586 pub const EXCLUDED_VIOLATION: Self = Self(ffi::X509_V_ERR_EXCLUDED_VIOLATION);
1587 pub const SUBTREE_MINMAX: Self = Self(ffi::X509_V_ERR_SUBTREE_MINMAX);
1588 pub const APPLICATION_VERIFICATION: Self = Self(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
1589 pub const UNSUPPORTED_CONSTRAINT_TYPE: Self = Self(ffi::X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE);
1590 pub const UNSUPPORTED_CONSTRAINT_SYNTAX: Self =
1591 Self(ffi::X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX);
1592 pub const UNSUPPORTED_NAME_SYNTAX: Self = Self(ffi::X509_V_ERR_UNSUPPORTED_NAME_SYNTAX);
1593 pub const CRL_PATH_VALIDATION_ERROR: Self = Self(ffi::X509_V_ERR_CRL_PATH_VALIDATION_ERROR);
1594 pub const HOSTNAME_MISMATCH: Self = Self(ffi::X509_V_ERR_HOSTNAME_MISMATCH);
1595 pub const EMAIL_MISMATCH: Self = Self(ffi::X509_V_ERR_EMAIL_MISMATCH);
1596 pub const IP_ADDRESS_MISMATCH: Self = Self(ffi::X509_V_ERR_IP_ADDRESS_MISMATCH);
1597 pub const INVALID_CALL: Self = Self(ffi::X509_V_ERR_INVALID_CALL);
1598 pub const STORE_LOOKUP: Self = Self(ffi::X509_V_ERR_STORE_LOOKUP);
1599 pub const NAME_CONSTRAINTS_WITHOUT_SANS: Self =
1600 Self(ffi::X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS);
1601}
1602
1603foreign_type_and_impl_send_sync! {
1604 type CType = ffi::GENERAL_NAME;
1605 fn drop = ffi::GENERAL_NAME_free;
1606
1607 pub struct GeneralName;
1609}
1610
1611impl GeneralName {
1612 unsafe fn new(
1613 type_: c_int,
1614 asn1_type: Asn1Type,
1615 value: &[u8],
1616 ) -> Result<GeneralName, ErrorStack> {
1617 ffi::init();
1618 unsafe {
1619 let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
1620 (*gn.as_ptr()).type_ = type_;
1621 let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
1622 ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
1623
1624 (*gn.as_ptr()).d.ptr = s.cast();
1625
1626 Ok(gn)
1627 }
1628 }
1629
1630 pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
1631 unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
1632 }
1633
1634 pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
1635 unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
1636 }
1637
1638 pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
1639 unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
1640 }
1641
1642 pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
1643 match ip {
1644 IpAddr::V4(addr) => unsafe {
1645 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
1646 },
1647 IpAddr::V6(addr) => unsafe {
1648 GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
1649 },
1650 }
1651 }
1652
1653 pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
1654 unsafe {
1655 ffi::init();
1656 let gn = cvt_p(ffi::GENERAL_NAME_new())?;
1657 (*gn).type_ = ffi::GEN_RID;
1658 (*gn).d.registeredID = oid.into_ptr();
1659
1660 Ok(GeneralName::from_ptr(gn))
1661 }
1662 }
1663}
1664
1665impl GeneralNameRef {
1666 fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
1667 unsafe {
1668 if (*self.as_ptr()).type_ != ffi_type {
1669 return None;
1670 }
1671
1672 let asn = Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ia5);
1673
1674 asn.to_str()
1678 }
1679 }
1680
1681 #[must_use]
1683 pub fn email(&self) -> Option<&str> {
1684 self.ia5_string(ffi::GEN_EMAIL)
1685 }
1686
1687 #[must_use]
1689 pub fn dnsname(&self) -> Option<&str> {
1690 self.ia5_string(ffi::GEN_DNS)
1691 }
1692
1693 #[must_use]
1695 pub fn uri(&self) -> Option<&str> {
1696 self.ia5_string(ffi::GEN_URI)
1697 }
1698
1699 #[must_use]
1701 pub fn ipaddress(&self) -> Option<&[u8]> {
1702 unsafe {
1703 if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
1704 return None;
1705 }
1706
1707 Some(Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ip).as_slice())
1708 }
1709 }
1710}
1711
1712impl fmt::Debug for GeneralNameRef {
1713 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1714 if let Some(email) = self.email() {
1715 formatter.write_str(email)
1716 } else if let Some(dnsname) = self.dnsname() {
1717 formatter.write_str(dnsname)
1718 } else if let Some(uri) = self.uri() {
1719 formatter.write_str(uri)
1720 } else if let Some(ipaddress) = self.ipaddress() {
1721 let result = String::from_utf8_lossy(ipaddress);
1722 formatter.write_str(&result)
1723 } else {
1724 formatter.write_str("(empty)")
1725 }
1726 }
1727}
1728
1729impl Stackable for GeneralName {
1730 type StackType = ffi::stack_st_GENERAL_NAME;
1731}
1732
1733foreign_type_and_impl_send_sync! {
1734 type CType = ffi::X509_ALGOR;
1735 fn drop = ffi::X509_ALGOR_free;
1736
1737 pub struct X509Algorithm;
1739}
1740
1741impl X509AlgorithmRef {
1742 #[must_use]
1744 pub fn object(&self) -> &Asn1ObjectRef {
1745 unsafe {
1746 let mut oid = ptr::null();
1747 X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
1748 assert!(!oid.is_null());
1749 Asn1ObjectRef::from_ptr(oid.cast_mut())
1750 }
1751 }
1752}
1753
1754foreign_type_and_impl_send_sync! {
1755 type CType = ffi::X509_OBJECT;
1756 fn drop = X509_OBJECT_free;
1757
1758 pub struct X509Object;
1760}
1761
1762impl X509ObjectRef {
1763 #[must_use]
1764 pub fn x509(&self) -> Option<&X509Ref> {
1765 unsafe {
1766 let ptr = X509_OBJECT_get0_X509(self.as_ptr());
1767 if ptr.is_null() {
1768 None
1769 } else {
1770 Some(X509Ref::from_ptr(ptr))
1771 }
1772 }
1773 }
1774}
1775
1776impl Stackable for X509Object {
1777 type StackType = ffi::stack_st_X509_OBJECT;
1778}
1779
1780use crate::ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
1781
1782use crate::ffi::{
1783 X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version, X509_STORE_CTX_get0_chain,
1784 X509_set1_notAfter, X509_set1_notBefore,
1785};
1786
1787use crate::ffi::X509_OBJECT_get0_X509;
1788
1789#[allow(bad_style)]
1790unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) {
1791 unsafe {
1792 ffi::X509_OBJECT_free_contents(x);
1793 ffi::OPENSSL_free(x.cast());
1794 }
1795}
1796
1797unsafe fn get_new_x509_store_ctx_idx(f: ffi::CRYPTO_EX_free) -> c_int {
1798 unsafe { ffi::X509_STORE_CTX_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f) }
1799}