Software / code / prosody-modules
Comparison
mod_sasl2_bind2/mod_sasl2_bind2.lua @ 5029:56b9f0b1409f
mod_sasl2_bind: Support for Bind 2.0 with SASL2
This is based on an experimental in-progress derivative of the current
XEP-0386.
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Fri, 02 Sep 2022 16:22:11 +0100 |
| child | 5031:62cdd8170563 |
comparison
equal
deleted
inserted
replaced
| 5028:1f2d2bfd29dd | 5029:56b9f0b1409f |
|---|---|
| 1 local base64 = require "util.encodings".base64; | |
| 2 local sha1 = require "util.hashes".sha1; | |
| 3 local st = require "util.stanza"; | |
| 4 | |
| 5 local sm_bind_resource = require "core.sessionmanager".bind_resource; | |
| 6 | |
| 7 local xmlns_bind2 = "urn:xmpp:bind2:1"; | |
| 8 local xmlns_sasl2 = "urn:xmpp:sasl2:1"; | |
| 9 | |
| 10 -- Advertise what we can do | |
| 11 | |
| 12 module:hook("stream-features", function(event) | |
| 13 local origin, features = event.origin, event.features; | |
| 14 | |
| 15 if origin.type ~= "c2s_unauthed" then | |
| 16 return; | |
| 17 end | |
| 18 | |
| 19 local inline = st.stanza("inline", { xmlns = xmlns_bind2 }); | |
| 20 module:fire_event("advertise-bind-features", { origin = origin, features = inline }); | |
| 21 features:add_direct_child(inline); | |
| 22 end, 1); | |
| 23 | |
| 24 module:hook("advertise-sasl-features", function(event) | |
| 25 event.features:tag("bind", { xmlns = xmlns_bind2 }):up(); | |
| 26 end, 1); | |
| 27 | |
| 28 -- Helper to actually bind a resource to a session | |
| 29 | |
| 30 local function do_bind(session, bind_request) | |
| 31 local resource; | |
| 32 | |
| 33 local client_id_tag = bind_request:get_child("client-id"); | |
| 34 local client_id = client_id_tag and client_id_tag:text() or session.client_id; | |
| 35 if client_id and client_id ~= "" then | |
| 36 local tag = client_id_tag and client_id_tag.attr.tag or "client"; | |
| 37 resource = ("%s~%s"):format(tag, base64.encode(sha1(client_id):sub(1, 9))); | |
| 38 end | |
| 39 | |
| 40 local success, err_type, err, err_msg = sm_bind_resource(session, resource); | |
| 41 if not success then | |
| 42 session.log("debug", "Resource bind failed: %s", err_msg or err); | |
| 43 return nil, { type = err_type, condition = err, text = err_msg }; | |
| 44 end | |
| 45 | |
| 46 session.log("debug", "Resource bound: %s", session.full_jid); | |
| 47 return st.stanza("bound", { xmlns = xmlns_bind2 }) | |
| 48 :text_tag("jid", session.full_jid) | |
| 49 end | |
| 50 | |
| 51 -- Enable inline features requested by the client | |
| 52 | |
| 53 local function enable_features(session, bind_request, bind_result) | |
| 54 local features = bind_request:get_child("features"); | |
| 55 if not features then return; end | |
| 56 module:fire_event("process-bind-features", { | |
| 57 session = session; | |
| 58 features = features; | |
| 59 result = bind_result; | |
| 60 }); | |
| 61 end | |
| 62 | |
| 63 -- SASL 2 integration | |
| 64 | |
| 65 module:hook_tag(xmlns_sasl2, "authenticate", function (session, auth) | |
| 66 -- Cache action for future processing (after auth success) | |
| 67 session.sasl2_bind_request = auth:child_with_ns(xmlns_bind2); | |
| 68 end, 100); | |
| 69 | |
| 70 module:hook("sasl2/c2s/success", function (event) | |
| 71 local session = event.session; | |
| 72 | |
| 73 local bind_request = session.sasl2_bind_request; | |
| 74 if not bind_request then return; end -- No bind requested | |
| 75 session.sasl2_bind_request = nil; | |
| 76 | |
| 77 local sm_success = event.sasl2_sm_success; | |
| 78 if sm_success and sm_success.type == "resumed" then | |
| 79 return; -- No need to bind a resource | |
| 80 end | |
| 81 | |
| 82 local bind_result, err = do_bind(session, bind_request); | |
| 83 if not bind_result then | |
| 84 bind_result = st.stanza("failed", { xmlns = xmlns_bind2 }) | |
| 85 :add_error(err); | |
| 86 else | |
| 87 enable_features(session, bind_request, bind_result); | |
| 88 end | |
| 89 | |
| 90 event.success:add_child(bind_result); | |
| 91 end, 100); | |
| 92 | |
| 93 -- Inline features | |
| 94 | |
| 95 module:hook("advertise-bind-features", function (event) | |
| 96 local features = event.features; | |
| 97 features:tag("feature", { var = "urn:xmpp:carbons:2" }):up(); | |
| 98 features:tag("feature", { var = "urn:xmpp:csi:0" }):up(); | |
| 99 end); | |
| 100 | |
| 101 module:hook("enable-bind-features", function (event) | |
| 102 local session, features = event.session, event.features; | |
| 103 | |
| 104 -- Carbons | |
| 105 session.want_carbons = not not features:get_child("enable", "urn:xmpp:carbons:2"); | |
| 106 | |
| 107 -- CSI | |
| 108 local csi_state_tag = features:child_with_ns("urn:xmpp:csi:0"); | |
| 109 if csi_state_tag then | |
| 110 session.state = csi_state_tag.name; | |
| 111 end | |
| 112 end, 10); |