From c2d4841e6ab6f4ae48104985b42c45b82814e275 Mon Sep 17 00:00:00 2001 From: Ross Williams Date: Tue, 13 Sep 2022 10:45:13 -0400 Subject: [PATCH] Add Display implementation to X509NameRef Internally, call `X509_NAME_print_ex(3)` with RFC2253 flags and disable UTF-8 escaping so the result is a UTF-8-encoded string. Signed-off-by: Ross Williams --- openssl-sys/src/handwritten/x509.rs | 6 ++++++ openssl-sys/src/x509.rs | 23 +++++++++++++++++++++++ openssl/src/x509/mod.rs | 21 ++++++++++++++++++++- openssl/src/x509/tests.rs | 28 ++++++++++++++++++++++++++++ openssl/test/emoji_cert.pem | 22 ++++++++++++++++++++++ 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 openssl/test/emoji_cert.pem diff --git a/openssl-sys/src/handwritten/x509.rs b/openssl-sys/src/handwritten/x509.rs index 0bb682764c..56d08f1b8d 100644 --- a/openssl-sys/src/handwritten/x509.rs +++ b/openssl-sys/src/handwritten/x509.rs @@ -489,6 +489,12 @@ const_ptr_api! { pub fn X509_NAME_entry_count(n: #[const_ptr_if(any(ossl110, libressl280))] X509_NAME) -> c_int; pub fn X509_NAME_get_index_by_NID(n: #[const_ptr_if(any(ossl300, libressl280))] X509_NAME, nid: c_int, last_pos: c_int) -> c_int; pub fn X509_NAME_get_entry(n: #[const_ptr_if(any(ossl110, libressl280))] X509_NAME, loc: c_int) -> *mut X509_NAME_ENTRY; + pub fn X509_NAME_print_ex( + out: *mut BIO, + n: #[const_ptr_if(any(ossl110, libressl280))] X509_NAME, + indent: c_int, + flags: c_ulong, + ) -> c_int; pub fn X509_NAME_add_entry_by_NID( x: *mut X509_NAME, field: c_int, diff --git a/openssl-sys/src/x509.rs b/openssl-sys/src/x509.rs index 714b06c9bc..d0698520da 100644 --- a/openssl-sys/src/x509.rs +++ b/openssl-sys/src/x509.rs @@ -4,8 +4,31 @@ pub const X509_FILETYPE_PEM: c_int = 1; pub const X509_FILETYPE_ASN1: c_int = 2; pub const X509_FILETYPE_DEFAULT: c_int = 3; +pub const XN_FLAG_SEP_COMMA_PLUS: c_ulong = 1 << 16; +pub const XN_FLAG_DN_REV: c_ulong = 1 << 20; +pub const XN_FLAG_FN_SN: c_ulong = 0; +pub const XN_FLAG_DUMP_UNKNOWN_FIELDS: c_ulong = 1 << 24; +pub const XN_FLAG_RFC2253: c_ulong = ASN1_STRFLGS_RFC2253 + | XN_FLAG_SEP_COMMA_PLUS + | XN_FLAG_DN_REV + | XN_FLAG_FN_SN + | XN_FLAG_DUMP_UNKNOWN_FIELDS; + pub const ASN1_R_HEADER_TOO_LONG: c_int = 123; +pub const ASN1_STRFLGS_ESC_2253: c_ulong = 1; +pub const ASN1_STRFLGS_ESC_CTRL: c_ulong = 2; +pub const ASN1_STRFLGS_ESC_MSB: c_ulong = 4; +pub const ASN1_STRFLGS_UTF8_CONVERT: c_ulong = 0x10; +pub const ASN1_STRFLGS_DUMP_UNKNOWN: c_ulong = 0x100; +pub const ASN1_STRFLGS_DUMP_DER: c_ulong = 0x200; +pub const ASN1_STRFLGS_RFC2253: c_ulong = ASN1_STRFLGS_ESC_2253 + | ASN1_STRFLGS_ESC_CTRL + | ASN1_STRFLGS_ESC_MSB + | ASN1_STRFLGS_UTF8_CONVERT + | ASN1_STRFLGS_DUMP_UNKNOWN + | ASN1_STRFLGS_DUMP_DER; + cfg_if! { if #[cfg(not(any(ossl110, libressl350)))] { pub const X509_LU_FAIL: c_int = 0; diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index a64524cbea..0c0b2681f4 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -9,7 +9,7 @@ use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; -use libc::{c_int, c_long, c_uint, c_void}; +use libc::{c_int, c_long, c_uint, c_ulong, c_void}; use std::cmp::{self, Ordering}; use std::convert::{TryFrom, TryInto}; use std::error::Error; @@ -1288,6 +1288,25 @@ impl fmt::Debug for X509NameRef { } } +impl fmt::Display for X509NameRef { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + const INDENT: i32 = 0; + const FLAGS: c_ulong = ffi::XN_FLAG_RFC2253 & !ffi::ASN1_STRFLGS_ESC_MSB; + + unsafe { + let bio = crate::bio::MemBio::new()?; + cvt_n(ffi::X509_NAME_print_ex( + bio.as_ptr(), + self.as_ptr(), + INDENT, + FLAGS, + )).or(Err(fmt::Error))?; + let value = core::str::from_utf8_unchecked(bio.get_buf()); + formatter.write_str(value) + } + } +} + /// A type to destructure and examine an `X509Name`. pub struct X509NameEntries<'a> { name: &'a X509NameRef, diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 25c2da0125..8445c989e7 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -1192,3 +1192,31 @@ fn test_store_all_certificates() { assert_eq!(store.all_certificates().len(), 1); } + +#[test] +fn test_name_display() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + const EXPECTED_TO_STRING_OUTPUT: &str = + "CN=foobar.com,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU"; + + let rdn = cert.subject_name().to_string(); + assert_eq!( + rdn, + EXPECTED_TO_STRING_OUTPUT, + "output did not match expected", + ); + + let emoji_cert = include_bytes!("../../test/emoji_cert.pem"); + let emoji_cert = X509::from_pem(emoji_cert).unwrap(); + + const EXPECTED_EMOJI_OUTPUT: &str = + "CN=www.example.com,O=\u{1f980}\u{1f9ea}\u{1f44d},ST=YY,C=XX"; + + let rdn = emoji_cert.subject_name().to_string(); + assert_eq!( + rdn, EXPECTED_EMOJI_OUTPUT, + "formatting of cert name with Unicode Emoji failed" + ); +} diff --git a/openssl/test/emoji_cert.pem b/openssl/test/emoji_cert.pem new file mode 100644 index 0000000000..4b6475fe3e --- /dev/null +++ b/openssl/test/emoji_cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIUWZnfBdTaxSrLilsn6tHRTyZU6PgwDQYJKoZIhvcNAQEL +BQAwSzELMAkGA1UEBhMCWFgxCzAJBgNVBAgMAllZMR0wGwYDVQQKDBRTb21lIENB +IG9yZ2FuaXphdGlvbjEQMA4GA1UEAwwHY2EgdGVzdDAeFw0yMjA5MTMxNDI0MTZa +Fw0yMzA5MTMxNDI0MTZaMEsxCzAJBgNVBAYTAlhYMQswCQYDVQQIDAJZWTEVMBMG +A1UECgwM8J+mgPCfp6rwn5GNMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCujLOIv3hsSToo4GXYDhiJ7zzV +dKJhDp3ME5YJWyQs26tFztAUL8B6Ns/yy1ZfdTrRaPkMLilVcweFXmDP/cfy/sZZ +u/N912WPOo0xWZz/5frZXhOPcXUkYPIZ0A7DsCy2FzSTvgaPl2wqPtvPi/EEzrrR +Q46wdaq98O4HrfBUA6I/wCzpaos43dIsJCS+pnwfnlQBSIBXGEFa84j1MbVbQICK +O20GEVymNLpo5PubutX7o2nJKj/WSYtqUt31xs6vTH+uk/sgkrZviWM5Jm2QWirn +rDdB0JSS8w7l7qyqfeHFViDZRuJawwS+8qRKxYfT/tU3ebP8zGW/wCJa8vEfAgMB +AAGjgYMwgYAwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0OBBYEFG9r +824kB/vwBdMAx/RhYFsiIcvTMB8GA1UdIwQYMBaAFKU8v/PGu848xpjGXj9j6wx0 +Xw4CMCMGA1UdEQQcMBqCDSouZXhhbXBsZS5jb22CCWhlbGxvLmNvbTANBgkqhkiG +9w0BAQsFAAOCAQEATZriAaxJX36BpQv5em2oFH0XEJ1B6gCNkRtlMD8XHFOAt0HN +1vHIJ4Yx93UQbbV3h+teUj3B1ksAJByWFqcpMSQov0/v6pwYjUmeWcEDQKrhv7kS +h5GaHDAJezJnPMx4kxrM12r5yWw9lkf5J/TGA9zAzud+/0A8+K3AQLMSYljLynvY +EkK4cLHrQ6LRhwLq4jtj6qATwN1k0iQmwhMPrfOo9s+9CxvZNuf7a/OdWGU0qrt3 +YNHMJsioM4Jvt+RvU8ccjXMviZbDe/2pIPv4eU309q1cSYf7pomK2vduk6vuTeAz +V1hhUFbr+gU00TVnzGIwpEMkoQEPlvYEXz7uVw== +-----END CERTIFICATE-----