Software /
code /
prosody-modules
Comparison
mod_s2s_auth_dane/mod_s2s_auth_dane.lua @ 1258:fc82d8eded7d
mod_s2s_auth_dane: Experimental DANE implementation
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Tue, 31 Dec 2013 02:16:19 +0100 |
child | 1261:6a37bd22c8df |
comparison
equal
deleted
inserted
replaced
1257:a02fbed74487 | 1258:fc82d8eded7d |
---|---|
1 -- mod_s2s_auth_dane | |
2 -- | |
3 -- Between the DNS lookup and the chertificate validation, there is a race condition. | |
4 -- Solving that probably requires changes to mod_s2s, like using util.async | |
5 | |
6 | |
7 module:set_global(); | |
8 | |
9 local dns_lookup = require"net.adns".lookup; | |
10 local hashes = require"util.hashes"; | |
11 local base64 = require"util.encodings".base64; | |
12 | |
13 local s2sout = module:depends"s2s".route_to_new_session.s2sout; | |
14 local _try_connect = s2sout.try_connect | |
15 | |
16 local pat = "%-%-%-%-%-BEGIN ([A-Z ]+)%-%-%-%-%-\r?\n".. | |
17 "([0-9A-Za-z=+/\r\n]*)\r?\n%-%-%-%-%-END %1%-%-%-%-%-"; | |
18 local function pem2der(pem) | |
19 local typ, data = pem:match(pat); | |
20 if typ and data then | |
21 return base64.decode(data), typ; | |
22 end | |
23 end | |
24 | |
25 -- TODO Things to test/handle: | |
26 -- Negative or bogus answers | |
27 -- No SRV records | |
28 | |
29 function s2sout.try_connect(host_session, connect_host, connect_port, err) | |
30 local srv_hosts = host_session.srv_hosts; | |
31 local srv_choice = host_session.srv_choice; | |
32 if srv_hosts and srv_hosts.answer.secure and not srv_hosts[srv_choice].dane then | |
33 dns_lookup(function(answer) | |
34 if answer and #answer > 0 then | |
35 srv_hosts[srv_choice].dane = answer; | |
36 for i, tlsa in ipairs(answer) do | |
37 module:log("debug", "TLSA %s", tostring(tlsa)); | |
38 end | |
39 end | |
40 end, ("_%d._tcp.%s"):format(connect_port, connect_host), "TLSA") | |
41 end | |
42 return _try_connect(host_session, connect_host, connect_port, err) | |
43 end | |
44 | |
45 module:hook("s2s-check-certificate", function(event) | |
46 local session, cert = event.session, event.cert; | |
47 local srv_hosts = session.srv_hosts; | |
48 local srv_choice = session.srv_choice; | |
49 local choosen = srv_hosts and srv_hosts[srv_choice]; | |
50 if choosen and choosen.dane then | |
51 local use, select, match, tlsa, certdata | |
52 for i, rr in ipairs(choosen.dane) do | |
53 tlsa = rr.tlsa | |
54 module:log("debug", "TLSA %s", tostring(tlsa)); | |
55 use, select, match, certdata = tlsa.use, tlsa.select, tlsa.match; | |
56 | |
57 if use == 1 or use == 3 then | |
58 | |
59 if select == 0 then | |
60 certdata = pem2der(cert:pem()); | |
61 elseif select == 1 then | |
62 certdata = pem2der(cert:pubkey()); | |
63 end | |
64 if match == 1 then | |
65 certdata = hashes.sha256(certdata); | |
66 elseif match == 2 then | |
67 certdata = hashes.sha512(certdata); | |
68 end | |
69 | |
70 -- Should we check if the cert subject matches? | |
71 if certdata == tlsa.data then | |
72 (session.log or module._log)("info", "DANE validation successful"); | |
73 session.cert_identity_status = "valid" | |
74 if use == 3 then | |
75 session.cert_chain_status = "valid" | |
76 -- for usage 1 the chain has to be valid already | |
77 end | |
78 break; | |
79 end | |
80 else | |
81 module:log("warn", "DANE %s is unsupported", tlsa:getUsage()); | |
82 -- TODO Ca checks needs to loop over the chain and stuff | |
83 end | |
84 end | |
85 end | |
86 | |
87 -- TODO Optionally, if no TLSA record matches, mark connection as untrusted. | |
88 end); | |
89 | |
90 function module.unload() | |
91 s2sout.try_connect = _try_connect; | |
92 end | |
93 |