Coverage Report

Created: 2020-09-16 07:52

/src/botan/src/lib/x509/x509_dn.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* X509_DN
3
* (C) 1999-2007,2018 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7
8
#include <botan/x509_dn.h>
9
#include <botan/der_enc.h>
10
#include <botan/ber_dec.h>
11
#include <botan/parsing.h>
12
#include <botan/internal/stl_util.h>
13
#include <botan/oids.h>
14
#include <ostream>
15
#include <sstream>
16
#include <cctype>
17
18
namespace Botan {
19
20
/*
21
* Add an attribute to a X509_DN
22
*/
23
void X509_DN::add_attribute(const std::string& type,
24
                            const std::string& str)
25
0
   {
26
0
   add_attribute(OID::from_string(type), str);
27
0
   }
28
29
/*
30
* Add an attribute to a X509_DN
31
*/
32
void X509_DN::add_attribute(const OID& oid, const ASN1_String& str)
33
112k
   {
34
112k
   if(str.empty())
35
2.11k
      return;
36
110k
37
110k
   m_rdn.push_back(std::make_pair(oid, str));
38
110k
   m_dn_bits.clear();
39
110k
   }
40
41
/*
42
* Get the attributes of this X509_DN
43
*/
44
std::multimap<OID, std::string> X509_DN::get_attributes() const
45
46.0k
   {
46
46.0k
   std::multimap<OID, std::string> retval;
47
46.0k
48
46.0k
   for(auto& i : m_rdn)
49
179k
      multimap_insert(retval, i.first, i.second.value());
50
46.0k
   return retval;
51
46.0k
   }
52
53
/*
54
* Get the contents of this X.500 Name
55
*/
56
std::multimap<std::string, std::string> X509_DN::contents() const
57
23.2k
   {
58
23.2k
   std::multimap<std::string, std::string> retval;
59
23.2k
60
23.2k
   for(auto& i : m_rdn)
61
74.4k
      {
62
74.4k
      multimap_insert(retval, i.first.to_formatted_string(), i.second.value());
63
74.4k
      }
64
23.2k
   return retval;
65
23.2k
   }
66
67
bool X509_DN::has_field(const std::string& attr) const
68
0
   {
69
0
   const OID o = OIDS::str2oid_or_empty(deref_info_field(attr));
70
0
   if(o.has_value())
71
0
      return has_field(o);
72
0
   else
73
0
      return false;
74
0
   }
75
76
bool X509_DN::has_field(const OID& oid) const
77
0
   {
78
0
   for(auto& i : m_rdn)
79
0
      {
80
0
      if(i.first == oid)
81
0
         return true;
82
0
      }
83
0
84
0
   return false;
85
0
   }
86
87
std::string X509_DN::get_first_attribute(const std::string& attr) const
88
0
   {
89
0
   const OID oid = OID::from_string(deref_info_field(attr));
90
0
   return get_first_attribute(oid).value();
91
0
   }
92
93
ASN1_String X509_DN::get_first_attribute(const OID& oid) const
94
0
   {
95
0
   for(auto& i : m_rdn)
96
0
      {
97
0
      if(i.first == oid)
98
0
         {
99
0
         return i.second;
100
0
         }
101
0
      }
102
0
103
0
   return ASN1_String();
104
0
   }
105
106
/*
107
* Get a single attribute type
108
*/
109
std::vector<std::string> X509_DN::get_attribute(const std::string& attr) const
110
0
   {
111
0
   const OID oid = OID::from_string(deref_info_field(attr));
112
0
113
0
   std::vector<std::string> values;
114
0
115
0
   for(auto& i : m_rdn)
116
0
      {
117
0
      if(i.first == oid)
118
0
         {
119
0
         values.push_back(i.second.value());
120
0
         }
121
0
      }
122
0
123
0
   return values;
124
0
   }
125
126
/*
127
* Deref aliases in a subject/issuer info request
128
*/
129
std::string X509_DN::deref_info_field(const std::string& info)
130
0
   {
131
0
   if(info == "Name" || info == "CommonName" || info == "CN") return "X520.CommonName";
132
0
   if(info == "SerialNumber" || info == "SN")                 return "X520.SerialNumber";
133
0
   if(info == "Country" || info == "C")                       return "X520.Country";
134
0
   if(info == "Organization" || info == "O")                  return "X520.Organization";
135
0
   if(info == "Organizational Unit" || info == "OrgUnit" || info == "OU")
136
0
      return "X520.OrganizationalUnit";
137
0
   if(info == "Locality" || info == "L")                      return "X520.Locality";
138
0
   if(info == "State" || info == "Province" || info == "ST")  return "X520.State";
139
0
   if(info == "Email")                                        return "RFC822";
140
0
   return info;
141
0
   }
142
143
/*
144
* Compare two X509_DNs for equality
145
*/
146
bool operator==(const X509_DN& dn1, const X509_DN& dn2)
147
21.5k
   {
148
21.5k
   auto attr1 = dn1.get_attributes();
149
21.5k
   auto attr2 = dn2.get_attributes();
150
21.5k
151
21.5k
   if(attr1.size() != attr2.size()) return false;
152
20.3k
153
20.3k
   auto p1 = attr1.begin();
154
20.3k
   auto p2 = attr2.begin();
155
20.3k
156
75.8k
   while(true)
157
75.8k
      {
158
75.8k
      if(p1 == attr1.end() && p2 == attr2.end())
159
16.0k
         break;
160
59.8k
      if(p1 == attr1.end())      return false;
161
59.8k
      if(p2 == attr2.end())      return false;
162
59.8k
      if(p1->first != p2->first) return false;
163
57.9k
      if(!x500_name_cmp(p1->second, p2->second))
164
2.38k
         return false;
165
55.5k
      ++p1;
166
55.5k
      ++p2;
167
55.5k
      }
168
16.0k
   return true;
169
20.3k
   }
170
171
/*
172
* Compare two X509_DNs for inequality
173
*/
174
bool operator!=(const X509_DN& dn1, const X509_DN& dn2)
175
0
   {
176
0
   return !(dn1 == dn2);
177
0
   }
178
179
/*
180
* Induce an arbitrary ordering on DNs
181
*/
182
bool operator<(const X509_DN& dn1, const X509_DN& dn2)
183
1.42k
   {
184
1.42k
   auto attr1 = dn1.get_attributes();
185
1.42k
   auto attr2 = dn2.get_attributes();
186
1.42k
187
   // If they are not the same size, choose the smaller as the "lessor"
188
1.42k
   if(attr1.size() < attr2.size())
189
267
      return true;
190
1.15k
   if(attr1.size() > attr2.size())
191
267
      return false;
192
888
193
   // We know they are the same # of elements, now compare the OIDs:
194
888
   auto p1 = attr1.begin();
195
888
   auto p2 = attr2.begin();
196
888
197
1.78k
   while(p1 != attr1.end() && p2 != attr2.end())
198
1.13k
      {
199
1.13k
      if(p1->first != p2->first)
200
236
         {
201
236
         return (p1->first < p2->first);
202
236
         }
203
898
204
898
      ++p1;
205
898
      ++p2;
206
898
      }
207
888
208
   // We know this is true because maps have the same size
209
652
   BOTAN_ASSERT_NOMSG(p1 == attr1.end());
210
652
   BOTAN_ASSERT_NOMSG(p2 == attr2.end());
211
652
212
   // Now we know all elements have the same OIDs, compare
213
   // their string values:
214
652
215
652
   p1 = attr1.begin();
216
652
   p2 = attr2.begin();
217
906
   while(p1 != attr1.end() && p2 != attr2.end())
218
766
      {
219
766
      BOTAN_DEBUG_ASSERT(p1->first == p2->first);
220
766
221
      // They may be binary different but same by X.500 rules, check this
222
766
      if(!x500_name_cmp(p1->second, p2->second))
223
512
         {
224
         // If they are not (by X.500) the same string, pick the
225
         // lexicographic first as the lessor
226
512
         return (p1->second < p2->second);
227
512
         }
228
254
229
254
      ++p1;
230
254
      ++p2;
231
254
      }
232
652
233
   // if we reach here, then the DNs should be identical
234
140
   BOTAN_DEBUG_ASSERT(dn1 == dn2);
235
140
   return false;
236
652
   }
237
238
/*
239
* DER encode a DistinguishedName
240
*/
241
void X509_DN::encode_into(DER_Encoder& der) const
242
0
   {
243
0
   der.start_cons(SEQUENCE);
244
0
245
0
   if(!m_dn_bits.empty())
246
0
      {
247
      /*
248
      If we decoded this from somewhere, encode it back exactly as
249
      we received it
250
      */
251
0
      der.raw_bytes(m_dn_bits);
252
0
      }
253
0
   else
254
0
      {
255
0
      for(const auto& dn : m_rdn)
256
0
         {
257
0
         der.start_cons(SET)
258
0
            .start_cons(SEQUENCE)
259
0
            .encode(dn.first)
260
0
            .encode(dn.second)
261
0
            .end_cons()
262
0
         .end_cons();
263
0
         }
264
0
      }
265
0
266
0
   der.end_cons();
267
0
   }
268
269
/*
270
* Decode a BER encoded DistinguishedName
271
*/
272
void X509_DN::decode_from(BER_Decoder& source)
273
37.7k
   {
274
37.7k
   std::vector<uint8_t> bits;
275
37.7k
276
37.7k
   source.start_cons(SEQUENCE)
277
37.7k
      .raw_bytes(bits)
278
37.7k
   .end_cons();
279
37.7k
280
37.7k
   BER_Decoder sequence(bits);
281
37.7k
282
156k
   while(sequence.more_items())
283
118k
      {
284
118k
      BER_Decoder rdn = sequence.start_cons(SET);
285
118k
286
232k
      while(rdn.more_items())
287
114k
         {
288
114k
         OID oid;
289
114k
         ASN1_String str;
290
114k
291
114k
         rdn.start_cons(SEQUENCE)
292
114k
            .decode(oid)
293
114k
            .decode(str) // TODO support Any
294
114k
            .end_cons().verify_end("Invalid X509_DN, data follows RDN");
295
114k
296
114k
         add_attribute(oid, str);
297
114k
         }
298
118k
      }
299
37.7k
300
37.7k
   m_dn_bits = bits;
301
37.7k
   }
302
303
namespace {
304
305
std::string to_short_form(const OID& oid)
306
6.04k
   {
307
6.04k
   const std::string long_id = oid.to_formatted_string();
308
6.04k
309
6.04k
   if(long_id == "X520.CommonName")
310
91
      return "CN";
311
5.95k
312
5.95k
   if(long_id == "X520.Country")
313
355
      return "C";
314
5.59k
315
5.59k
   if(long_id == "X520.Organization")
316
237
      return "O";
317
5.36k
318
5.36k
   if(long_id == "X520.OrganizationalUnit")
319
119
      return "OU";
320
5.24k
321
5.24k
   return long_id;
322
5.24k
   }
323
324
}
325
326
std::string X509_DN::to_string() const
327
0
   {
328
0
   std::ostringstream out;
329
0
   out << *this;
330
0
   return out.str();
331
0
   }
332
333
std::ostream& operator<<(std::ostream& out, const X509_DN& dn)
334
3.76k
   {
335
3.76k
   auto info = dn.dn_info();
336
3.76k
337
9.80k
   for(size_t i = 0; i != info.size(); ++i)
338
6.04k
      {
339
6.04k
      out << to_short_form(info[i].first) << "=\"";
340
6.04k
      for(char c : info[i].second.value())
341
22.1k
         {
342
22.1k
         if(c == '\\' || c == '\"')
343
1.01k
            {
344
1.01k
            out << "\\";
345
1.01k
            }
346
22.1k
         out << c;
347
22.1k
         }
348
6.04k
      out << "\"";
349
6.04k
350
6.04k
      if(i + 1 < info.size())
351
2.80k
         {
352
2.80k
         out << ",";
353
2.80k
         }
354
6.04k
      }
355
3.76k
   return out;
356
3.76k
   }
357
358
std::istream& operator>>(std::istream& in, X509_DN& dn)
359
0
   {
360
0
   in >> std::noskipws;
361
0
   do
362
0
      {
363
0
      std::string key;
364
0
      std::string val;
365
0
      char c;
366
0
367
0
      while(in.good())
368
0
         {
369
0
         in >> c;
370
0
371
0
         if(std::isspace(c) && key.empty())
372
0
            continue;
373
0
         else if(!std::isspace(c))
374
0
            {
375
0
            key.push_back(c);
376
0
            break;
377
0
            }
378
0
         else
379
0
            break;
380
0
         }
381
0
382
0
      while(in.good())
383
0
         {
384
0
         in >> c;
385
0
386
0
         if(!std::isspace(c) && c != '=')
387
0
            key.push_back(c);
388
0
         else if(c == '=')
389
0
            break;
390
0
         else
391
0
            throw Invalid_Argument("Ill-formed X.509 DN");
392
0
         }
393
0
394
0
      bool in_quotes = false;
395
0
      while(in.good())
396
0
         {
397
0
         in >> c;
398
0
399
0
         if(std::isspace(c))
400
0
            {
401
0
            if(!in_quotes && !val.empty())
402
0
               break;
403
0
            else if(in_quotes)
404
0
               val.push_back(' ');
405
0
            }
406
0
         else if(c == '"')
407
0
            in_quotes = !in_quotes;
408
0
         else if(c == '\\')
409
0
            {
410
0
            if(in.good())
411
0
               in >> c;
412
0
            val.push_back(c);
413
0
            }
414
0
         else if(c == ',' && !in_quotes)
415
0
            break;
416
0
         else
417
0
            val.push_back(c);
418
0
         }
419
0
420
0
      if(!key.empty() && !val.empty())
421
0
         dn.add_attribute(X509_DN::deref_info_field(key),val);
422
0
      else
423
0
         break;
424
0
      }
425
0
   while(in.good());
426
0
   return in;
427
0
   }
428
}