Skip to content

Commit 5c2a215

Browse files
s-arashArash Sahebolamri
andauthored
Add x509 verify methods that accept expected_common_name (#332)
Co-authored-by: Arash Sahebolamri <[email protected]>
1 parent 5b54663 commit 5c2a215

File tree

2 files changed

+130
-4
lines changed

2 files changed

+130
-4
lines changed

mbedtls/src/alloc.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* option. This file may not be copied, modified, or distributed except
77
* according to those terms. */
88

9+
use core::ffi::{c_char, CStr};
910
use core::fmt;
1011
use core::mem::ManuallyDrop;
1112
use core::ops::{Deref, DerefMut};
@@ -71,3 +72,43 @@ unsafe impl<T: Sync> Sync for Box<T> {}
7172
pub struct List<T> {
7273
pub(crate) inner: Option<Box<T>>,
7374
}
75+
76+
/// Modeled after std's [`CString`](https://doc.rust-lang.org/std/ffi/struct.CString.html)
77+
pub struct CString {
78+
/// Pointer to the allocated buffer
79+
inner: NonNull<u8>,
80+
}
81+
82+
impl CString {
83+
pub fn new(str: &str) -> Self {
84+
unsafe {
85+
let buff = crate::alloc::mbedtls_calloc(1, str.len() + 1) as *mut u8;
86+
buff.copy_from(str.as_ptr(), str.len());
87+
*buff.add(str.len()) = 0;
88+
Self {
89+
inner: NonNull::new(buff).unwrap(),
90+
}
91+
}
92+
}
93+
}
94+
95+
impl Drop for CString {
96+
fn drop(&mut self) {
97+
unsafe { crate::alloc::mbedtls_free(self.inner.as_ptr() as *mut c_void) }
98+
}
99+
}
100+
101+
impl Deref for CString {
102+
type Target = CStr;
103+
104+
fn deref(&self) -> &Self::Target {
105+
unsafe { CStr::from_ptr(self.inner.as_ptr() as *const c_char) }
106+
}
107+
}
108+
109+
#[test]
110+
fn test_c_string() {
111+
let str = "spooky code here!";
112+
let c_str = CString::new(str);
113+
assert_eq!(str, c_str.to_str().unwrap())
114+
}

mbedtls/src/x509/certificate.rs

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use core::ptr::NonNull;
1313
use mbedtls_sys::types::raw_types::{c_char, c_void};
1414
use mbedtls_sys::*;
1515

16-
use crate::alloc::{mbedtls_calloc, Box as MbedtlsBox, List as MbedtlsList};
16+
use crate::alloc::{mbedtls_calloc, Box as MbedtlsBox, CString, List as MbedtlsList};
1717
#[cfg(not(feature = "std"))]
1818
use crate::alloc_prelude::*;
1919
use crate::error::{Error, IntoResult, Result};
@@ -224,6 +224,7 @@ impl Certificate {
224224
ca_crl: Option<&mut Crl>,
225225
err_info: Option<&mut String>,
226226
cb: Option<F>,
227+
expected_common_name: Option<&str>,
227228
) -> Result<()>
228229
where
229230
F: VerifyCallback + 'static,
@@ -236,20 +237,25 @@ impl Certificate {
236237
} else {
237238
(None, ::core::ptr::null_mut())
238239
};
240+
241+
let cn = expected_common_name.map(|cn| CString::new(cn));
239242
let mut flags = 0;
240243
let result = unsafe {
241244
x509_crt_verify(
242245
chain.inner_ffi_mut(),
243246
trust_ca.inner_ffi_mut(),
244247
ca_crl.map_or(::core::ptr::null_mut(), |crl| crl.handle_mut()),
245-
::core::ptr::null(),
248+
cn.as_ref().map_or(::core::ptr::null(), |cn| cn.as_ptr()),
246249
&mut flags,
247250
f_vrfy,
248251
p_vrfy,
249252
)
250253
}
251254
.into_result();
252255

256+
// Asserts cn is still alive here. Prevents bugs (e.g., forgetting to insert `.as_ref()` when using cn)
257+
drop(cn);
258+
253259
if result.is_err() {
254260
if let Some(err_info) = err_info {
255261
let verify_info = crate::private::alloc_string_repeat(|buf, size| unsafe {
@@ -270,7 +276,32 @@ impl Certificate {
270276
ca_crl: Option<&mut Crl>,
271277
err_info: Option<&mut String>,
272278
) -> Result<()> {
273-
Self::verify_ex(chain, trust_ca, ca_crl, err_info, None::<&dyn VerifyCallback>)
279+
Self::verify_ex(chain, trust_ca, ca_crl, err_info, None::<&dyn VerifyCallback>, None)
280+
}
281+
282+
/// Like `verify`, optionally accepts an `expected_common_name` arg.
283+
///
284+
/// * `expected_common_name`
285+
/// (From mbedtls documentation) The expected Common Name. This will be checked to be present in the certificate’s
286+
/// subjectAltNames extension or, if this extension is absent, as a CN component in its Subject name. DNS names
287+
/// and IP addresses are fully supported, while the URI subtype is partially supported: only exact matching,
288+
/// without any normalization procedures described in 7.4 of RFC5280, will result in a positive URI verification.
289+
/// This may be `None` if the CN need not be verified.
290+
pub fn verify_with_expected_common_name(
291+
chain: &MbedtlsList<Certificate>,
292+
trust_ca: &MbedtlsList<Certificate>,
293+
ca_crl: Option<&mut Crl>,
294+
err_info: Option<&mut String>,
295+
expected_common_name: Option<&str>,
296+
) -> Result<()> {
297+
Self::verify_ex(
298+
chain,
299+
trust_ca,
300+
ca_crl,
301+
err_info,
302+
None::<&dyn VerifyCallback>,
303+
expected_common_name,
304+
)
274305
}
275306

276307
pub fn verify_with_callback<F>(
@@ -283,7 +314,29 @@ impl Certificate {
283314
where
284315
F: VerifyCallback + 'static,
285316
{
286-
Self::verify_ex(chain, trust_ca, ca_crl, err_info, Some(cb))
317+
Self::verify_ex(chain, trust_ca, ca_crl, err_info, Some(cb), None)
318+
}
319+
320+
/// Like `verify_with_callback`, optionally accepts an `expected_common_name` arg.
321+
///
322+
/// * `expected_common_name`
323+
/// (From mbedtls documentation) The expected Common Name. This will be checked to be present in the certificate’s
324+
/// subjectAltNames extension or, if this extension is absent, as a CN component in its Subject name. DNS names
325+
/// and IP addresses are fully supported, while the URI subtype is partially supported: only exact matching,
326+
/// without any normalization procedures described in 7.4 of RFC5280, will result in a positive URI verification.
327+
/// This may be `None` if the CN need not be verified.
328+
pub fn verify_with_callback_expected_common_name<F>(
329+
chain: &MbedtlsList<Certificate>,
330+
trust_ca: &MbedtlsList<Certificate>,
331+
ca_crl: Option<&mut Crl>,
332+
err_info: Option<&mut String>,
333+
cb: F,
334+
expected_common_name: Option<&str>,
335+
) -> Result<()>
336+
where
337+
F: VerifyCallback + 'static,
338+
{
339+
Self::verify_ex(chain, trust_ca, ca_crl, err_info, Some(cb), expected_common_name)
287340
}
288341
}
289342

@@ -1489,4 +1542,36 @@ cYp0bH/RcPTC0Z+ZaqSWMtfxRrk63MJQF9EXpDCdvQRcTMD9D85DJrMKn8aumq0M
14891542
);
14901543
assert_eq!(err, "The certificate has been revoked (is on a CRL)\n");
14911544
}
1545+
1546+
#[test]
1547+
fn expected_common_name_test() {
1548+
const C_CERT: &'static str = concat!(include_str!("../../tests/data/certificate.crt"), "\0");
1549+
const C_ROOT: &'static str = concat!(include_str!("../../tests/data/root.crt"), "\0");
1550+
1551+
let mut certs = MbedtlsList::new();
1552+
certs.push(Certificate::from_pem(&C_CERT.as_bytes()).unwrap());
1553+
let mut roots = MbedtlsList::new();
1554+
roots.push(Certificate::from_pem(&C_ROOT.as_bytes()).unwrap());
1555+
1556+
let mut err = String::new();
1557+
assert!(
1558+
Certificate::verify_with_expected_common_name(&certs, &roots, None, Some(&mut err), Some("example.com")).is_ok(),
1559+
);
1560+
}
1561+
1562+
#[test]
1563+
fn expected_common_name_wrong_name_test() {
1564+
const C_CERT: &'static str = concat!(include_str!("../../tests/data/certificate.crt"), "\0");
1565+
const C_ROOT: &'static str = concat!(include_str!("../../tests/data/root.crt"), "\0");
1566+
1567+
let mut certs = MbedtlsList::new();
1568+
certs.push(Certificate::from_pem(&C_CERT.as_bytes()).unwrap());
1569+
let mut roots = MbedtlsList::new();
1570+
roots.push(Certificate::from_pem(&C_ROOT.as_bytes()).unwrap());
1571+
1572+
let mut err = String::new();
1573+
assert!(
1574+
Certificate::verify_with_expected_common_name(&certs, &roots, None, Some(&mut err), Some("notit.com")).is_err()
1575+
);
1576+
}
14921577
}

0 commit comments

Comments
 (0)