Mercurial > prosody-modules
comparison mod_sasl2/mod_sasl2.lua @ 3905:5ae2e865eea0
mod_sasl2: Experimental implementation of XEP-0388
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sat, 28 Sep 2019 00:16:13 +0200 |
| parents | |
| children | 9d57aa79c5d9 |
comparison
equal
deleted
inserted
replaced
| 3904:d14fc974efbc | 3905:5ae2e865eea0 |
|---|---|
| 1 -- Prosody IM | |
| 2 -- Copyright (C) 2019 Kim Alvefur | |
| 3 -- | |
| 4 -- This project is MIT/X11 licensed. Please see the | |
| 5 -- COPYING file in the source package for more information. | |
| 6 -- | |
| 7 -- XEP-0388: Extensible SASL Profile | |
| 8 -- | |
| 9 | |
| 10 local st = require "util.stanza"; | |
| 11 local errors = require "util.error"; | |
| 12 local base64 = require "util.encodings".base64; | |
| 13 local jid_join = require "util.jid".join; | |
| 14 | |
| 15 local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; | |
| 16 local sm_make_authenticated = require "core.sessionmanager".make_authenticated; | |
| 17 | |
| 18 local xmlns_sasl2 = "urn:xmpp:sasl:1"; | |
| 19 | |
| 20 local allow_unencrypted_plain_auth = module:get_option_boolean("allow_unencrypted_plain_auth", false) | |
| 21 local insecure_mechanisms = module:get_option_set("insecure_sasl_mechanisms", allow_unencrypted_plain_auth and {} or {"PLAIN", "LOGIN"}); | |
| 22 local disabled_mechanisms = module:get_option_set("disable_sasl_mechanisms", { "DIGEST-MD5" }); | |
| 23 | |
| 24 local host = module.host; | |
| 25 | |
| 26 local function tls_unique(self) | |
| 27 return self.userdata["tls-unique"]:getpeerfinished(); | |
| 28 end | |
| 29 | |
| 30 module:hook("stream-features", function(event) | |
| 31 local origin, features = event.origin, event.features; | |
| 32 local log = origin.log or module._log; | |
| 33 | |
| 34 if origin.type ~= "c2s_unauthed" then | |
| 35 log("debug", "Already authenticated"); | |
| 36 return | |
| 37 end | |
| 38 | |
| 39 local sasl_handler = usermanager_get_sasl_handler(host, origin) | |
| 40 origin.sasl_handler = sasl_handler; | |
| 41 | |
| 42 if sasl_handler.add_cb_handler then | |
| 43 local socket = origin.conn:socket(); | |
| 44 if socket.getpeerfinished then | |
| 45 sasl_handler:add_cb_handler("tls-unique", tls_unique); | |
| 46 end | |
| 47 sasl_handler["userdata"] = { | |
| 48 ["tls-unique"] = socket; | |
| 49 }; | |
| 50 end | |
| 51 | |
| 52 local mechanisms = st.stanza("mechanisms", { xmlns = xmlns_sasl2 }); | |
| 53 | |
| 54 local available_mechanisms = sasl_handler:mechanisms() | |
| 55 for mechanism in pairs(available_mechanisms) do | |
| 56 if disabled_mechanisms:contains(mechanism) then | |
| 57 log("debug", "Not offering disabled mechanism %s", mechanism); | |
| 58 elseif not origin.secure and insecure_mechanisms:contains(mechanism) then | |
| 59 log("debug", "Not offering mechanism %s on insecure connection", mechanism); | |
| 60 else | |
| 61 log("debug", "Offering mechanism %s", mechanism); | |
| 62 mechanisms:text_tag("mechanism", mechanism); | |
| 63 end | |
| 64 end | |
| 65 | |
| 66 features:add_direct_child(mechanisms); | |
| 67 end, 1); | |
| 68 | |
| 69 local function handle_status(session, status, ret, err_msg) | |
| 70 local err = nil; | |
| 71 if status == "error" then | |
| 72 ret, err = nil, ret; | |
| 73 if not errors.is_err(err) then | |
| 74 err = errors.new({ condition = err, text = err_msg }, { session = session }); | |
| 75 end | |
| 76 end | |
| 77 | |
| 78 module:fire_event("sasl2/"..session.base_type.."/"..status, { | |
| 79 session = session, | |
| 80 message = ret; | |
| 81 error = err; | |
| 82 }); | |
| 83 end | |
| 84 | |
| 85 module:hook("sasl2/c2s/failure", function (event) | |
| 86 local session = event.session | |
| 87 session.send(st.stanza("failure", { xmlns = xmlns_sasl2 }) | |
| 88 :tag(event.error.condition)); | |
| 89 return true; | |
| 90 end); | |
| 91 | |
| 92 module:hook("sasl2/c2s/challenge", function (event) | |
| 93 local session = event.session; | |
| 94 session.send(st.stanza("challenge", { xmlns = xmlns_sasl2 }) | |
| 95 :text_tag(event.message)); | |
| 96 end); | |
| 97 | |
| 98 module:hook("sasl2/c2s/success", function (event) | |
| 99 local session = event.session | |
| 100 local ok, err = sm_make_authenticated(session, session.sasl_handler.username); | |
| 101 if not ok then | |
| 102 handle_status(session, "failure", err); | |
| 103 return true; | |
| 104 end | |
| 105 event.success = st.stanza("success", { xmlns = xmlns_sasl2 }); | |
| 106 end, 1000); | |
| 107 | |
| 108 module:hook("sasl2/c2s/success", function (event) | |
| 109 local session = event.session | |
| 110 event.success:text_tag("authorization-identifier", jid_join(session.username, session.host, session.resource)); | |
| 111 session.send(event.success); | |
| 112 local features = st.stanza("stream:features"); | |
| 113 module:fire_event("stream-features", { origin = session, features = features }); | |
| 114 session.send(features); | |
| 115 end, -1000); | |
| 116 | |
| 117 local function process_cdata(session, cdata) | |
| 118 if cdata then | |
| 119 cdata = base64.decode(cdata); | |
| 120 if not cdata then | |
| 121 return handle_status(session, "failure"); | |
| 122 end | |
| 123 end | |
| 124 return handle_status(session, session.sasl_handler:process(cdata)); | |
| 125 end | |
| 126 | |
| 127 module:hook_tag(xmlns_sasl2, "authenticate", function (session, auth) | |
| 128 local sasl_handler = session.sasl_handler; | |
| 129 if not sasl_handler then | |
| 130 sasl_handler = usermanager_get_sasl_handler(host, session); | |
| 131 session.sasl_handler = sasl_handler; | |
| 132 end | |
| 133 local mechanism = assert(auth.attr.mechanism); | |
| 134 if not sasl_handler:select(mechanism) then | |
| 135 return handle_status(session, "failure"); | |
| 136 end | |
| 137 local initial = auth:get_child_text("initial-response"); | |
| 138 return process_cdata(session, initial); | |
| 139 end); | |
| 140 | |
| 141 module:hook_tag(xmlns_sasl2, "response", function (session, response) | |
| 142 local sasl_handler = session.sasl_handler; | |
| 143 if not sasl_handler or not sasl_handler.selected then | |
| 144 return handle_status(session, "failure"); | |
| 145 end | |
| 146 return process_cdata(session, response:get_text()); | |
| 147 end); |
