From: Marek VavruĊĦa Date: Fri, 25 Sep 2015 14:58:24 +0000 (+0200) Subject: daemon/trust_anchors: finished state table, own module, cleanup X-Git-Tag: v1.0.0-beta1~53^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e9aeeebe9cecfd808893052243b393affd256875;p=thirdparty%2Fknot-resolver.git daemon/trust_anchors: finished state table, own module, cleanup todo: active refresh --- diff --git a/daemon/daemon.mk b/daemon/daemon.mk index b0103e83a..526c68a95 100644 --- a/daemon/daemon.mk +++ b/daemon/daemon.mk @@ -16,8 +16,8 @@ daemon/engine.o: daemon/lua/sandbox.inc daemon/lua/config.inc %.inc: %.lua @$(call quiet,XXD,$<) $< > $@ # Installed FFI bindings -bindings-install: daemon/lua/kres.lua - $(INSTALL) $< $(PREFIX)/$(MODULEDIR) +bindings-install: daemon/lua/kres.lua daemon/lua/trust_anchors.lua + $(INSTALL) $^ $(PREFIX)/$(MODULEDIR) # Dependencies kresd_DEPEND := $(libkres) diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua index 4ab856465..1b19e1348 100644 --- a/daemon/lua/kres.lua +++ b/daemon/lua/kres.lua @@ -274,7 +274,6 @@ ffi.metatype( kr_request_t, { }) -- Module API -local kres_context = ffi.cast('struct kr_context *', __engine) local kres = { -- Constants class = ffi.new('struct rr_class'), @@ -288,131 +287,7 @@ local kres = { -- Global API functions str2dname = function(name) return ffi.string(ffi.gc(C.knot_dname_from_str(nil, name, 0), C.free)) end, dname2str = function(dname) return ffi.string(ffi.gc(C.knot_dname_to_str(nil, dname, 0), C.free)) end, - context = function () return kres_context end, -} - --- RFC5011 state table -local key_state = { - Start = 'Start', AddPend = 'AddPend', Valid = 'Valid', - Missing = 'Missing', Revoked = 'Revoked', Removed = 'Removed' -} - --- Find key in current keyset -local function ta_find(keyset, rr) - for i = 1, #keyset do - local ta = keyset[i] - -- Match key owner and content - if ta.owner == rr.owner and - C.kr_dnssec_key_match(ta.rdata, #ta.rdata, rr.rdata, #rr.rdata) then - return ta - end - end - return nil -end - --- Evaluate TA status according to RFC5011 -local function ta_present(keyset, rr, force) - if not C.kr_dnssec_key_ksk(rr.rdata) then - return false -- Ignore - end - -- Find the key in current key set and check its status - local key_revoked = C.kr_dnssec_key_revoked(rr.rdata) - local key_tag = C.kr_dnssec_key_tag(rr.type, rr.rdata, #rr.rdata) - local ta = ta_find(keyset, rr) - if ta then - -- Key reappears (KeyPres) - if ta.state == key_state.Missing then ta.state = key_state.Valid end - -- Key is revoked (RevBit) - if ta.state == key_state.Valid or ta.state == key_state.Missing then - if key_revoked then - ta.state = key_state.Revoked - -- @todo: ta.time = ... - end - end - -- @todo RemTime - -- @todo AddTime - -- Preserve key (KeyPres) - print('[trust_anchors] key: '..key_tag..' state: '..ta.state) - return true - elseif not key_revoked then -- First time seen (NewKey) - rr.state = force and key_state.Valid or key_state.AddPend - -- rr.time = ... - print('[trust_anchors] key: '..key_tag..' state: '..rr.state) - table.insert(keyset, rr) - return true - end - return false -end - --- TA is missing in the new key set -local function ta_missing(keyset, ta) - -- Key is removed (KeyRem) - if ta.state == key_state.Valid then - ta.state = key_state.Missing - -- ta.time = ... - elseif ta.state == key_state.AddPend then - -- @todo: remove from the set (Start) - end - local key_tag = C.kr_dnssec_key_tag(ta.type, ta.rdata, #ta.rdata) - print('[trust_anchors] key: '..key_tag..' state: '..ta.state) -end - --- TA store management -kres.trust_anchors = { - keyset = {}, - insecure = {}, - -- Update existing keyset - update = function (new_keys, initial) - -- Flag keys missing in new set (KeyRem) - local keyset = kres.trust_anchors.keyset - for i = 1, #keyset do - local ta = keyset[i] - if not ta_find(new_keys, ta) then - ta_missing(keyset, ta) - end - end - -- Evaluate new TAs - if not new_keys then return false end - for i = 1, #new_keys do - local rr = new_keys[i] - if rr.type == kres.type.DNSKEY then - ta_present(keyset, rr, initial) - end - end - -- Publish active TAs - local store = kres_context.trust_anchors - C.kr_ta_clear(store) - for i = 1, #keyset do - local ta = keyset[i] - -- Key MAY be used as a TA only in these two states (RFC5011, 4.2) - if ta.state == key_state.Valid or ta.state == key_state.Missing then - C.kr_ta_add(store, ta.owner, ta.type, ta.ttl, ta.rdata, #ta.rdata) - end - end - return true - end, - -- Load keys from a file (managed) - config = function (path) - local new_keys = require('zonefile').parse_file(path) - return kres.trust_anchors.update(new_keys, true) - end, - -- Add DS/DNSKEY record(s) (unmanaged) - add = function (keystr) - local store = kres_context.trust_anchors - require('zonefile').parser(function (p) - local rr = p:current_rr() - C.kr_ta_add(store, rr.owner, rr.type, rr.ttl, rr.rdata, #rr.rdata) - end):read(keystr..'\n') - end, - -- Negative TA management - set_insecure = function (list) - C.kr_ta_clear(kres_context.negative_anchors) - for i = 1, #list do - local dname = kres.str2dname(list[i]) - C.kr_ta_add(kres_context.negative_anchors, dname, kres.type.DS, 0, nil, 0) - end - kres.trust_anchors.insecure = list - end, + context = function () return ffi.cast('struct kr_context *', __engine) end, } return kres \ No newline at end of file diff --git a/daemon/lua/sandbox.lua b/daemon/lua/sandbox.lua index 048814133..b25a9deb4 100644 --- a/daemon/lua/sandbox.lua +++ b/daemon/lua/sandbox.lua @@ -6,9 +6,11 @@ GB = 1024*MB sec = 1000 minute = 60 * sec hour = 60 * minute +day = 24 * hour -- Resolver bindings kres = require('kres') +trust_anchors = require('trust_anchors') -- Function aliases -- `env.VAR returns os.getenv(VAR)` @@ -70,7 +72,6 @@ setmetatable(cache, { cache.size = 10 * MB -- Syntactic sugar for TA store -trust_anchors = kres.trust_anchors setmetatable(trust_anchors, { __newindex = function (t,k,v) if k == 'file' then t.config(v) diff --git a/daemon/lua/trust_anchors.lua b/daemon/lua/trust_anchors.lua new file mode 100644 index 000000000..7f3c287bc --- /dev/null +++ b/daemon/lua/trust_anchors.lua @@ -0,0 +1,158 @@ +local kres = require('kres') +local C = require('ffi').C + +-- Add or remove hold-down timer +local hold_down_time = 30 * day + +-- RFC5011 state table +local key_state = { + Start = 'Start', AddPend = 'AddPend', Valid = 'Valid', + Missing = 'Missing', Revoked = 'Revoked', Removed = 'Removed' +} + +-- Find key in current keyset +local function ta_find(keyset, rr) + for i = 1, #keyset do + local ta = keyset[i] + -- Match key owner and content + if ta.owner == rr.owner and + C.kr_dnssec_key_match(ta.rdata, #ta.rdata, rr.rdata, #rr.rdata) then + return ta + end + end + return nil +end + +-- Evaluate TA status according to RFC5011 +local function ta_present(keyset, rr, force) + if not C.kr_dnssec_key_ksk(rr.rdata) then + return false -- Ignore + end + -- Find the key in current key set and check its status + local now = os.time() + local key_revoked = C.kr_dnssec_key_revoked(rr.rdata) + local key_tag = C.kr_dnssec_key_tag(rr.type, rr.rdata, #rr.rdata) + local ta = ta_find(keyset, rr) + if ta then + -- Key reappears (KeyPres) + if ta.state == key_state.Missing then + ta.state = key_state.Valid + ta.timer = nil + end + -- Key is revoked (RevBit) + if ta.state == key_state.Valid or ta.state == key_state.Missing then + if key_revoked then + ta.state = key_state.Revoked + ta.timer = os.time() + hold_down_time + end + end + -- Remove hold-down timer expires (RemTime) + if ta.state == key_state.Revoked and os.difftime(ta.timer, now) <= 0 then + ta.state = key_state.Removed + ta.timer = nil + end + -- Add hold-down timer expires (AddTime) + if ta.state == key_state.AddPend and os.difftime(ta.timer, now) <= 0 then + ta.state = key_state.Valid + ta.timer = nil + end + print('[trust_anchors] key: '..key_tag..' state: '..ta.state) + return true + elseif not key_revoked then -- First time seen (NewKey) + if force then + rr.state = key_state.Valid + else + rr.state = key_state.AddPend + rr.timer = now + hold_down_time + end + print('[trust_anchors] key: '..key_tag..' state: '..rr.state) + table.insert(keyset, rr) + return true + end + return false +end + +-- TA is missing in the new key set +local function ta_missing(keyset, ta) + -- Key is removed (KeyRem) + local keep_ta = true + local key_tag = C.kr_dnssec_key_tag(ta.type, ta.rdata, #ta.rdata) + if ta.state == key_state.Valid then + ta.state = key_state.Missing + ta.timer = os.time() + hold_down_time + -- Purge pending key + elseif ta.state == key_state.AddPend then + print('[trust_anchors] key: '..key_tag..' purging') + keep_ta = false + end + print('[trust_anchors] key: '..key_tag..' state: '..ta.state) + return keep_ta +end + +-- TA store management +local trust_anchors = { + keyset = {}, + insecure = {}, + -- Update existing keyset + update = function (new_keys, initial) + if not new_keys then return false end + -- Filter TAs to be purged from the keyset (KeyRem) + local keyset_keep = {} + local keyset = trust_anchors.keyset + for i = 1, #keyset do + local ta = keyset[i] + local keep = true + if not ta_find(new_keys, ta) then + keep = ta_missing(keyset, ta) + end + if keep then + table.insert(keyset_keep, rr) + end + end + keyset = keyset_keep + -- Evaluate new TAs + for i = 1, #new_keys do + local rr = new_keys[i] + if rr.type == kres.type.DNSKEY then + ta_present(keyset, rr, initial) + end + end + -- Publish active TAs + local store = kres.context().trust_anchors + C.kr_ta_clear(store) + for i = 1, #keyset do + local ta = keyset[i] + -- Key MAY be used as a TA only in these two states (RFC5011, 4.2) + if ta.state == key_state.Valid or ta.state == key_state.Missing then + C.kr_ta_add(store, ta.owner, ta.type, ta.ttl, ta.rdata, #ta.rdata) + end + end + trust_anchors.keyset = keyset + return true + end, + -- Load keys from a file (managed) + config = function (path) + local new_keys = require('zonefile').parse_file(path) + trust_anchors.update(new_keys, true) + end, + -- Add DS/DNSKEY record(s) (unmanaged) + add = function (keystr) + local store = kres.context().trust_anchors + require('zonefile').parser(function (p) + local rr = p:current_rr() + C.kr_ta_add(store, rr.owner, rr.type, rr.ttl, rr.rdata, #rr.rdata) + end):read(keystr..'\n') + end, + -- Negative TA management + set_insecure = function (list) + local store = kres.context().negative_anchors + C.kr_ta_clear(store) + for i = 1, #list do + local dname = kres.str2dname(list[i]) + C.kr_ta_add(store, dname, kres.type.DS, 0, nil, 0) + end + trust_anchors.insecure = list + end, +} + +return trust_anchors \ No newline at end of file