Annotate

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