Coverage Report

Created: 2020-11-21 08:34

/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/internal/parsing.h>
11
#include <botan/exceptn.h>
12
#include <botan/internal/charset.h>
13
#include <botan/internal/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_obj.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
29
0
   if(x >> 16)
30
0
      throw Invalid_Argument("Integer value exceeds 16 bit range");
31
32
0
   return static_cast<uint16_t>(x);
33
0
   }
34
35
uint32_t to_u32bit(const std::string& str)
36
2.68M
   {
37
   // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
38
2.68M
   for(const char chr : str)
39
4.05M
      {
40
4.05M
      if(chr < '0' || chr > '9')
41
572
         {
42
572
         std::string chrAsString(1, chr);
43
572
         throw Invalid_Argument("String contains non-digit char: " + chrAsString);
44
572
         }
45
4.05M
      }
46
47
2.68M
   const unsigned long int x = std::stoul(str);
48
49
2.68M
   if(sizeof(unsigned long int) > 4)
50
2.68M
      {
51
      // x might be uint64
52
2.68M
      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.68M
      }
57
58
2.68M
   return static_cast<uint32_t>(x);
59
2.68M
   }
60
61
/*
62
* Parse a SCAN-style algorithm name
63
*/
64
std::vector<std::string> parse_algorithm_name(const std::string& namex)
65
1.78k
   {
66
1.78k
   if(namex.find('(') == std::string::npos &&
67
993
      namex.find(')') == std::string::npos)
68
993
      return std::vector<std::string>(1, namex);
69
70
793
   std::string name = namex, substring;
71
793
   std::vector<std::string> elems;
72
793
   size_t level = 0;
73
74
793
   elems.push_back(name.substr(0, name.find('(')));
75
793
   name = name.substr(name.find('('));
76
77
2.97k
   for(auto i = name.begin(); i != name.end(); ++i)
78
2.97k
      {
79
2.97k
      char c = *i;
80
81
2.97k
      if(c == '(')
82
793
         ++level;
83
2.97k
      if(c == ')')
84
793
         {
85
793
         if(level == 1 && i == name.end() - 1)
86
793
            {
87
793
            if(elems.size() == 1)
88
793
               elems.push_back(substring.substr(1));
89
0
            else
90
0
               elems.push_back(substring);
91
793
            return elems;
92
793
            }
93
94
0
         if(level == 0 || (level == 1 && i != name.end() - 1))
95
0
            throw Invalid_Algorithm_Name(namex);
96
0
         --level;
97
0
         }
98
99
2.18k
      if(c == ',' && level == 1)
100
0
         {
101
0
         if(elems.size() == 1)
102
0
            elems.push_back(substring.substr(1));
103
0
         else
104
0
            elems.push_back(substring);
105
0
         substring.clear();
106
0
         }
107
2.18k
      else
108
2.18k
         substring += c;
109
2.18k
      }
110
111
0
   if(!substring.empty())
112
0
      throw Invalid_Algorithm_Name(namex);
113
114
0
   return elems;
115
0
   }
116
117
std::vector<std::string> split_on(const std::string& str, char delim)
118
32.3k
   {
119
32.3k
   std::vector<std::string> elems;
120
32.3k
   if(str.empty()) return elems;
121
122
29.6k
   std::string substr;
123
311k
   for(auto i = str.begin(); i != str.end(); ++i)
124
281k
      {
125
281k
      if(*i == delim)
126
10.4k
         {
127
10.4k
         if(!substr.empty())
128
10.4k
            elems.push_back(substr);
129
10.4k
         substr.clear();
130
10.4k
         }
131
271k
      else
132
271k
         substr += *i;
133
281k
      }
134
135
29.6k
   if(substr.empty())
136
0
      throw Invalid_Argument("Unable to split string: " + str);
137
29.6k
   elems.push_back(substr);
138
139
29.6k
   return elems;
140
29.6k
   }
141
142
/*
143
* Join a string
144
*/
145
std::string string_join(const std::vector<std::string>& strs, char delim)
146
0
   {
147
0
   std::string out = "";
148
149
0
   for(size_t i = 0; i != strs.size(); ++i)
150
0
      {
151
0
      if(i != 0)
152
0
         out += delim;
153
0
      out += strs[i];
154
0
      }
155
156
0
   return out;
157
0
   }
158
159
/*
160
* X.500 String Comparison
161
*/
162
bool x500_name_cmp(const std::string& name1, const std::string& name2)
163
60.7k
   {
164
60.7k
   auto p1 = name1.begin();
165
60.7k
   auto p2 = name2.begin();
166
167
62.4k
   while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
168
65.2k
   while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
169
170
652k
   while(p1 != name1.end() && p2 != name2.end())
171
595k
      {
172
595k
      if(Charset::is_space(*p1))
173
44.8k
         {
174
44.8k
         if(!Charset::is_space(*p2))
175
506
            return false;
176
177
90.0k
         while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
178
89.9k
         while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
179
180
44.3k
         if(p1 == name1.end() && p2 == name2.end())
181
140
            return true;
182
44.1k
         if(p1 == name1.end() || p2 == name2.end())
183
201
            return false;
184
594k
         }
185
186
594k
      if(!Charset::caseless_cmp(*p1, *p2))
187
2.09k
         return false;
188
592k
      ++p1;
189
592k
      ++p2;
190
592k
      }
191
192
58.4k
   while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
193
58.7k
   while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
194
195
57.7k
   if((p1 != name1.end()) || (p2 != name2.end()))
196
383
      return false;
197
57.3k
   return true;
198
57.3k
   }
199
200
/*
201
* Convert a decimal-dotted string to binary IP
202
*/
203
uint32_t string_to_ipv4(const std::string& str)
204
0
   {
205
0
   std::vector<std::string> parts = split_on(str, '.');
206
207
0
   if(parts.size() != 4)
208
0
      throw Decoding_Error("Invalid IP string " + str);
209
210
0
   uint32_t ip = 0;
211
212
0
   for(auto part = parts.begin(); part != parts.end(); ++part)
213
0
      {
214
0
      uint32_t octet = to_u32bit(*part);
215
216
0
      if(octet > 255)
217
0
         throw Decoding_Error("Invalid IP string " + str);
218
219
0
      ip = (ip << 8) | (octet & 0xFF);
220
0
      }
221
222
0
   return ip;
223
0
   }
224
225
/*
226
* Convert an IP address to decimal-dotted string
227
*/
228
std::string ipv4_to_string(uint32_t ip)
229
4.59k
   {
230
4.59k
   std::string str;
231
232
22.9k
   for(size_t i = 0; i != sizeof(ip); ++i)
233
18.3k
      {
234
18.3k
      if(i)
235
13.7k
         str += ".";
236
18.3k
      str += std::to_string(get_byte(i, ip));
237
18.3k
      }
238
239
4.59k
   return str;
240
4.59k
   }
241
242
namespace {
243
244
std::string tolower_string(const std::string& in)
245
0
   {
246
0
   std::string s = in;
247
0
   for(size_t i = 0; i != s.size(); ++i)
248
0
      {
249
0
      const int cu = static_cast<unsigned char>(s[i]);
250
0
      if(std::isalpha(cu))
251
0
         s[i] = static_cast<char>(std::tolower(cu));
252
0
      }
253
0
   return s;
254
0
   }
255
256
}
257
258
bool host_wildcard_match(const std::string& issued_, const std::string& host_)
259
0
   {
260
0
   const std::string issued = tolower_string(issued_);
261
0
   const std::string host = tolower_string(host_);
262
263
0
   if(host.empty() || issued.empty())
264
0
      return false;
265
266
   /*
267
   If there are embedded nulls in your issued name
268
   Well I feel bad for you son
269
   */
270
0
   if(std::count(issued.begin(), issued.end(), char(0)) > 0)
271
0
      return false;
272
273
   // If more than one wildcard, then issued name is invalid
274
0
   const size_t stars = std::count(issued.begin(), issued.end(), '*');
275
0
   if(stars > 1)
276
0
      return false;
277
278
   // '*' is not a valid character in DNS names so should not appear on the host side
279
0
   if(std::count(host.begin(), host.end(), '*') != 0)
280
0
      return false;
281
282
   // Similarly a DNS name can't end in .
283
0
   if(host[host.size() - 1] == '.')
284
0
      return false;
285
286
   // And a host can't have an empty name component, so reject that
287
0
   if(host.find("..") != std::string::npos)
288
0
      return false;
289
290
   // Exact match: accept
291
0
   if(issued == host)
292
0
      {
293
0
      return true;
294
0
      }
295
296
   /*
297
   Otherwise it might be a wildcard
298
299
   If the issued size is strictly longer than the hostname size it
300
   couldn't possibly be a match, even if the issued value is a
301
   wildcard. The only exception is when the wildcard ends up empty
302
   (eg www.example.com matches www*.example.com)
303
   */
304
0
   if(issued.size() > host.size() + 1)
305
0
      {
306
0
      return false;
307
0
      }
308
309
   // If no * at all then not a wildcard, and so not a match
310
0
   if(stars != 1)
311
0
      {
312
0
      return false;
313
0
      }
314
315
   /*
316
   Now walk through the issued string, making sure every character
317
   matches. When we come to the (singular) '*', jump forward in the
318
   hostname by the corresponding amount. We know exactly how much
319
   space the wildcard takes because it must be exactly `len(host) -
320
   len(issued) + 1 chars`.
321
322
   We also verify that the '*' comes in the leftmost component, and
323
   doesn't skip over any '.' in the hostname.
324
   */
325
0
   size_t dots_seen = 0;
326
0
   size_t host_idx = 0;
327
328
0
   for(size_t i = 0; i != issued.size(); ++i)
329
0
      {
330
0
      dots_seen += (issued[i] == '.');
331
332
0
      if(issued[i] == '*')
333
0
         {
334
         // Fail: wildcard can only come in leftmost component
335
0
         if(dots_seen > 0)
336
0
            {
337
0
            return false;
338
0
            }
339
340
         /*
341
         Since there is only one * we know the tail of the issued and
342
         hostname must be an exact match. In this case advance host_idx
343
         to match.
344
         */
345
0
         const size_t advance = (host.size() - issued.size() + 1);
346
347
0
         if(host_idx + advance > host.size()) // shouldn't happen
348
0
            return false;
349
350
         // Can't be any intervening .s that we would have skipped
351
0
         if(std::count(host.begin() + host_idx,
352
0
                       host.begin() + host_idx + advance, '.') != 0)
353
0
            return false;
354
355
0
         host_idx += advance;
356
0
         }
357
0
      else
358
0
         {
359
0
         if(issued[i] != host[host_idx])
360
0
            {
361
0
            return false;
362
0
            }
363
364
0
         host_idx += 1;
365
0
         }
366
0
      }
367
368
   // Wildcard issued name must have at least 3 components
369
0
   if(dots_seen < 2)
370
0
      {
371
0
      return false;
372
0
      }
373
374
0
   return true;
375
0
   }
376
377
}