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