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