Software /
code /
prosody
Comparison
util/sasl.lua @ 1166:5499a028d4ae
Automated merge with http://waqas.ath.cx:8000/
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Fri, 15 May 2009 20:38:30 +0100 |
parent | 1161:5bc2b7b5b81d |
child | 1305:37657578ea85 |
comparison
equal
deleted
inserted
replaced
1165:ec69bcc7ceb5 | 1166:5499a028d4ae |
---|---|
58 end | 58 end |
59 end | 59 end |
60 return object | 60 return object |
61 end | 61 end |
62 | 62 |
63 | |
64 -- implementing RFC 2831 | |
63 local function new_digest_md5(realm, password_handler) | 65 local function new_digest_md5(realm, password_handler) |
64 --TODO maybe support for authzid | 66 --TODO complete support for authzid |
65 | 67 |
66 local function serialize(message) | 68 local function serialize(message) |
67 local data = "" | 69 local data = "" |
68 | 70 |
69 if type(message) ~= "table" then error("serialize needs an argument of type table.") end | 71 if type(message) ~= "table" then error("serialize needs an argument of type table.") end |
127 return t_concat(p); | 129 return t_concat(p); |
128 end | 130 end |
129 local function parse(data) | 131 local function parse(data) |
130 message = {} | 132 message = {} |
131 for k, v in gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder | 133 for k, v in gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder |
132 message[k] = v | 134 message[k] = v; |
133 end | 135 end |
134 return message | 136 return message; |
135 end | 137 end |
136 | 138 |
137 local object = { mechanism = "DIGEST-MD5", realm = realm, password_handler = password_handler} | 139 local object = { mechanism = "DIGEST-MD5", realm = realm, password_handler = password_handler}; |
138 | 140 |
139 --TODO: something better than math.random would be nice, maybe OpenSSL's random number generator | 141 --TODO: something better than math.random would be nice, maybe OpenSSL's random number generator |
140 object.nonce = generate_uuid() | 142 object.nonce = generate_uuid(); |
141 object.step = 0 | 143 object.step = 0; |
142 object.nonce_count = {} | 144 object.nonce_count = {}; |
143 | 145 |
144 function object.feed(self, message) | 146 function object.feed(self, message) |
145 self.step = self.step + 1 | 147 self.step = self.step + 1; |
146 if (self.step == 1) then | 148 if (self.step == 1) then |
147 local challenge = serialize({ nonce = object.nonce, | 149 local challenge = serialize({ nonce = object.nonce, |
148 qop = "auth", | 150 qop = "auth", |
149 charset = "utf-8", | 151 charset = "utf-8", |
150 algorithm = "md5-sess", | 152 algorithm = "md5-sess", |
151 realm = self.realm}); | 153 realm = self.realm}); |
152 return "challenge", challenge | 154 return "challenge", challenge; |
153 elseif (self.step == 2) then | 155 elseif (self.step == 2) then |
154 local response = parse(message) | 156 local response = parse(message); |
155 -- check for replay attack | 157 -- check for replay attack |
156 if response["nc"] then | 158 if response["nc"] then |
157 if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end | 159 if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end |
158 end | 160 end |
159 | 161 |
160 -- check for username, it's REQUIRED by RFC 2831 | 162 -- check for username, it's REQUIRED by RFC 2831 |
161 if not response["username"] then | 163 if not response["username"] then |
162 return "failure", "malformed-request" | 164 return "failure", "malformed-request"; |
163 end | 165 end |
164 self["username"] = response["username"] | 166 self["username"] = response["username"]; |
165 | 167 |
166 -- check for nonce, ... | 168 -- check for nonce, ... |
167 if not response["nonce"] then | 169 if not response["nonce"] then |
168 return "failure", "malformed-request" | 170 return "failure", "malformed-request"; |
169 else | 171 else |
170 -- check if it's the right nonce | 172 -- check if it's the right nonce |
171 if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end | 173 if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end |
172 end | 174 end |
173 | 175 |
182 | 184 |
183 local decoder; | 185 local decoder; |
184 if response["charset"] == nil then | 186 if response["charset"] == nil then |
185 decoder = utf8tolatin1ifpossible; | 187 decoder = utf8tolatin1ifpossible; |
186 elseif response["charset"] ~= "utf-8" then | 188 elseif response["charset"] ~= "utf-8" then |
187 return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8." | 189 return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; |
188 end | 190 end |
189 | 191 |
190 local domain = "" | 192 local domain = ""; |
191 local protocol = "" | 193 local protocol = ""; |
192 if response["digest-uri"] then | 194 if response["digest-uri"] then |
193 protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$") | 195 protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); |
194 if protocol == nil or domain == nil then return "failure", "malformed-request" end | 196 if protocol == nil or domain == nil then return "failure", "malformed-request" end |
195 else | 197 else |
196 return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." | 198 return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." |
197 end | 199 end |
198 | 200 |
199 --TODO maybe realm support | 201 --TODO maybe realm support |
200 self.username = response["username"] | 202 self.username = response["username"]; |
201 local password_encoding, Y = self.password_handler(response["username"], response["realm"], "DIGEST-MD5", decoder) | 203 local password_encoding, Y = self.password_handler(response["username"], response["realm"], "DIGEST-MD5", decoder) |
202 if Y == nil then return "failure", "not-authorized" | 204 if Y == nil then return "failure", "not-authorized" |
203 elseif Y == false then return "failure", "account-disabled" end | 205 elseif Y == false then return "failure", "account-disabled" end |
204 | 206 local A1 = ""; |
205 local A1 = Y..":"..response["nonce"]..":"..response["cnonce"]--:authzid | 207 if response.authzid then |
208 if response.authzid == self.username.."@"..self.realm then | |
209 log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); | |
210 A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; | |
211 else | |
212 A1 = "?"; | |
213 end | |
214 else | |
215 A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; | |
216 end | |
206 local A2 = "AUTHENTICATE:"..protocol.."/"..domain; | 217 local A2 = "AUTHENTICATE:"..protocol.."/"..domain; |
207 | 218 |
208 local HA1 = md5(A1, true) | 219 local HA1 = md5(A1, true); |
209 local HA2 = md5(A2, true) | 220 local HA2 = md5(A2, true); |
210 | 221 |
211 local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 | 222 local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; |
212 local response_value = md5(KD, true) | 223 local response_value = md5(KD, true); |
213 | 224 |
214 if response_value == response["response"] then | 225 if response_value == response["response"] then |
215 -- calculate rspauth | 226 -- calculate rspauth |
216 A2 = ":"..protocol.."/"..domain; | 227 A2 = ":"..protocol.."/"..domain; |
217 | 228 |
218 HA1 = md5(A1, true) | 229 HA1 = md5(A1, true); |
219 HA2 = md5(A2, true) | 230 HA2 = md5(A2, true); |
220 | 231 |
221 KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 | 232 KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 |
222 local rspauth = md5(KD, true) | 233 local rspauth = md5(KD, true); |
223 self.authenticated = true | 234 self.authenticated = true; |
224 return "challenge", serialize({rspauth = rspauth}) | 235 return "challenge", serialize({rspauth = rspauth}); |
225 else | 236 else |
226 return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." | 237 return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." |
227 end | 238 end |
228 elseif self.step == 3 then | 239 elseif self.step == 3 then |
229 if self.authenticated ~= nil then return "success" | 240 if self.authenticated ~= nil then return "success" |
230 else return "failure", "malformed-request" end | 241 else return "failure", "malformed-request" end |
231 end | 242 end |
232 end | 243 end |
233 return object | 244 return object; |
234 end | 245 end |
235 | 246 |
236 local function new_anonymous(realm, password_handler) | 247 local function new_anonymous(realm, password_handler) |
237 local object = { mechanism = "ANONYMOUS", realm = realm, password_handler = password_handler} | 248 local object = { mechanism = "ANONYMOUS", realm = realm, password_handler = password_handler} |
238 function object.feed(self, message) | 249 function object.feed(self, message) |