| Index: chrome/browser/ssl/ssl_client_auth_handler_gtk.cc
|
| diff --git a/chrome/browser/ssl/ssl_client_auth_handler_gtk.cc b/chrome/browser/ssl/ssl_client_auth_handler_gtk.cc
|
| index 7ea1aec025c3b89db0d443cbd59d620acfa3316b..49fa4c5af239eb19e27d90b19c0f511be4a75552 100644
|
| --- a/chrome/browser/ssl/ssl_client_auth_handler_gtk.cc
|
| +++ b/chrome/browser/ssl/ssl_client_auth_handler_gtk.cc
|
| @@ -3,10 +3,328 @@
|
| // found in the LICENSE file.
|
|
|
| #include "chrome/browser/ssl/ssl_client_auth_handler.h"
|
| +
|
| +#include <cert.h>
|
| +#include <gtk/gtk.h>
|
| +
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#include "app/gfx/native_widget_types.h"
|
| +#include "app/l10n_util.h"
|
| +#include "base/i18n/time_formatting.h"
|
| #include "base/logging.h"
|
| +#include "base/nss_util.h"
|
| +#include "chrome/browser/gtk/certificate_viewer.h"
|
| +#include "chrome/browser/gtk/gtk_util.h"
|
| +#include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h"
|
| +#include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h"
|
| +#include "chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h"
|
| +#include "grit/generated_resources.h"
|
| #include "net/base/x509_certificate.h"
|
|
|
| +// PSM = Mozilla's Personal Security Manager.
|
| +namespace psm = mozilla_security_manager;
|
| +
|
| +namespace {
|
| +
|
| +enum {
|
| + RESPONSE_SHOW_CERT_INFO = 1,
|
| +};
|
| +
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +// SSLClientCertificateSelector
|
| +
|
| +class SSLClientCertificateSelector {
|
| + public:
|
| + SSLClientCertificateSelector(gfx::NativeWindow parent,
|
| + net::SSLCertRequestInfo* cert_request_info,
|
| + SSLClientAuthHandler* delegate);
|
| +
|
| + void Show();
|
| +
|
| + private:
|
| + void PopulateCerts();
|
| +
|
| + static std::string FormatComboBoxText(CERTCertificate* cert,
|
| + const char* nickname);
|
| + static std::string FormatDetailsText(CERTCertificate* cert);
|
| +
|
| + static void OnComboBoxChanged(GtkComboBox* combo_box,
|
| + SSLClientCertificateSelector* cert_selector);
|
| + static void OnResponse(GtkDialog* dialog, gint response_id,
|
| + SSLClientCertificateSelector* cert_selector);
|
| + static void OnDestroy(GtkDialog* dialog,
|
| + SSLClientCertificateSelector* cert_selector);
|
| +
|
| + SSLClientAuthHandler* delegate_;
|
| + scoped_refptr<net::SSLCertRequestInfo> cert_request_info_;
|
| +
|
| + std::vector<std::string> details_strings_;
|
| +
|
| + GtkWidget* dialog_;
|
| + GtkWidget* cert_combo_box_;
|
| + GtkTextBuffer* cert_details_buffer_;
|
| +};
|
| +
|
| +SSLClientCertificateSelector::SSLClientCertificateSelector(
|
| + gfx::NativeWindow parent,
|
| + net::SSLCertRequestInfo* cert_request_info,
|
| + SSLClientAuthHandler* delegate)
|
| + : delegate_(delegate),
|
| + cert_request_info_(cert_request_info) {
|
| + dialog_ = gtk_dialog_new_with_buttons(
|
| + l10n_util::GetStringFUTF8(
|
| + IDS_CERT_SELECTOR_DIALOG_TITLE,
|
| + UTF8ToUTF16(cert_request_info->host_and_port)).c_str(),
|
| + parent,
|
| + // Non-modal.
|
| + GTK_DIALOG_NO_SEPARATOR,
|
| + l10n_util::GetStringUTF8(IDS_PAGEINFO_CERT_INFO_BUTTON).c_str(),
|
| + RESPONSE_SHOW_CERT_INFO,
|
| + GTK_STOCK_CANCEL,
|
| + GTK_RESPONSE_CANCEL,
|
| + GTK_STOCK_OK,
|
| + GTK_RESPONSE_OK,
|
| + NULL);
|
| + gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox),
|
| + gtk_util::kContentAreaSpacing);
|
| + gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_OK);
|
| +
|
| + GtkWidget* site_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
|
| + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), site_vbox,
|
| + FALSE, FALSE, 0);
|
| +
|
| + GtkWidget* site_description_label = gtk_util::CreateBoldLabel(
|
| + l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_SITE_DESCRIPTION_LABEL));
|
| + gtk_box_pack_start(GTK_BOX(site_vbox), site_description_label,
|
| + FALSE, FALSE, 0);
|
| +
|
| + GtkWidget* site_label = gtk_label_new(
|
| + cert_request_info->host_and_port.c_str());
|
| + gtk_util::LeftAlignMisc(site_label);
|
| + gtk_box_pack_start(GTK_BOX(site_vbox), site_label, FALSE, FALSE, 0);
|
| +
|
| + GtkWidget* selector_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
|
| + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), selector_vbox,
|
| + TRUE, TRUE, 0);
|
| +
|
| + GtkWidget* choose_description_label = gtk_util::CreateBoldLabel(
|
| + l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_CHOOSE_DESCRIPTION_LABEL));
|
| + gtk_box_pack_start(GTK_BOX(selector_vbox), choose_description_label,
|
| + FALSE, FALSE, 0);
|
| +
|
| +
|
| + cert_combo_box_ = gtk_combo_box_new_text();
|
| + g_signal_connect(cert_combo_box_, "changed", G_CALLBACK(OnComboBoxChanged),
|
| + this);
|
| + gtk_box_pack_start(GTK_BOX(selector_vbox), cert_combo_box_,
|
| + FALSE, FALSE, 0);
|
| +
|
| + GtkWidget* details_label = gtk_label_new(l10n_util::GetStringUTF8(
|
| + IDS_CERT_SELECTOR_DETAILS_DESCRIPTION_LABEL).c_str());
|
| + gtk_util::LeftAlignMisc(details_label);
|
| + gtk_box_pack_start(GTK_BOX(selector_vbox), details_label, FALSE, FALSE, 0);
|
| +
|
| + // TODO(mattm): fix text view coloring (should have grey background).
|
| + GtkWidget* cert_details_view = gtk_text_view_new();
|
| + gtk_text_view_set_editable(GTK_TEXT_VIEW(cert_details_view), FALSE);
|
| + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(cert_details_view), GTK_WRAP_WORD);
|
| + cert_details_buffer_ = gtk_text_view_get_buffer(
|
| + GTK_TEXT_VIEW(cert_details_view));
|
| + // We put the details in a frame instead of a scrolled window so that the
|
| + // entirety will be visible without requiring scrolling or expanding the
|
| + // dialog. This does however mean the dialog will grow itself if you switch
|
| + // to different cert that has longer details text.
|
| + GtkWidget* details_frame = gtk_frame_new(NULL);
|
| + gtk_frame_set_shadow_type(GTK_FRAME(details_frame), GTK_SHADOW_ETCHED_IN);
|
| + gtk_container_add(GTK_CONTAINER(details_frame), cert_details_view);
|
| + gtk_box_pack_start(GTK_BOX(selector_vbox), details_frame, TRUE, TRUE, 0);
|
| +
|
| + PopulateCerts();
|
| +
|
| + g_signal_connect(dialog_, "response", G_CALLBACK(OnResponse), this);
|
| + g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroy), this);
|
| +}
|
| +
|
| +void SSLClientCertificateSelector::Show() {
|
| + gtk_widget_show_all(dialog_);
|
| +}
|
| +
|
| +void SSLClientCertificateSelector::PopulateCerts() {
|
| + CERTCertList* cert_list = CERT_NewCertList();
|
| + for (size_t i = 0; i < cert_request_info_->client_certs.size(); ++i) {
|
| + CERT_AddCertToListTail(
|
| + cert_list,
|
| + CERT_DupCertificate(
|
| + cert_request_info_->client_certs[i]->os_cert_handle()));
|
| + }
|
| + // Would like to use CERT_GetCertNicknameWithValidity on each cert
|
| + // individually instead of having to build a CERTCertList for this, but that
|
| + // function is not exported.
|
| + CERTCertNicknames* nicknames = CERT_NicknameStringsFromCertList(
|
| + cert_list,
|
| + const_cast<char*>(l10n_util::GetStringUTF8(
|
| + IDS_CERT_SELECTOR_CERT_EXPIRED).c_str()),
|
| + const_cast<char*>(l10n_util::GetStringUTF8(
|
| + IDS_CERT_SELECTOR_CERT_NOT_YET_VALID).c_str()));
|
| + DCHECK_EQ(nicknames->numnicknames,
|
| + static_cast<int>(cert_request_info_->client_certs.size()));
|
| +
|
| + for (size_t i = 0; i < cert_request_info_->client_certs.size(); ++i) {
|
| + CERTCertificate* cert =
|
| + cert_request_info_->client_certs[i]->os_cert_handle();
|
| +
|
| + details_strings_.push_back(FormatDetailsText(cert));
|
| +
|
| + gtk_combo_box_append_text(
|
| + GTK_COMBO_BOX(cert_combo_box_),
|
| + FormatComboBoxText(cert, nicknames->nicknames[i]).c_str());
|
| + }
|
| +
|
| + CERT_FreeNicknames(nicknames);
|
| + CERT_DestroyCertList(cert_list);
|
| +
|
| + // Auto-select the first cert.
|
| + gtk_combo_box_set_active(GTK_COMBO_BOX(cert_combo_box_), 0);
|
| +}
|
| +
|
| +// static
|
| +std::string SSLClientCertificateSelector::FormatComboBoxText(
|
| + CERTCertificate* cert, const char* nickname) {
|
| + std::string rv(nickname);
|
| + char* serial_hex = CERT_Hexify(&cert->serialNumber, TRUE);
|
| + rv += " [";
|
| + rv += serial_hex;
|
| + rv += ']';
|
| + PORT_Free(serial_hex);
|
| + return rv;
|
| +}
|
| +
|
| +// static
|
| +std::string SSLClientCertificateSelector::FormatDetailsText(
|
| + CERTCertificate* cert) {
|
| + std::string rv;
|
| +
|
| + rv += l10n_util::GetStringFUTF8(IDS_CERT_SUBJECTNAME_FORMAT,
|
| + UTF8ToUTF16(cert->subjectName));
|
| +
|
| + char* serial_hex = CERT_Hexify(&cert->serialNumber, TRUE);
|
| + rv += "\n ";
|
| + rv += l10n_util::GetStringFUTF8(IDS_CERT_SERIAL_NUMBER_FORMAT,
|
| + UTF8ToUTF16(serial_hex));
|
| + PORT_Free(serial_hex);
|
| +
|
| + PRTime issued, expires;
|
| + if (CERT_GetCertTimes(cert, &issued, &expires) == SECSuccess) {
|
| + string16 issued_str = WideToUTF16(
|
| + base::TimeFormatShortDateAndTime(base::PRTimeToBaseTime(issued)));
|
| + string16 expires_str = WideToUTF16(
|
| + base::TimeFormatShortDateAndTime(base::PRTimeToBaseTime(expires)));
|
| + rv += "\n ";
|
| + rv += l10n_util::GetStringFUTF8(IDS_CERT_VALIDITY_RANGE_FORMAT,
|
| + issued_str, expires_str);
|
| + }
|
| +
|
| + std::vector<std::string> usages;
|
| + psm::GetCertUsageStrings(cert, &usages);
|
| + if (usages.size()) {
|
| + rv += "\n ";
|
| + rv += l10n_util::GetStringFUTF8(IDS_CERT_X509_EXTENDED_KEY_USAGE_FORMAT,
|
| + UTF8ToUTF16(JoinString(usages, ',')));
|
| + }
|
| +
|
| + SECItem key_usage;
|
| + key_usage.data = NULL;
|
| + if (CERT_FindKeyUsageExtension(cert, &key_usage) == SECSuccess) {
|
| + std::string key_usage_str = psm::ProcessKeyUsageBitString(&key_usage, ',');
|
| + PORT_Free(key_usage.data);
|
| + if (!key_usage_str.empty()) {
|
| + rv += "\n ";
|
| + rv += l10n_util::GetStringFUTF8(IDS_CERT_X509_KEY_USAGE_FORMAT,
|
| + UTF8ToUTF16(key_usage_str));
|
| + }
|
| + }
|
| +
|
| + std::vector<std::string> email_addresses;
|
| + for (const char* addr = CERT_GetFirstEmailAddress(cert);
|
| + addr; addr = CERT_GetNextEmailAddress(cert, addr)) {
|
| + // The first email addr (from Subject) may be duplicated in Subject
|
| + // Alternative Name, so check subsequent addresses are not equal to the
|
| + // first one before adding to the list.
|
| + if (!email_addresses.size() || email_addresses[0] != addr)
|
| + email_addresses.push_back(addr);
|
| + }
|
| + if (email_addresses.size()) {
|
| + rv += "\n ";
|
| + rv += l10n_util::GetStringFUTF8(
|
| + IDS_CERT_EMAIL_ADDRESSES_FORMAT,
|
| + UTF8ToUTF16(JoinString(email_addresses, ',')));
|
| + }
|
| +
|
| + rv += '\n';
|
| + rv += l10n_util::GetStringFUTF8(IDS_CERT_ISSUERNAME_FORMAT,
|
| + UTF8ToUTF16(cert->issuerName));
|
| +
|
| + string16 token(UTF8ToUTF16(psm::GetCertTokenName(cert)));
|
| + if (!token.empty()) {
|
| + rv += '\n';
|
| + rv += l10n_util::GetStringFUTF8(IDS_CERT_TOKEN_FORMAT, token);
|
| + }
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +// static
|
| +void SSLClientCertificateSelector::OnComboBoxChanged(
|
| + GtkComboBox* combo_box, SSLClientCertificateSelector* cert_selector) {
|
| + int selected = gtk_combo_box_get_active(
|
| + GTK_COMBO_BOX(cert_selector->cert_combo_box_));
|
| + if (selected < 0)
|
| + return;
|
| + gtk_text_buffer_set_text(cert_selector->cert_details_buffer_,
|
| + cert_selector->details_strings_[selected].c_str(),
|
| + cert_selector->details_strings_[selected].size());
|
| +}
|
| +
|
| +// static
|
| +void SSLClientCertificateSelector::OnResponse(
|
| + GtkDialog* dialog, gint response_id,
|
| + SSLClientCertificateSelector* cert_selector) {
|
| + net::X509Certificate* cert = NULL;
|
| + if (response_id == GTK_RESPONSE_OK ||
|
| + response_id == RESPONSE_SHOW_CERT_INFO) {
|
| + int selected = gtk_combo_box_get_active(
|
| + GTK_COMBO_BOX(cert_selector->cert_combo_box_));
|
| + if (selected >= 0 &&
|
| + selected < static_cast<int>(
|
| + cert_selector->cert_request_info_->client_certs.size()))
|
| + cert = cert_selector->cert_request_info_->client_certs[selected];
|
| + }
|
| + if (response_id == RESPONSE_SHOW_CERT_INFO) {
|
| + if (cert)
|
| + ShowCertificateViewer(GTK_WINDOW(cert_selector->dialog_),
|
| + cert->os_cert_handle());
|
| + return;
|
| + }
|
| + cert_selector->delegate_->CertificateSelected(cert);
|
| + gtk_widget_destroy(GTK_WIDGET(dialog));
|
| +}
|
| +
|
| +// static
|
| +void SSLClientCertificateSelector::OnDestroy(
|
| + GtkDialog* dialog,
|
| + SSLClientCertificateSelector* cert_selector) {
|
| + delete cert_selector;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +// SSLClientAuthHandler platform specific implementation:
|
| +
|
| void SSLClientAuthHandler::DoSelectCertificate() {
|
| - NOTIMPLEMENTED();
|
| - CertificateSelected(NULL);
|
| + // TODO(mattm): Pipe parent gfx::NativeWindow param into here somehow.
|
| + (new SSLClientCertificateSelector(NULL, cert_request_info_, this))->Show();
|
| }
|
|
|