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 |