view mod_auth_ccert/mod_auth_ccert.lua @ 5585:5b316088bef5

mod_rest: Use logger of HTTP request in trunk In Prosody trunk rev c975dafa4303 each HTTP request gained its own log sink, to make it easy to log things related to each request and group those messages. Especially where async is used, spreading the request and response apart as mod_rest does with iq stanzas, this grouped logging should help find related messages.
author Kim Alvefur <zash@zash.se>
date Fri, 07 Jul 2023 00:10:37 +0200
parents 86acfa44dc24
children
line wrap: on
line source

-- Copyright (C) 2013 Kim Alvefur
--
-- This file is MIT/X11 licensed.
--
-- luacheck: ignore 131/get_sasl_handler

local jid_compare = require "util.jid".compare;
local jid_split = require "util.jid".prepped_split;
local new_sasl = require "util.sasl".new;
local now = os.time;
local log = module._log;

local subject_alternative_name = "2.5.29.17";
local id_on_xmppAddr = "1.3.6.1.5.5.7.8.5";
local oid_emailAddress = "1.2.840.113549.1.9.1";

local cert_match = module:get_option("certificate_match", "xmppaddr");

local username_extractor = {};

function username_extractor.xmppaddr(cert, authz, session)
	local extensions = cert:extensions();
	local SANs = extensions[subject_alternative_name];
	local xmppAddrs = SANs and SANs[id_on_xmppAddr];

	if not xmppAddrs then
		(session.log or log)("warn", "Client certificate contains no xmppAddrs");
		return nil, false;
	end

	for i=1,#xmppAddrs do
		if authz == "" or jid_compare(authz, xmppAddrs[i]) then
			(session.log or log)("debug", "xmppAddrs[%d] %q matches authz %q", i, xmppAddrs[i], authz)
			local username, host = jid_split(xmppAddrs[i]);
			if host == module.host then
				return username, true
			end
		end
	end
end

function username_extractor.email(cert)
	local subject = cert:subject();
	for i=1,#subject do
		local ava = subject[i];
		if ava.oid == oid_emailAddress then
			local username, host = jid_split(ava.value);
			if host == module.host then
				return username, true
			end
		end
	end
end

local find_username = username_extractor[cert_match];
if not find_username then
	module:log("error", "certificate_match = %q is not supported");
	return
end


function get_sasl_handler(session)
	return new_sasl(module.host, {
		external = session.secure and function(authz)
			if not session.secure then
				-- getpeercertificate() on a TCP connection would be bad, abort!
				(session.log or log)("error", "How did you manage to select EXTERNAL without TLS?");
				return nil, false;
			end
			local sock = session.conn:socket();
			local cert = sock:getpeercertificate();
			if not cert then
				(session.log or log)("warn", "No certificate provided");
				return nil, false;
			end

			if not cert:validat(now()) then
				(session.log or log)("warn", "Client certificate expired")
				return nil, "expired";
			end

			local chain_valid, chain_errors = sock:getpeerverification();
			if not chain_valid then
				(session.log or log)("warn", "Invalid client certificate chain");
				for i, error in ipairs(chain_errors) do
					(session.log or log)("warn", "%d: %s", i, table.concat(error, ", "));
				end
				return nil, false;
			end

			return find_username(cert, authz, session);
		end
	});
end

module:provides "auth";