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