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