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 8 #include <vector> 9 10 #include "base/base64.h" 11 #include "base/bind.h" 12 #include "base/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 "content/public/browser/browser_thread.h" 18 #include "grit/generated_resources.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 base64; 54 base::Base64Encode(x509_certificate_model::GetDerString(cert), &base64); 55 return "-----BEGIN CERTIFICATE-----\r\n" + 56 WrapAt64(base64) + 57 "-----END CERTIFICATE-----\r\n"; 58 } 59 60 //////////////////////////////////////////////////////////////////////////////// 61 // General utility functions. 62 63 class Exporter : public ui::SelectFileDialog::Listener { 64 public: 65 Exporter(WebContents* web_contents, gfx::NativeWindow parent, 66 net::X509Certificate::OSCertHandle cert); 67 virtual ~Exporter(); 68 69 // SelectFileDialog::Listener implemenation. 70 virtual void FileSelected(const base::FilePath& path, 71 int index, void* params) OVERRIDE; 72 virtual void FileSelectionCanceled(void* params) OVERRIDE; 73 private: 74 scoped_refptr<ui::SelectFileDialog> select_file_dialog_; 75 76 // The certificate hierarchy (leaf cert first). 77 net::X509Certificate::OSCertHandles cert_chain_list_; 78 }; 79 80 Exporter::Exporter(WebContents* web_contents, 81 gfx::NativeWindow parent, 82 net::X509Certificate::OSCertHandle cert) 83 : select_file_dialog_(ui::SelectFileDialog::Create( 84 this, new ChromeSelectFilePolicy(web_contents))) { 85 x509_certificate_model::GetCertChainFromCert(cert, &cert_chain_list_); 86 87 // TODO(mattm): should this default to some directory? 88 // Maybe SavePackage::GetSaveDirPreference? (Except that it's private.) 89 std::string cert_title = x509_certificate_model::GetTitle(cert); 90 base::FilePath suggested_path = 91 net::GenerateFileName(GURL::EmptyGURL(), // url 92 std::string(), // content_disposition 93 std::string(), // referrer_charset 94 cert_title, // suggested_name 95 std::string(), // mime_type 96 "certificate"); // default_name 97 98 ShowCertSelectFileDialog(select_file_dialog_.get(), 99 ui::SelectFileDialog::SELECT_SAVEAS_FILE, 100 suggested_path, 101 parent, 102 NULL); 103 } 104 105 Exporter::~Exporter() { 106 // There may be pending file dialogs, we need to tell them that we've gone 107 // away so they don't try and call back to us. 108 if (select_file_dialog_.get()) 109 select_file_dialog_->ListenerDestroyed(); 110 111 x509_certificate_model::DestroyCertChain(&cert_chain_list_); 112 } 113 114 void Exporter::FileSelected(const base::FilePath& path, int index, 115 void* params) { 116 std::string data; 117 switch (index) { 118 case 2: 119 for (size_t i = 0; i < cert_chain_list_.size(); ++i) 120 data += GetBase64String(cert_chain_list_[i]); 121 break; 122 case 3: 123 data = x509_certificate_model::GetDerString(cert_chain_list_[0]); 124 break; 125 case 4: 126 data = x509_certificate_model::GetCMSString(cert_chain_list_, 0, 1); 127 break; 128 case 5: 129 data = x509_certificate_model::GetCMSString( 130 cert_chain_list_, 0, cert_chain_list_.size()); 131 break; 132 case 1: 133 default: 134 data = GetBase64String(cert_chain_list_[0]); 135 break; 136 } 137 138 if (!data.empty()) 139 WriteFileOnFileThread(path, data); 140 141 delete this; 142 } 143 144 void Exporter::FileSelectionCanceled(void* params) { 145 delete this; 146 } 147 148 } // namespace 149 150 void ShowCertSelectFileDialog(ui::SelectFileDialog* select_file_dialog, 151 ui::SelectFileDialog::Type type, 152 const base::FilePath& suggested_path, 153 gfx::NativeWindow parent, 154 void* params) { 155 ui::SelectFileDialog::FileTypeInfo file_type_info; 156 file_type_info.extensions.resize(5); 157 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pem")); 158 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("crt")); 159 file_type_info.extension_description_overrides.push_back( 160 l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64)); 161 file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("pem")); 162 file_type_info.extensions[1].push_back(FILE_PATH_LITERAL("crt")); 163 file_type_info.extension_description_overrides.push_back( 164 l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_BASE64_CHAIN)); 165 file_type_info.extensions[2].push_back(FILE_PATH_LITERAL("der")); 166 file_type_info.extension_description_overrides.push_back( 167 l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_DER)); 168 file_type_info.extensions[3].push_back(FILE_PATH_LITERAL("p7c")); 169 file_type_info.extension_description_overrides.push_back( 170 l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7)); 171 file_type_info.extensions[4].push_back(FILE_PATH_LITERAL("p7c")); 172 file_type_info.extension_description_overrides.push_back( 173 l10n_util::GetStringUTF16(IDS_CERT_EXPORT_TYPE_PKCS7_CHAIN)); 174 file_type_info.include_all_files = true; 175 select_file_dialog->SelectFile( 176 type, base::string16(), 177 suggested_path, &file_type_info, 178 1, // 1-based index for |file_type_info.extensions| to specify default. 179 FILE_PATH_LITERAL("crt"), 180 parent, params); 181 } 182 183 void ShowCertExportDialog(WebContents* web_contents, 184 gfx::NativeWindow parent, 185 net::X509Certificate::OSCertHandle cert) { 186 new Exporter(web_contents, parent, cert); 187 } 188