Merge 0.10->trunk
authorKim Alvefur <zash@zash.se>
Mon, 28 Aug 2017 21:05:12 +0200
changeset 8204 a0ad62a269df
parent 8188 e89320b8a789 (current diff)
parent 8203 e92585ab4998 (diff)
child 8206 a7863f4aae65
Merge 0.10->trunk
net/http.lua
plugins/mod_admin_telnet.lua
plugins/mod_c2s.lua
plugins/muc/muc.lib.lua
prosody
prosodyctl
--- a/core/usermanager.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/core/usermanager.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -76,8 +76,12 @@
 	return hosts[host].users.get_password(username);
 end
 
-local function set_password(username, password, host)
-	return hosts[host].users.set_password(username, password);
+local function set_password(username, password, host, resource)
+	local ok, err = hosts[host].users.set_password(username, password);
+	if ok then
+		prosody.events.fire_event("user-password-changed", { username = username, host = host, resource = resource });
+	end
+	return ok, err;
 end
 
 local function user_exists(username, host)
--- a/net/http.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/net/http.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -11,6 +11,7 @@
 local httpstream_new = require "net.http.parser".new;
 local util_http = require "util.http";
 local events = require "util.events";
+local verify_identity = require"util.x509".verify_identity;
 
 local ssl_available = pcall(require, "ssl");
 
@@ -34,6 +35,26 @@
 
 function listener.onconnect(conn)
 	local req = requests[conn];
+
+	-- Validate certificate
+	if not req.insecure and conn:ssl() then
+		local sock = conn:socket();
+		local chain_valid = sock.getpeerverification and sock:getpeerverification();
+		if not chain_valid then
+			req.callback("certificate-chain-invalid", 0, req);
+			req.callback = nil;
+			conn:close();
+			return;
+		end
+		local cert = sock.getpeercertificate and sock:getpeercertificate();
+		if not cert or not verify_identity(req.host, false, cert) then
+			req.callback("certificate-verify-failed", 0, req);
+			req.callback = nil;
+			conn:close();
+			return;
+		end
+	end
+
 	-- Send the request
 	local request_line = { req.method or "GET", " ", req.path, " HTTP/1.1\r\n" };
 	if req.query then
@@ -181,6 +202,7 @@
 				headers[k] = v;
 			end
 		end
+		req.insecure = ex.insecure;
 	end
 
 	log("debug", "Making %s %s request '%s' to %s", req.scheme:upper(), method or "GET", req.id, (ex and ex.suppress_url and host_header) or u);
@@ -196,7 +218,7 @@
 
 	local sslctx = false;
 	if using_https then
-		sslctx = ex and ex.sslctx or { mode = "client", protocol = "sslv23", options = { "no_sslv2", "no_sslv3" } };
+		sslctx = ex and ex.sslctx or self.options and self.options.sslctx;
 	end
 
 	local handler, conn = server.addclient(host, port_number, listener, "*a", sslctx)
@@ -235,17 +257,19 @@
 			return new(setmetatable(new_options, { __index = options }));
 		end or new;
 		events = events.new();
-		request = request;
 	};
 	return http;
 end
 
-local default_http = new();
+local default_http = new({
+	sslctx = { mode = "client", protocol = "sslv23", options = { "no_sslv2", "no_sslv3" } };
+});
 
 return {
 	request = function (u, ex, callback)
 		return default_http:request(u, ex, callback);
 	end;
+	default = default_http;
 	new = new;
 	events = default_http.events;
 	-- COMPAT
--- a/net/websocket.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/net/websocket.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -38,7 +38,7 @@
 end
 
 local function fail(s, code, reason)
-	module:log("warn", "WebSocket connection failed, closing. %d %s", code, reason);
+	log("warn", "WebSocket connection failed, closing. %d %s", code, reason);
 	s:close(code, reason);
 	s.handler:close();
 	return false
--- a/plugins/mod_admin_adhoc.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/plugins/mod_admin_adhoc.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -97,7 +97,7 @@
 	if module_host ~= host then
 		return { status = "completed", error = { message = "Trying to change the password of a user on " .. host .. " but command was sent to " .. module_host}};
 	end
-	if usermanager_user_exists(username, host) and usermanager_set_password(username, fields.password, host) then
+	if usermanager_user_exists(username, host) and usermanager_set_password(username, fields.password, host, nil) then
 		return { status = "completed", info = "Password successfully changed" };
 	else
 		return { status = "completed", error = { message = "User does not exist" } };
--- a/plugins/mod_admin_telnet.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/plugins/mod_admin_telnet.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -1067,7 +1067,7 @@
 	elseif not um.user_exists(username, host) then
 		return nil, "No such user";
 	end
-	local ok, err = um.set_password(username, password, host);
+	local ok, err = um.set_password(username, password, host, nil);
 	if ok then
 		return true, "User password changed";
 	else
--- a/plugins/mod_auth_internal_hashed.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/plugins/mod_auth_internal_hashed.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -120,7 +120,9 @@
 			local credentials = accounts:get(username);
 			if not credentials then return; end
 			if credentials.password then
-				usermanager.set_password(username, credentials.password, host);
+				if provider.set_password(username, credentials.password) == nil then
+					return nil, "Auth failed. Could not set hashed password from plaintext.";
+				end
 				credentials = accounts:get(username);
 				if not credentials then return; end
 			end
--- a/plugins/mod_c2s.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/plugins/mod_c2s.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -201,6 +201,18 @@
 	end
 end, 200);
 
+module:hook_global("user-password-changed", function(event)
+	local username, host, resource = event.username, event.host, event.resource;
+	local user = hosts[host].sessions[username];
+	if user and user.sessions then
+		for r, session in pairs(user.sessions) do
+			if r ~= resource then
+				session:close{ condition = "reset", text = "Password changed" };
+			end
+		end
+	end
+end, 200);
+
 function runner_callbacks:ready()
 	self.data.conn:resume();
 end
--- a/plugins/mod_disco.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/plugins/mod_disco.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -148,7 +148,7 @@
 
 -- Handle caps stream feature
 module:hook("stream-features", function (event)
-	if event.origin.type == "c2s" or event.origin.type == "c2s_unauthed" then
+	if event.origin.type == "c2s" or event.origin.type == "c2s_unbound" then
 		event.features:add_child(get_server_caps_feature());
 	end
 end);
--- a/plugins/mod_mam/mod_mam.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/plugins/mod_mam/mod_mam.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -243,15 +243,19 @@
 	local with = jid_bare(c2s and orig_to or orig_from);
 
 	-- Filter out <stanza-id> that claim to be from us
-	stanza:maptags(function (tag)
-		if tag.name == "stanza-id" and tag.attr.xmlns == xmlns_st_id then
-			local by_user, by_host, res = jid_prepped_split(tag.attr.by);
-			if not res and by_host == module.host and by_user == store_user then
-				return nil;
+	if stanza:get_child("stanza-id", xmlns_st_id) then
+		stanza = st.clone(stanza);
+		stanza:maptags(function (tag)
+			if tag.name == "stanza-id" and tag.attr.xmlns == xmlns_st_id then
+				local by_user, by_host, res = jid_prepped_split(tag.attr.by);
+				if not res and by_host == module.host and by_user == store_user then
+					return nil;
+				end
 			end
-		end
-		return tag;
-	end);
+			return tag;
+		end);
+		event.stanza = stanza;
+	end
 
 	-- We store chat messages or normal messages that have a body
 	if not(orig_type == "chat" or (orig_type == "normal" and stanza:get_child("body")) ) then
@@ -268,18 +272,21 @@
 		end
 	end
 
+	local clone_for_storage;
 	if not strip_tags:empty() then
-		stanza = st.clone(stanza);
-		stanza:maptags(function (tag)
+		clone_for_storage = st.clone(stanza);
+		clone_for_storage:maptags(function (tag)
 			if strip_tags:contains(tag.attr.xmlns) then
 				return nil;
 			else
 				return tag;
 			end
 		end);
-		if #stanza.tags == 0 then
+		if #clone_for_storage.tags == 0 then
 			return;
 		end
+	else
+		clone_for_storage = stanza;
 	end
 
 	-- Check with the users preferences
@@ -287,12 +294,14 @@
 		log("debug", "Archiving stanza: %s", stanza:top_tag());
 
 		-- And stash it
-		local ok = archive:append(store_user, nil, stanza, time_now(), with);
+		local ok = archive:append(store_user, nil, clone_for_storage, time_now(), with);
 		if ok then
+			local clone_for_other_handlers = st.clone(stanza);
 			local id = ok;
-			event.stanza:tag("stanza-id", { xmlns = xmlns_st_id, by = store_user.."@"..host, id = id }):up();
+			clone_for_other_handlers:tag("stanza-id", { xmlns = xmlns_st_id, by = store_user.."@"..host, id = id }):up();
+			event.stanza = clone_for_other_handlers;
 			if cleanup then cleanup[store_user] = true; end
-			module:fire_event("archive-message-added", { origin = origin, stanza = stanza, for_user = store_user, id = id });
+			module:fire_event("archive-message-added", { origin = origin, stanza = clone_for_storage, for_user = store_user, id = id });
 		end
 	else
 		log("debug", "Not archiving stanza: %s (prefs)", stanza:top_tag());
--- a/plugins/mod_register.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/plugins/mod_register.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -91,6 +91,7 @@
 	features:add_child(register_stream_feature);
 end);
 
+-- Password change and account deletion handler
 local function handle_registration_stanza(event)
 	local session, stanza = event.origin, event.stanza;
 	local log = session.log or module._log;
@@ -130,7 +131,7 @@
 			local password = query:get_child_text("password");
 			if username and password then
 				if username == session.username then
-					if usermanager_set_password(username, password, session.host) then
+					if usermanager_set_password(username, password, session.host, session.resource) then
 						session.send(st.reply(stanza));
 					else
 						-- TODO unable to write file, file may be locked, etc, what's the correct error?
@@ -207,6 +208,7 @@
 	return throttle:poll(1);
 end
 
+-- In-band registration
 module:hook("stanza/iq/jabber:iq:register:query", function(event)
 	local session, stanza = event.origin, event.stanza;
 	local log = session.log or module._log;
--- a/prosody	Fri Jul 28 23:47:38 2017 +0100
+++ b/prosody	Mon Aug 28 21:05:12 2017 +0200
@@ -321,7 +321,11 @@
 		return function() end
 	end});
 
-	require "net.http"
+	local http = require "net.http"
+	local config_ssl = config.get("*", "ssl")
+	local https_client = config.get("*", "client_https_ssl")
+	http.default.options.sslctx = require "core.certmanager".create_context("client_https port 0", "client",
+		{ capath = config_ssl.capath, cafile = config_ssl.cafile, verify = "peer", }, https_client);
 
 	require "util.array"
 	require "util.datetime"
--- a/prosodyctl	Fri Jul 28 23:47:38 2017 +0100
+++ b/prosodyctl	Mon Aug 28 21:05:12 2017 +0200
@@ -249,6 +249,13 @@
 
 local prosodyctl = require "util.prosodyctl"
 local socket = require "socket"
+
+local http = require "net.http"
+local config_ssl = config.get("*", "ssl")
+local https_client = config.get("*", "client_https_ssl")
+http.default.options.sslctx = require "core.certmanager".create_context("client_https port 0", "client",
+	{ capath = config_ssl.capath, cafile = config_ssl.cafile, verify = "peer", }, https_client);
+
 -----------------------
 
  -- FIXME: Duplicate code waiting for util.startup
@@ -1334,7 +1341,14 @@
 			print("This version of LuaSec (" .. ssl._VERSION .. ") does not support certificate checking");
 			cert_ok = false
 		else
-			for host in enabled_hosts() do
+			local function skip_bare_jid_hosts(host)
+				if jid_split(host) then
+					-- See issue #779
+					return false;
+				end
+				return true;
+			end
+			for host in it.filter(skip_bare_jid_hosts, enabled_hosts()) do
 				print("Checking certificate for "..host);
 				-- First, let's find out what certificate this host uses.
 				local host_ssl_config = config.rawget(host, "ssl")
--- a/util/sql.lua	Fri Jul 28 23:47:38 2017 +0100
+++ b/util/sql.lua	Mon Aug 28 21:05:12 2017 +0200
@@ -175,7 +175,11 @@
 	sql = self:prepquery(sql);
 	local stmt = assert(self.conn:prepare(sql));
 	assert(stmt:execute(...));
-	return stmt:rows();
+	local result = {};
+	for row in stmt:rows() do result[#result + 1] = row; end
+	stmt:close();
+	local i = 0;
+	return function() i=i+1; return result[i]; end;
 end
 function engine:execute_update(sql, ...)
 	sql = self:prepquery(sql);