// 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/ui/certificate_dialogs.h" #include #include #include "base/base64.h" #include "base/bind.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/ui/chrome_select_file_policy.h" #include "chrome/common/net/x509_certificate_model.h" #include "chrome/grit/generated_resources.h" #include "content/public/browser/browser_thread.h" #include "net/base/filename_util.h" #include "ui/base/l10n/l10n_util.h" #include "ui/shell_dialogs/select_file_dialog.h" #include "url/gurl.h" using content::BrowserThread; using content::WebContents; namespace { void WriterCallback(const base::FilePath& path, const std::string& data) { int bytes_written = base::WriteFile(path, data.data(), data.size()); if (bytes_written != static_cast(data.size())) { LOG(ERROR) << "Writing " << path.value() << " (" << data.size() << "B) returned " << bytes_written; } } void WriteFileOnFileThread(const base::FilePath& path, const std::string& data) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(&WriterCallback, path, data)); } std::string WrapAt64(const std::string &str) { std::string result; for (size_t i = 0; i < str.size(); i += 64) { result.append(str, i, 64); // Append clamps the len arg internally. result.append("\r\n"); } return result; } std::string GetBase64String(net::X509Certificate::OSCertHandle cert) { std::string der_cert; if (!net::X509Certificate::GetDEREncoded(cert, &der_cert)) return std::string(); std::string base64; base::Base64Encode(der_cert, &base64); return "-----BEGIN CERTIFICATE-----\r\n" + WrapAt64(base64) + "-----END CERTIFICATE-----\r\n"; } //////////////////////////////////////////////////////////////////////////////// // General utility functions. class Exporter : public ui::SelectFileDialog::Listener { public: Exporter(WebContents* web_contents, gfx::NativeWindow parent, net::X509Certificate::OSCertHandles::iterator certs_begin, net::X509Certificate::OSCertHandles::iterator certs_end); virtual ~Exporter(); // SelectFileDialog::Listener implemenation. virtual void FileSelected(const base::FilePath& path, int index, void* params) OVERRIDE; virtual void FileSelectionCanceled(void* params) OVERRIDE; private: scoped_refptr select_file_dialog_; // The certificate hierarchy (leaf cert first). net::X509Certificate::OSCertHandles cert_chain_list_; }; Exporter::Exporter(WebContents* web_contents, gfx::NativeWindow parent, net::X509Certificate::OSCertHandles::iterator certs_begin, net::X509Certificate::OSCertHandles::iterator certs_end) : select_file_dialog_(ui::SelectFileDialog::Create( this, new ChromeSelectFilePolicy(web_contents))) { DCHECK(certs_begin != certs_end); for (net::X509Certificate::OSCertHandles::iterator i = certs_begin; i != certs_end; ++i) { cert_chain_list_.push_back(net::X509Certificate::DupOSCertHandle(*i)); } // TODO(mattm): should this default to some directory? // Maybe SavePackage::GetSaveDirPreference? (Except that it's private.) std::string cert_title = x509_certificate_model::GetTitle(*certs_begin); base::FilePath suggested_path = net::GenerateFileName(GURL::EmptyGURL(), // url std::string(), // content_disposition std::string(), // referrer_charset cert_title, // suggested_name std::string(), // mime_type "certificate"); // default_name ShowCertSelectFileDialog(select_file_dialog_.get(), ui::SelectFileDialog::SELECT_SAVEAS_FILE, suggested_path, parent, NULL); } Exporter::~Exporter() { // There may be pending file dialogs, we need to tell them that we've gone // away so they don't try and call back to us. if (select_file_dialog_.get()) select_file_dialog_->ListenerDestroyed(); std::for_each(cert_chain_list_.begin(), cert_chain_list_.end(), &net::X509Certificate::FreeOSCertHandle); } void Exporter::FileSelected(const base::FilePath& path, int index, void* params) { std::string data; switch (index) { case 2: for (size_t i = 0; i < cert_chain_list_.size(); ++i) data += GetBase64String(cert_chain_list_[i]); break; case 3: net::X509Certificate::GetDEREncoded(cert_chain_list_[0], &data); break; case 4: data = x509_certificate_model::GetCMSString(cert_chain_list_, 0, 1); break; case 5: data = x509_certificate_model::GetCMSString( cert_chain_list_, 0, cert_chain_list_.size()); break; case 1: default: data = GetBase64String(cert_chain_list_[0]); break; } if (!data.empty()) WriteFileOnFileThread(path, data); delete this; } void Exporter::FileSelectionCanceled(void* params) { delete this; } } // namespace void ShowCertSelectFileDialog(ui::SelectFileDialog* select_file_dialog, ui::SelectFileDialog::Type type, const base::FilePath& suggested_path, gfx::NativeWindow parent, void* params) { ui::SelectFileDialog::FileTypeInfo file_type_info; file_type_info.extensions.resize(5); file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pem")); file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("crt")); file_type_info.extension_description_overrides.push_back( l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64)); file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("pem")); file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("crt")); file_type_info.extension_description_overrides.push_back( l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64_CHAIN)); file_type_info.extensions[2].push_back(FILE_PATH_LITERAL("der")); file_type_info.extension_description_overrides.push_back( l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_DER)); file_type_info.extensions[3].push_back(FILE_PATH_LITERAL("p7c")); file_type_info.extension_description_overrides.push_back( l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7)); file_type_info.extensions[4].push_back(FILE_PATH_LITERAL("p7c")); file_type_info.extension_description_overrides.push_back( l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7_CHAIN)); file_type_info.include_all_files = true; select_file_dialog->SelectFile( type, base::string16(), suggested_path, &file_type_info, 1, // 1-based index for |file_type_info.extensions| to specify default. FILE_PATH_LITERAL("crt"), parent, params); } void ShowCertExportDialog(WebContents* web_contents, gfx::NativeWindow parent, const scoped_refptr& cert) { net::X509Certificate::OSCertHandles cert_chain; cert_chain.push_back(cert->os_cert_handle()); const net::X509Certificate::OSCertHandles& certs = cert->GetIntermediateCertificates(); cert_chain.insert(cert_chain.end(), certs.begin(), certs.end()); new Exporter(web_contents, parent, cert_chain.begin(), cert_chain.end()); } void ShowCertExportDialog( content::WebContents* web_contents, gfx::NativeWindow parent, net::X509Certificate::OSCertHandles::iterator certs_begin, net::X509Certificate::OSCertHandles::iterator certs_end) { new Exporter(web_contents, parent, certs_begin, certs_end); }