Coverage Report

Created: 2023-02-22 06:14

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