comparison plugins/mod_saslauth.lua @ 6428:3ee09bfe16e1

Merge 0.10->trunk
author Kim Alvefur <zash@zash.se>
date Tue, 23 Sep 2014 23:22:13 +0200
parents 7653bbd5247e
children edc63dc72566
comparison
equal deleted inserted replaced
6423:1c78f10f05d0 6428:3ee09bfe16e1
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 15
16 local cert_verify_identity = require "util.x509".verify_identity;
17
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");
22 local allow_unencrypted_plain_auth = module:get_option("allow_unencrypted_plain_auth") 20 local allow_unencrypted_plain_auth = module:get_option("allow_unencrypted_plain_auth")
26 local xmlns_sasl ='urn:ietf:params:xml:ns:xmpp-sasl'; 24 local xmlns_sasl ='urn:ietf:params:xml:ns:xmpp-sasl';
27 local xmlns_bind ='urn:ietf:params:xml:ns:xmpp-bind'; 25 local xmlns_bind ='urn:ietf:params:xml:ns:xmpp-bind';
28 26
29 local function build_reply(status, ret, err_msg) 27 local function build_reply(status, ret, err_msg)
30 local reply = st.stanza(status, {xmlns = xmlns_sasl}); 28 local reply = st.stanza(status, {xmlns = xmlns_sasl});
31 if status == "challenge" then 29 if status == "failure" then
32 --log("debug", "CHALLENGE: %s", ret or "");
33 reply:text(base64.encode(ret or ""));
34 elseif status == "failure" then
35 reply:tag(ret):up(); 30 reply:tag(ret):up();
36 if err_msg then reply:tag("text"):text(err_msg); end 31 if err_msg then reply:tag("text"):text(err_msg); end
37 elseif status == "success" then 32 elseif status == "challenge" or status == "success" then
38 --log("debug", "SUCCESS: %s", ret or ""); 33 if ret == "" then
39 reply:text(base64.encode(ret or "")); 34 reply:text("=")
35 elseif ret then
36 reply:text(base64.encode(ret));
37 end
40 else 38 else
41 module:log("error", "Unknown sasl status: %s", status); 39 module:log("error", "Unknown sasl status: %s", status);
42 end 40 end
43 return reply; 41 return reply;
44 end 42 end
97 if session.type ~= "s2sout_unauthed" or session.external_auth ~= "attempting" then return; end 95 if session.type ~= "s2sout_unauthed" or session.external_auth ~= "attempting" then return; end
98 96
99 module:log("info", "SASL EXTERNAL with %s failed", session.to_host) 97 module:log("info", "SASL EXTERNAL with %s failed", session.to_host)
100 -- TODO: Log the failure reason 98 -- TODO: Log the failure reason
101 session.external_auth = "failed" 99 session.external_auth = "failed"
100 session:close();
101 return true;
102 end, 500) 102 end, 500)
103
104 module:hook_stanza(xmlns_sasl, "failure", function (session, stanza)
105 -- TODO: Dialback wasn't loaded. Do something useful.
106 end, 90)
107 103
108 module:hook_stanza("http://etherx.jabber.org/streams", "features", function (session, stanza) 104 module:hook_stanza("http://etherx.jabber.org/streams", "features", function (session, stanza)
109 if session.type ~= "s2sout_unauthed" or not session.secure then return; end 105 if session.type ~= "s2sout_unauthed" or not session.secure then return; end
110 106
111 local mechanisms = stanza:get_child("mechanisms", xmlns_sasl) 107 local mechanisms = stanza:get_child("mechanisms", xmlns_sasl)
122 end 118 end
123 end 119 end
124 end, 150); 120 end, 150);
125 121
126 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
127 local mechanism = stanza.attr.mechanism; 125 local mechanism = stanza.attr.mechanism;
128 126
127 if mechanism ~= "EXTERNAL" then
128 session.sends2s(build_reply("failure", "invalid-mechanism"));
129 return true;
130 end
131
129 if not session.secure then 132 if not session.secure then
130 if mechanism == "EXTERNAL" then 133 session.sends2s(build_reply("failure", "encryption-required"));
131 session.sends2s(build_reply("failure", "encryption-required")) 134 return true;
132 else 135 end
133 session.sends2s(build_reply("failure", "invalid-mechanism")) 136
134 end 137 local text = stanza[1];
135 return true;
136 end
137
138 if mechanism ~= "EXTERNAL" or session.cert_chain_status ~= "valid" then
139 session.sends2s(build_reply("failure", "invalid-mechanism"))
140 return true;
141 end
142
143 local text = stanza[1]
144 if not text then 138 if not text then
145 session.sends2s(build_reply("failure", "malformed-request")) 139 session.sends2s(build_reply("failure", "malformed-request"));
146 return true 140 return true;
147 end 141 end
148 142
149 -- Either the value is "=" and we've already verified the external 143 text = base64.decode(text);
150 -- cert identity, or the value is a string and either matches the
151 -- from_host (
152
153 text = base64.decode(text)
154 if not text then 144 if not text then
155 session.sends2s(build_reply("failure", "incorrect-encoding")) 145 session.sends2s(build_reply("failure", "incorrect-encoding"));
156 return true; 146 return true;
157 end 147 end
158 148
159 if session.cert_identity_status == "valid" then 149 -- The text value is either "" or equals session.from_host
160 if text ~= "" and text ~= session.from_host then 150 if not ( text == "" or text == session.from_host ) then
161 session.sends2s(build_reply("failure", "invalid-authzid")) 151 session.sends2s(build_reply("failure", "invalid-authzid"));
162 return true 152 return true;
163 end 153 end
164 else 154
165 if text == "" then 155 -- We've already verified the external cert identity before offering EXTERNAL
166 session.sends2s(build_reply("failure", "invalid-authzid")) 156 if session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid" then
167 return true 157 session.sends2s(build_reply("failure", "not-authorized"));
168 end 158 session:close();
169 159 return true;
170 local cert = session.conn:socket():getpeercertificate() 160 end
171 if (cert_verify_identity(text, "xmpp-server", cert)) then 161
172 session.cert_identity_status = "valid" 162 -- Success!
173 else 163 session.external_auth = "succeeded";
174 session.cert_identity_status = "invalid" 164 session.sends2s(build_reply("success"));
175 session.sends2s(build_reply("failure", "invalid-authzid")) 165 module:log("info", "Accepting SASL EXTERNAL identity from %s", session.from_host);
176 return true 166 module:fire_event("s2s-authenticated", { session = session, host = session.from_host });
177 end
178 end
179
180 session.external_auth = "succeeded"
181
182 if not session.from_host then
183 session.from_host = text;
184 end
185 session.sends2s(build_reply("success"))
186
187 local domain = text ~= "" and text or session.from_host;
188 module:log("info", "Accepting SASL EXTERNAL identity from %s", domain);
189 module:fire_event("s2s-authenticated", { session = session, host = domain });
190 session:reset_stream(); 167 session:reset_stream();
191 return true 168 return true;
192 end 169 end
193 170
194 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)
195 local session, stanza = event.origin, event.stanza; 172 local session, stanza = event.origin, event.stanza;
196 if session.type == "s2sin_unauthed" then 173 if session.type == "s2sin_unauthed" then
266 end); 243 end);
267 244
268 module:hook("s2s-stream-features", function(event) 245 module:hook("s2s-stream-features", function(event)
269 local origin, features = event.origin, event.features; 246 local origin, features = event.origin, event.features;
270 if origin.secure and origin.type == "s2sin_unauthed" then 247 if origin.secure and origin.type == "s2sin_unauthed" then
271 -- Offer EXTERNAL if chain is valid and either we didn't validate 248 -- Offer EXTERNAL only if both chain and identity is valid.
272 -- the identity or it passed. 249 if origin.cert_chain_status == "valid" and origin.cert_identity_status == "valid" then
273 if origin.cert_chain_status == "valid" and origin.cert_identity_status ~= "invalid" then --TODO: Configurable 250 module:log("debug", "Offering SASL EXTERNAL");
274 module:log("debug", "Offering SASL EXTERNAL") 251 origin.external_auth = "offered"
275 features:tag("mechanisms", { xmlns = xmlns_sasl }) 252 features:tag("mechanisms", { xmlns = xmlns_sasl })
276 :tag("mechanism"):text("EXTERNAL") 253 :tag("mechanism"):text("EXTERNAL")
277 :up():up(); 254 :up():up();
278 end 255 end
279 end 256 end