1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/pki/resources/content/exceptionDialog.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,389 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 + 1.9 +var gDialog; 1.10 +var gBundleBrand; 1.11 +var gPKIBundle; 1.12 +var gSSLStatus; 1.13 +var gCert; 1.14 +var gChecking; 1.15 +var gBroken; 1.16 +var gNeedReset; 1.17 +var gSecHistogram; 1.18 +var gNsISecTel; 1.19 + 1.20 +Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); 1.21 + 1.22 +function badCertListener() {} 1.23 +badCertListener.prototype = { 1.24 + getInterface: function (aIID) { 1.25 + return this.QueryInterface(aIID); 1.26 + }, 1.27 + QueryInterface: function(aIID) { 1.28 + if (aIID.equals(Components.interfaces.nsIBadCertListener2) || 1.29 + aIID.equals(Components.interfaces.nsIInterfaceRequestor) || 1.30 + aIID.equals(Components.interfaces.nsISupports)) 1.31 + return this; 1.32 + 1.33 + throw Components.results.NS_ERROR_NO_INTERFACE; 1.34 + }, 1.35 + handle_test_result: function () { 1.36 + if (gSSLStatus) 1.37 + gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert; 1.38 + }, 1.39 + notifyCertProblem: function MSR_notifyCertProblem(socketInfo, sslStatus, targetHost) { 1.40 + gBroken = true; 1.41 + gSSLStatus = sslStatus; 1.42 + this.handle_test_result(); 1.43 + return true; // suppress error UI 1.44 + } 1.45 +} 1.46 + 1.47 +function initExceptionDialog() { 1.48 + gNeedReset = false; 1.49 + gDialog = document.documentElement; 1.50 + gBundleBrand = document.getElementById("brand_bundle"); 1.51 + gPKIBundle = document.getElementById("pippki_bundle"); 1.52 + gSecHistogram = Components.classes["@mozilla.org/base/telemetry;1"]. 1.53 + getService(Components.interfaces.nsITelemetry). 1.54 + getHistogramById("SECURITY_UI"); 1.55 + gNsISecTel = Components.interfaces.nsISecurityUITelemetry; 1.56 + 1.57 + var brandName = gBundleBrand.getString("brandShortName"); 1.58 + setText("warningText", gPKIBundle.getFormattedString("addExceptionBrandedWarning2", [brandName])); 1.59 + gDialog.getButton("extra1").disabled = true; 1.60 + 1.61 + var args = window.arguments; 1.62 + if (args && args[0]) { 1.63 + if (args[0].location) { 1.64 + // We were pre-seeded with a location. 1.65 + document.getElementById("locationTextBox").value = args[0].location; 1.66 + document.getElementById('checkCertButton').disabled = false; 1.67 + 1.68 + // We can optionally pre-fetch the certificate too. Don't do this 1.69 + // synchronously, since it would prevent the window from appearing 1.70 + // until the fetch is completed, which could be multiple seconds. 1.71 + // Instead, let's use a timer to spawn the actual fetch, but update 1.72 + // the dialog to "checking..." state right away, so that the UI 1.73 + // is appropriately responsive. Bug 453855 1.74 + if (args[0].prefetchCert) { 1.75 + 1.76 + document.getElementById("checkCertButton").disabled = true; 1.77 + gChecking = true; 1.78 + updateCertStatus(); 1.79 + 1.80 + window.setTimeout(checkCert, 0); 1.81 + } 1.82 + } 1.83 + 1.84 + // Set out parameter to false by default 1.85 + args[0].exceptionAdded = false; 1.86 + } 1.87 +} 1.88 + 1.89 +// returns true if found and global status could be set 1.90 +function findRecentBadCert(uri) { 1.91 + try { 1.92 + var certDB = Components.classes["@mozilla.org/security/x509certdb;1"] 1.93 + .getService(Components.interfaces.nsIX509CertDB); 1.94 + if (!certDB) 1.95 + return false; 1.96 + var recentCertsSvc = certDB.getRecentBadCerts(inPrivateBrowsingMode()); 1.97 + if (!recentCertsSvc) 1.98 + return false; 1.99 + 1.100 + var hostWithPort = uri.host + ":" + uri.port; 1.101 + gSSLStatus = recentCertsSvc.getRecentBadCert(hostWithPort); 1.102 + if (!gSSLStatus) 1.103 + return false; 1.104 + 1.105 + gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert; 1.106 + if (!gCert) 1.107 + return false; 1.108 + 1.109 + gBroken = true; 1.110 + } 1.111 + catch (e) { 1.112 + return false; 1.113 + } 1.114 + updateCertStatus(); 1.115 + return true; 1.116 +} 1.117 + 1.118 +/** 1.119 + * Attempt to download the certificate for the location specified, and populate 1.120 + * the Certificate Status section with the result. 1.121 + */ 1.122 +function checkCert() { 1.123 + 1.124 + gCert = null; 1.125 + gSSLStatus = null; 1.126 + gChecking = true; 1.127 + gBroken = false; 1.128 + updateCertStatus(); 1.129 + 1.130 + var uri = getURI(); 1.131 + 1.132 + // Is the cert already known in the list of recently seen bad certs? 1.133 + if (findRecentBadCert(uri) == true) 1.134 + return; 1.135 + 1.136 + var req = new XMLHttpRequest(); 1.137 + try { 1.138 + if(uri) { 1.139 + req.open('GET', uri.prePath, false); 1.140 + req.channel.notificationCallbacks = new badCertListener(); 1.141 + req.send(null); 1.142 + } 1.143 + } catch (e) { 1.144 + // We *expect* exceptions if there are problems with the certificate 1.145 + // presented by the site. Log it, just in case, but we can proceed here, 1.146 + // with appropriate sanity checks 1.147 + Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " + 1.148 + "This results in a (mostly harmless) exception being thrown. " + 1.149 + "Logged for information purposes only: " + e); 1.150 + } finally { 1.151 + gChecking = false; 1.152 + } 1.153 + 1.154 + if(req.channel && req.channel.securityInfo) { 1.155 + const Ci = Components.interfaces; 1.156 + gSSLStatus = req.channel.securityInfo 1.157 + .QueryInterface(Ci.nsISSLStatusProvider).SSLStatus; 1.158 + gCert = gSSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert; 1.159 + } 1.160 + updateCertStatus(); 1.161 +} 1.162 + 1.163 +/** 1.164 + * Build and return a URI, based on the information supplied in the 1.165 + * Certificate Location fields 1.166 + */ 1.167 +function getURI() { 1.168 + // Use fixup service instead of just ioservice's newURI since it's quite likely 1.169 + // that the host will be supplied without a protocol prefix, resulting in malformed 1.170 + // uri exceptions being thrown. 1.171 + var fus = Components.classes["@mozilla.org/docshell/urifixup;1"] 1.172 + .getService(Components.interfaces.nsIURIFixup); 1.173 + var uri = fus.createFixupURI(document.getElementById("locationTextBox").value, 0); 1.174 + 1.175 + if(!uri) 1.176 + return null; 1.177 + 1.178 + if(uri.scheme == "http") 1.179 + uri.scheme = "https"; 1.180 + 1.181 + if (uri.port == -1) 1.182 + uri.port = 443; 1.183 + 1.184 + return uri; 1.185 +} 1.186 + 1.187 +function resetDialog() { 1.188 + document.getElementById("viewCertButton").disabled = true; 1.189 + document.getElementById("permanent").disabled = true; 1.190 + gDialog.getButton("extra1").disabled = true; 1.191 + setText("headerDescription", ""); 1.192 + setText("statusDescription", ""); 1.193 + setText("statusLongDescription", ""); 1.194 + setText("status2Description", ""); 1.195 + setText("status2LongDescription", ""); 1.196 + setText("status3Description", ""); 1.197 + setText("status3LongDescription", ""); 1.198 +} 1.199 + 1.200 +/** 1.201 + * Called by input textboxes to manage UI state 1.202 + */ 1.203 +function handleTextChange() { 1.204 + var checkCertButton = document.getElementById('checkCertButton'); 1.205 + checkCertButton.disabled = !(document.getElementById("locationTextBox").value); 1.206 + if (gNeedReset) { 1.207 + gNeedReset = false; 1.208 + resetDialog(); 1.209 + } 1.210 +} 1.211 + 1.212 +function updateCertStatus() { 1.213 + var shortDesc, longDesc; 1.214 + var shortDesc2, longDesc2; 1.215 + var shortDesc3, longDesc3; 1.216 + var use2 = false; 1.217 + var use3 = false; 1.218 + let bucketId = gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_BASE; 1.219 + if(gCert) { 1.220 + if(gBroken) { 1.221 + var mms = "addExceptionDomainMismatchShort"; 1.222 + var mml = "addExceptionDomainMismatchLong"; 1.223 + var exs = "addExceptionExpiredShort"; 1.224 + var exl = "addExceptionExpiredLong"; 1.225 + var uts = "addExceptionUnverifiedOrBadSignatureShort"; 1.226 + var utl = "addExceptionUnverifiedOrBadSignatureLong"; 1.227 + var use1 = false; 1.228 + if (gSSLStatus.isDomainMismatch) { 1.229 + bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_DOMAIN; 1.230 + use1 = true; 1.231 + shortDesc = mms; 1.232 + longDesc = mml; 1.233 + } 1.234 + if (gSSLStatus.isNotValidAtThisTime) { 1.235 + bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_TIME; 1.236 + if (!use1) { 1.237 + use1 = true; 1.238 + shortDesc = exs; 1.239 + longDesc = exl; 1.240 + } 1.241 + else { 1.242 + use2 = true; 1.243 + shortDesc2 = exs; 1.244 + longDesc2 = exl; 1.245 + } 1.246 + } 1.247 + if (gSSLStatus.isUntrusted) { 1.248 + bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_UNTRUSTED; 1.249 + if (!use1) { 1.250 + use1 = true; 1.251 + shortDesc = uts; 1.252 + longDesc = utl; 1.253 + } 1.254 + else if (!use2) { 1.255 + use2 = true; 1.256 + shortDesc2 = uts; 1.257 + longDesc2 = utl; 1.258 + } 1.259 + else { 1.260 + use3 = true; 1.261 + shortDesc3 = uts; 1.262 + longDesc3 = utl; 1.263 + } 1.264 + } 1.265 + gSecHistogram.add(bucketId); 1.266 + 1.267 + // In these cases, we do want to enable the "Add Exception" button 1.268 + gDialog.getButton("extra1").disabled = false; 1.269 + 1.270 + // If the Private Browsing service is available and the mode is active, 1.271 + // don't store permanent exceptions, since they would persist after 1.272 + // private browsing mode was disabled. 1.273 + var inPrivateBrowsing = inPrivateBrowsingMode(); 1.274 + var pe = document.getElementById("permanent"); 1.275 + pe.disabled = inPrivateBrowsing; 1.276 + pe.checked = !inPrivateBrowsing; 1.277 + 1.278 + setText("headerDescription", gPKIBundle.getString("addExceptionInvalidHeader")); 1.279 + } 1.280 + else { 1.281 + shortDesc = "addExceptionValidShort"; 1.282 + longDesc = "addExceptionValidLong"; 1.283 + gDialog.getButton("extra1").disabled = true; 1.284 + document.getElementById("permanent").disabled = true; 1.285 + } 1.286 + 1.287 + // We're done checking the certificate, so allow the user to check it again. 1.288 + document.getElementById("checkCertButton").disabled = false; 1.289 + document.getElementById("viewCertButton").disabled = false; 1.290 + 1.291 + // Notify observers about the availability of the certificate 1.292 + Components.classes["@mozilla.org/observer-service;1"] 1.293 + .getService(Components.interfaces.nsIObserverService) 1.294 + .notifyObservers(null, "cert-exception-ui-ready", null); 1.295 + } 1.296 + else if (gChecking) { 1.297 + shortDesc = "addExceptionCheckingShort"; 1.298 + longDesc = "addExceptionCheckingLong"; 1.299 + // We're checking the certificate, so we disable the Get Certificate 1.300 + // button to make sure that the user can't interrupt the process and 1.301 + // trigger another certificate fetch. 1.302 + document.getElementById("checkCertButton").disabled = true; 1.303 + document.getElementById("viewCertButton").disabled = true; 1.304 + gDialog.getButton("extra1").disabled = true; 1.305 + document.getElementById("permanent").disabled = true; 1.306 + } 1.307 + else { 1.308 + shortDesc = "addExceptionNoCertShort"; 1.309 + longDesc = "addExceptionNoCertLong"; 1.310 + // We're done checking the certificate, so allow the user to check it again. 1.311 + document.getElementById("checkCertButton").disabled = false; 1.312 + document.getElementById("viewCertButton").disabled = true; 1.313 + gDialog.getButton("extra1").disabled = true; 1.314 + document.getElementById("permanent").disabled = true; 1.315 + } 1.316 + 1.317 + setText("statusDescription", gPKIBundle.getString(shortDesc)); 1.318 + setText("statusLongDescription", gPKIBundle.getString(longDesc)); 1.319 + 1.320 + if (use2) { 1.321 + setText("status2Description", gPKIBundle.getString(shortDesc2)); 1.322 + setText("status2LongDescription", gPKIBundle.getString(longDesc2)); 1.323 + } 1.324 + 1.325 + if (use3) { 1.326 + setText("status3Description", gPKIBundle.getString(shortDesc3)); 1.327 + setText("status3LongDescription", gPKIBundle.getString(longDesc3)); 1.328 + } 1.329 + 1.330 + gNeedReset = true; 1.331 +} 1.332 + 1.333 +/** 1.334 + * Handle user request to display certificate details 1.335 + */ 1.336 +function viewCertButtonClick() { 1.337 + gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_CLICK_VIEW_CERT); 1.338 + if (gCert) 1.339 + viewCertHelper(this, gCert); 1.340 + 1.341 +} 1.342 + 1.343 +/** 1.344 + * Handle user request to add an exception for the specified cert 1.345 + */ 1.346 +function addException() { 1.347 + if(!gCert || !gSSLStatus) 1.348 + return; 1.349 + 1.350 + var overrideService = Components.classes["@mozilla.org/security/certoverride;1"] 1.351 + .getService(Components.interfaces.nsICertOverrideService); 1.352 + var flags = 0; 1.353 + let confirmBucketId = gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_BASE; 1.354 + if (gSSLStatus.isUntrusted) { 1.355 + flags |= overrideService.ERROR_UNTRUSTED; 1.356 + confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED; 1.357 + } 1.358 + if (gSSLStatus.isDomainMismatch) { 1.359 + flags |= overrideService.ERROR_MISMATCH; 1.360 + confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN; 1.361 + } 1.362 + if (gSSLStatus.isNotValidAtThisTime) { 1.363 + flags |= overrideService.ERROR_TIME; 1.364 + confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_TIME; 1.365 + } 1.366 + 1.367 + var permanentCheckbox = document.getElementById("permanent"); 1.368 + var shouldStorePermanently = permanentCheckbox.checked && !inPrivateBrowsingMode(); 1.369 + if(!permanentCheckbox.checked) 1.370 + gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_DONT_REMEMBER_EXCEPTION); 1.371 + 1.372 + gSecHistogram.add(confirmBucketId); 1.373 + var uri = getURI(); 1.374 + overrideService.rememberValidityOverride( 1.375 + uri.asciiHost, uri.port, 1.376 + gCert, 1.377 + flags, 1.378 + !shouldStorePermanently); 1.379 + 1.380 + var args = window.arguments; 1.381 + if (args && args[0]) 1.382 + args[0].exceptionAdded = true; 1.383 + 1.384 + gDialog.acceptDialog(); 1.385 +} 1.386 + 1.387 +/** 1.388 + * Returns true if this dialog is in private browsing mode. 1.389 + */ 1.390 +function inPrivateBrowsingMode() { 1.391 + return PrivateBrowsingUtils.isWindowPrivate(window); 1.392 +}