Mercurial > prosody-modules
comparison mod_saslauth_muc/mod_saslauth_muc.lua @ 284:3b96bba9f7e5
mod_saslauth_muc: Initial commit. Implements SASL auth for MUC rooms <http://xmpp.org/extensions/inbox/remote-auth.html>.
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Thu, 02 Dec 2010 17:22:34 +0500 |
parents | |
children | 6144fe6161f1 |
comparison
equal
deleted
inserted
replaced
283:10c3f6c6a04c | 284:3b96bba9f7e5 |
---|---|
1 -- | |
2 -- mod_saslauth_muc | |
3 -- This module implements http://xmpp.org/extensions/inbox/remote-auth.html for Prosody's MUC component | |
4 -- | |
5 -- In your config: | |
6 -- Component "conference.example.com" "muc" | |
7 -- modules_enabled = { "saslauth_muc" }; | |
8 -- | |
9 -- | |
10 | |
11 local timeout = 60; -- SASL timeout in seconds | |
12 | |
13 -- various imports | |
14 local new_sasl = require "util.sasl".new; | |
15 local st = require "util.stanza"; | |
16 local timer = require "util.timer"; | |
17 | |
18 local jid_bare = require "util.jid".bare; | |
19 local jid_prep = require "util.jid".prep; | |
20 local base64 = require "util.encodings".base64; | |
21 | |
22 local hosts = hosts; | |
23 local module = module; | |
24 local pairs, next = pairs, next; | |
25 local os_time = os.time; | |
26 | |
27 -- SASL sessions management | |
28 local _rooms = {}; -- SASL data | |
29 local function get_handler_for(room, jid) return _rooms[room] and _rooms[room][jid]; end | |
30 local function remove_handler_for(room, jid) if _rooms[room] then _rooms[room][jid] = nil; end end | |
31 local function create_handler_for(room_jid, jid) | |
32 _rooms[room_jid] = _rooms[room_jid] or {}; | |
33 _rooms[room_jid][jid] = new_sasl(module.host, { plain = function(username, realm) | |
34 local muc = hosts[module.host].modules.muc; | |
35 local room = muc and muc.rooms[room_jid]; | |
36 local password = room and room:get_password(); | |
37 local ret = password and true or false; | |
38 return password, true; | |
39 end }); | |
40 _rooms[room_jid][jid].timeout = os_time() + timeout; | |
41 return _rooms[room_jid][jid]; | |
42 end | |
43 | |
44 -- Timer to clear SASL sessions | |
45 timer.add_task(timeout, function() | |
46 local now = os_time(); | |
47 for room, handlers in pairs(_rooms) do | |
48 for jid, handler in pairs(handlers) do | |
49 if handler.timeout <= now then handlers[jid] = nil; end | |
50 end | |
51 if next(handlers) == nil then _rooms[room] = nil; end | |
52 end | |
53 return timeout; | |
54 end); | |
55 | |
56 -- Stanza handlers | |
57 module:hook("presence/full", function(event) | |
58 local origin, stanza = event.origin, event.stanza; | |
59 | |
60 if not stanza.attr.type then -- available presence | |
61 local room_jid = jid_bare(stanza.attr.to); | |
62 local room = hosts[module.host].modules.muc.rooms[room_jid]; | |
63 | |
64 if room and not room:get_role(stanza.attr.from) then -- this is a room join | |
65 if room:get_password() then -- room has a password | |
66 local x = stanza:get_child("x", "http://jabber.org/protocol/muc"); | |
67 local password = x and x:get_child("password"); | |
68 if not password then -- no password sent | |
69 local sasl_handler = get_handler_for(jid_bare(stanza.attr.to), stanza.attr.from); | |
70 if x and sasl_handler and sasl_handler.authorized then -- if already passed SASL | |
71 x:reset():tag("password", { xmlns = "http://jabber.org/protocol/muc" }):text(room:get_password()); | |
72 else | |
73 origin.send(st.error_reply(stanza, "auth", "not-authorized") | |
74 :tag("sasl-required", { xmlns = "urn:xmpp:errors" })); | |
75 return true; | |
76 end | |
77 end | |
78 end | |
79 end | |
80 end | |
81 end, 10); | |
82 | |
83 module:hook("iq-get/bare/urn:ietf:params:xml:ns:xmpp-sasl:mechanisms", function(event) | |
84 local origin, stanza = event.origin, event.stanza; | |
85 | |
86 local reply = st.reply(stanza):tag("mechanisms", { xmlns='urn:ietf:params:xml:ns:xmpp-sasl' }); | |
87 for mechanism in pairs(create_handler_for(stanza.attr.to, true):mechanisms()) do | |
88 reply:tag("mechanism"):text(mechanism):up(); | |
89 end | |
90 origin.send(reply:up()); | |
91 return true; | |
92 end); | |
93 | |
94 local function build_reply(stanza, status, ret, err_msg) | |
95 local reply = st.stanza(status, {xmlns = "urn:ietf:params:xml:ns:xmpp-sasl"}); | |
96 if status == "challenge" then | |
97 reply:text(base64.encode(ret or "")); | |
98 elseif status == "failure" then | |
99 reply:tag(ret):up(); | |
100 if err_msg then reply:tag("text"):text(err_msg); end | |
101 elseif status == "success" then | |
102 reply:text(base64.encode(ret or "")); | |
103 else | |
104 module:log("error", "Unknown sasl status: %s", status); | |
105 end | |
106 return st.reply(stanza):add_child(reply); | |
107 end | |
108 local function handle_status(stanza, status) | |
109 if status == "failure" then | |
110 remove_handler_for(stanza.attr.to, stanza.attr.from); | |
111 elseif status == "success" then | |
112 get_handler_for(stanza.attr.to, stanza.attr.from).authorized = true; | |
113 end | |
114 end | |
115 local function sasl_process_cdata(session, stanza) | |
116 local text = stanza.tags[1][1]; | |
117 if text then | |
118 text = base64.decode(text); | |
119 if not text then | |
120 remove_handler_for(stanza.attr.to, stanza.attr.from); | |
121 session.send(build_reply(stanza, "failure", "incorrect-encoding")); | |
122 return true; | |
123 end | |
124 end | |
125 local status, ret, err_msg = get_handler_for(stanza.attr.to, stanza.attr.from):process(text); | |
126 handle_status(stanza, status); | |
127 local s = build_reply(stanza, status, ret, err_msg); | |
128 session.send(s); | |
129 return true; | |
130 end | |
131 | |
132 module:hook("iq-set/bare/urn:ietf:params:xml:ns:xmpp-sasl:auth", function(event) | |
133 local session, stanza = event.origin, event.stanza; | |
134 | |
135 if not create_handler_for(stanza.attr.to, stanza.attr.from):select(stanza.tags[1].attr.mechanism) then | |
136 remove_handler_for(stanza.attr.to, stanza.attr.from); | |
137 session.send(build_reply(stanza, "failure", "invalid-mechanism")); | |
138 return true; | |
139 end | |
140 return sasl_process_cdata(session, stanza); | |
141 end); | |
142 module:hook("iq-set/bare/urn:ietf:params:xml:ns:xmpp-sasl:response", function(event) | |
143 local session, stanza = event.origin, event.stanza; | |
144 if not get_handler_for(stanza.attr.to, stanza.attr.from) then | |
145 session.send(build_reply(stanza, "failure", "not-authorized", "Out of order SASL element")); | |
146 return true; | |
147 end | |
148 return sasl_process_cdata(session, event.stanza); | |
149 end); | |
150 module:hook("iq-set/bare/urn:ietf:params:xml:ns:xmpp-sasl:abort", function(event) | |
151 local session, stanza = event.origin, event.stanza; | |
152 remove_handler_for(stanza.attr.to, stanza.attr.from); | |
153 session.send(build_reply(stanza, "failure", "aborted")); | |
154 return true; | |
155 end); |