1// Copyright (c) 2011 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/wifi_config_model.h"
6
7#include <algorithm>
8
9#include "base/utf_string_conversions.h"
10#include "chrome/browser/browser_process.h"  // g_browser_process
11#include "chrome/common/net/x509_certificate_model.h"
12#include "net/base/cert_database.h"
13#include "net/base/x509_certificate.h"
14#include "ui/base/l10n/l10n_util_collator.h"  // CompareString16WithCollator
15#include "unicode/coll.h"  // icu::Collator
16
17namespace chromeos {
18
19namespace {
20
21typedef scoped_refptr<net::X509Certificate> X509CertificateRefPtr;
22
23// Root CA certificates that are built into Chrome use this token name.
24const char* const kRootCertificateTokenName = "Builtin Object Token";
25
26// Returns a user-visible name for a given certificate.
27string16 GetCertDisplayString(const net::X509Certificate* cert) {
28  DCHECK(cert);
29  std::string name_or_nick =
30      x509_certificate_model::GetCertNameOrNickname(cert->os_cert_handle());
31  return UTF8ToUTF16(name_or_nick);
32}
33
34// Comparison functor for locale-sensitive sorting of certificates by name.
35class CertNameComparator {
36 public:
37  explicit CertNameComparator(icu::Collator* collator)
38      : collator_(collator) {
39  }
40
41  bool operator()(const X509CertificateRefPtr& lhs,
42                  const X509CertificateRefPtr& rhs) const {
43    string16 lhs_name = GetCertDisplayString(lhs);
44    string16 rhs_name = GetCertDisplayString(rhs);
45    if (collator_ == NULL)
46      return lhs_name < rhs_name;
47    return l10n_util::CompareString16WithCollator(
48        collator_, lhs_name, rhs_name) == UCOL_LESS;
49  }
50
51 private:
52  icu::Collator* collator_;
53};
54
55}  // namespace
56
57WifiConfigModel::WifiConfigModel() {
58}
59
60WifiConfigModel::~WifiConfigModel() {
61}
62
63void WifiConfigModel::UpdateCertificates() {
64  // CertDatabase and its wrappers do not have random access to certificates,
65  // so build filtered lists once.
66  net::CertificateList cert_list;
67  cert_db_.ListCerts(&cert_list);
68  for (net::CertificateList::const_iterator it = cert_list.begin();
69       it != cert_list.end();
70       ++it) {
71    net::X509Certificate* cert = it->get();
72    net::X509Certificate::OSCertHandle cert_handle = cert->os_cert_handle();
73    net::CertType type = x509_certificate_model::GetType(cert_handle);
74    switch (type) {
75      case net::USER_CERT:
76        user_certs_.push_back(*it);
77        break;
78      case net::CA_CERT: {
79        // Exclude root CA certificates that are built into Chrome.
80        std::string token_name =
81            x509_certificate_model::GetTokenName(cert_handle);
82        if (token_name != kRootCertificateTokenName)
83          server_ca_certs_.push_back(*it);
84        break;
85      }
86      default:
87        // We only care about those two types.
88        break;
89    }
90  }
91
92  // Perform locale-sensitive sorting by certificate name.
93  scoped_ptr<icu::Collator> collator;
94  UErrorCode error = U_ZERO_ERROR;
95  collator.reset(
96      icu::Collator::createInstance(
97          icu::Locale(g_browser_process->GetApplicationLocale().c_str()),
98          error));
99  if (U_FAILURE(error))
100    collator.reset(NULL);
101  CertNameComparator cert_name_comparator(collator.get());
102  std::sort(user_certs_.begin(), user_certs_.end(), cert_name_comparator);
103  std::sort(server_ca_certs_.begin(), server_ca_certs_.end(),
104            cert_name_comparator);
105}
106
107int WifiConfigModel::GetUserCertCount() const {
108  return static_cast<int>(user_certs_.size());
109}
110
111string16 WifiConfigModel::GetUserCertName(int cert_index) const {
112  DCHECK(cert_index >= 0);
113  DCHECK(cert_index < static_cast<int>(user_certs_.size()));
114  net::X509Certificate* cert = user_certs_[cert_index].get();
115  return GetCertDisplayString(cert);
116}
117
118std::string WifiConfigModel::GetUserCertPkcs11Id(int cert_index) const {
119  DCHECK(cert_index >= 0);
120  DCHECK(cert_index < static_cast<int>(user_certs_.size()));
121  net::X509Certificate* cert = user_certs_[cert_index].get();
122  net::X509Certificate::OSCertHandle cert_handle = cert->os_cert_handle();
123  return x509_certificate_model::GetPkcs11Id(cert_handle);
124}
125
126int WifiConfigModel::GetUserCertIndex(const std::string& pkcs11_id) const {
127  // The list of user certs is small, so just test each one.
128  for (int index = 0; index < static_cast<int>(user_certs_.size()); ++index) {
129    net::X509Certificate* cert = user_certs_[index].get();
130    net::X509Certificate::OSCertHandle cert_handle = cert->os_cert_handle();
131    std::string id = x509_certificate_model::GetPkcs11Id(cert_handle);
132    if (id == pkcs11_id)
133      return index;
134  }
135  // Not found.
136  return -1;
137}
138
139int WifiConfigModel::GetServerCaCertCount() const {
140  return static_cast<int>(server_ca_certs_.size());
141}
142
143string16 WifiConfigModel::GetServerCaCertName(int cert_index) const {
144  DCHECK(cert_index >= 0);
145  DCHECK(cert_index < static_cast<int>(server_ca_certs_.size()));
146  net::X509Certificate* cert = server_ca_certs_[cert_index].get();
147  return GetCertDisplayString(cert);
148}
149
150std::string WifiConfigModel::GetServerCaCertNssNickname(int cert_index) const {
151  DCHECK(cert_index >= 0);
152  DCHECK(cert_index < static_cast<int>(server_ca_certs_.size()));
153  net::X509Certificate* cert = server_ca_certs_[cert_index].get();
154  net::X509Certificate::OSCertHandle cert_handle = cert->os_cert_handle();
155  return x509_certificate_model::GetNickname(cert_handle);
156}
157
158int WifiConfigModel::GetServerCaCertIndex(
159    const std::string& nss_nickname) const {
160  // List of server certs is small, so just test each one.
161  for (int i = 0; i < static_cast<int>(server_ca_certs_.size()); ++i) {
162    net::X509Certificate* cert = server_ca_certs_[i].get();
163    net::X509Certificate::OSCertHandle cert_handle = cert->os_cert_handle();
164    std::string nickname = x509_certificate_model::GetNickname(cert_handle);
165    if (nickname == nss_nickname)
166      return i;
167  }
168  // Not found.
169  return -1;
170}
171
172}  // namespace chromeos
173