summaryrefslogtreecommitdiffstats
path: root/net/cert/x509_cert_types_win.cc
blob: 1ec64c02f96caa785069bbec3af2697a6bcdd1b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/cert/x509_cert_types.h"

#include <windows.h>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "crypto/capi_util.h"
#include "crypto/wincrypt_shim.h"

#pragma comment(lib, "crypt32.lib")

namespace net {

namespace {

// A list of OIDs to decode. Any OID not on this list will be ignored for
// purposes of parsing.
const char* const kOIDs[] = {
  szOID_COMMON_NAME,
  szOID_LOCALITY_NAME,
  szOID_STATE_OR_PROVINCE_NAME,
  szOID_COUNTRY_NAME,
  szOID_STREET_ADDRESS,
  szOID_ORGANIZATION_NAME,
  szOID_ORGANIZATIONAL_UNIT_NAME,
  szOID_DOMAIN_COMPONENT
};

// Converts the value for |attribute| to an UTF-8 string, storing the result
// in |value|. Returns false if the string cannot be converted.
bool GetAttributeValue(PCERT_RDN_ATTR attribute,
                       std::string* value) {
  DWORD chars_needed = CertRDNValueToStrW(attribute->dwValueType,
                                          &attribute->Value, NULL, 0);
  if (chars_needed == 0)
    return false;
  if (chars_needed == 1) {
    // The value is actually an empty string (chars_needed includes a single
    // char for a NULL value). Don't bother converting - just clear the
    // string.
    value->clear();
    return true;
  }
  std::wstring wide_name;
  DWORD chars_written = CertRDNValueToStrW(
      attribute->dwValueType, &attribute->Value,
      base::WriteInto(&wide_name, chars_needed), chars_needed);
  if (chars_written <= 1)
    return false;
  wide_name.resize(chars_written - 1);
  *value = base::WideToUTF8(wide_name);
  return true;
}

// Adds a type+value pair to the appropriate vector from a C array.
// The array is keyed by the matching OIDs from kOIDS[].
bool AddTypeValuePair(PCERT_RDN_ATTR attribute,
                      std::vector<std::string>* values[]) {
  for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
    if (strcmp(attribute->pszObjId, kOIDs[oid]) == 0) {
      std::string value;
      if (!GetAttributeValue(attribute, &value))
        return false;
      values[oid]->push_back(value);
      break;
    }
  }
  return true;
}

// Stores the first string of the vector, if any, to *single_value.
void SetSingle(const std::vector<std::string>& values,
               std::string* single_value) {
  // We don't expect to have more than one CN, L, S, and C.
  LOG_IF(WARNING, values.size() > 1) << "Didn't expect multiple values";
  if (!values.empty())
    *single_value = values[0];
}

}  // namespace

bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data,
                                           size_t length) {
  DCHECK(ber_name_data);

  CRYPT_DECODE_PARA decode_para;
  decode_para.cbSize = sizeof(decode_para);
  decode_para.pfnAlloc = crypto::CryptAlloc;
  decode_para.pfnFree = crypto::CryptFree;
  CERT_NAME_INFO* name_info = NULL;
  DWORD name_info_size = 0;
  BOOL rv;
  rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                           WINCRYPT_X509_NAME,
                           reinterpret_cast<const BYTE*>(ber_name_data),
                           length,
                           CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
                           &decode_para,
                           &name_info, &name_info_size);
  if (!rv)
    return false;
  scoped_ptr<CERT_NAME_INFO, base::FreeDeleter> scoped_name_info(name_info);

  std::vector<std::string> common_names, locality_names, state_names,
      country_names;

  std::vector<std::string>* values[] = {
      &common_names, &locality_names,
      &state_names, &country_names,
      &this->street_addresses,
      &this->organization_names,
      &this->organization_unit_names,
      &this->domain_components
  };
  DCHECK(arraysize(kOIDs) == arraysize(values));

  for (DWORD cur_rdn = 0; cur_rdn < name_info->cRDN; ++cur_rdn) {
    PCERT_RDN rdn = &name_info->rgRDN[cur_rdn];
    for (DWORD cur_ava = 0; cur_ava < rdn->cRDNAttr; ++cur_ava) {
      PCERT_RDN_ATTR ava = &rdn->rgRDNAttr[cur_ava];
      if (!AddTypeValuePair(ava, values))
        return false;
    }
  }

  SetSingle(common_names, &this->common_name);
  SetSingle(locality_names, &this->locality_name);
  SetSingle(state_names, &this->state_or_province_name);
  SetSingle(country_names, &this->country_name);
  return true;
}

}  // namespace net