Coverage Report

Created: 2021-05-04 09:02

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