Mercurial > 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 |
| parents | |
| children | 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 |
