From 08cb8436d57dc1efb03141e5d2a3b2cda597592d Mon Sep 17 00:00:00 2001 From: Robert Scheck Date: Apr 23 2014 20:51:03 +0000 Subject: Added upstream patches to avoid resource consumption denial of service when using XMPP application-layer compression (#1085692) --- diff --git a/prosody.compression-dos.patch b/prosody.compression-dos.patch new file mode 100644 index 0000000..6953cbd --- /dev/null +++ b/prosody.compression-dos.patch @@ -0,0 +1,282 @@ +Patch by Matthew Wild for prosody <= 0.8.2 which backports the patches from +https://hg.prosody.im/0.8/ for CVE-2014-2744/CVE-2014-2745 to fix resource consumption denial of +service when using XMPP application-layer compression). See also Red Hat Bugzilla, ID #1085694 +for further information: https://bugzilla.redhat.com/show_bug.cgi?id=1085694 + +--- prosody-0.8.2/net/xmppclient_listener.lua 2011-06-12 23:21:39.000000000 +0200 ++++ prosody-0.8.2/net/xmppclient_listener.lua.compression-dos 2014-04-23 22:27:21.000000000 +0200 +@@ -26,6 +26,7 @@ + + local config = require "core.configmanager"; + local opt_keepalives = config.get("*", "core", "tcp_keepalives"); ++local default_mode = config.get("*", "network_default_read_size") or 4096; + + local stream_callbacks = { default_ns = "jabber:client", + streamopened = sm_streamopened, streamclosed = sm_streamclosed, handlestanza = core_process_stanza }; +@@ -68,7 +69,7 @@ + end + + local sessions = {}; +-local xmppclient = { default_port = 5222, default_mode = "*a" }; ++local xmppclient = { default_port = 5222, default_mode = default_mode }; + + -- These are session methods -- + +--- prosody-0.8.2/net/xmppserver_listener.lua 2011-06-12 23:21:40.000000000 +0200 ++++ prosody-0.8.2/net/xmppserver_listener.lua.compression-dos 2014-04-23 22:27:21.000000000 +0200 +@@ -26,6 +26,9 @@ + local stream_callbacks = { default_ns = "jabber:server", + streamopened = s2s_streamopened, streamclosed = s2s_streamclosed, handlestanza = core_process_stanza }; + ++local config = require "core.configmanager"; ++local default_mode = config.get("*", "network_default_read_size") or 4096; ++ + local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; + + function stream_callbacks.error(session, error, data) +@@ -66,7 +69,7 @@ + end + + local sessions = {}; +-local xmppserver = { default_port = 5269, default_mode = "*a" }; ++local xmppserver = { default_port = 5269, default_mode = default_mode }; + + -- These are session methods -- + +--- prosody-0.8.2/plugins/mod_compression.lua 2011-06-12 23:21:40.000000000 +0200 ++++ prosody-0.8.2/plugins/mod_compression.lua.compression-dos 2014-04-23 22:27:46.000000000 +0200 +@@ -30,7 +30,7 @@ + + module:hook("stream-features", function(event) + local origin, features = event.origin, event.features; +- if not origin.compressed then ++ if not origin.compressed and origin.type == "c2s" then + -- FIXME only advertise compression support when TLS layer has no compression enabled + features:add_child(compression_stream_feature); + end +@@ -39,7 +39,7 @@ + module:hook("s2s-stream-features", function(event) + local origin, features = event.origin, event.features; + -- FIXME only advertise compression support when TLS layer has no compression enabled +- if not origin.compressed then ++ if not origin.compressed and origin.type == "s2sin" then + features:add_child(compression_stream_feature); + end + end); +@@ -130,7 +130,7 @@ + module:hook("stanza/http://jabber.org/protocol/compress:compressed", function(event) + local session = event.origin; + +- if session.type == "s2sout_unauthed" or session.type == "s2sout" then ++ if session.type == "s2sout" then + session.log("debug", "Activating compression...") + -- create deflate and inflate streams + local deflate_stream = get_deflate_stream(session); +@@ -157,7 +157,7 @@ + module:hook("stanza/http://jabber.org/protocol/compress:compress", function(event) + local session, stanza = event.origin, event.stanza; + +- if session.type == "c2s" or session.type == "s2sin" or session.type == "c2s_unauthed" or session.type == "s2sin_unauthed" then ++ if session.type == "c2s" or session.type == "s2sin" then + -- fail if we are already compressed + if session.compressed then + local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); +--- prosody-0.8.2/util/xmppstream.lua 2011-06-12 23:21:41.000000000 +0200 ++++ prosody-0.8.2/util/xmppstream.lua.compression-dos 2014-04-23 22:27:21.000000000 +0200 +@@ -6,7 +6,6 @@ + -- COPYING file in the source package for more information. + -- + +- + local lxp = require "lxp"; + local st = require "util.stanza"; + +@@ -18,11 +17,15 @@ + + -- COMPAT: w/LuaExpat 1.1.0 + local lxp_supports_doctype = pcall(lxp.new, { StartDoctypeDecl = false }); ++local lxp_supports_xmldecl = pcall(lxp.new, { XmlDecl = false }); ++local lxp_supports_bytecount = not not lxp.new({}).getcurrentbytecount; ++ ++local default_stanza_size_limit = 1024*1024*10; -- 10MB + +-if not lxp_supports_doctype then ++if not lxp_supports_doctype or not lxp_supports_bytecount then + default_log("warn", "The version of LuaExpat on your system leaves Prosody " + .."vulnerable to denial-of-service attacks. You should upgrade to " +- .."LuaExpat 1.1.1 or higher as soon as possible. See " ++ .."LuaExpat 1.3.0 or higher as soon as possible. See " + .."http://prosody.im/doc/depends#luaexpat for more information."); + end + +@@ -44,7 +47,9 @@ + _M.ns_separator = ns_separator; + _M.ns_pattern = ns_pattern; + +-function new_sax_handlers(session, stream_callbacks) ++local function dummy_cb() end ++ ++function new_sax_handlers(session, stream_callbacks, cb_handleprogress) + local xml_handlers = {}; + + local log = session.log or default_log; +@@ -53,6 +58,7 @@ + local cb_streamclosed = stream_callbacks.streamclosed; + local cb_error = stream_callbacks.error or function(session, e) error("XML stream error: "..tostring(e)); end; + local cb_handlestanza = stream_callbacks.handlestanza; ++ cb_handleprogress = cb_handleprogress or dummy_cb; + + local stream_ns = stream_callbacks.stream_ns or xmlns_streams; + local stream_tag = stream_callbacks.stream_tag or "stream"; +@@ -64,6 +70,7 @@ + local stream_default_ns = stream_callbacks.default_ns; + + local chardata, stanza = {}; ++ local stanza_size = 0; + local non_streamns_depth = 0; + function xml_handlers:StartElement(tagname, attr) + if stanza and #chardata > 0 then +@@ -96,10 +103,17 @@ + end + + if not stanza then --if we are not currently inside a stanza ++ if lxp_supports_bytecount then ++ stanza_size = self:getcurrentbytecount(); ++ end + if session.notopen then + if tagname == stream_tag then + non_streamns_depth = 0; + if cb_streamopened then ++ if lxp_supports_bytecount then ++ cb_handleprogress(stanza_size); ++ stanza_size = 0; ++ end + cb_streamopened(session, attr); + end + else +@@ -114,15 +128,51 @@ + + stanza = st.stanza(name, attr); + else -- we are inside a stanza, so add a tag ++ if lxp_supports_bytecount then ++ stanza_size = stanza_size + self:getcurrentbytecount(); ++ end + stanza:tag(name, attr); + end + end ++ if lxp_supports_xmldecl then ++ function xml_handlers:XmlDecl(version, encoding, standalone) ++ if lxp_supports_bytecount then ++ cb_handleprogress(self:getcurrentbytecount()); ++ end ++ end ++ end ++ function xml_handlers:StartCdataSection() ++ if lxp_supports_bytecount then ++ if stanza then ++ stanza_size = stanza_size + self:getcurrentbytecount(); ++ else ++ cb_handleprogress(self:getcurrentbytecount()); ++ end ++ end ++ end ++ function xml_handlers:EndCdataSection() ++ if lxp_supports_bytecount then ++ if stanza then ++ stanza_size = stanza_size + self:getcurrentbytecount(); ++ else ++ cb_handleprogress(self:getcurrentbytecount()); ++ end ++ end ++ end + function xml_handlers:CharacterData(data) + if stanza then ++ if lxp_supports_bytecount then ++ stanza_size = stanza_size + self:getcurrentbytecount(); ++ end + t_insert(chardata, data); ++ elseif lxp_supports_bytecount then ++ cb_handleprogress(self:getcurrentbytecount()); + end + end + function xml_handlers:EndElement(tagname) ++ if lxp_supports_bytecount then ++ stanza_size = stanza_size + self:getcurrentbytecount() ++ end + if non_streamns_depth > 0 then + non_streamns_depth = non_streamns_depth - 1; + end +@@ -135,6 +185,10 @@ + -- Complete stanza + local last_add = stanza.last_add; + if not last_add or #last_add == 0 then ++ if lxp_supports_bytecount then ++ cb_handleprogress(stanza_size); ++ end ++ stanza_size = 0; + if tagname ~= stream_error_tag then + cb_handlestanza(session, stanza); + else +@@ -156,7 +210,7 @@ + end + cb_error(session, "parse-error", "unexpected-element-close", name); + end +- stanza, chardata = nil, {}; ++ stanza, chardata, stanza_size = nil, {}, 0; + end + end + +@@ -174,7 +228,7 @@ + xml_handlers.ProcessingInstruction = restricted_handler; + + local function reset() +- stanza, chardata = nil, {}; ++ stanza, chardata, stanza_size = nil, {}, 0; + end + + local function set_session(stream, new_session) +@@ -185,19 +239,39 @@ + return xml_handlers, { reset = reset, set_session = set_session }; + end + +-function new(session, stream_callbacks) +- local handlers, meta = new_sax_handlers(session, stream_callbacks); +- local parser = new_parser(handlers, ns_separator); ++function new(session, stream_callbacks, stanza_size_limit) ++ -- Used to track parser progress (e.g. to enforce size limits) ++ local n_outstanding_bytes = 0; ++ local handle_progress; ++ if lxp_supports_bytecount then ++ function handle_progress(n_parsed_bytes) ++ n_outstanding_bytes = n_outstanding_bytes - n_parsed_bytes; ++ end ++ stanza_size_limit = stanza_size_limit or default_stanza_size_limit; ++ elseif stanza_size_limit then ++ error("Stanza size limits are not supported on this version of LuaExpat") ++ end ++ ++ local handlers, meta = new_sax_handlers(session, stream_callbacks, handle_progress); ++ local parser = new_parser(handlers, ns_separator, false); + local parse = parser.parse; + + return { + reset = function () +- parser = new_parser(handlers, ns_separator); ++ parser = new_parser(handlers, ns_separator, false); + parse = parser.parse; ++ n_outstanding_bytes = 0; + meta.reset(); + end, + feed = function (self, data) +- return parse(parser, data); ++ if lxp_supports_bytecount then ++ n_outstanding_bytes = n_outstanding_bytes + #data; ++ end ++ local ok, err = parse(parser, data); ++ if lxp_supports_bytecount and n_outstanding_bytes > stanza_size_limit then ++ return nil, "stanza-too-large"; ++ end ++ return ok, err; + end, + set_session = meta.set_session; + }; diff --git a/prosody.spec b/prosody.spec index 68124c3..2a783b7 100644 --- a/prosody.spec +++ b/prosody.spec @@ -10,7 +10,7 @@ Name: prosody Version: 0.8.2 -Release: 10%{?dist} +Release: 11%{?dist} Summary: Flexible communications server for Jabber/XMPP Group: System Environment/Daemons @@ -22,6 +22,7 @@ Source2: %{name}.tmpfiles Source3: %{name}.service Patch0: %{name}.config.patch Patch1: %{name}.sslcerts.patch +Patch2: %{name}.compression-dos.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: lua-devel @@ -60,6 +61,7 @@ develop added functionality, or prototype new protocols. #do the sed atfer patch1, to avoid a i686 build issue sed -e 's|$(PREFIX)/lib|$(PREFIX)/%{_lib}|' -i Makefile rm -rf certs/ +%patch2 -p1 # fix wrong end of line encoding pushd doc sed -i -e 's|\r||g' stanza.txt session.txt roster_format.txt @@ -185,6 +187,10 @@ fi %changelog +* Wed Apr 23 2014 Robert Scheck - 0.8.2-11 +- Added upstream patches to avoid resource consumption denial of + service when using XMPP application-layer compression (#1085692) + * Wed Sep 18 2013 Johan Cwiklinski - 0.8.2-10 - Bump version to submit again to koji