• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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