Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chromeos / network / onc / onc_certificate_importer_impl.cc
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 "chromeos/network/onc/onc_certificate_importer_impl.h"
6
7 #include <cert.h>
8 #include <keyhi.h>
9 #include <pk11pub.h>
10
11 #include "base/base64.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/values.h"
21 #include "chromeos/network/network_event_log.h"
22 #include "chromeos/network/onc/onc_utils.h"
23 #include "components/onc/onc_constants.h"
24 #include "crypto/scoped_nss_types.h"
25 #include "net/base/crypto_module.h"
26 #include "net/base/net_errors.h"
27 #include "net/cert/nss_cert_database.h"
28 #include "net/cert/x509_certificate.h"
29
30 namespace chromeos {
31 namespace onc {
32
33 namespace {
34
35 void CallBackOnOriginLoop(
36     const scoped_refptr<base::SingleThreadTaskRunner>& origin_loop,
37     const CertificateImporter::DoneCallback& callback,
38     bool success,
39     const net::CertificateList& onc_trusted_certificates) {
40   origin_loop->PostTask(
41       FROM_HERE, base::Bind(callback, success, onc_trusted_certificates));
42 }
43
44 }  // namespace
45
46 CertificateImporterImpl::CertificateImporterImpl(
47     const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
48     net::NSSCertDatabase* target_nssdb)
49     : io_task_runner_(io_task_runner),
50       target_nssdb_(target_nssdb),
51       weak_factory_(this) {
52   CHECK(target_nssdb);
53 }
54
55 CertificateImporterImpl::~CertificateImporterImpl() {
56 }
57
58 void CertificateImporterImpl::ImportCertificates(
59     const base::ListValue& certificates,
60     ::onc::ONCSource source,
61     const DoneCallback& done_callback) {
62   VLOG(2) << "ONC file has " << certificates.GetSize() << " certificates";
63   // |done_callback| must only be called as long as |this| still exists.
64   // Thereforce, call back to |this|. This check of |this| must happen last and
65   // on the origin thread.
66   DoneCallback callback_to_this =
67       base::Bind(&CertificateImporterImpl::RunDoneCallback,
68                  weak_factory_.GetWeakPtr(),
69                  done_callback);
70
71   // |done_callback| must be called on the origin thread.
72   DoneCallback callback_on_origin_loop =
73       base::Bind(&CallBackOnOriginLoop,
74                  base::ThreadTaskRunnerHandle::Get(),
75                  callback_to_this);
76
77   // This is the actual function that imports the certificates.
78   base::Closure import_certs_callback =
79       base::Bind(&ParseAndStoreCertificates,
80                  source,
81                  callback_on_origin_loop,
82                  base::Owned(certificates.DeepCopy()),
83                  target_nssdb_);
84
85   // The NSSCertDatabase must be accessed on |io_task_runner_|
86   io_task_runner_->PostTask(FROM_HERE, import_certs_callback);
87 }
88
89 // static
90 void CertificateImporterImpl::ParseAndStoreCertificates(
91     ::onc::ONCSource source,
92     const DoneCallback& done_callback,
93     base::ListValue* certificates,
94     net::NSSCertDatabase* nssdb) {
95   // Web trust is only granted to certificates imported by the user.
96   bool allow_trust_imports = source == ::onc::ONC_SOURCE_USER_IMPORT;
97   net::CertificateList onc_trusted_certificates;
98   bool success = true;
99   for (size_t i = 0; i < certificates->GetSize(); ++i) {
100     const base::DictionaryValue* certificate = NULL;
101     certificates->GetDictionary(i, &certificate);
102     DCHECK(certificate != NULL);
103
104     VLOG(2) << "Parsing certificate at index " << i << ": " << *certificate;
105
106     if (!ParseAndStoreCertificate(allow_trust_imports,
107                                   *certificate,
108                                   nssdb,
109                                   &onc_trusted_certificates)) {
110       success = false;
111       LOG(ERROR) << "Cannot parse certificate at index " << i;
112     } else {
113       VLOG(2) << "Successfully imported certificate at index " << i;
114     }
115   }
116
117   done_callback.Run(success, onc_trusted_certificates);
118 }
119
120 // static
121 void CertificateImporterImpl::ListCertsWithNickname(
122     const std::string& label,
123     net::CertificateList* result,
124     net::NSSCertDatabase* target_nssdb) {
125   net::CertificateList all_certs;
126   // TODO(tbarzic): Use async |ListCerts|.
127   target_nssdb->ListCertsSync(&all_certs);
128   result->clear();
129   for (net::CertificateList::iterator iter = all_certs.begin();
130        iter != all_certs.end(); ++iter) {
131     if (iter->get()->os_cert_handle()->nickname) {
132       // Separate the nickname stored in the certificate at the colon, since
133       // NSS likes to store it as token:nickname.
134       const char* delimiter =
135           ::strchr(iter->get()->os_cert_handle()->nickname, ':');
136       if (delimiter) {
137         ++delimiter;  // move past the colon.
138         if (strcmp(delimiter, label.c_str()) == 0) {
139           result->push_back(*iter);
140           continue;
141         }
142       }
143     }
144     // Now we find the private key for this certificate and see if it has a
145     // nickname that matches.  If there is a private key, and it matches,
146     // then this is a client cert that we are looking for.
147     SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
148         iter->get()->os_cert_handle()->slot,
149         iter->get()->os_cert_handle(),
150         NULL);  // wincx
151     if (private_key) {
152       char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key);
153       if (private_key_nickname && std::string(label) == private_key_nickname)
154         result->push_back(*iter);
155       PORT_Free(private_key_nickname);
156       SECKEY_DestroyPrivateKey(private_key);
157     }
158   }
159 }
160
161 // static
162 bool CertificateImporterImpl::DeleteCertAndKeyByNickname(
163     const std::string& label,
164     net::NSSCertDatabase* target_nssdb) {
165   net::CertificateList cert_list;
166   ListCertsWithNickname(label, &cert_list, target_nssdb);
167   bool result = true;
168   for (net::CertificateList::iterator iter = cert_list.begin();
169        iter != cert_list.end(); ++iter) {
170     // If we fail, we try and delete the rest still.
171     // TODO(gspencer): this isn't very "transactional".  If we fail on some, but
172     // not all, then it's possible to leave things in a weird state.
173     // Luckily there should only be one cert with a particular
174     // label, and the cert not being found is one of the few reasons the
175     // delete could fail, but still...  The other choice is to return
176     // failure immediately, but that doesn't seem to do what is intended.
177     if (!target_nssdb->DeleteCertAndKey(iter->get()))
178       result = false;
179   }
180   return result;
181 }
182
183 void CertificateImporterImpl::RunDoneCallback(
184     const CertificateImporter::DoneCallback& callback,
185     bool success,
186     const net::CertificateList& onc_trusted_certificates) {
187   if (!success)
188     NET_LOG_ERROR("ONC Certificate Import Error", "");
189   callback.Run(success, onc_trusted_certificates);
190 }
191
192 bool CertificateImporterImpl::ParseAndStoreCertificate(
193     bool allow_trust_imports,
194     const base::DictionaryValue& certificate,
195     net::NSSCertDatabase* nssdb,
196     net::CertificateList* onc_trusted_certificates) {
197   // Get out the attributes of the given certificate.
198   std::string guid;
199   certificate.GetStringWithoutPathExpansion(::onc::certificate::kGUID, &guid);
200   DCHECK(!guid.empty());
201
202   bool remove = false;
203   if (certificate.GetBooleanWithoutPathExpansion(::onc::kRemove, &remove) &&
204       remove) {
205     if (!DeleteCertAndKeyByNickname(guid, nssdb)) {
206       LOG(ERROR) << "Unable to delete certificate";
207       return false;
208     } else {
209       return true;
210     }
211   }
212
213   // Not removing, so let's get the data we need to add this certificate.
214   std::string cert_type;
215   certificate.GetStringWithoutPathExpansion(::onc::certificate::kType,
216                                             &cert_type);
217   if (cert_type == ::onc::certificate::kServer ||
218       cert_type == ::onc::certificate::kAuthority) {
219     return ParseServerOrCaCertificate(allow_trust_imports,
220                                       cert_type,
221                                       guid,
222                                       certificate,
223                                       nssdb,
224                                       onc_trusted_certificates);
225   } else if (cert_type == ::onc::certificate::kClient) {
226     return ParseClientCertificate(guid, certificate, nssdb);
227   }
228
229   NOTREACHED();
230   return false;
231 }
232
233 bool CertificateImporterImpl::ParseServerOrCaCertificate(
234     bool allow_trust_imports,
235     const std::string& cert_type,
236     const std::string& guid,
237     const base::DictionaryValue& certificate,
238     net::NSSCertDatabase* nssdb,
239     net::CertificateList* onc_trusted_certificates) {
240   bool web_trust_flag = false;
241   const base::ListValue* trust_list = NULL;
242   if (certificate.GetListWithoutPathExpansion(::onc::certificate::kTrustBits,
243                                               &trust_list)) {
244     for (base::ListValue::const_iterator it = trust_list->begin();
245          it != trust_list->end(); ++it) {
246       std::string trust_type;
247       if (!(*it)->GetAsString(&trust_type))
248         NOTREACHED();
249
250       if (trust_type == ::onc::certificate::kWeb) {
251         // "Web" implies that the certificate is to be trusted for SSL
252         // identification.
253         web_trust_flag = true;
254       } else {
255         // Trust bits should only increase trust and never restrict. Thus,
256         // ignoring unknown bits should be safe.
257         LOG(WARNING) << "Certificate contains unknown trust type "
258                      << trust_type;
259       }
260     }
261   }
262
263   bool import_with_ssl_trust = false;
264   if (web_trust_flag) {
265     if (!allow_trust_imports)
266       LOG(WARNING) << "Web trust not granted for certificate: " << guid;
267     else
268       import_with_ssl_trust = true;
269   }
270
271   std::string x509_data;
272   if (!certificate.GetStringWithoutPathExpansion(::onc::certificate::kX509,
273                                                  &x509_data) ||
274       x509_data.empty()) {
275     LOG(ERROR) << "Certificate missing appropriate certificate data for type: "
276                << cert_type;
277     return false;
278   }
279
280   scoped_refptr<net::X509Certificate> x509_cert =
281       DecodePEMCertificate(x509_data);
282   if (!x509_cert.get()) {
283     LOG(ERROR) << "Unable to create certificate from PEM encoding, type: "
284                << cert_type;
285     return false;
286   }
287
288   net::NSSCertDatabase::TrustBits trust = (import_with_ssl_trust ?
289                                            net::NSSCertDatabase::TRUSTED_SSL :
290                                            net::NSSCertDatabase::TRUST_DEFAULT);
291
292   if (x509_cert->os_cert_handle()->isperm) {
293     net::CertType net_cert_type =
294         cert_type == ::onc::certificate::kServer ? net::SERVER_CERT
295                                                  : net::CA_CERT;
296     VLOG(1) << "Certificate is already installed.";
297     net::NSSCertDatabase::TrustBits missing_trust_bits =
298         trust & ~nssdb->GetCertTrust(x509_cert.get(), net_cert_type);
299     if (missing_trust_bits) {
300       std::string error_reason;
301       bool success = false;
302       if (nssdb->IsReadOnly(x509_cert.get())) {
303         error_reason = " Certificate is stored read-only.";
304       } else {
305         success = nssdb->SetCertTrust(x509_cert.get(), net_cert_type, trust);
306       }
307       if (!success) {
308         LOG(ERROR) << "Certificate of type " << cert_type
309                    << " was already present, but trust couldn't be set."
310                    << error_reason;
311       }
312     }
313   } else {
314     net::CertificateList cert_list;
315     cert_list.push_back(x509_cert);
316     net::NSSCertDatabase::ImportCertFailureList failures;
317     bool success = false;
318     if (cert_type == ::onc::certificate::kServer)
319       success = nssdb->ImportServerCert(cert_list, trust, &failures);
320     else  // Authority cert
321       success = nssdb->ImportCACerts(cert_list, trust, &failures);
322
323     if (!failures.empty()) {
324       std::string error_string = net::ErrorToString(failures[0].net_error);
325       LOG(ERROR) << "Error ( " << error_string
326                  << " ) importing certificate of type " << cert_type;
327       return false;
328     }
329
330     if (!success) {
331       LOG(ERROR) << "Unknown error importing " << cert_type << " certificate.";
332       return false;
333     }
334   }
335
336   if (web_trust_flag && onc_trusted_certificates)
337     onc_trusted_certificates->push_back(x509_cert);
338
339   return true;
340 }
341
342 bool CertificateImporterImpl::ParseClientCertificate(
343     const std::string& guid,
344     const base::DictionaryValue& certificate,
345     net::NSSCertDatabase* nssdb) {
346   std::string pkcs12_data;
347   if (!certificate.GetStringWithoutPathExpansion(::onc::certificate::kPKCS12,
348                                                  &pkcs12_data) ||
349       pkcs12_data.empty()) {
350     LOG(ERROR) << "PKCS12 data is missing for client certificate.";
351     return false;
352   }
353
354   std::string decoded_pkcs12;
355   if (!base::Base64Decode(pkcs12_data, &decoded_pkcs12)) {
356     LOG(ERROR) << "Unable to base64 decode PKCS#12 data: \"" << pkcs12_data
357                << "\".";
358     return false;
359   }
360
361   // Since this has a private key, always use the private module.
362   crypto::ScopedPK11Slot private_slot(nssdb->GetPrivateSlot());
363   if (!private_slot)
364     return false;
365   scoped_refptr<net::CryptoModule> module(
366       net::CryptoModule::CreateFromHandle(private_slot.get()));
367   net::CertificateList imported_certs;
368
369   int import_result = nssdb->ImportFromPKCS12(
370       module.get(), decoded_pkcs12, base::string16(), false, &imported_certs);
371   if (import_result != net::OK) {
372     std::string error_string = net::ErrorToString(import_result);
373     LOG(ERROR) << "Unable to import client certificate, error: "
374                << error_string;
375     return false;
376   }
377
378   if (imported_certs.size() == 0) {
379     LOG(WARNING) << "PKCS12 data contains no importable certificates.";
380     return true;
381   }
382
383   if (imported_certs.size() != 1) {
384     LOG(WARNING) << "ONC File: PKCS12 data contains more than one certificate. "
385                     "Only the first one will be imported.";
386   }
387
388   scoped_refptr<net::X509Certificate> cert_result = imported_certs[0];
389
390   // Find the private key associated with this certificate, and set the
391   // nickname on it.
392   SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
393       cert_result->os_cert_handle()->slot,
394       cert_result->os_cert_handle(),
395       NULL);  // wincx
396   if (private_key) {
397     PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str()));
398     SECKEY_DestroyPrivateKey(private_key);
399   } else {
400     LOG(WARNING) << "Unable to find private key for certificate.";
401   }
402   return true;
403 }
404
405 }  // namespace onc
406 }  // namespace chromeos