Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 }