Botan 3.0.0-alpha0
Crypto and TLS for C&
parsing.cpp
Go to the documentation of this file.
1/*
2* Various string utils and parsing functions
3* (C) 1999-2007,2013,2014,2015,2018 Jack Lloyd
4* (C) 2015 Simon Warta (Kullo GmbH)
5* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/internal/parsing.h>
11#include <botan/internal/loadstor.h>
12#include <botan/exceptn.h>
13#include <algorithm>
14#include <cctype>
15#include <limits>
16
17namespace Botan {
18
19uint16_t to_uint16(const std::string& str)
20 {
21 const uint32_t x = to_u32bit(str);
22
23 if(x >> 16)
24 throw Invalid_Argument("Integer value exceeds 16 bit range");
25
26 return static_cast<uint16_t>(x);
27 }
28
29uint32_t to_u32bit(const std::string& str)
30 {
31 // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
32 for(const char chr : str)
33 {
34 if(chr < '0' || chr > '9')
35 {
36 std::string chrAsString(1, chr);
37 throw Invalid_Argument("String contains non-digit char: " + chrAsString);
38 }
39 }
40
41 const unsigned long int x = std::stoul(str);
42
43 if constexpr(sizeof(unsigned long int) > 4)
44 {
45 // x might be uint64
46 if(x > std::numeric_limits<uint32_t>::max())
47 {
48 throw Invalid_Argument("Integer value of " + str + " exceeds 32 bit range");
49 }
50 }
51
52 return static_cast<uint32_t>(x);
53 }
54
55/*
56* Parse a SCAN-style algorithm name
57*/
58std::vector<std::string> parse_algorithm_name(const std::string& namex)
59 {
60 if(namex.find('(') == std::string::npos &&
61 namex.find(')') == std::string::npos)
62 return std::vector<std::string>(1, namex);
63
64 std::string name = namex, substring;
65 std::vector<std::string> elems;
66 size_t level = 0;
67
68 elems.push_back(name.substr(0, name.find('(')));
69 name = name.substr(name.find('('));
70
71 for(auto i = name.begin(); i != name.end(); ++i)
72 {
73 char c = *i;
74
75 if(c == '(')
76 ++level;
77 if(c == ')')
78 {
79 if(level == 1 && i == name.end() - 1)
80 {
81 if(elems.size() == 1)
82 elems.push_back(substring.substr(1));
83 else
84 elems.push_back(substring);
85 return elems;
86 }
87
88 if(level == 0 || (level == 1 && i != name.end() - 1))
89 throw Invalid_Algorithm_Name(namex);
90 --level;
91 }
92
93 if(c == ',' && level == 1)
94 {
95 if(elems.size() == 1)
96 elems.push_back(substring.substr(1));
97 else
98 elems.push_back(substring);
99 substring.clear();
100 }
101 else
102 substring += c;
103 }
104
105 if(!substring.empty())
106 throw Invalid_Algorithm_Name(namex);
107
108 return elems;
109 }
110
111std::vector<std::string> split_on(const std::string& str, char delim)
112 {
113 std::vector<std::string> elems;
114 if(str.empty()) return elems;
115
116 std::string substr;
117 for(auto i = str.begin(); i != str.end(); ++i)
118 {
119 if(*i == delim)
120 {
121 if(!substr.empty())
122 elems.push_back(substr);
123 substr.clear();
124 }
125 else
126 substr += *i;
127 }
128
129 if(substr.empty())
130 throw Invalid_Argument("Unable to split string: " + str);
131 elems.push_back(substr);
132
133 return elems;
134 }
135
136/*
137* Join a string
138*/
139std::string string_join(const std::vector<std::string>& strs, char delim)
140 {
141 std::string out = "";
142
143 for(size_t i = 0; i != strs.size(); ++i)
144 {
145 if(i != 0)
146 out += delim;
147 out += strs[i];
148 }
149
150 return out;
151 }
152
153/*
154* Convert a decimal-dotted string to binary IP
155*/
156uint32_t string_to_ipv4(const std::string& str)
157 {
158 std::vector<std::string> parts = split_on(str, '.');
159
160 if(parts.size() != 4)
161 throw Decoding_Error("Invalid IP string " + str);
162
163 uint32_t ip = 0;
164
165 for(auto part = parts.begin(); part != parts.end(); ++part)
166 {
167 uint32_t octet = to_u32bit(*part);
168
169 if(octet > 255)
170 throw Decoding_Error("Invalid IP string " + str);
171
172 ip = (ip << 8) | (octet & 0xFF);
173 }
174
175 return ip;
176 }
177
178/*
179* Convert an IP address to decimal-dotted string
180*/
181std::string ipv4_to_string(uint32_t ip)
182 {
183 std::string str;
184 uint8_t bits[4];
185 store_be(ip, bits);
186
187 for(size_t i = 0; i != 4; ++i)
188 {
189 if(i > 0)
190 {
191 str += ".";
192 }
193 str += std::to_string(bits[i]);
194 }
195
196 return str;
197 }
198
199std::string tolower_string(const std::string& in)
200 {
201 std::string s = in;
202 for(size_t i = 0; i != s.size(); ++i)
203 {
204 const int cu = static_cast<unsigned char>(s[i]);
205 if(std::isalpha(cu))
206 s[i] = static_cast<char>(std::tolower(cu));
207 }
208 return s;
209 }
210
211bool host_wildcard_match(const std::string& issued_, const std::string& host_)
212 {
213 const std::string issued = tolower_string(issued_);
214 const std::string host = tolower_string(host_);
215
216 if(host.empty() || issued.empty())
217 return false;
218
219 /*
220 If there are embedded nulls in your issued name
221 Well I feel bad for you son
222 */
223 if(std::count(issued.begin(), issued.end(), char(0)) > 0)
224 return false;
225
226 // If more than one wildcard, then issued name is invalid
227 const size_t stars = std::count(issued.begin(), issued.end(), '*');
228 if(stars > 1)
229 return false;
230
231 // '*' is not a valid character in DNS names so should not appear on the host side
232 if(std::count(host.begin(), host.end(), '*') != 0)
233 return false;
234
235 // Similarly a DNS name can't end in .
236 if(host[host.size() - 1] == '.')
237 return false;
238
239 // And a host can't have an empty name component, so reject that
240 if(host.find("..") != std::string::npos)
241 return false;
242
243 // Exact match: accept
244 if(issued == host)
245 {
246 return true;
247 }
248
249 /*
250 Otherwise it might be a wildcard
251
252 If the issued size is strictly longer than the hostname size it
253 couldn't possibly be a match, even if the issued value is a
254 wildcard. The only exception is when the wildcard ends up empty
255 (eg www.example.com matches www*.example.com)
256 */
257 if(issued.size() > host.size() + 1)
258 {
259 return false;
260 }
261
262 // If no * at all then not a wildcard, and so not a match
263 if(stars != 1)
264 {
265 return false;
266 }
267
268 /*
269 Now walk through the issued string, making sure every character
270 matches. When we come to the (singular) '*', jump forward in the
271 hostname by the corresponding amount. We know exactly how much
272 space the wildcard takes because it must be exactly `len(host) -
273 len(issued) + 1 chars`.
274
275 We also verify that the '*' comes in the leftmost component, and
276 doesn't skip over any '.' in the hostname.
277 */
278 size_t dots_seen = 0;
279 size_t host_idx = 0;
280
281 for(size_t i = 0; i != issued.size(); ++i)
282 {
283 dots_seen += (issued[i] == '.');
284
285 if(issued[i] == '*')
286 {
287 // Fail: wildcard can only come in leftmost component
288 if(dots_seen > 0)
289 {
290 return false;
291 }
292
293 /*
294 Since there is only one * we know the tail of the issued and
295 hostname must be an exact match. In this case advance host_idx
296 to match.
297 */
298 const size_t advance = (host.size() - issued.size() + 1);
299
300 if(host_idx + advance > host.size()) // shouldn't happen
301 return false;
302
303 // Can't be any intervening .s that we would have skipped
304 if(std::count(host.begin() + host_idx,
305 host.begin() + host_idx + advance, '.') != 0)
306 return false;
307
308 host_idx += advance;
309 }
310 else
311 {
312 if(issued[i] != host[host_idx])
313 {
314 return false;
315 }
316
317 host_idx += 1;
318 }
319 }
320
321 // Wildcard issued name must have at least 3 components
322 if(dots_seen < 2)
323 {
324 return false;
325 }
326
327 return true;
328 }
329
330}
std::string name
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:209
Definition: alg_id.cpp:13
uint32_t string_to_ipv4(const std::string &str)
Definition: parsing.cpp:156
uint16_t to_uint16(const std::string &str)
Definition: parsing.cpp:19
bool host_wildcard_match(const std::string &issued_, const std::string &host_)
Definition: parsing.cpp:211
std::string string_join(const std::vector< std::string > &strs, char delim)
Definition: parsing.cpp:139
std::vector< std::string > split_on(const std::string &str, char delim)
Definition: parsing.cpp:111
constexpr void store_be(uint16_t in, uint8_t out[2])
Definition: loadstor.h:449
std::string tolower_string(const std::string &in)
Definition: parsing.cpp:199
std::vector< std::string > parse_algorithm_name(const std::string &namex)
Definition: parsing.cpp:58
std::string ipv4_to_string(uint32_t ip)
Definition: parsing.cpp:181
uint32_t to_u32bit(const std::string &str)
Definition: parsing.cpp:29