FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Rewrite constraint matching to avoid permissive catch-all branch · rustls/webpki@1219622 · GitHub

/ webpki Public

Commit 1219622

Browse files
authored andcommitted
Rewrite constraint matching to avoid permissive catch-all branch
1 parent 57bc62c commit 1219622

3 files changed

Lines changed: 105 additions & 6 deletions

File tree

‎src/subject_name/mod.rs‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@ fn check_presented_id_conforms_to_constraints(
126126
Err(err) => return Some(Err(err)),
127127
};
128128

129+
// Avoid having a catch-all branch here which might fail open on new variants
129130
let matches = match (name, base) {
130131
(GeneralName::DnsName(name), GeneralName::DnsName(base)) => {
131132
dns_name::presented_id_matches_reference_id(name, IdRole::NameConstraint, base)
132133
}
134+
(GeneralName::DnsName(_), _) => continue,
133135

134136
(GeneralName::DirectoryName, GeneralName::DirectoryName) => Ok(
135137
// Reject any uses of directory name constraints; we don't implement this.
@@ -150,10 +152,19 @@ fn check_presented_id_conforms_to_constraints(
150152
Subtrees::ExcludedSubtrees => true,
151153
},
152154
),
155+
(GeneralName::DirectoryName, _) => continue,
153156

154157
(GeneralName::IpAddress(name), GeneralName::IpAddress(base)) => {
155158
ip_address::presented_id_matches_constraint(name, base)
156159
}
160+
(GeneralName::IpAddress(_), _) => continue,
161+
162+
// We currently don't support URI constraints -- fail closed for now.
163+
(
164+
GeneralName::UniformResourceIdentifier(_),
165+
GeneralName::UniformResourceIdentifier(_),
166+
) => Ok(false),
167+
(GeneralName::UniformResourceIdentifier(_), _) => continue,
157168

158169
// RFC 4280 says "If a name constraints extension that is marked as
159170
// critical imposes constraints on a particular name form, and an
@@ -168,12 +179,7 @@ fn check_presented_id_conforms_to_constraints(
168179
{
169180
Err(Error::NameConstraintViolation)
170181
}
171-
172-
_ => {
173-
// mismatch between constraint and name types; continue with current
174-
// name and next constraint
175-
continue;
176-
}
182+
(GeneralName::Unsupported(_), _) => continue,
177183
};
178184

179185
match (subtrees, matches) {

‎tests/common/mod.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(dead_code, unreachable_pub)]
2+
13
use std::error::Error as StdError;
24

35
use rcgen::{

‎tests/tls_server_certs.rs‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,60 @@
1616
use core::time::Duration;
1717

1818
use pki_types::{CertificateDer, ServerName, UnixTime};
19+
use rcgen::{
20+
Certificate, CertificateParams, CertifiedIssuer, CustomExtension, DnType, IsCa, KeyPair,
21+
SanType, date_time_ymd,
22+
};
1923
use webpki::{InvalidNameContext, KeyUsage, anchor_from_trusted_cert};
2024

25+
mod common;
26+
use common::issuer_params;
27+
28+
/// Since we don't have real constraint matching implemented for URI names, fail closed.
29+
#[test]
30+
fn uri_san_rejected_against_uri_permitted_subtree() {
31+
let ca_key = KeyPair::generate().unwrap();
32+
let mut ca_params = issuer_params("issuer.example.com").unwrap();
33+
ca_params
34+
.custom_extensions
35+
.push(uri_permitted_name_constraints(
36+
b"https://allowed.example.com",
37+
));
38+
let issuer = CertifiedIssuer::self_signed(ca_params, ca_key).expect("failed to generate CA");
39+
40+
let ee = generate_cert(
41+
vec![SanType::URI("https://evil.example.com".try_into().unwrap())],
42+
&issuer,
43+
);
44+
assert_eq!(
45+
check_cert(ee.der(), issuer.der(), &[], &[], &[]),
46+
Err(webpki::Error::NameConstraintViolation),
47+
);
48+
}
49+
50+
// Hand-encode a NameConstraints extension (OID 2.5.29.30) with a single
51+
// permittedSubtree containing a URI GeneralName. rcgen's GeneralSubtree enum
52+
// doesn't expose a URI variant, so we emit the DER directly.
53+
fn uri_permitted_name_constraints(uri: &[u8]) -> CustomExtension {
54+
assert!(uri.len() < 128);
55+
// URI GeneralName: [6] IMPLICIT IA5String
56+
let mut uri_gn = vec![0x86, uri.len() as u8];
57+
uri_gn.extend_from_slice(uri);
58+
// GeneralSubtree SEQUENCE { base GeneralName, ... }
59+
let mut subtree = vec![0x30, uri_gn.len() as u8];
60+
subtree.extend_from_slice(&uri_gn);
61+
// permittedSubtrees [0] IMPLICIT GeneralSubtrees
62+
let mut permitted = vec![0xa0, subtree.len() as u8];
63+
permitted.extend_from_slice(&subtree);
64+
// NameConstraints SEQUENCE
65+
let mut nc = vec![0x30, permitted.len() as u8];
66+
nc.extend_from_slice(&permitted);
67+
68+
let mut ext = CustomExtension::from_oid_content(&[2, 5, 29, 30], nc);
69+
ext.set_criticality(true);
70+
ext
71+
}
72+
2173
#[track_caller]
2274
fn check_cert(
2375
ee: &[u8],
@@ -61,6 +113,45 @@ fn check_cert(
61113
Ok(())
62114
}
63115

116+
fn generate_cert(sans: Vec<SanType>, issuer: &CertifiedIssuer<'_, KeyPair>) -> Certificate {
117+
generate_cert_with_names(None, None, sans, issuer)
118+
}
119+
120+
fn generate_cert_with_names(
121+
subject_cn: Option<&str>,
122+
subject_email: Option<&str>,
123+
sans: Vec<SanType>,
124+
issuer: &CertifiedIssuer<'_, KeyPair>,
125+
) -> Certificate {
126+
let (not_before, not_after) = (date_time_ymd(1970, 1, 1), date_time_ymd(2050, 1, 1));
127+
128+
// Generate end entity certificate
129+
let ee_key = KeyPair::generate().unwrap();
130+
let mut ee_params = CertificateParams::new([]).expect("failed to create EE params");
131+
ee_params.subject_alt_names = sans;
132+
if let Some(cn) = subject_cn {
133+
ee_params.distinguished_name.push(DnType::CommonName, cn);
134+
}
135+
if let Some(email) = subject_email {
136+
ee_params
137+
.distinguished_name
138+
.push(DnType::from_oid(OID_EMAIL_ADDRESS), email);
139+
}
140+
ee_params
141+
.distinguished_name
142+
.push(DnType::OrganizationName, "test");
143+
ee_params.is_ca = IsCa::ExplicitNoCa;
144+
ee_params.not_before = not_before;
145+
ee_params.not_after = not_after;
146+
147+
ee_params
148+
.signed_by(&ee_key, issuer)
149+
.expect("failed to generate EE cert")
150+
}
151+
152+
// OID for emailAddress in subject DN (pkcs9-emailAddress)
153+
const OID_EMAIL_ADDRESS: &[u64] = &[1, 2, 840, 113549, 1, 9, 1];
154+
64155
// DO NOT EDIT BELOW: generated by tests/generate.py
65156

66157
#[test]

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL