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; |