Coverage Report

Created: 2021-10-13 08:49

/src/botan/src/lib/x509/name_constraint.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* X.509 Name Constraint
3
* (C) 2015 Kai Michaelis
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7
8
#include <botan/pkix_types.h>
9
#include <botan/ber_dec.h>
10
#include <botan/internal/loadstor.h>
11
#include <botan/x509cert.h>
12
#include <botan/internal/parsing.h>
13
#include <sstream>
14
#include <functional>
15
16
namespace Botan {
17
18
class DER_Encoder;
19
20
GeneralName::GeneralName(const std::string& str) : GeneralName()
21
0
   {
22
0
   size_t p = str.find(':');
23
24
0
   if(p != std::string::npos)
25
0
      {
26
0
      m_type = str.substr(0, p);
27
0
      m_name = str.substr(p + 1, std::string::npos);
28
0
      }
29
0
   else
30
0
      {
31
0
      throw Invalid_Argument("Failed to decode Name Constraint");
32
0
      }
33
0
   }
34
35
void GeneralName::encode_into(DER_Encoder&) const
36
0
   {
37
0
   throw Not_Implemented("GeneralName encoding");
38
0
   }
39
40
void GeneralName::decode_from(class BER_Decoder& ber)
41
4.53k
   {
42
4.53k
   BER_Object obj = ber.get_next_object();
43
44
4.53k
   if(obj.is_a(1, ASN1_Class::ContextSpecific))
45
943
      {
46
943
      m_type = "RFC822";
47
943
      m_name = ASN1::to_string(obj);
48
943
      }
49
3.59k
   else if(obj.is_a(2, ASN1_Class::ContextSpecific))
50
867
      {
51
867
      m_type = "DNS";
52
867
      m_name = ASN1::to_string(obj);
53
867
      }
54
2.72k
   else if(obj.is_a(6, ASN1_Class::ContextSpecific))
55
1.26k
      {
56
1.26k
      m_type = "URI";
57
1.26k
      m_name = ASN1::to_string(obj);
58
1.26k
      }
59
1.45k
   else if(obj.is_a(4, ASN1_Class::ContextSpecific | ASN1_Class::Constructed))
60
659
      {
61
659
      m_type = "DN";
62
659
      X509_DN dn;
63
659
      BER_Decoder dec(obj);
64
659
      std::stringstream ss;
65
66
659
      dn.decode_from(dec);
67
659
      ss << dn;
68
69
659
      m_name = ss.str();
70
659
      }
71
796
   else if(obj.is_a(7, ASN1_Class::ContextSpecific))
72
469
      {
73
469
      if(obj.length() == 8)
74
343
         {
75
343
         m_type = "IP";
76
343
         m_name = ipv4_to_string(load_be<uint32_t>(obj.bits(), 0)) + "/" +
77
343
                  ipv4_to_string(load_be<uint32_t>(obj.bits(), 1));
78
343
         }
79
126
      else if(obj.length() == 32)
80
11
         {
81
11
         throw Decoding_Error("Unsupported IPv6 name constraint");
82
11
         }
83
115
      else
84
115
         {
85
115
         throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length()));
86
115
         }
87
469
      }
88
327
   else
89
327
      {
90
327
      throw Decoding_Error("Found unknown GeneralName type");
91
327
      }
92
4.53k
   }
93
94
GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) const
95
0
   {
96
0
   std::vector<std::string> nam;
97
0
   std::function<bool(const GeneralName*, const std::string&)> match_fn;
98
99
0
   const X509_DN& dn = cert.subject_dn();
100
0
   const AlternativeName& alt_name = cert.subject_alt_name();
101
102
0
   if(type() == "DNS")
103
0
      {
104
0
      match_fn = std::mem_fn(&GeneralName::matches_dns);
105
106
0
      nam = alt_name.get_attribute("DNS");
107
108
0
      if(nam.empty())
109
0
         {
110
0
         nam = dn.get_attribute("CN");
111
0
         }
112
0
      }
113
0
   else if(type() == "DN")
114
0
      {
115
0
      match_fn = std::mem_fn(&GeneralName::matches_dn);
116
117
0
      nam.push_back(dn.to_string());
118
119
0
      const auto alt_dn = alt_name.dn();
120
0
      if(alt_dn.empty() == false)
121
0
         {
122
0
         nam.push_back(alt_dn.to_string());
123
0
         }
124
0
      }
125
0
   else if(type() == "IP")
126
0
      {
127
0
      match_fn = std::mem_fn(&GeneralName::matches_ip);
128
0
      nam = alt_name.get_attribute("IP");
129
0
      }
130
0
   else
131
0
      {
132
0
      return MatchResult::UnknownType;
133
0
      }
134
135
0
   if(nam.empty())
136
0
      {
137
0
      return MatchResult::NotFound;
138
0
      }
139
140
0
   bool some = false;
141
0
   bool all = true;
142
143
0
   for(const std::string& n: nam)
144
0
      {
145
0
      bool m = match_fn(this, n);
146
147
0
      some |= m;
148
0
      all &= m;
149
0
      }
150
151
0
   if(all)
152
0
      {
153
0
      return MatchResult::All;
154
0
      }
155
0
   else if(some)
156
0
      {
157
0
      return MatchResult::Some;
158
0
      }
159
0
   else
160
0
      {
161
0
      return MatchResult::None;
162
0
      }
163
0
   }
164
165
bool GeneralName::matches_dns(const std::string& nam) const
166
0
   {
167
0
   if(nam.size() == name().size())
168
0
      {
169
0
      return tolower_string(nam) == tolower_string(name());
170
0
      }
171
0
   else if(name().size() > nam.size())
172
0
      {
173
      // The constraint is longer than the issued name: not possibly a match
174
0
      return false;
175
0
      }
176
0
   else // name.size() < nam.size()
177
0
      {
178
      // constr is suffix of nam
179
0
      const std::string constr = name().front() == '.' ? name() : "." + name();
180
0
      const std::string substr = nam.substr(nam.size() - constr.size(), constr.size());
181
0
      return tolower_string(constr) == tolower_string(substr);
182
0
      }
183
0
   }
184
185
bool GeneralName::matches_dn(const std::string& nam) const
186
0
   {
187
0
   std::stringstream ss(nam);
188
0
   std::stringstream tt(name());
189
0
   X509_DN nam_dn, my_dn;
190
191
0
   ss >> nam_dn;
192
0
   tt >> my_dn;
193
194
0
   auto attr = nam_dn.get_attributes();
195
0
   bool ret = true;
196
0
   size_t trys = 0;
197
198
0
   for(const auto& c: my_dn.dn_info())
199
0
      {
200
0
      auto i = attr.equal_range(c.first);
201
202
0
      if(i.first != i.second)
203
0
         {
204
0
         trys += 1;
205
0
         ret = ret && (i.first->second == c.second.value());
206
0
         }
207
0
      }
208
209
0
   return trys > 0 && ret;
210
0
   }
211
212
bool GeneralName::matches_ip(const std::string& nam) const
213
0
   {
214
0
   uint32_t ip = string_to_ipv4(nam);
215
0
   std::vector<std::string> p = split_on(name(), '/');
216
217
0
   if(p.size() != 2)
218
0
      throw Decoding_Error("failed to parse IPv4 address");
219
220
0
   uint32_t net = string_to_ipv4(p.at(0));
221
0
   uint32_t mask = string_to_ipv4(p.at(1));
222
223
0
   return (ip & mask) == net;
224
0
   }
225
226
std::ostream& operator<<(std::ostream& os, const GeneralName& gn)
227
0
   {
228
0
   os << gn.type() << ":" << gn.name();
229
0
   return os;
230
0
   }
231
232
GeneralSubtree::GeneralSubtree(const std::string& str) : GeneralSubtree()
233
0
   {
234
0
   size_t p0, p1;
235
0
   const auto min = std::stoull(str, &p0, 10);
236
0
   const auto max = std::stoull(str.substr(p0 + 1), &p1, 10);
237
0
   GeneralName gn(str.substr(p0 + p1 + 2));
238
239
0
   if(p0 > 0 && p1 > 0)
240
0
      {
241
0
      m_minimum = static_cast<size_t>(min);
242
0
      m_maximum = static_cast<size_t>(max);
243
0
      m_base = gn;
244
0
      }
245
0
   else
246
0
      {
247
0
      throw Invalid_Argument("Failed to decode Name Constraint");
248
0
      }
249
0
   }
250
251
void GeneralSubtree::encode_into(DER_Encoder&) const
252
0
   {
253
0
   throw Not_Implemented("General Subtree encoding");
254
0
   }
255
256
void GeneralSubtree::decode_from(class BER_Decoder& ber)
257
5.04k
   {
258
5.04k
   ber.start_sequence()
259
5.04k
      .decode(m_base)
260
5.04k
      .decode_optional(m_minimum, ASN1_Type(0), ASN1_Class::ContextSpecific, size_t(0))
261
5.04k
   .end_cons();
262
263
5.04k
   if(m_minimum != 0)
264
303
     throw Decoding_Error("GeneralSubtree minimum must be 0");
265
266
4.74k
   m_maximum = std::numeric_limits<std::size_t>::max();
267
4.74k
   }
268
269
std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs)
270
0
   {
271
0
   os << gs.minimum() << "," << gs.maximum() << "," << gs.base();
272
0
   return os;
273
0
   }
274
}