Software / code / prosody
Comparison
plugins/mod_saslauth.lua @ 6425:436a670a0189
mod_saslauth: Stricter SASL EXTERNAL handling more in line with XEP-0178
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Tue, 23 Sep 2014 19:29:14 +0200 |
| parent | 6424:89c42aff8510 |
| child | 6427:7653bbd5247e |
comparison
equal
deleted
inserted
replaced
| 6424:89c42aff8510 | 6425:436a670a0189 |
|---|---|
| 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 | |
| 16 local cert_verify_identity = require "util.x509".verify_identity; | |
| 17 | 15 |
| 18 local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; | 16 local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; |
| 19 local tostring = tostring; | 17 local tostring = tostring; |
| 20 | 18 |
| 21 local secure_auth_only = module:get_option("c2s_require_encryption") or module:get_option("require_encryption"); | 19 local secure_auth_only = module:get_option("c2s_require_encryption") or module:get_option("require_encryption"); |
| 120 end | 118 end |
| 121 end | 119 end |
| 122 end, 150); | 120 end, 150); |
| 123 | 121 |
| 124 local function s2s_external_auth(session, stanza) | 122 local function s2s_external_auth(session, stanza) |
| 123 if session.external_auth ~= "offered" then return end -- Unexpected request | |
| 124 | |
| 125 local mechanism = stanza.attr.mechanism; | 125 local mechanism = stanza.attr.mechanism; |
| 126 | 126 |
| 127 if mechanism ~= "EXTERNAL" then | |
| 128 session.sends2s(build_reply("failure", "invalid-mechanism")); | |
| 129 return true; | |
| 130 end | |
| 131 | |
| 127 if not session.secure then | 132 if not session.secure then |
| 128 if mechanism == "EXTERNAL" then | 133 session.sends2s(build_reply("failure", "encryption-required")); |
| 129 session.sends2s(build_reply("failure", "encryption-required")) | 134 return true; |
| 130 else | 135 end |
| 131 session.sends2s(build_reply("failure", "invalid-mechanism")) | 136 |
| 132 end | 137 local text = stanza[1]; |
| 133 return true; | |
| 134 end | |
| 135 | |
| 136 if mechanism ~= "EXTERNAL" or session.cert_chain_status ~= "valid" then | |
| 137 session.sends2s(build_reply("failure", "invalid-mechanism")) | |
| 138 return true; | |
| 139 end | |
| 140 | |
| 141 local text = stanza[1] | |
| 142 if not text then | 138 if not text then |
| 143 session.sends2s(build_reply("failure", "malformed-request")) | 139 session.sends2s(build_reply("failure", "malformed-request")); |
| 144 return true | 140 return true; |
| 145 end | 141 end |
| 146 | 142 |
| 147 -- Either the value is "=" and we've already verified the external | 143 text = base64.decode(text); |
| 148 -- cert identity, or the value is a string and either matches the | |
| 149 -- from_host ( | |
| 150 | |
| 151 text = base64.decode(text) | |
| 152 if not text then | 144 if not text then |
| 153 session.sends2s(build_reply("failure", "incorrect-encoding")) | 145 session.sends2s(build_reply("failure", "incorrect-encoding")); |
| 154 return true; | 146 return true; |
| 155 end | 147 end |
| 156 | 148 |
| 157 if session.cert_identity_status == "valid" then | 149 -- The text value is either "" or equals session.from_host |
| 158 if text ~= "" and text ~= session.from_host then | 150 if not ( text == "" or text == session.from_host ) then |
| 159 session.sends2s(build_reply("failure", "invalid-authzid")) | 151 session.sends2s(build_reply("failure", "invalid-authzid")); |
| 160 return true | 152 return true; |
| 161 end | 153 end |
| 162 else | 154 |
| 163 if text == "" then | 155 -- We've already verified the external cert identity before offering EXTERNAL |
| 164 session.sends2s(build_reply("failure", "invalid-authzid")) | 156 if session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid" then |
| 165 return true | 157 session.sends2s(build_reply("failure", "not-authorized")); |
| 166 end | 158 session:close(); |
| 167 | 159 return true; |
| 168 local cert = session.conn:socket():getpeercertificate() | 160 end |
| 169 if (cert_verify_identity(text, "xmpp-server", cert)) then | 161 |
| 170 session.cert_identity_status = "valid" | 162 -- Success! |
| 171 else | 163 session.external_auth = "succeeded"; |
| 172 session.cert_identity_status = "invalid" | 164 session.sends2s(build_reply("success")); |
| 173 session.sends2s(build_reply("failure", "invalid-authzid")) | 165 module:log("info", "Accepting SASL EXTERNAL identity from %s", session.from_host); |
| 174 return true | 166 module:fire_event("s2s-authenticated", { session = session, host = session.from_host }); |
| 175 end | |
| 176 end | |
| 177 | |
| 178 session.external_auth = "succeeded" | |
| 179 | |
| 180 if not session.from_host then | |
| 181 session.from_host = text; | |
| 182 end | |
| 183 session.sends2s(build_reply("success")) | |
| 184 | |
| 185 local domain = text ~= "" and text or session.from_host; | |
| 186 module:log("info", "Accepting SASL EXTERNAL identity from %s", domain); | |
| 187 module:fire_event("s2s-authenticated", { session = session, host = domain }); | |
| 188 session:reset_stream(); | 167 session:reset_stream(); |
| 189 return true | 168 return true; |
| 190 end | 169 end |
| 191 | 170 |
| 192 module:hook("stanza/urn:ietf:params:xml:ns:xmpp-sasl:auth", function(event) | 171 module:hook("stanza/urn:ietf:params:xml:ns:xmpp-sasl:auth", function(event) |
| 193 local session, stanza = event.origin, event.stanza; | 172 local session, stanza = event.origin, event.stanza; |
| 194 if session.type == "s2sin_unauthed" then | 173 if session.type == "s2sin_unauthed" then |
| 264 end); | 243 end); |
| 265 | 244 |
| 266 module:hook("s2s-stream-features", function(event) | 245 module:hook("s2s-stream-features", function(event) |
| 267 local origin, features = event.origin, event.features; | 246 local origin, features = event.origin, event.features; |
| 268 if origin.secure and origin.type == "s2sin_unauthed" then | 247 if origin.secure and origin.type == "s2sin_unauthed" then |
| 269 -- Offer EXTERNAL if chain is valid and either we didn't validate | 248 -- Offer EXTERNAL only if both chain and identity is valid. |
| 270 -- the identity or it passed. | 249 if origin.cert_chain_status == "valid" and origin.cert_identity_status == "valid" then |
| 271 if origin.cert_chain_status == "valid" and origin.cert_identity_status ~= "invalid" then --TODO: Configurable | 250 module:log("debug", "Offering SASL EXTERNAL"); |
| 272 module:log("debug", "Offering SASL EXTERNAL") | 251 origin.external_auth = "offered" |
| 273 features:tag("mechanisms", { xmlns = xmlns_sasl }) | 252 features:tag("mechanisms", { xmlns = xmlns_sasl }) |
| 274 :tag("mechanism"):text("EXTERNAL") | 253 :tag("mechanism"):text("EXTERNAL") |
| 275 :up():up(); | 254 :up():up(); |
| 276 end | 255 end |
| 277 end | 256 end |