gecko-dev/services/common/KintoCertificateBlocklist.js

116 строки
4.3 KiB
JavaScript

/* 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/. */
"use strict";
this.EXPORTED_SYMBOLS = ["OneCRLClient"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://services-common/moz-kinto-client.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
const PREF_KINTO_BASE = "services.kinto.base";
const PREF_KINTO_BUCKET = "services.kinto.bucket";
const PREF_KINTO_ONECRL_COLLECTION = "services.kinto.onecrl.collection";
const PREF_KINTO_ONECRL_CHECKED_SECONDS = "services.kinto.onecrl.checked";
const RE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
// Kinto.js assumes version 4 UUIDs but allows you to specify custom
// validators and generators. The tooling that generates records in the
// certificates collection currently uses a version 1 UUID so we must
// specify a validator that's less strict. We must also supply a generator
// since Kinto.js does not allow one without the other.
function makeIDSchema() {
return {
validate: RE_UUID.test.bind(RE_UUID),
generate: function() {
return uuidgen.generateUUID().toString();
}
};
}
// A Kinto based client to keep the OneCRL certificate blocklist up to date.
function CertBlocklistClient() {
// maybe sync the collection of certificates with remote data.
// lastModified - the lastModified date (on the server, milliseconds since
// epoch) of data in the remote collection
// serverTime - the time on the server (milliseconds since epoch)
// returns a promise which rejects on sync failure
this.maybeSync = function(lastModified, serverTime) {
let base = Services.prefs.getCharPref(PREF_KINTO_BASE);
let bucket = Services.prefs.getCharPref(PREF_KINTO_BUCKET);
let Kinto = loadKinto();
let FirefoxAdapter = Kinto.adapters.FirefoxAdapter;
let certList = Cc["@mozilla.org/security/certblocklist;1"]
.getService(Ci.nsICertBlocklist);
// Future blocklist clients can extract the sync-if-stale logic. For
// now, since this is currently the only client, we'll do this here.
let config = {
remote: base,
bucket: bucket,
adapter: FirefoxAdapter,
};
let db = new Kinto(config);
let collectionName = Services.prefs.getCharPref(PREF_KINTO_ONECRL_COLLECTION,
"certificates");
let blocklist = db.collection(collectionName,
{ idSchema: makeIDSchema() });
let updateLastCheck = function() {
let checkedServerTimeInSeconds = Math.round(serverTime / 1000);
Services.prefs.setIntPref(PREF_KINTO_ONECRL_CHECKED_SECONDS,
checkedServerTimeInSeconds);
}
return Task.spawn(function* () {
try {
yield blocklist.db.open();
let collectionLastModified = yield blocklist.db.getLastModified();
// if the data is up to date, there's no need to sync. We still need
// to record the fact that a check happened.
if (lastModified <= collectionLastModified) {
updateLastCheck();
return;
}
yield blocklist.sync();
let list = yield blocklist.list();
for (let item of list.data) {
if (item.issuerName && item.serialNumber) {
certList.revokeCertByIssuerAndSerial(item.issuerName,
item.serialNumber);
} else if (item.subject && item.pubKeyHash) {
certList.revokeCertBySubjectAndPubKey(item.subject,
item.pubKeyHash);
} else {
throw new Error("Cert blocklist record has incomplete data");
}
}
// We explicitly do not want to save entries or update the
// last-checked time if sync fails
certList.saveEntries();
updateLastCheck();
} finally {
blocklist.db.close()
}
});
}
}
this.OneCRLClient = new CertBlocklistClient();