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)