ssl_add_certificate.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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/ssl/ssl_add_certificate.h"
6
7#include "base/basictypes.h"
8#include "base/strings/string_number_conversions.h"
9#include "base/strings/utf_string_conversions.h"
10#include "chrome/browser/certificate_viewer.h"
11#include "chrome/browser/infobars/infobar_service.h"
12#include "chrome/browser/infobars/simple_alert_infobar_delegate.h"
13#include "components/infobars/core/confirm_infobar_delegate.h"
14#include "components/infobars/core/infobar.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/browser/render_frame_host.h"
17#include "content/public/browser/web_contents.h"
18#include "grit/generated_resources.h"
19#include "grit/theme_resources.h"
20#include "net/base/net_errors.h"
21#include "net/cert/cert_database.h"
22#include "net/cert/x509_certificate.h"
23#include "ui/base/l10n/l10n_util.h"
24
25using content::BrowserThread;
26using content::RenderFrameHost;
27using content::WebContents;
28
29namespace chrome {
30
31namespace {
32
33class SSLAddCertificateInfoBarDelegate : public ConfirmInfoBarDelegate {
34 public:
35  // Creates an SSL certificate enrollment result infobar and delegate.
36  static void Create(InfoBarService* infobar_service,
37                     net::X509Certificate* cert) {
38    infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
39        scoped_ptr<ConfirmInfoBarDelegate>(
40            new SSLAddCertificateInfoBarDelegate(cert))));
41  }
42
43 private:
44  explicit SSLAddCertificateInfoBarDelegate(net::X509Certificate* cert)
45      : cert_(cert) {}
46  virtual ~SSLAddCertificateInfoBarDelegate() {}
47
48  // ConfirmInfoBarDelegate implementation:
49  virtual int GetIconID() const OVERRIDE {
50    // TODO(davidben): Use a more appropriate icon.
51    return IDR_INFOBAR_SAVE_PASSWORD;
52  }
53
54  virtual Type GetInfoBarType() const OVERRIDE {
55    return PAGE_ACTION_TYPE;
56  }
57
58  virtual base::string16 GetMessageText() const OVERRIDE {
59    // TODO(evanm): GetDisplayName should return UTF-16.
60    return l10n_util::GetStringFUTF16(IDS_ADD_CERT_SUCCESS_INFOBAR_LABEL,
61                                      base::UTF8ToUTF16(
62                                          cert_->issuer().GetDisplayName()));
63  }
64
65  virtual int GetButtons() const OVERRIDE {
66    return BUTTON_OK;
67  }
68
69  virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE {
70    DCHECK_EQ(BUTTON_OK, button);
71    return l10n_util::GetStringUTF16(IDS_ADD_CERT_SUCCESS_INFOBAR_BUTTON);
72  }
73
74  virtual bool Accept() OVERRIDE {
75    WebContents* web_contents =
76        InfoBarService::WebContentsFromInfoBar(infobar());
77    ShowCertificateViewer(web_contents,
78                          web_contents->GetTopLevelNativeWindow(),
79                          cert_.get());
80    // It looks weird to hide the infobar just as the dialog opens.
81    return false;
82  }
83
84  // The certificate that was added.
85  scoped_refptr<net::X509Certificate> cert_;
86
87  DISALLOW_COPY_AND_ASSIGN(SSLAddCertificateInfoBarDelegate);
88};
89
90void ShowErrorInfoBar(int message_id,
91                      int render_process_id,
92                      int render_frame_id,
93                      int cert_error) {
94  WebContents* web_contents = WebContents::FromRenderFrameHost(
95      RenderFrameHost::FromID(render_process_id, render_frame_id));
96  if (!web_contents)
97    return;
98
99  // TODO(davidben): Use a more appropriate icon.
100  // TODO(davidben): Display a more user-friendly error string.
101  SimpleAlertInfoBarDelegate::Create(
102      InfoBarService::FromWebContents(web_contents),
103      IDR_INFOBAR_SAVE_PASSWORD,
104      l10n_util::GetStringFUTF16(IDS_ADD_CERT_ERR_INVALID_CERT,
105                                 base::IntToString16(-cert_error),
106                                 base::ASCIIToUTF16(
107                                     net::ErrorToString(cert_error))),
108      true);
109}
110
111void ShowSuccessInfoBar(int render_process_id,
112                        int render_frame_id,
113                        net::X509Certificate* cert) {
114  WebContents* web_contents = WebContents::FromRenderFrameHost(
115      RenderFrameHost::FromID(render_process_id, render_frame_id));
116  if (!web_contents)
117    return;
118
119  SSLAddCertificateInfoBarDelegate::Create(
120      InfoBarService::FromWebContents(web_contents), cert);
121}
122
123}  // namespace
124
125void SSLAddCertificate(
126    net::CertificateMimeType cert_type,
127    const void* cert_data,
128    size_t cert_size,
129    int render_process_id,
130    int render_frame_id) {
131  // Chromium only supports X.509 User certificates on non-Android
132  // platforms. Note that this method should not be called for other
133  // certificate mime types.
134  if (cert_type != net::CERTIFICATE_MIME_TYPE_X509_USER_CERT)
135    return;
136
137  scoped_refptr<net::X509Certificate> cert;
138  if (cert_data != NULL) {
139    cert = net::X509Certificate::CreateFromBytes(
140        reinterpret_cast<const char*>(cert_data), cert_size);
141  }
142  // NOTE: Passing a NULL cert pointer if |cert_data| was NULL is
143  // intentional here.
144
145  // Check if we have a corresponding private key.
146  int cert_error = net::CertDatabase::GetInstance()->CheckUserCert(cert.get());
147  if (cert_error != net::OK) {
148    LOG_IF(ERROR, cert_error == net::ERR_NO_PRIVATE_KEY_FOR_CERT)
149        << "No corresponding private key in store for cert: "
150        << (cert.get() ? cert->subject().GetDisplayName() : "NULL");
151
152    BrowserThread::PostTask(
153      BrowserThread::UI, FROM_HERE,
154      base::Bind(&ShowErrorInfoBar, IDS_ADD_CERT_ERR_INVALID_CERT,
155                 render_process_id, render_frame_id, cert_error));
156    return;
157  }
158
159  // Install it.
160  cert_error = net::CertDatabase::GetInstance()->AddUserCert(cert.get());
161
162  // Show the appropriate infobar.
163  if (cert_error != net::OK) {
164    BrowserThread::PostTask(
165      BrowserThread::UI, FROM_HERE,
166      base::Bind(&ShowErrorInfoBar, IDS_ADD_CERT_ERR_FAILED,
167                 render_process_id, render_frame_id, cert_error));
168  } else {
169    BrowserThread::PostTask(
170        BrowserThread::UI, FROM_HERE,
171        base::Bind(&ShowSuccessInfoBar,
172                   render_process_id, render_frame_id, cert));
173  }
174}
175
176}  // namespace chrome
177