view mod_sasl2_bind2/mod_sasl2_bind2.lua @ 5818:d3b69859553a

mod_password_policy: Change error type from 'cancel' to 'modify' This makes more sense, as the problem relates to the data that has been entered, and therefore the request could be retried with different data.
author Matthew Wild <mwild1@gmail.com>
date Mon, 08 Jan 2024 17:28:39 +0000
parents 6361afcda1a3
children
line wrap: on
line source

local base64 = require "util.encodings".base64;
local id = require "util.id";
local sha1 = require "util.hashes".sha1;
local st = require "util.stanza";

local sm_bind_resource = require "core.sessionmanager".bind_resource;

local xmlns_bind2 = "urn:xmpp:bind:0";
local xmlns_sasl2 = "urn:xmpp:sasl:2";

module:depends("sasl2");

-- Advertise what we can do

module:hook("advertise-sasl-features", function(event)
	local bind = st.stanza("bind", { xmlns = xmlns_bind2 });
	local inline = st.stanza("inline");
	module:fire_event("advertise-bind-features", { origin = event.origin, features = inline });
	bind:add_direct_child(inline);

	event.features:add_direct_child(bind);
end, 1);

-- Helper to actually bind a resource to a session

local function do_bind(session, bind_request)
	local resource = session.sasl_handler.resource;

	if not resource then
		local client_name_tag = bind_request:get_child_text("tag");
		if client_name_tag then
			local client_id = session.client_id;
			local tag_suffix = client_id and base64.encode(sha1(client_id):sub(1, 9)) or id.medium();
			resource = ("%s~%s"):format(client_name_tag, tag_suffix);
		end
	end

	local success, err_type, err, err_msg = sm_bind_resource(session, resource);
	if not success then
		session.log("debug", "Resource bind failed: %s", err_msg or err);
		return nil, { type = err_type, condition = err, text = err_msg };
	end

	session.log("debug", "Resource bound: %s", session.full_jid);
	return st.stanza("bound", { xmlns = xmlns_bind2 });
end

-- Enable inline features requested by the client

local function enable_features(session, bind_request, bind_result)
	module:fire_event("enable-bind-features", {
		session = session;
		request = bind_request;
		result = bind_result;
	});
end

-- SASL 2 integration

module:hook_tag(xmlns_sasl2, "authenticate", function (session, auth)
	-- Cache action for future processing (after auth success)
	session.sasl2_bind_request = auth:child_with_ns(xmlns_bind2);
end, 100);

module:hook("sasl2/c2s/success", function (event)
	local session = event.session;

	local bind_request = session.sasl2_bind_request;
	if not bind_request then return; end -- No bind requested
	session.sasl2_bind_request = nil;

	local sm_success = session.sasl2_sm_success;
	if sm_success and sm_success.type == "resumed" then
		return; -- No need to bind a resource
	end

	local bind_result, err = do_bind(session, bind_request);
	if not bind_result then
		bind_result = st.stanza("failed", { xmlns = xmlns_bind2 })
			:add_error(err);
	else
		enable_features(session, bind_request, bind_result);
	end

	event.success:add_child(bind_result);
end, 100);

-- Inline features

module:hook("advertise-bind-features", function (event)
	local features = event.features;
	features:tag("feature", { var = "urn:xmpp:carbons:2" }):up();
	features:tag("feature", { var = "urn:xmpp:csi:0" }):up();
end);

module:hook("enable-bind-features", function (event)
	local session, request = event.session, event.request;

	-- Carbons
	if request:get_child("enable", "urn:xmpp:carbons:2") then
		session.want_carbons = true;
		event.result:tag("enabled", { xmlns = "urn:xmpp:carbons:2" }):up();
	end

	-- CSI
	local csi_state_tag = request:child_with_ns("urn:xmpp:csi:0");
	if csi_state_tag then
		session.state = csi_state_tag.name;
	end
end, 10);