/src/botan/src/lib/x509/name_constraint.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * X.509 Name Constraint |
3 | | * (C) 2015 Kai Michaelis |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/name_constraint.h> |
9 | | #include <botan/asn1_alt_name.h> |
10 | | #include <botan/ber_dec.h> |
11 | | #include <botan/loadstor.h> |
12 | | #include <botan/x509_dn.h> |
13 | | #include <botan/x509cert.h> |
14 | | #include <botan/parsing.h> |
15 | | #include <sstream> |
16 | | |
17 | | namespace Botan { |
18 | | |
19 | | class DER_Encoder; |
20 | | |
21 | | GeneralName::GeneralName(const std::string& str) : GeneralName() |
22 | 0 | { |
23 | 0 | size_t p = str.find(':'); |
24 | 0 |
|
25 | 0 | if(p != std::string::npos) |
26 | 0 | { |
27 | 0 | m_type = str.substr(0, p); |
28 | 0 | m_name = str.substr(p + 1, std::string::npos); |
29 | 0 | } |
30 | 0 | else |
31 | 0 | { |
32 | 0 | throw Invalid_Argument("Failed to decode Name Constraint"); |
33 | 0 | } |
34 | 0 | } |
35 | | |
36 | | void GeneralName::encode_into(DER_Encoder&) const |
37 | 0 | { |
38 | 0 | throw Not_Implemented("GeneralName encoding"); |
39 | 0 | } |
40 | | |
41 | | void GeneralName::decode_from(class BER_Decoder& ber) |
42 | 4.91k | { |
43 | 4.91k | BER_Object obj = ber.get_next_object(); |
44 | 4.91k | |
45 | 4.91k | if(obj.is_a(1, CONTEXT_SPECIFIC)) |
46 | 415 | { |
47 | 415 | m_type = "RFC822"; |
48 | 415 | m_name = ASN1::to_string(obj); |
49 | 415 | } |
50 | 4.49k | else if(obj.is_a(2, CONTEXT_SPECIFIC)) |
51 | 540 | { |
52 | 540 | m_type = "DNS"; |
53 | 540 | m_name = ASN1::to_string(obj); |
54 | 540 | } |
55 | 3.95k | else if(obj.is_a(6, CONTEXT_SPECIFIC)) |
56 | 2.83k | { |
57 | 2.83k | m_type = "URI"; |
58 | 2.83k | m_name = ASN1::to_string(obj); |
59 | 2.83k | } |
60 | 1.12k | else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))) |
61 | 478 | { |
62 | 478 | m_type = "DN"; |
63 | 478 | X509_DN dn; |
64 | 478 | BER_Decoder dec(obj); |
65 | 478 | std::stringstream ss; |
66 | 478 | |
67 | 478 | dn.decode_from(dec); |
68 | 478 | ss << dn; |
69 | 478 | |
70 | 478 | m_name = ss.str(); |
71 | 478 | } |
72 | 643 | else if(obj.is_a(7, CONTEXT_SPECIFIC)) |
73 | 187 | { |
74 | 187 | if(obj.length() == 8) |
75 | 114 | { |
76 | 114 | m_type = "IP"; |
77 | 114 | m_name = ipv4_to_string(load_be<uint32_t>(obj.bits(), 0)) + "/" + |
78 | 114 | ipv4_to_string(load_be<uint32_t>(obj.bits(), 1)); |
79 | 114 | } |
80 | 73 | else if(obj.length() == 32) |
81 | 5 | { |
82 | 5 | throw Decoding_Error("Unsupported IPv6 name constraint"); |
83 | 5 | } |
84 | 68 | else |
85 | 68 | { |
86 | 68 | throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length())); |
87 | 68 | } |
88 | 456 | } |
89 | 456 | else |
90 | 456 | { |
91 | 456 | throw Decoding_Error("Found unknown GeneralName type"); |
92 | 456 | } |
93 | 4.91k | } |
94 | | |
95 | | GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) const |
96 | 0 | { |
97 | 0 | std::vector<std::string> nam; |
98 | 0 | std::function<bool(const GeneralName*, const std::string&)> match_fn; |
99 | 0 |
|
100 | 0 | const X509_DN& dn = cert.subject_dn(); |
101 | 0 | const AlternativeName& alt_name = cert.subject_alt_name(); |
102 | 0 |
|
103 | 0 | if(type() == "DNS") |
104 | 0 | { |
105 | 0 | match_fn = std::mem_fn(&GeneralName::matches_dns); |
106 | 0 |
|
107 | 0 | nam = alt_name.get_attribute("DNS"); |
108 | 0 |
|
109 | 0 | if(nam.empty()) |
110 | 0 | { |
111 | 0 | nam = dn.get_attribute("CN"); |
112 | 0 | } |
113 | 0 | } |
114 | 0 | else if(type() == "DN") |
115 | 0 | { |
116 | 0 | match_fn = std::mem_fn(&GeneralName::matches_dn); |
117 | 0 |
|
118 | 0 | std::stringstream ss; |
119 | 0 | ss << dn; |
120 | 0 | nam.push_back(ss.str()); |
121 | 0 | } |
122 | 0 | else if(type() == "IP") |
123 | 0 | { |
124 | 0 | match_fn = std::mem_fn(&GeneralName::matches_ip); |
125 | 0 | nam = alt_name.get_attribute("IP"); |
126 | 0 | } |
127 | 0 | else |
128 | 0 | { |
129 | 0 | return MatchResult::UnknownType; |
130 | 0 | } |
131 | 0 | |
132 | 0 | if(nam.empty()) |
133 | 0 | { |
134 | 0 | return MatchResult::NotFound; |
135 | 0 | } |
136 | 0 | |
137 | 0 | bool some = false; |
138 | 0 | bool all = true; |
139 | 0 |
|
140 | 0 | for(const std::string& n: nam) |
141 | 0 | { |
142 | 0 | bool m = match_fn(this, n); |
143 | 0 |
|
144 | 0 | some |= m; |
145 | 0 | all &= m; |
146 | 0 | } |
147 | 0 |
|
148 | 0 | if(all) |
149 | 0 | { |
150 | 0 | return MatchResult::All; |
151 | 0 | } |
152 | 0 | else if(some) |
153 | 0 | { |
154 | 0 | return MatchResult::Some; |
155 | 0 | } |
156 | 0 | else |
157 | 0 | { |
158 | 0 | return MatchResult::None; |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | bool GeneralName::matches_dns(const std::string& nam) const |
163 | 0 | { |
164 | 0 | if(nam.size() == name().size()) |
165 | 0 | { |
166 | 0 | return nam == name(); |
167 | 0 | } |
168 | 0 | else if(name().size() > nam.size()) |
169 | 0 | { |
170 | 0 | return false; |
171 | 0 | } |
172 | 0 | else // name.size() < nam.size() |
173 | 0 | { |
174 | 0 | std::string constr = name().front() == '.' ? name() : "." + name(); |
175 | 0 | // constr is suffix of nam |
176 | 0 | return constr == nam.substr(nam.size() - constr.size(), constr.size()); |
177 | 0 | } |
178 | 0 | } |
179 | | |
180 | | bool GeneralName::matches_dn(const std::string& nam) const |
181 | 0 | { |
182 | 0 | std::stringstream ss(nam); |
183 | 0 | std::stringstream tt(name()); |
184 | 0 | X509_DN nam_dn, my_dn; |
185 | 0 |
|
186 | 0 | ss >> nam_dn; |
187 | 0 | tt >> my_dn; |
188 | 0 |
|
189 | 0 | auto attr = nam_dn.get_attributes(); |
190 | 0 | bool ret = true; |
191 | 0 | size_t trys = 0; |
192 | 0 |
|
193 | 0 | for(const auto& c: my_dn.dn_info()) |
194 | 0 | { |
195 | 0 | auto i = attr.equal_range(c.first); |
196 | 0 |
|
197 | 0 | if(i.first != i.second) |
198 | 0 | { |
199 | 0 | trys += 1; |
200 | 0 | ret = ret && (i.first->second == c.second.value()); |
201 | 0 | } |
202 | 0 | } |
203 | 0 |
|
204 | 0 | return trys > 0 && ret; |
205 | 0 | } |
206 | | |
207 | | bool GeneralName::matches_ip(const std::string& nam) const |
208 | 0 | { |
209 | 0 | uint32_t ip = string_to_ipv4(nam); |
210 | 0 | std::vector<std::string> p = split_on(name(), '/'); |
211 | 0 |
|
212 | 0 | if(p.size() != 2) |
213 | 0 | throw Decoding_Error("failed to parse IPv4 address"); |
214 | 0 | |
215 | 0 | uint32_t net = string_to_ipv4(p.at(0)); |
216 | 0 | uint32_t mask = string_to_ipv4(p.at(1)); |
217 | 0 |
|
218 | 0 | return (ip & mask) == net; |
219 | 0 | } |
220 | | |
221 | | std::ostream& operator<<(std::ostream& os, const GeneralName& gn) |
222 | 177 | { |
223 | 177 | os << gn.type() << ":" << gn.name(); |
224 | 177 | return os; |
225 | 177 | } |
226 | | |
227 | | GeneralSubtree::GeneralSubtree(const std::string& str) : GeneralSubtree() |
228 | 0 | { |
229 | 0 | size_t p0, p1; |
230 | 0 | size_t min = std::stoull(str, &p0, 10); |
231 | 0 | size_t max = std::stoull(str.substr(p0 + 1), &p1, 10); |
232 | 0 | GeneralName gn(str.substr(p0 + p1 + 2)); |
233 | 0 |
|
234 | 0 | if(p0 > 0 && p1 > 0) |
235 | 0 | { |
236 | 0 | m_minimum = min; |
237 | 0 | m_maximum = max; |
238 | 0 | m_base = gn; |
239 | 0 | } |
240 | 0 | else |
241 | 0 | { |
242 | 0 | throw Invalid_Argument("Failed to decode Name Constraint"); |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | void GeneralSubtree::encode_into(DER_Encoder&) const |
247 | 0 | { |
248 | 0 | throw Not_Implemented("General Subtree encoding"); |
249 | 0 | } |
250 | | |
251 | | void GeneralSubtree::decode_from(class BER_Decoder& ber) |
252 | 5.55k | { |
253 | 5.55k | ber.start_cons(SEQUENCE) |
254 | 5.55k | .decode(m_base) |
255 | 5.55k | .decode_optional(m_minimum,ASN1_Tag(0), CONTEXT_SPECIFIC,size_t(0)) |
256 | 5.55k | .end_cons(); |
257 | 5.55k | |
258 | 5.55k | if(m_minimum != 0) |
259 | 169 | throw Decoding_Error("GeneralSubtree minimum must be 0"); |
260 | 5.38k | |
261 | 5.38k | m_maximum = std::numeric_limits<std::size_t>::max(); |
262 | 5.38k | } |
263 | | |
264 | | std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs) |
265 | 177 | { |
266 | 177 | os << gs.minimum() << "," << gs.maximum() << "," << gs.base(); |
267 | 177 | return os; |
268 | 177 | } |
269 | | } |