From e81cec40589aff1688917306ea0c57b3e85016d8 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Mon, 18 Apr 2016 10:41:31 +0530 Subject: [PATCH] app: api to import client certificate --- atom/browser/api/atom_api_app.cc | 69 ++++++- atom/browser/api/atom_api_app.h | 11 ++ atom/browser/atom_browser_client.cc | 30 --- .../browser/certificate_manager_model.cc | 177 ++++++++++++++++++ .../browser/certificate_manager_model.h | 116 ++++++++++++ filenames.gypi | 2 + 6 files changed, 374 insertions(+), 31 deletions(-) create mode 100644 chromium_src/chrome/browser/certificate_manager_model.cc create mode 100644 chromium_src/chrome/browser/certificate_manager_model.h diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 52d6ec3d338c..4cbae0885e9a 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -24,6 +24,7 @@ #include "base/command_line.h" #include "base/environment.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/path_service.h" #include "brightray/browser/brightray_paths.h" #include "chrome/common/chrome_paths.h" @@ -157,6 +158,43 @@ void PassLoginInformation(scoped_refptr login_handler, login_handler->CancelAuth(); } +net::CertificateList ImportCertsFromFile( + const base::FilePath& path) { + net::CertificateList certs; + if (path.empty()) + return certs; + + std::string cert_data; + if (!base::ReadFileToString(path, &cert_data)) + return certs; + + certs = net::X509Certificate::CreateCertificateListFromBytes( + cert_data.data(), cert_data.size(), + net::X509Certificate::FORMAT_AUTO); + + return certs; +} + +int ImportCertificateIntoCertStore( + CertificateManagerModel* model, + const base::FilePath& path, + const base::FilePath& ca_path, + const base::string16& password) { + LOG(WARNING) << "importing ...."; + + std::string file_data; + int result = -1; + net::CertificateList ca_certs; + net::NSSCertDatabase::ImportCertFailureList not_imported; + auto module = model->cert_db()->GetPublicModule(); + if (base::ReadFileToString(path, &file_data)) { + result &= model->ImportFromPKCS12(module, file_data, password, true); + ca_certs = ImportCertsFromFile(ca_path); + result &= model->ImportCACerts(ca_certs, net::NSSCertDatabase::TRUST_DEFAULT, ¬_imported); + } + return result; +} + } // namespace App::App() { @@ -369,6 +407,34 @@ bool App::MakeSingleInstance( } } +void App::ImportClientCertificate( + const base::FilePath& path, + const base::FilePath& ca_path, + + const base::string16& password, + const net::CompletionCallback& callback) { + auto browser_context = AtomBrowserMainParts::Get()->browser_context(); + if (!certificate_manager_model_) { + CertificateManagerModel::Create(browser_context, base::Bind(&App::OnCertificateManagerModelCreated, base::Unretained(this), path, ca_path, password, callback)); + return; + } + + int rv = ImportCertificateIntoCertStore(certificate_manager_model_.get(), path, ca_path, password); + callback.Run(rv); +} + +void App::OnCertificateManagerModelCreated( + const base::FilePath& path, + const base::FilePath& ca_path, + const base::string16& password, + const net::CompletionCallback& callback, + scoped_ptr model) { + certificate_manager_model_ = std::move(model); + + int rv = ImportCertificateIntoCertStore(certificate_manager_model_.get(), path, ca_path, password); + callback.Run(rv); +} + mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder( v8::Isolate* isolate) { auto browser = base::Unretained(Browser::Get()); @@ -408,7 +474,8 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder( .SetMethod("allowNTLMCredentialsForAllDomains", &App::AllowNTLMCredentialsForAllDomains) .SetMethod("getLocale", &App::GetLocale) - .SetMethod("makeSingleInstance", &App::MakeSingleInstance); + .SetMethod("makeSingleInstance", &App::MakeSingleInstance) + .SetMethod("importClientCertificate", &App::ImportClientCertificate); } // static diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index 5025a3869dd4..b5c797df0030 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -11,9 +11,11 @@ #include "atom/browser/atom_browser_client.h" #include "atom/browser/browser_observer.h" #include "atom/common/native_mate_converters/callback.h" +#include "chrome/browser/certificate_manager_model.h" #include "chrome/browser/process_singleton.h" #include "content/public/browser/gpu_data_manager_observer.h" #include "native_mate/handle.h" +#include "net/base/completion_callback.h" namespace base { class FilePath; @@ -41,6 +43,13 @@ class App : public AtomBrowserClient::Delegate, int render_process_id, int render_frame_id); + void OnCertificateManagerModelCreated( + const base::FilePath& path, + const base::FilePath& ca_path, + const base::string16& password, + const net::CompletionCallback& callback, + scoped_ptr model); + protected: App(); virtual ~App(); @@ -97,12 +106,14 @@ class App : public AtomBrowserClient::Delegate, bool MakeSingleInstance( const ProcessSingleton::NotificationCallback& callback); std::string GetLocale(); + void ImportClientCertificate(const base::FilePath& path, const base::FilePath& ca_path, const base::string16& password, const net::CompletionCallback& callback); #if defined(OS_WIN) bool IsAeroGlassEnabled(); #endif scoped_ptr process_singleton_; + scoped_ptr certificate_manager_model_; DISALLOW_COPY_AND_ASSIGN(App); }; diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 9a59c3f9d412..ddefd0688a2a 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -55,26 +55,6 @@ std::string g_custom_schemes = ""; // Custom schemes to be registered to handle service worker. std::string g_custom_service_worker_schemes = ""; -scoped_refptr ImportCertFromFile( - const base::FilePath& path) { - if (path.empty()) - return nullptr; - - std::string cert_data; - if (!base::ReadFileToString(path, &cert_data)) - return nullptr; - - net::CertificateList certs = - net::X509Certificate::CreateCertificateListFromBytes( - cert_data.data(), cert_data.size(), - net::X509Certificate::FORMAT_AUTO); - - if (certs.empty()) - return nullptr; - - return certs[0]; -} - } // namespace // static @@ -242,16 +222,6 @@ void AtomBrowserClient::SelectClientCertificate( content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, scoped_ptr delegate) { - // --client-certificate=`path` - auto cmd = base::CommandLine::ForCurrentProcess(); - if (cmd->HasSwitch(switches::kClientCertificate)) { - auto cert_path = cmd->GetSwitchValuePath(switches::kClientCertificate); - auto certificate = ImportCertFromFile(cert_path); - if (certificate.get()) - delegate->ContinueWithCertificate(certificate.get()); - return; - } - if (!cert_request_info->client_certs.empty() && delegate_) { delegate_->SelectClientCertificate( web_contents, cert_request_info, std::move(delegate)); diff --git a/chromium_src/chrome/browser/certificate_manager_model.cc b/chromium_src/chrome/browser/certificate_manager_model.cc new file mode 100644 index 000000000000..83ab12af7609 --- /dev/null +++ b/chromium_src/chrome/browser/certificate_manager_model.cc @@ -0,0 +1,177 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/certificate_manager_model.h" + +#include + +#include "base/bind.h" +#include "base/i18n/time_formatting.h" +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_context.h" +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" +#include "net/base/crypto_module.h" +#include "net/base/net_errors.h" +#include "net/cert/nss_cert_database.h" +#include "net/cert/x509_certificate.h" +#include "ui/base/l10n/l10n_util.h" + +using content::BrowserThread; + +namespace { + +net::NSSCertDatabase* g_nss_cert_database = nullptr; + +net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext( + content::ResourceContext* context, + const base::Callback& callback) { + // This initialization is not thread safe. This CHECK ensures that this code + // is only run on a single thread. + CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + if (!g_nss_cert_database) { + // Linux has only a single persistent slot compared to ChromeOS's separate + // public and private slot. + // Redirect any slot usage to this persistent slot on Linux. + g_nss_cert_database = new net::NSSCertDatabase( + crypto::ScopedPK11Slot( + crypto::GetPersistentNSSKeySlot()) /* public slot */, + crypto::ScopedPK11Slot( + crypto::GetPersistentNSSKeySlot()) /* private slot */); + } + return g_nss_cert_database; +} + +} // namespace + +// CertificateManagerModel is created on the UI thread. It needs a +// NSSCertDatabase handle (and on ChromeOS it needs to get the TPM status) which +// needs to be done on the IO thread. +// +// The initialization flow is roughly: +// +// UI thread IO Thread +// +// CertificateManagerModel::Create +// \--------------------------------------v +// CertificateManagerModel::GetCertDBOnIOThread +// | +// GetNSSCertDatabaseForResourceContext +// | +// CertificateManagerModel::DidGetCertDBOnIOThread +// | +// crypto::IsTPMTokenEnabledForNSS +// v--------------------------------------/ +// CertificateManagerModel::DidGetCertDBOnUIThread +// | +// new CertificateManagerModel +// | +// callback + +// static +void CertificateManagerModel::Create( + content::BrowserContext* browser_context, + const CreationCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&CertificateManagerModel::GetCertDBOnIOThread, + browser_context->GetResourceContext(), + callback)); +} + +CertificateManagerModel::CertificateManagerModel( + net::NSSCertDatabase* nss_cert_database, + bool is_user_db_available) + : cert_db_(nss_cert_database), + is_user_db_available_(is_user_db_available) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +CertificateManagerModel::~CertificateManagerModel() { +} + +int CertificateManagerModel::ImportFromPKCS12(net::CryptoModule* module, + const std::string& data, + const base::string16& password, + bool is_extractable) { + return cert_db_->ImportFromPKCS12(module, data, password, + is_extractable, NULL); +} + +int CertificateManagerModel::ImportUserCert(const std::string& data) { + return cert_db_->ImportUserCert(data); +} + +bool CertificateManagerModel::ImportCACerts( + const net::CertificateList& certificates, + net::NSSCertDatabase::TrustBits trust_bits, + net::NSSCertDatabase::ImportCertFailureList* not_imported) { + return cert_db_->ImportCACerts(certificates, trust_bits, not_imported); +} + +bool CertificateManagerModel::ImportServerCert( + const net::CertificateList& certificates, + net::NSSCertDatabase::TrustBits trust_bits, + net::NSSCertDatabase::ImportCertFailureList* not_imported) { + return cert_db_->ImportServerCert(certificates, trust_bits, + not_imported); +} + +bool CertificateManagerModel::SetCertTrust( + const net::X509Certificate* cert, + net::CertType type, + net::NSSCertDatabase::TrustBits trust_bits) { + return cert_db_->SetCertTrust(cert, type, trust_bits); +} + +bool CertificateManagerModel::Delete(net::X509Certificate* cert) { + return cert_db_->DeleteCertAndKey(cert); +} + +// static +void CertificateManagerModel::DidGetCertDBOnUIThread( + net::NSSCertDatabase* cert_db, + bool is_user_db_available, + const CreationCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + scoped_ptr model(new CertificateManagerModel( + cert_db, is_user_db_available)); + callback.Run(std::move(model)); +} + +// static +void CertificateManagerModel::DidGetCertDBOnIOThread( + const CreationCallback& callback, + net::NSSCertDatabase* cert_db) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + bool is_user_db_available = !!cert_db->GetPublicSlot(); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&CertificateManagerModel::DidGetCertDBOnUIThread, + cert_db, + is_user_db_available, + callback)); +} + +// static +void CertificateManagerModel::GetCertDBOnIOThread( + content::ResourceContext* context, + const CreationCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext( + context, + base::Bind(&CertificateManagerModel::DidGetCertDBOnIOThread, + callback)); + if (cert_db) + DidGetCertDBOnIOThread(callback, cert_db); +} diff --git a/chromium_src/chrome/browser/certificate_manager_model.h b/chromium_src/chrome/browser/certificate_manager_model.h new file mode 100644 index 000000000000..bd0d5ddfb291 --- /dev/null +++ b/chromium_src/chrome/browser/certificate_manager_model.h @@ -0,0 +1,116 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CERTIFICATE_MANAGER_MODEL_H_ +#define CHROME_BROWSER_CERTIFICATE_MANAGER_MODEL_H_ + +#include +#include + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "net/cert/nss_cert_database.h" + +namespace content { +class BrowserContext; +class ResourceContext; +} // namespace content + +// CertificateManagerModel provides the data to be displayed in the certificate +// manager dialog, and processes changes from the view. +class CertificateManagerModel { + public: + typedef base::Callback)> + CreationCallback; + + // Creates a CertificateManagerModel. The model will be passed to the callback + // when it is ready. The caller must ensure the model does not outlive the + // |browser_context|. + static void Create(content::BrowserContext* browser_context, + const CreationCallback& callback); + + ~CertificateManagerModel(); + + bool is_user_db_available() const { return is_user_db_available_; } + + // Accessor for read-only access to the underlying NSSCertDatabase. + const net::NSSCertDatabase* cert_db() const { return cert_db_; } + + // Import private keys and certificates from PKCS #12 encoded + // |data|, using the given |password|. If |is_extractable| is false, + // mark the private key as unextractable from the module. + // Returns a net error code on failure. + int ImportFromPKCS12(net::CryptoModule* module, const std::string& data, + const base::string16& password, bool is_extractable); + + // Import user certificate from DER encoded |data|. + // Returns a net error code on failure. + int ImportUserCert(const std::string& data); + + // Import CA certificates. + // Tries to import all the certificates given. The root will be trusted + // according to |trust_bits|. Any certificates that could not be imported + // will be listed in |not_imported|. + // |trust_bits| should be a bit field of TRUST* values from NSSCertDatabase. + // Returns false if there is an internal error, otherwise true is returned and + // |not_imported| should be checked for any certificates that were not + // imported. + bool ImportCACerts(const net::CertificateList& certificates, + net::NSSCertDatabase::TrustBits trust_bits, + net::NSSCertDatabase::ImportCertFailureList* not_imported); + + // Import server certificate. The first cert should be the server cert. Any + // additional certs should be intermediate/CA certs and will be imported but + // not given any trust. + // Any certificates that could not be imported will be listed in + // |not_imported|. + // |trust_bits| can be set to explicitly trust or distrust the certificate, or + // use TRUST_DEFAULT to inherit trust as normal. + // Returns false if there is an internal error, otherwise true is returned and + // |not_imported| should be checked for any certificates that were not + // imported. + bool ImportServerCert( + const net::CertificateList& certificates, + net::NSSCertDatabase::TrustBits trust_bits, + net::NSSCertDatabase::ImportCertFailureList* not_imported); + + // Set trust values for certificate. + // |trust_bits| should be a bit field of TRUST* values from NSSCertDatabase. + // Returns true on success or false on failure. + bool SetCertTrust(const net::X509Certificate* cert, + net::CertType type, + net::NSSCertDatabase::TrustBits trust_bits); + + // Delete the cert. Returns true on success. |cert| is still valid when this + // function returns. + bool Delete(net::X509Certificate* cert); + + private: + CertificateManagerModel(net::NSSCertDatabase* nss_cert_database, + bool is_user_db_available); + + // Methods used during initialization, see the comment at the top of the .cc + // file for details. + static void DidGetCertDBOnUIThread( + net::NSSCertDatabase* cert_db, + bool is_user_db_available, + const CreationCallback& callback); + static void DidGetCertDBOnIOThread( + const CreationCallback& callback, + net::NSSCertDatabase* cert_db); + static void GetCertDBOnIOThread(content::ResourceContext* context, + const CreationCallback& callback); + + net::NSSCertDatabase* cert_db_; + // Whether the certificate database has a public slot associated with the + // profile. If not set, importing certificates is not allowed with this model. + bool is_user_db_available_; + + DISALLOW_COPY_AND_ASSIGN(CertificateManagerModel); +}; + +#endif // CHROME_BROWSER_CERTIFICATE_MANAGER_MODEL_H_ diff --git a/filenames.gypi b/filenames.gypi index dd9edb409f59..4de1b366b4fc 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -390,6 +390,8 @@ 'atom/utility/atom_content_utility_client.h', 'chromium_src/chrome/browser/browser_process.cc', 'chromium_src/chrome/browser/browser_process.h', + 'chromium_src/chrome/browser/certificate_manager_model.cc', + 'chromium_src/chrome/browser/certificate_manager_model.h', 'chromium_src/chrome/browser/chrome_process_finder_win.cc', 'chromium_src/chrome/browser/chrome_process_finder_win.h', 'chromium_src/chrome/browser/chrome_notification_types.h',