Source code

Revision control

Other Tools

1
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
* License, v. 2.0. If a copy of the MPL was not distributed with this
4
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
var EXPORTED_SYMBOLS = ["NetErrorChild"];
7
8
const { XPCOMUtils } = ChromeUtils.import(
10
);
11
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
12
const { ActorChild } = ChromeUtils.import(
14
);
15
16
ChromeUtils.defineModuleGetter(
17
this,
18
"WebNavigationFrames",
20
);
21
22
XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
23
24
XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
25
return Services.strings.createBundle(
27
);
28
});
29
XPCOMUtils.defineLazyGetter(this, "gNSSErrorsBundle", function() {
30
return Services.strings.createBundle(
32
);
33
});
34
35
const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
36
const SEC_ERROR_REUSED_ISSUER_AND_SERIAL = SEC_ERROR_BASE + 138;
37
38
const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
39
const SSL_ERROR_SSL_DISABLED = SSL_ERROR_BASE + 20;
40
const SSL_ERROR_SSL2_DISABLED = SSL_ERROR_BASE + 14;
41
42
const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."];
43
44
function getSerializedSecurityInfo(docShell) {
45
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"].getService(
46
Ci.nsISerializationHelper
47
);
48
49
let securityInfo =
50
docShell.failedChannel && docShell.failedChannel.securityInfo;
51
if (!securityInfo) {
52
return "";
53
}
54
securityInfo
55
.QueryInterface(Ci.nsITransportSecurityInfo)
56
.QueryInterface(Ci.nsISerializable);
57
58
return serhelper.serializeToString(securityInfo);
59
}
60
61
class NetErrorChild extends ActorChild {
62
isAboutNetError(doc) {
63
return doc.documentURI.startsWith("about:neterror");
64
}
65
66
isAboutCertError(doc) {
67
return doc.documentURI.startsWith("about:certerror");
68
}
69
70
getParams(doc) {
71
let searchParams = new URL(doc.documentURI).searchParams;
72
return {
73
cssClass: searchParams.get("s"),
74
error: searchParams.get("e"),
75
};
76
}
77
78
handleEvent(aEvent) {
79
// Documents have a null ownerDocument.
80
let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
81
82
switch (aEvent.type) {
83
case "AboutNetErrorLoad":
84
this.onPageLoad(doc.defaultView);
85
break;
86
case "AboutNetErrorSetAutomatic":
87
this.onSetAutomatic(aEvent);
88
break;
89
case "AboutNetErrorResetPreferences":
90
this.onResetPreferences(aEvent);
91
break;
92
case "click":
93
if (aEvent.button == 0) {
94
if (this.isAboutCertError(doc)) {
95
this.recordClick(aEvent.originalTarget);
96
this.onCertError(aEvent.originalTarget, doc.defaultView);
97
} else {
98
this.onClick(aEvent);
99
}
100
} else if (this.isAboutCertError(doc)) {
101
this.recordClick(aEvent.originalTarget);
102
}
103
break;
104
}
105
}
106
107
changedCertPrefs() {
108
let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
109
return prefs.concat(Services.prefs.getChildList(root));
110
}, []);
111
for (let prefName of prefSSLImpact) {
112
if (Services.prefs.prefHasUserValue(prefName)) {
113
return true;
114
}
115
}
116
117
return false;
118
}
119
120
_getErrorMessageFromCode(securityInfo, doc) {
121
let uri = Services.io.newURI(doc.location);
122
let hostString = uri.host;
123
if (uri.port != 443 && uri.port != -1) {
124
hostString = uri.hostPort;
125
}
126
127
let id_str = "";
128
switch (securityInfo.errorCode) {
129
case SSL_ERROR_SSL_DISABLED:
130
id_str = "PSMERR_SSL_Disabled";
131
break;
132
case SSL_ERROR_SSL2_DISABLED:
133
id_str = "PSMERR_SSL2_Disabled";
134
break;
135
case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
136
id_str = "PSMERR_HostReusedIssuerSerial";
137
break;
138
}
139
let nss_error_id_str = securityInfo.errorCodeString;
140
let msg2 = "";
141
try {
142
if (id_str) {
143
msg2 = gPipNSSBundle.GetStringFromName(id_str) + "\n";
144
} else if (nss_error_id_str) {
145
msg2 = gNSSErrorsBundle.GetStringFromName(nss_error_id_str) + "\n";
146
}
147
} catch (e) {
148
msg2 = "";
149
}
150
151
if (!msg2) {
152
// We couldn't get an error message. Use the error string.
153
// Note that this is different from before where we used PR_ErrorToString.
154
msg2 = nss_error_id_str;
155
}
156
let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2", [
157
hostString,
158
msg2,
159
]);
160
161
if (nss_error_id_str && msg2 != nss_error_id_str) {
162
msg +=
163
gPipNSSBundle.formatStringFromName("certErrorCodePrefix3", [
164
nss_error_id_str,
165
]) + "\n";
166
}
167
return msg;
168
}
169
170
onPageLoad(win) {
171
// Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
172
const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
173
174
if (this.isAboutNetError(win.document)) {
175
let docShell = win.docShell;
176
if (docShell) {
177
let { securityInfo } = docShell.failedChannel;
178
// We don't have a securityInfo when this is for example a DNS error.
179
if (securityInfo) {
180
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
181
let msg = this._getErrorMessageFromCode(securityInfo, win.document);
182
let id = win.document.getElementById("errorShortDescText");
183
id.textContent = msg;
184
}
185
}
186
187
let learnMoreLink = win.document.getElementById("learnMoreLink");
188
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
189
learnMoreLink.setAttribute("href", baseURL + "connection-not-secure");
190
191
let automatic = Services.prefs.getBoolPref(
192
"security.ssl.errorReporting.automatic"
193
);
194
win.dispatchEvent(
195
new win.CustomEvent("AboutNetErrorOptions", {
196
detail: JSON.stringify({
197
enabled: Services.prefs.getBoolPref(
198
"security.ssl.errorReporting.enabled"
199
),
200
changedCertPrefs: this.changedCertPrefs(),
201
automatic,
202
}),
203
})
204
);
205
206
this.mm.sendAsyncMessage("Browser:SSLErrorReportTelemetry", {
207
reportStatus: TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN,
208
});
209
}
210
}
211
212
onResetPreferences(evt) {
213
this.mm.sendAsyncMessage("Browser:ResetSSLPreferences");
214
}
215
216
onSetAutomatic(evt) {
217
this.mm.sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
218
automatic: evt.detail,
219
});
220
221
// If we're enabling reports, send a report for this failure.
222
if (evt.detail) {
223
let win = evt.originalTarget.ownerGlobal;
224
let docShell = win.docShell;
225
226
let { securityInfo } = docShell.failedChannel;
227
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
228
let { host, port } = win.document.mozDocumentURIIfNotForErrorPages;
229
230
let errorReporter = Cc["@mozilla.org/securityreporter;1"].getService(
231
Ci.nsISecurityReporter
232
);
233
errorReporter.reportTLSError(securityInfo, host, port);
234
}
235
}
236
237
onCertError(target, win) {
238
this.mm.sendAsyncMessage("Browser:CertExceptionError", {
239
frameId: WebNavigationFrames.getFrameId(win),
240
location: win.document.location.href,
241
elementId: target.getAttribute("id"),
242
isTopFrame: win.parent === win,
243
securityInfoAsString: getSerializedSecurityInfo(win.docShell),
244
});
245
}
246
247
getCSSClass(doc) {
248
let searchParams = new URL(doc.documentURI).searchParams;
249
return searchParams.get("s");
250
}
251
252
recordClick(element) {
253
let telemetryId = element.dataset.telemetryId;
254
if (!telemetryId) {
255
return;
256
}
257
let doc = element.ownerDocument;
258
let cssClass = this.getCSSClass(doc);
259
// Telemetry values for events are max. 80 bytes.
260
let errorCode = doc.body.getAttribute("code").substring(0, 40);
261
let panel = doc.getElementById("badCertAdvancedPanel");
262
Services.telemetry.recordEvent(
263
"security.ui.certerror",
264
"click",
265
telemetryId,
266
errorCode,
267
{
268
panel_open: (panel.style.display == "none").toString(),
269
has_sts: (cssClass == "badStsCert").toString(),
270
is_frame: (doc.ownerGlobal.parent != doc.ownerGlobal).toString(),
271
}
272
);
273
}
274
275
onClick(event) {
276
let { documentURI } = event.target.ownerDocument;
277
278
let elmId = event.originalTarget.getAttribute("id");
279
if (elmId == "returnButton") {
280
this.mm.sendAsyncMessage("Browser:SSLErrorGoBack", {});
281
return;
282
}
283
284
if (
285
!event.originalTarget.classList.contains("try-again") ||
286
!/e=netOffline/.test(documentURI)
287
) {
288
return;
289
}
290
// browser front end will handle clearing offline mode and refreshing
291
// the page *if* we're in offline mode now. Otherwise let the error page
292
// handle the click.
293
if (Services.io.offline) {
294
event.preventDefault();
295
this.mm.sendAsyncMessage("Browser:EnableOnlineMode", {});
296
}
297
}
298
}