-- 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