summaryrefslogtreecommitdiffstats
path: root/modules/detect_time_skew
diff options
context:
space:
mode:
Diffstat (limited to 'modules/detect_time_skew')
-rw-r--r--modules/detect_time_skew/.packaging/test.config4
-rw-r--r--modules/detect_time_skew/README.rst23
-rw-r--r--modules/detect_time_skew/detect_time_skew.lua83
3 files changed, 110 insertions, 0 deletions
diff --git a/modules/detect_time_skew/.packaging/test.config b/modules/detect_time_skew/.packaging/test.config
new file mode 100644
index 0000000..3a37907
--- /dev/null
+++ b/modules/detect_time_skew/.packaging/test.config
@@ -0,0 +1,4 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+modules.load('detect_time_skew')
+assert(detect_time_skew)
+quit()
diff --git a/modules/detect_time_skew/README.rst b/modules/detect_time_skew/README.rst
new file mode 100644
index 0000000..be66bd0
--- /dev/null
+++ b/modules/detect_time_skew/README.rst
@@ -0,0 +1,23 @@
+.. SPDX-License-Identifier: GPL-3.0-or-later
+
+.. _mod-detect_time_skew:
+
+System time skew detector
+=========================
+
+This module compares local system time with inception and expiration time
+bounds in DNSSEC signatures for ``. NS`` records. If the local system time is
+outside of these bounds, it is likely a misconfiguration which will cause
+all DNSSEC validation (and resolution) to fail.
+
+In case of mismatch, a warning message will be logged to help with
+further diagnostics.
+
+.. warning:: Information printed by this module can be forged by a network attacker!
+ System administrator MUST verify values printed by this module and
+ fix local system time using a trusted source.
+
+This module is useful for debugging purposes. It runs only once during resolver
+start does not anything after that. It is enabled by default.
+You may disable the module by appending
+``modules.unload('detect_time_skew')`` to your configuration.
diff --git a/modules/detect_time_skew/detect_time_skew.lua b/modules/detect_time_skew/detect_time_skew.lua
new file mode 100644
index 0000000..6669ce8
--- /dev/null
+++ b/modules/detect_time_skew/detect_time_skew.lua
@@ -0,0 +1,83 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+-- Module interface
+local ffi = require('ffi')
+
+local mod = {}
+local event_id = nil
+
+-- Resolve callback
+-- Check time validity of RRSIGs in priming query
+-- luacheck: no unused args
+local function check_time_callback(pkt, req)
+ if pkt == nil or pkt:rcode() ~= kres.rcode.NOERROR then
+ log_warn(ffi.C.LOG_GRP_DETECTTIMESKEW, "cannot resolve '.' NS")
+ return nil
+ end
+ local seen_rrsigs = 0
+ local valid_rrsigs = 0
+ local section = pkt:rrsets(kres.section.ANSWER)
+ local now = os.time()
+ local time_diff = 0
+ local inception = 0
+ local expiration = 0
+ for i = 1, #section do
+ local rr = section[i]
+ assert(rr.type)
+ if rr.type == kres.type.RRSIG then
+ for k = 0, rr.rrs.count - 1 do
+ seen_rrsigs = seen_rrsigs + 1
+ local rdata = rr:rdata_pt(k)
+ inception = ffi.C.kr_rrsig_sig_inception(rdata)
+ expiration = ffi.C.kr_rrsig_sig_expiration(rdata)
+ if now > expiration then
+ -- positive value = in the future
+ time_diff = now - expiration
+ elseif now < inception then
+ -- negative value = in the past
+ time_diff = now - inception
+ else
+ valid_rrsigs = valid_rrsigs + 1
+ end
+ end
+ end
+ end
+ if seen_rrsigs == 0 then
+ log_info(ffi.C.LOG_GRP_DETECTTIMESKEW, "No RRSIGs received! "..
+ "You really should configure DNSSEC trust anchor for the root.")
+ elseif valid_rrsigs == 0 then
+ log_warn(ffi.C.LOG_GRP_DETECTTIMESKEW, "Local system time %q seems to be at "..
+ "least %u seconds in the %s. DNSSEC signatures for '.' NS "..
+ "are not valid %s. Please check your system clock!",
+ os.date("%c", now),
+ math.abs(time_diff),
+ time_diff > 0 and "future" or "past",
+ time_diff > 0 and "anymore" or "yet")
+ else
+ log_info(ffi.C.LOG_GRP_DETECTTIMESKEW, "Local system time %q is within "..
+ "RRSIG validity interval <%q,%q>.", os.date("%c", now),
+ os.date("%c", inception), os.date("%c", expiration))
+ end
+end
+
+-- Do uncached priming query and check time validity of RRSIGs.
+local function check_time()
+ resolve(".", kres.type.NS, kres.class.IN, {"DNSSEC_WANT", "DNSSEC_CD", "NO_CACHE"},
+ check_time_callback)
+end
+
+function mod.init()
+ if event_id then
+ error("Module is already loaded.")
+ else
+ event_id = event.after(0 , check_time)
+ end
+end
+
+function mod.deinit()
+ if event_id then
+ event.cancel(event_id)
+ event_id = nil
+ end
+end
+
+return mod