Changeset

6304:ace08821e4ee

Merge 0.10->trunk
author Kim Alvefur <zash@zash.se>
date Fri, 04 Jul 2014 23:05:27 +0200
parents 6290:54e748acc07a (current diff) 6303:d289582d3518 (diff)
children 6305:38d82f8ead25
files
diffstat 12 files changed, 169 insertions(+), 132 deletions(-) [+]
line wrap: on
line diff
--- a/core/certmanager.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/core/certmanager.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -10,13 +10,13 @@
 local log = require "util.logger".init("certmanager");
 local ssl = ssl;
 local ssl_newcontext = ssl and ssl.newcontext;
+local new_config = require"util.sslconfig".new;
 
 local tostring = tostring;
 local pairs = pairs;
 local type = type;
 local io_open = io.open;
-local t_concat = table.concat;
-local t_insert = table.insert;
+local select = select;
 
 local prosody = prosody;
 local resolve_path = require"util.paths".resolve_relative_path;
@@ -55,9 +55,6 @@
 local path_options = { -- These we pass through resolve_path()
 	key = true, certificate = true, cafile = true, capath = true, dhparam = true
 }
-local set_options = {
-	options = true, verify = true, verifyext = true
-}
 
 if ssl and not luasec_has_verifyext and ssl.x509 then
 	-- COMPAT mw/luasec-hg
@@ -66,85 +63,34 @@
 	end
 end
 
-local function merge_set(t, o)
-	if type(t) ~= "table" then t = { t } end
-	for k,v in pairs(t) do
-		if v == true or v == false then
-			o[k] = v;
-		else
-			o[v] = true;
-		end
-	end
-	return o;
-end
-
-local protocols = { "sslv2", "sslv3", "tlsv1", "tlsv1_1", "tlsv1_2" };
-for i = 1, #protocols do protocols[protocols[i] .. "+"] = i - 1; end
-
-function create_context(host, mode, user_ssl_config)
-	user_ssl_config = user_ssl_config or {}
-	user_ssl_config.mode = mode;
-
+function create_context(host, mode, ...)
 	if not ssl then return nil, "LuaSec (required for encryption) was not found"; end
 
-	if global_ssl_config then
-		for option,default_value in pairs(global_ssl_config) do
-			if user_ssl_config[option] == nil then
-				user_ssl_config[option] = default_value;
-			end
-		end
-	end
-
-	for option,default_value in pairs(core_defaults) do
-		if user_ssl_config[option] == nil then
-			user_ssl_config[option] = default_value;
-		end
-	end
+	local cfg = new_config();
+	cfg:apply(core_defaults);
+	cfg:apply(global_ssl_config);
+	cfg:apply({
+		mode = mode,
+		-- We can't read the password interactively when daemonized
+		password = function() log("error", "Encrypted certificate for %s requires 'ssl' 'password' to be set in config", host); end;
+	});
 
-	for option in pairs(set_options) do
-		local merged = {};
-		merge_set(core_defaults[option], merged);
-		if global_ssl_config then
-			merge_set(global_ssl_config[option], merged);
-		end
-		merge_set(user_ssl_config[option], merged);
-		local final_array = {};
-		for opt, enable in pairs(merged) do
-			if enable then
-				final_array[#final_array+1] = opt;
-			end
-		end
-		user_ssl_config[option] = final_array;
+	for i = select('#', ...), 1, -1 do
+		cfg:apply(select(i, ...));
 	end
+	local user_ssl_config = cfg:final();
 
-	local min_protocol = protocols[user_ssl_config.protocol];
-	if min_protocol then
-		user_ssl_config.protocol = "sslv23";
-		for i = 1, min_protocol do
-			t_insert(user_ssl_config.options, "no_"..protocols[i]);
-		end
+	if mode == "server" then
+		if not user_ssl_config.key then return nil, "No key present in SSL/TLS configuration for "..host; end
+		if not user_ssl_config.certificate then return nil, "No certificate present in SSL/TLS configuration for "..host; end
 	end
 
-	-- We can't read the password interactively when daemonized
-	user_ssl_config.password = user_ssl_config.password or
-		function() log("error", "Encrypted certificate for %s requires 'ssl' 'password' to be set in config", host); end;
-
 	for option in pairs(path_options) do
 		if type(user_ssl_config[option]) == "string" then
 			user_ssl_config[option] = resolve_path(config_path, user_ssl_config[option]);
 		end
 	end
 
-	-- Allow the cipher list to be a table
-	if type(user_ssl_config.ciphers) == "table" then
-		user_ssl_config.ciphers = t_concat(user_ssl_config.ciphers, ":")
-	end
-
-	if mode == "server" then
-		if not user_ssl_config.key then return nil, "No key present in SSL/TLS configuration for "..host; end
-		if not user_ssl_config.certificate then return nil, "No certificate present in SSL/TLS configuration for "..host; end
-	end
-
 	-- LuaSec expects dhparam to be a callback that takes two arguments.
 	-- We ignore those because it is mostly used for having a separate
 	-- set of params for EXPORT ciphers, which we don't have by default.
--- a/core/hostmanager.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/core/hostmanager.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -74,7 +74,6 @@
 		host = host;
 		s2sout = {};
 		events = events_new();
-		dialback_secret = configmanager.get(host, "dialback_secret") or uuid_gen();
 		send = host_send;
 		modules = {};
 	};
--- a/core/portmanager.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/core/portmanager.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -72,16 +72,6 @@
 	unregister_service(item.name, item);
 end);
 
-local function duplicate_ssl_config(ssl_config)
-	local ssl_config = type(ssl_config) == "table" and ssl_config or {};
-
-	local _config = {};
-	for k, v in pairs(ssl_config) do
-		_config[k] = v;
-	end
-	return _config;
-end
-
 --- Public API
 
 function activate(service_name)
@@ -127,24 +117,15 @@
 				local err;
 				-- Create SSL context for this service/port
 				if service_info.encryption == "ssl" then
-					local ssl_config = duplicate_ssl_config((config.get("*", config_prefix.."ssl") and config.get("*", config_prefix.."ssl")[interface])
-								or (config.get("*", config_prefix.."ssl") and config.get("*", config_prefix.."ssl")[port])
-								or config.get("*", config_prefix.."ssl")
-								or (config.get("*", "ssl") and config.get("*", "ssl")[interface])
-								or (config.get("*", "ssl") and config.get("*", "ssl")[port])
-								or config.get("*", "ssl"));
-					-- add default entries for, or override ssl configuration
-					if ssl_config and service_info.ssl_config then
-						for key, value in pairs(service_info.ssl_config) do
-							if not service_info.ssl_config_override and not ssl_config[key] then
-								ssl_config[key] = value;
-							elseif service_info.ssl_config_override then
-								ssl_config[key] = value;
-							end
-						end
-					end
-
-					ssl, err = certmanager.create_context(service_info.name.." port "..port, "server", ssl_config);
+					local global_ssl_config = config.get("*", "ssl") or {};
+					local prefix_ssl_config = config.get("*", config_prefix.."ssl") or global_ssl_config;
+					ssl, err = certmanager.create_context(service_info.name.." port "..port, "server",
+						service_info.ssl_config or {},
+						prefix_ssl_config[interface],
+						prefix_ssl_config[port],
+						prefix_ssl_config,
+						global_ssl_config[interface],
+						global_ssl_config[port]);
 					if not ssl then
 						log("error", "Error binding encrypted port for %s: %s", service_info.name, error_to_friendly_message(service_name, port_number, err) or "unknown error");
 					end
--- a/plugins/adhoc/adhoc.lib.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/plugins/adhoc/adhoc.lib.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -25,12 +25,13 @@
 end
 
 function _M.handle_cmd(command, origin, stanza)
-	local sessionid = stanza.tags[1].attr.sessionid or uuid.generate();
+	local cmdtag = stanza.tags[1]
+	local sessionid = cmdtag.attr.sessionid or uuid.generate();
 	local dataIn = {};
 	dataIn.to = stanza.attr.to;
 	dataIn.from = stanza.attr.from;
-	dataIn.action = stanza.tags[1].attr.action or "execute";
-	dataIn.form = stanza.tags[1]:child_with_ns("jabber:x:data");
+	dataIn.action = cmdtag.attr.action or "execute";
+	dataIn.form = cmdtag:get_child("x", "jabber:x:data");
 
 	local data, state = command:handler(dataIn, states[sessionid]);
 	states[sessionid] = state;
--- a/plugins/mod_dialback.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/plugins/mod_dialback.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -13,13 +13,26 @@
 local st = require "util.stanza";
 local sha256_hash = require "util.hashes".sha256;
 local nameprep = require "util.encodings".stringprep.nameprep;
+local check_cert_status = module:depends"s2s".check_cert_status;
+local uuid_gen = require"util.uuid".generate;
 
 local xmlns_stream = "http://etherx.jabber.org/streams";
 
 local dialback_requests = setmetatable({}, { __mode = 'v' });
 
+local dialback_secret = module.host .. module:get_option_string("dialback_secret", uuid_gen());
+local dwd = module:get_option_boolean("dialback_without_dialback", false);
+
+function module.save()
+	return { dialback_secret = dialback_secret };
+end
+
+function module.restore(state)
+	dialback_secret = state.dialback_secret;
+end
+
 function generate_dialback(id, to, from)
-	return sha256_hash(id..to..from..hosts[from].dialback_secret, true);
+	return sha256_hash(id..to..dialback_secret, true);
 end
 
 function initiate_dialback(session)
@@ -69,6 +82,16 @@
 		local attr = stanza.attr;
 		local to, from = nameprep(attr.to), nameprep(attr.from);
 
+		if 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 });
+				return true;
+			end
+		end
+
 		if not hosts[to] then
 			-- Not a host that we serve
 			origin.log("warn", "%s tried to connect to %s, which we don't serve", from, to);
--- a/plugins/mod_lastactivity.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/plugins/mod_lastactivity.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -19,8 +19,7 @@
 	local stanza = event.stanza;
 	if not(stanza.attr.to) and stanza.attr.type == "unavailable" then
 		local t = os.time();
-		local s = stanza:child_with_name("status");
-		s = s and #s.tags == 0 and s[1] or "";
+		local s = stanza:get_child_text("status");
 		map[event.origin.username] = {s = s, t = t};
 	end
 end, 10);
--- a/plugins/mod_legacyauth.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/plugins/mod_legacyauth.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -44,9 +44,10 @@
 		return true;
 	end
 
-	local username = stanza.tags[1]:child_with_name("username");
-	local password = stanza.tags[1]:child_with_name("password");
-	local resource = stanza.tags[1]:child_with_name("resource");
+	local query = stanza.tags[1];
+	local username = query:get_child("username");
+	local password = query:get_child("password");
+	local resource = query:get_child("resource");
 	if not (username and password and resource) then
 		local reply = st.reply(stanza);
 		session.send(reply:query("jabber:iq:auth")
--- a/plugins/mod_presence.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/plugins/mod_presence.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -55,14 +55,14 @@
 
 function handle_normal_presence(origin, stanza)
 	if ignore_presence_priority then
-		local priority = stanza:child_with_name("priority");
+		local priority = stanza:get_child("priority");
 		if priority and priority[1] ~= "0" then
 			for i=#priority.tags,1,-1 do priority.tags[i] = nil; end
 			for i=#priority,1,-1 do priority[i] = nil; end
 			priority[1] = "0";
 		end
 	end
-	local priority = stanza:child_with_name("priority");
+	local priority = stanza:get_child("priority");
 	if priority and #priority > 0 then
 		priority = t_concat(priority);
 		if s_find(priority, "^[+-]?[0-9]+$") then
@@ -90,6 +90,7 @@
 		end
 	end
 	if stanza.attr.type == nil and not origin.presence then -- initial presence
+		module:fire_event("presence/initial", { origin = origin, stanza = stanza } );
 		origin.presence = stanza; -- FIXME repeated later
 		local probe = st.presence({from = origin.full_jid, type = "probe"});
 		for jid, item in pairs(roster) do -- probe all contacts we are subscribed to
@@ -137,9 +138,6 @@
 			origin.directed = nil;
 		end
 	else
-		if not origin.presence then
-			module:fire_event("presence/initial", { origin = origin, stanza = stanza } );
-		end
 		origin.presence = stanza;
 		stanza:tag("delay", { xmlns = "urn:xmpp:delay", from = host, stamp = datetime.datetime() }):up();
 		if origin.priority ~= priority then
--- a/plugins/mod_s2s/mod_s2s.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/plugins/mod_s2s/mod_s2s.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -235,7 +235,7 @@
 end
 
 --- Helper to check that a session peer's certificate is valid
-local function check_cert_status(session)
+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_saslauth.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/plugins/mod_saslauth.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -284,7 +284,7 @@
 	local resource;
 	if stanza.attr.type == "set" then
 		local bind = stanza.tags[1];
-		resource = bind:child_with_name("resource");
+		resource = bind:get_child("resource");
 		resource = resource and #resource.tags == 0 and resource[1] or nil;
 	end
 	local success, err_type, err, err_msg = sm_bind_resource(origin, resource);
--- a/plugins/mod_tls.lua	Mon Jun 30 13:12:16 2014 +0200
+++ b/plugins/mod_tls.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -6,7 +6,6 @@
 -- COPYING file in the source package for more information.
 --
 
-local config = require "core.configmanager";
 local create_context = require "core.certmanager".create_context;
 local st = require "util.stanza";
 
@@ -34,23 +33,26 @@
 
 local ssl_ctx_c2s, ssl_ctx_s2sout, ssl_ctx_s2sin;
 do
-	local function get_ssl_cfg(typ)
-		local cfg_key = (typ and typ.."_" or "").."ssl";
-		local ssl_config = config.rawget(module.host, cfg_key);
-		if not ssl_config then
-			local base_host = module.host:match("%.(.*)");
-			ssl_config = config.get(base_host, cfg_key);
-		end
-		return ssl_config or typ and get_ssl_cfg();
-	end
+	local NULL, err = {};
+	local global = module:context("*");
+	local parent = module:context(module.host:match("%.(.*)$"));
+
+	local parent_ssl = parent:get_option("ssl");
+	local host_ssl   = module:get_option("ssl", parent_ssl);
 
-	local ssl_config, err = get_ssl_cfg("c2s");
-	ssl_ctx_c2s, err = create_context(host.host, "server", ssl_config); -- for incoming client connections
+	local global_c2s = global:get_option("c2s_ssl", NULL);
+	local parent_c2s = parent:get_option("c2s_ssl", NULL);
+	local host_c2s   = module:get_option("c2s_ssl", parent_c2s);
+
+	local global_s2s = global:get_option("s2s_ssl", NULL);
+	local parent_s2s = parent:get_option("s2s_ssl", NULL);
+	local host_s2s   = module:get_option("s2s_ssl", parent_s2s);
+
+	ssl_ctx_c2s, err = create_context(host.host, "server", host_c2s, host_ssl, global_c2s); -- for incoming client connections
 	if err then module:log("error", "Error creating context for c2s: %s", err); end
 
-	ssl_config = get_ssl_cfg("s2s");
-	ssl_ctx_s2sin, err = create_context(host.host, "server", ssl_config); -- for incoming server connections
-	ssl_ctx_s2sout = create_context(host.host, "client", ssl_config); -- for outgoing server connections
+	ssl_ctx_s2sin, err = create_context(host.host, "server", host_s2s, host_ssl, global_s2s); -- for incoming server connections
+	ssl_ctx_s2sout = create_context(host.host, "client", host_s2s, host_ssl, global_s2s); -- for outgoing server connections
 	if err then module:log("error", "Error creating context for s2s: %s", err); end -- Both would have the same issue
 end
 
@@ -106,7 +108,7 @@
 -- For s2sout connections, start TLS if we can
 module:hook_stanza("http://etherx.jabber.org/streams", "features", function (session, stanza)
 	module:log("debug", "Received features element");
-	if can_do_tls(session) and stanza:child_with_ns(xmlns_starttls) then
+	if can_do_tls(session) and stanza:get_child("starttls", xmlns_starttls) then
 		module:log("debug", "%s is offering TLS, taking up the offer...", session.to_host);
 		session.sends2s("<starttls xmlns='"..xmlns_starttls.."'/>");
 		return true;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/sslconfig.lua	Fri Jul 04 23:05:27 2014 +0200
@@ -0,0 +1,87 @@
+
+local handlers = { };
+local finalisers = { };
+local id = function (v) return v end
+
+function handlers.options(a, k, b)
+	local o = a[k] or { };
+	if type(b) ~= "table" then b = { b } end
+	for k,v in pairs(b) do
+		if v == true or v == false then
+			o[k] = v;
+		else
+			o[v] = true;
+		end
+	end
+	a[k] = o;
+end
+
+handlers.verify = handlers.options;
+handlers.verifyext = handlers.options;
+
+function finalisers.options(a)
+	local o = {};
+	for opt, enable in pairs(a) do
+		if enable then
+			o[#o+1] = opt;
+		end
+	end
+	return o;
+end
+
+finalisers.verify = finalisers.options;
+finalisers.verifyext = finalisers.options;
+
+function finalisers.ciphers(a)
+	if type(a) == "table" then
+		return table.concat(a, ":");
+	end
+	return a;
+end
+
+local protocols = { "sslv2", "sslv3", "tlsv1", "tlsv1_1", "tlsv1_2" };
+for i = 1, #protocols do protocols[protocols[i] .. "+"] = i - 1; end
+
+local function protocol(a)
+	local min_protocol = protocols[a.protocol];
+	if min_protocol then
+		a.protocol = "sslv23";
+		for i = 1, min_protocol do
+			table.insert(a.options, "no_"..protocols[i]);
+		end
+	end
+end
+
+local function apply(a, b)
+	if type(b) == "table" then
+		for k,v in pairs(b) do
+			(handlers[k] or rawset)(a, k, v);
+		end
+	end
+end
+
+local function final(a)
+	local f = { };
+	for k,v in pairs(a) do
+		f[k] = (finalisers[k] or id)(v);
+	end
+	protocol(f);
+	return f;
+end
+
+local sslopts_mt = {
+	__index = {
+		apply = apply;
+		final = final;
+	};
+};
+
+local function new()
+	return setmetatable({options={}}, sslopts_mt);
+end
+
+return {
+	apply = apply;
+	final = final;
+	new = new;
+};