diff options
author | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-04 01:46:57 +0000 |
---|---|---|
committer | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-04 01:46:57 +0000 |
commit | b1f18494233f64c720729039222d9ad66c8ae95f (patch) | |
tree | 0f2848bdedba2a46c3d3b3fc8c7855d2786fd14d | |
parent | b1995975f4e6b17df513a6d0dae44b37c46c5e00 (diff) | |
download | chromium_src-b1f18494233f64c720729039222d9ad66c8ae95f.zip chromium_src-b1f18494233f64c720729039222d9ad66c8ae95f.tar.gz chromium_src-b1f18494233f64c720729039222d9ad66c8ae95f.tar.bz2 |
Linux: implement Client SSL Certificate selection UI
BUG=25241
TEST=Visit site for which you have a client certificate, verify that dialog comes up and you can select your cert (or cancel)
Review URL: http://codereview.chromium.org/661241
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40587 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/generated_resources.grd | 46 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/login_manager_view.cc | 7 | ||||
-rw-r--r-- | chrome/browser/gtk/certificate_viewer.cc | 40 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.cc | 9 | ||||
-rw-r--r-- | chrome/browser/ssl/ssl_client_auth_handler.h | 10 | ||||
-rw-r--r-- | chrome/browser/ssl/ssl_client_auth_handler_gtk.cc | 322 | ||||
-rwxr-xr-x | chrome/chrome_browser.gypi | 4 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 10 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 4 | ||||
-rw-r--r-- | chrome/third_party/mozilla_security_manager/nsNSSCertHelper.cpp | 68 | ||||
-rw-r--r-- | chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h | 1 | ||||
-rw-r--r-- | chrome/third_party/mozilla_security_manager/nsNSSCertificate.cpp | 13 | ||||
-rw-r--r-- | chrome/third_party/mozilla_security_manager/nsNSSCertificate.h | 3 | ||||
-rw-r--r-- | chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.cpp | 73 | ||||
-rw-r--r-- | chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h | 53 |
15 files changed, 573 insertions, 90 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 3d0f3ea..8db5e7e 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -2497,6 +2497,49 @@ each locale. --> Unknown server certificate error </message> + + <!-- Certificate selector dialog strings. These are only used on platforms that don't have a native certificate selection dialog, such as Linux. --> + <message name="IDS_CERT_SELECTOR_DIALOG_TITLE" desc="Title of dialog asking user to select a certificate for SSL client certificate authentication"> + Client Certificate Request: <ph name="REQUESTING_HOST_AND_PORT">$1<ex>pip.verisignlabs.com:443</ex></ph> + </message> + <message name="IDS_CERT_SELECTOR_SITE_DESCRIPTION_LABEL" desc="Text in the certificate selection dialog before the site"> + This site has requested that you identify yourself with a certificate: + </message> + <message name="IDS_CERT_SELECTOR_CHOOSE_DESCRIPTION_LABEL" desc="Text in the certificate selection dialog describing the certificate selector combobox and certificate details"> + Choose a certificate to present as identification: + </message> + <message name="IDS_CERT_SELECTOR_DETAILS_DESCRIPTION_LABEL" desc="Text in the certificate selection dialog in the 'choose a certificate' section describing the certificate details"> + Details of selected certificate: + </message> + <message name="IDS_CERT_SELECTOR_CERT_EXPIRED" desc="In the certificate selection dialog's combobox for choosing certificates, this text will be appended to any of the user's certs which are expired."> + (expired) + </message> + <message name="IDS_CERT_SELECTOR_CERT_NOT_YET_VALID" desc="In the certificate selection dialog's combobox for choosing certificates, this text will be appended to any of the user's certs which are not yet valid."> + (not yet valid) + </message> + <message name="IDS_CERT_SUBJECTNAME_FORMAT" desc="Format for detailed certificate subject in certificate details"> + Issued to: <ph name="NAME">$1<ex>CN=VeriSign Browser Certificate,OU=Device Identifier - r1923847</ex></ph> + </message> + <message name="IDS_CERT_ISSUERNAME_FORMAT" desc="Format for detailed certificate issuer in certificate details"> + Issued by: <ph name="NAME">$1<ex>CN=VeriSign Device CA,O="VeriSign, Inc.",C=US</ex></ph> + </message> + <message name="IDS_CERT_VALIDITY_RANGE_FORMAT" desc="Format for showing the range of dates a certificate is valid in the certificate details"> + Valid from <ph name="START_DATE_TIME">$1<ex>7/2/09 7:18:34 PM</ex></ph> to <ph name="END_DATE_TIME">$2<ex>7/2/10 7:28:34 PM</ex></ph> + </message> + <message name="IDS_CERT_X509_EXTENDED_KEY_USAGE_FORMAT" desc="Format for showing the usages a certificate is valid for in the certificate details"> + Purposes: <ph name="USAGES">$1<ex>SSL Client Certificate,Email Signer Certificate</ex></ph> + </message> + <message name="IDS_CERT_X509_KEY_USAGE_FORMAT" desc="Format for showing the key usages a certificate is valid for in the certificate details"> + Certificate Key Usage: <ph name="USAGES">$1<ex>Signing,Key Encipherment</ex></ph> + </message> + <message name="IDS_CERT_EMAIL_ADDRESSES_FORMAT" desc="Format for showing the email address(es) a certificate is associated with in the certificate details"> + Email: <ph name="EMAIL_ADDRESSES">$1<ex>foo@example.com</ex></ph> + </message> + <message name="IDS_CERT_TOKEN_FORMAT" desc="Format for showing the location a certificate is stored"> + Stored in: <ph name="CERT_LOCATION">$1<ex>NSS Certificate Database</ex></ph> + </message> + + <!-- Certificate viewer dialog strings. These are only used on platforms that don't have a native certificate info dialog, such as Linux. --> <message name="IDS_CERT_INFO_DIALOG_TITLE" desc="Title of dialog displaying info about a certificate"> Certificate Viewer: <ph name="CERTIFICATE_NAME">$1<ex>www.google.com</ex></ph> @@ -2800,6 +2843,9 @@ each locale. --> <message name="IDS_CERT_X509_KEY_USAGE_CRL_SIGNER" desc="description of certificate usage CRL Signer"> CRL Signer </message> + <message name="IDS_CERT_X509_KEY_USAGE_ENCIPHER_ONLY" desc="description of certificate usage Encipher Only"> + Encipher Only + </message> <message name="IDS_CERT_X509_BASIC_CONSTRAINT_IS_CA" desc="description of certificate constraint Is a Certificate Authority"> Is a Certificate Authority </message> diff --git a/chrome/browser/chromeos/login/login_manager_view.cc b/chrome/browser/chromeos/login/login_manager_view.cc index 82c1816..63d1c66 100644 --- a/chrome/browser/chromeos/login/login_manager_view.cc +++ b/chrome/browser/chromeos/login/login_manager_view.cc @@ -321,13 +321,6 @@ void LoginManagerView::SetupSession(const std::string& username) { if (observer_) { observer_->OnExit(chromeos::ScreenObserver::LOGIN_SIGN_IN_SELECTED); } - if (username.find("@google.com") != std::string::npos) { - // This isn't thread-safe. However, the login window is specifically - // supposed to be run in a blocking fashion, before any other threads are - // created by the initial browser process. - CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kAutoSSLClientAuth); - } if (chromeos::LoginLibrary::EnsureLoaded()) chromeos::LoginLibrary::Get()->StartSession(username, ""); } diff --git a/chrome/browser/gtk/certificate_viewer.cc b/chrome/browser/gtk/certificate_viewer.cc index a4cc92b..75a9d4a 100644 --- a/chrome/browser/gtk/certificate_viewer.cc +++ b/chrome/browser/gtk/certificate_viewer.cc @@ -21,6 +21,7 @@ #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" // PSM = Mozilla's Personal Security Manager. @@ -255,37 +256,14 @@ void CertificateViewer::InitGeneralPage() { l10n_util::GetStringUTF8(IDS_CERT_INFO_VERIFIED_USAGES_GROUP)), FALSE, FALSE, 0); - SECCertificateUsage usages = 0; - // TODO(wtc): See if we should use X509Certificate::Verify instead. - if (CERT_VerifyCertificateNow(CERT_GetDefaultCertDB(), cert, PR_TRUE, - certificateUsageCheckAllUsages, - NULL, &usages) == SECSuccess) { - // List of usages to display is borrowed from - // mozilla/source/security/manager/ssl/src/nsUsageArrayHelper.cpp - static const struct { - SECCertificateUsage usage; - int string_id; - } usage_string_map[] = { - {certificateUsageSSLClient, IDS_CERT_USAGE_SSL_CLIENT}, - {certificateUsageSSLServer, IDS_CERT_USAGE_SSL_SERVER}, - {certificateUsageSSLServerWithStepUp, - IDS_CERT_USAGE_SSL_SERVER_WITH_STEPUP}, - {certificateUsageEmailSigner, IDS_CERT_USAGE_EMAIL_SIGNER}, - {certificateUsageEmailRecipient, IDS_CERT_USAGE_EMAIL_RECEIVER}, - {certificateUsageObjectSigner, IDS_CERT_USAGE_OBJECT_SIGNER}, - {certificateUsageSSLCA, IDS_CERT_USAGE_SSL_CA}, - {certificateUsageStatusResponder, IDS_CERT_USAGE_STATUS_RESPONDER}, - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(usage_string_map); ++i) { - if (usages & usage_string_map[i].usage) - gtk_box_pack_start( - GTK_BOX(uses_vbox), - gtk_util::IndentWidget(gtk_util::LeftAlignMisc(gtk_label_new( - l10n_util::GetStringUTF8( - usage_string_map[i].string_id).c_str()))), - FALSE, FALSE, 0); - } - } + std::vector<std::string> usages; + psm::GetCertUsageStrings(cert, &usages); + for (size_t i = 0; i < usages.size(); ++i) + gtk_box_pack_start( + GTK_BOX(uses_vbox), + gtk_util::IndentWidget(gtk_util::LeftAlignMisc(gtk_label_new( + usages[i].c_str()))), + FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(general_page_vbox_), gtk_hseparator_new(), FALSE, FALSE, 0); diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index c7e6050..8837247 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -969,14 +969,6 @@ void ResourceDispatcherHost::OnCertificateRequested( net::SSLCertRequestInfo* cert_request_info) { DCHECK(request); -#if defined(OS_LINUX) - bool select_first_cert = CommandLine::ForCurrentProcess()->HasSwitch( - switches::kAutoSSLClientAuth); - net::X509Certificate* cert = - select_first_cert && !cert_request_info->client_certs.empty() ? - cert_request_info->client_certs[0] : NULL; - request->ContinueWithCertificate(cert); -#else if (cert_request_info->client_certs.empty()) { // No need to query the user if there are no certs to choose from. request->ContinueWithCertificate(NULL); @@ -989,7 +981,6 @@ void ResourceDispatcherHost::OnCertificateRequested( info->set_ssl_client_auth_handler( new SSLClientAuthHandler(request, cert_request_info)); info->ssl_client_auth_handler()->SelectCertificate(); -#endif } void ResourceDispatcherHost::OnSSLCertificateError( diff --git a/chrome/browser/ssl/ssl_client_auth_handler.h b/chrome/browser/ssl/ssl_client_auth_handler.h index 537b0ea..f63d4c9 100644 --- a/chrome/browser/ssl/ssl_client_auth_handler.h +++ b/chrome/browser/ssl/ssl_client_auth_handler.h @@ -33,6 +33,11 @@ class SSLClientAuthHandler // Should only be called on the IO thread. void OnRequestCancelled(); + // Calls DoCertificateSelected on the I/O thread. + // Called on the UI thread after the user has made a selection (which may + // be long after DoSelectCertificate returns, if the UI is modeless/async.) + void CertificateSelected(net::X509Certificate* cert); + private: friend class base::RefCountedThreadSafe<SSLClientAuthHandler>; @@ -42,11 +47,6 @@ class SSLClientAuthHandler // Called on the UI thread. void DoSelectCertificate(); - // Calls DoCertificateSelected on the I/O thread. - // Called on the UI thread after the user has made a selection (which may - // be long after DoSelectCertificate returns, if the UI is modeless/async.) - void CertificateSelected(net::X509Certificate* cert); - // Notifies that the user has selected a cert. // Called on the IO thread. void DoCertificateSelected(net::X509Certificate* cert); diff --git a/chrome/browser/ssl/ssl_client_auth_handler_gtk.cc b/chrome/browser/ssl/ssl_client_auth_handler_gtk.cc index 7ea1aec..49fa4c5 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(); } diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 1435d94..8c54ce5 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2291,6 +2291,8 @@ 'third_party/mozilla_security_manager/nsNSSCertHelper.h', 'third_party/mozilla_security_manager/nsNSSCertificate.cpp', 'third_party/mozilla_security_manager/nsNSSCertificate.h', + 'third_party/mozilla_security_manager/nsUsageArrayHelper.cpp', + 'third_party/mozilla_security_manager/nsUsageArrayHelper.h', ], 'conditions': [ ['linux_breakpad==1', { @@ -2733,6 +2735,8 @@ ['include', '^browser/gtk/cairo_cached_surface.h'], ['include', '^browser/gtk/clear_browsing_data_dialog_gtk.cc'], ['include', '^browser/gtk/clear_browsing_data_dialog_gtk.h'], + ['include', '^browser/gtk/certificate_viewer.cc'], + ['include', '^browser/gtk/certificate_viewer.h'], ['include', '^browser/gtk/constrained_window_gtk.cc'], ['include', '^browser/gtk/constrained_window_gtk.h'], ['include', '^browser/gtk/download_started_animation_gtk.cc'], diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 3b538b33..ac8ee7b 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -795,16 +795,6 @@ const char kChromeosFrame[] = "chromeos-frame"; const char kUseNSSForSSL[] = "use-nss-for-ssl"; #endif -#if defined(OS_LINUX) -// A temporary switch before we implement the client certificate selection UI. -// When an SSL server requests client authentication, select a client -// certificate automatically. -// WARNING: This switch has privacy issues because it reveals the user's -// identity to any server that requests a client certificate without the -// user's consent. -const char kAutoSSLClientAuth[] = "auto-ssl-client-auth"; -#endif - #if defined(OS_POSIX) // Bypass the error dialog when the profile lock couldn't be attained. // A flag, generated internally by Chrome for renderer and other helper process diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index c40675d..5ceb5fb 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -233,10 +233,6 @@ extern const char kChromeosFrame[]; extern const char kUseNSSForSSL[]; #endif -#if defined(OS_LINUX) -extern const char kAutoSSLClientAuth[]; -#endif - #if defined(OS_POSIX) extern const char kEnableCrashReporter[]; extern const char kNoProcessSingletonDialog[]; diff --git a/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.cpp b/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.cpp index 6f6d9f2..3c16cf0 100644 --- a/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.cpp +++ b/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.cpp @@ -688,13 +688,34 @@ std::string ProcessBMPString(SECItem* extension_data) { } struct MaskIdPair { - unsigned char mask; + unsigned int mask; int string_id; }; +static std::string ProcessBitField(SECItem* bitfield, + const MaskIdPair* string_map, + size_t len, + char separator) { + unsigned int bits = 0; + std::string rv; + // NSS bit flags like KU_DIGITAL_SIGNATURE, etc. are defined with the + // assumption that the bitfields have at most 8 bits. + if (bitfield->len) + bits = bitfield->data[0]; + for (size_t i = 0; i < len; ++i) { + if (bits & string_map[i].mask) { + if (!rv.empty()) + rv += separator; + rv += l10n_util::GetStringUTF8(string_map[i].string_id); + } + } + return rv; +} + static std::string ProcessBitStringExtension(SECItem* extension_data, const MaskIdPair* string_map, - size_t len) { + size_t len, + char separator) { SECItem decoded; decoded.type = siBuffer; decoded.data = NULL; @@ -702,19 +723,13 @@ static std::string ProcessBitStringExtension(SECItem* extension_data, if (SEC_ASN1DecodeItem(NULL, &decoded, SEC_ASN1_GET(SEC_BitStringTemplate), extension_data) != SECSuccess) return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); - - std::string rv; - for (size_t i = 0; i < len; ++i) { - if (decoded.data[0] & string_map[i].mask) { - rv += l10n_util::GetStringUTF8(string_map[i].string_id) + '\n'; - } - } + std::string rv = ProcessBitField(&decoded, string_map, len, separator); PORT_Free(decoded.data); return rv; } std::string ProcessNSCertTypeExtension(SECItem* extension_data) { - MaskIdPair usage_string_map[] = { + static const MaskIdPair usage_string_map[] = { {NS_CERT_TYPE_SSL_CLIENT, IDS_CERT_USAGE_SSL_CLIENT}, {NS_CERT_TYPE_SSL_SERVER, IDS_CERT_USAGE_SSL_SERVER}, {NS_CERT_TYPE_EMAIL, IDS_CERT_EXT_NS_CERT_TYPE_EMAIL}, @@ -724,21 +739,30 @@ std::string ProcessNSCertTypeExtension(SECItem* extension_data) { {NS_CERT_TYPE_OBJECT_SIGNING_CA, IDS_CERT_USAGE_OBJECT_SIGNER}, }; return ProcessBitStringExtension(extension_data, usage_string_map, - ARRAYSIZE_UNSAFE(usage_string_map)); + ARRAYSIZE_UNSAFE(usage_string_map), '\n'); +} + +static const MaskIdPair key_usage_string_map[] = { + {KU_DIGITAL_SIGNATURE, IDS_CERT_X509_KEY_USAGE_SIGNING}, + {KU_NON_REPUDIATION, IDS_CERT_X509_KEY_USAGE_NONREP}, + {KU_KEY_ENCIPHERMENT, IDS_CERT_X509_KEY_USAGE_ENCIPHERMENT}, + {KU_DATA_ENCIPHERMENT, IDS_CERT_X509_KEY_USAGE_DATA_ENCIPHERMENT}, + {KU_KEY_AGREEMENT, IDS_CERT_X509_KEY_USAGE_KEY_AGREEMENT}, + {KU_KEY_CERT_SIGN, IDS_CERT_X509_KEY_USAGE_CERT_SIGNER}, + {KU_CRL_SIGN, IDS_CERT_X509_KEY_USAGE_CRL_SIGNER}, + {KU_ENCIPHER_ONLY, IDS_CERT_X509_KEY_USAGE_ENCIPHER_ONLY}, + // NSS is missing a flag for dechiperOnly, see: + // https://bugzilla.mozilla.org/show_bug.cgi?id=549952 +}; + +std::string ProcessKeyUsageBitString(SECItem* bitstring, char sep) { + return ProcessBitField(bitstring, key_usage_string_map, + arraysize(key_usage_string_map), sep); } std::string ProcessKeyUsageExtension(SECItem* extension_data) { - MaskIdPair usage_string_map[] = { - {KU_DIGITAL_SIGNATURE, IDS_CERT_X509_KEY_USAGE_SIGNING}, - {KU_NON_REPUDIATION, IDS_CERT_X509_KEY_USAGE_NONREP}, - {KU_KEY_ENCIPHERMENT, IDS_CERT_X509_KEY_USAGE_ENCIPHERMENT}, - {KU_DATA_ENCIPHERMENT, IDS_CERT_X509_KEY_USAGE_DATA_ENCIPHERMENT}, - {KU_KEY_AGREEMENT, IDS_CERT_X509_KEY_USAGE_KEY_AGREEMENT}, - {KU_KEY_CERT_SIGN, IDS_CERT_X509_KEY_USAGE_CERT_SIGNER}, - {KU_CRL_SIGN, IDS_CERT_X509_KEY_USAGE_CRL_SIGNER}, - }; - return ProcessBitStringExtension(extension_data, usage_string_map, - ARRAYSIZE_UNSAFE(usage_string_map)); + return ProcessBitStringExtension(extension_data, key_usage_string_map, + arraysize(key_usage_string_map), '\n'); } std::string ProcessExtKeyUsage(SECItem* extension_data) { diff --git a/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h b/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h index ba0a445..5741c69 100644 --- a/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h +++ b/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h @@ -88,6 +88,7 @@ std::string ProcessAuthInfoAccess(SECItem* extension_data); std::string ProcessIA5String(SECItem* extension_data); std::string ProcessBMPString(SECItem* extension_data); std::string ProcessNSCertTypeExtension(SECItem* extension_data); +std::string ProcessKeyUsageBitString(SECItem* bitstring, char sep); std::string ProcessKeyUsageExtension(SECItem* extension_data); std::string ProcessExtKeyUsage(SECItem* extension_data); std::string ProcessExtensionData(SECOidTag oid_tag, SECItem* extension_data); diff --git a/chrome/third_party/mozilla_security_manager/nsNSSCertificate.cpp b/chrome/third_party/mozilla_security_manager/nsNSSCertificate.cpp index 9d1abe8..74624ab 100644 --- a/chrome/third_party/mozilla_security_manager/nsNSSCertificate.cpp +++ b/chrome/third_party/mozilla_security_manager/nsNSSCertificate.cpp @@ -40,6 +40,11 @@ #include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h" +#include <pk11func.h> + +#include "app/l10n_util.h" +#include "grit/generated_resources.h" + namespace mozilla_security_manager { std::string GetCertTitle(CERTCertificate* cert) { @@ -62,4 +67,12 @@ std::string GetCertTitle(CERTCertificate* cert) { return rv; } +std::string GetCertTokenName(CERTCertificate* cert) { + std::string token; + if (cert->slot) { + token = PK11_GetTokenName(cert->slot); + } + return token; +} + } // namespace mozilla_security_manager diff --git a/chrome/third_party/mozilla_security_manager/nsNSSCertificate.h b/chrome/third_party/mozilla_security_manager/nsNSSCertificate.h index 8ac10e6..b436601 100644 --- a/chrome/third_party/mozilla_security_manager/nsNSSCertificate.h +++ b/chrome/third_party/mozilla_security_manager/nsNSSCertificate.h @@ -50,6 +50,9 @@ namespace mozilla_security_manager { // Based on nsNSSCertificate::GetWindowTitle. std::string GetCertTitle(CERTCertificate* cert); +// Based on nsNSSCertificate::GetTokenName. +std::string GetCertTokenName(CERTCertificate* cert); + } // namespace mozilla_security_manager #endif // CHROME_THIRD_PARTY_MOZILLA_SECURITY_MANAGER_NSNSSCERTIFICATE_H_ diff --git a/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.cpp b/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.cpp new file mode 100644 index 0000000..234e4dd --- /dev/null +++ b/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.cpp @@ -0,0 +1,73 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gardiner Myers <jgmyers@speakeasy.net> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h" + +#include "app/l10n_util.h" +#include "grit/generated_resources.h" + +namespace mozilla_security_manager { + +void GetCertUsageStrings(CERTCertificate* cert, std::vector<std::string>* out) { + SECCertificateUsage usages = 0; + // TODO(wtc): See if we should use X509Certificate::Verify instead. + if (CERT_VerifyCertificateNow(CERT_GetDefaultCertDB(), cert, PR_TRUE, + certificateUsageCheckAllUsages, + NULL, &usages) == SECSuccess) { + static const struct { + SECCertificateUsage usage; + int string_id; + } usage_string_map[] = { + {certificateUsageSSLClient, IDS_CERT_USAGE_SSL_CLIENT}, + {certificateUsageSSLServer, IDS_CERT_USAGE_SSL_SERVER}, + {certificateUsageSSLServerWithStepUp, + IDS_CERT_USAGE_SSL_SERVER_WITH_STEPUP}, + {certificateUsageEmailSigner, IDS_CERT_USAGE_EMAIL_SIGNER}, + {certificateUsageEmailRecipient, IDS_CERT_USAGE_EMAIL_RECEIVER}, + {certificateUsageObjectSigner, IDS_CERT_USAGE_OBJECT_SIGNER}, + {certificateUsageSSLCA, IDS_CERT_USAGE_SSL_CA}, + {certificateUsageStatusResponder, IDS_CERT_USAGE_STATUS_RESPONDER}, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(usage_string_map); ++i) { + if (usages & usage_string_map[i].usage) + out->push_back(l10n_util::GetStringUTF8( + usage_string_map[i].string_id)); + } + } +} + +} // namespace mozilla_security_manager diff --git a/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h b/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h new file mode 100644 index 0000000..31ba550 --- /dev/null +++ b/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h @@ -0,0 +1,53 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gardiner Myers <jgmyers@speakeasy.net> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef CHROME_THIRD_PARTY_MOZILLA_SECURITY_MANAGER_NSUSAGEARRAYHELPER_H_ +#define CHROME_THIRD_PARTY_MOZILLA_SECURITY_MANAGER_NSUSAGEARRAYHELPER_H_ + +#include <cert.h> + +#include <string> +#include <vector> + +namespace mozilla_security_manager { + +// Based on nsUsageArrayHelper::GetUsagesArray. +void GetCertUsageStrings(CERTCertificate* cert, std::vector<std::string>* out); + +} // namespace mozilla_security_manager + +#endif // CHROME_THIRD_PARTY_MOZILLA_SECURITY_MANAGER_NSUSAGEARRAYHELPER_H_ |