Coverage Report

Created: 2022-06-23 06:44

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