Software / code / prosody
Comparison
plugins/mod_saslauth.lua @ 3651:337391d34b70
s2s: SASL EXTERNAL
| author | Paul Aurich <paul@darkrain42.org> |
|---|---|
| date | Sun, 21 Nov 2010 21:10:43 -0800 |
| parent | 3553:1f0af8572f15 |
| child | 3733:26571a99f6e6 |
comparison
equal
deleted
inserted
replaced
| 3650:2b80450bd7ae | 3651:337391d34b70 |
|---|---|
| 9 | 9 |
| 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 s2s_make_authenticated = require "core.s2smanager".make_authenticated; | |
| 14 local base64 = require "util.encodings".base64; | 15 local base64 = require "util.encodings".base64; |
| 16 | |
| 17 local cert_verify_identity = require "util.certverification".verify_identity; | |
| 15 | 18 |
| 16 local nodeprep = require "util.encodings".stringprep.nodeprep; | 19 local nodeprep = require "util.encodings".stringprep.nodeprep; |
| 17 local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; | 20 local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; |
| 18 local t_concat, t_insert = table.concat, table.insert; | 21 local t_concat, t_insert = table.concat, table.insert; |
| 19 local tostring = tostring; | 22 local tostring = tostring; |
| 89 log("debug", "sasl reply: %s", tostring(s)); | 92 log("debug", "sasl reply: %s", tostring(s)); |
| 90 session.send(s); | 93 session.send(s); |
| 91 return true; | 94 return true; |
| 92 end | 95 end |
| 93 | 96 |
| 97 module:hook_stanza(xmlns_sasl, "success", function (session, stanza) | |
| 98 if session.type ~= "s2sout_unauthed" or session.external_auth ~= "attempting" then return; end | |
| 99 module:log("debug", "SASL EXTERNAL with %s succeeded", session.to_host); | |
| 100 session.external_auth = "succeeded" | |
| 101 session:reset_stream(); | |
| 102 | |
| 103 local default_stream_attr = {xmlns = "jabber:server", ["xmlns:stream"] = "http://etherx.jabber.org/streams", | |
| 104 ["xmlns:db"] = 'jabber:server:dialback', version = "1.0", to = session.to_host, from = session.from_host}; | |
| 105 session.sends2s("<?xml version='1.0'?>"); | |
| 106 session.sends2s(st.stanza("stream:stream", default_stream_attr):top_tag()); | |
| 107 | |
| 108 s2s_make_authenticated(session, session.to_host); | |
| 109 return true; | |
| 110 end) | |
| 111 | |
| 112 module:hook_stanza(xmlns_sasl, "failure", function (session, stanza) | |
| 113 if session.type ~= "s2sout_unauthed" or session.external_auth ~= "attempting" then return; end | |
| 114 | |
| 115 module:log("info", "SASL EXTERNAL with %s failed", session.to_host) | |
| 116 -- TODO: Log the failure reason | |
| 117 session.external_auth = "failed" | |
| 118 end, 500) | |
| 119 | |
| 120 module:hook_stanza(xmlns_sasl, "failure", function (session, stanza) | |
| 121 -- TODO: Dialback wasn't loaded. Do something useful. | |
| 122 end, 90) | |
| 123 | |
| 124 module:hook_stanza("http://etherx.jabber.org/streams", "features", function (session, stanza) | |
| 125 if session.type ~= "s2sout_unauthed" or not session.secure then return; end | |
| 126 | |
| 127 local mechanisms = stanza:get_child("mechanisms", xmlns_sasl) | |
| 128 if mechanisms then | |
| 129 for mech in mechanisms:childtags() do | |
| 130 if mech[1] == "EXTERNAL" then | |
| 131 module:log("debug", "Initiating SASL EXTERNAL with %s", session.to_host); | |
| 132 local reply = st.stanza("auth", {xmlns = xmlns_sasl, mechanism = "EXTERNAL"}); | |
| 133 reply:text(base64.encode(session.from_host)) | |
| 134 session.sends2s(reply) | |
| 135 session.external_auth = "attempting" | |
| 136 return true | |
| 137 end | |
| 138 end | |
| 139 end | |
| 140 end, 150); | |
| 141 | |
| 142 local function s2s_external_auth(session, stanza) | |
| 143 local mechanism = stanza.attr.mechanism; | |
| 144 | |
| 145 if not session.secure then | |
| 146 if mechanism == "EXTERNAL" then | |
| 147 session.sends2s(build_reply("failure", "encryption-required")) | |
| 148 else | |
| 149 session.sends2s(build_reply("failure", "invalid-mechanism")) | |
| 150 end | |
| 151 return true; | |
| 152 end | |
| 153 | |
| 154 if mechanism ~= "EXTERNAL" or session.cert_chain_status ~= "valid" then | |
| 155 session.sends2s(build_reply("failure", "invalid-mechanism")) | |
| 156 return true; | |
| 157 end | |
| 158 | |
| 159 local text = stanza[1] | |
| 160 if not text then | |
| 161 session.sends2s(build_reply("failure", "malformed-request")) | |
| 162 return true | |
| 163 end | |
| 164 | |
| 165 -- Either the value is "=" and we've already verified the external | |
| 166 -- cert identity, or the value is a string and either matches the | |
| 167 -- from_host ( | |
| 168 | |
| 169 text = base64.decode(text) | |
| 170 if not text then | |
| 171 session.sends2s(build_reply("failure", "incorrect-encoding")) | |
| 172 return true; | |
| 173 end | |
| 174 | |
| 175 if session.cert_identity_status == "valid" then | |
| 176 if text ~= "" and text ~= session.from_host then | |
| 177 session.sends2s(build_reply("failure", "invalid-authzid")) | |
| 178 return true | |
| 179 end | |
| 180 else | |
| 181 if text == "" then | |
| 182 session.sends2s(build_reply("failure", "invalid-authzid")) | |
| 183 return true | |
| 184 end | |
| 185 | |
| 186 local cert = session.conn:socket():getpeercertificate() | |
| 187 if (cert_verify_identity(text, "xmpp-server", cert)) then | |
| 188 session.cert_identity_status = "valid" | |
| 189 else | |
| 190 session.cert_identity_status = "invalid" | |
| 191 session.sends2s(build_reply("failure", "invalid-authzid")) | |
| 192 return true | |
| 193 end | |
| 194 end | |
| 195 | |
| 196 session.external_auth = "succeeded" | |
| 197 | |
| 198 if not session.from_host then | |
| 199 session.from_host = text; | |
| 200 end | |
| 201 session.sends2s(build_reply("success")) | |
| 202 module:log("info", "Accepting SASL EXTERNAL identity from %s", text or session.from_host); | |
| 203 s2s_make_authenticated(session, text or session.from_host) | |
| 204 session:reset_stream(); | |
| 205 return true | |
| 206 end | |
| 207 | |
| 94 module:hook("stanza/urn:ietf:params:xml:ns:xmpp-sasl:auth", function(event) | 208 module:hook("stanza/urn:ietf:params:xml:ns:xmpp-sasl:auth", function(event) |
| 95 local session, stanza = event.origin, event.stanza; | 209 local session, stanza = event.origin, event.stanza; |
| 210 if session.type == "s2sin_unauthed" then | |
| 211 return s2s_external_auth(session, stanza) | |
| 212 end | |
| 213 | |
| 96 if session.type ~= "c2s_unauthed" then return; end | 214 if session.type ~= "c2s_unauthed" then return; end |
| 97 | 215 |
| 98 if session.sasl_handler and session.sasl_handler.selected then | 216 if session.sasl_handler and session.sasl_handler.selected then |
| 99 session.sasl_handler = nil; -- allow starting a new SASL negotiation before completing an old one | 217 session.sasl_handler = nil; -- allow starting a new SASL negotiation before completing an old one |
| 100 end | 218 end |
| 163 end | 281 end |
| 164 features:up(); | 282 features:up(); |
| 165 else | 283 else |
| 166 features:tag("bind", bind_attr):tag("required"):up():up(); | 284 features:tag("bind", bind_attr):tag("required"):up():up(); |
| 167 features:tag("session", xmpp_session_attr):tag("optional"):up():up(); | 285 features:tag("session", xmpp_session_attr):tag("optional"):up():up(); |
| 286 end | |
| 287 end); | |
| 288 | |
| 289 module:hook("s2s-stream-features", function(event) | |
| 290 local origin, features = event.origin, event.features; | |
| 291 if origin.secure and origin.type == "s2sin_unauthed" then | |
| 292 -- Offer EXTERNAL if chain is valid and either we didn't validate | |
| 293 -- the identity or it passed. | |
| 294 if origin.cert_chain_status == "valid" and origin.cert_identity_status ~= "invalid" then --TODO: Configurable | |
| 295 module:log("debug", "Offering SASL EXTERNAL") | |
| 296 features:tag("mechanisms", { xmlns = xmlns_sasl }) | |
| 297 :tag("mechanism"):text("EXTERNAL") | |
| 298 :up():up(); | |
| 299 end | |
| 168 end | 300 end |
| 169 end); | 301 end); |
| 170 | 302 |
| 171 module:hook("iq/self/urn:ietf:params:xml:ns:xmpp-bind:bind", function(event) | 303 module:hook("iq/self/urn:ietf:params:xml:ns:xmpp-bind:bind", function(event) |
| 172 local origin, stanza = event.origin, event.stanza; | 304 local origin, stanza = event.origin, event.stanza; |