security/manager/pki/resources/content/exceptionDialog.js

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 var gDialog;
     7 var gBundleBrand;
     8 var gPKIBundle;
     9 var gSSLStatus;
    10 var gCert;
    11 var gChecking;
    12 var gBroken;
    13 var gNeedReset;
    14 var gSecHistogram;
    15 var gNsISecTel;
    17 Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
    19 function badCertListener() {}
    20 badCertListener.prototype = {
    21   getInterface: function (aIID) {
    22     return this.QueryInterface(aIID);
    23   },
    24   QueryInterface: function(aIID) {
    25     if (aIID.equals(Components.interfaces.nsIBadCertListener2) ||
    26         aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
    27         aIID.equals(Components.interfaces.nsISupports))
    28       return this;
    30     throw Components.results.NS_ERROR_NO_INTERFACE;
    31   },  
    32   handle_test_result: function () {
    33     if (gSSLStatus)
    34       gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert;
    35   },
    36   notifyCertProblem: function MSR_notifyCertProblem(socketInfo, sslStatus, targetHost) {
    37     gBroken = true;
    38     gSSLStatus = sslStatus;
    39     this.handle_test_result();
    40     return true; // suppress error UI
    41   }
    42 }
    44 function initExceptionDialog() {
    45   gNeedReset = false;
    46   gDialog = document.documentElement;
    47   gBundleBrand = document.getElementById("brand_bundle");
    48   gPKIBundle = document.getElementById("pippki_bundle");
    49   gSecHistogram = Components.classes["@mozilla.org/base/telemetry;1"].
    50                     getService(Components.interfaces.nsITelemetry).
    51                     getHistogramById("SECURITY_UI");
    52   gNsISecTel = Components.interfaces.nsISecurityUITelemetry;
    54   var brandName = gBundleBrand.getString("brandShortName");
    55   setText("warningText", gPKIBundle.getFormattedString("addExceptionBrandedWarning2", [brandName]));
    56   gDialog.getButton("extra1").disabled = true;
    58   var args = window.arguments;
    59   if (args && args[0]) {
    60     if (args[0].location) {
    61       // We were pre-seeded with a location.
    62       document.getElementById("locationTextBox").value = args[0].location;
    63       document.getElementById('checkCertButton').disabled = false;
    65       // We can optionally pre-fetch the certificate too.  Don't do this
    66       // synchronously, since it would prevent the window from appearing
    67       // until the fetch is completed, which could be multiple seconds.
    68       // Instead, let's use a timer to spawn the actual fetch, but update
    69       // the dialog to "checking..." state right away, so that the UI
    70       // is appropriately responsive.  Bug 453855
    71       if (args[0].prefetchCert) {
    73         document.getElementById("checkCertButton").disabled = true;
    74         gChecking = true;
    75         updateCertStatus();
    77         window.setTimeout(checkCert, 0);
    78       }
    79     }
    81     // Set out parameter to false by default
    82     args[0].exceptionAdded = false; 
    83   }
    84 }
    86 // returns true if found and global status could be set
    87 function findRecentBadCert(uri) {
    88   try {
    89     var certDB = Components.classes["@mozilla.org/security/x509certdb;1"]
    90                            .getService(Components.interfaces.nsIX509CertDB);
    91     if (!certDB)
    92       return false;
    93     var recentCertsSvc = certDB.getRecentBadCerts(inPrivateBrowsingMode());
    94     if (!recentCertsSvc)
    95       return false;
    97     var hostWithPort = uri.host + ":" + uri.port;
    98     gSSLStatus = recentCertsSvc.getRecentBadCert(hostWithPort);
    99     if (!gSSLStatus)
   100       return false;
   102     gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert;
   103     if (!gCert)
   104       return false;
   106     gBroken = true;
   107   }
   108   catch (e) {
   109     return false;
   110   }
   111   updateCertStatus();  
   112   return true;
   113 }
   115 /**
   116  * Attempt to download the certificate for the location specified, and populate
   117  * the Certificate Status section with the result.
   118  */
   119 function checkCert() {
   121   gCert = null;
   122   gSSLStatus = null;
   123   gChecking = true;
   124   gBroken = false;
   125   updateCertStatus();
   127   var uri = getURI();
   129   // Is the cert already known in the list of recently seen bad certs?
   130   if (findRecentBadCert(uri) == true)
   131     return;
   133   var req = new XMLHttpRequest();
   134   try {
   135     if(uri) {
   136       req.open('GET', uri.prePath, false);
   137       req.channel.notificationCallbacks = new badCertListener();
   138       req.send(null);
   139     }
   140   } catch (e) {
   141     // We *expect* exceptions if there are problems with the certificate
   142     // presented by the site.  Log it, just in case, but we can proceed here,
   143     // with appropriate sanity checks
   144     Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " +
   145                                  "This results in a (mostly harmless) exception being thrown. " +
   146                                  "Logged for information purposes only: " + e);
   147   } finally {
   148     gChecking = false;
   149   }
   151   if(req.channel && req.channel.securityInfo) {
   152     const Ci = Components.interfaces;
   153     gSSLStatus = req.channel.securityInfo
   154                     .QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
   155     gCert = gSSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
   156   }
   157   updateCertStatus();  
   158 }
   160 /**
   161  * Build and return a URI, based on the information supplied in the
   162  * Certificate Location fields
   163  */
   164 function getURI() {
   165   // Use fixup service instead of just ioservice's newURI since it's quite likely
   166   // that the host will be supplied without a protocol prefix, resulting in malformed
   167   // uri exceptions being thrown.
   168   var fus = Components.classes["@mozilla.org/docshell/urifixup;1"]
   169                       .getService(Components.interfaces.nsIURIFixup);
   170   var uri = fus.createFixupURI(document.getElementById("locationTextBox").value, 0);
   172   if(!uri)
   173     return null;
   175   if(uri.scheme == "http")
   176     uri.scheme = "https";
   178   if (uri.port == -1)
   179     uri.port = 443;
   181   return uri;
   182 }
   184 function resetDialog() {
   185   document.getElementById("viewCertButton").disabled = true;
   186   document.getElementById("permanent").disabled = true;
   187   gDialog.getButton("extra1").disabled = true;
   188   setText("headerDescription", "");
   189   setText("statusDescription", "");
   190   setText("statusLongDescription", "");
   191   setText("status2Description", "");
   192   setText("status2LongDescription", "");
   193   setText("status3Description", "");
   194   setText("status3LongDescription", "");
   195 }
   197 /**
   198  * Called by input textboxes to manage UI state
   199  */
   200 function handleTextChange() {
   201   var checkCertButton = document.getElementById('checkCertButton');
   202   checkCertButton.disabled = !(document.getElementById("locationTextBox").value);
   203   if (gNeedReset) {
   204     gNeedReset = false;
   205     resetDialog();
   206   }
   207 }
   209 function updateCertStatus() {
   210   var shortDesc, longDesc;
   211   var shortDesc2, longDesc2;
   212   var shortDesc3, longDesc3;
   213   var use2 = false;
   214   var use3 = false;
   215   let bucketId = gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_BASE;
   216   if(gCert) {
   217     if(gBroken) { 
   218       var mms = "addExceptionDomainMismatchShort";
   219       var mml = "addExceptionDomainMismatchLong";
   220       var exs = "addExceptionExpiredShort";
   221       var exl = "addExceptionExpiredLong";
   222       var uts = "addExceptionUnverifiedOrBadSignatureShort";
   223       var utl = "addExceptionUnverifiedOrBadSignatureLong";
   224       var use1 = false;
   225       if (gSSLStatus.isDomainMismatch) {
   226         bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_DOMAIN;
   227         use1 = true;
   228         shortDesc = mms;
   229         longDesc  = mml;
   230       }
   231       if (gSSLStatus.isNotValidAtThisTime) {
   232         bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_TIME;
   233         if (!use1) {
   234           use1 = true;
   235           shortDesc = exs;
   236           longDesc  = exl;
   237         }
   238         else {
   239           use2 = true;
   240           shortDesc2 = exs;
   241           longDesc2  = exl;
   242         }
   243       }
   244       if (gSSLStatus.isUntrusted) {
   245         bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_UNTRUSTED;
   246         if (!use1) {
   247           use1 = true;
   248           shortDesc = uts;
   249           longDesc  = utl;
   250         }
   251         else if (!use2) {
   252           use2 = true;
   253           shortDesc2 = uts;
   254           longDesc2  = utl;
   255         } 
   256         else {
   257           use3 = true;
   258           shortDesc3 = uts;
   259           longDesc3  = utl;
   260         }
   261       }
   262       gSecHistogram.add(bucketId);
   264       // In these cases, we do want to enable the "Add Exception" button
   265       gDialog.getButton("extra1").disabled = false;
   267       // If the Private Browsing service is available and the mode is active,
   268       // don't store permanent exceptions, since they would persist after
   269       // private browsing mode was disabled.
   270       var inPrivateBrowsing = inPrivateBrowsingMode();
   271       var pe = document.getElementById("permanent");
   272       pe.disabled = inPrivateBrowsing;
   273       pe.checked = !inPrivateBrowsing;
   275       setText("headerDescription", gPKIBundle.getString("addExceptionInvalidHeader"));
   276     }
   277     else {
   278       shortDesc = "addExceptionValidShort";
   279       longDesc  = "addExceptionValidLong";
   280       gDialog.getButton("extra1").disabled = true;
   281       document.getElementById("permanent").disabled = true;
   282     }
   284     // We're done checking the certificate, so allow the user to check it again.
   285     document.getElementById("checkCertButton").disabled = false;
   286     document.getElementById("viewCertButton").disabled = false;
   288     // Notify observers about the availability of the certificate
   289     Components.classes["@mozilla.org/observer-service;1"]
   290               .getService(Components.interfaces.nsIObserverService)
   291               .notifyObservers(null, "cert-exception-ui-ready", null);
   292   }
   293   else if (gChecking) {
   294     shortDesc = "addExceptionCheckingShort";
   295     longDesc  = "addExceptionCheckingLong";
   296     // We're checking the certificate, so we disable the Get Certificate
   297     // button to make sure that the user can't interrupt the process and
   298     // trigger another certificate fetch.
   299     document.getElementById("checkCertButton").disabled = true;
   300     document.getElementById("viewCertButton").disabled = true;
   301     gDialog.getButton("extra1").disabled = true;
   302     document.getElementById("permanent").disabled = true;
   303   }
   304   else {
   305     shortDesc = "addExceptionNoCertShort";
   306     longDesc  = "addExceptionNoCertLong";
   307     // We're done checking the certificate, so allow the user to check it again.
   308     document.getElementById("checkCertButton").disabled = false;
   309     document.getElementById("viewCertButton").disabled = true;
   310     gDialog.getButton("extra1").disabled = true;
   311     document.getElementById("permanent").disabled = true;
   312   }
   314   setText("statusDescription", gPKIBundle.getString(shortDesc));
   315   setText("statusLongDescription", gPKIBundle.getString(longDesc));
   317   if (use2) {
   318     setText("status2Description", gPKIBundle.getString(shortDesc2));
   319     setText("status2LongDescription", gPKIBundle.getString(longDesc2));
   320   }
   322   if (use3) {
   323     setText("status3Description", gPKIBundle.getString(shortDesc3));
   324     setText("status3LongDescription", gPKIBundle.getString(longDesc3));
   325   }
   327   gNeedReset = true;
   328 }
   330 /**
   331  * Handle user request to display certificate details
   332  */
   333 function viewCertButtonClick() {
   334   gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_CLICK_VIEW_CERT);
   335   if (gCert)
   336     viewCertHelper(this, gCert);
   338 }
   340 /**
   341  * Handle user request to add an exception for the specified cert
   342  */
   343 function addException() {
   344   if(!gCert || !gSSLStatus)
   345     return;
   347   var overrideService = Components.classes["@mozilla.org/security/certoverride;1"]
   348                                   .getService(Components.interfaces.nsICertOverrideService);
   349   var flags = 0;
   350   let confirmBucketId = gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_BASE;
   351   if (gSSLStatus.isUntrusted) {
   352     flags |= overrideService.ERROR_UNTRUSTED;
   353     confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED;
   354   }
   355   if (gSSLStatus.isDomainMismatch) {
   356     flags |= overrideService.ERROR_MISMATCH;
   357     confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN;
   358   }
   359   if (gSSLStatus.isNotValidAtThisTime) {
   360     flags |= overrideService.ERROR_TIME;
   361     confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_TIME;
   362   }
   364   var permanentCheckbox = document.getElementById("permanent");
   365   var shouldStorePermanently = permanentCheckbox.checked && !inPrivateBrowsingMode();
   366   if(!permanentCheckbox.checked)
   367    gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_DONT_REMEMBER_EXCEPTION);
   369   gSecHistogram.add(confirmBucketId);
   370   var uri = getURI();
   371   overrideService.rememberValidityOverride(
   372     uri.asciiHost, uri.port,
   373     gCert,
   374     flags,
   375     !shouldStorePermanently);
   377   var args = window.arguments;
   378   if (args && args[0])
   379     args[0].exceptionAdded = true;
   381   gDialog.acceptDialog();
   382 }
   384 /**
   385  * Returns true if this dialog is in private browsing mode.
   386  */
   387 function inPrivateBrowsingMode() {
   388   return PrivateBrowsingUtils.isWindowPrivate(window);
   389 }

mercurial