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/webui/certificate_viewer_webui.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/i18n/time_formatting.h"
13 #include "base/json/json_writer.h"
14 #include "base/macros.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_piece.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h"
21 #include "chrome/browser/certificate_viewer.h"
22 #include "chrome/browser/platform_util.h"
23 #include "chrome/browser/ui/certificate_dialogs.h"
24 #include "chrome/browser/ui/webui/certificate_viewer_ui.h"
25 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
26 #include "chrome/common/net/x509_certificate_model_nss.h"
27 #include "chrome/common/url_constants.h"
28 #include "chrome/grit/generated_resources.h"
29 #include "components/strings/grit/components_strings.h"
30 #include "content/public/browser/host_zoom_map.h"
31 #include "content/public/browser/web_contents.h"
32 #include "net/cert/x509_util_nss.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/gfx/geometry/size.h"
35 
36 using content::WebContents;
37 using content::WebUIMessageHandler;
38 
39 namespace {
40 
41 // Helper class for building a Value representation of a certificate. The class
42 // gathers data for a single node of the representation tree and builds a
43 // DictionaryValue out of that.
44 class CertNodeBuilder {
45  public:
46   // Starts the node with "label" set to |label|.
47   explicit CertNodeBuilder(base::StringPiece label);
48 
49   // Convenience version: Converts |label_id| to the corresponding resource
50   // string, then delegates to the other constructor.
51   explicit CertNodeBuilder(int label_id);
52 
53   // Builder methods all return |*this| so that they can be chained in single
54   // expressions.
55 
56   // Sets the "payload.val" field. Call this at most once.
57   CertNodeBuilder& Payload(base::StringPiece payload);
58 
59   // Adds |child| in the list keyed "children". Can be called multiple times.
60   CertNodeBuilder& Child(std::unique_ptr<base::DictionaryValue> child);
61 
62   // Similar to Child, but if the argument is null, then this does not add
63   // anything.
64   CertNodeBuilder& ChildIfNotNull(std::unique_ptr<base::DictionaryValue> child);
65 
66   // Creates a DictionaryValue representation of the collected information. Only
67   // call this once.
68   std::unique_ptr<base::DictionaryValue> Build();
69 
70  private:
71   base::DictionaryValue node_;
72   base::ListValue children_;
73   // |built_| is false until Build() is called. Once it is |true|, |node_| and
74   // |children_| are no longer valid for use.
75   bool built_ = false;
76 
77   DISALLOW_COPY_AND_ASSIGN(CertNodeBuilder);
78 };
79 
CertNodeBuilder(base::StringPiece label)80 CertNodeBuilder::CertNodeBuilder(base::StringPiece label) {
81   node_.SetString("label", label);
82 }
83 
CertNodeBuilder(int label_id)84 CertNodeBuilder::CertNodeBuilder(int label_id)
85     : CertNodeBuilder(l10n_util::GetStringUTF8(label_id)) {}
86 
Payload(base::StringPiece payload)87 CertNodeBuilder& CertNodeBuilder::Payload(base::StringPiece payload) {
88   DCHECK(!node_.HasKey("payload.val"));
89   node_.SetString("payload.val", payload);
90   return *this;
91 }
92 
Child(std::unique_ptr<base::DictionaryValue> child)93 CertNodeBuilder& CertNodeBuilder::Child(
94     std::unique_ptr<base::DictionaryValue> child) {
95   children_.Append(std::move(child));
96   return *this;
97 }
98 
ChildIfNotNull(std::unique_ptr<base::DictionaryValue> child)99 CertNodeBuilder& CertNodeBuilder::ChildIfNotNull(
100     std::unique_ptr<base::DictionaryValue> child) {
101   if (child)
102     return Child(std::move(child));
103   return *this;
104 }
105 
Build()106 std::unique_ptr<base::DictionaryValue> CertNodeBuilder::Build() {
107   DCHECK(!built_);
108   if (!children_.empty()) {
109     node_.SetKey("children", std::move(children_));
110   }
111   built_ = true;
112   return std::make_unique<base::DictionaryValue>(std::move(node_));
113 }
114 
115 }  // namespace
116 
117 // Shows a certificate using the WebUI certificate viewer.
ShowCertificateViewer(WebContents * web_contents,gfx::NativeWindow parent,net::X509Certificate * cert)118 void ShowCertificateViewer(WebContents* web_contents,
119                            gfx::NativeWindow parent,
120                            net::X509Certificate* cert) {
121   net::ScopedCERTCertificateList nss_certs =
122       net::x509_util::CreateCERTCertificateListFromX509Certificate(cert);
123   if (nss_certs.empty())
124     return;
125 
126   CertificateViewerDialog::ShowConstrained(std::move(nss_certs), web_contents,
127                                            parent);
128 }
129 
130 ////////////////////////////////////////////////////////////////////////////////
131 // CertificateViewerDialog
132 
133 // static
ShowConstrained(net::ScopedCERTCertificateList certs,WebContents * web_contents,gfx::NativeWindow parent)134 CertificateViewerDialog* CertificateViewerDialog::ShowConstrained(
135     net::ScopedCERTCertificateList certs,
136     WebContents* web_contents,
137     gfx::NativeWindow parent) {
138   CertificateViewerDialog* dialog_ptr =
139       new CertificateViewerDialog(std::move(certs));
140   auto dialog = base::WrapUnique(dialog_ptr);
141 
142   // TODO(bshe): UI tweaks needed for Aura HTML Dialog, such as adding padding
143   // on the title for Aura ConstrainedWebDialogUI.
144   dialog_ptr->delegate_ = ShowConstrainedWebDialog(
145       web_contents->GetBrowserContext(), std::move(dialog), web_contents);
146 
147   // Clear the zoom level for the dialog so that it is not affected by the page
148   // zoom setting.
149   content::WebContents* dialog_web_contents =
150       dialog_ptr->delegate_->GetWebContents();
151   const GURL dialog_url = dialog_ptr->GetDialogContentURL();
152   content::HostZoomMap::Get(dialog_web_contents->GetSiteInstance())
153       ->SetZoomLevelForHostAndScheme(dialog_url.scheme(), dialog_url.host(), 0);
154   return dialog_ptr;  // For tests.
155 }
156 
GetNativeWebContentsModalDialog()157 gfx::NativeWindow CertificateViewerDialog::GetNativeWebContentsModalDialog() {
158   return delegate_->GetNativeDialog();
159 }
160 
CertificateViewerDialog(net::ScopedCERTCertificateList certs)161 CertificateViewerDialog::CertificateViewerDialog(
162     net::ScopedCERTCertificateList certs)
163     : nss_certs_(std::move(certs)) {
164   // Construct the dialog title from the certificate.
165   title_ = l10n_util::GetStringFUTF16(
166       IDS_CERT_INFO_DIALOG_TITLE,
167       base::UTF8ToUTF16(
168           x509_certificate_model::GetTitle(nss_certs_.front().get())));
169 }
170 
171 CertificateViewerDialog::~CertificateViewerDialog() = default;
172 
GetDialogModalType() const173 ui::ModalType CertificateViewerDialog::GetDialogModalType() const {
174   return ui::MODAL_TYPE_NONE;
175 }
176 
GetDialogTitle() const177 base::string16 CertificateViewerDialog::GetDialogTitle() const {
178   return title_;
179 }
180 
GetDialogContentURL() const181 GURL CertificateViewerDialog::GetDialogContentURL() const {
182   return GURL(chrome::kChromeUICertificateViewerURL);
183 }
184 
GetWebUIMessageHandlers(std::vector<WebUIMessageHandler * > * handlers) const185 void CertificateViewerDialog::GetWebUIMessageHandlers(
186     std::vector<WebUIMessageHandler*>* handlers) const {
187   handlers->push_back(new CertificateViewerDialogHandler(
188       const_cast<CertificateViewerDialog*>(this),
189       net::x509_util::DupCERTCertificateList(nss_certs_)));
190 }
191 
GetDialogSize(gfx::Size * size) const192 void CertificateViewerDialog::GetDialogSize(gfx::Size* size) const {
193   const int kDefaultWidth = 544;
194   const int kDefaultHeight = 628;
195   size->SetSize(kDefaultWidth, kDefaultHeight);
196 }
197 
GetDialogArgs() const198 std::string CertificateViewerDialog::GetDialogArgs() const {
199   std::string data;
200 
201   // Certificate information. The keys in this dictionary's general key
202   // correspond to the IDs in the Html page.
203   base::DictionaryValue cert_info;
204   CERTCertificate* cert_hnd = nss_certs_.front().get();
205 
206   // Certificate usage.
207   std::vector<std::string> usages;
208   x509_certificate_model::GetUsageStrings(cert_hnd, &usages);
209   cert_info.SetString("general.usages", base::JoinString(usages, "\n"));
210 
211   // Standard certificate details.
212   const std::string alternative_text =
213       l10n_util::GetStringUTF8(IDS_CERT_INFO_FIELD_NOT_PRESENT);
214   cert_info.SetString(
215       "general.title",
216       l10n_util::GetStringFUTF8(
217           IDS_CERT_INFO_DIALOG_TITLE,
218           base::UTF8ToUTF16(x509_certificate_model::GetTitle(cert_hnd))));
219 
220   // Issued to information.
221   cert_info.SetString("general.issued-cn",
222       x509_certificate_model::GetSubjectCommonName(cert_hnd, alternative_text));
223   cert_info.SetString("general.issued-o",
224       x509_certificate_model::GetSubjectOrgName(cert_hnd, alternative_text));
225   cert_info.SetString("general.issued-ou",
226       x509_certificate_model::GetSubjectOrgUnitName(cert_hnd,
227                                                     alternative_text));
228 
229   // Issuer information.
230   cert_info.SetString("general.issuer-cn",
231       x509_certificate_model::GetIssuerCommonName(cert_hnd, alternative_text));
232   cert_info.SetString("general.issuer-o",
233       x509_certificate_model::GetIssuerOrgName(cert_hnd, alternative_text));
234   cert_info.SetString("general.issuer-ou",
235       x509_certificate_model::GetIssuerOrgUnitName(cert_hnd, alternative_text));
236 
237   // Validity period.
238   base::Time issued, expires;
239   std::string issued_str, expires_str;
240   if (x509_certificate_model::GetTimes(cert_hnd, &issued, &expires)) {
241     issued_str = base::UTF16ToUTF8(
242         base::TimeFormatFriendlyDateAndTime(issued));
243     expires_str = base::UTF16ToUTF8(
244         base::TimeFormatFriendlyDateAndTime(expires));
245   } else {
246     issued_str = alternative_text;
247     expires_str = alternative_text;
248   }
249   cert_info.SetString("general.issue-date", issued_str);
250   cert_info.SetString("general.expiry-date", expires_str);
251 
252   cert_info.SetString("general.sha256",
253       x509_certificate_model::HashCertSHA256(cert_hnd));
254   cert_info.SetString("general.sha1",
255       x509_certificate_model::HashCertSHA1(cert_hnd));
256 
257   // Certificate hierarchy is constructed from bottom up.
258   base::Value children;
259   int index = 0;
260   for (const auto& cert : nss_certs_) {
261     base::Value cert_node(base::Value::Type::DICTIONARY);
262     cert_node.SetKey("label",
263                      base::Value(x509_certificate_model::GetTitle(cert.get())));
264     cert_node.SetPath({"payload", "index"}, base::Value(index));
265     // Add the child from the previous iteration.
266     if (!children.is_none())
267       cert_node.SetKey("children", std::move(children));
268 
269     // Add this node to the children list for the next iteration.
270     children = base::Value(base::Value::Type::LIST);
271     children.Append(std::move(cert_node));
272     ++index;
273   }
274   // Set the last node as the top of the certificate hierarchy.
275   cert_info.SetKey("hierarchy", std::move(children));
276 
277   base::JSONWriter::Write(cert_info, &data);
278 
279   return data;
280 }
281 
OnDialogShown(content::WebUI * webui)282 void CertificateViewerDialog::OnDialogShown(content::WebUI* webui) {
283   webui_ = webui;
284 }
285 
OnDialogClosed(const std::string & json_retval)286 void CertificateViewerDialog::OnDialogClosed(const std::string& json_retval) {
287   // Don't |delete this|: owned by the constrained dialog manager.
288 }
289 
OnCloseContents(WebContents * source,bool * out_close_dialog)290 void CertificateViewerDialog::OnCloseContents(WebContents* source,
291                                               bool* out_close_dialog) {
292   *out_close_dialog = true;
293 }
294 
ShouldShowDialogTitle() const295 bool CertificateViewerDialog::ShouldShowDialogTitle() const {
296   return true;
297 }
298 
299 ////////////////////////////////////////////////////////////////////////////////
300 // CertificateViewerDialogHandler
301 
CertificateViewerDialogHandler(CertificateViewerDialog * dialog,net::ScopedCERTCertificateList cert_chain)302 CertificateViewerDialogHandler::CertificateViewerDialogHandler(
303     CertificateViewerDialog* dialog,
304     net::ScopedCERTCertificateList cert_chain)
305     : dialog_(dialog), cert_chain_(std::move(cert_chain)) {}
306 
~CertificateViewerDialogHandler()307 CertificateViewerDialogHandler::~CertificateViewerDialogHandler() {
308 }
309 
RegisterMessages()310 void CertificateViewerDialogHandler::RegisterMessages() {
311   web_ui()->RegisterMessageCallback(
312       "exportCertificate",
313       base::BindRepeating(&CertificateViewerDialogHandler::ExportCertificate,
314                           base::Unretained(this)));
315   web_ui()->RegisterMessageCallback(
316       "requestCertificateFields",
317       base::BindRepeating(
318           &CertificateViewerDialogHandler::RequestCertificateFields,
319           base::Unretained(this)));
320 }
321 
ExportCertificate(const base::ListValue * args)322 void CertificateViewerDialogHandler::ExportCertificate(
323     const base::ListValue* args) {
324   int cert_index = GetCertificateIndex(args);
325   if (cert_index < 0)
326     return;
327 
328   gfx::NativeWindow window =
329       platform_util::GetTopLevel(dialog_->GetNativeWebContentsModalDialog());
330   ShowCertExportDialog(web_ui()->GetWebContents(),
331                        window,
332                        cert_chain_.begin() + cert_index,
333                        cert_chain_.end());
334 }
335 
RequestCertificateFields(const base::ListValue * args)336 void CertificateViewerDialogHandler::RequestCertificateFields(
337     const base::ListValue* args) {
338   int cert_index = GetCertificateIndex(args);
339   if (cert_index < 0)
340     return;
341 
342   CERTCertificate* cert = cert_chain_[cert_index].get();
343 
344   CertNodeBuilder version_node(IDS_CERT_DETAILS_VERSION);
345   std::string version = x509_certificate_model::GetVersion(cert);
346   if (!version.empty()) {
347     version_node.Payload(l10n_util::GetStringFUTF8(
348         IDS_CERT_DETAILS_VERSION_FORMAT, base::UTF8ToUTF16(version)));
349   }
350 
351   CertNodeBuilder issued_node_builder(IDS_CERT_DETAILS_NOT_BEFORE);
352   CertNodeBuilder expires_node_builder(IDS_CERT_DETAILS_NOT_AFTER);
353   base::Time issued, expires;
354   if (x509_certificate_model::GetTimes(cert, &issued, &expires)) {
355     issued_node_builder.Payload(base::UTF16ToUTF8(
356         base::TimeFormatShortDateAndTimeWithTimeZone(issued)));
357     expires_node_builder.Payload(base::UTF16ToUTF8(
358         base::TimeFormatShortDateAndTimeWithTimeZone(expires)));
359   }
360 
361   x509_certificate_model::Extensions extensions;
362   x509_certificate_model::GetExtensions(
363       l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_CRITICAL),
364       l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_NON_CRITICAL),
365       cert, &extensions);
366 
367   std::unique_ptr<base::DictionaryValue> details_extensions;
368   if (!extensions.empty()) {
369     CertNodeBuilder details_extensions_builder(IDS_CERT_DETAILS_EXTENSIONS);
370     for (const x509_certificate_model::Extension& extension : extensions) {
371       details_extensions_builder.Child(
372           CertNodeBuilder(extension.name).Payload(extension.value).Build());
373     }
374     details_extensions = details_extensions_builder.Build();
375   }
376 
377   base::ListValue root_list;
378   root_list.Append(
379       CertNodeBuilder(x509_certificate_model::GetTitle(cert))
380           .Child(
381               CertNodeBuilder(
382                   l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE))
383                   // Main certificate fields.
384                   .Child(version_node.Build())
385                   .Child(
386                       CertNodeBuilder(IDS_CERT_DETAILS_SERIAL_NUMBER)
387                           .Payload(
388                               x509_certificate_model::GetSerialNumberHexified(
389                                   cert, l10n_util::GetStringUTF8(
390                                             IDS_CERT_INFO_FIELD_NOT_PRESENT)))
391                           .Build())
392                   .Child(CertNodeBuilder(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG)
393                              .Payload(x509_certificate_model::
394                                           ProcessSecAlgorithmSignature(cert))
395                              .Build())
396                   .Child(
397                       CertNodeBuilder(IDS_CERT_DETAILS_ISSUER)
398                           .Payload(x509_certificate_model::GetIssuerName(cert))
399                           .Build())
400                   // Validity period.
401                   .Child(CertNodeBuilder(IDS_CERT_DETAILS_VALIDITY)
402                              .Child(issued_node_builder.Build())
403                              .Child(expires_node_builder.Build())
404                              .Build())
405                   .Child(
406                       CertNodeBuilder(IDS_CERT_DETAILS_SUBJECT)
407                           .Payload(x509_certificate_model::GetSubjectName(cert))
408                           .Build())
409                   // Subject key information.
410                   .Child(
411                       CertNodeBuilder(IDS_CERT_DETAILS_SUBJECT_KEY_INFO)
412                           .Child(
413                               CertNodeBuilder(IDS_CERT_DETAILS_SUBJECT_KEY_ALG)
414                                   .Payload(
415                                       x509_certificate_model::
416                                           ProcessSecAlgorithmSubjectPublicKey(
417                                               cert))
418                                   .Build())
419                           .Child(CertNodeBuilder(IDS_CERT_DETAILS_SUBJECT_KEY)
420                                      .Payload(
421                                          x509_certificate_model::
422                                              ProcessSubjectPublicKeyInfo(cert))
423                                      .Build())
424                           .Build())
425                   // Extensions.
426                   .ChildIfNotNull(std::move(details_extensions))
427                   .Child(
428                       CertNodeBuilder(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG)
429                           .Payload(x509_certificate_model::
430                                        ProcessSecAlgorithmSignatureWrap(cert))
431                           .Build())
432                   .Child(CertNodeBuilder(IDS_CERT_DETAILS_CERTIFICATE_SIG_VALUE)
433                              .Payload(x509_certificate_model::
434                                           ProcessRawBitsSignatureWrap(cert))
435                              .Build())
436                   .Child(
437                       CertNodeBuilder(IDS_CERT_INFO_FINGERPRINTS_GROUP)
438                           .Child(CertNodeBuilder(
439                                      IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL)
440                                      .Payload(
441                                          x509_certificate_model::HashCertSHA256(
442                                              cert))
443                                      .Build())
444                           .Child(
445                               CertNodeBuilder(
446                                   IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL)
447                                   .Payload(x509_certificate_model::HashCertSHA1(
448                                       cert))
449                                   .Build())
450                           .Build())
451                   .Build())
452           .Build());
453 
454   // Send certificate information to javascript.
455   web_ui()->CallJavascriptFunctionUnsafe("cert_viewer.getCertificateFields",
456                                          root_list);
457 }
458 
GetCertificateIndex(const base::ListValue * args) const459 int CertificateViewerDialogHandler::GetCertificateIndex(
460     const base::ListValue* args) const {
461   int cert_index;
462   double val;
463   if (!(args->GetDouble(0, &val)))
464     return -1;
465   cert_index = static_cast<int>(val);
466   if (cert_index < 0 || cert_index >= static_cast<int>(cert_chain_.size()))
467     return -1;
468   return cert_index;
469 }
470