58 end |
59 end |
59 end |
60 end |
60 local configured_uses = module:get_option_set("dane_uses", { "DANE-EE", "DANE-TA" }); |
61 local configured_uses = module:get_option_set("dane_uses", { "DANE-EE", "DANE-TA" }); |
61 local enabled_uses = set.intersection(implemented_uses, configured_uses) / function(use) return use_map[use] end; |
62 local enabled_uses = set.intersection(implemented_uses, configured_uses) / function(use) return use_map[use] end; |
62 |
63 |
63 local function dane_lookup(host_session, cb, a,b,c,e) |
64 -- Find applicable TLSA records |
64 if host_session.dane ~= nil then return end |
65 -- Takes a s2sin/out and a callback |
|
66 local function dane_lookup(host_session, cb) |
|
67 cb = cb or noop; |
|
68 if host_session.dane ~= nil then return end -- Has already done a lookup |
|
69 |
65 if host_session.direction == "incoming" then |
70 if host_session.direction == "incoming" then |
|
71 -- We don't know what hostname or port to use for Incoming connections |
|
72 -- so we do a SRV lookup and then request TLSA records for each SRV |
|
73 -- Most servers will probably use the same certificate on outgoing |
|
74 -- and incoming connections, so this should work well |
66 local name = host_session.from_host and idna_to_ascii(host_session.from_host); |
75 local name = host_session.from_host and idna_to_ascii(host_session.from_host); |
67 if not name then return end |
76 if not name then |
68 host_session.dane = dns_lookup(function (answer) |
77 module:log("error", "Could not convert '%s' to ASCII for DNS lookup", tostring(host_session.from_host)); |
69 host_session.dane = false; |
78 return; |
|
79 end |
|
80 host_session.dane = dns_lookup(function (answer, err) |
|
81 host_session.dane = false; -- Mark that we already did the lookup |
|
82 |
|
83 if not answer then |
|
84 module:log("debug", "Resolver error: %s", tostring(err)); |
|
85 return cb(host_session); |
|
86 end |
|
87 |
70 if not answer.secure then |
88 if not answer.secure then |
71 if cb then return cb(a,b,c,e); end |
89 module:log("debug", "Results are not secure"); |
72 return; |
90 return cb(host_session); |
73 end |
91 end |
|
92 |
74 local n = #answer |
93 local n = #answer |
75 if n == 0 then if cb then return cb(a,b,c,e); end return end |
94 if n == 0 then |
76 if n == 1 and answer[1].srv.target == '.' then return end |
95 -- No SRV records, we could proceed with the domainname and |
|
96 -- default port but that will currently not work properly since |
|
97 -- mod_s2s doesn't keep the answer around for that |
|
98 return cb(host_session); |
|
99 end |
|
100 if n == 1 and answer[1].srv.target == '.' then |
|
101 return cb(host_session); -- No service ... This shouldn't happen? |
|
102 end |
77 local srv_hosts = { answer = answer }; |
103 local srv_hosts = { answer = answer }; |
78 local dane = {}; |
104 local dane = {}; |
79 host_session.dane = dane; |
105 host_session.dane = dane; |
80 host_session.srv_hosts = srv_hosts; |
106 host_session.srv_hosts = srv_hosts; |
81 for _, record in ipairs(answer) do |
107 for _, record in ipairs(answer) do |
82 t_insert(srv_hosts, record.srv); |
108 t_insert(srv_hosts, record.srv); |
83 dns_lookup(function(dane_answer) |
109 dns_lookup(function(dane_answer) |
84 n = n - 1; |
110 n = n - 1; |
85 if dane_answer.bogus then |
111 if dane_answer.bogus then |
86 -- How to handle this? |
112 dane.bogus = dane_answer.bogus; |
87 elseif dane_answer.secure then |
113 elseif dane_answer.secure then |
88 for _, record in ipairs(dane_answer) do |
114 for _, record in ipairs(dane_answer) do |
89 t_insert(dane, record); |
115 t_insert(dane, record); |
90 end |
116 end |
91 end |
117 end |
92 if n == 0 and cb then return cb(a,b,c,e); end |
118 if n == 0 then |
|
119 if #dane > 0 and dane.bogus then |
|
120 -- Got at least one non-bogus reply, |
|
121 -- This should trigger a failure if one of them did not match |
|
122 host_session.log("warn", "Ignoring bogus replies"); |
|
123 dane.bogus = nil; |
|
124 end |
|
125 if #dane == 0 and dane.bogus == nil then |
|
126 -- Got no usable data |
|
127 host_session.dane = false; |
|
128 end |
|
129 return cb(host_session); |
|
130 end |
93 end, ("_%d._tcp.%s."):format(record.srv.port, record.srv.target), "TLSA"); |
131 end, ("_%d._tcp.%s."):format(record.srv.port, record.srv.target), "TLSA"); |
94 end |
132 end |
95 end, "_xmpp-server._tcp."..name..".", "SRV"); |
133 end, "_xmpp-server._tcp."..name..".", "SRV"); |
96 return true; |
134 return true; |
97 elseif host_session.direction == "outgoing" then |
135 elseif host_session.direction == "outgoing" then |
|
136 -- Prosody has already done SRV lookups for outgoing session, so check if those are secure |
98 local srv_hosts = host_session.srv_hosts; |
137 local srv_hosts = host_session.srv_hosts; |
99 if not ( srv_hosts and srv_hosts.answer and srv_hosts.answer.secure ) then return end |
138 if not ( srv_hosts and srv_hosts.answer and srv_hosts.answer.secure ) then |
|
139 return; -- No secure SRV records, fall back to non-DANE mode |
|
140 end |
|
141 -- Do TLSA lookup for currently selected SRV record |
100 local srv_choice = srv_hosts[host_session.srv_choice]; |
142 local srv_choice = srv_hosts[host_session.srv_choice]; |
101 host_session.dane = dns_lookup(function(answer) |
143 host_session.dane = dns_lookup(function(answer) |
102 if answer and ((answer.secure and #answer > 0) or answer.bogus) then |
144 if answer and ((answer.secure and #answer > 0) or answer.bogus) then |
103 srv_choice.dane = answer; |
145 srv_choice.dane = answer; |
104 else |
146 else |
105 srv_choice.dane = false; |
147 srv_choice.dane = false; |
106 end |
148 end |
107 host_session.dane = srv_choice.dane; |
149 host_session.dane = srv_choice.dane; |
108 if cb then return cb(a,b,c,e); end |
150 return cb(host_session); |
109 end, ("_%d._tcp.%s."):format(srv_choice.port, srv_choice.target), "TLSA"); |
151 end, ("_%d._tcp.%s."):format(srv_choice.port, srv_choice.target), "TLSA"); |
110 return true; |
152 return true; |
111 end |
153 end |
|
154 end |
|
155 |
|
156 local function resume(host_session) |
|
157 host_session.log("debug", "DANE lookup completed, resuming connection"); |
|
158 host_session.conn:resume() |
112 end |
159 end |
113 |
160 |
114 function module.add_host(module) |
161 function module.add_host(module) |
115 local function on_new_s2s(event) |
162 local function on_new_s2s(event) |
116 local host_session = event.origin; |
163 local host_session = event.origin; |
117 if host_session.type == "s2sout" or host_session.type == "s2sin" or host_session.dane ~= nil then return end -- Already authenticated |
164 if host_session.type == "s2sout" or host_session.type == "s2sin" then |
118 local function resume() |
165 return; -- Already authenticated |
119 host_session.log("debug", "DANE lookup completed, resuming connection"); |
166 end |
120 host_session.conn:resume() |
167 if host_session.dane ~= nil then |
|
168 return; -- Already done DANE lookup |
121 end |
169 end |
122 if dane_lookup(host_session, resume) then |
170 if dane_lookup(host_session, resume) then |
123 host_session.log("debug", "Pausing connection until DANE lookup is completed"); |
171 host_session.log("debug", "Pausing connection until DANE lookup is completed"); |
124 host_session.conn:pause() |
172 host_session.conn:pause() |
125 end |
173 end |