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 add_handler("c2s_unauthed", "auth", xmlns_sasl,
57 function (session, stanza)
58 if not session.sasl_handler then
59 session.sasl_handler = new_sasl(stanza.attr.mechanism, session.host, password_callback);
60 local status, ret = session.sasl_handler:feed(stanza[1]);
61 handle_status(session, status);
62 session.send(build_reply(status, ret));
63 --[[session.sasl_handler = new_sasl(stanza.attr.mechanism,
64 function (username, password)
66 require "core.usermanager"
67 if usermanager_validate_credentials(session.host, username, password) then
74 local success, err = sessionmanager.make_authenticated(session, username);
76 sessionmanager.destroy_session(session);
79 session.sasl_handler = nil;
80 session:reset_stream();
84 log("debug", "SASL failure, reason: %s", reason);
88 log("debug", "SASL writes: %s", tostring(stanza));
89 send(session, stanza);
92 session.sasl_handler:feed(stanza); ]]
94 error("Client tried to negotiate SASL again", 0);
98 add_handler("c2s_unauthed", "abort", xmlns_sasl,
99 function(session, stanza)
100 if not session.sasl_handler then error("Attempt to abort when sasl has not started"); end
101 local status, ret = session.sasl_handler:feed(stanza[1]);
102 handle_status(session, status);
103 session.send(build_reply(status, ret));
106 add_handler("c2s_unauthed", "response", xmlns_sasl,
107 function(session, stanza)
108 if not session.sasl_handler then error("Attempt to respond when sasl has not started"); end
109 local status, ret = session.sasl_handler:feed(stanza[1]);
110 handle_status(session, status);
111 session.send(build_reply(status, ret));
114 add_event_hook("stream-features",
115 function (session, features)
116 if not session.username then
117 t_insert(features, "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>");
118 t_insert(features, "<mechanism>PLAIN</mechanism>");
119 t_insert(features, "<mechanism>DIGEST-MD5</mechanism>");
120 t_insert(features, "</mechanisms>");
122 t_insert(features, "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><required/></bind>");
123 t_insert(features, "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>");
125 --send [[<register xmlns="http://jabber.org/features/iq-register"/> ]]
128 add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-bind",
129 function (session, stanza)
130 log("debug", "Client tried to bind to a resource");
132 if stanza.attr.type == "set" then
133 local bind = stanza.tags[1];
135 if bind and bind.attr.xmlns == xmlns_bind then
136 resource = bind:child_with_name("resource");
138 resource = resource[1];
142 local success, err = sm_bind_resource(session, resource);
144 local reply = st.reply(stanza);
145 reply.attr.type = "error";
146 if err == "conflict" then
147 reply:tag("error", { type = "modify" })
148 :tag("conflict", { xmlns = xmlns_stanzas });
149 elseif err == "constraint" then
150 reply:tag("error", { type = "cancel" })
151 :tag("resource-constraint", { xmlns = xmlns_stanzas });
152 elseif err == "auth" then
153 reply:tag("error", { type = "cancel" })
154 :tag("not-allowed", { xmlns = xmlns_stanzas });
156 send(session, reply);
158 local reply = st.reply(stanza);
159 reply:tag("bind", { xmlns = xmlns_bind})
160 :tag("jid"):text(session.full_jid);
161 send(session, reply);
165 add_iq_handler("c2s", "urn:ietf:params:xml:ns:xmpp-session",
166 function (session, stanza)
167 log("debug", "Client tried to bind to a resource");
168 send(session, st.reply(stanza));