diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index e4d77868d..8128a65c2 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -26,9 +26,13 @@ jobs:
run: |
echo deb http://packages.prosody.im/debian $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/prosody.list
sudo wget https://prosody.im/files/prosody-debian-packages.key -O/etc/apt/trusted.gpg.d/prosody.gpg
- sudo apt-get update
- sudo apt-get -y install lua5.3 prosody-trunk lua-bitop lua-sec
+ sudo apt update
+ sudo apt install lua5.4 prosody-trunk lua-bitop lua-sec luarocks
sudo service prosody stop
+ prosodyctl --config server/prosody.cfg.lua install mod_sasl2
+ prosodyctl --config server/prosody.cfg.lua install mod_sasl2_bind2
+ prosodyctl --config server/prosody.cfg.lua install mod_sasl2_fast
+ prosodyctl --config server/prosody.cfg.lua install mod_sasl2_sm
# - run: npm install -g npm
- run: make
diff --git a/packages/sasl2/test.js b/packages/sasl2/test.js
index fc53c62d1..844430eae 100644
--- a/packages/sasl2/test.js
+++ b/packages/sasl2/test.js
@@ -195,7 +195,12 @@ test("with bind2", async () => {
,
);
- entity.mockInput();
+ entity.mockInput(
+
+ {entity.jid}
+
+ ,
+ );
entity.mockInput();
await promise(entity, "online");
diff --git a/server/modules/.gitkeep b/server/modules/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/server/modules/mod_sasl2.lua b/server/modules/mod_sasl2.lua
deleted file mode 100644
index 9eeff4b15..000000000
--- a/server/modules/mod_sasl2.lua
+++ /dev/null
@@ -1,228 +0,0 @@
--- Prosody IM
--- Copyright (C) 2019 Kim Alvefur
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
--- XEP-0388: Extensible SASL Profile
---
-
-local st = require "util.stanza";
-local errors = require "util.error";
-local base64 = require "util.encodings".base64;
-local jid_join = require "util.jid".join;
-local set = require "util.set";
-
-local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler;
-local sm_make_authenticated = require "core.sessionmanager".make_authenticated;
-
-local xmlns_sasl2 = "urn:xmpp:sasl:2";
-
-local secure_auth_only = module:get_option_boolean("c2s_require_encryption", module:get_option_boolean("require_encryption", true));
-local allow_unencrypted_plain_auth = module:get_option_boolean("allow_unencrypted_plain_auth", false)
-local insecure_mechanisms = module:get_option_set("insecure_sasl_mechanisms", allow_unencrypted_plain_auth and {} or {"PLAIN", "LOGIN"});
-local disabled_mechanisms = module:get_option_set("disable_sasl_mechanisms", { "DIGEST-MD5" });
-
-local host = module.host;
-
-local function tls_unique(self)
- return self.userdata["tls-unique"]:ssl_peerfinished();
-end
-
-local function tls_exporter(conn)
- if not conn.ssl_exportkeyingmaterial then return end
- return conn:ssl_exportkeyingmaterial("EXPORTER-Channel-Binding", 32, "");
-end
-
-local function sasl_tls_exporter(self)
- return tls_exporter(self.userdata["tls-exporter"]);
-end
-
-module:hook("stream-features", function(event)
- local origin, features = event.origin, event.features;
- local log = origin.log or module._log;
-
- if origin.type ~= "c2s_unauthed" then
- log("debug", "Already authenticated");
- return
- elseif secure_auth_only and not origin.secure then
- log("debug", "Not offering authentication on insecure connection");
- return;
- end
-
- local sasl_handler = usermanager_get_sasl_handler(host, origin)
- origin.sasl_handler = sasl_handler;
-
- local channel_bindings = set.new()
- if origin.encrypted then
- -- check whether LuaSec has the nifty binding to the function needed for tls-unique
- -- FIXME: would be nice to have this check only once and not for every socket
- if sasl_handler.add_cb_handler then
- local info = origin.conn:ssl_info();
- if info and info.protocol == "TLSv1.3" then
- log("debug", "Channel binding 'tls-unique' undefined in context of TLS 1.3");
- if tls_exporter(origin.conn) then
- log("debug", "Channel binding 'tls-exporter' supported");
- sasl_handler:add_cb_handler("tls-exporter", sasl_tls_exporter);
- channel_bindings:add("tls-exporter");
- else
- log("debug", "Channel binding 'tls-exporter' not supported");
- end
- elseif origin.conn.ssl_peerfinished and origin.conn:ssl_peerfinished() then
- log("debug", "Channel binding 'tls-unique' supported");
- sasl_handler:add_cb_handler("tls-unique", tls_unique);
- channel_bindings:add("tls-unique");
- else
- log("debug", "Channel binding 'tls-unique' not supported (by LuaSec?)");
- end
- sasl_handler["userdata"] = {
- ["tls-unique"] = origin.conn;
- ["tls-exporter"] = origin.conn;
- };
- else
- log("debug", "Channel binding not supported by SASL handler");
- end
- end
-
- local mechanisms = st.stanza("authentication", { xmlns = xmlns_sasl2 });
-
- local available_mechanisms = sasl_handler:mechanisms()
- for mechanism in pairs(available_mechanisms) do
- if disabled_mechanisms:contains(mechanism) then
- log("debug", "Not offering disabled mechanism %s", mechanism);
- elseif not origin.secure and insecure_mechanisms:contains(mechanism) then
- log("debug", "Not offering mechanism %s on insecure connection", mechanism);
- else
- log("debug", "Offering mechanism %s", mechanism);
- mechanisms:text_tag("mechanism", mechanism);
- end
- end
-
- features:add_direct_child(mechanisms);
-
- local inline = st.stanza("inline");
- module:fire_event("advertise-sasl-features", { origin = origin, features = inline, stream = event.stream });
- mechanisms:add_direct_child(inline);
-end, 1);
-
-local function handle_status(session, status, ret, err_msg)
- local err = nil;
- if status == "error" then
- ret, err = nil, ret;
- if not errors.is_err(err) then
- err = errors.new({ condition = err, text = err_msg }, { session = session });
- end
- end
-
- return module:fire_event("sasl2/"..session.base_type.."/"..status, {
- session = session,
- message = ret;
- error = err;
- error_text = err_msg;
- });
-end
-
-module:hook("sasl2/c2s/failure", function (event)
- module:fire_event("authentication-failure", event);
- local session, condition, text = event.session, event.message, event.error_text;
- local failure = st.stanza("failure", { xmlns = xmlns_sasl2 })
- :tag(condition, { xmlns = "urn:ietf:params:xml:ns:xmpp-sasl" }):up();
- if text then
- failure:text_tag("text", text);
- end
- session.send(failure);
- return true;
-end);
-
-module:hook("sasl2/c2s/error", function (event)
- local session = event.session
- session.send(st.stanza("failure", { xmlns = xmlns_sasl2 })
- :tag(event.error and event.error.condition));
- return true;
-end);
-
-module:hook("sasl2/c2s/challenge", function (event)
- local session = event.session;
- session.send(st.stanza("challenge", { xmlns = xmlns_sasl2 })
- :text(base64.encode(event.message)));
- return true;
-end);
-
-module:hook("sasl2/c2s/success", function (event)
- local session = event.session
- local ok, err = sm_make_authenticated(session, session.sasl_handler.username);
- if not ok then
- handle_status(session, "failure", err);
- return true;
- end
- event.success = st.stanza("success", { xmlns = xmlns_sasl2 });
- if event.message then
- event.success:text_tag("additional-data", base64.encode(event.message));
- end
-end, 1000);
-
-module:hook("sasl2/c2s/success", function (event)
- local session = event.session
- event.success:text_tag("authorization-identifier", jid_join(session.username, session.host, session.resource));
- session.send(event.success);
-end, -1000);
-
-module:hook("sasl2/c2s/success", function (event)
- module:fire_event("authentication-success", event);
- local session = event.session;
- local features = st.stanza("stream:features");
- module:fire_event("stream-features", { origin = session, features = features });
- session.send(features);
-end, -1500);
-
--- The gap here is to allow modules to do stuff to the stream after the stanza
--- is sent, but before we proceed with anything else. This is expected to be
--- a common pattern with SASL2, which allows atomic negotiation of a bunch of
--- stream features.
-module:hook("sasl2/c2s/success", function (event) --luacheck: ignore 212/event
- event.session.sasl_handler = nil;
- return true;
-end, -2000);
-
-local function process_cdata(session, cdata)
- if cdata then
- cdata = base64.decode(cdata);
- if not cdata then
- return handle_status(session, "failure", "incorrect-encoding");
- end
- end
- return handle_status(session, session.sasl_handler:process(cdata));
-end
-
-module:hook_tag(xmlns_sasl2, "authenticate", function (session, auth)
- if secure_auth_only and not session.secure then
- return handle_status(session, "failure", "encryption-required");
- end
- local sasl_handler = session.sasl_handler;
- if not sasl_handler then
- sasl_handler = usermanager_get_sasl_handler(host, session);
- session.sasl_handler = sasl_handler;
- end
- local mechanism = assert(auth.attr.mechanism);
- if not sasl_handler:select(mechanism) then
- return handle_status(session, "failure", "invalid-mechanism");
- end
- local user_agent = auth:get_child("user-agent");
- if user_agent then
- session.client_id = user_agent.attr.id;
- sasl_handler.user_agent = {
- software = user_agent:get_child_text("software");
- device = user_agent:get_child_text("device");
- };
- end
- local initial = auth:get_child_text("initial-response");
- return process_cdata(session, initial);
-end);
-
-module:hook_tag(xmlns_sasl2, "response", function (session, response)
- local sasl_handler = session.sasl_handler;
- if not sasl_handler or not sasl_handler.selected then
- return handle_status(session, "failure", "invalid-mechanism");
- end
- return process_cdata(session, response:get_text());
-end);
diff --git a/server/modules/mod_sasl2_bind2.lua b/server/modules/mod_sasl2_bind2.lua
deleted file mode 100644
index fb02ffd59..000000000
--- a/server/modules/mod_sasl2_bind2.lua
+++ /dev/null
@@ -1,110 +0,0 @@
-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);
diff --git a/server/modules/mod_sasl2_fast.lua b/server/modules/mod_sasl2_fast.lua
deleted file mode 100644
index 4a4c4475f..000000000
--- a/server/modules/mod_sasl2_fast.lua
+++ /dev/null
@@ -1,271 +0,0 @@
-local usermanager = require "core.usermanager";
-
-local sasl = require "util.sasl";
-local dt = require "util.datetime";
-local id = require "util.id";
-local jid = require "util.jid";
-local st = require "util.stanza";
-local now = require "util.time".now;
-local hash = require "util.hashes";
-
-local sasl_mt = getmetatable(sasl.new("", { mechanisms = {} }));
-local function is_util_sasl(sasl_handler)
- return getmetatable(sasl_handler) == sasl_mt;
-end
-
-module:depends("sasl2");
-
--- Tokens expire after 21 days by default
-local fast_token_ttl = module:get_option_number("sasl2_fast_token_ttl", 86400*21);
--- Tokens are automatically rotated daily
-local fast_token_min_ttl = module:get_option_number("sasl2_fast_token_min_ttl", 86400);
-
-local xmlns_fast = "urn:xmpp:fast:0";
-local xmlns_sasl2 = "urn:xmpp:sasl:2";
-
-local token_store = module:open_store("fast_tokens", "map");
-
-local log = module._log;
-
-local function make_token(username, client_id, mechanism)
- local new_token = "secret-token:fast-"..id.long();
- local key = hash.sha256(client_id, true).."-new";
- local issued_at = now();
- local token_info = {
- mechanism = mechanism;
- secret = new_token;
- issued_at = issued_at;
- expires_at = issued_at + fast_token_ttl;
- };
- if not token_store:set(username, key, token_info) then
- return nil;
- end
- return token_info;
-end
-
-local function new_token_tester(hmac_f)
- return function (mechanism, username, client_id, token_hash, cb_data, invalidate)
- local account_info = usermanager.get_account_info(username, module.host);
- local last_password_change = account_info and account_info.password_updated;
- local tried_current_token = false;
- local key = hash.sha256(client_id, true).."-new";
- local token;
- repeat
- log("debug", "Looking for %s token %s/%s", mechanism, username, key);
- token = token_store:get(username, key);
- if token and token.mechanism == mechanism then
- local expected_hash = hmac_f(token.secret, "Initiator"..(cb_data or ""));
- if hash.equals(expected_hash, token_hash) then
- local current_time = now();
- if token.expires_at < current_time then
- log("debug", "Token found, but it has expired (%ds ago). Cleaning up...", current_time - token.expires_at);
- token_store:set(username, key, nil);
- return nil, "credentials-expired";
- elseif last_password_change and token.issued_at < last_password_change then
- log("debug", "Token found, but issued prior to password change (%ds ago). Cleaning up...",
- current_time - last_password_change
- );
- token_store:set(username, key, nil);
- return nil, "credentials-expired";
- end
- if not tried_current_token and not invalidate then
- -- The new token is becoming the current token
- token_store:set_keys(username, {
- [key] = token_store.remove;
- [key:sub(1, -5).."-cur"] = token;
- });
- end
- local rotation_needed;
- if invalidate then
- token_store:set(username, key, nil);
- elseif current_time - token.issued_at > fast_token_min_ttl then
- log("debug", "FAST token due for rotation (age: %d)", current_time - token.issued_at);
- rotation_needed = true;
- end
- return true, username, hmac_f(token.secret, "Responder"..(cb_data or "")), rotation_needed;
- end
- end
- if not tried_current_token then
- log("debug", "Trying next token...");
- -- Try again with the current token instead
- tried_current_token = true;
- key = key:sub(1, -5).."-cur";
- else
- log("debug", "No matching %s token found for %s/%s", mechanism, username, key);
- return nil;
- end
- until false;
- end
-end
-
-function get_sasl_handler()
- local token_auth_profile = {
- ht_sha_256 = new_token_tester(hash.hmac_sha256);
- };
- local handler = sasl.new(module.host, token_auth_profile);
- handler.fast = true;
- return handler;
-end
-
--- Advertise FAST to connecting clients
-module:hook("advertise-sasl-features", function (event)
- local session = event.origin;
- local username = session.username;
- if not username then
- username = jid.node(event.stream.from);
- if not username then return; end
- end
- local sasl_handler = get_sasl_handler(username);
- if not sasl_handler then return; end
- sasl_handler.fast_auth = true; -- For informational purposes
- -- Copy channel binding info from primary SASL handler if it's compatible
- if is_util_sasl(session.sasl_handler) then
- sasl_handler.profile.cb = session.sasl_handler.profile.cb;
- sasl_handler.userdata = session.sasl_handler.userdata;
- end
- -- Store this handler, in case we later want to use it for authenticating
- session.fast_sasl_handler = sasl_handler;
- local fast = st.stanza("fast", { xmlns = xmlns_fast });
- for mech in pairs(sasl_handler:mechanisms()) do
- fast:text_tag("mechanism", mech);
- end
- event.features:add_child(fast);
-end);
-
--- Process any FAST elements in
-module:hook_tag(xmlns_sasl2, "authenticate", function (session, auth)
- -- Cache action for future processing (after auth success)
- local fast_auth = auth:get_child("fast", xmlns_fast);
- if fast_auth then
- -- Client says it is using FAST auth, so set our SASL handler
- local fast_sasl_handler = session.fast_sasl_handler;
- local client_id = auth:get_child_attr("user-agent", nil, "id");
- if fast_sasl_handler and client_id then
- session.log("debug", "Client is authenticating using FAST");
- fast_sasl_handler.client_id = client_id;
- fast_sasl_handler.profile.cb = session.sasl_handler.profile.cb;
- fast_sasl_handler.userdata = session.sasl_handler.userdata;
- local invalidate = fast_auth.attr.invalidate;
- fast_sasl_handler.invalidate = invalidate == "1" or invalidate == "true";
- -- Set our SASL handler as the session's SASL handler
- session.sasl_handler = fast_sasl_handler;
- else
- session.log("warn", "Client asked to auth via FAST, but SASL handler or client id missing");
- local failure = st.stanza("failure", { xmlns = xmlns_sasl2 })
- :tag("malformed-request"):up()
- :text_tag("text", "FAST is not available on this stream");
- session.send(failure);
- return true;
- end
- end
- session.fast_sasl_handler = nil;
- local fast_token_request = auth:get_child("request-token", xmlns_fast);
- if fast_token_request then
- local mech = fast_token_request.attr.mechanism;
- session.log("debug", "Client requested new FAST token for %s", mech);
- session.fast_token_request = {
- mechanism = mech;
- };
- end
-end, 100);
-
--- Process post-success (new token generation, etc.)
-module:hook("sasl2/c2s/success", function (event)
- local session = event.session;
-
- local token_request = session.fast_token_request;
- local client_id = session.client_id;
- local sasl_handler = session.sasl_handler;
- if token_request or (sasl_handler.fast and sasl_handler.rotation_needed) then
- if not client_id then
- session.log("warn", "FAST token requested, but missing client id");
- return;
- end
- local mechanism = token_request and token_request.mechanism or session.sasl_handler.selected;
- local token_info = make_token(session.username, client_id, mechanism)
- if token_info then
- session.log("debug", "Provided new FAST token to client");
- event.success:tag("token", {
- xmlns = xmlns_fast;
- expiry = dt.datetime(token_info.expires_at);
- token = token_info.secret;
- }):up();
- end
- end
-end, 75);
-
--- HT-* mechanisms
-
-local function new_ht_mechanism(mechanism_name, backend_profile_name, cb_name)
- return function (sasl_handler, message)
- local backend = sasl_handler.profile[backend_profile_name];
- local authc_username, token_hash = message:match("^([^%z]+)%z(.+)$");
- if not authc_username then
- return "failure", "malformed-request";
- end
- local cb_data;
- if cb_name then
- if not sasl_handler.profile.cb then
- module:log("warn", "Attempt to use channel binding %s with SASL profile that does not support any channel binding (FAST: %s)", cb_name, sasl_handler.fast);
- return "failure", "malformed-request";
- elseif not sasl_handler.profile.cb[cb_name] then
- module:log("warn", "SASL profile does not support %s channel binding (FAST: %s)", cb_name, sasl_handler.fast);
- return "failure", "malformed-request";
- end
- cb_data = sasl_handler.profile.cb[cb_name](sasl_handler) or "";
- end
- local ok, authz_username, response, rotation_needed = backend(
- mechanism_name,
- authc_username,
- sasl_handler.client_id,
- token_hash,
- cb_data,
- sasl_handler.invalidate
- );
- if not ok then
- -- authz_username is error condition
- return "failure", authz_username or "not-authorized";
- end
- sasl_handler.username = authz_username;
- sasl_handler.rotation_needed = rotation_needed;
- return "success", response;
- end
-end
-
-local function register_ht_mechanism(name, backend_profile_name, cb_name)
- return sasl.registerMechanism(name, { backend_profile_name }, new_ht_mechanism(
- name,
- backend_profile_name,
- cb_name
- ),
- cb_name and { cb_name } or nil);
-end
-
-register_ht_mechanism("HT-SHA-256-NONE", "ht_sha_256", nil);
-register_ht_mechanism("HT-SHA-256-UNIQ", "ht_sha_256", "tls-unique");
-register_ht_mechanism("HT-SHA-256-ENDP", "ht_sha_256", "tls-server-end-point");
-register_ht_mechanism("HT-SHA-256-EXPR", "ht_sha_256", "tls-exporter");
-
--- Public API
-
---luacheck: ignore 131
-function is_client_fast(username, client_id, last_password_change)
- local client_id_hash = hash.sha256(client_id, true);
- local curr_time = now();
- local cur = token_store:get(username, client_id_hash.."-cur");
- if cur and cur.expires_at >= curr_time and (not last_password_change or last_password_change < cur.issued_at) then
- return true;
- end
- local new = token_store:get(username, client_id_hash.."-new");
- if new and new.expires_at >= curr_time and (not last_password_change or last_password_change < new.issued_at) then
- return true;
- end
- return false;
-end
-
-function revoke_fast_tokens(username, client_id)
- local client_id_hash = hash.sha256(client_id, true);
- local cur_ok = token_store:set(username, client_id_hash.."-cur", nil);
- local new_ok = token_store:set(username, client_id_hash.."-new", nil);
- return cur_ok and new_ok;
-end
diff --git a/server/modules/mod_sasl2_sm.lua b/server/modules/mod_sasl2_sm.lua
deleted file mode 100644
index 4ed0fb1ac..000000000
--- a/server/modules/mod_sasl2_sm.lua
+++ /dev/null
@@ -1,90 +0,0 @@
-local st = require "util.stanza";
-
-local mod_smacks = module:depends("smacks");
-
-local xmlns_sasl2 = "urn:xmpp:sasl:2";
-local xmlns_sm = "urn:xmpp:sm:3";
-
-module:depends("sasl2");
-
--- Advertise what we can do
-
-module:hook("advertise-sasl-features", function (event)
- local features = event.features;
- features:tag("sm", { xmlns = xmlns_sm }):up();
-end);
-
-module:hook("advertise-bind-features", function (event)
- local features = event.features;
- features:tag("feature", { var = xmlns_sm }):up();
-end);
-
-module:hook_tag(xmlns_sasl2, "authenticate", function (session, auth)
- -- Cache action for future processing (after auth success)
- session.sasl2_sm_request = auth:child_with_ns(xmlns_sm);
-end, 100);
-
--- SASL 2 integration (for resume)
-
-module:hook("sasl2/c2s/success", function (event)
- local session = event.session;
- local sm_request = session.sasl2_sm_request;
- if not sm_request then return; end
- session.sasl2_sm_request = nil;
- local sm_result;
- if sm_request.name ~= "resume" then return; end
-
- local resumed, err = mod_smacks.do_resume(session, sm_request);
- if not resumed then
- local h = err.context and err.context.h;
- sm_result = st.stanza("failed", { xmlns = xmlns_sm, h = h and ("%d"):format(h) or nil })
- :add_error(err);
- else
- event.session = resumed.session; -- Update to resumed session
- event.session.sasl2_sm_success = resumed; -- To be called after sending final SASL response
- sm_result = st.stanza("resumed", { xmlns = xmlns_sm,
- h = ("%d"):format(event.session.handled_stanza_count);
- previd = resumed.id; });
- end
-
- if sm_result then
- event.success:add_child(sm_result);
- end
-end, 110);
-
--- Bind 2 integration (for enable)
-
-module:hook("enable-bind-features", function (event)
- local sm_enable = event.request:get_child("enable", xmlns_sm);
- if not sm_enable then return; end
-
- local sm_result;
- local enabled, err = mod_smacks.do_enable(event.session, sm_enable);
- if not enabled then
- sm_result = st.stanza("failed", { xmlns = xmlns_sm })
- :add_error(err);
- else
- event.session.sasl2_sm_success = enabled; -- To be called after sending final SASL response
- sm_result = st.stanza("enabled", {
- xmlns = xmlns_sm;
- id = enabled.id;
- resume = enabled.id and "1" or nil;
- max = enabled.resume_max;
- });
- end
- event.result:add_child(sm_result);
-end, 100);
-
--- Finish and/or clean up after SASL 2 completed
-
-module:hook("sasl2/c2s/success", function (event)
- -- The authenticate response has already been sent at this point
- local success = event.session.sasl2_sm_success;
- if success then
- success.finish(); -- Finish enable/resume and sync stanzas
- end
-end, -1100);
-
-module:hook("sasl2/c2s/failure", function (event)
- event.session.sasl2_sm_request = nil;
-end);
diff --git a/server/prosody.cfg.lua b/server/prosody.cfg.lua
index 2d14a9fca..9fd2a7749 100644
--- a/server/prosody.cfg.lua
+++ b/server/prosody.cfg.lua
@@ -4,7 +4,9 @@
local lfs = require "lfs";
-plugin_paths = { "modules" }
+-- plugin_paths = { "modules" }
+plugin_server = "https://modules.prosody.im/rocks/"
+installer_plugin_path = lfs.currentdir() .. "/modules";
modules_enabled = {
"roster";