Coverage Report

Created: 2020-02-14 15:38

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