Coverage Report

Created: 2020-05-23 13:54

/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
102k
   {
34
102k
   if(str.empty())
35
1.89k
      return;
36
100k
37
100k
   m_rdn.push_back(std::make_pair(oid, str));
38
100k
   m_dn_bits.clear();
39
100k
   }
40
41
/*
42
* Get the attributes of this X509_DN
43
*/
44
std::multimap<OID, std::string> X509_DN::get_attributes() const
45
39.5k
   {
46
39.5k
   std::multimap<OID, std::string> retval;
47
39.5k
48
39.5k
   for(auto& i : m_rdn)
49
160k
      multimap_insert(retval, i.first, i.second.value());
50
39.5k
   return retval;
51
39.5k
   }
52
53
/*
54
* Get the contents of this X.500 Name
55
*/
56
std::multimap<std::string, std::string> X509_DN::contents() const
57
20.4k
   {
58
20.4k
   std::multimap<std::string, std::string> retval;
59
20.4k
60
20.4k
   for(auto& i : m_rdn)
61
66.1k
      {
62
66.1k
      multimap_insert(retval, i.first.to_formatted_string(), i.second.value());
63
66.1k
      }
64
20.4k
   return retval;
65
20.4k
   }
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
18.5k
   {
148
18.5k
   auto attr1 = dn1.get_attributes();
149
18.5k
   auto attr2 = dn2.get_attributes();
150
18.5k
151
18.5k
   if(attr1.size() != attr2.size()) return false;
152
17.2k
153
17.2k
   auto p1 = attr1.begin();
154
17.2k
   auto p2 = attr2.begin();
155
17.2k
156
65.2k
   while(true)
157
65.2k
      {
158
65.2k
      if(p1 == attr1.end() && p2 == attr2.end())
159
13.8k
         break;
160
51.3k
      if(p1 == attr1.end())      return false;
161
51.3k
      if(p2 == attr2.end())      return false;
162
51.3k
      if(p1->first != p2->first) return false;
163
50.1k
      if(!x500_name_cmp(p1->second, p2->second))
164
2.22k
         return false;
165
47.9k
      ++p1;
166
47.9k
      ++p2;
167
47.9k
      }
168
17.2k
   return true;
169
17.2k
   }
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.24k
   {
184
1.24k
   auto attr1 = dn1.get_attributes();
185
1.24k
   auto attr2 = dn2.get_attributes();
186
1.24k
187
1.24k
   // If they are not the same size, choose the smaller as the "lessor"
188
1.24k
   if(attr1.size() < attr2.size())
189
235
      return true;
190
1.00k
   if(attr1.size() > attr2.size())
191
235
      return false;
192
774
193
774
   // We know they are the same # of elements, now compare the OIDs:
194
774
   auto p1 = attr1.begin();
195
774
   auto p2 = attr2.begin();
196
774
197
1.56k
   while(p1 != attr1.end() && p2 != attr2.end())
198
1.00k
      {
199
1.00k
      if(p1->first != p2->first)
200
218
         {
201
218
         return (p1->first < p2->first);
202
218
         }
203
790
204
790
      ++p1;
205
790
      ++p2;
206
790
      }
207
774
208
774
   // We know this is true because maps have the same size
209
774
   BOTAN_ASSERT_NOMSG(p1 == attr1.end());
210
556
   BOTAN_ASSERT_NOMSG(p2 == attr2.end());
211
556
212
556
   // Now we know all elements have the same OIDs, compare
213
556
   // their string values:
214
556
215
556
   p1 = attr1.begin();
216
556
   p2 = attr2.begin();
217
758
   while(p1 != attr1.end() && p2 != attr2.end())
218
664
      {
219
664
      BOTAN_DEBUG_ASSERT(p1->first == p2->first);
220
664
221
664
      // They may be binary different but same by X.500 rules, check this
222
664
      if(!x500_name_cmp(p1->second, p2->second))
223
462
         {
224
462
         // If they are not (by X.500) the same string, pick the
225
462
         // lexicographic first as the lessor
226
462
         return (p1->second < p2->second);
227
462
         }
228
202
229
202
      ++p1;
230
202
      ++p2;
231
202
      }
232
556
233
556
   // if we reach here, then the DNs should be identical
234
556
   BOTAN_DEBUG_ASSERT(dn1 == dn2);
235
94
   return false;
236
556
   }
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
0
      /*
248
0
      If we decoded this from somewhere, encode it back exactly as
249
0
      we received it
250
0
      */
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
32.7k
   {
274
32.7k
   std::vector<uint8_t> bits;
275
32.7k
276
32.7k
   source.start_cons(SEQUENCE)
277
32.7k
      .raw_bytes(bits)
278
32.7k
   .end_cons();
279
32.7k
280
32.7k
   BER_Decoder sequence(bits);
281
32.7k
282
139k
   while(sequence.more_items())
283
106k
      {
284
106k
      BER_Decoder rdn = sequence.start_cons(SET);
285
106k
286
210k
      while(rdn.more_items())
287
103k
         {
288
103k
         OID oid;
289
103k
         ASN1_String str;
290
103k
291
103k
         rdn.start_cons(SEQUENCE)
292
103k
            .decode(oid)
293
103k
            .decode(str) // TODO support Any
294
103k
            .end_cons().verify_end("Invalid X509_DN, data follows RDN");
295
103k
296
103k
         add_attribute(oid, str);
297
103k
         }
298
106k
      }
299
32.7k
300
32.7k
   m_dn_bits = bits;
301
32.7k
   }
302
303
namespace {
304
305
std::string to_short_form(const OID& oid)
306
5.12k
   {
307
5.12k
   const std::string long_id = oid.to_formatted_string();
308
5.12k
309
5.12k
   if(long_id == "X520.CommonName")
310
89
      return "CN";
311
5.04k
312
5.04k
   if(long_id == "X520.Country")
313
286
      return "C";
314
4.75k
315
4.75k
   if(long_id == "X520.Organization")
316
210
      return "O";
317
4.54k
318
4.54k
   if(long_id == "X520.OrganizationalUnit")
319
148
      return "OU";
320
4.39k
321
4.39k
   return long_id;
322
4.39k
   }
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.07k
   {
335
3.07k
   auto info = dn.dn_info();
336
3.07k
337
8.20k
   for(size_t i = 0; i != info.size(); ++i)
338
5.12k
      {
339
5.12k
      out << to_short_form(info[i].first) << "=\"";
340
5.12k
      for(char c : info[i].second.value())
341
18.8k
         {
342
18.8k
         if(c == '\\' || c == '\"')
343
1.00k
            {
344
1.00k
            out << "\\";
345
1.00k
            }
346
18.8k
         out << c;
347
18.8k
         }
348
5.12k
      out << "\"";
349
5.12k
350
5.12k
      if(i + 1 < info.size())
351
2.39k
         {
352
2.39k
         out << ",";
353
2.39k
         }
354
5.12k
      }
355
3.07k
   return out;
356
3.07k
   }
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
}