From e17420a3e1b855c1f3968d2100d3e821f307e0e0 Mon Sep 17 00:00:00 2001 From: Felix Huettner Date: Mon, 1 May 2023 21:14:10 +0200 Subject: [PATCH] add other name support the issue with other name SANs is that they can contain arbitary data. As we can no longer use the old method for other_name for security reasons we now add `other_name2` as an alternative. --- openssl-sys/src/handwritten/asn1.rs | 9 +++++++ openssl-sys/src/handwritten/x509v3.rs | 5 ++++ openssl/src/asn1.rs | 6 +++++ openssl/src/x509/extension.rs | 38 ++++++++++++++++++++++++--- openssl/src/x509/mod.rs | 22 ++++++++++++++++ 5 files changed, 77 insertions(+), 3 deletions(-) diff --git a/openssl-sys/src/handwritten/asn1.rs b/openssl-sys/src/handwritten/asn1.rs index fa43a7a5c1..99c27ffaa2 100644 --- a/openssl-sys/src/handwritten/asn1.rs +++ b/openssl-sys/src/handwritten/asn1.rs @@ -10,6 +10,7 @@ pub struct ASN1_ENCODING { extern "C" { pub fn ASN1_OBJECT_free(x: *mut ASN1_OBJECT); + pub fn OBJ_dup(x: *mut ASN1_OBJECT) -> *mut ASN1_OBJECT; } stack!(stack_st_ASN1_OBJECT); @@ -94,7 +95,15 @@ extern "C" { #[cfg(ossl110)] pub fn ASN1_ENUMERATED_get_int64(pr: *mut i64, a: *const ASN1_ENUMERATED) -> c_int; + pub fn ASN1_TYPE_new() -> *mut ASN1_TYPE; + pub fn ASN1_TYPE_set(a: *mut ASN1_TYPE, type_: c_int, value: *mut c_void); pub fn ASN1_TYPE_free(x: *mut ASN1_TYPE); + pub fn d2i_ASN1_TYPE( + k: *mut *mut ASN1_TYPE, + buf: *mut *const u8, + len: c_long, + ) -> *mut ASN1_TYPE; + pub fn i2d_ASN1_TYPE(a: *mut ASN1_TYPE, pp: *mut *mut c_uchar) -> c_int; } const_ptr_api! { diff --git a/openssl-sys/src/handwritten/x509v3.rs b/openssl-sys/src/handwritten/x509v3.rs index 7789b629a6..5a1abaf2e4 100644 --- a/openssl-sys/src/handwritten/x509v3.rs +++ b/openssl-sys/src/handwritten/x509v3.rs @@ -6,6 +6,11 @@ pub enum CONF_METHOD {} extern "C" { pub fn GENERAL_NAME_new() -> *mut GENERAL_NAME; pub fn GENERAL_NAME_free(name: *mut GENERAL_NAME); + pub fn GENERAL_NAME_set0_othername( + gen: *mut GENERAL_NAME, + oid: *mut ASN1_OBJECT, + value: *mut ASN1_TYPE, + ) -> c_int; } #[repr(C)] diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs index d75e05166e..fbb4f96eea 100644 --- a/openssl/src/asn1.rs +++ b/openssl/src/asn1.rs @@ -705,6 +705,12 @@ impl Asn1Object { } } +impl Clone for Asn1Object { + fn clone(&self) -> Self { + unsafe { Asn1Object::from_ptr(cvt_p(ffi::OBJ_dup(self.as_ptr())).unwrap()) } + } +} + impl Asn1ObjectRef { /// Returns the NID associated with this OID. pub fn nid(&self) -> Nid { diff --git a/openssl/src/x509/extension.rs b/openssl/src/x509/extension.rs index 075227dec3..a0fca2cce1 100644 --- a/openssl/src/x509/extension.rs +++ b/openssl/src/x509/extension.rs @@ -16,12 +16,16 @@ //! //! let extension: X509Extension = bc.build().unwrap(); //! ``` +use std::convert::TryInto; use std::fmt::Write; +use std::ptr; use crate::asn1::Asn1Object; +use crate::cvt_p; use crate::error::ErrorStack; use crate::nid::Nid; use crate::x509::{GeneralName, Stack, X509Extension, X509v3Context}; +use ffi::ASN1_TYPE; use foreign_types::ForeignType; /// An extension which indicates whether a certificate is a CA certificate. @@ -434,6 +438,7 @@ enum RustGeneralName { Uri(String), Ip(String), Rid(String), + OtherName(Asn1Object, *mut ASN1_TYPE), } /// An extension that allows additional identities to be bound to the subject @@ -506,14 +511,38 @@ impl SubjectAlternativeName { /// Sets the `otherName` flag. /// - /// Not currently actually supported, always panics. - #[deprecated = "other_name is deprecated and always panics. Please file a bug if you have a use case for this."] + /// Not currently actually supported, always panics. Please use other_name2 or other_name_ia5string + #[deprecated = "other_name is deprecated and always panics. Please use other_name2 or other_name_ia5string."] pub fn other_name(&mut self, _other_name: &str) -> &mut SubjectAlternativeName { unimplemented!( - "This has not yet been adapted for the new internals. File a bug if you need this." + "This has not yet been adapted for the new internals. Use other_name2 or other_name_ia5string." ); } + /// Sets the `otherName` flag. + /// + /// `content` must be a valid der encoded ASN1_TYPE + /// + /// If you want to add just a ia5string use `other_name_ia5string` + pub fn other_name2( + &mut self, + oid: Asn1Object, + content: &[u8], + ) -> Result<&mut SubjectAlternativeName, ErrorStack> { + let typ: *mut ASN1_TYPE; + unsafe { + ffi::init(); + + typ = cvt_p(ffi::d2i_ASN1_TYPE( + ptr::null_mut(), + &mut content.as_ptr().cast(), + content.len().try_into().unwrap(), + ))?; + } + self.items.push(RustGeneralName::OtherName(oid, typ)); + Ok(self) + } + /// Return a `SubjectAlternativeName` extension as an `X509Extension`. pub fn build(&self, _ctx: &X509v3Context<'_>) -> Result { let mut stack = Stack::new()?; @@ -526,6 +555,9 @@ impl SubjectAlternativeName { GeneralName::new_ip(s.parse().map_err(|_| ErrorStack::get())?)? } RustGeneralName::Rid(s) => GeneralName::new_rid(Asn1Object::from_str(s)?)?, + RustGeneralName::OtherName(oid, content) => { + GeneralName::new_other_name(oid.clone(), *content)? + } }; stack.push(gn)?; } diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index a8e298bf3f..00bde06103 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -8,6 +8,7 @@ //! the secure protocol for browsing the web. use cfg_if::cfg_if; +use ffi::ASN1_TYPE; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; use libc::{c_int, c_long, c_uint, c_void}; use std::cmp::{self, Ordering}; @@ -2046,6 +2047,27 @@ impl GeneralName { Ok(GeneralName::from_ptr(gn)) } } + + pub(crate) fn new_other_name( + oid: Asn1Object, + value: *mut ASN1_TYPE, + ) -> Result { + unsafe { + ffi::init(); + let gn = cvt_p(ffi::GENERAL_NAME_new())?; + (*gn).type_ = ffi::GEN_OTHERNAME; + + cvt(ffi::GENERAL_NAME_set0_othername( + gn, + oid.as_ptr().cast(), + value, + ))?; + + mem::forget(oid); + + Ok(GeneralName::from_ptr(gn)) + } + } } impl GeneralNameRef {