mod_s2s_auth_dane/mod_s2s_auth_dane.lua
author Kim Alvefur <zash@zash.se>
Fri, 03 Jan 2014 15:14:26 +0100
changeset 1262 1e84eebf3f46
parent 1261 6a37bd22c8df
child 1265 020165014e56
permissions -rw-r--r--
mod_s2s_auth_dane: Invalidate trust if there are TLSA records but no matches, or bogus results
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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