]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/trust_anchors: finished state table, own module, cleanup
authorMarek Vavruša <marek.vavrusa@nic.cz>
Fri, 25 Sep 2015 14:58:24 +0000 (16:58 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Fri, 25 Sep 2015 14:58:24 +0000 (16:58 +0200)
todo: active refresh

daemon/daemon.mk
daemon/lua/kres.lua
daemon/lua/sandbox.lua
daemon/lua/trust_anchors.lua [new file with mode: 0644]

index b0103e83ae21fd39dcb74a9554d253b44663aed0..526c68a95705468af13c6c2b8866dc07d5bc51df 100644 (file)
@@ -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)
index 4ab85646514f87a4f33fa976fe6f3faa3dc1dade..1b19e134848b41ef1c855fdacde833e6c7a8c2ee 100644 (file)
@@ -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
index 048814133ed10e5f71c32d601865ed1dc3aacb53..b25a9deb44ddc65cf97e3e9faf01b6a09f38ca84 100644 (file)
@@ -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 (file)
index 0000000..7f3c287
--- /dev/null
@@ -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