2 local st = require "util.stanza";
3 local send = require "core.sessionmanager".send_to_session;
4 local sm_bind_resource = require "core.sessionmanager".bind_resource;
7 local usermanager_validate_credentials = require "core.usermanager".validate_credentials;
8 local t_concat, t_insert = table.concat, table.insert;
9 local tostring = tostring;
11 local log = require "util.logger".init("mod_saslauth");
13 local xmlns_sasl ='urn:ietf:params:xml:ns:xmpp-sasl';
14 local xmlns_bind ='urn:ietf:params:xml:ns:xmpp-bind';
15 local xmlns_stanzas ='urn:ietf:params:xml:ns:xmpp-stanzas';
17 local new_sasl = require "util.sasl".new;
19 local function build_reply(status, ret)
20 local reply = st.stanza(status, {xmlns = xmlns_sasl});
21 if status == "challenge" then
22 reply:text(ret or "");
23 elseif status == "failure" then
25 elseif status == "success" then
26 reply:text(ret or "");
28 error("Unknown sasl status: "..status);
33 local function handle_status(session, status)
34 if status == "failure" then
35 session.sasl_handler = nil;
36 elseif status == "success" then
37 session.sasl_handler = nil;
38 session:reset_stream();
42 local function password_callback(jid, mechanism)
43 local node, host = jid_split(jid);
44 local password = (datamanager.load(node, host, "accounts") or {}).password; -- FIXME handle hashed passwords
45 local func = function(x) return x; end;
47 if mechanism == "PLAIN" then
48 return func, password;
49 elseif mechanism == "DIGEST-MD5" then
50 return func, require "hashes".md5(node.."::"..password);
56 function do_sasl(session, stanza)
57 local text = stanza[1];
59 text = base64.decode(text);
61 session.sasl_handler = nil;
62 session.send(build_reply("failure", "incorrect-encoding"));
66 local status, ret = session.sasl_handler:feed(text);
67 handle_status(session, status);
68 session.send(build_reply(status, ret));
71 add_handler("c2s_unauthed", "auth", xmlns_sasl,
72 function (session, stanza)
73 if not session.sasl_handler then
74 session.sasl_handler = new_sasl(stanza.attr.mechanism, session.host, password_callback);
75 do_sasl(session, stanza);
77 error("Client tried to negotiate SASL again", 0);
81 add_handler("c2s_unauthed", "abort", xmlns_sasl,
82 function(session, stanza)
83 if not session.sasl_handler then error("Attempt to abort when sasl has not started"); end
84 do_sasl(session, stanza);
87 add_handler("c2s_unauthed", "response", xmlns_sasl,
88 function(session, stanza)
89 if not session.sasl_handler then error("Attempt to respond when sasl has not started"); end
90 do_sasl(session, stanza);
93 add_event_hook("stream-features",
94 function (session, features)
95 if not session.username then
96 t_insert(features, "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>");
97 t_insert(features, "<mechanism>PLAIN</mechanism>");
98 t_insert(features, "</mechanisms>");
100 t_insert(features, "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><required/></bind>");
101 t_insert(features, "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>");
103 --send [[<register xmlns="http://jabber.org/features/iq-register"/> ]]
106 add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-bind",
107 function (session, stanza)
108 log("debug", "Client tried to bind to a resource");
110 if stanza.attr.type == "set" then
111 local bind = stanza.tags[1];
113 if bind and bind.attr.xmlns == xmlns_bind then
114 resource = bind:child_with_name("resource");
116 resource = resource[1];
120 local success, err = sm_bind_resource(session, resource);
122 local reply = st.reply(stanza);
123 reply.attr.type = "error";
124 if err == "conflict" then
125 reply:tag("error", { type = "modify" })
126 :tag("conflict", { xmlns = xmlns_stanzas });
127 elseif err == "constraint" then
128 reply:tag("error", { type = "cancel" })
129 :tag("resource-constraint", { xmlns = xmlns_stanzas });
130 elseif err == "auth" then
131 reply:tag("error", { type = "cancel" })
132 :tag("not-allowed", { xmlns = xmlns_stanzas });
134 send(session, reply);
136 local reply = st.reply(stanza);
137 reply:tag("bind", { xmlns = xmlns_bind})
138 :tag("jid"):text(session.full_jid);
139 send(session, reply);
143 add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-session",
144 function (session, stanza)
145 log("debug", "Client tried to bind to a resource");
146 send(session, st.reply(stanza));