File

plugins/muc/password.lib.lua @ 12181:783056b4e448 0.11 0.11.12

util.xml: Do not allow doctypes, comments or processing instructions Yes. This is as bad as it sounds. CVE pending. In Prosody itself, this only affects mod_websocket, which uses util.xml to parse the <open/> frame, thus allowing unauthenticated remote DoS using Billion Laughs. However, third-party modules using util.xml may also be affected by this. This commit installs handlers which disallow the use of doctype declarations and processing instructions without any escape hatch. It, by default, also introduces such a handler for comments, however, there is a way to enable comments nontheless. This is because util.xml is used to parse human-facing data, where comments are generally a desirable feature, and also because comments are generally harmless.
author Jonas Schäfer <jonas@wielicki.name>
date Mon, 10 Jan 2022 18:23:54 +0100
parent 9455:c62c983b8be3
child 10447:b5fd1637f15c
line wrap: on
line source

-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
-- Copyright (C) 2014 Daurnimator
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--

local st = require "util.stanza";

local function get_password(room)
	return room._data.password;
end

local function set_password(room, password)
	if password == "" then password = nil; end
	if room._data.password == password then return false; end
	room._data.password = password;
	return true;
end

module:hook("muc-disco#info", function(event)
	event.reply:tag("feature", {var = get_password(event.room) and "muc_passwordprotected" or "muc_unsecured"}):up();
end);

module:hook("muc-config-form", function(event)
	table.insert(event.form, {
		name = "muc#roomconfig_roomsecret";
		type = "text-private";
		label = "Password";
		value = get_password(event.room) or "";
	});
end, 90-2);

module:hook("muc-config-submitted/muc#roomconfig_roomsecret", function(event)
	if set_password(event.room, event.value) then
		event.status_codes["104"] = true;
	end
end);

-- Don't allow anyone to join room unless they provide the password
module:hook("muc-occupant-pre-join", function(event)
	local room, stanza = event.room, event.stanza;
	if not get_password(room) then return end
	local muc_x = stanza:get_child("x", "http://jabber.org/protocol/muc");
	if not muc_x then return end
	local password = muc_x:get_child_text("password", "http://jabber.org/protocol/muc");
	if not password or password == "" then password = nil; end
	if get_password(room) ~= password then
		local from, to = stanza.attr.from, stanza.attr.to;
		module:log("debug", "%s couldn't join due to invalid password: %s", from, to);
		local reply = st.error_reply(stanza, "auth", "not-authorized"):up();
		reply.tags[1].attr.code = "401";
		event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"}));
		return true;
	end
end, -20);

-- Add password to outgoing invite
module:hook("muc-invite", function(event)
	local password = get_password(event.room);
	if password then
		local x = event.stanza:get_child("x", "http://jabber.org/protocol/muc#user");
		x:tag("password"):text(password):up();
	end
end);

module:hook("muc-room-pre-create", function (event)
	local stanza, room = event.stanza, event.room;
	local muc_x = stanza:get_child("x", "http://jabber.org/protocol/muc");
	if not muc_x then return end
	local password = muc_x:get_child_text("password", "http://jabber.org/protocol/muc");
	set_password(room, password);
end);

return {
	get = get_password;
	set = set_password;
};