comparison plugins/mod_s2s/mod_s2s.lua @ 6054:7a5ddbaf758d

Merge 0.9->0.10
author Matthew Wild <mwild1@gmail.com>
date Wed, 02 Apr 2014 17:41:38 +0100
parents d21ea6001bba
children e626ee2fe106
comparison
equal deleted inserted replaced
6053:2f93a04564b2 6054:7a5ddbaf758d
1 -- Prosody IM 1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild 2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain 3 -- Copyright (C) 2008-2010 Waqas Hussain
4 -- 4 --
5 -- This project is MIT/X11 licensed. Please see the 5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information. 6 -- COPYING file in the source package for more information.
7 -- 7 --
8 8
9 module:set_global(); 9 module:set_global();
133 return false; 133 return false;
134 end 134 end
135 return true; 135 return true;
136 end 136 end
137 137
138 local function keepalive(event)
139 return event.session.sends2s(' ');
140 end
141
142 module:hook("s2s-read-timeout", keepalive, -1);
143
138 function module.add_host(module) 144 function module.add_host(module)
139 if module:get_option_boolean("disallow_s2s", false) then 145 if module:get_option_boolean("disallow_s2s", false) then
140 module:log("warn", "The 'disallow_s2s' config option is deprecated, please see http://prosody.im/doc/s2s#disabling"); 146 module:log("warn", "The 'disallow_s2s' config option is deprecated, please see http://prosody.im/doc/s2s#disabling");
141 return nil, "This host has disallow_s2s set"; 147 return nil, "This host has disallow_s2s set";
142 end 148 end
143 module:hook("route/remote", route_to_existing_session, -1); 149 module:hook("route/remote", route_to_existing_session, -1);
144 module:hook("route/remote", route_to_new_session, -10); 150 module:hook("route/remote", route_to_new_session, -10);
145 module:hook("s2s-authenticated", make_authenticated, -1); 151 module:hook("s2s-authenticated", make_authenticated, -1);
152 module:hook("s2s-read-timeout", keepalive, -1);
146 end 153 end
147 154
148 -- Stream is authorised, and ready for normal stanzas 155 -- Stream is authorised, and ready for normal stanzas
149 function mark_connected(session) 156 function mark_connected(session)
150 local sendq, send = session.sendq, session.sends2s; 157 local sendq, send = session.sendq, session.sends2s;
151 158
152 local from, to = session.from_host, session.to_host; 159 local from, to = session.from_host, session.to_host;
153 160
154 session.log("info", "%s s2s connection %s->%s complete", session.direction, from, to); 161 session.log("info", "%s s2s connection %s->%s complete", session.direction:gsub("^.", string.upper), from, to);
155 162
156 local event_data = { session = session }; 163 local event_data = { session = session };
157 if session.type == "s2sout" then 164 if session.type == "s2sout" then
158 fire_global_event("s2sout-established", event_data); 165 fire_global_event("s2sout-established", event_data);
159 hosts[from].events.fire_event("s2sout-established", event_data); 166 hosts[from].events.fire_event("s2sout-established", event_data);
164 end; 171 end;
165 172
166 fire_global_event("s2sin-established", event_data); 173 fire_global_event("s2sin-established", event_data);
167 hosts[to].events.fire_event("s2sin-established", event_data); 174 hosts[to].events.fire_event("s2sin-established", event_data);
168 end 175 end
169 176
170 if session.direction == "outgoing" then 177 if session.direction == "outgoing" then
171 if sendq then 178 if sendq then
172 session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host); 179 session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host);
173 for i, data in ipairs(sendq) do 180 for i, data in ipairs(sendq) do
174 send(data[1]); 181 send(data[1]);
175 sendq[i] = nil; 182 sendq[i] = nil;
176 end 183 end
177 session.sendq = nil; 184 session.sendq = nil;
178 end 185 end
179 186
180 session.ip_hosts = nil; 187 session.ip_hosts = nil;
181 session.srv_hosts = nil; 188 session.srv_hosts = nil;
182 end 189 end
183 end 190 end
184 191
209 session.hosts[host].authed = true; 216 session.hosts[host].authed = true;
210 else 217 else
211 return false; 218 return false;
212 end 219 end
213 session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host); 220 session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host);
214 221
215 mark_connected(session); 222 mark_connected(session);
216 223
217 return true; 224 return true;
218 end 225 end
219 226
220 --- Helper to check that a session peer's certificate is valid 227 --- Helper to check that a session peer's certificate is valid
221 local function check_cert_status(session) 228 local function check_cert_status(session)
268 275
269 local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; 276 local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
270 277
271 function stream_callbacks.streamopened(session, attr) 278 function stream_callbacks.streamopened(session, attr)
272 local send = session.sends2s; 279 local send = session.sends2s;
273 280
274 session.version = tonumber(attr.version) or 0; 281 session.version = tonumber(attr.version) or 0;
275 282
276 -- TODO: Rename session.secure to session.encrypted 283 -- TODO: Rename session.secure to session.encrypted
277 if session.secure == false then 284 if session.secure == false then
278 session.secure = true; 285 session.secure = true;
279 286 session.encrypted = true;
280 -- Check if TLS compression is used 287
281 local sock = session.conn:socket(); 288 local sock = session.conn:socket();
282 if sock.info then 289 if sock.info then
283 session.compressed = sock:info"compression"; 290 local info = sock:info();
284 elseif sock.compression then 291 (session.log or log)("info", "Stream encrypted (%s with %s)", info.protocol, info.cipher);
285 session.compressed = sock:compression(); --COMPAT mw/luasec-hg 292 session.compressed = info.compression;
293 else
294 (session.log or log)("info", "Stream encrypted");
295 session.compressed = sock.compression and sock:compression(); --COMPAT mw/luasec-hg
286 end 296 end
287 end 297 end
288 298
289 if session.direction == "incoming" then 299 if session.direction == "incoming" then
290 -- Send a reply stream header 300 -- Send a reply stream header
291 301
292 -- Validate to/from 302 -- Validate to/from
293 local to, from = nameprep(attr.to), nameprep(attr.from); 303 local to, from = nameprep(attr.to), nameprep(attr.from);
294 if not to and attr.to then -- COMPAT: Some servers do not reliably set 'to' (especially on stream restarts) 304 if not to and attr.to then -- COMPAT: Some servers do not reliably set 'to' (especially on stream restarts)
295 session:close({ condition = "improper-addressing", text = "Invalid 'to' address" }); 305 session:close({ condition = "improper-addressing", text = "Invalid 'to' address" });
296 return; 306 return;
297 end 307 end
298 if not from and attr.from then -- COMPAT: Some servers do not reliably set 'from' (especially on stream restarts) 308 if not from and attr.from then -- COMPAT: Some servers do not reliably set 'from' (especially on stream restarts)
299 session:close({ condition = "improper-addressing", text = "Invalid 'from' address" }); 309 session:close({ condition = "improper-addressing", text = "Invalid 'from' address" });
300 return; 310 return;
301 end 311 end
302 312
303 -- Set session.[from/to]_host if they have not been set already and if 313 -- Set session.[from/to]_host if they have not been set already and if
304 -- this session isn't already authenticated 314 -- this session isn't already authenticated
305 if session.type == "s2sin_unauthed" and from and not session.from_host then 315 if session.type == "s2sin_unauthed" and from and not session.from_host then
306 session.from_host = from; 316 session.from_host = from;
307 elseif from ~= session.from_host then 317 elseif from ~= session.from_host then
312 session.to_host = to; 322 session.to_host = to;
313 elseif to ~= session.to_host then 323 elseif to ~= session.to_host then
314 session:close({ condition = "improper-addressing", text = "New stream 'to' attribute does not match original" }); 324 session:close({ condition = "improper-addressing", text = "New stream 'to' attribute does not match original" });
315 return; 325 return;
316 end 326 end
317 327
318 -- For convenience we'll put the sanitised values into these variables 328 -- For convenience we'll put the sanitised values into these variables
319 to, from = session.to_host, session.from_host; 329 to, from = session.to_host, session.from_host;
320 330
321 session.streamid = uuid_gen(); 331 session.streamid = uuid_gen();
322 (session.log or log)("debug", "Incoming s2s received %s", st.stanza("stream:stream", attr):top_tag()); 332 (session.log or log)("debug", "Incoming s2s received %s", st.stanza("stream:stream", attr):top_tag());
323 if to then 333 if to then
324 if not hosts[to] then 334 if not hosts[to] then
325 -- Attempting to connect to a host we don't serve 335 -- Attempting to connect to a host we don't serve
350 end 360 end
351 361
352 session:open_stream(session.to_host, session.from_host) 362 session:open_stream(session.to_host, session.from_host)
353 if session.version >= 1.0 then 363 if session.version >= 1.0 then
354 local features = st.stanza("stream:features"); 364 local features = st.stanza("stream:features");
355 365
356 if to then 366 if to then
357 hosts[to].events.fire_event("s2s-stream-features", { origin = session, features = features }); 367 hosts[to].events.fire_event("s2s-stream-features", { origin = session, features = features });
358 else 368 else
359 (session.log or log)("warn", "No 'to' on stream header from %s means we can't offer any features", from or session.ip or "unknown host"); 369 (session.log or log)("warn", "No 'to' on stream header from %s means we can't offer any features", from or session.ip or "unknown host");
360 end 370 end
361 371
362 log("debug", "Sending stream features: %s", tostring(features)); 372 log("debug", "Sending stream features: %s", tostring(features));
363 send(features); 373 send(features);
364 end 374 end
365 elseif session.direction == "outgoing" then 375 elseif session.direction == "outgoing" then
366 -- If we are just using the connection for verifying dialback keys, we won't try and auth it 376 -- If we are just using the connection for verifying dialback keys, we won't try and auth it
384 session.sends2s(tostring(data)); 394 session.sends2s(tostring(data));
385 send_buffer[i] = nil; 395 send_buffer[i] = nil;
386 end 396 end
387 end 397 end
388 session.send_buffer = nil; 398 session.send_buffer = nil;
389 399
390 -- If server is pre-1.0, don't wait for features, just do dialback 400 -- If server is pre-1.0, don't wait for features, just do dialback
391 if session.version < 1.0 then 401 if session.version < 1.0 then
392 if not session.dialback_verifying then 402 if not session.dialback_verifying then
393 hosts[session.from_host].events.fire_event("s2sout-authenticate-legacy", { origin = session }); 403 hosts[session.from_host].events.fire_event("s2sout-authenticate-legacy", { origin = session });
394 else 404 else
477 end 487 end
478 end 488 end
479 489
480 session.sends2s("</stream:stream>"); 490 session.sends2s("</stream:stream>");
481 function session.sends2s() return false; end 491 function session.sends2s() return false; end
482 492
483 local reason = remote_reason or (reason and (reason.text or reason.condition)) or reason; 493 local reason = remote_reason or (reason and (reason.text or reason.condition)) or reason;
484 session.log("info", "%s s2s stream %s->%s closed: %s", session.direction, session.from_host or "(unknown host)", session.to_host or "(unknown host)", reason or "stream closed"); 494 session.log("info", "%s s2s stream %s->%s closed: %s", session.direction:gsub("^.", string.upper), session.from_host or "(unknown host)", session.to_host or "(unknown host)", reason or "stream closed");
485 495
486 -- Authenticated incoming stream may still be sending us stanzas, so wait for </stream:stream> from remote 496 -- Authenticated incoming stream may still be sending us stanzas, so wait for </stream:stream> from remote
487 local conn = session.conn; 497 local conn = session.conn;
488 if reason == nil and not session.notopen and session.type == "s2sin" then 498 if reason == nil and not session.notopen and session.type == "s2sin" then
489 add_task(stream_close_timeout, function () 499 add_task(stream_close_timeout, function ()
490 if not session.destroyed then 500 if not session.destroyed then
520 530
521 -- Session initialization logic shared by incoming and outgoing 531 -- Session initialization logic shared by incoming and outgoing
522 local function initialize_session(session) 532 local function initialize_session(session)
523 local stream = new_xmpp_stream(session, stream_callbacks); 533 local stream = new_xmpp_stream(session, stream_callbacks);
524 session.stream = stream; 534 session.stream = stream;
525 535
526 session.notopen = true; 536 session.notopen = true;
527 537
528 function session.reset_stream() 538 function session.reset_stream()
529 session.notopen = true; 539 session.notopen = true;
530 session.stream:reset(); 540 session.stream:reset();
531 end 541 end
532 542
533 session.open_stream = session_open_stream; 543 session.open_stream = session_open_stream;
534 544
535 local filter = session.filter; 545 local filter = session.filter;
536 function session.data(data) 546 function session.data(data)
537 data = filter("bytes/in", data); 547 data = filter("bytes/in", data);
538 if data then 548 if data then
539 local ok, err = stream:feed(data); 549 local ok, err = stream:feed(data);
584 if t then 594 if t then
585 return w(conn, t); 595 return w(conn, t);
586 end 596 end
587 end 597 end
588 end 598 end
589 599
590 initialize_session(session); 600 initialize_session(session);
591 else -- Outgoing session connected 601 else -- Outgoing session connected
592 session:open_stream(session.from_host, session.to_host); 602 session:open_stream(session.from_host, session.to_host);
593 end 603 end
604 session.ip = conn:ip();
594 end 605 end
595 606
596 function listener.onincoming(conn, data) 607 function listener.onincoming(conn, data)
597 local session = sessions[conn]; 608 local session = sessions[conn];
598 if session then 609 if session then
599 session.data(data); 610 session.data(data);
600 end 611 end
601 end 612 end
602 613
603 function listener.onstatus(conn, status) 614 function listener.onstatus(conn, status)
604 if status == "ssl-handshake-complete" then 615 if status == "ssl-handshake-complete" then
605 local session = sessions[conn]; 616 local session = sessions[conn];
606 if session and session.direction == "outgoing" then 617 if session and session.direction == "outgoing" then
607 session.log("debug", "Sending stream header..."); 618 session.log("debug", "Sending stream header...");
615 if session then 626 if session then
616 sessions[conn] = nil; 627 sessions[conn] = nil;
617 if err and session.direction == "outgoing" and session.notopen then 628 if err and session.direction == "outgoing" and session.notopen then
618 (session.log or log)("debug", "s2s connection attempt failed: %s", err); 629 (session.log or log)("debug", "s2s connection attempt failed: %s", err);
619 if s2sout.attempt_connection(session, err) then 630 if s2sout.attempt_connection(session, err) then
620 (session.log or log)("debug", "...so we're going to try another target");
621 return; -- Session lives for now 631 return; -- Session lives for now
622 end 632 end
623 end 633 end
624 (session.log or log)("debug", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "connection closed")); 634 (session.log or log)("debug", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "connection closed"));
625 s2s_destroy_session(session, err); 635 s2s_destroy_session(session, err);
636 end
637 end
638
639 function listener.onreadtimeout(conn)
640 local session = sessions[conn];
641 if session then
642 return (hosts[session.host] or prosody).events.fire_event("s2s-read-timeout", { session = session });
626 end 643 end
627 end 644 end
628 645
629 function listener.register_outgoing(conn, session) 646 function listener.register_outgoing(conn, session)
630 session.direction = "outgoing"; 647 session.direction = "outgoing";
639 if not must_secure and secure_domains[host] then 656 if not must_secure and secure_domains[host] then
640 must_secure = true; 657 must_secure = true;
641 elseif must_secure and insecure_domains[host] then 658 elseif must_secure and insecure_domains[host] then
642 must_secure = false; 659 must_secure = false;
643 end 660 end
644 661
645 if must_secure and (session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid") then 662 if must_secure and (session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid") then
646 module:log("warn", "Forbidding insecure connection to/from %s", host or session.ip or "(unknown host)"); 663 module:log("warn", "Forbidding insecure connection to/from %s", host or session.ip or "(unknown host)");
647 if session.direction == "incoming" then 664 if session.direction == "incoming" then
648 session:close({ condition = "not-authorized", text = "Your server's certificate is invalid, expired, or not trusted by "..session.to_host }); 665 session:close({ condition = "not-authorized", text = "Your server's certificate is invalid, expired, or not trusted by "..session.to_host });
649 else -- Close outgoing connections without warning 666 else -- Close outgoing connections without warning