Software /
code /
prosody
Comparison
plugins/mod_s2s_auth_dane_in.lua @ 13297:7264c4d16072
mod_s2s_auth_dane_in: DANE support for s2sin
Complements the DANE support for outgoing connections included in
net.connect
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Wed, 01 Nov 2023 22:49:56 +0100 |
child | 13322:28211ed70b4c |
comparison
equal
deleted
inserted
replaced
13296:803a4ef6244d | 13297:7264c4d16072 |
---|---|
1 module:set_global(); | |
2 | |
3 local dns = require "prosody.net.adns"; | |
4 local async = require "prosody.util.async"; | |
5 local encodings = require "prosody.util.encodings"; | |
6 local hashes = require "prosody.util.hashes"; | |
7 local promise = require "prosody.util.promise"; | |
8 local x509 = require "prosody.util.x509"; | |
9 | |
10 local idna_to_ascii = encodings.idna.to_ascii; | |
11 local sha256 = hashes.sha256; | |
12 local sha512 = hashes.sha512; | |
13 | |
14 local use_dane = module:get_option_boolean("use_dane", nil); | |
15 if use_dane == nil then | |
16 module:log("warn", "DANE support incomplete, add use_dane = true in the global section to support outgoing s2s connections"); | |
17 elseif use_dane == false then | |
18 module:log("debug", "DANE support disabled with use_dane = false, disabling.") | |
19 return | |
20 end | |
21 | |
22 local function ensure_secure(r) | |
23 assert(r.secure, "insecure"); | |
24 return r; | |
25 end | |
26 | |
27 local lazy_tlsa_mt = { | |
28 __index = function(t, i) | |
29 if i == 1 then | |
30 local h = sha256(t[0]); | |
31 t[1] = h; | |
32 return h; | |
33 elseif i == 2 then | |
34 local h = sha512(t[0]); | |
35 t[1] = h; | |
36 return h; | |
37 end | |
38 end; | |
39 } | |
40 local function lazy_hash(t) | |
41 return setmetatable(t, lazy_tlsa_mt); | |
42 end | |
43 | |
44 module:hook("s2s-check-certificate", function(event) | |
45 local session, host, cert = event.session, event.host, event.cert; | |
46 local log = session.log or module._log; | |
47 | |
48 if not host or not cert or session.direction ~= "incoming" then | |
49 return | |
50 end | |
51 | |
52 local by_select_match = { | |
53 [0] = lazy_hash { | |
54 -- cert | |
55 [0] = x509.pem2der(cert:pem()); | |
56 | |
57 }; | |
58 } | |
59 if cert.pubkey then | |
60 by_select_match[1] = lazy_hash { | |
61 -- spki | |
62 [0] = x509.pem2der(cert:pubkey()); | |
63 }; | |
64 end | |
65 | |
66 local resolver = dns.resolver(); | |
67 | |
68 local dns_domain = idna_to_ascii(host); | |
69 | |
70 local function fetch_tlsa(res) | |
71 local tlsas = {}; | |
72 for _, rr in ipairs(res) do | |
73 table.insert(tlsas, resolver:lookup_promise(("_%d._tcp.%s"):format(rr.srv.port, rr.srv.target), "TLSA"):next(ensure_secure)); | |
74 end | |
75 return promise.all(tlsas); | |
76 end | |
77 | |
78 local ret = async.wait_for(promise.all({ | |
79 resolver:lookup_promise("_xmpps-server._tcp." .. dns_domain, "SRV"):next(ensure_secure):next(fetch_tlsa); | |
80 resolver:lookup_promise("_xmpp-server._tcp." .. dns_domain, "SRV"):next(ensure_secure):next(fetch_tlsa); | |
81 })); | |
82 | |
83 if not ret then | |
84 return | |
85 end | |
86 | |
87 local found_supported = false; | |
88 for _, by_proto in ipairs(ret) do | |
89 for _, by_srv in ipairs(by_proto) do | |
90 for _, by_target in ipairs(by_srv) do | |
91 for _, rr in ipairs(by_target) do | |
92 if rr.tlsa.use == 3 and by_select_match[rr.tlsa.select] and rr.tlsa.match <= 2 then | |
93 found_supported = true; | |
94 if rr.tlsa.data == by_select_match[rr.tlsa.select][rr.tlsa.match] then | |
95 module:log("debug", "%s matches", rr) | |
96 session.cert_chain_status = "valid"; | |
97 session.cert_identity_status = "valid"; | |
98 return true; | |
99 end | |
100 else | |
101 log("debug", "Unsupported DANE TLSA record: %s", rr); | |
102 end | |
103 end | |
104 end | |
105 end | |
106 end | |
107 | |
108 if found_supported then | |
109 session.cert_chain_status = "invalid"; | |
110 session.cert_identity_status = nil; | |
111 return true; | |
112 end | |
113 | |
114 end, 800); |