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
WriterCallback(const base::FilePath & path,const std::string & data)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
WriteFileOnFileThread(const base::FilePath & path,const std::string & data)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
WrapAt64(const std::string & str)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
GetBase64String(net::X509Certificate::OSCertHandle cert)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
Exporter(WebContents * web_contents,gfx::NativeWindow parent,net::X509Certificate::OSCertHandles::iterator certs_begin,net::X509Certificate::OSCertHandles::iterator certs_end)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
~Exporter()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
FileSelected(const base::FilePath & path,int index,void * params)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
FileSelectionCanceled(void * params)158 void Exporter::FileSelectionCanceled(void* params) {
159 delete this;
160 }
161
162 } // namespace
163
ShowCertSelectFileDialog(ui::SelectFileDialog * select_file_dialog,ui::SelectFileDialog::Type type,const base::FilePath & suggested_path,gfx::NativeWindow parent,void * params)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
ShowCertExportDialog(WebContents * web_contents,gfx::NativeWindow parent,const scoped_refptr<net::X509Certificate> & cert)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
ShowCertExportDialog(content::WebContents * web_contents,gfx::NativeWindow parent,net::X509Certificate::OSCertHandles::iterator certs_begin,net::X509Certificate::OSCertHandles::iterator certs_end)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