view mod_watchuntrusted/mod_watchuntrusted.lua @ 1995:0e008f36a91c

mod_log_events: No such thing as module.remove_host. Fix this module so it can be loaded/unloaded dynamically and behave (mostly) correctly
author Matthew Wild <mwild1@gmail.com>
date Tue, 22 Dec 2015 17:54:25 +0000
parents 4c4a4191b825
children dd1f0173f538
line wrap: on
line source

local jid_prep = require "util.jid".prep;

local secure_auth = module:get_option_boolean("s2s_secure_auth", false);
local secure_domains, insecure_domains =
	module:get_option_set("s2s_secure_domains", {})._items, module:get_option_set("s2s_insecure_domains", {})._items;

local untrusted_fail_watchers = module:get_option_set("untrusted_fail_watchers", module:get_option("admins", {})) / jid_prep;
local untrusted_fail_notification = module:get_option("untrusted_fail_notification", "Establishing a secure connection from $from_host to $to_host failed. Certificate hash: $sha1. $errors");

local st = require "util.stanza";

local notified_about_already = { };

module:hook_global("s2s-check-certificate", function (event)
    local session, host = event.session, event.host;
    if not host then return end
    local conn = session.conn:socket();
    local local_host = session.direction == "outgoing" and session.from_host or session.to_host;

    if not (local_host == module:get_host()) then return end

    module:log("debug", "Checking certificate...");
    local must_secure = secure_auth;

    if not must_secure and secure_domains[host] then
            must_secure = true;
    elseif must_secure and insecure_domains[host] then
            must_secure = false;
    end

    if must_secure and (session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid") and not notified_about_already[host] then
		notified_about_already[host] = os.time();
		local _, errors = conn:getpeerverification();
		local error_message = "";

		for depth, t in pairs(errors or {}) do
			if #t > 0 then
				error_message = error_message .. "Error with certificate " .. (depth - 1) .. ": " .. table.concat(t, ", ") .. ". ";
			end
		end

		if session.cert_identity_status then
			error_message = error_message .. "This certificate is " .. session.cert_identity_status .. " for " .. host .. ".";
		end

		local replacements = {
			sha1 = event.cert and event.cert:digest("sha1") or "(No certificate)",
			sha256 = event.cert and event.cert:digest("sha256") or "(No certificate)",
			errors = error_message
		};

		local message = st.message{ type = "chat", from = local_host }
			:tag("body")
				:text(untrusted_fail_notification:gsub("%$([%w_]+)", function (v)
					return event[v] or session and session[v] or replacements and replacements[v] or nil;
				end));
		for jid in untrusted_fail_watchers do
			module:log("debug", "Notifying %s", jid);
			message.attr.to = jid;
			module:send(message);
		end
	end
end, -0.5);

module:add_timer(14400, function (now)
	for host, time in pairs(notified_about_already) do
		if time + 86400 > now then
			notified_about_already[host] = nil;
		end
	end
	return 14400;
end)