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