Coverage Report

Created: 2021-01-13 07:05

/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/pkix_types.h>
9
#include <botan/der_enc.h>
10
#include <botan/ber_dec.h>
11
#include <botan/internal/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
113k
   {
34
113k
   if(str.empty())
35
4.60k
      return;
36
37
109k
   m_rdn.push_back(std::make_pair(oid, str));
38
109k
   m_dn_bits.clear();
39
109k
   }
40
41
/*
42
* Get the attributes of this X509_DN
43
*/
44
std::multimap<OID, std::string> X509_DN::get_attributes() const
45
48.8k
   {
46
48.8k
   std::multimap<OID, std::string> retval;
47
48
48.8k
   for(auto& i : m_rdn)
49
182k
      multimap_insert(retval, i.first, i.second.value());
50
48.8k
   return retval;
51
48.8k
   }
52
53
/*
54
* Get the contents of this X.500 Name
55
*/
56
std::multimap<std::string, std::string> X509_DN::contents() const
57
0
   {
58
0
   std::multimap<std::string, std::string> retval;
59
60
0
   for(auto& i : m_rdn)
61
0
      {
62
0
      multimap_insert(retval, i.first.to_formatted_string(), i.second.value());
63
0
      }
64
0
   return retval;
65
0
   }
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
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
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
113
0
   std::vector<std::string> values;
114
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
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
22.9k
   {
148
22.9k
   auto attr1 = dn1.get_attributes();
149
22.9k
   auto attr2 = dn2.get_attributes();
150
151
22.9k
   if(attr1.size() != attr2.size()) return false;
152
153
21.6k
   auto p1 = attr1.begin();
154
21.6k
   auto p2 = attr2.begin();
155
156
80.0k
   while(true)
157
80.0k
      {
158
80.0k
      if(p1 == attr1.end() && p2 == attr2.end())
159
17.4k
         break;
160
62.5k
      if(p1 == attr1.end())      return false;
161
62.5k
      if(p2 == attr2.end())      return false;
162
62.5k
      if(p1->first != p2->first) return false;
163
60.7k
      if(!x500_name_cmp(p1->second, p2->second))
164
2.39k
         return false;
165
58.3k
      ++p1;
166
58.3k
      ++p2;
167
58.3k
      }
168
17.4k
   return true;
169
21.6k
   }
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.50k
   {
184
1.50k
   auto attr1 = dn1.get_attributes();
185
1.50k
   auto attr2 = dn2.get_attributes();
186
187
   // If they are not the same size, choose the smaller as the "lessor"
188
1.50k
   if(attr1.size() < attr2.size())
189
283
      return true;
190
1.21k
   if(attr1.size() > attr2.size())
191
283
      return false;
192
193
   // We know they are the same # of elements, now compare the OIDs:
194
934
   auto p1 = attr1.begin();
195
934
   auto p2 = attr2.begin();
196
197
2.07k
   while(p1 != attr1.end() && p2 != attr2.end())
198
1.35k
      {
199
1.35k
      if(p1->first != p2->first)
200
216
         {
201
216
         return (p1->first < p2->first);
202
216
         }
203
204
1.13k
      ++p1;
205
1.13k
      ++p2;
206
1.13k
      }
207
208
   // We know this is true because maps have the same size
209
718
   BOTAN_ASSERT_NOMSG(p1 == attr1.end());
210
718
   BOTAN_ASSERT_NOMSG(p2 == attr2.end());
211
212
   // Now we know all elements have the same OIDs, compare
213
   // their string values:
214
215
718
   p1 = attr1.begin();
216
718
   p2 = attr2.begin();
217
1.14k
   while(p1 != attr1.end() && p2 != attr2.end())
218
970
      {
219
970
      BOTAN_DEBUG_ASSERT(p1->first == p2->first);
220
221
      // They may be binary different but same by X.500 rules, check this
222
970
      if(!x500_name_cmp(p1->second, p2->second))
223
540
         {
224
         // If they are not (by X.500) the same string, pick the
225
         // lexicographic first as the lessor
226
540
         return (p1->second < p2->second);
227
540
         }
228
229
430
      ++p1;
230
430
      ++p2;
231
430
      }
232
233
   // if we reach here, then the DNs should be identical
234
178
   BOTAN_DEBUG_ASSERT(dn1 == dn2);
235
178
   return false;
236
718
   }
237
238
std::vector<uint8_t> X509_DN::DER_encode() const
239
0
   {
240
0
   std::vector<uint8_t> result;
241
0
   DER_Encoder der(result);
242
0
   this->encode_into(der);
243
0
   return result;
244
0
   }
245
246
/*
247
* DER encode a DistinguishedName
248
*/
249
void X509_DN::encode_into(DER_Encoder& der) const
250
0
   {
251
0
   der.start_sequence();
252
253
0
   if(!m_dn_bits.empty())
254
0
      {
255
      /*
256
      If we decoded this from somewhere, encode it back exactly as
257
      we received it
258
      */
259
0
      der.raw_bytes(m_dn_bits);
260
0
      }
261
0
   else
262
0
      {
263
0
      for(const auto& dn : m_rdn)
264
0
         {
265
0
         der.start_cons(ASN1_Type::SET)
266
0
            .start_sequence()
267
0
            .encode(dn.first)
268
0
            .encode(dn.second)
269
0
            .end_cons()
270
0
         .end_cons();
271
0
         }
272
0
      }
273
274
0
   der.end_cons();
275
0
   }
276
277
/*
278
* Decode a BER encoded DistinguishedName
279
*/
280
void X509_DN::decode_from(BER_Decoder& source)
281
46.9k
   {
282
46.9k
   std::vector<uint8_t> bits;
283
284
46.9k
   source.start_sequence()
285
46.9k
      .raw_bytes(bits)
286
46.9k
   .end_cons();
287
288
46.9k
   BER_Decoder sequence(bits);
289
290
46.9k
   m_rdn.clear();
291
292
167k
   while(sequence.more_items())
293
120k
      {
294
120k
      BER_Decoder rdn = sequence.start_cons(ASN1_Type::SET);
295
296
235k
      while(rdn.more_items())
297
115k
         {
298
115k
         OID oid;
299
115k
         ASN1_String str;
300
301
115k
         rdn.start_sequence()
302
115k
            .decode(oid)
303
115k
            .decode(str) // TODO support Any
304
115k
            .end_cons().verify_end("Invalid X509_DN, data follows RDN");
305
306
115k
         add_attribute(oid, str);
307
115k
         }
308
120k
      }
309
310
   // Have to assign last as add_attribute zaps m_dn_bits
311
46.9k
   m_dn_bits = bits;
312
46.9k
   }
313
314
namespace {
315
316
std::string to_short_form(const OID& oid)
317
5.86k
   {
318
5.86k
   const std::string long_id = oid.to_formatted_string();
319
320
5.86k
   if(long_id == "X520.CommonName")
321
89
      return "CN";
322
323
5.77k
   if(long_id == "X520.Country")
324
172
      return "C";
325
326
5.60k
   if(long_id == "X520.Organization")
327
143
      return "O";
328
329
5.46k
   if(long_id == "X520.OrganizationalUnit")
330
124
      return "OU";
331
332
5.33k
   return long_id;
333
5.33k
   }
334
335
}
336
337
std::string X509_DN::to_string() const
338
0
   {
339
0
   std::ostringstream out;
340
0
   out << *this;
341
0
   return out.str();
342
0
   }
343
344
std::ostream& operator<<(std::ostream& out, const X509_DN& dn)
345
3.78k
   {
346
3.78k
   auto info = dn.dn_info();
347
348
9.65k
   for(size_t i = 0; i != info.size(); ++i)
349
5.86k
      {
350
5.86k
      out << to_short_form(info[i].first) << "=\"";
351
5.86k
      for(char c : info[i].second.value())
352
23.7k
         {
353
23.7k
         if(c == '\\' || c == '\"')
354
968
            {
355
968
            out << "\\";
356
968
            }
357
23.7k
         out << c;
358
23.7k
         }
359
5.86k
      out << "\"";
360
361
5.86k
      if(i + 1 < info.size())
362
2.55k
         {
363
2.55k
         out << ",";
364
2.55k
         }
365
5.86k
      }
366
3.78k
   return out;
367
3.78k
   }
368
369
std::istream& operator>>(std::istream& in, X509_DN& dn)
370
0
   {
371
0
   in >> std::noskipws;
372
0
   do
373
0
      {
374
0
      std::string key;
375
0
      std::string val;
376
0
      char c;
377
378
0
      while(in.good())
379
0
         {
380
0
         in >> c;
381
382
0
         if(std::isspace(c) && key.empty())
383
0
            continue;
384
0
         else if(!std::isspace(c))
385
0
            {
386
0
            key.push_back(c);
387
0
            break;
388
0
            }
389
0
         else
390
0
            break;
391
0
         }
392
393
0
      while(in.good())
394
0
         {
395
0
         in >> c;
396
397
0
         if(!std::isspace(c) && c != '=')
398
0
            key.push_back(c);
399
0
         else if(c == '=')
400
0
            break;
401
0
         else
402
0
            throw Invalid_Argument("Ill-formed X.509 DN");
403
0
         }
404
405
0
      bool in_quotes = false;
406
0
      while(in.good())
407
0
         {
408
0
         in >> c;
409
410
0
         if(std::isspace(c))
411
0
            {
412
0
            if(!in_quotes && !val.empty())
413
0
               break;
414
0
            else if(in_quotes)
415
0
               val.push_back(' ');
416
0
            }
417
0
         else if(c == '"')
418
0
            in_quotes = !in_quotes;
419
0
         else if(c == '\\')
420
0
            {
421
0
            if(in.good())
422
0
               in >> c;
423
0
            val.push_back(c);
424
0
            }
425
0
         else if(c == ',' && !in_quotes)
426
0
            break;
427
0
         else
428
0
            val.push_back(c);
429
0
         }
430
431
0
      if(!key.empty() && !val.empty())
432
0
         dn.add_attribute(X509_DN::deref_info_field(key),val);
433
0
      else
434
0
         break;
435
0
      }
436
0
   while(in.good());
437
0
   return in;
438
0
   }
439
}