Software /
code /
prosody
Comparison
plugins/mod_saslauth.lua @ 11200:bf8f2da84007
Merge 0.11->trunk
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Thu, 05 Nov 2020 22:31:25 +0100 |
parent | 10643:417eadd0f567 |
child | 11214:5fb6563eee1e |
comparison
equal
deleted
inserted
replaced
11199:6c7c50a4de32 | 11200:bf8f2da84007 |
---|---|
10 | 10 |
11 local st = require "util.stanza"; | 11 local st = require "util.stanza"; |
12 local sm_bind_resource = require "core.sessionmanager".bind_resource; | 12 local sm_bind_resource = require "core.sessionmanager".bind_resource; |
13 local sm_make_authenticated = require "core.sessionmanager".make_authenticated; | 13 local sm_make_authenticated = require "core.sessionmanager".make_authenticated; |
14 local base64 = require "util.encodings".base64; | 14 local base64 = require "util.encodings".base64; |
15 local set = require "util.set"; | |
16 local errors = require "util.error"; | |
15 | 17 |
16 local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; | 18 local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; |
17 local tostring = tostring; | |
18 | 19 |
19 local secure_auth_only = module:get_option_boolean("c2s_require_encryption", module:get_option_boolean("require_encryption", false)); | 20 local secure_auth_only = module:get_option_boolean("c2s_require_encryption", module:get_option_boolean("require_encryption", false)); |
20 local allow_unencrypted_plain_auth = module:get_option_boolean("allow_unencrypted_plain_auth", false) | 21 local allow_unencrypted_plain_auth = module:get_option_boolean("allow_unencrypted_plain_auth", false) |
21 local insecure_mechanisms = module:get_option_set("insecure_sasl_mechanisms", allow_unencrypted_plain_auth and {} or {"PLAIN", "LOGIN"}); | 22 local insecure_mechanisms = module:get_option_set("insecure_sasl_mechanisms", allow_unencrypted_plain_auth and {} or {"PLAIN", "LOGIN"}); |
22 local disabled_mechanisms = module:get_option_set("disable_sasl_mechanisms", { "DIGEST-MD5" }); | 23 local disabled_mechanisms = module:get_option_set("disable_sasl_mechanisms", { "DIGEST-MD5" }); |
46 local function handle_status(session, status, ret, err_msg) | 47 local function handle_status(session, status, ret, err_msg) |
47 if status == "failure" then | 48 if status == "failure" then |
48 module:fire_event("authentication-failure", { session = session, condition = ret, text = err_msg }); | 49 module:fire_event("authentication-failure", { session = session, condition = ret, text = err_msg }); |
49 session.sasl_handler = session.sasl_handler:clean_clone(); | 50 session.sasl_handler = session.sasl_handler:clean_clone(); |
50 elseif status == "success" then | 51 elseif status == "success" then |
51 local ok, err = sm_make_authenticated(session, session.sasl_handler.username); | 52 local ok, err = sm_make_authenticated(session, session.sasl_handler.username, session.sasl_handler.scope); |
52 if ok then | 53 if ok then |
53 module:fire_event("authentication-success", { session = session }); | 54 module:fire_event("authentication-success", { session = session }); |
54 session.sasl_handler = nil; | 55 session.sasl_handler = nil; |
55 session:reset_stream(); | 56 session:reset_stream(); |
56 else | 57 else |
65 | 66 |
66 local function sasl_process_cdata(session, stanza) | 67 local function sasl_process_cdata(session, stanza) |
67 local text = stanza[1]; | 68 local text = stanza[1]; |
68 if text then | 69 if text then |
69 text = base64.decode(text); | 70 text = base64.decode(text); |
70 --log("debug", "AUTH: %s", text:gsub("[%z\001-\008\011\012\014-\031]", " ")); | |
71 if not text then | 71 if not text then |
72 session.sasl_handler = nil; | 72 session.sasl_handler = nil; |
73 session.send(build_reply("failure", "incorrect-encoding")); | 73 session.send(build_reply("failure", "incorrect-encoding")); |
74 return true; | 74 return true; |
75 end | 75 end |
76 end | 76 end |
77 local status, ret, err_msg = session.sasl_handler:process(text); | 77 local status, ret, err_msg = session.sasl_handler:process(text); |
78 status, ret, err_msg = handle_status(session, status, ret, err_msg); | 78 status, ret, err_msg = handle_status(session, status, ret, err_msg); |
79 local s = build_reply(status, ret, err_msg); | 79 local s = build_reply(status, ret, err_msg); |
80 log("debug", "sasl reply: %s", tostring(s)); | |
81 session.send(s); | 80 session.send(s); |
82 return true; | 81 return true; |
83 end | 82 end |
84 | 83 |
85 module:hook_tag(xmlns_sasl, "success", function (session) | 84 module:hook_tag(xmlns_sasl, "success", function (session) |
102 if child.name ~= "text" then | 101 if child.name ~= "text" then |
103 condition = child.name; | 102 condition = child.name; |
104 break; | 103 break; |
105 end | 104 end |
106 end | 105 end |
107 if text and condition then | 106 local err = errors.new({ |
108 condition = condition .. ": " .. text; | 107 -- TODO type = what? |
109 end | 108 text = text, |
110 module:log("info", "SASL EXTERNAL with %s failed: %s", session.to_host, condition); | 109 condition = condition, |
110 }, { | |
111 session = session, | |
112 stanza = stanza, | |
113 }); | |
114 | |
115 module:log("info", "SASL EXTERNAL with %s failed: %s", session.to_host, err); | |
111 | 116 |
112 session.external_auth = "failed" | 117 session.external_auth = "failed" |
113 session.external_auth_failure_reason = condition; | 118 session.external_auth_failure_reason = err; |
114 end, 500) | 119 end, 500) |
115 | 120 |
116 module:hook_tag(xmlns_sasl, "failure", function (session, stanza) -- luacheck: ignore 212/stanza | 121 module:hook_tag(xmlns_sasl, "failure", function (session, stanza) -- luacheck: ignore 212/stanza |
117 session.log("debug", "No fallback from SASL EXTERNAL failure, giving up"); | 122 session.log("debug", "No fallback from SASL EXTERNAL failure, giving up"); |
118 session:close(nil, session.external_auth_failure_reason); | 123 session:close(nil, session.external_auth_failure_reason, errors.new({ |
124 type = "wait", condition = "remote-server-timeout", | |
125 text = "Could not authenticate to remote server", | |
126 }, { session = session, sasl_failure = session.external_auth_failure_reason, })); | |
119 return true; | 127 return true; |
120 end, 90) | 128 end, 90) |
121 | 129 |
122 module:hook_tag("http://etherx.jabber.org/streams", "features", function (session, stanza) | 130 module:hook_tag("http://etherx.jabber.org/streams", "features", function (session, stanza) |
123 if session.type ~= "s2sout_unauthed" or not session.secure then return; end | 131 if session.type ~= "s2sout_unauthed" or not session.secure then return; end |
246 return; | 254 return; |
247 end | 255 end |
248 local sasl_handler = usermanager_get_sasl_handler(module.host, origin) | 256 local sasl_handler = usermanager_get_sasl_handler(module.host, origin) |
249 origin.sasl_handler = sasl_handler; | 257 origin.sasl_handler = sasl_handler; |
250 if origin.encrypted then | 258 if origin.encrypted then |
251 -- check wether LuaSec has the nifty binding to the function needed for tls-unique | 259 -- check whether LuaSec has the nifty binding to the function needed for tls-unique |
252 -- FIXME: would be nice to have this check only once and not for every socket | 260 -- FIXME: would be nice to have this check only once and not for every socket |
253 if sasl_handler.add_cb_handler then | 261 if sasl_handler.add_cb_handler then |
254 local socket = origin.conn:socket(); | 262 local socket = origin.conn:socket(); |
255 if socket.getpeerfinished then | 263 if socket.getpeerfinished then |
264 log("debug", "Channel binding 'tls-unique' supported"); | |
256 sasl_handler:add_cb_handler("tls-unique", tls_unique); | 265 sasl_handler:add_cb_handler("tls-unique", tls_unique); |
266 else | |
267 log("debug", "Channel binding 'tls-unique' not supported (by LuaSec?)"); | |
257 end | 268 end |
258 sasl_handler["userdata"] = { | 269 sasl_handler["userdata"] = { |
259 ["tls-unique"] = socket; | 270 ["tls-unique"] = socket; |
260 }; | 271 }; |
272 else | |
273 log("debug", "Channel binding not supported by SASL handler"); | |
261 end | 274 end |
262 end | 275 end |
263 local mechanisms = st.stanza("mechanisms", mechanisms_attr); | 276 local mechanisms = st.stanza("mechanisms", mechanisms_attr); |
264 local sasl_mechanisms = sasl_handler:mechanisms() | 277 local sasl_mechanisms = sasl_handler:mechanisms() |
278 local available_mechanisms = set.new(); | |
265 for mechanism in pairs(sasl_mechanisms) do | 279 for mechanism in pairs(sasl_mechanisms) do |
266 if disabled_mechanisms:contains(mechanism) then | 280 available_mechanisms:add(mechanism); |
267 log("debug", "Not offering disabled mechanism %s", mechanism); | 281 end |
268 elseif not origin.secure and insecure_mechanisms:contains(mechanism) then | 282 log("debug", "SASL mechanisms supported by handler: %s", available_mechanisms); |
269 log("debug", "Not offering mechanism %s on insecure connection", mechanism); | 283 |
270 else | 284 local usable_mechanisms = available_mechanisms - disabled_mechanisms; |
271 log("debug", "Offering mechanism %s", mechanism); | 285 |
286 local available_disabled = set.intersection(available_mechanisms, disabled_mechanisms); | |
287 if not available_disabled:empty() then | |
288 log("debug", "Not offering disabled mechanisms: %s", available_disabled); | |
289 end | |
290 | |
291 local available_insecure = set.intersection(available_mechanisms, insecure_mechanisms); | |
292 if not origin.secure and not available_insecure:empty() then | |
293 log("debug", "Session is not secure, not offering insecure mechanisms: %s", available_insecure); | |
294 usable_mechanisms = usable_mechanisms - insecure_mechanisms; | |
295 end | |
296 | |
297 if not usable_mechanisms:empty() then | |
298 log("debug", "Offering usable mechanisms: %s", usable_mechanisms); | |
299 for mechanism in usable_mechanisms do | |
272 mechanisms:tag("mechanism"):text(mechanism):up(); | 300 mechanisms:tag("mechanism"):text(mechanism):up(); |
273 end | 301 end |
274 end | |
275 if mechanisms[1] then | |
276 features:add_child(mechanisms); | 302 features:add_child(mechanisms); |
277 elseif not next(sasl_mechanisms) then | 303 return; |
278 log("warn", "No available SASL mechanisms, verify that the configured authentication module is working"); | 304 end |
279 else | 305 |
280 log("warn", "All available authentication mechanisms are either disabled or not suitable for an insecure connection"); | 306 local authmod = module:get_option_string("authentication", "internal_plain"); |
281 end | 307 if available_mechanisms:empty() then |
308 log("warn", "No available SASL mechanisms, verify that the configured authentication module '%s' is loaded and configured correctly", authmod); | |
309 return; | |
310 end | |
311 | |
312 if not origin.secure and not available_insecure:empty() then | |
313 if not available_disabled:empty() then | |
314 log("warn", "All SASL mechanisms provided by authentication module '%s' are forbidden on insecure connections (%s) or disabled (%s)", | |
315 authmod, available_insecure, available_disabled); | |
316 else | |
317 log("warn", "All SASL mechanisms provided by authentication module '%s' are forbidden on insecure connections (%s)", | |
318 authmod, available_insecure); | |
319 end | |
320 elseif not available_disabled:empty() then | |
321 log("warn", "All SASL mechanisms provided by authentication module '%s' are disabled (%s)", | |
322 authmod, available_disabled); | |
323 end | |
324 | |
282 else | 325 else |
283 features:tag("bind", bind_attr):tag("required"):up():up(); | 326 features:tag("bind", bind_attr):tag("required"):up():up(); |
284 features:tag("session", xmpp_session_attr):tag("optional"):up():up(); | 327 features:tag("session", xmpp_session_attr):tag("optional"):up():up(); |
285 end | 328 end |
286 end); | 329 end); |