summaryrefslogtreecommitdiffstats
path: root/security/manager/pki/resources/content/exceptionDialog.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/pki/resources/content/exceptionDialog.js')
-rw-r--r--security/manager/pki/resources/content/exceptionDialog.js368
1 files changed, 368 insertions, 0 deletions
diff --git a/security/manager/pki/resources/content/exceptionDialog.js b/security/manager/pki/resources/content/exceptionDialog.js
new file mode 100644
index 000000000..0ca24a614
--- /dev/null
+++ b/security/manager/pki/resources/content/exceptionDialog.js
@@ -0,0 +1,368 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+var gDialog;
+var gBundleBrand;
+var gPKIBundle;
+var gSSLStatus;
+var gCert;
+var gChecking;
+var gBroken;
+var gNeedReset;
+var gSecHistogram;
+var gNsISecTel;
+
+Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+function badCertListener() {}
+badCertListener.prototype = {
+ getInterface: function (aIID) {
+ return this.QueryInterface(aIID);
+ },
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Components.interfaces.nsIBadCertListener2) ||
+ aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
+ aIID.equals(Components.interfaces.nsISupports)) {
+ return this;
+ }
+
+ throw new Error(Components.results.NS_ERROR_NO_INTERFACE);
+ },
+ handle_test_result: function () {
+ if (gSSLStatus) {
+ gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert;
+ }
+ },
+ notifyCertProblem: function MSR_notifyCertProblem(socketInfo, sslStatus, targetHost) {
+ gBroken = true;
+ gSSLStatus = sslStatus;
+ this.handle_test_result();
+ return true; // suppress error UI
+ }
+};
+
+function initExceptionDialog() {
+ gNeedReset = false;
+ gDialog = document.documentElement;
+ gBundleBrand = document.getElementById("brand_bundle");
+ gPKIBundle = document.getElementById("pippki_bundle");
+ gSecHistogram = Components.classes["@mozilla.org/base/telemetry;1"].
+ getService(Components.interfaces.nsITelemetry).
+ getHistogramById("SECURITY_UI");
+ gNsISecTel = Components.interfaces.nsISecurityUITelemetry;
+
+ var brandName = gBundleBrand.getString("brandShortName");
+ setText("warningText", gPKIBundle.getFormattedString("addExceptionBrandedWarning2", [brandName]));
+ gDialog.getButton("extra1").disabled = true;
+
+ var args = window.arguments;
+ if (args && args[0]) {
+ if (args[0].location) {
+ // We were pre-seeded with a location.
+ document.getElementById("locationTextBox").value = args[0].location;
+ document.getElementById('checkCertButton').disabled = false;
+
+ if (args[0].sslStatus) {
+ gSSLStatus = args[0].sslStatus;
+ gCert = gSSLStatus.serverCert;
+ gBroken = true;
+ updateCertStatus();
+ } else if (args[0].prefetchCert) {
+ // We can optionally pre-fetch the certificate too. Don't do this
+ // synchronously, since it would prevent the window from appearing
+ // until the fetch is completed, which could be multiple seconds.
+ // Instead, let's use a timer to spawn the actual fetch, but update
+ // the dialog to "checking..." state right away, so that the UI
+ // is appropriately responsive. Bug 453855
+ document.getElementById("checkCertButton").disabled = true;
+ gChecking = true;
+ updateCertStatus();
+
+ window.setTimeout(checkCert, 0);
+ }
+ }
+
+ // Set out parameter to false by default
+ args[0].exceptionAdded = false;
+ }
+}
+
+/**
+ * Attempt to download the certificate for the location specified, and populate
+ * the Certificate Status section with the result.
+ */
+function checkCert() {
+ gCert = null;
+ gSSLStatus = null;
+ gChecking = true;
+ gBroken = false;
+ updateCertStatus();
+
+ var uri = getURI();
+
+ var req = new XMLHttpRequest();
+ try {
+ if (uri) {
+ req.open('GET', uri.prePath, false);
+ req.channel.notificationCallbacks = new badCertListener();
+ req.send(null);
+ }
+ } catch (e) {
+ // We *expect* exceptions if there are problems with the certificate
+ // presented by the site. Log it, just in case, but we can proceed here,
+ // with appropriate sanity checks
+ Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " +
+ "This results in a (mostly harmless) exception being thrown. " +
+ "Logged for information purposes only: " + e);
+ } finally {
+ gChecking = false;
+ }
+
+ if (req.channel && req.channel.securityInfo) {
+ const Ci = Components.interfaces;
+ gSSLStatus = req.channel.securityInfo
+ .QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+ gCert = gSSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
+ }
+
+ updateCertStatus();
+}
+
+/**
+ * Build and return a URI, based on the information supplied in the
+ * Certificate Location fields
+ */
+function getURI() {
+ // Use fixup service instead of just ioservice's newURI since it's quite
+ // likely that the host will be supplied without a protocol prefix, resulting
+ // in malformed uri exceptions being thrown.
+ let fus = Components.classes["@mozilla.org/docshell/urifixup;1"]
+ .getService(Components.interfaces.nsIURIFixup);
+ let locationTextBox = document.getElementById("locationTextBox");
+ let uri = fus.createFixupURI(locationTextBox.value, 0);
+
+ if (!uri) {
+ return null;
+ }
+
+ if (uri.scheme == "http") {
+ uri.scheme = "https";
+ }
+
+ if (uri.port == -1) {
+ uri.port = 443;
+ }
+
+ return uri;
+}
+
+function resetDialog() {
+ document.getElementById("viewCertButton").disabled = true;
+ document.getElementById("permanent").disabled = true;
+ gDialog.getButton("extra1").disabled = true;
+ setText("headerDescription", "");
+ setText("statusDescription", "");
+ setText("statusLongDescription", "");
+ setText("status2Description", "");
+ setText("status2LongDescription", "");
+ setText("status3Description", "");
+ setText("status3LongDescription", "");
+}
+
+/**
+ * Called by input textboxes to manage UI state
+ */
+function handleTextChange() {
+ var checkCertButton = document.getElementById('checkCertButton');
+ checkCertButton.disabled = !(document.getElementById("locationTextBox").value);
+ if (gNeedReset) {
+ gNeedReset = false;
+ resetDialog();
+ }
+}
+
+function updateCertStatus() {
+ var shortDesc, longDesc;
+ var shortDesc2, longDesc2;
+ var shortDesc3, longDesc3;
+ var use2 = false;
+ var use3 = false;
+ let bucketId = gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_BASE;
+ if (gCert) {
+ if (gBroken) {
+ var mms = "addExceptionDomainMismatchShort";
+ var mml = "addExceptionDomainMismatchLong2";
+ var exs = "addExceptionExpiredShort";
+ var exl = "addExceptionExpiredLong2";
+ var uts = "addExceptionUnverifiedOrBadSignatureShort";
+ var utl = "addExceptionUnverifiedOrBadSignatureLong2";
+ var use1 = false;
+ if (gSSLStatus.isDomainMismatch) {
+ bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_DOMAIN;
+ use1 = true;
+ shortDesc = mms;
+ longDesc = mml;
+ }
+ if (gSSLStatus.isNotValidAtThisTime) {
+ bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_TIME;
+ if (!use1) {
+ use1 = true;
+ shortDesc = exs;
+ longDesc = exl;
+ }
+ else {
+ use2 = true;
+ shortDesc2 = exs;
+ longDesc2 = exl;
+ }
+ }
+ if (gSSLStatus.isUntrusted) {
+ bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_UNTRUSTED;
+ if (!use1) {
+ use1 = true;
+ shortDesc = uts;
+ longDesc = utl;
+ } else if (!use2) {
+ use2 = true;
+ shortDesc2 = uts;
+ longDesc2 = utl;
+ } else {
+ use3 = true;
+ shortDesc3 = uts;
+ longDesc3 = utl;
+ }
+ }
+ gSecHistogram.add(bucketId);
+
+ // In these cases, we do want to enable the "Add Exception" button
+ gDialog.getButton("extra1").disabled = false;
+
+ // If the Private Browsing service is available and the mode is active,
+ // don't store permanent exceptions, since they would persist after
+ // private browsing mode was disabled.
+ var inPrivateBrowsing = inPrivateBrowsingMode();
+ var pe = document.getElementById("permanent");
+ pe.disabled = inPrivateBrowsing;
+ pe.checked = !inPrivateBrowsing;
+
+ setText("headerDescription", gPKIBundle.getString("addExceptionInvalidHeader"));
+ }
+ else {
+ shortDesc = "addExceptionValidShort";
+ longDesc = "addExceptionValidLong";
+ gDialog.getButton("extra1").disabled = true;
+ document.getElementById("permanent").disabled = true;
+ }
+
+ // We're done checking the certificate, so allow the user to check it again.
+ document.getElementById("checkCertButton").disabled = false;
+ document.getElementById("viewCertButton").disabled = false;
+
+ // Notify observers about the availability of the certificate
+ Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService)
+ .notifyObservers(null, "cert-exception-ui-ready", null);
+ }
+ else if (gChecking) {
+ shortDesc = "addExceptionCheckingShort";
+ longDesc = "addExceptionCheckingLong2";
+ // We're checking the certificate, so we disable the Get Certificate
+ // button to make sure that the user can't interrupt the process and
+ // trigger another certificate fetch.
+ document.getElementById("checkCertButton").disabled = true;
+ document.getElementById("viewCertButton").disabled = true;
+ gDialog.getButton("extra1").disabled = true;
+ document.getElementById("permanent").disabled = true;
+ }
+ else {
+ shortDesc = "addExceptionNoCertShort";
+ longDesc = "addExceptionNoCertLong2";
+ // We're done checking the certificate, so allow the user to check it again.
+ document.getElementById("checkCertButton").disabled = false;
+ document.getElementById("viewCertButton").disabled = true;
+ gDialog.getButton("extra1").disabled = true;
+ document.getElementById("permanent").disabled = true;
+ }
+
+ setText("statusDescription", gPKIBundle.getString(shortDesc));
+ setText("statusLongDescription", gPKIBundle.getString(longDesc));
+
+ if (use2) {
+ setText("status2Description", gPKIBundle.getString(shortDesc2));
+ setText("status2LongDescription", gPKIBundle.getString(longDesc2));
+ }
+
+ if (use3) {
+ setText("status3Description", gPKIBundle.getString(shortDesc3));
+ setText("status3LongDescription", gPKIBundle.getString(longDesc3));
+ }
+
+ gNeedReset = true;
+}
+
+/**
+ * Handle user request to display certificate details
+ */
+function viewCertButtonClick() {
+ gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_CLICK_VIEW_CERT);
+ if (gCert) {
+ viewCertHelper(this, gCert);
+ }
+}
+
+/**
+ * Handle user request to add an exception for the specified cert
+ */
+function addException() {
+ if (!gCert || !gSSLStatus) {
+ return;
+ }
+
+ var overrideService = Components.classes["@mozilla.org/security/certoverride;1"]
+ .getService(Components.interfaces.nsICertOverrideService);
+ var flags = 0;
+ let confirmBucketId = gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_BASE;
+ if (gSSLStatus.isUntrusted) {
+ flags |= overrideService.ERROR_UNTRUSTED;
+ confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED;
+ }
+ if (gSSLStatus.isDomainMismatch) {
+ flags |= overrideService.ERROR_MISMATCH;
+ confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN;
+ }
+ if (gSSLStatus.isNotValidAtThisTime) {
+ flags |= overrideService.ERROR_TIME;
+ confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_TIME;
+ }
+
+ var permanentCheckbox = document.getElementById("permanent");
+ var shouldStorePermanently = permanentCheckbox.checked && !inPrivateBrowsingMode();
+ if (!permanentCheckbox.checked) {
+ gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_DONT_REMEMBER_EXCEPTION);
+ }
+
+ gSecHistogram.add(confirmBucketId);
+ var uri = getURI();
+ overrideService.rememberValidityOverride(
+ uri.asciiHost, uri.port,
+ gCert,
+ flags,
+ !shouldStorePermanently);
+
+ let args = window.arguments;
+ if (args && args[0]) {
+ args[0].exceptionAdded = true;
+ }
+
+ gDialog.acceptDialog();
+}
+
+/**
+ * Returns true if this dialog is in private browsing mode.
+ */
+function inPrivateBrowsingMode() {
+ return PrivateBrowsingUtils.isWindowPrivate(window);
+}