Bug 783047 - Remove MAC support from SafeBrowsing code. r=mmc,dcamp

This commit is contained in:
Gian-Carlo Pascutto 2014-01-16 09:27:58 +01:00
parent 3e6531d3e8
commit 87ba1b8817
20 changed files with 70 additions and 1009 deletions

View File

@ -143,13 +143,13 @@
#define NS_URLCLASSIFIERPREFIXSET_CID \
{ 0x3d8579f0, 0x75fa, 0x4e00, { 0xba, 0x41, 0x38, 0x66, 0x1d, 0x5b, 0x5d, 0x17} }
// {5eb7c3c1-ec1f-4007-87cc-eefb37d68ce6}
// {8a389f21-f821-4e29-9c6b-3de6f33cd7cf}
#define NS_URLCLASSIFIERDBSERVICE_CID \
{ 0x5eb7c3c1, 0xec1f, 0x4007, { 0x87, 0xcc, 0xee, 0xfb, 0x37, 0xd6, 0x8c, 0xe6} }
{ 0x8a389f21, 0xf821, 0x4e29, { 0x9c, 0x6b, 0x3d, 0xe6, 0xf3, 0x3c, 0xd7, 0xcf} }
// {c2be6dc0-ef1e-4abd-86a2-4f864ddc57f6}
// {79e6b710-ce68-4639-ac6b-7d293af424a1}
#define NS_URLCLASSIFIERSTREAMUPDATER_CID \
{ 0xc2be6dc0, 0xef1e, 0x4abd, { 0x86, 0xa2, 0x4f, 0x86, 0x4d, 0xdc, 0x57, 0xf6} }
{ 0x79e6b710, 0xce68, 0x4639, { 0xac, 0x6b, 0x7d, 0x29, 0x3a, 0xf4, 0x24, 0xa1} }
// {b7b2ccec-7912-4ea6-a548-b038447004bd}
#define NS_URLCLASSIFIERUTILS_CID \

View File

@ -5,7 +5,6 @@
#include "ProtocolParser.h"
#include "LookupCache.h"
#include "nsIKeyModule.h"
#include "nsNetCID.h"
#include "prlog.h"
#include "prnetdb.h"
@ -70,7 +69,6 @@ ProtocolParser::ProtocolParser()
, mUpdateStatus(NS_OK)
, mUpdateWait(0)
, mResetRequested(false)
, mRekeyRequested(false)
{
}
@ -86,81 +84,6 @@ ProtocolParser::Init(nsICryptoHash* aHasher)
return NS_OK;
}
/**
* Initialize HMAC for the stream.
*
* If serverMAC is empty, the update stream will need to provide a
* server MAC.
*/
nsresult
ProtocolParser::InitHMAC(const nsACString& aClientKey,
const nsACString& aServerMAC)
{
mServerMAC = aServerMAC;
nsresult rv;
nsCOMPtr<nsIKeyObjectFactory> keyObjectFactory(
do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv));
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get nsIKeyObjectFactory service");
mUpdateStatus = rv;
return mUpdateStatus;
}
nsCOMPtr<nsIKeyObject> keyObject;
rv = keyObjectFactory->KeyFromString(nsIKeyObject::HMAC, aClientKey,
getter_AddRefs(keyObject));
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create key object, maybe not FIPS compliant?");
mUpdateStatus = rv;
return mUpdateStatus;
}
mHMAC = do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create nsICryptoHMAC instance");
mUpdateStatus = rv;
return mUpdateStatus;
}
rv = mHMAC->Init(nsICryptoHMAC::SHA1, keyObject);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to initialize nsICryptoHMAC instance");
mUpdateStatus = rv;
return mUpdateStatus;
}
return NS_OK;
}
nsresult
ProtocolParser::FinishHMAC()
{
if (NS_FAILED(mUpdateStatus)) {
return mUpdateStatus;
}
if (mRekeyRequested) {
mUpdateStatus = NS_ERROR_FAILURE;
return mUpdateStatus;
}
if (!mHMAC) {
return NS_OK;
}
nsAutoCString clientMAC;
mHMAC->Finish(true, clientMAC);
if (clientMAC != mServerMAC) {
NS_WARNING("Invalid update MAC!");
LOG(("Invalid update MAC: expected %s, got %s",
clientMAC.get(), mServerMAC.get()));
mUpdateStatus = NS_ERROR_FAILURE;
}
return mUpdateStatus;
}
void
ProtocolParser::SetCurrentTable(const nsACString& aTable)
{
@ -174,17 +97,6 @@ ProtocolParser::AppendStream(const nsACString& aData)
return mUpdateStatus;
nsresult rv;
// Digest the data if we have a server MAC.
if (mHMAC && !mServerMAC.IsEmpty()) {
rv = mHMAC->Update(reinterpret_cast<const uint8_t*>(aData.BeginReading()),
aData.Length());
if (NS_FAILED(rv)) {
mUpdateStatus = rv;
return rv;
}
}
mPending.Append(aData);
bool done = false;
@ -215,13 +127,7 @@ ProtocolParser::ProcessControl(bool* aDone)
while (NextLine(line)) {
//LOG(("Processing %s\n", line.get()));
if (line.EqualsLiteral("e:pleaserekey")) {
mRekeyRequested = true;
return NS_OK;
} else if (mHMAC && mServerMAC.IsEmpty()) {
rv = ProcessMAC(line);
NS_ENSURE_SUCCESS(rv, rv);
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) {
if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) {
// Set the table name from the table header line.
SetCurrentTable(Substring(line, 2));
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) {
@ -251,28 +157,6 @@ ProtocolParser::ProcessControl(bool* aDone)
return NS_OK;
}
nsresult
ProtocolParser::ProcessMAC(const nsCString& aLine)
{
nsresult rv;
LOG(("line: %s", aLine.get()));
if (StringBeginsWith(aLine, NS_LITERAL_CSTRING("m:"))) {
mServerMAC = Substring(aLine, 2);
nsUrlClassifierUtils::UnUrlsafeBase64(mServerMAC);
// The remainder of the pending update wasn't digested, digest it now.
rv = mHMAC->Update(reinterpret_cast<const uint8_t*>(mPending.BeginReading()),
mPending.Length());
return rv;
}
LOG(("No MAC specified!"));
return NS_ERROR_FAILURE;
}
nsresult
ProtocolParser::ProcessExpirations(const nsCString& aLine)
{
@ -364,29 +248,11 @@ nsresult
ProtocolParser::ProcessForward(const nsCString& aLine)
{
const nsCSubstring &forward = Substring(aLine, 2);
if (mHMAC) {
// We're expecting MACs alongside any url forwards.
nsCSubstring::const_iterator begin, end, sepBegin, sepEnd;
forward.BeginReading(begin);
sepBegin = begin;
forward.EndReading(end);
sepEnd = end;
if (!RFindInReadable(NS_LITERAL_CSTRING(","), sepBegin, sepEnd)) {
NS_WARNING("No MAC specified for a redirect in a request that expects a MAC");
return NS_ERROR_FAILURE;
}
nsCString serverMAC(Substring(sepEnd, end));
nsUrlClassifierUtils::UnUrlsafeBase64(serverMAC);
return AddForward(Substring(begin, sepBegin), serverMAC);
}
return AddForward(forward, mServerMAC);
return AddForward(forward);
}
nsresult
ProtocolParser::AddForward(const nsACString& aUrl, const nsACString& aMac)
ProtocolParser::AddForward(const nsACString& aUrl)
{
if (!mTableUpdate) {
NS_WARNING("Forward without a table name.");
@ -396,7 +262,6 @@ ProtocolParser::AddForward(const nsACString& aUrl, const nsACString& aMac)
ForwardedUpdate *forward = mForwards.AppendElement();
forward->table = mTableUpdate->TableName();
forward->url.Assign(aUrl);
forward->mac.Assign(aMac);
return NS_OK;
}

View File

@ -20,7 +20,6 @@ public:
struct ForwardedUpdate {
nsCString table;
nsCString url;
nsCString mac;
};
ProtocolParser();
@ -30,10 +29,6 @@ public:
nsresult Init(nsICryptoHash* aHasher);
nsresult InitHMAC(const nsACString& aClientKey,
const nsACString& aServerMAC);
nsresult FinishHMAC();
void SetCurrentTable(const nsACString& aTable);
nsresult Begin();
@ -49,15 +44,13 @@ public:
const nsTArray<ForwardedUpdate> &Forwards() const { return mForwards; }
int32_t UpdateWait() { return mUpdateWait; }
bool ResetRequested() { return mResetRequested; }
bool RekeyRequested() { return mRekeyRequested; }
private:
nsresult ProcessControl(bool* aDone);
nsresult ProcessMAC(const nsCString& aLine);
nsresult ProcessExpirations(const nsCString& aLine);
nsresult ProcessChunkControl(const nsCString& aLine);
nsresult ProcessForward(const nsCString& aLine);
nsresult AddForward(const nsACString& aUrl, const nsACString& aMac);
nsresult AddForward(const nsACString& aUrl);
nsresult ProcessChunk(bool* done);
// Remove this, it's only used for testing
nsresult ProcessPlaintextChunk(const nsACString& aChunk);
@ -110,12 +103,8 @@ private:
nsresult mUpdateStatus;
nsCString mPending;
nsCOMPtr<nsICryptoHMAC> mHMAC;
nsCString mServerMAC;
uint32_t mUpdateWait;
bool mResetRequested;
bool mRekeyRequested;
nsTArray<ForwardedUpdate> mForwards;
// Keep track of updates to apply before passing them to the DBServiceWorkers.

View File

@ -59,7 +59,6 @@ this.SafeBrowsing = {
malwareEnabled: false,
updateURL: null,
keyURL: null,
gethashURL: null,
reportURL: null,
@ -111,21 +110,15 @@ this.SafeBrowsing = {
// Urls used to update DB
this.updateURL = Services.urlFormatter.formatURLPref(basePref + "updateURL");
this.keyURL = Services.urlFormatter.formatURLPref(basePref + "keyURL");
this.gethashURL = Services.urlFormatter.formatURLPref(basePref + "gethashURL");
this.updateURL = this.updateURL.replace("SAFEBROWSING_ID", clientID);
this.keyURL = this.keyURL.replace("SAFEBROWSING_ID", clientID);
this.gethashURL = this.gethashURL.replace("SAFEBROWSING_ID", clientID);
let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
getService(Ci.nsIUrlListManager);
listManager.setUpdateUrl(this.updateURL);
// XXX Bug 779317 - setKeyUrl has the side effect of fetching a key from the server.
// This shouldn't happen if anti-phishing/anti-malware is disabled.
if (this.phishingEnabled || this.malwareEnabled)
listManager.setKeyUrl(this.keyURL);
listManager.setGethashUrl(this.gethashURL);
},

View File

@ -47,16 +47,6 @@ function PROT_ListManager() {
BindToObject(this.shutdown_, this),
true /*only once*/);
// Lazily create the key manager (to avoid fetching keys when they
// aren't needed).
this.keyManager_ = null;
this.rekeyObserver_ = new G_ObserverServiceObserver(
'url-classifier-rekey-requested',
BindToObject(this.rekey_, this),
false);
this.updateWaitingForKey_ = false;
this.cookieObserver_ = new G_ObserverServiceObserver(
'cookie-changed',
BindToObject(this.cookieChanged_, this),
@ -85,10 +75,6 @@ function PROT_ListManager() {
* Delete all of our data tables which seem to leak otherwise.
*/
PROT_ListManager.prototype.shutdown_ = function() {
if (this.keyManager_) {
this.keyManager_.shutdown();
}
for (var name in this.tablesData) {
delete this.tablesData[name];
}
@ -127,23 +113,6 @@ PROT_ListManager.prototype.setGethashUrl = function(url) {
}
}
/**
* Set the crypto key url.
* @param url String
*/
PROT_ListManager.prototype.setKeyUrl = function(url) {
G_Debug(this, "Set key url: " + url);
if (!this.keyManager_) {
this.keyManager_ = new PROT_UrlCryptoKeyManager();
this.keyManager_.onNewKey(BindToObject(this.newKey_, this));
this.hashCompleter_.setKeys(this.keyManager_.getClientKey(),
this.keyManager_.getWrappedKey());
}
this.keyManager_.setKeyUrl(url);
}
/**
* Register a new table table
* @param tableName - the name of the table
@ -362,27 +331,6 @@ PROT_ListManager.prototype.checkForUpdates = function() {
* tablename;<chunk ranges>\n
*/
PROT_ListManager.prototype.makeUpdateRequest_ = function(tableData) {
if (!this.keyManager_)
return;
if (!this.keyManager_.hasKey()) {
// We don't have a client key yet. Schedule a rekey, and rerequest
// when we have one.
// If there's already an update waiting for a new key, don't bother.
if (this.updateWaitingForKey_)
return;
// If maybeReKey() returns false we have asked for too many keys,
// and won't be getting a new one. Since we don't want to do
// updates without a client key, we'll skip this update if maybeReKey()
// fails.
if (this.keyManager_.maybeReKey())
this.updateWaitingForKey_ = true;
return;
}
var tableList;
var tableNames = {};
for (var tableName in this.tablesData) {
@ -403,7 +351,7 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(tableData) {
for (var i = 0; i < lines.length; i++) {
var fields = lines[i].split(";");
if (tableNames[fields[0]]) {
request += lines[i] + ":mac\n";
request += lines[i] + "\n";
delete tableNames[fields[0]];
}
}
@ -411,15 +359,14 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(tableData) {
// For each requested table that didn't have chunk data in the database,
// request it fresh
for (var tableName in tableNames) {
request += tableName + ";mac\n";
request += tableName + "\n";
}
G_Debug(this, 'checkForUpdates: scheduling request..');
var streamer = Cc["@mozilla.org/url-classifier/streamupdater;1"]
.getService(Ci.nsIUrlClassifierStreamUpdater);
try {
streamer.updateUrl = this.updateserverURL_ +
"&wrkey=" + this.keyManager_.getWrappedKey();
streamer.updateUrl = this.updateserverURL_;
} catch (e) {
G_Debug(this, 'invalid url');
return;
@ -429,7 +376,6 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(tableData) {
if (!streamer.downloadUpdates(tableList,
request,
this.keyManager_.getClientKey(),
BindToObject(this.updateSuccess_, this),
BindToObject(this.updateError_, this),
BindToObject(this.downloadError_, this))) {
@ -488,43 +434,13 @@ PROT_ListManager.prototype.downloadError_ = function(status) {
}
/**
* Called when either the update process or a gethash request signals
* that the server requested a rekey.
*/
PROT_ListManager.prototype.rekey_ = function() {
G_Debug(this, "rekey requested");
// The current key is no good anymore.
this.keyManager_.dropKey();
this.keyManager_.maybeReKey();
}
/**
* Called when cookies are cleared - clears the current MAC keys.
* Called when cookies are cleared
*/
PROT_ListManager.prototype.cookieChanged_ = function(subject, topic, data) {
if (data != "cleared")
return;
G_Debug(this, "cookies cleared");
this.keyManager_.dropKey();
}
/**
* Called when we've received a new key from the server.
*/
PROT_ListManager.prototype.newKey_ = function() {
G_Debug(this, "got a new MAC key");
this.hashCompleter_.setKeys(this.keyManager_.getClientKey(),
this.keyManager_.getWrappedKey());
if (this.keyManager_.hasKey()) {
if (this.updateWaitingForKey_) {
this.updateWaitingForKey_ = false;
this.checkForUpdates();
}
}
}
PROT_ListManager.prototype.QueryInterface = function(iid) {

View File

@ -1,386 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This file implements the tricky business of managing the keys for our
// URL encryption. The protocol is:
//
// - Server generates secret key K_S
// - Client starts up and requests a new key K_C from the server via HTTPS
// - Server generates K_C and WrappedKey, which is K_C encrypted with K_S
// - Server resonse with K_C and WrappedKey
// - When client wants to encrypt a URL, it encrypts it with K_C and sends
// the encrypted URL along with WrappedKey
// - Server decrypts WrappedKey with K_S to get K_C, and the URL with K_C
//
// This is, however, trickier than it sounds for two reasons. First,
// we want to keep the number of HTTPS requests to an aboslute minimum
// (like 1 or 2 per browser session). Second, the HTTPS request at
// startup might fail, for example the user might be offline or a URL
// fetch might need to be issued before the HTTPS request has
// completed.
//
// We implement the following policy:
//
// - Firefox will issue at most two HTTPS getkey requests per session
// - Firefox will issue one HTTPS getkey request at startup if more than 24
// hours has passed since the last getkey request.
// - Firefox will serialize to disk any key it gets
// - Firefox will fall back on this serialized key until it has a
// fresh key
// - The front-end can respond with a flag in a lookup request that tells
// the client to re-key. Firefox will issue a new HTTPS getkey request
// at this time if it has only issued one before
// We store the user key in this file. The key can be used to verify signed
// server updates.
const kKeyFilename = "urlclassifierkey3.txt";
Components.utils.import("resource://gre/modules/osfile.jsm");
Components.utils.import("resource://gre/modules/Promise.jsm");
/**
* A key manager for UrlCrypto. There should be exactly one of these
* per appplication, and all UrlCrypto's should share it. This is
* currently implemented by having the manager attach itself to the
* UrlCrypto's prototype at startup. We could've opted for a global
* instead, but I like this better, even though it is spooky action
* at a distance.
* XXX: Should be an XPCOM service
*
* @param opt_keyFilename String containing the name of the
* file we should serialize keys to/from. Used
* mostly for testing.
*
* @param opt_testing Boolean indicating whether we are testing. If we
* are, then we skip trying to read the old key from
* file and automatically trying to rekey; presumably
* the tester will drive these manually.
*
* @constructor
*/
function PROT_UrlCryptoKeyManager(opt_keyFilename, opt_testing) {
this.debugZone = "urlcryptokeymanager";
this.testing_ = !!opt_testing;
this.clientKey_ = null; // Base64-encoded, as fetched from server
this.clientKeyArray_ = null; // Base64-decoded into an array of numbers
this.wrappedKey_ = null; // Opaque websafe base64-encoded server key
this.rekeyTries_ = 0;
this.updating_ = false;
// Don't do anything until keyUrl_ is set.
this.keyUrl_ = null;
this.keyFilename_ = opt_keyFilename ?
opt_keyFilename : kKeyFilename;
this.onNewKey_ = null;
// Convenience properties
this.MAX_REKEY_TRIES = PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES;
this.CLIENT_KEY_NAME = PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME;
this.WRAPPED_KEY_NAME = PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME;
if (!this.testing_) {
this.maybeLoadOldKey();
}
}
// Do ***** NOT ***** set this higher; HTTPS is expensive
PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES = 2;
// Base pref for keeping track of when we updated our key.
// We store the time as seconds since the epoch.
PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF = "urlclassifier.keyupdatetime.";
// Once every 30 days (interval in seconds)
PROT_UrlCryptoKeyManager.KEY_MIN_UPDATE_TIME = 30 * 24 * 60 * 60;
// These are the names the server will respond with in protocol4 format
PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME = "clientkey";
PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME = "wrappedkey";
/**
* Called to get ClientKey
* @returns urlsafe-base64-encoded client key or null if we haven't gotten one.
*/
PROT_UrlCryptoKeyManager.prototype.getClientKey = function() {
return this.clientKey_;
}
/**
* Called by a UrlCrypto to get the current K_C
*
* @returns Array of numbers making up the client key or null if we
* have no key
*/
PROT_UrlCryptoKeyManager.prototype.getClientKeyArray = function() {
return this.clientKeyArray_;
}
/**
* Called by a UrlCrypto to get WrappedKey
*
* @returns Opaque base64-encoded WrappedKey or null if we haven't
* gotten one
*/
PROT_UrlCryptoKeyManager.prototype.getWrappedKey = function() {
return this.wrappedKey_;
}
/**
* Change the key url. When we do this, we go ahead and rekey.
* @param keyUrl String
*/
PROT_UrlCryptoKeyManager.prototype.setKeyUrl = function(keyUrl) {
// If it's the same key url, do nothing.
if (keyUrl == this.keyUrl_)
return;
this.keyUrl_ = keyUrl;
this.rekeyTries_ = 0;
// Check to see if we should make a new getkey request.
var prefs = new G_Preferences(PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF);
var nextRekey = prefs.getPref(this.getPrefName_(this.keyUrl_), 0);
if (nextRekey < parseInt(Date.now() / 1000, 10)) {
this.reKey();
}
}
/**
* Given a url, return the pref value to use (pref contains last update time).
* We basically use the url up until query parameters. This avoids duplicate
* pref entries as version number changes over time.
* @param url String getkey URL
*/
PROT_UrlCryptoKeyManager.prototype.getPrefName_ = function(url) {
var queryParam = url.indexOf("?");
if (queryParam != -1) {
return url.substring(0, queryParam);
}
return url;
}
/**
* Tell the manager to re-key. For safety, this method still obeys the
* max-tries limit. Clients should generally use maybeReKey() if they
* want to try a re-keying: it's an error to call reKey() after we've
* hit max-tries, but not an error to call maybeReKey().
*/
PROT_UrlCryptoKeyManager.prototype.reKey = function() {
if (this.updating_) {
G_Debug(this, "Already re-keying, ignoring this request");
return true;
}
if (this.rekeyTries_ > this.MAX_REKEY_TRIES)
throw new Error("Have already rekeyed " + this.rekeyTries_ + " times");
this.rekeyTries_++;
G_Debug(this, "Attempting to re-key");
// If the keyUrl isn't set, we don't do anything.
if (!this.testing_ && this.keyUrl_) {
this.fetcher_ = new PROT_XMLFetcher();
this.fetcher_.get(this.keyUrl_, BindToObject(this.onGetKeyResponse, this));
this.updating_ = true;
// Calculate the next time we're allowed to re-key.
var prefs = new G_Preferences(PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF);
var nextRekey = parseInt(Date.now() / 1000, 10)
+ PROT_UrlCryptoKeyManager.KEY_MIN_UPDATE_TIME;
prefs.setPref(this.getPrefName_(this.keyUrl_), nextRekey);
}
}
/**
* Try to re-key if we haven't already hit our limit. It's OK to call
* this method multiple times, even if we've already tried to rekey
* more than the max. It will simply refuse to do so.
*
* @returns Boolean indicating if it actually issued a rekey request (that
* is, if we haven' already hit the max)
*/
PROT_UrlCryptoKeyManager.prototype.maybeReKey = function() {
if (this.rekeyTries_ > this.MAX_REKEY_TRIES) {
G_Debug(this, "Not re-keying; already at max");
return false;
}
this.reKey();
return true;
}
/**
* Drop the existing set of keys. Resets the rekeyTries variable to
* allow a rekey to succeed.
*/
PROT_UrlCryptoKeyManager.prototype.dropKey = function() {
this.rekeyTries_ = 0;
this.replaceKey_(null, null);
}
/**
* @returns Boolean indicating if we have a key we can use
*/
PROT_UrlCryptoKeyManager.prototype.hasKey = function() {
return this.clientKey_ != null && this.wrappedKey_ != null;
}
PROT_UrlCryptoKeyManager.prototype.unUrlSafe = function(key)
{
return key ? key.replace(/-/g, "+").replace(/_/g, "/") : "";
}
/**
* Set a new key and serialize it to disk.
*
* @param clientKey String containing the base64-encoded client key
* we wish to use
*
* @param wrappedKey String containing the opaque base64-encoded WrappedKey
* the server gave us (i.e., K_C encrypted with K_S)
*
* @returns Promise of a boolean indicating whether we succeeded in replacing
*/
PROT_UrlCryptoKeyManager.prototype.replaceKey_ = function(clientKey,
wrappedKey) {
if (this.clientKey_)
G_Debug(this, "Replacing " + this.clientKey_ + " with " + clientKey);
this.clientKey_ = clientKey;
this.clientKeyArray_ = Array.map(atob(this.unUrlSafe(clientKey)),
function(c) { return c.charCodeAt(0); });
this.wrappedKey_ = wrappedKey;
let promise = this.serializeKey_(this.clientKey_, this.wrappedKey_);
return promise.then(() => {
if (this.onNewKey_) {
this.onNewKey_();
}
return true;
});
}
/**
* Try to write the key to disk so we can fall back on it. Fail
* silently if we cannot. The keys are serialized in protocol4 format.
*
* @returns Promise of a boolean indicating whether we succeeded in serializing
*/
PROT_UrlCryptoKeyManager.prototype.serializeKey_ = function() {
var map = {};
map[this.CLIENT_KEY_NAME] = this.clientKey_;
map[this.WRAPPED_KEY_NAME] = this.wrappedKey_;
let keypath = OS.Path.join(OS.Constants.Path.profileDir, this.keyFilename_);
// if we have an invalid client key or wrapped key, we remove the
// invalid keyfile from disk
if (!this.clientKey_ || !this.wrappedKey_) {
return OS.File.remove(keypath).then(() => false,
e => {
if (!e.becauseNoSuchFile)
throw e;
return false;
});
}
let data = (new G_Protocol4Parser()).serialize(map);
let encoder = new TextEncoder();
let array = encoder.encode(data);
let promise = OS.File.writeAtomic(keypath, array, { tmpPath: keypath + ".tmp",
flush: false });
return promise.then(() => true,
e => {
G_Error(this, "Failed to serialize new key: " + e);
return false;
});
}
/**
* Invoked when we've received a protocol4 response to our getkey
* request. Try to parse it and set this key as the new one if we can.
*
* @param responseText String containing the protocol4 getkey response
*
* @returns Promise of a boolean indicating whether we succeeded in setting
* the new key
*/
PROT_UrlCryptoKeyManager.prototype.onGetKeyResponse = function(responseText) {
var response = (new G_Protocol4Parser).parse(responseText);
var clientKey = response[this.CLIENT_KEY_NAME];
var wrappedKey = response[this.WRAPPED_KEY_NAME];
this.updating_ = false;
this.fetcher_ = null;
if (response && clientKey && wrappedKey) {
G_Debug(this, "Got new key from: " + responseText);
return this.replaceKey_(clientKey, wrappedKey);
} else {
G_Debug(this, "Not a valid response for /newkey");
return Promise.resolve(false);
}
}
/**
* Set the callback to be called whenever we get a new key.
*
* @param callback The callback.
*/
PROT_UrlCryptoKeyManager.prototype.onNewKey = function(callback)
{
this.onNewKey_ = callback;
}
/**
* Attempt to read a key we've previously serialized from disk, so
* that we can fall back on it in case we can't get one from the
* server. If we get a key, only use it if we don't already have one
* (i.e., if our startup HTTPS request died or hasn't yet completed).
*
* This method should be invoked early, like when the user's profile
* becomes available.
*
* @returns Promise of a boolean indicating whether we succeeded in
* loading old key
*/
PROT_UrlCryptoKeyManager.prototype.maybeLoadOldKey = function() {
let keypath = OS.Path.join(OS.Constants.Path.profileDir, this.keyFilename_);
let decoder = new TextDecoder();
let promise = OS.File.read(keypath);
return promise.then(array => {
let oldKey = decoder.decode(array);
if (!oldKey) {
G_Debug(this, "Couldn't find old key.");
return false;
}
oldKey = (new G_Protocol4Parser).parse(oldKey);
var clientKey = oldKey[this.CLIENT_KEY_NAME];
var wrappedKey = oldKey[this.WRAPPED_KEY_NAME];
if (oldKey && clientKey && wrappedKey && !this.hasKey()) {
G_Debug(this, "Read old key from disk.");
return this.replaceKey_(clientKey, wrappedKey);
}
}, e => {
G_Debug(this, "Caught " + e + " trying to read keyfile");
return false;
});
}
PROT_UrlCryptoKeyManager.prototype.shutdown = function() {
if (this.fetcher_) {
this.fetcher_.cancel();
this.fetcher_ = null;
}
}

View File

@ -27,7 +27,7 @@ interface nsIUrlClassifierCallback : nsISupports {
* clients streaming updates to the url-classifier (usually
* nsUrlClassifierStreamUpdater.
*/
[scriptable, uuid(bbb33c65-e783-476c-8db0-6ddb91826c07)]
[scriptable, uuid(9fa11561-5816-4e1b-bcc9-b629ca05cce6)]
interface nsIUrlClassifierUpdateObserver : nsISupports {
/**
* The update requested a new URL whose contents should be downloaded
@ -36,18 +36,9 @@ interface nsIUrlClassifierUpdateObserver : nsISupports {
* @param url The url that was requested.
* @param table The table name that this URL's contents will be associated
* with. This should be passed back to beginStream().
* @param serverMAC The server-supplied MAC of the data at this URL. This
* should be passed back to beginStream().
*/
void updateUrlRequested(in ACString url,
in ACString table,
in ACString serverMAC);
/**
* The server has requested that the client get a new client key for
* MAC requests.
*/
void rekeyRequested();
in ACString table);
/**
* A stream update has completed.
@ -75,7 +66,7 @@ interface nsIUrlClassifierUpdateObserver : nsISupports {
* It provides async methods for querying and updating the database. As the
* methods complete, they call the callback function.
*/
[scriptable, uuid(e326ec41-46fd-4127-ad3c-3c58b2cdf196)]
[scriptable, uuid(8a389f21-f821-4e29-9c6b-3de6f33cd7cf)]
interface nsIUrlClassifierDBService : nsISupports
{
/**
@ -134,12 +125,9 @@ interface nsIUrlClassifierDBService : nsISupports
*
* @param updater The update observer tied to this update.
* @param tables A comma-separated list of tables included in this update.
* @param clientKey The client key for calculating an update's MAC,
* or empty to ignore MAC.
*/
void beginUpdate(in nsIUrlClassifierUpdateObserver updater,
in ACString tables,
in ACString clientKey);
in ACString tables);
/**
* Begin a stream update. This should be called once per url being
@ -147,12 +135,8 @@ interface nsIUrlClassifierDBService : nsISupports
*
* @param table The table the contents of this stream will be associated
* with, or empty for the initial stream.
* @param serverMAC The MAC specified by the update server for this stream.
* If the server has not specified a MAC (which is the case
* for the initial stream), this will be empty.
*/
void beginStream(in ACString table,
in ACString serverMAC);
void beginStream(in ACString table);
/**
* Update the table incrementally.

View File

@ -7,7 +7,7 @@
/**
* This interface is implemented by nsIUrlClassifierHashCompleter clients.
*/
[scriptable, uuid(bbd6c954-7cb4-4447-bc55-8cefd1ceed89)]
[scriptable, uuid(da16de40-df26-414d-bde7-c4faf4504868)]
interface nsIUrlClassifierHashCompleterCallback : nsISupports
{
/**
@ -21,13 +21,10 @@ interface nsIUrlClassifierHashCompleterCallback : nsISupports
* The name of the table that this hash belongs to.
* @param chunkId
* The database chunk that this hash belongs to.
* @param trusted
* The completion was verified with a MAC and can be cached.
*/
void completion(in ACString hash,
in ACString table,
in uint32_t chunkId,
in boolean trusted);
in uint32_t chunkId);
/**
* The completion is complete. This method is called once per
@ -62,12 +59,6 @@ interface nsIUrlClassifierHashCompleter : nsISupports
*/
void complete(in ACString partialHash,
in nsIUrlClassifierHashCompleterCallback callback);
/**
* Set the client and wrapped key for verified updates.
*/
void setKeys(in ACString clientKey, in ACString wrappedKey);
/**
* The URL for the gethash request
*/

View File

@ -11,7 +11,7 @@
* downloading the whole update and then updating the sqlite database, we
* update tables as the data is streaming in.
*/
[scriptable, uuid(daf3038a-556c-47d3-a3d2-36caa9a762a0)]
[scriptable, uuid(79e6b710-ce68-4639-ac6b-7d293af424a1)]
interface nsIUrlClassifierStreamUpdater : nsISupports
{
/**
@ -27,7 +27,6 @@ interface nsIUrlClassifierStreamUpdater : nsISupports
* @param aRequestTables Comma-separated list of tables included in this
* update.
* @param aRequestBody The body for the request.
* @param aClientKey The client key for checking the update's MAC.
* @param aSuccessCallback Called after a successful update.
* @param aUpdateErrorCallback Called for problems applying the update
* @param aDownloadErrorCallback Called if we get an http error or a
@ -35,7 +34,6 @@ interface nsIUrlClassifierStreamUpdater : nsISupports
*/
boolean downloadUpdates(in ACString aRequestTables,
in ACString aRequestBody,
in ACString aClientKey,
in nsIUrlClassifierCallback aSuccessCallback,
in nsIUrlClassifierCallback aUpdateErrorCallback,
in nsIUrlClassifierCallback aDownloadErrorCallback);

View File

@ -18,7 +18,7 @@ interface nsIUrlListManagerCallback : nsISupports {
};
[scriptable, uuid(5b4645b6-f9ca-4cb1-a821-2bdb3c3902f8)]
[scriptable, uuid(62484bb5-bf7e-4988-9055-8803b16b48a1)]
interface nsIUrlListManager : nsISupports
{
/**
@ -26,12 +26,6 @@ interface nsIUrlListManager : nsISupports
*/
void setUpdateUrl(in ACString url);
/**
* Set the URL we use to get keys used to decrypt URLs in
* enchash tables.
*/
void setKeyUrl(in ACString url);
/**
* Set the URL that we will query for complete hashes after a partial
* hash match.
@ -43,8 +37,7 @@ interface nsIUrlListManager : nsISupports
* string of the format provider_name-semantic_type-table_type. For
* example, goog-white-enchash or goog-black-url.
*/
boolean registerTable(in ACString tableName,
in boolean requireMac);
boolean registerTable(in ACString tableName);
/**
* Turn on update checking for a table. I.e., during the next server

View File

@ -181,9 +181,6 @@ private:
nsCOMPtr<nsIUrlClassifierUpdateObserver> mUpdateObserver;
bool mInStream;
// The client key with which the data from the server will be MAC'ed.
nsCString mUpdateClientKey;
// The number of noise entries to add to the set of lookup results.
uint32_t mGethashNoise;
@ -422,7 +419,6 @@ nsUrlClassifierDBServiceWorker::ResetUpdate()
mUpdateWait = 0;
mUpdateStatus = NS_OK;
mUpdateObserver = nullptr;
mUpdateClientKey.Truncate();
}
NS_IMETHODIMP
@ -434,8 +430,7 @@ nsUrlClassifierDBServiceWorker::SetHashCompleter(const nsACString &tableName,
NS_IMETHODIMP
nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
const nsACString &tables,
const nsACString &clientKey)
const nsACString &tables)
{
LOG(("nsUrlClassifierDBServiceWorker::BeginUpdate [%s]", PromiseFlatCString(tables).get()));
@ -454,19 +449,12 @@ nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *obse
mUpdateObserver = observer;
SplitTables(tables, mUpdateTables);
if (!clientKey.IsEmpty()) {
rv = nsUrlClassifierUtils::DecodeClientKey(clientKey, mUpdateClientKey);
NS_ENSURE_SUCCESS(rv, rv);
LOG(("clientKey present, marking update key"));
}
return NS_OK;
}
// Called from the stream updater.
NS_IMETHODIMP
nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table,
const nsACString &serverMAC)
nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table)
{
LOG(("nsUrlClassifierDBServiceWorker::BeginStream"));
@ -486,17 +474,6 @@ nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table,
mProtocolParser->Init(mCryptoHash);
nsresult rv;
// If we're expecting a MAC, create the nsICryptoHMAC component now.
if (!mUpdateClientKey.IsEmpty()) {
LOG(("Expecting MAC in this stream"));
rv = mProtocolParser->InitHMAC(mUpdateClientKey, serverMAC);
NS_ENSURE_SUCCESS(rv, rv);
} else {
LOG(("No MAC in this stream"));
}
if (!table.IsEmpty()) {
mProtocolParser->SetCurrentTable(table);
}
@ -560,7 +537,6 @@ nsUrlClassifierDBServiceWorker::FinishStream()
mInStream = false;
mProtocolParser->FinishHMAC();
if (NS_SUCCEEDED(mProtocolParser->Status())) {
if (mProtocolParser->UpdateWait()) {
mUpdateWait = mProtocolParser->UpdateWait();
@ -570,7 +546,7 @@ nsUrlClassifierDBServiceWorker::FinishStream()
mProtocolParser->Forwards();
for (uint32_t i = 0; i < forwards.Length(); i++) {
const ProtocolParser::ForwardedUpdate &forward = forwards[i];
mUpdateObserver->UpdateUrlRequested(forward.url, forward.table, forward.mac);
mUpdateObserver->UpdateUrlRequested(forward.url, forward.table);
}
// Hold on to any TableUpdate objects that were created by the
// parser.
@ -581,18 +557,12 @@ nsUrlClassifierDBServiceWorker::FinishStream()
}
mUpdateObserver->StreamFinished(mProtocolParser->Status(), 0);
// Only reset if MAC was OK
if (NS_SUCCEEDED(mUpdateStatus)) {
if (mProtocolParser->ResetRequested()) {
mClassifier->Reset();
}
}
// Rekey will cause update to fail (can't check MACs)
if (mProtocolParser->RekeyRequested()) {
mUpdateObserver->RekeyRequested();
}
mProtocolParser = nullptr;
return NS_OK;
@ -908,11 +878,10 @@ nsUrlClassifierLookupCallback::CompletionFinished(nsresult status)
NS_IMETHODIMP
nsUrlClassifierLookupCallback::Completion(const nsACString& completeHash,
const nsACString& tableName,
uint32_t chunkId,
bool verified)
uint32_t chunkId)
{
LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d, %d]",
this, PromiseFlatCString(tableName).get(), chunkId, verified));
LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d]",
this, PromiseFlatCString(tableName).get(), chunkId));
mozilla::safebrowsing::Completion hash;
hash.Assign(completeHash);
@ -923,15 +892,13 @@ nsUrlClassifierLookupCallback::Completion(const nsACString& completeHash,
return NS_ERROR_OUT_OF_MEMORY;
}
if (verified) {
CacheResult result;
result.entry.addChunk = chunkId;
result.entry.complete = hash;
result.table = tableName;
CacheResult result;
result.entry.addChunk = chunkId;
result.entry.complete = hash;
result.table = tableName;
// OK if this fails, we just won't cache the item.
mCacheResults->AppendElement(result);
}
// OK if this fails, we just won't cache the item.
mCacheResults->AppendElement(result);
// Check if this matched any of our results.
for (uint32_t i = 0; i < mResults->Length(); i++) {
@ -1329,8 +1296,7 @@ nsUrlClassifierDBService::SetHashCompleter(const nsACString &tableName,
NS_IMETHODIMP
nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
const nsACString &updateTables,
const nsACString &clientKey)
const nsACString &updateTables)
{
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
@ -1343,16 +1309,15 @@ nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
nsCOMPtr<nsIUrlClassifierUpdateObserver> proxyObserver =
new UrlClassifierUpdateObserverProxy(observer);
return mWorkerProxy->BeginUpdate(proxyObserver, updateTables, clientKey);
return mWorkerProxy->BeginUpdate(proxyObserver, updateTables);
}
NS_IMETHODIMP
nsUrlClassifierDBService::BeginStream(const nsACString &table,
const nsACString &serverMAC)
nsUrlClassifierDBService::BeginStream(const nsACString &table)
{
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
return mWorkerProxy->BeginStream(table, serverMAC);
return mWorkerProxy->BeginStream(table);
}
NS_IMETHODIMP

View File

@ -30,8 +30,6 @@ const BACKOFF_TIME = 5 * 60;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let keyFactory = Cc["@mozilla.org/security/keyobjectfactory;1"]
.getService(Ci.nsIKeyObjectFactory);
function HashCompleter() {
// This is a HashCompleterRequest and is used by multiple calls to |complete|
@ -39,13 +37,6 @@ function HashCompleter() {
// started, this is set to null again.
this._currentRequest = null;
// Key used in the HMAC process by the client to verify the request has not
// been tampered with. It is stored as a binary blob.
this._clientKey = "";
// Key used in the HMAC process and sent remotely to the server in the URL
// of the request. It is stored as a base64 string.
this._wrappedKey = "";
// Whether we have been informed of a shutdown by the xpcom-shutdown event.
this._shuttingDown = false;
@ -110,10 +101,6 @@ HashCompleter.prototype = {
}
let url = this._getHashUrl;
if (this._clientKey) {
this._currentRequest.clientKey = this._clientKey;
url += "&wrkey=" + this._wrappedKey;
}
let uri = Services.io.newURI(url, null, null);
this._currentRequest.setURI(uri);
@ -127,32 +114,6 @@ HashCompleter.prototype = {
}
},
// When a rekey has been requested, we can only clear our keys and make
// unauthenticated requests.
// The HashCompleter does not handle the rekeying but instead sends a
// notification to have listeners do the work.
rekeyRequested: function HC_rekeyRequested() {
this.setKeys("", "");
Services.obs.notifyObservers(this, "url-classifier-rekey-requested", null);
},
// setKeys expects clientKey and wrappedKey to be url safe, base64 strings.
// When called with an empty client string, setKeys resets both the client
// key and wrapped key.
setKeys: function HC_setKeys(aClientKey, aWrappedKey) {
if (aClientKey == "") {
this._clientKey = "";
this._wrappedKey = "";
return;
}
// The decoding of clientKey was originally done by using
// nsUrlClassifierUtils::DecodeClientKey.
this._clientKey = atob(unUrlsafeBase64(aClientKey));
this._wrappedKey = aWrappedKey;
},
get gethashUrl() {
return this._getHashUrl;
},
@ -232,14 +193,6 @@ function HashCompleterRequest(aCompleter) {
this._channel = null;
// Response body of hash completion. Created in onDataAvailable.
this._response = "";
// Client key when HMAC is used.
this._clientKey = "";
// Request was rescheduled, possibly due to a "e:pleaserekey" request from
// the server.
this._rescheduled = false;
// Whether the request was encrypted. This is also used as the |trusted|
// parameter to the nsIUrlClassifierHashCompleterCallback.
this._verified = false;
// Whether we have been informed of a shutdown by the xpcom-shutdown event.
this._shuttingDown = false;
}
@ -351,73 +304,18 @@ HashCompleterRequest.prototype = {
}
let start = 0;
if (this._clientKey) {
start = this.handleMAC(start);
if (this._rescheduled) {
return;
}
}
let length = this._response.length;
while (start != length)
start = this.handleTable(start);
},
// This parses and confirms that the MAC in the response matches the expected
// value. This throws an error if the MAC does not match or otherwise, returns
// the index after the MAC header.
handleMAC: function HCR_handleMAC(aStart) {
this._verified = false;
let body = this._response.substring(aStart);
// We have to deal with the index of the new line character instead of
// splitting the string as there could be new line characters in the data
// parts.
let newlineIndex = body.indexOf("\n");
if (newlineIndex == -1) {
throw errorWithStack();
}
let serverMAC = body.substring(0, newlineIndex);
if (serverMAC == "e:pleaserekey") {
this.rescheduleItems();
this._completer.rekeyRequested();
return this._response.length;
}
serverMAC = unUrlsafeBase64(serverMAC);
let keyObject = keyFactory.keyFromString(Ci.nsIKeyObject.HMAC,
this._clientKey);
let data = body.substring(newlineIndex + 1).split("")
.map(function(x) x.charCodeAt(0));
let hmac = Cc["@mozilla.org/security/hmac;1"]
.createInstance(Ci.nsICryptoHMAC);
hmac.init(Ci.nsICryptoHMAC.SHA1, keyObject);
hmac.update(data, data.length);
let clientMAC = hmac.finish(true);
if (clientMAC != serverMAC) {
throw errorWithStack();
}
this._verified = true;
return aStart + newlineIndex + 1;
},
// This parses a table entry in the response body and calls |handleItem|
// for complete hash in the table entry. Like |handleMAC|, it returns the
// index in |_response| right after the table it parsed.
// for complete hash in the table entry.
handleTable: function HCR_handleTable(aStart) {
let body = this._response.substring(aStart);
// Like in handleMAC, we deal with new line indexes as there could be
// deal with new line indexes as there could be
// new line characters in the data parts.
let newlineIndex = body.indexOf("\n");
if (newlineIndex == -1) {
@ -473,7 +371,7 @@ HashCompleterRequest.prototype = {
for (let j = 0; j < request.responses.length; j++) {
let response = request.responses[j];
request.callback.completion(response.completeHash, response.tableName,
response.chunkId, this._verified);
response.chunkId);
}
request.callback.completionFinished(Cr.NS_OK);
@ -486,23 +384,6 @@ HashCompleterRequest.prototype = {
}
},
// rescheduleItems is called after a "e:pleaserekey" response. It is meant
// to be called after |rekeyRequested| has been called as it re-calls
// the HashCompleter with |complete| for all the items on this request.
rescheduleItems: function HCR_rescheduleItems() {
for (let i = 0; i < this._requests[i]; i++) {
let request = this._requests[i];
try {
this._completer.complete(request.partialHash, request.callback);
}
catch (err) {
request.callback.completionFinished(err);
}
}
this._rescheduled = true;
},
onDataAvailable: function HCR_onDataAvailable(aRequest, aContext,
aInputStream, aOffset, aCount) {
let sis = Cc["@mozilla.org/scriptableinputstream;1"].
@ -545,19 +426,13 @@ HashCompleterRequest.prototype = {
}
}
if (!this._rescheduled) {
if (success) {
this.notifySuccess();
} else {
this.notifyFailure(aStatusCode);
}
if (success) {
this.notifySuccess();
} else {
this.notifyFailure(aStatusCode);
}
},
set clientKey(aVal) {
this._clientKey = aVal;
},
observe: function HCR_observe(aSubject, aTopic, aData) {
if (aTopic != "xpcom-shutdown") {
return;

View File

@ -21,7 +21,6 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
#include ./content/moz/protocol4.js
#include ./content/request-backoff.js
#include ./content/url-crypto-key-manager.js
#include ./content/xml-fetcher.js
// Expose this whole component.

View File

@ -24,7 +24,6 @@ function Init() {
modScope.G_Alarm = jslib.G_Alarm;
modScope.BindToObject = jslib.BindToObject;
modScope.PROT_XMLFetcher = jslib.PROT_XMLFetcher;
modScope.PROT_UrlCryptoKeyManager = jslib.PROT_UrlCryptoKeyManager;
modScope.RequestBackoff = jslib.RequestBackoff;
// We only need to call Init once.

View File

@ -61,34 +61,32 @@ UrlClassifierDBServiceWorkerProxy::SetHashCompleter
NS_IMETHODIMP
UrlClassifierDBServiceWorkerProxy::BeginUpdate
(nsIUrlClassifierUpdateObserver* aUpdater,
const nsACString& aTables,
const nsACString& aClientKey)
const nsACString& aTables)
{
nsCOMPtr<nsIRunnable> r = new BeginUpdateRunnable(mTarget, aUpdater,
aTables, aClientKey);
aTables);
return DispatchToWorkerThread(r);
}
NS_IMETHODIMP
UrlClassifierDBServiceWorkerProxy::BeginUpdateRunnable::Run()
{
mTarget->BeginUpdate(mUpdater, mTables, mClientKey);
mTarget->BeginUpdate(mUpdater, mTables);
return NS_OK;
}
NS_IMETHODIMP
UrlClassifierDBServiceWorkerProxy::BeginStream(const nsACString& aTable,
const nsACString& aServerMAC)
UrlClassifierDBServiceWorkerProxy::BeginStream(const nsACString& aTable)
{
nsCOMPtr<nsIRunnable> r =
new BeginStreamRunnable(mTarget, aTable, aServerMAC);
new BeginStreamRunnable(mTarget, aTable);
return DispatchToWorkerThread(r);
}
NS_IMETHODIMP
UrlClassifierDBServiceWorkerProxy::BeginStreamRunnable::Run()
{
mTarget->BeginStream(mTable, mServerMAC);
mTarget->BeginStream(mTable);
return NS_OK;
}
@ -222,32 +220,17 @@ NS_IMPL_ISUPPORTS1(UrlClassifierUpdateObserverProxy,
NS_IMETHODIMP
UrlClassifierUpdateObserverProxy::UpdateUrlRequested
(const nsACString& aURL,
const nsACString& aTable,
const nsACString& aServerMAC)
const nsACString& aTable)
{
nsCOMPtr<nsIRunnable> r =
new UpdateUrlRequestedRunnable(mTarget, aURL, aTable, aServerMAC);
new UpdateUrlRequestedRunnable(mTarget, aURL, aTable);
return NS_DispatchToMainThread(r);
}
NS_IMETHODIMP
UrlClassifierUpdateObserverProxy::UpdateUrlRequestedRunnable::Run()
{
mTarget->UpdateUrlRequested(mURL, mTable, mServerMAC);
return NS_OK;
}
NS_IMETHODIMP
UrlClassifierUpdateObserverProxy::RekeyRequested()
{
nsCOMPtr<nsIRunnable> r = new RekeyRequestedRunnable(mTarget);
return NS_DispatchToMainThread(r);
}
NS_IMETHODIMP
UrlClassifierUpdateObserverProxy::RekeyRequestedRunnable::Run()
{
mTarget->RekeyRequested();
mTarget->UpdateUrlRequested(mURL, mTable);
return NS_OK;
}

View File

@ -69,12 +69,10 @@ public:
public:
BeginUpdateRunnable(nsIUrlClassifierDBServiceWorker* aTarget,
nsIUrlClassifierUpdateObserver* aUpdater,
const nsACString& aTables,
const nsACString& aClientKey)
const nsACString& aTables)
: mTarget(aTarget)
, mUpdater(aUpdater)
, mTables(aTables)
, mClientKey(aClientKey)
{ }
NS_DECL_NSIRUNNABLE
@ -82,25 +80,23 @@ public:
private:
nsCOMPtr<nsIUrlClassifierDBServiceWorker> mTarget;
nsCOMPtr<nsIUrlClassifierUpdateObserver> mUpdater;
nsCString mTables, mClientKey;
nsCString mTables;
};
class BeginStreamRunnable : public nsRunnable
{
public:
BeginStreamRunnable(nsIUrlClassifierDBServiceWorker* aTarget,
const nsACString& aTable,
const nsACString& aServerMAC)
const nsACString& aTable)
: mTarget(aTarget)
, mTable(aTable)
, mServerMAC(aServerMAC)
{ }
NS_DECL_NSIRUNNABLE
private:
nsCOMPtr<nsIUrlClassifierDBServiceWorker> mTarget;
nsCString mTable, mServerMAC;
nsCString mTable;
};
class UpdateStreamRunnable : public nsRunnable
@ -234,32 +230,17 @@ public:
public:
UpdateUrlRequestedRunnable(const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget,
const nsACString& aURL,
const nsACString& aTable,
const nsACString& aServerMAC)
const nsACString& aTable)
: mTarget(aTarget)
, mURL(aURL)
, mTable(aTable)
, mServerMAC(aServerMAC)
{ }
NS_DECL_NSIRUNNABLE
private:
nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
nsCString mURL, mTable, mServerMAC;
};
class RekeyRequestedRunnable : public nsRunnable
{
public:
RekeyRequestedRunnable(const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget)
: mTarget(aTarget)
{ }
NS_DECL_NSIRUNNABLE
private:
nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
nsCString mURL, mTable;
};
class StreamFinishedRunnable : public nsRunnable

View File

@ -101,8 +101,7 @@ nsUrlClassifierStreamUpdater::SetUpdateUrl(const nsACString & aUpdateUrl)
nsresult
nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl,
const nsACString & aRequestBody,
const nsACString & aStreamTable,
const nsACString & aServerMAC)
const nsACString & aStreamTable)
{
nsresult rv;
uint32_t loadFlags = nsIChannel::INHIBIT_CACHING |
@ -141,7 +140,6 @@ nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl,
NS_ENSURE_SUCCESS(rv, rv);
mStreamTable = aStreamTable;
mServerMAC = aServerMAC;
return NS_OK;
}
@ -149,8 +147,7 @@ nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl,
nsresult
nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl,
const nsACString & aRequestBody,
const nsACString & aStreamTable,
const nsACString & aServerMAC)
const nsACString & aStreamTable)
{
LOG(("(pre) Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get()));
@ -163,14 +160,13 @@ nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl,
LOG(("(post) Fetching update from %s\n", urlSpec.get()));
return FetchUpdate(uri, aRequestBody, aStreamTable, aServerMAC);
return FetchUpdate(uri, aRequestBody, aStreamTable);
}
NS_IMETHODIMP
nsUrlClassifierStreamUpdater::DownloadUpdates(
const nsACString &aRequestTables,
const nsACString &aRequestBody,
const nsACString &aClientKey,
nsIUrlClassifierCallback *aSuccessCallback,
nsIUrlClassifierCallback *aUpdateErrorCallback,
nsIUrlClassifierCallback *aDownloadErrorCallback,
@ -210,7 +206,7 @@ nsUrlClassifierStreamUpdater::DownloadUpdates(
mInitialized = true;
}
rv = mDBService->BeginUpdate(this, aRequestTables, aClientKey);
rv = mDBService->BeginUpdate(this, aRequestTables);
if (rv == NS_ERROR_NOT_AVAILABLE) {
LOG(("already updating, skipping update"));
*_retval = false;
@ -233,7 +229,7 @@ nsUrlClassifierStreamUpdater::DownloadUpdates(
//LOG(("requestBody: %s", aRequestBody.Data()));
LOG(("Calling into FetchUpdate"));
return FetchUpdate(mUpdateUrl, aRequestBody, EmptyCString(), EmptyCString());
return FetchUpdate(mUpdateUrl, aRequestBody, EmptyCString());
}
///////////////////////////////////////////////////////////////////////////////
@ -241,8 +237,7 @@ nsUrlClassifierStreamUpdater::DownloadUpdates(
NS_IMETHODIMP
nsUrlClassifierStreamUpdater::UpdateUrlRequested(const nsACString &aUrl,
const nsACString &aTable,
const nsACString &aServerMAC)
const nsACString &aTable)
{
LOG(("Queuing requested update from %s\n", PromiseFlatCString(aUrl).get()));
@ -261,25 +256,10 @@ nsUrlClassifierStreamUpdater::UpdateUrlRequested(const nsACString &aUrl,
update->mUrl = NS_LITERAL_CSTRING("http://") + aUrl;
}
update->mTable = aTable;
update->mServerMAC = aServerMAC;
return NS_OK;
}
NS_IMETHODIMP
nsUrlClassifierStreamUpdater::RekeyRequested()
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService)
return NS_ERROR_FAILURE;
return observerService->NotifyObservers(static_cast<nsIUrlClassifierStreamUpdater*>(this),
"url-classifier-rekey-requested",
nullptr);
}
nsresult
nsUrlClassifierStreamUpdater::FetchNext()
{
@ -290,7 +270,7 @@ nsUrlClassifierStreamUpdater::FetchNext()
PendingUpdate &update = mPendingUpdates[0];
LOG(("Fetching update url: %s\n", update.mUrl.get()));
nsresult rv = FetchUpdate(update.mUrl, EmptyCString(),
update.mTable, update.mServerMAC);
update.mTable);
if (NS_FAILED(rv)) {
LOG(("Error fetching update url: %s\n", update.mUrl.get()));
// We can commit the urls that we've applied so far. This is
@ -453,12 +433,11 @@ nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest *request,
status = NS_ERROR_ABORT;
} else if (NS_SUCCEEDED(status)) {
mBeganStream = true;
rv = mDBService->BeginStream(mStreamTable, mServerMAC);
rv = mDBService->BeginStream(mStreamTable);
NS_ENSURE_SUCCESS(rv, rv);
}
mStreamTable.Truncate();
mServerMAC.Truncate();
return status;
}

View File

@ -55,13 +55,11 @@ private:
// Fetches an update for a single table.
nsresult FetchUpdate(nsIURI *aURI,
const nsACString &aRequestBody,
const nsACString &aTable,
const nsACString &aServerMAC);
const nsACString &aTable);
// Dumb wrapper so we don't have to create URIs.
nsresult FetchUpdate(const nsACString &aURI,
const nsACString &aRequestBody,
const nsACString &aTable,
const nsACString &aServerMAC);
const nsACString &aTable);
// Fetches the next table, from mPendingUpdates.
nsresult FetchNext();
@ -72,7 +70,6 @@ private:
bool mBeganStream;
nsCOMPtr<nsIURI> mUpdateUrl;
nsCString mStreamTable;
nsCString mServerMAC;
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIUrlClassifierDBService> mDBService;
nsCOMPtr<nsITimer> mTimer;
@ -80,7 +77,6 @@ private:
struct PendingUpdate {
nsCString mUrl;
nsCString mTable;
nsCString mServerMAC;
};
nsTArray<PendingUpdate> mPendingUpdates;

View File

@ -362,54 +362,3 @@ nsUrlClassifierUtils::ShouldURLEscape(const unsigned char c) const
{
return c <= 32 || c == '%' || c >=127;
}
/* static */
void
nsUrlClassifierUtils::UnUrlsafeBase64(nsACString &str)
{
nsACString::iterator iter, end;
str.BeginWriting(iter);
str.EndWriting(end);
while (iter != end) {
if (*iter == '-') {
*iter = '+';
} else if (*iter == '_') {
*iter = '/';
}
iter++;
}
}
/* static */
nsresult
nsUrlClassifierUtils::DecodeClientKey(const nsACString &key,
nsACString &_retval)
{
// Client key is sent in urlsafe base64, we need to decode it first.
nsAutoCString base64(key);
UnUrlsafeBase64(base64);
// PL_Base64Decode doesn't null-terminate unless we let it allocate,
// so we need to calculate the length ourselves.
uint32_t destLength;
destLength = base64.Length();
if (destLength > 0 && base64[destLength - 1] == '=') {
if (destLength > 1 && base64[destLength - 2] == '=') {
destLength -= 2;
} else {
destLength -= 1;
}
}
destLength = ((destLength * 3) / 4);
_retval.SetLength(destLength);
if (destLength != _retval.Length())
return NS_ERROR_OUT_OF_MEMORY;
if (!PL_Base64Decode(base64.BeginReading(), base64.Length(),
_retval.BeginWriting())) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}

View File

@ -73,14 +73,6 @@ public:
bool allowOctal,
nsACString & _retval);
// Convert an urlsafe base64 string to a normal base64 string.
// This method will leave an already-normal base64 string alone.
static void UnUrlsafeBase64(nsACString & str);
// Takes an urlsafe-base64 encoded client key and gives back binary
// key data
static nsresult DecodeClientKey(const nsACString & clientKey,
nsACString & _retval);
private:
// Disallow copy constructor
nsUrlClassifierUtils(const nsUrlClassifierUtils&);