Coverage Report

Created: 2020-06-30 13:58

/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
4.38k
   {
43
4.38k
   BER_Object obj = ber.get_next_object();
44
4.38k
45
4.38k
   if(obj.is_a(1, CONTEXT_SPECIFIC))
46
497
      {
47
497
      m_type = "RFC822";
48
497
      m_name = ASN1::to_string(obj);
49
497
      }
50
3.88k
   else if(obj.is_a(2, CONTEXT_SPECIFIC))
51
655
      {
52
655
      m_type = "DNS";
53
655
      m_name = ASN1::to_string(obj);
54
655
      }
55
3.23k
   else if(obj.is_a(6, CONTEXT_SPECIFIC))
56
2.19k
      {
57
2.19k
      m_type = "URI";
58
2.19k
      m_name = ASN1::to_string(obj);
59
2.19k
      }
60
1.04k
   else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)))
61
462
      {
62
462
      m_type = "DN";
63
462
      X509_DN dn;
64
462
      BER_Decoder dec(obj);
65
462
      std::stringstream ss;
66
462
67
462
      dn.decode_from(dec);
68
462
      ss << dn;
69
462
70
462
      m_name = ss.str();
71
462
      }
72
578
   else if(obj.is_a(7, CONTEXT_SPECIFIC))
73
172
      {
74
172
      if(obj.length() == 8)
75
101
         {
76
101
         m_type = "IP";
77
101
         m_name = ipv4_to_string(load_be<uint32_t>(obj.bits(), 0)) + "/" +
78
101
                  ipv4_to_string(load_be<uint32_t>(obj.bits(), 1));
79
101
         }
80
71
      else if(obj.length() == 32)
81
5
         {
82
5
         throw Decoding_Error("Unsupported IPv6 name constraint");
83
5
         }
84
66
      else
85
66
         {
86
66
         throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length()));
87
66
         }
88
406
      }
89
406
   else
90
406
      {
91
406
      throw Decoding_Error("Found unknown GeneralName type");
92
406
      }
93
4.38k
   }
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
      nam.push_back(dn.to_string());
119
0
120
0
      const auto alt_dn = alt_name.dn();
121
0
      if(alt_dn.empty() == false)
122
0
         {
123
0
         nam.push_back(alt_dn.to_string());
124
0
         }
125
0
      }
126
0
   else if(type() == "IP")
127
0
      {
128
0
      match_fn = std::mem_fn(&GeneralName::matches_ip);
129
0
      nam = alt_name.get_attribute("IP");
130
0
      }
131
0
   else
132
0
      {
133
0
      return MatchResult::UnknownType;
134
0
      }
135
0
136
0
   if(nam.empty())
137
0
      {
138
0
      return MatchResult::NotFound;
139
0
      }
140
0
141
0
   bool some = false;
142
0
   bool all = true;
143
0
144
0
   for(const std::string& n: nam)
145
0
      {
146
0
      bool m = match_fn(this, n);
147
0
148
0
      some |= m;
149
0
      all &= m;
150
0
      }
151
0
152
0
   if(all)
153
0
      {
154
0
      return MatchResult::All;
155
0
      }
156
0
   else if(some)
157
0
      {
158
0
      return MatchResult::Some;
159
0
      }
160
0
   else
161
0
      {
162
0
      return MatchResult::None;
163
0
      }
164
0
   }
165
166
bool GeneralName::matches_dns(const std::string& nam) const
167
0
   {
168
0
   if(nam.size() == name().size())
169
0
      {
170
0
      return nam == name();
171
0
      }
172
0
   else if(name().size() > nam.size())
173
0
      {
174
0
      return false;
175
0
      }
176
0
   else // name.size() < nam.size()
177
0
      {
178
0
      std::string constr = name().front() == '.' ? name() : "." + name();
179
0
      // constr is suffix of nam
180
0
      return constr == nam.substr(nam.size() - constr.size(), constr.size());
181
0
      }
182
0
   }
183
184
bool GeneralName::matches_dn(const std::string& nam) const
185
0
   {
186
0
   std::stringstream ss(nam);
187
0
   std::stringstream tt(name());
188
0
   X509_DN nam_dn, my_dn;
189
0
190
0
   ss >> nam_dn;
191
0
   tt >> my_dn;
192
0
193
0
   auto attr = nam_dn.get_attributes();
194
0
   bool ret = true;
195
0
   size_t trys = 0;
196
0
197
0
   for(const auto& c: my_dn.dn_info())
198
0
      {
199
0
      auto i = attr.equal_range(c.first);
200
0
201
0
      if(i.first != i.second)
202
0
         {
203
0
         trys += 1;
204
0
         ret = ret && (i.first->second == c.second.value());
205
0
         }
206
0
      }
207
0
208
0
   return trys > 0 && ret;
209
0
   }
210
211
bool GeneralName::matches_ip(const std::string& nam) const
212
0
   {
213
0
   uint32_t ip = string_to_ipv4(nam);
214
0
   std::vector<std::string> p = split_on(name(), '/');
215
0
216
0
   if(p.size() != 2)
217
0
      throw Decoding_Error("failed to parse IPv4 address");
218
0
219
0
   uint32_t net = string_to_ipv4(p.at(0));
220
0
   uint32_t mask = string_to_ipv4(p.at(1));
221
0
222
0
   return (ip & mask) == net;
223
0
   }
224
225
std::ostream& operator<<(std::ostream& os, const GeneralName& gn)
226
226
   {
227
226
   os << gn.type() << ":" << gn.name();
228
226
   return os;
229
226
   }
230
231
GeneralSubtree::GeneralSubtree(const std::string& str) : GeneralSubtree()
232
0
   {
233
0
   size_t p0, p1;
234
0
   const auto min = std::stoull(str, &p0, 10);
235
0
   const auto max = std::stoull(str.substr(p0 + 1), &p1, 10);
236
0
   GeneralName gn(str.substr(p0 + p1 + 2));
237
0
238
0
   if(p0 > 0 && p1 > 0)
239
0
      {
240
0
      m_minimum = static_cast<size_t>(min);
241
0
      m_maximum = static_cast<size_t>(max);
242
0
      m_base = gn;
243
0
      }
244
0
   else
245
0
      {
246
0
      throw Invalid_Argument("Failed to decode Name Constraint");
247
0
      }
248
0
   }
249
250
void GeneralSubtree::encode_into(DER_Encoder&) const
251
0
   {
252
0
   throw Not_Implemented("General Subtree encoding");
253
0
   }
254
255
void GeneralSubtree::decode_from(class BER_Decoder& ber)
256
4.97k
   {
257
4.97k
   ber.start_cons(SEQUENCE)
258
4.97k
      .decode(m_base)
259
4.97k
      .decode_optional(m_minimum,ASN1_Tag(0), CONTEXT_SPECIFIC,size_t(0))
260
4.97k
   .end_cons();
261
4.97k
262
4.97k
   if(m_minimum != 0)
263
177
     throw Decoding_Error("GeneralSubtree minimum must be 0");
264
4.79k
265
4.79k
   m_maximum = std::numeric_limits<std::size_t>::max();
266
4.79k
   }
267
268
std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs)
269
226
   {
270
226
   os << gs.minimum() << "," << gs.maximum() << "," << gs.base();
271
226
   return os;
272
226
   }
273
}