Home | History | Annotate | Download | only in options
      1 // Copyright 2013 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 "chrome/browser/chromeos/options/cert_library.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/command_line.h"
     10 #include "base/i18n/string_compare.h"
     11 #include "base/memory/weak_ptr.h"
     12 #include "base/observer_list_threadsafe.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/string_util.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "chrome/browser/browser_process.h"  // g_browser_process
     17 #include "chrome/common/chrome_switches.h"
     18 #include "chrome/common/net/x509_certificate_model.h"
     19 #include "chrome/grit/generated_resources.h"
     20 #include "chromeos/dbus/cryptohome_client.h"
     21 #include "chromeos/dbus/dbus_thread_manager.h"
     22 #include "chromeos/login/login_state.h"
     23 #include "chromeos/network/onc/onc_utils.h"
     24 #include "content/public/browser/browser_thread.h"
     25 #include "crypto/nss_util.h"
     26 #include "net/cert/cert_database.h"
     27 #include "net/cert/nss_cert_database.h"
     28 #include "third_party/icu/source/i18n/unicode/coll.h"  // icu::Collator
     29 #include "ui/base/l10n/l10n_util.h"
     30 #include "ui/base/l10n/l10n_util_collator.h"
     31 
     32 namespace chromeos {
     33 
     34 namespace {
     35 
     36 // Root CA certificates that are built into Chrome use this token name.
     37 const char kRootCertificateTokenName[] = "Builtin Object Token";
     38 
     39 base::string16 GetDisplayString(net::X509Certificate* cert,
     40                                 bool hardware_backed) {
     41   std::string org;
     42   if (!cert->subject().organization_names.empty())
     43     org = cert->subject().organization_names[0];
     44   if (org.empty())
     45     org = cert->subject().GetDisplayName();
     46   base::string16 issued_by = base::UTF8ToUTF16(
     47       x509_certificate_model::GetIssuerCommonName(cert->os_cert_handle(),
     48                                                   org));  // alternative text
     49   base::string16 issued_to = base::UTF8ToUTF16(
     50       x509_certificate_model::GetCertNameOrNickname(cert->os_cert_handle()));
     51 
     52   if (hardware_backed) {
     53     return l10n_util::GetStringFUTF16(
     54         IDS_CERT_MANAGER_HARDWARE_BACKED_KEY_FORMAT_LONG,
     55         issued_by,
     56         issued_to,
     57         l10n_util::GetStringUTF16(IDS_CERT_MANAGER_HARDWARE_BACKED));
     58   } else {
     59     return l10n_util::GetStringFUTF16(
     60         IDS_CERT_MANAGER_KEY_FORMAT_LONG,
     61         issued_by,
     62         issued_to);
     63   }
     64 }
     65 
     66 std::string CertToPEM(const net::X509Certificate& cert) {
     67   std::string pem_encoded_cert;
     68   if (!net::X509Certificate::GetPEMEncoded(cert.os_cert_handle(),
     69                                            &pem_encoded_cert)) {
     70     LOG(ERROR) << "Couldn't PEM-encode certificate";
     71     return std::string();
     72   }
     73   return pem_encoded_cert;
     74 }
     75 
     76 }  // namespace
     77 
     78 class CertNameComparator {
     79  public:
     80   explicit CertNameComparator(icu::Collator* collator)
     81       : collator_(collator) {
     82   }
     83 
     84   bool operator()(const scoped_refptr<net::X509Certificate>& lhs,
     85                   const scoped_refptr<net::X509Certificate>& rhs) const {
     86     base::string16 lhs_name = GetDisplayString(lhs.get(), false);
     87     base::string16 rhs_name = GetDisplayString(rhs.get(), false);
     88     if (collator_ == NULL)
     89       return lhs_name < rhs_name;
     90     return base::i18n::CompareString16WithCollator(
     91         collator_, lhs_name, rhs_name) == UCOL_LESS;
     92   }
     93 
     94  private:
     95   icu::Collator* collator_;
     96 };
     97 
     98 static CertLibrary* g_cert_library = NULL;
     99 
    100 // static
    101 void CertLibrary::Initialize() {
    102   CHECK(!g_cert_library);
    103   g_cert_library = new CertLibrary();
    104 }
    105 
    106 // static
    107 void CertLibrary::Shutdown() {
    108   CHECK(g_cert_library);
    109   delete g_cert_library;
    110   g_cert_library = NULL;
    111 }
    112 
    113 // static
    114 CertLibrary* CertLibrary::Get() {
    115   CHECK(g_cert_library) << "CertLibrary::Get() called before Initialize()";
    116   return g_cert_library;
    117 }
    118 
    119 // static
    120 bool CertLibrary::IsInitialized() {
    121   return g_cert_library;
    122 }
    123 
    124 CertLibrary::CertLibrary() {
    125   CertLoader::Get()->AddObserver(this);
    126 }
    127 
    128 CertLibrary::~CertLibrary() {
    129   CertLoader::Get()->RemoveObserver(this);
    130 }
    131 
    132 void CertLibrary::AddObserver(CertLibrary::Observer* observer) {
    133   observer_list_.AddObserver(observer);
    134 }
    135 
    136 void CertLibrary::RemoveObserver(CertLibrary::Observer* observer) {
    137   observer_list_.RemoveObserver(observer);
    138 }
    139 
    140 bool CertLibrary::CertificatesLoading() const {
    141   return CertLoader::Get()->CertificatesLoading();
    142 }
    143 
    144 bool CertLibrary::CertificatesLoaded() const {
    145   return CertLoader::Get()->certificates_loaded();
    146 }
    147 
    148 bool CertLibrary::IsHardwareBacked() const {
    149   return CertLoader::Get()->IsHardwareBacked();
    150 }
    151 
    152 int CertLibrary::NumCertificates(CertType type) const {
    153   const net::CertificateList& cert_list = GetCertificateListForType(type);
    154   return static_cast<int>(cert_list.size());
    155 }
    156 
    157 base::string16 CertLibrary::GetCertDisplayStringAt(CertType type,
    158                                                    int index) const {
    159   net::X509Certificate* cert = GetCertificateAt(type, index);
    160   bool hardware_backed = IsCertHardwareBackedAt(type, index);
    161   return GetDisplayString(cert, hardware_backed);
    162 }
    163 
    164 std::string CertLibrary::GetServerCACertPEMAt(int index) const {
    165   return CertToPEM(*GetCertificateAt(CERT_TYPE_SERVER_CA, index));
    166 }
    167 
    168 std::string CertLibrary::GetUserCertPkcs11IdAt(int index, int* slot_id) const {
    169   net::X509Certificate* cert = GetCertificateAt(CERT_TYPE_USER, index);
    170   return CertLoader::GetPkcs11IdAndSlotForCert(*cert, slot_id);
    171 }
    172 
    173 bool CertLibrary::IsCertHardwareBackedAt(CertType type, int index) const {
    174   net::X509Certificate* cert = GetCertificateAt(type, index);
    175   return CertLoader::Get()->IsCertificateHardwareBacked(cert);
    176 }
    177 
    178 int CertLibrary::GetServerCACertIndexByPEM(
    179     const std::string& pem_encoded) const {
    180   int num_certs = NumCertificates(CERT_TYPE_SERVER_CA);
    181   for (int index = 0; index < num_certs; ++index) {
    182     net::X509Certificate* cert = GetCertificateAt(CERT_TYPE_SERVER_CA, index);
    183     if (CertToPEM(*cert) != pem_encoded)
    184       continue;
    185     return index;
    186   }
    187   return -1;
    188 }
    189 
    190 int CertLibrary::GetUserCertIndexByPkcs11Id(
    191     const std::string& pkcs11_id) const {
    192   int num_certs = NumCertificates(CERT_TYPE_USER);
    193   for (int index = 0; index < num_certs; ++index) {
    194     net::X509Certificate* cert = GetCertificateAt(CERT_TYPE_USER, index);
    195     int slot_id = -1;
    196     std::string id = CertLoader::GetPkcs11IdAndSlotForCert(*cert, &slot_id);
    197     if (id == pkcs11_id)
    198       return index;
    199   }
    200   return -1;  // Not found.
    201 }
    202 
    203 void CertLibrary::OnCertificatesLoaded(const net::CertificateList& cert_list,
    204                                        bool initial_load) {
    205   CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    206   VLOG(1) << "CertLibrary::OnCertificatesLoaded: " << cert_list.size();
    207   certs_.clear();
    208   user_certs_.clear();
    209   server_certs_.clear();
    210   server_ca_certs_.clear();
    211 
    212   // Add certificates to the appropriate list.
    213   for (net::CertificateList::const_iterator iter = cert_list.begin();
    214        iter != cert_list.end(); ++iter) {
    215     certs_.push_back(iter->get());
    216     net::X509Certificate::OSCertHandle cert_handle =
    217         iter->get()->os_cert_handle();
    218     net::CertType type = x509_certificate_model::GetType(cert_handle);
    219     switch (type) {
    220       case net::USER_CERT:
    221         user_certs_.push_back(iter->get());
    222         break;
    223       case net::SERVER_CERT:
    224         server_certs_.push_back(iter->get());
    225         break;
    226       case net::CA_CERT: {
    227         // Exclude root CA certificates that are built into Chrome.
    228         std::string token_name =
    229             x509_certificate_model::GetTokenName(cert_handle);
    230         if (token_name != kRootCertificateTokenName)
    231           server_ca_certs_.push_back(iter->get());
    232         break;
    233       }
    234       default:
    235         break;
    236     }
    237   }
    238 
    239   // Perform locale-sensitive sorting by certificate name.
    240   UErrorCode error = U_ZERO_ERROR;
    241   scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(
    242       icu::Locale(g_browser_process->GetApplicationLocale().c_str()), error));
    243   if (U_FAILURE(error))
    244     collator.reset();
    245   CertNameComparator cert_name_comparator(collator.get());
    246   std::sort(certs_.begin(), certs_.end(), cert_name_comparator);
    247   std::sort(user_certs_.begin(), user_certs_.end(), cert_name_comparator);
    248   std::sort(server_certs_.begin(), server_certs_.end(), cert_name_comparator);
    249   std::sort(server_ca_certs_.begin(), server_ca_certs_.end(),
    250             cert_name_comparator);
    251 
    252   VLOG(1) << "certs_: " << certs_.size();
    253   VLOG(1) << "user_certs_: " << user_certs_.size();
    254   VLOG(1) << "server_certs_: " << server_certs_.size();
    255   VLOG(1) << "server_ca_certs_: " << server_ca_certs_.size();
    256 
    257   FOR_EACH_OBSERVER(CertLibrary::Observer, observer_list_,
    258                     OnCertificatesLoaded(initial_load));
    259 }
    260 
    261 net::X509Certificate* CertLibrary::GetCertificateAt(CertType type,
    262                                                     int index) const {
    263   const net::CertificateList& cert_list = GetCertificateListForType(type);
    264   DCHECK_GE(index, 0);
    265   DCHECK_LT(index, static_cast<int>(cert_list.size()));
    266   return cert_list[index].get();
    267 }
    268 
    269 const net::CertificateList& CertLibrary::GetCertificateListForType(
    270     CertType type) const {
    271   if (type == CERT_TYPE_USER)
    272     return user_certs_;
    273   if (type == CERT_TYPE_SERVER)
    274     return server_certs_;
    275   if (type == CERT_TYPE_SERVER_CA)
    276     return server_ca_certs_;
    277   DCHECK(type == CERT_TYPE_DEFAULT);
    278   return certs_;
    279 }
    280 
    281 }  // namespace chromeos
    282