Coverage Report

Created: 2020-05-23 13:54

/src/botan/src/lib/utils/parsing.cpp
Line
Count
Source (jump to first uncovered line)
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/parsing.h>
11
#include <botan/exceptn.h>
12
#include <botan/charset.h>
13
#include <botan/loadstor.h>
14
#include <algorithm>
15
#include <cctype>
16
#include <limits>
17
#include <set>
18
19
#if defined(BOTAN_HAS_ASN1)
20
  #include <botan/asn1_oid.h>
21
#endif
22
23
namespace Botan {
24
25
uint16_t to_uint16(const std::string& str)
26
0
   {
27
0
   const uint32_t x = to_u32bit(str);
28
0
29
0
   if(x >> 16)
30
0
      throw Invalid_Argument("Integer value exceeds 16 bit range");
31
0
32
0
   return static_cast<uint16_t>(x);
33
0
   }
34
35
uint32_t to_u32bit(const std::string& str)
36
2.27M
   {
37
2.27M
   // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
38
2.27M
   for(const char chr : str)
39
3.43M
      {
40
3.43M
      if(chr < '0' || chr > '9')
41
567
         {
42
567
         std::string chrAsString(1, chr);
43
567
         throw Invalid_Argument("String contains non-digit char: " + chrAsString);
44
567
         }
45
3.43M
      }
46
2.27M
47
2.27M
   const unsigned long int x = std::stoul(str);
48
2.27M
49
2.27M
   if(sizeof(unsigned long int) > 4)
50
2.27M
      {
51
2.27M
      // x might be uint64
52
2.27M
      if (x > std::numeric_limits<uint32_t>::max())
53
0
         {
54
0
         throw Invalid_Argument("Integer value of " + str + " exceeds 32 bit range");
55
0
         }
56
2.27M
      }
57
2.27M
58
2.27M
   return static_cast<uint32_t>(x);
59
2.27M
   }
60
61
/*
62
* Convert a string into a time duration
63
*/
64
uint32_t timespec_to_u32bit(const std::string& timespec)
65
0
   {
66
0
   if(timespec.empty())
67
0
      return 0;
68
0
69
0
   const char suffix = timespec[timespec.size()-1];
70
0
   std::string value = timespec.substr(0, timespec.size()-1);
71
0
72
0
   uint32_t scale = 1;
73
0
74
0
   if(Charset::is_digit(suffix))
75
0
      value += suffix;
76
0
   else if(suffix == 's')
77
0
      scale = 1;
78
0
   else if(suffix == 'm')
79
0
      scale = 60;
80
0
   else if(suffix == 'h')
81
0
      scale = 60 * 60;
82
0
   else if(suffix == 'd')
83
0
      scale = 24 * 60 * 60;
84
0
   else if(suffix == 'y')
85
0
      scale = 365 * 24 * 60 * 60;
86
0
   else
87
0
      throw Decoding_Error("timespec_to_u32bit: Bad input " + timespec);
88
0
89
0
   return scale * to_u32bit(value);
90
0
   }
91
92
/*
93
* Parse a SCAN-style algorithm name
94
*/
95
std::vector<std::string> parse_algorithm_name(const std::string& namex)
96
1.69k
   {
97
1.69k
   if(namex.find('(') == std::string::npos &&
98
1.69k
      namex.find(')') == std::string::npos)
99
972
      return std::vector<std::string>(1, namex);
100
727
101
727
   std::string name = namex, substring;
102
727
   std::vector<std::string> elems;
103
727
   size_t level = 0;
104
727
105
727
   elems.push_back(name.substr(0, name.find('(')));
106
727
   name = name.substr(name.find('('));
107
727
108
2.69k
   for(auto i = name.begin(); i != name.end(); ++i)
109
2.69k
      {
110
2.69k
      char c = *i;
111
2.69k
112
2.69k
      if(c == '(')
113
727
         ++level;
114
2.69k
      if(c == ')')
115
727
         {
116
727
         if(level == 1 && i == name.end() - 1)
117
727
            {
118
727
            if(elems.size() == 1)
119
727
               elems.push_back(substring.substr(1));
120
0
            else
121
0
               elems.push_back(substring);
122
727
            return elems;
123
727
            }
124
0
125
0
         if(level == 0 || (level == 1 && i != name.end() - 1))
126
0
            throw Invalid_Algorithm_Name(namex);
127
0
         --level;
128
0
         }
129
2.69k
130
2.69k
      if(c == ',' && level == 1)
131
0
         {
132
0
         if(elems.size() == 1)
133
0
            elems.push_back(substring.substr(1));
134
0
         else
135
0
            elems.push_back(substring);
136
0
         substring.clear();
137
0
         }
138
1.97k
      else
139
1.97k
         substring += c;
140
1.97k
      }
141
727
142
727
   if(!substring.empty())
143
0
      throw Invalid_Algorithm_Name(namex);
144
0
145
0
   return elems;
146
0
   }
147
148
std::vector<std::string> split_on(const std::string& str, char delim)
149
29.2k
   {
150
249k
   return split_on_pred(str, [delim](char c) { return c == delim; });
151
29.2k
   }
152
153
std::vector<std::string> split_on_pred(const std::string& str,
154
                                       std::function<bool (char)> pred)
155
29.2k
   {
156
29.2k
   std::vector<std::string> elems;
157
29.2k
   if(str.empty()) return elems;
158
26.9k
159
26.9k
   std::string substr;
160
276k
   for(auto i = str.begin(); i != str.end(); ++i)
161
249k
      {
162
249k
      if(pred(*i))
163
9.60k
         {
164
9.60k
         if(!substr.empty())
165
9.60k
            elems.push_back(substr);
166
9.60k
         substr.clear();
167
9.60k
         }
168
240k
      else
169
240k
         substr += *i;
170
249k
      }
171
26.9k
172
26.9k
   if(substr.empty())
173
0
      throw Invalid_Argument("Unable to split string: " + str);
174
26.9k
   elems.push_back(substr);
175
26.9k
176
26.9k
   return elems;
177
26.9k
   }
178
179
/*
180
* Join a string
181
*/
182
std::string string_join(const std::vector<std::string>& strs, char delim)
183
0
   {
184
0
   std::string out = "";
185
0
186
0
   for(size_t i = 0; i != strs.size(); ++i)
187
0
      {
188
0
      if(i != 0)
189
0
         out += delim;
190
0
      out += strs[i];
191
0
      }
192
0
193
0
   return out;
194
0
   }
195
196
/*
197
* Parse an ASN.1 OID string
198
*/
199
std::vector<uint32_t> parse_asn1_oid(const std::string& oid)
200
0
   {
201
0
#if defined(BOTAN_HAS_ASN1)
202
0
   return OID(oid).get_components();
203
#else
204
   BOTAN_UNUSED(oid);
205
   throw Not_Implemented("ASN1 support not available");
206
#endif
207
   }
208
209
/*
210
* X.500 String Comparison
211
*/
212
bool x500_name_cmp(const std::string& name1, const std::string& name2)
213
50.8k
   {
214
50.8k
   auto p1 = name1.begin();
215
50.8k
   auto p2 = name2.begin();
216
50.8k
217
52.7k
   while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
218
55.1k
   while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
219
50.8k
220
542k
   while(p1 != name1.end() && p2 != name2.end())
221
494k
      {
222
494k
      if(Charset::is_space(*p1))
223
36.1k
         {
224
36.1k
         if(!Charset::is_space(*p2))
225
211
            return false;
226
35.9k
227
73.2k
         while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
228
73.1k
         while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
229
35.9k
230
35.9k
         if(p1 == name1.end() && p2 == name2.end())
231
40
            return true;
232
35.9k
         if(p1 == name1.end() || p2 == name2.end())
233
173
            return false;
234
493k
         }
235
493k
236
493k
      if(!Charset::caseless_cmp(*p1, *p2))
237
1.93k
         return false;
238
491k
      ++p1;
239
491k
      ++p2;
240
491k
      }
241
50.8k
242
50.8k
   while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
243
49.3k
   while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
244
48.4k
245
48.4k
   if((p1 != name1.end()) || (p2 != name2.end()))
246
371
      return false;
247
48.1k
   return true;
248
48.1k
   }
249
250
/*
251
* Convert a decimal-dotted string to binary IP
252
*/
253
uint32_t string_to_ipv4(const std::string& str)
254
0
   {
255
0
   std::vector<std::string> parts = split_on(str, '.');
256
0
257
0
   if(parts.size() != 4)
258
0
      throw Decoding_Error("Invalid IP string " + str);
259
0
260
0
   uint32_t ip = 0;
261
0
262
0
   for(auto part = parts.begin(); part != parts.end(); ++part)
263
0
      {
264
0
      uint32_t octet = to_u32bit(*part);
265
0
266
0
      if(octet > 255)
267
0
         throw Decoding_Error("Invalid IP string " + str);
268
0
269
0
      ip = (ip << 8) | (octet & 0xFF);
270
0
      }
271
0
272
0
   return ip;
273
0
   }
274
275
/*
276
* Convert an IP address to decimal-dotted string
277
*/
278
std::string ipv4_to_string(uint32_t ip)
279
2.70k
   {
280
2.70k
   std::string str;
281
2.70k
282
13.5k
   for(size_t i = 0; i != sizeof(ip); ++i)
283
10.8k
      {
284
10.8k
      if(i)
285
8.12k
         str += ".";
286
10.8k
      str += std::to_string(get_byte(i, ip));
287
10.8k
      }
288
2.70k
289
2.70k
   return str;
290
2.70k
   }
291
292
std::string erase_chars(const std::string& str, const std::set<char>& chars)
293
0
   {
294
0
   std::string out;
295
0
296
0
   for(auto c: str)
297
0
      if(chars.count(c) == 0)
298
0
         out += c;
299
0
300
0
   return out;
301
0
   }
302
303
std::string replace_chars(const std::string& str,
304
                          const std::set<char>& chars,
305
                          char to_char)
306
0
   {
307
0
   std::string out = str;
308
0
309
0
   for(size_t i = 0; i != out.size(); ++i)
310
0
      if(chars.count(out[i]))
311
0
         out[i] = to_char;
312
0
313
0
   return out;
314
0
   }
315
316
std::string replace_char(const std::string& str, char from_char, char to_char)
317
0
   {
318
0
   std::string out = str;
319
0
320
0
   for(size_t i = 0; i != out.size(); ++i)
321
0
      if(out[i] == from_char)
322
0
         out[i] = to_char;
323
0
324
0
   return out;
325
0
   }
326
327
namespace {
328
329
std::string tolower_string(const std::string& in)
330
0
   {
331
0
   std::string s = in;
332
0
   for(size_t i = 0; i != s.size(); ++i)
333
0
      {
334
0
      const int cu = static_cast<unsigned char>(s[i]);
335
0
      if(std::isalpha(cu))
336
0
         s[i] = static_cast<char>(std::tolower(cu));
337
0
      }
338
0
   return s;
339
0
   }
340
341
}
342
343
bool host_wildcard_match(const std::string& issued_, const std::string& host_)
344
0
   {
345
0
   const std::string issued = tolower_string(issued_);
346
0
   const std::string host = tolower_string(host_);
347
0
348
0
   if(host.empty() || issued.empty())
349
0
      return false;
350
0
351
0
   /*
352
0
   If there are embedded nulls in your issued name
353
0
   Well I feel bad for you son
354
0
   */
355
0
   if(std::count(issued.begin(), issued.end(), char(0)) > 0)
356
0
      return false;
357
0
358
0
   // If more than one wildcard, then issued name is invalid
359
0
   const size_t stars = std::count(issued.begin(), issued.end(), '*');
360
0
   if(stars > 1)
361
0
      return false;
362
0
363
0
   // '*' is not a valid character in DNS names so should not appear on the host side
364
0
   if(std::count(host.begin(), host.end(), '*') != 0)
365
0
      return false;
366
0
367
0
   // Similarly a DNS name can't end in .
368
0
   if(host[host.size() - 1] == '.')
369
0
      return false;
370
0
371
0
   // And a host can't have an empty name component, so reject that
372
0
   if(host.find("..") != std::string::npos)
373
0
      return false;
374
0
375
0
   // Exact match: accept
376
0
   if(issued == host)
377
0
      {
378
0
      return true;
379
0
      }
380
0
381
0
   /*
382
0
   Otherwise it might be a wildcard
383
0
384
0
   If the issued size is strictly longer than the hostname size it
385
0
   couldn't possibly be a match, even if the issued value is a
386
0
   wildcard. The only exception is when the wildcard ends up empty
387
0
   (eg www.example.com matches www*.example.com)
388
0
   */
389
0
   if(issued.size() > host.size() + 1)
390
0
      {
391
0
      return false;
392
0
      }
393
0
394
0
   // If no * at all then not a wildcard, and so not a match
395
0
   if(stars != 1)
396
0
      {
397
0
      return false;
398
0
      }
399
0
400
0
   /*
401
0
   Now walk through the issued string, making sure every character
402
0
   matches. When we come to the (singular) '*', jump forward in the
403
0
   hostname by the corresponding amount. We know exactly how much
404
0
   space the wildcard takes because it must be exactly `len(host) -
405
0
   len(issued) + 1 chars`.
406
0
407
0
   We also verify that the '*' comes in the leftmost component, and
408
0
   doesn't skip over any '.' in the hostname.
409
0
   */
410
0
   size_t dots_seen = 0;
411
0
   size_t host_idx = 0;
412
0
413
0
   for(size_t i = 0; i != issued.size(); ++i)
414
0
      {
415
0
      dots_seen += (issued[i] == '.');
416
0
417
0
      if(issued[i] == '*')
418
0
         {
419
0
         // Fail: wildcard can only come in leftmost component
420
0
         if(dots_seen > 0)
421
0
            {
422
0
            return false;
423
0
            }
424
0
425
0
         /*
426
0
         Since there is only one * we know the tail of the issued and
427
0
         hostname must be an exact match. In this case advance host_idx
428
0
         to match.
429
0
         */
430
0
         const size_t advance = (host.size() - issued.size() + 1);
431
0
432
0
         if(host_idx + advance > host.size()) // shouldn't happen
433
0
            return false;
434
0
435
0
         // Can't be any intervening .s that we would have skipped
436
0
         if(std::count(host.begin() + host_idx,
437
0
                       host.begin() + host_idx + advance, '.') != 0)
438
0
            return false;
439
0
440
0
         host_idx += advance;
441
0
         }
442
0
      else
443
0
         {
444
0
         if(issued[i] != host[host_idx])
445
0
            {
446
0
            return false;
447
0
            }
448
0
449
0
         host_idx += 1;
450
0
         }
451
0
      }
452
0
453
0
   // Wildcard issued name must have at least 3 components
454
0
   if(dots_seen < 2)
455
0
      {
456
0
      return false;
457
0
      }
458
0
459
0
   return true;
460
0
   }
461
462
}