Software /
code /
verse
Comparison
plugins/sasl.lua @ 354:58cd27b74ba5
Almost a SASL framework, supports negotiation and challenge-response, mechanism code split out into util/sasl/
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Thu, 18 Sep 2014 18:59:34 +0200 |
parent | 315:3742107e2505 |
child | 358:a8f6fd6a70ed |
comparison
equal
deleted
inserted
replaced
353:8cd05c3d0f1f | 354:58cd27b74ba5 |
---|---|
1 local base64 = require "mime".b64; | 1 -- local verse = require"verse"; |
2 local base64, unbase64 = require "mime".b64, require"mime".unb64; | |
2 local xmlns_sasl = "urn:ietf:params:xml:ns:xmpp-sasl"; | 3 local xmlns_sasl = "urn:ietf:params:xml:ns:xmpp-sasl"; |
3 | 4 |
4 function verse.plugins.sasl(stream) | 5 function verse.plugins.sasl(stream) |
5 local function handle_features(features_stanza) | 6 local function handle_features(features_stanza) |
6 if stream.authenticated then return; end | 7 if stream.authenticated then return; end |
7 stream:debug("Authenticating with SASL..."); | 8 stream:debug("Authenticating with SASL..."); |
8 --stream.sasl_state, initial_data = sasl_new({"PLAIN"}, stream.username, stream.password, stream.jid); | 9 local sasl_mechanisms = features_stanza:get_child("mechanisms", xmlns_sasl); |
9 local mechanism , initial_data | 10 if not sasl_mechanisms then return end |
10 if stream.username then | 11 |
11 mechanism = "PLAIN" | 12 local mechanisms = {}; |
12 initial_data = base64("\0"..stream.username.."\0"..stream.password); | 13 local preference = {}; |
13 else | 14 |
14 mechanism = "ANONYMOUS" | 15 for mech in sasl_mechanisms:childtags("mechanism") do |
16 mech = mech:get_text(); | |
17 stream:debug("Server offers %s", mech); | |
18 if not mechanisms[mech] then | |
19 local name = mech:match("[^-]+"); | |
20 local ok, impl = pcall(require, "util.sasl."..name:lower()); | |
21 if ok then | |
22 stream:debug("Loaded SASL %s module", name); | |
23 impl(stream, mechanisms, preference); | |
24 elseif not tostring(impl):match("not found") then | |
25 stream:debug("Loading failed: %s", tostring(impl)); | |
26 end | |
27 end | |
15 end | 28 end |
16 stream:debug("Selecting %s mechanism...",mechanism); | 29 |
30 local supported = {}; -- by the server | |
31 for mech in pairs(mechanisms) do | |
32 table.insert(supported, mech); | |
33 end | |
34 if not supported[1] then | |
35 stream:event("authentication-failure", { condition = "no-supported-sasl-mechanisms" }); | |
36 stream:close(); | |
37 return; | |
38 end | |
39 table.sort(supported, function (a, b) return preference[a] > preference[b]; end); | |
40 local mechanism, initial_data = supported[1]; | |
41 stream:debug("Selecting %s mechanism...", mechanism); | |
42 stream.sasl_mechanism = coroutine.wrap(mechanisms[mechanism]); | |
43 initial_data = stream:sasl_mechanism(mechanism); | |
17 local auth_stanza = verse.stanza("auth", { xmlns = xmlns_sasl, mechanism = mechanism }); | 44 local auth_stanza = verse.stanza("auth", { xmlns = xmlns_sasl, mechanism = mechanism }); |
18 if initial_data then | 45 if initial_data then |
19 auth_stanza:text(initial_data); | 46 auth_stanza:text(base64(initial_data)); |
20 end | 47 end |
21 stream:send(auth_stanza); | 48 stream:send(auth_stanza); |
22 return true; | 49 return true; |
23 end | 50 end |
24 | 51 |
25 local function handle_sasl(sasl_stanza) | 52 local function handle_sasl(sasl_stanza) |
26 if sasl_stanza.name == "success" then | 53 if sasl_stanza.name == "failure" then |
27 stream.authenticated = true; | |
28 stream:event("authentication-success"); | |
29 elseif sasl_stanza.name == "failure" then | |
30 local err = sasl_stanza.tags[1]; | 54 local err = sasl_stanza.tags[1]; |
31 local text = sasl_stanza:get_child_text("text"); | 55 local text = sasl_stanza:get_child_text("text"); |
32 stream:event("authentication-failure", { condition = err.name, text = text }); | 56 stream:event("authentication-failure", { condition = err.name, text = text }); |
57 stream:close(); | |
58 return false; | |
33 end | 59 end |
34 stream:reopen(); | 60 local ok, err = stream.sasl_mechanism(sasl_stanza.name, unbase64(sasl_stanza:get_text())); |
61 if not ok then | |
62 stream:event("authentication-failure", { condition = err }); | |
63 stream:close(); | |
64 return false; | |
65 elseif ok == true then | |
66 stream:event("authentication-success"); | |
67 stream.authenticated = true | |
68 stream:reopen(); | |
69 else | |
70 stream:send(verse.stanza("response", { xmlns = xmlns_sasl }):text(base64(ok))); | |
71 end | |
35 return true; | 72 return true; |
36 end | 73 end |
37 | 74 |
38 stream:hook("stream-features", handle_features, 300); | 75 stream:hook("stream-features", handle_features, 300); |
39 stream:hook("stream/"..xmlns_sasl, handle_sasl); | 76 stream:hook("stream/"..xmlns_sasl, handle_sasl); |