Home | History | Annotate | Download | only in ui
      1 // Copyright (c) 2012 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 "chrome/browser/ui/certificate_dialogs.h"
      6 
      7 #include <algorithm>
      8 #include <vector>
      9 
     10 #include "base/base64.h"
     11 #include "base/bind.h"
     12 #include "base/files/file_util.h"
     13 #include "base/logging.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "chrome/browser/ui/chrome_select_file_policy.h"
     16 #include "chrome/common/net/x509_certificate_model.h"
     17 #include "chrome/grit/generated_resources.h"
     18 #include "content/public/browser/browser_thread.h"
     19 #include "net/base/filename_util.h"
     20 #include "ui/base/l10n/l10n_util.h"
     21 #include "ui/shell_dialogs/select_file_dialog.h"
     22 #include "url/gurl.h"
     23 
     24 using content::BrowserThread;
     25 using content::WebContents;
     26 
     27 namespace {
     28 
     29 void WriterCallback(const base::FilePath& path, const std::string& data) {
     30   int bytes_written = base::WriteFile(path, data.data(), data.size());
     31   if (bytes_written != static_cast<ssize_t>(data.size())) {
     32     LOG(ERROR) << "Writing " << path.value() << " ("
     33                << data.size() << "B) returned " << bytes_written;
     34   }
     35 }
     36 
     37 void WriteFileOnFileThread(const base::FilePath& path,
     38                            const std::string& data) {
     39   BrowserThread::PostTask(
     40       BrowserThread::FILE, FROM_HERE, base::Bind(&WriterCallback, path, data));
     41 }
     42 
     43 std::string WrapAt64(const std::string &str) {
     44   std::string result;
     45   for (size_t i = 0; i < str.size(); i += 64) {
     46     result.append(str, i, 64);  // Append clamps the len arg internally.
     47     result.append("\r\n");
     48   }
     49   return result;
     50 }
     51 
     52 std::string GetBase64String(net::X509Certificate::OSCertHandle cert) {
     53   std::string der_cert;
     54   if (!net::X509Certificate::GetDEREncoded(cert, &der_cert))
     55     return std::string();
     56   std::string base64;
     57   base::Base64Encode(der_cert, &base64);
     58   return "-----BEGIN CERTIFICATE-----\r\n" +
     59       WrapAt64(base64) +
     60       "-----END CERTIFICATE-----\r\n";
     61 }
     62 
     63 ////////////////////////////////////////////////////////////////////////////////
     64 // General utility functions.
     65 
     66 class Exporter : public ui::SelectFileDialog::Listener {
     67  public:
     68   Exporter(WebContents* web_contents,
     69            gfx::NativeWindow parent,
     70            net::X509Certificate::OSCertHandles::iterator certs_begin,
     71            net::X509Certificate::OSCertHandles::iterator certs_end);
     72   virtual ~Exporter();
     73 
     74   // SelectFileDialog::Listener implemenation.
     75   virtual void FileSelected(const base::FilePath& path,
     76                             int index, void* params) OVERRIDE;
     77   virtual void FileSelectionCanceled(void* params) OVERRIDE;
     78  private:
     79   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
     80 
     81   // The certificate hierarchy (leaf cert first).
     82   net::X509Certificate::OSCertHandles cert_chain_list_;
     83 };
     84 
     85 Exporter::Exporter(WebContents* web_contents,
     86                    gfx::NativeWindow parent,
     87                    net::X509Certificate::OSCertHandles::iterator certs_begin,
     88                    net::X509Certificate::OSCertHandles::iterator certs_end)
     89     : select_file_dialog_(ui::SelectFileDialog::Create(
     90           this,
     91           new ChromeSelectFilePolicy(web_contents))) {
     92   DCHECK(certs_begin != certs_end);
     93   for (net::X509Certificate::OSCertHandles::iterator i = certs_begin;
     94        i != certs_end;
     95        ++i) {
     96     cert_chain_list_.push_back(net::X509Certificate::DupOSCertHandle(*i));
     97   }
     98 
     99   // TODO(mattm): should this default to some directory?
    100   // Maybe SavePackage::GetSaveDirPreference? (Except that it's private.)
    101   std::string cert_title = x509_certificate_model::GetTitle(*certs_begin);
    102   base::FilePath suggested_path =
    103       net::GenerateFileName(GURL::EmptyGURL(),  // url
    104                             std::string(),      // content_disposition
    105                             std::string(),      // referrer_charset
    106                             cert_title,         // suggested_name
    107                             std::string(),      // mime_type
    108                             "certificate");     // default_name
    109 
    110   ShowCertSelectFileDialog(select_file_dialog_.get(),
    111                            ui::SelectFileDialog::SELECT_SAVEAS_FILE,
    112                            suggested_path,
    113                            parent,
    114                            NULL);
    115 }
    116 
    117 Exporter::~Exporter() {
    118   // There may be pending file dialogs, we need to tell them that we've gone
    119   // away so they don't try and call back to us.
    120   if (select_file_dialog_.get())
    121     select_file_dialog_->ListenerDestroyed();
    122 
    123   std::for_each(cert_chain_list_.begin(),
    124                 cert_chain_list_.end(),
    125                 &net::X509Certificate::FreeOSCertHandle);
    126 }
    127 
    128 void Exporter::FileSelected(const base::FilePath& path, int index,
    129                             void* params) {
    130   std::string data;
    131   switch (index) {
    132     case 2:
    133       for (size_t i = 0; i < cert_chain_list_.size(); ++i)
    134         data += GetBase64String(cert_chain_list_[i]);
    135       break;
    136     case 3:
    137       net::X509Certificate::GetDEREncoded(cert_chain_list_[0], &data);
    138       break;
    139     case 4:
    140       data = x509_certificate_model::GetCMSString(cert_chain_list_, 0, 1);
    141       break;
    142     case 5:
    143       data = x509_certificate_model::GetCMSString(
    144           cert_chain_list_, 0, cert_chain_list_.size());
    145       break;
    146     case 1:
    147     default:
    148       data = GetBase64String(cert_chain_list_[0]);
    149       break;
    150   }
    151 
    152   if (!data.empty())
    153     WriteFileOnFileThread(path, data);
    154 
    155   delete this;
    156 }
    157 
    158 void Exporter::FileSelectionCanceled(void* params) {
    159   delete this;
    160 }
    161 
    162 } // namespace
    163 
    164 void ShowCertSelectFileDialog(ui::SelectFileDialog* select_file_dialog,
    165                               ui::SelectFileDialog::Type type,
    166                               const base::FilePath& suggested_path,
    167                               gfx::NativeWindow parent,
    168                               void* params) {
    169   ui::SelectFileDialog::FileTypeInfo file_type_info;
    170   file_type_info.extensions.resize(5);
    171   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pem"));
    172   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("crt"));
    173   file_type_info.extension_description_overrides.push_back(
    174       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64));
    175   file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("pem"));
    176   file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("crt"));
    177   file_type_info.extension_description_overrides.push_back(
    178       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64_CHAIN));
    179   file_type_info.extensions[2].push_back(FILE_PATH_LITERAL("der"));
    180   file_type_info.extension_description_overrides.push_back(
    181       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_DER));
    182   file_type_info.extensions[3].push_back(FILE_PATH_LITERAL("p7c"));
    183   file_type_info.extension_description_overrides.push_back(
    184       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7));
    185   file_type_info.extensions[4].push_back(FILE_PATH_LITERAL("p7c"));
    186   file_type_info.extension_description_overrides.push_back(
    187       l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7_CHAIN));
    188   file_type_info.include_all_files = true;
    189   select_file_dialog->SelectFile(
    190       type, base::string16(),
    191       suggested_path, &file_type_info,
    192       1,  // 1-based index for |file_type_info.extensions| to specify default.
    193       FILE_PATH_LITERAL("crt"),
    194       parent, params);
    195 }
    196 
    197 void ShowCertExportDialog(WebContents* web_contents,
    198                           gfx::NativeWindow parent,
    199                           const scoped_refptr<net::X509Certificate>& cert) {
    200   net::X509Certificate::OSCertHandles cert_chain;
    201   cert_chain.push_back(cert->os_cert_handle());
    202   const net::X509Certificate::OSCertHandles& certs =
    203       cert->GetIntermediateCertificates();
    204   cert_chain.insert(cert_chain.end(), certs.begin(), certs.end());
    205   new Exporter(web_contents, parent, cert_chain.begin(), cert_chain.end());
    206 }
    207 
    208 void ShowCertExportDialog(
    209     content::WebContents* web_contents,
    210     gfx::NativeWindow parent,
    211     net::X509Certificate::OSCertHandles::iterator certs_begin,
    212     net::X509Certificate::OSCertHandles::iterator certs_end) {
    213   new Exporter(web_contents, parent, certs_begin, certs_end);
    214 }
    215