Changeset

11560:3bbb1af92514

Merge 0.11->trunk
author Matthew Wild <mwild1@gmail.com>
date Thu, 13 May 2021 11:17:13 +0100 (2021-05-13)
parents 11538:30feeb4d9d0b (current diff) 11559:56785f32e1d4 (diff)
children 11561:d2f33b8fdc96
files core/certmanager.lua plugins/mod_auth_internal_hashed.lua plugins/mod_bosh.lua plugins/mod_c2s.lua plugins/mod_component.lua plugins/mod_dialback.lua plugins/mod_limits.lua plugins/mod_proxy65.lua plugins/mod_s2s.lua plugins/mod_websocket.lua plugins/muc/members_only.lib.lua prosody.cfg.lua.dist prosodyctl util-src/hashes.c util/prosodyctl/check.lua util/set.lua util/startup.lua util/xmppstream.lua
diffstat 20 files changed, 112 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Fri May 07 16:47:58 2021 +0200
+++ b/.hgtags	Thu May 13 11:17:13 2021 +0100
@@ -76,3 +76,4 @@
 0000000000000000000000000000000000000000 0.11.7
 ece430d4980997b216c2240015bf922bdeb12dd6 0.11.7
 774811e2c6abfc5a1b1dd60007cf564bb7c1f969 0.11.8
+d0e9ffccdef934af554ea2d4a5beb9a52e9e951d 0.11.9
--- a/core/certmanager.lua	Fri May 07 16:47:58 2021 +0200
+++ b/core/certmanager.lua	Thu May 13 11:17:13 2021 +0100
@@ -42,12 +42,13 @@
 local resolve_path = pathutil.resolve_relative_path;
 local config_path = prosody.paths.config or ".";
 
+local function test_option(option)
+	return not not ssl_newcontext({mode="server",protocol="sslv23",options={ option }});
+end
+
 local luasec_major, luasec_minor = ssl._VERSION:match("^(%d+)%.(%d+)");
 local luasec_version = tonumber(luasec_major) * 100 + tonumber(luasec_minor);
--- TODO Use ssl.config instead of require here once we are sure that the fix
--- in LuaSec has been widely distributed
--- https://github.com/brunoos/luasec/issues/149
-local luasec_has = softreq"ssl.config" or {
+local luasec_has = ssl.config or softreq"ssl.config" or {
 	algorithms = {
 		ec = luasec_version >= 5;
 	};
@@ -55,11 +56,12 @@
 		curves_list = luasec_version >= 7;
 	};
 	options = {
-		cipher_server_preference = luasec_version >= 2;
-		no_ticket = luasec_version >= 4;
-		no_compression = luasec_version >= 5;
-		single_dh_use = luasec_version >= 2;
-		single_ecdh_use = luasec_version >= 2;
+		cipher_server_preference = test_option("cipher_server_preference");
+		no_ticket = test_option("no_ticket");
+		no_compression = test_option("no_compression");
+		single_dh_use = test_option("single_dh_use");
+		single_ecdh_use = test_option("single_ecdh_use");
+		no_renegotiation = test_option("no_renegotiation");
 	};
 };
 
@@ -219,6 +221,7 @@
 		no_compression = luasec_has.options.no_compression and configmanager.get("*", "ssl_compression") ~= true;
 		single_dh_use = luasec_has.options.single_dh_use;
 		single_ecdh_use = luasec_has.options.single_ecdh_use;
+		no_renegotiation = luasec_has.options.no_renegotiation;
 	};
 	verifyext = {
 		"lsec_continue", -- Continue past certificate verification errors
--- a/plugins/mod_auth_internal_hashed.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_auth_internal_hashed.lua	Thu May 13 11:17:13 2021 +0100
@@ -16,6 +16,7 @@
 local hex = require"util.hex";
 local to_hex, from_hex = hex.to, hex.from;
 local saslprep = require "util.encodings".stringprep.saslprep;
+local secure_equals = require "util.hashes".equals;
 
 local log = module._log;
 local host = module.host;
@@ -41,7 +42,7 @@
 	end
 
 	if credentials.password ~= nil and string.len(credentials.password) ~= 0 then
-		if saslprep(credentials.password) ~= password then
+		if not secure_equals(saslprep(credentials.password), password) then
 			return nil, "Auth failed. Provided password is incorrect.";
 		end
 
@@ -61,7 +62,7 @@
 	local stored_key_hex = to_hex(stored_key);
 	local server_key_hex = to_hex(server_key);
 
-	if valid and stored_key_hex == credentials.stored_key and server_key_hex == credentials.server_key then
+	if valid and secure_equals(stored_key_hex, credentials.stored_key) and secure_equals(server_key_hex, credentials.server_key) then
 		return true;
 	else
 		return nil, "Auth failed. Invalid username, password, or password hash information.";
--- a/plugins/mod_auth_internal_plain.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_auth_internal_plain.lua	Thu May 13 11:17:13 2021 +0100
@@ -9,6 +9,7 @@
 local usermanager = require "core.usermanager";
 local new_sasl = require "util.sasl".new;
 local saslprep = require "util.encodings".stringprep.saslprep;
+local secure_equals = require "util.hashes".equals;
 
 local log = module._log;
 local host = module.host;
@@ -26,7 +27,7 @@
 		return nil, "Password fails SASLprep.";
 	end
 
-	if password == saslprep(credentials.password) then
+	if secure_equals(password, saslprep(credentials.password)) then
 		return true;
 	else
 		return nil, "Auth failed. Invalid username or password.";
--- a/plugins/mod_bosh.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_bosh.lua	Thu May 13 11:17:13 2021 +0100
@@ -45,6 +45,7 @@
 
 local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure");
 local cross_domain = module:get_option("cross_domain_bosh");
+local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 1024*256);
 
 if cross_domain ~= nil then
 	module:log("info", "The 'cross_domain_bosh' option has been deprecated");
@@ -122,7 +123,7 @@
 	local body = request.body;
 
 	local context = { request = request, response = response, notopen = true };
-	local stream = new_xmpp_stream(context, stream_callbacks);
+	local stream = new_xmpp_stream(context, stream_callbacks, stanza_size_limit);
 	response.context = context;
 
 	local headers = response.headers;
--- a/plugins/mod_c2s.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_c2s.lua	Thu May 13 11:17:13 2021 +0100
@@ -27,7 +27,7 @@
 local c2s_timeout = module:get_option_number("c2s_timeout", 300);
 local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5);
 local opt_keepalives = module:get_option_boolean("c2s_tcp_keepalives", module:get_option_boolean("tcp_keepalives", true));
-local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit"); -- TODO come up with a sensible default (util.xmppstream defaults to 10M)
+local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 1024*256);
 
 local measure_connections = module:metric("gauge", "connections", "", "Established c2s connections", {"host", "type", "ip_family"});
 
--- a/plugins/mod_component.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_component.lua	Thu May 13 11:17:13 2021 +0100
@@ -27,6 +27,7 @@
 local log = module._log;
 
 local opt_keepalives = module:get_option_boolean("component_tcp_keepalives", module:get_option_boolean("tcp_keepalives", true));
+local stanza_size_limit = module:get_option_number("component_stanza_size_limit", module:get_option_number("s2s_stanza_size_limit", 1024*512));
 
 local sessions = module:shared("sessions");
 
@@ -304,7 +305,7 @@
 
 	session.log("info", "Incoming Jabber component connection");
 
-	local stream = new_xmpp_stream(session, stream_callbacks);
+	local stream = new_xmpp_stream(session, stream_callbacks, stanza_size_limit);
 	session.stream = stream;
 
 	session.notopen = true;
--- a/plugins/mod_dialback.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_dialback.lua	Thu May 13 11:17:13 2021 +0100
@@ -13,6 +13,7 @@
 local st = require "util.stanza";
 local sha256_hash = require "util.hashes".sha256;
 local sha256_hmac = require "util.hashes".hmac_sha256;
+local secure_equals = require "util.hashes".equals;
 local nameprep = require "util.encodings".stringprep.nameprep;
 local uuid_gen = require"util.uuid".generate;
 
@@ -21,20 +22,6 @@
 local dialback_requests = setmetatable({}, { __mode = 'v' });
 
 local dialback_secret = sha256_hash(module:get_option_string("dialback_secret", uuid_gen()), true);
-local dwd = module:get_option_boolean("dialback_without_dialback", false);
-
---- Helper to check that a session peer's certificate is valid
-function check_cert_status(session)
-	local host = session.direction == "outgoing" and session.to_host or session.from_host
-	local conn = session.conn:socket()
-	local cert
-	if conn.getpeercertificate then
-		cert = conn:getpeercertificate()
-	end
-
-	return module:fire_event("s2s-check-certificate", { host = host, session = session, cert = cert });
-end
-
 
 function module.save()
 	return { dialback_secret = dialback_secret };
@@ -56,7 +43,7 @@
 end
 
 function verify_dialback(id, to, from, key)
-	return key == generate_dialback(id, to, from);
+	return secure_equals(key, generate_dialback(id, to, from));
 end
 
 module:hook("stanza/jabber:server:dialback:verify", function(event)
@@ -110,15 +97,6 @@
 			return true;
 		end
 
-		if dwd and origin.secure then
-			if check_cert_status(origin, from) == false then
-				return
-			elseif origin.cert_chain_status == "valid" and origin.cert_identity_status == "valid" then
-				origin.sends2s(st.stanza("db:result", { to = from, from = to, id = attr.id, type = "valid" }));
-				module:fire_event("s2s-authenticated", { session = origin, host = from, mechanism = "dialback" });
-				return true;
-			end
-		end
 
 		origin.hosts[from] = { dialback_key = stanza[1] };
 
--- a/plugins/mod_limits.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_limits.lua	Thu May 13 11:17:13 2021 +0100
@@ -31,7 +31,7 @@
 		burst = burst:match("^(%d+) ?s$");
 	end
 	local n_burst = tonumber(burst);
-	if not n_burst then
+	if burst and not n_burst then
 		module:log("error", "Unable to parse burst for %s: %q, using default burst interval (%ds)", sess_type, burst, default_burst);
 	end
 	return n_burst or default_burst;
@@ -39,7 +39,16 @@
 
 -- Process config option into limits table:
 -- limits = { c2s = { bytes_per_second = X, burst_seconds = Y } }
-local limits = {};
+local limits = {
+	c2s = {
+		bytes_per_second = 10 * 1024;
+		burst_seconds = 2;
+	};
+	s2sin = {
+		bytes_per_second = 30 * 1024;
+		burst_seconds = 2;
+	};
+};
 
 for sess_type, sess_limits in pairs(limits_cfg) do
 	limits[sess_type] = {
--- a/plugins/mod_proxy65.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_proxy65.lua	Thu May 13 11:17:13 2021 +0100
@@ -93,6 +93,7 @@
 
 	local proxy_address = module:get_option_string("proxy65_address", host);
 	local proxy_acl = module:get_option_array("proxy65_acl");
+	local proxy_open_access = module:get_option_boolean("proxy65_open_access", false);
 
 	-- COMPAT w/pre-0.9 where proxy65_port was specified in the components section of the config
 	local legacy_config = module:get_option_number("proxy65_port");
@@ -109,13 +110,20 @@
 
 		-- check ACL
 		-- using 'while' instead of 'if' so we can break out of it
-		while proxy_acl and #proxy_acl > 0 do --luacheck: ignore 512
+		local allow;
+		if proxy_acl and #proxy_acl > 0 then
 			local jid = stanza.attr.from;
-			local allow;
 			for _, acl in ipairs(proxy_acl) do
-				if jid_compare(jid, acl) then allow = true; break; end
+				if jid_compare(jid, acl) then
+					allow = true;
+					break;
+				end
 			end
-			if allow then break; end
+		elseif proxy_open_access or origin.type == "c2s" then
+			allow = true;
+		end
+
+		if not allow then
 			module:log("warn", "Denying use of proxy for %s", stanza.attr.from);
 			origin.send(st.error_reply(stanza, "auth", "forbidden"));
 			return true;
--- a/plugins/mod_s2s.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_s2s.lua	Thu May 13 11:17:13 2021 +0100
@@ -39,7 +39,7 @@
 local secure_domains, insecure_domains =
 	module:get_option_set("s2s_secure_domains", {})._items, module:get_option_set("s2s_insecure_domains", {})._items;
 local require_encryption = module:get_option_boolean("s2s_require_encryption", false);
-local stanza_size_limit = module:get_option_number("s2s_stanza_size_limit"); -- TODO come up with a sensible default (util.xmppstream defaults to 10M)
+local stanza_size_limit = module:get_option_number("s2s_stanza_size_limit", 1024*512);
 
 local measure_connections_inbound = module:metric(
 	"gauge", "connections_inbound", "",
@@ -343,7 +343,7 @@
 end
 
 --- Helper to check that a session peer's certificate is valid
-function check_cert_status(session)
+local function check_cert_status(session)
 	local host = session.direction == "outgoing" and session.to_host or session.from_host
 	local conn = session.conn:socket()
 	local cert
--- a/plugins/mod_websocket.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/mod_websocket.lua	Thu May 13 11:17:13 2021 +0100
@@ -28,7 +28,7 @@
 
 local t_concat = table.concat;
 
-local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 10 * 1024 * 1024);
+local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 1024 * 256);
 local frame_buffer_limit = module:get_option_number("websocket_frame_buffer_limit", 2 * stanza_size_limit);
 local frame_fragment_limit = module:get_option_number("websocket_frame_fragment_limit", 8);
 local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5);
--- a/plugins/muc/members_only.lib.lua	Fri May 07 16:47:58 2021 +0200
+++ b/plugins/muc/members_only.lib.lua	Thu May 13 11:17:13 2021 +0100
@@ -61,12 +61,20 @@
 end
 
 module:hook("muc-disco#info", function(event)
-	event.reply:tag("feature", {var = get_members_only(event.room) and "muc_membersonly" or "muc_open"}):up();
+	local members_only_room = not not get_members_only(event.room);
+	local members_can_invite = not not get_allow_member_invites(event.room);
+	event.reply:tag("feature", {var = members_only_room and "muc_membersonly" or "muc_open"}):up();
 	table.insert(event.form, {
 		name = "{http://prosody.im/protocol/muc}roomconfig_allowmemberinvites";
 		label = "Allow members to invite new members";
 		type = "boolean";
-		value = not not get_allow_member_invites(event.room);
+		value = members_can_invite;
+	});
+	table.insert(event.form, {
+		name = "muc#roomconfig_allowinvites";
+		label = "Allow users to invite other users";
+		type = "boolean";
+		value = not members_only_room or members_can_invite;
 	});
 end);
 
--- a/prosody.cfg.lua.dist	Fri May 07 16:47:58 2021 +0200
+++ b/prosody.cfg.lua.dist	Thu May 13 11:17:13 2021 +0100
@@ -55,6 +55,7 @@
 		"blocklist"; -- Allow users to block communications with other users
 		"vcard4"; -- User profiles (stored in PEP)
 		"vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
+		"limits"; -- Enable bandwidth limiting for XMPP connections
 
 	-- Nice to have
 		"version"; -- Replies to server version requests
@@ -75,7 +76,6 @@
 		--"http_files"; -- Serve static files from a directory over HTTP
 
 	-- Other specific functionality
-		--"limits"; -- Enable bandwidth limiting for XMPP connections
 		--"groups"; -- Shared roster support
 		--"server_contact_info"; -- Publish contact information for this service
 		--"announce"; -- Send announcement to all online users
@@ -125,6 +125,17 @@
 
 --s2s_secure_domains = { "jabber.org" }
 
+-- Enable rate limits for incoming client and server connections
+
+limits = {
+  c2s = {
+    rate = "10kb/s";
+  };
+  s2sin = {
+    rate = "30kb/s";
+  };
+}
+
 -- Select the authentication backend to use. The 'internal' providers
 -- use Prosody's configured data storage to store the authentication data.
 
--- a/prosodyctl	Fri May 07 16:47:58 2021 +0200
+++ b/prosodyctl	Thu May 13 11:17:13 2021 +0100
@@ -466,6 +466,7 @@
 		ssl = "LuaSec";
 	};
 	local lunbound = dependencies.softreq"lunbound";
+	local lxp = dependencies.softreq"lxp";
 	for name, module in pairs(package.loaded) do
 		if type(module) == "table" and rawget(module, "_VERSION")
 			and name ~= "_G" and not name:match("%.") then
@@ -486,6 +487,9 @@
 		end
 		library_versions["libunbound"] = lunbound._LIBVER;
 	end
+	if lxp then
+		library_versions["libexpat"] = lxp._EXPAT_VERSION;
+	end
 	for name, version in sorted_pairs(module_versions) do
 		print(name..":"..string.rep(" ", longest_name-#name), version);
 	end
--- a/util-src/hashes.c	Fri May 07 16:47:58 2021 +0200
+++ b/util-src/hashes.c	Thu May 13 11:17:13 2021 +0100
@@ -23,6 +23,7 @@
 
 #include "lua.h"
 #include "lauxlib.h"
+#include <openssl/crypto.h>
 #include <openssl/sha.h>
 #include <openssl/md5.h>
 #include <openssl/hmac.h>
@@ -133,6 +134,18 @@
 	return 1;
 }
 
+static int Lhash_equals(lua_State *L) {
+	size_t len1, len2;
+	const char *s1 = luaL_checklstring(L, 1, &len1);
+	const char *s2 = luaL_checklstring(L, 2, &len2);
+	if(len1 == len2) {
+		lua_pushboolean(L, CRYPTO_memcmp(s1, s2, len1) == 0);
+	} else {
+		lua_pushboolean(L, 0);
+	}
+	return 1;
+}
+
 static const luaL_Reg Reg[] = {
 	{ "sha1",		Lsha1		},
 	{ "sha224",		Lsha224		},
@@ -147,6 +160,7 @@
 	{ "scram_Hi_sha1",	Lpbkdf2_sha1	}, /* COMPAT */
 	{ "pbkdf2_hmac_sha1",	Lpbkdf2_sha1	},
 	{ "pbkdf2_hmac_sha256",	Lpbkdf2_sha256	},
+	{ "equals",             Lhash_equals    },
 	{ NULL,			NULL		}
 };
 
--- a/util/prosodyctl/check.lua	Fri May 07 16:47:58 2021 +0200
+++ b/util/prosodyctl/check.lua	Thu May 13 11:17:13 2021 +0100
@@ -47,7 +47,7 @@
 			"umask", "prosodyctl_timeout", "use_ipv6", "use_libevent", "network_settings",
 			"network_backend", "http_default_host",
 			"statistics_interval", "statistics", "statistics_config",
-			"plugin_server", "installer_plugin_path",
+			"plugin_server", "installer_plugin_path", "gc"
 		});
 		local config = configmanager.getconfig();
 		-- Check that we have any global options (caused by putting a host at the top)
--- a/util/set.lua	Fri May 07 16:47:58 2021 +0200
+++ b/util/set.lua	Thu May 13 11:17:13 2021 +0100
@@ -32,6 +32,11 @@
 	return a;
 end
 
+local function is_set(o)
+	local mt = getmetatable(o);
+	return mt == set_mt;
+end
+
 local function new(list)
 	local items = setmetatable({}, items_mt);
 	local set = { _items = items };
@@ -177,6 +182,7 @@
 
 return {
 	new = new;
+	is_set = is_set;
 	union = union;
 	difference = difference;
 	intersection = intersection;
--- a/util/startup.lua	Fri May 07 16:47:58 2021 +0200
+++ b/util/startup.lua	Thu May 13 11:17:13 2021 +0100
@@ -14,7 +14,13 @@
 
 local original_logging_config;
 
-local default_gc_params = { mode = "incremental", threshold = 105, speed = 250 };
+local default_gc_params = {
+	mode = "incremental";
+	-- Incremental mode defaults
+	threshold = 105, speed = 500;
+	-- Generational mode defaults
+	minor_threshold = 20, major_threshold = 50;
+};
 
 local short_params = { D = "daemonize", F = "no-daemonize" };
 local value_params = { config = true };
--- a/util/xmppstream.lua	Fri May 07 16:47:58 2021 +0200
+++ b/util/xmppstream.lua	Thu May 13 11:17:13 2021 +0100
@@ -22,7 +22,7 @@
 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
+local default_stanza_size_limit = 1024*1024*1; -- 1MB
 
 local _ENV = nil;
 -- luacheck: std none
@@ -194,6 +194,9 @@
 				stanza = t_remove(stack);
 			end
 		else
+			if lxp_supports_bytecount then
+				cb_handleprogress(stanza_size);
+			end
 			if cb_streamclosed then
 				cb_streamclosed(session);
 			end
@@ -295,6 +298,9 @@
 			return ok, err;
 		end,
 		set_session = meta.set_session;
+		set_stanza_size_limit = function (_, new_stanza_size_limit)
+			stanza_size_limit = new_stanza_size_limit;
+		end;
 	};
 end