Skip to content
Snippets Groups Projects
Commit 0795c1f4 authored by ma1's avatar ma1 Committed by Richard Pospesel
Browse files

Bug 8324: Prevent DNS proxy bypasses caused by Drag&Drop

parent fc894e21
Branches
Tags
1 merge request!545Rebase tor-browser to esr102.8
......@@ -1903,7 +1903,11 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtils, "URI_FLAVORS", () => {
return [PlacesUtils.TYPE_X_MOZ_URL, TAB_DROP_TYPE, PlacesUtils.TYPE_UNICODE];
});
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "SUPPORTED_FLAVORS", () => {
return [...PlacesUIUtils.PLACES_FLAVORS, ...PlacesUIUtils.URI_FLAVORS];
return [
...PlacesUIUtils.PLACES_FLAVORS,
...PlacesUIUtils.URI_FLAVORS,
"application/x-torbrowser-opaque",
];
});
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() {
......
......@@ -1251,6 +1251,7 @@ PlacesController.prototype = {
[
PlacesUtils.TYPE_X_MOZ_PLACE,
PlacesUtils.TYPE_X_MOZ_URL,
"application/x-torbrowser-opaque",
PlacesUtils.TYPE_UNICODE,
].forEach(type => xferable.addDataFlavor(type));
......
......@@ -5,6 +5,16 @@
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyGetter(this, "gOpaqueDrag", () => {
return Cc["@torproject.org/torbutton-dragDropFilter;1"].getService(
Ci.nsISupports
).wrappedJSObject.opaqueDrag;
});
// This component is used for handling dragover and drop of urls.
//
// It checks to see whether a drop of a url is allowed. For instance, a url
......@@ -43,10 +53,15 @@ ContentAreaDropListener.prototype = {
}
}
type = "text/x-moz-url";
if (types.contains(type)) {
for (let type of ["text/x-moz-url", "application/x-torbrowser-opaque"]) {
if (!types.contains(type)) {
continue;
}
data = dt.mozGetDataAt(type, i);
if (data) {
if (type === "application/x-torbrowser-opaque") {
({ type, value: data = "" } = gOpaqueDrag.get(data));
}
let lines = data.split("\n");
for (let i = 0, length = lines.length; i < length; i += 2) {
this._addLink(links, lines[i], lines[i + 1], type);
......@@ -250,6 +265,7 @@ ContentAreaDropListener.prototype = {
if (
!types.includes("application/x-moz-file") &&
!types.includes("text/x-moz-url") &&
!types.includes("application/x-torbrowser-opaque") &&
!types.includes("text/uri-list") &&
!types.includes("text/x-moz-text-internal") &&
!types.includes("text/plain")
......
......@@ -32,6 +32,12 @@ XPCOMUtils.defineLazyGetter(this, "gCryptoHash", () => {
return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
});
XPCOMUtils.defineLazyGetter(this, "gOpaqueDrag", () => {
return Cc["@torproject.org/torbutton-dragDropFilter;1"].getService(
Ci.nsISupports
).wrappedJSObject.opaqueDrag;
});
// On Mac OSX, the transferable system converts "\r\n" to "\n\n", where
// we really just want "\n". On other platforms, the transferable system
// converts "\r\n" to "\n".
......@@ -1132,6 +1138,9 @@ var PlacesUtils = {
unwrapNodes: function PU_unwrapNodes(blob, type) {
// We split on "\n" because the transferable system converts "\r\n" to "\n"
var nodes = [];
if (type === "application/x-torbrowser-opaque") {
({ value: blob, type } = gOpaqueDrag.get(blob));
}
switch (type) {
case this.TYPE_X_MOZ_PLACE:
case this.TYPE_X_MOZ_PLACE_SEPARATOR:
......
/*************************************************************************
* Drag and Drop Handler.
*
* Implements an observer that filters drag events to prevent OS
* access to URLs (a potential proxy bypass vector).
*************************************************************************/
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
ComponentUtils: "resource://gre/modules/ComponentUtils.jsm",
});
XPCOMUtils.defineLazyGlobalGetters(this, ["crypto"]);
// Module specific constants
const kMODULE_NAME = "Torbutton Drag and Drop Handler";
const kCONTRACT_ID = "@torproject.org/torbutton-dragDropFilter;1";
const kMODULE_CID = Components.ID("f605ec27-d867-44b5-ad97-2a29276642c3");
const kInterfaces = [Ci.nsIObserver, Ci.nsIClassInfo];
const URLISH_TYPES = Object.freeze([
"text/x-moz-url",
"text/x-moz-url-data",
"text/uri-list",
"application/x-moz-file-promise-url",
]);
const MAIN_PROCESS =
Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
const EMPTY_PAYLOAD = {};
const OpaqueDrag = {
listening: false,
payload: EMPTY_PAYLOAD,
store(value, type) {
let opaqueKey = crypto.randomUUID();
this.payload = { opaqueKey, value, type };
if (!this.listening && MAIN_PROCESS) {
Services.ppmm.addMessageListener(
"DragDropFilter:GetOpaqueDrag",
() => this.payload
);
this.listening = true;
}
return opaqueKey;
},
retrieve(key) {
let { opaqueKey, value, type } = this.payload;
if (opaqueKey === key) {
return { value, type };
}
if (!MAIN_PROCESS) {
this.payload = Services.cpmm.sendSyncMessage(
"DragDropFilter:GetOpaqueDrag"
)[0];
if (key === this.payload.opaqueKey) {
return this.retrieve(key);
}
}
return EMPTY_PAYLOAD;
},
};
function DragDropFilter() {
this.logger = Cc["@torproject.org/torbutton-logger;1"].getService(
Ci.nsISupports
).wrappedJSObject;
this.logger.log(3, "Component Load 0: New DragDropFilter.");
if (MAIN_PROCESS) {
// We want to update our status in the main process only, in order to
// serve the same opaque drag payload in every process.
try {
Services.obs.addObserver(this, "on-datatransfer-available");
} catch (e) {
this.logger.log(5, "Failed to register drag observer");
}
}
}
DragDropFilter.prototype = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
// make this an nsIClassInfo object
flags: Ci.nsIClassInfo.DOM_OBJECT,
classDescription: kMODULE_NAME,
contractID: kCONTRACT_ID,
classID: kMODULE_CID,
// method of nsIClassInfo
getInterfaces(count) {
count.value = kInterfaces.length;
return kInterfaces;
},
// method of nsIClassInfo
getHelperForLanguage(count) {
return null;
},
// method of nsIObserver
observe(subject, topic, data) {
if (topic === "on-datatransfer-available") {
this.logger.log(3, "The DataTransfer is available");
this.filterDataTransferURLs(subject);
}
},
filterDataTransferURLs(aDataTransfer) {
for (let i = 0, count = aDataTransfer.mozItemCount; i < count; ++i) {
this.logger.log(3, `Inspecting the data transfer: ${i}.`);
const types = aDataTransfer.mozTypesAt(i);
for (const type of types) {
this.logger.log(3, `Type is: ${type}.`);
if (URLISH_TYPES.includes(type)) {
this.logger.log(
3,
`Removing transfer data ${aDataTransfer.mozGetDataAt(type, i)}`
);
const urlType = "text/x-moz-url";
// Fallback url type, to be parsed by this browser but not externally
const INTERNAL_FALLBACK = "application/x-torbrowser-opaque";
if (types.contains(urlType)) {
const link = aDataTransfer.mozGetDataAt(urlType, i);
const opaqueKey = OpaqueDrag.store(link, urlType);
aDataTransfer.mozSetDataAt(INTERNAL_FALLBACK, opaqueKey, i);
}
for (const type of types) {
if (
type !== INTERNAL_FALLBACK &&
type !== "text/x-moz-place" // don't touch bookmarks
) {
aDataTransfer.mozClearDataAt(type, i);
}
}
break;
}
}
}
},
opaqueDrag: {
get(opaqueKey) {
return OpaqueDrag.retrieve(opaqueKey);
},
},
};
// Assign factory to global object.
const NSGetFactory = XPCOMUtils.generateNSGetFactory
? XPCOMUtils.generateNSGetFactory([DragDropFilter])
: ComponentUtils.generateNSGetFactory([DragDropFilter]);
......@@ -21,6 +21,9 @@ torbutton.jar:
% skin torbutton classic/1.0 %skin/
# Firefox 4-style component registration
% component {f605ec27-d867-44b5-ad97-2a29276642c3} %components/dragDropFilter.js
% contract @torproject.org/torbutton-dragDropFilter;1 {f605ec27-d867-44b5-ad97-2a29276642c3}
% component {3da0269f-fc29-4e9e-a678-c3b1cafcf13f} %components/external-app-blocker.js
% contract @torproject.org/torbutton-extAppBlocker;1 {3da0269f-fc29-4e9e-a678-c3b1cafcf13f}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment