Home | History | Annotate | Download | only in cert
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/cert/x509_cert_types.h"
      6 
      7 #include <windows.h>
      8 #include <wincrypt.h>
      9 
     10 #include "base/logging.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "crypto/capi_util.h"
     15 
     16 #pragma comment(lib, "crypt32.lib")
     17 
     18 namespace net {
     19 
     20 namespace {
     21 
     22 // A list of OIDs to decode. Any OID not on this list will be ignored for
     23 // purposes of parsing.
     24 const char* kOIDs[] = {
     25   szOID_COMMON_NAME,
     26   szOID_LOCALITY_NAME,
     27   szOID_STATE_OR_PROVINCE_NAME,
     28   szOID_COUNTRY_NAME,
     29   szOID_STREET_ADDRESS,
     30   szOID_ORGANIZATION_NAME,
     31   szOID_ORGANIZATIONAL_UNIT_NAME,
     32   szOID_DOMAIN_COMPONENT
     33 };
     34 
     35 // Converts the value for |attribute| to an UTF-8 string, storing the result
     36 // in |value|. Returns false if the string cannot be converted.
     37 bool GetAttributeValue(PCERT_RDN_ATTR attribute,
     38                        std::string* value) {
     39   DWORD chars_needed = CertRDNValueToStrW(attribute->dwValueType,
     40                                           &attribute->Value, NULL, 0);
     41   if (chars_needed == 0)
     42     return false;
     43   if (chars_needed == 1) {
     44     // The value is actually an empty string (chars_needed includes a single
     45     // char for a NULL value). Don't bother converting - just clear the
     46     // string.
     47     value->clear();
     48     return true;
     49   }
     50   std::wstring wide_name;
     51   DWORD chars_written = CertRDNValueToStrW(
     52       attribute->dwValueType, &attribute->Value,
     53       WriteInto(&wide_name, chars_needed), chars_needed);
     54   if (chars_written <= 1)
     55     return false;
     56   wide_name.resize(chars_written - 1);
     57   *value = base::WideToUTF8(wide_name);
     58   return true;
     59 }
     60 
     61 // Adds a type+value pair to the appropriate vector from a C array.
     62 // The array is keyed by the matching OIDs from kOIDS[].
     63 bool AddTypeValuePair(PCERT_RDN_ATTR attribute,
     64                       std::vector<std::string>* values[]) {
     65   for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
     66     if (strcmp(attribute->pszObjId, kOIDs[oid]) == 0) {
     67       std::string value;
     68       if (!GetAttributeValue(attribute, &value))
     69         return false;
     70       values[oid]->push_back(value);
     71       break;
     72     }
     73   }
     74   return true;
     75 }
     76 
     77 // Stores the first string of the vector, if any, to *single_value.
     78 void SetSingle(const std::vector<std::string>& values,
     79                std::string* single_value) {
     80   // We don't expect to have more than one CN, L, S, and C.
     81   LOG_IF(WARNING, values.size() > 1) << "Didn't expect multiple values";
     82   if (!values.empty())
     83     *single_value = values[0];
     84 }
     85 
     86 }  // namespace
     87 
     88 bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data,
     89                                            size_t length) {
     90   DCHECK(ber_name_data);
     91 
     92   CRYPT_DECODE_PARA decode_para;
     93   decode_para.cbSize = sizeof(decode_para);
     94   decode_para.pfnAlloc = crypto::CryptAlloc;
     95   decode_para.pfnFree = crypto::CryptFree;
     96   CERT_NAME_INFO* name_info = NULL;
     97   DWORD name_info_size = 0;
     98   BOOL rv;
     99   rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
    100                            X509_NAME,
    101                            reinterpret_cast<const BYTE*>(ber_name_data),
    102                            length,
    103                            CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
    104                            &decode_para,
    105                            &name_info, &name_info_size);
    106   if (!rv)
    107     return false;
    108   scoped_ptr<CERT_NAME_INFO, base::FreeDeleter> scoped_name_info(name_info);
    109 
    110   std::vector<std::string> common_names, locality_names, state_names,
    111       country_names;
    112 
    113   std::vector<std::string>* values[] = {
    114       &common_names, &locality_names,
    115       &state_names, &country_names,
    116       &this->street_addresses,
    117       &this->organization_names,
    118       &this->organization_unit_names,
    119       &this->domain_components
    120   };
    121   DCHECK(arraysize(kOIDs) == arraysize(values));
    122 
    123   for (DWORD cur_rdn = 0; cur_rdn < name_info->cRDN; ++cur_rdn) {
    124     PCERT_RDN rdn = &name_info->rgRDN[cur_rdn];
    125     for (DWORD cur_ava = 0; cur_ava < rdn->cRDNAttr; ++cur_ava) {
    126       PCERT_RDN_ATTR ava = &rdn->rgRDNAttr[cur_ava];
    127       if (!AddTypeValuePair(ava, values))
    128         return false;
    129     }
    130   }
    131 
    132   SetSingle(common_names, &this->common_name);
    133   SetSingle(locality_names, &this->locality_name);
    134   SetSingle(state_names, &this->state_or_province_name);
    135   SetSingle(country_names, &this->country_name);
    136   return true;
    137 }
    138 
    139 }  // namespace net
    140