annotate mod_s2s_auth_dane/mod_s2s_auth_dane.lua @ 1262:1e84eebf3f46

mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
author Kim Alvefur <zash@zash.se>
date Fri, 03 Jan 2014 15:14:26 +0100
parents 6a37bd22c8df
children 020165014e56
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)
1262
1e84eebf3f46 mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
Kim Alvefur <zash@zash.se>
parents: 1261
diff changeset
34 if answer and ( #answer > 0 or answer.bogus ) then
1258
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
1262
1e84eebf3f46 mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
Kim Alvefur <zash@zash.se>
parents: 1261
diff changeset
51 local use, select, match, tlsa, certdata, match_found
1258
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());
1261
6a37bd22c8df mod_s2s_auth_dane: Warn about unsupported DANE params
Kim Alvefur <zash@zash.se>
parents: 1258
diff changeset
63 else
6a37bd22c8df mod_s2s_auth_dane: Warn about unsupported DANE params
Kim Alvefur <zash@zash.se>
parents: 1258
diff changeset
64 module:log("warn", "DANE selector %d is unsupported", select);
1258
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
65 end
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
66 if match == 1 then
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
67 certdata = hashes.sha256(certdata);
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
68 elseif match == 2 then
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
69 certdata = hashes.sha512(certdata);
1261
6a37bd22c8df mod_s2s_auth_dane: Warn about unsupported DANE params
Kim Alvefur <zash@zash.se>
parents: 1258
diff changeset
70 elseif match ~= 0 then
6a37bd22c8df mod_s2s_auth_dane: Warn about unsupported DANE params
Kim Alvefur <zash@zash.se>
parents: 1258
diff changeset
71 module:log("warn", "DANE match rule %d is unsupported", match);
6a37bd22c8df mod_s2s_auth_dane: Warn about unsupported DANE params
Kim Alvefur <zash@zash.se>
parents: 1258
diff changeset
72 certdata = nil
1258
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
73 end
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
74
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
75 -- Should we check if the cert subject matches?
1261
6a37bd22c8df mod_s2s_auth_dane: Warn about unsupported DANE params
Kim Alvefur <zash@zash.se>
parents: 1258
diff changeset
76 if certdata and certdata == tlsa.data then
1258
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
77 (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
78 session.cert_identity_status = "valid"
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
79 if use == 3 then
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
80 session.cert_chain_status = "valid"
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
81 -- 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
82 end
1262
1e84eebf3f46 mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
Kim Alvefur <zash@zash.se>
parents: 1261
diff changeset
83 match_found = true
1258
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
84 break;
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 else
1261
6a37bd22c8df mod_s2s_auth_dane: Warn about unsupported DANE params
Kim Alvefur <zash@zash.se>
parents: 1258
diff changeset
87 module:log("warn", "DANE %s is unsupported", tlsa:getUsage() or ("usage "..tostring(use)));
1258
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
88 -- 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
89 end
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
90 end
1262
1e84eebf3f46 mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
Kim Alvefur <zash@zash.se>
parents: 1261
diff changeset
91 if not match_found then
1e84eebf3f46 mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
Kim Alvefur <zash@zash.se>
parents: 1261
diff changeset
92 (session.log or module._log)("info", "DANE validation successful");
1e84eebf3f46 mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
Kim Alvefur <zash@zash.se>
parents: 1261
diff changeset
93 session.cert_identity_status = "invalid";
1e84eebf3f46 mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
Kim Alvefur <zash@zash.se>
parents: 1261
diff changeset
94 session.cert_chain_status = "invalid";
1e84eebf3f46 mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
Kim Alvefur <zash@zash.se>
parents: 1261
diff changeset
95 end
1258
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
96 end
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
97 end);
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
98
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
99 function module.unload()
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
100 s2sout.try_connect = _try_connect;
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
101 end
fc82d8eded7d mod_s2s_auth_dane: Experimental DANE implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
102