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