comparison plugins/mod_s2s.lua @ 14122:8a4417d32b0f 13.0

mod_c2s,mod_s2s: Introduce separate pre-authentication stanza size limit This should prevent unauthenticated resource use via the XML parser.
author Kim Alvefur <zash@zash.se>
date Tue, 07 Apr 2026 20:35:41 +0200
parents a1fb05cbd44e
children 791426dda30d
comparison
equal deleted inserted replaced
14120:0e9e3efa381f 14122:8a4417d32b0f
39 local opt_keepalives = module:get_option_boolean("s2s_tcp_keepalives", module:get_option_boolean("tcp_keepalives", true)); 39 local opt_keepalives = module:get_option_boolean("s2s_tcp_keepalives", module:get_option_boolean("tcp_keepalives", true));
40 local secure_auth = module:get_option_boolean("s2s_secure_auth", false); -- One day... 40 local secure_auth = module:get_option_boolean("s2s_secure_auth", false); -- One day...
41 local secure_domains, insecure_domains = 41 local secure_domains, insecure_domains =
42 module:get_option_set("s2s_secure_domains", {})._items, module:get_option_set("s2s_insecure_domains", {})._items; 42 module:get_option_set("s2s_secure_domains", {})._items, module:get_option_set("s2s_insecure_domains", {})._items;
43 local require_encryption = module:get_option_boolean("s2s_require_encryption", true); 43 local require_encryption = module:get_option_boolean("s2s_require_encryption", true);
44 local stanza_size_limit = module:get_option_integer("s2s_stanza_size_limit", 1024*512, 10000); 44 local unauthed_stanza_size_limit = module:get_option_integer("s2s_unauthed_stanza_size_limit", 10000, 1000);
45 local authed_stanza_size_limit = module:get_option_integer("s2s_stanza_size_limit", 1024*512, 10000);
45 local sendq_size = module:get_option_integer("s2s_send_queue_size", 1024*32, 1); 46 local sendq_size = module:get_option_integer("s2s_send_queue_size", 1024*32, 1);
46 47
47 local advertised_idle_timeout = 14*60; -- default in all net.server implementations 48 local advertised_idle_timeout = 14*60; -- default in all net.server implementations
48 local network_settings = module:get_option("network_settings"); 49 local network_settings = module:get_option("network_settings");
49 if type(network_settings) == "table" and type(network_settings.read_timeout) == "number" then 50 if type(network_settings) == "table" and type(network_settings.read_timeout) == "number" then
224 local from_host, to_host, stanza = event.from_host, event.to_host, event.stanza; 225 local from_host, to_host, stanza = event.from_host, event.to_host, event.stanza;
225 log("debug", "opening a new outgoing connection for this stanza"); 226 log("debug", "opening a new outgoing connection for this stanza");
226 local host_session = s2s_new_outgoing(from_host, to_host); 227 local host_session = s2s_new_outgoing(from_host, to_host);
227 host_session.version = 1; 228 host_session.version = 1;
228 229
230 host_session.stanza_size_limit = unauthed_stanza_size_limit;
231
229 -- Store in buffer 232 -- Store in buffer
230 host_session.bounce_sendq = bounce_sendq; 233 host_session.bounce_sendq = bounce_sendq;
231 host_session.sendq = queue.new(sendq_size); 234 host_session.sendq = queue.new(sendq_size);
232 host_session.sendq:push(st.clone(stanza)); 235 host_session.sendq:push(st.clone(stanza));
233 log("debug", "stanza [%s] queued until connection complete", stanza.name); 236 log("debug", "stanza [%s] queued until connection complete", stanza.name);
268 return nil, "This host has disallow_s2s set"; 271 return nil, "This host has disallow_s2s set";
269 end 272 end
270 module:hook("route/remote", route_to_existing_session, -1); 273 module:hook("route/remote", route_to_existing_session, -1);
271 module:hook("route/remote", route_to_new_session, -10); 274 module:hook("route/remote", route_to_new_session, -10);
272 module:hook("s2sout-stream-features", function (event) 275 module:hook("s2sout-stream-features", function (event)
273 if not (stanza_size_limit or advertised_idle_timeout) then return end 276 local session = event.origin;
277 if not (session.stanza_size_limit or advertised_idle_timeout) then return end
274 local limits = event.features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" }) 278 local limits = event.features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" })
275 if stanza_size_limit then 279 if session.stanza_size_limit then
276 limits:text_tag("max-bytes", string.format("%d", stanza_size_limit)); 280 limits:text_tag("max-bytes", string.format("%d", session.stanza_size_limit));
277 end 281 end
278 if advertised_idle_timeout then 282 if advertised_idle_timeout then
279 limits:text_tag("idle-seconds", string.format("%d", advertised_idle_timeout)); 283 limits:text_tag("idle-seconds", string.format("%d", advertised_idle_timeout));
280 end 284 end
281 limits:up(); 285 limits:up();
413 session.type = "s2sin"; 417 session.type = "s2sin";
414 elseif session.type ~= "s2sin" and session.type ~= "s2sout" then 418 elseif session.type ~= "s2sin" and session.type ~= "s2sout" then
415 return false; 419 return false;
416 end 420 end
417 421
422 session.stanza_size_limit = authed_stanza_size_limit;
423 session.stream:set_stanza_size_limit(session.stanza_size_limit);
424
418 if session.incoming and host then 425 if session.incoming and host then
419 if not session.hosts[host] then session.hosts[host] = {}; end 426 if not session.hosts[host] then session.hosts[host] = {}; end
420 session.hosts[host].authed = true; 427 session.hosts[host].authed = true;
421 end 428 end
422 session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host); 429 session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host);
571 (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"); 578 (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");
572 module:fire_event("s2s-stream-features-legacy", { origin = session, features = features }); 579 module:fire_event("s2s-stream-features-legacy", { origin = session, features = features });
573 end 580 end
574 581
575 if ( session.type == "s2sin" or session.type == "s2sout" ) or features.tags[1] then 582 if ( session.type == "s2sin" or session.type == "s2sout" ) or features.tags[1] then
576 if stanza_size_limit or advertised_idle_timeout then 583 if session.stanza_size_limit or advertised_idle_timeout then
577 features:reset(); 584 features:reset();
578 local limits = features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" }); 585 local limits = features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" });
579 if stanza_size_limit then 586 if session.stanza_size_limit then
580 limits:text_tag("max-bytes", string.format("%d", stanza_size_limit)); 587 limits:text_tag("max-bytes", string.format("%d", session.stanza_size_limit));
581 end 588 end
582 if advertised_idle_timeout then 589 if advertised_idle_timeout then
583 limits:text_tag("idle-seconds", string.format("%d", advertised_idle_timeout)); 590 limits:text_tag("idle-seconds", string.format("%d", advertised_idle_timeout));
584 end 591 end
585 features:reset(); 592 features:reset();
792 end 799 end
793 end 800 end
794 801
795 -- Session initialization logic shared by incoming and outgoing 802 -- Session initialization logic shared by incoming and outgoing
796 local function initialize_session(session) 803 local function initialize_session(session)
797 local stream = new_xmpp_stream(session, stream_callbacks, stanza_size_limit); 804 local stream = new_xmpp_stream(session, stream_callbacks, session.stanza_size_limit or unauthed_stanza_size_limit);
798 805
799 session.thread = runner(function (item) 806 session.thread = runner(function (item)
800 if st.is_stanza(item) then 807 if st.is_stanza(item) then
801 core_process_stanza(session, item); 808 core_process_stanza(session, item);
802 else 809 else