/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.38k | { |
43 | 4.38k | BER_Object obj = ber.get_next_object(); |
44 | 4.38k | |
45 | 4.38k | if(obj.is_a(1, CONTEXT_SPECIFIC)) |
46 | 497 | { |
47 | 497 | m_type = "RFC822"; |
48 | 497 | m_name = ASN1::to_string(obj); |
49 | 497 | } |
50 | 3.88k | else if(obj.is_a(2, CONTEXT_SPECIFIC)) |
51 | 655 | { |
52 | 655 | m_type = "DNS"; |
53 | 655 | m_name = ASN1::to_string(obj); |
54 | 655 | } |
55 | 3.23k | else if(obj.is_a(6, CONTEXT_SPECIFIC)) |
56 | 2.19k | { |
57 | 2.19k | m_type = "URI"; |
58 | 2.19k | m_name = ASN1::to_string(obj); |
59 | 2.19k | } |
60 | 1.04k | else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))) |
61 | 462 | { |
62 | 462 | m_type = "DN"; |
63 | 462 | X509_DN dn; |
64 | 462 | BER_Decoder dec(obj); |
65 | 462 | std::stringstream ss; |
66 | 462 | |
67 | 462 | dn.decode_from(dec); |
68 | 462 | ss << dn; |
69 | 462 | |
70 | 462 | m_name = ss.str(); |
71 | 462 | } |
72 | 578 | else if(obj.is_a(7, CONTEXT_SPECIFIC)) |
73 | 172 | { |
74 | 172 | if(obj.length() == 8) |
75 | 101 | { |
76 | 101 | m_type = "IP"; |
77 | 101 | m_name = ipv4_to_string(load_be<uint32_t>(obj.bits(), 0)) + "/" + |
78 | 101 | ipv4_to_string(load_be<uint32_t>(obj.bits(), 1)); |
79 | 101 | } |
80 | 71 | else if(obj.length() == 32) |
81 | 5 | { |
82 | 5 | throw Decoding_Error("Unsupported IPv6 name constraint"); |
83 | 5 | } |
84 | 66 | else |
85 | 66 | { |
86 | 66 | throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length())); |
87 | 66 | } |
88 | 406 | } |
89 | 406 | else |
90 | 406 | { |
91 | 406 | throw Decoding_Error("Found unknown GeneralName type"); |
92 | 406 | } |
93 | 4.38k | } |
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 | nam.push_back(dn.to_string()); |
119 | 0 |
|
120 | 0 | const auto alt_dn = alt_name.dn(); |
121 | 0 | if(alt_dn.empty() == false) |
122 | 0 | { |
123 | 0 | nam.push_back(alt_dn.to_string()); |
124 | 0 | } |
125 | 0 | } |
126 | 0 | else if(type() == "IP") |
127 | 0 | { |
128 | 0 | match_fn = std::mem_fn(&GeneralName::matches_ip); |
129 | 0 | nam = alt_name.get_attribute("IP"); |
130 | 0 | } |
131 | 0 | else |
132 | 0 | { |
133 | 0 | return MatchResult::UnknownType; |
134 | 0 | } |
135 | 0 | |
136 | 0 | if(nam.empty()) |
137 | 0 | { |
138 | 0 | return MatchResult::NotFound; |
139 | 0 | } |
140 | 0 | |
141 | 0 | bool some = false; |
142 | 0 | bool all = true; |
143 | 0 |
|
144 | 0 | for(const std::string& n: nam) |
145 | 0 | { |
146 | 0 | bool m = match_fn(this, n); |
147 | 0 |
|
148 | 0 | some |= m; |
149 | 0 | all &= m; |
150 | 0 | } |
151 | 0 |
|
152 | 0 | if(all) |
153 | 0 | { |
154 | 0 | return MatchResult::All; |
155 | 0 | } |
156 | 0 | else if(some) |
157 | 0 | { |
158 | 0 | return MatchResult::Some; |
159 | 0 | } |
160 | 0 | else |
161 | 0 | { |
162 | 0 | return MatchResult::None; |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | | bool GeneralName::matches_dns(const std::string& nam) const |
167 | 0 | { |
168 | 0 | if(nam.size() == name().size()) |
169 | 0 | { |
170 | 0 | return nam == name(); |
171 | 0 | } |
172 | 0 | else if(name().size() > nam.size()) |
173 | 0 | { |
174 | 0 | return false; |
175 | 0 | } |
176 | 0 | else // name.size() < nam.size() |
177 | 0 | { |
178 | 0 | std::string constr = name().front() == '.' ? name() : "." + name(); |
179 | 0 | // constr is suffix of nam |
180 | 0 | return constr == nam.substr(nam.size() - constr.size(), constr.size()); |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | | bool GeneralName::matches_dn(const std::string& nam) const |
185 | 0 | { |
186 | 0 | std::stringstream ss(nam); |
187 | 0 | std::stringstream tt(name()); |
188 | 0 | X509_DN nam_dn, my_dn; |
189 | 0 |
|
190 | 0 | ss >> nam_dn; |
191 | 0 | tt >> my_dn; |
192 | 0 |
|
193 | 0 | auto attr = nam_dn.get_attributes(); |
194 | 0 | bool ret = true; |
195 | 0 | size_t trys = 0; |
196 | 0 |
|
197 | 0 | for(const auto& c: my_dn.dn_info()) |
198 | 0 | { |
199 | 0 | auto i = attr.equal_range(c.first); |
200 | 0 |
|
201 | 0 | if(i.first != i.second) |
202 | 0 | { |
203 | 0 | trys += 1; |
204 | 0 | ret = ret && (i.first->second == c.second.value()); |
205 | 0 | } |
206 | 0 | } |
207 | 0 |
|
208 | 0 | return trys > 0 && ret; |
209 | 0 | } |
210 | | |
211 | | bool GeneralName::matches_ip(const std::string& nam) const |
212 | 0 | { |
213 | 0 | uint32_t ip = string_to_ipv4(nam); |
214 | 0 | std::vector<std::string> p = split_on(name(), '/'); |
215 | 0 |
|
216 | 0 | if(p.size() != 2) |
217 | 0 | throw Decoding_Error("failed to parse IPv4 address"); |
218 | 0 | |
219 | 0 | uint32_t net = string_to_ipv4(p.at(0)); |
220 | 0 | uint32_t mask = string_to_ipv4(p.at(1)); |
221 | 0 |
|
222 | 0 | return (ip & mask) == net; |
223 | 0 | } |
224 | | |
225 | | std::ostream& operator<<(std::ostream& os, const GeneralName& gn) |
226 | 226 | { |
227 | 226 | os << gn.type() << ":" << gn.name(); |
228 | 226 | return os; |
229 | 226 | } |
230 | | |
231 | | GeneralSubtree::GeneralSubtree(const std::string& str) : GeneralSubtree() |
232 | 0 | { |
233 | 0 | size_t p0, p1; |
234 | 0 | const auto min = std::stoull(str, &p0, 10); |
235 | 0 | const auto max = std::stoull(str.substr(p0 + 1), &p1, 10); |
236 | 0 | GeneralName gn(str.substr(p0 + p1 + 2)); |
237 | 0 |
|
238 | 0 | if(p0 > 0 && p1 > 0) |
239 | 0 | { |
240 | 0 | m_minimum = static_cast<size_t>(min); |
241 | 0 | m_maximum = static_cast<size_t>(max); |
242 | 0 | m_base = gn; |
243 | 0 | } |
244 | 0 | else |
245 | 0 | { |
246 | 0 | throw Invalid_Argument("Failed to decode Name Constraint"); |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | | void GeneralSubtree::encode_into(DER_Encoder&) const |
251 | 0 | { |
252 | 0 | throw Not_Implemented("General Subtree encoding"); |
253 | 0 | } |
254 | | |
255 | | void GeneralSubtree::decode_from(class BER_Decoder& ber) |
256 | 4.97k | { |
257 | 4.97k | ber.start_cons(SEQUENCE) |
258 | 4.97k | .decode(m_base) |
259 | 4.97k | .decode_optional(m_minimum,ASN1_Tag(0), CONTEXT_SPECIFIC,size_t(0)) |
260 | 4.97k | .end_cons(); |
261 | 4.97k | |
262 | 4.97k | if(m_minimum != 0) |
263 | 177 | throw Decoding_Error("GeneralSubtree minimum must be 0"); |
264 | 4.79k | |
265 | 4.79k | m_maximum = std::numeric_limits<std::size_t>::max(); |
266 | 4.79k | } |
267 | | |
268 | | std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs) |
269 | 226 | { |
270 | 226 | os << gs.minimum() << "," << gs.maximum() << "," << gs.base(); |
271 | 226 | return os; |
272 | 226 | } |
273 | | } |