1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::extensions::*;
use crate::validate::*;
use std::collections::HashSet;
const WARN_SHOULD_BE_CRITICAL: bool = false;
macro_rules! test_critical {
(MUST $ext:ident, $l:ident, $name:expr) => {
if !$ext.critical {
$l.err(&format!("Extension {} MUST be critical, but is not", $name));
}
};
(MUST NOT $ext:ident, $l:ident, $name:expr) => {
if $ext.critical {
$l.err(&format!("Extension {} MUST NOT be critical, but is", $name));
}
};
(SHOULD $ext:ident, $l:ident, $name:expr) => {
if WARN_SHOULD_BE_CRITICAL && !$ext.critical {
$l.warn(&format!(
"Extension {} SHOULD be critical, but is not",
$name
));
}
};
(SHOULD NOT $ext:ident, $l:ident, $name:expr) => {
if WARN_SHOULD_BE_CRITICAL && $ext.critical {
$l.warn(&format!(
"Extension {} SHOULD NOT be critical, but is",
$name
));
}
};
}
#[derive(Debug)]
pub struct X509ExtensionsValidator;
impl<'a> Validator<'a> for X509ExtensionsValidator {
type Item = &'a [X509Extension<'a>];
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let mut res = true;
{
let mut m = HashSet::new();
for ext in item.iter() {
if m.contains(&ext.oid) {
l.err(&format!("Duplicate extension {}", ext.oid));
res = false;
} else {
m.insert(ext.oid.clone());
}
}
}
for ext in item.iter() {
match ext.parsed_extension() {
ParsedExtension::AuthorityKeyIdentifier(aki) => {
test_critical!(MUST NOT ext, l, "AKI");
if aki.authority_cert_issuer.is_some() ^ aki.authority_cert_serial.is_some() {
l.warn("AKI: only one of Issuer and Serial is present");
}
}
ParsedExtension::CertificatePolicies(policies) => {
let mut policy_oids = HashSet::new();
for policy_info in policies {
if policy_oids.contains(&policy_info.policy_id) {
l.err(&format!(
"Certificate Policies: duplicate policy {}",
policy_info.policy_id
));
res = false;
} else {
policy_oids.insert(policy_info.policy_id.clone());
}
}
}
ParsedExtension::KeyUsage(ku) => {
test_critical!(SHOULD ext, l, "KeyUsage");
if ku.flags == 0 {
l.err("KeyUsage: all flags are set to 0");
}
}
ParsedExtension::SubjectAlternativeName(san) => {
test_critical!(SHOULD NOT ext, l, "SubjectAltName");
for name in &san.general_names {
match name {
GeneralName::DNSName(ref s) | GeneralName::RFC822Name(ref s) => {
if !s.as_bytes().iter().all(u8::is_ascii) {
l.warn(&format!("Invalid charset in 'SAN' entry '{}'", s));
}
}
_ => (),
}
}
}
_ => (),
}
}
res
}
}