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