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); |