Comparison

util/sasl.lua @ 1160:7e48324f946e

Some beauty treatment.
author Tobias Markmann <tm@ayena.de>
date Fri, 15 May 2009 17:28:22 +0200
parent 1159:f81c8cec0e71
child 1161:5bc2b7b5b81d
comparison
equal deleted inserted replaced
1159:f81c8cec0e71 1160:7e48324f946e
129 return t_concat(p); 129 return t_concat(p);
130 end 130 end
131 local function parse(data) 131 local function parse(data)
132 message = {} 132 message = {}
133 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
134 message[k] = v 134 message[k] = v;
135 end 135 end
136 return message 136 return message;
137 end 137 end
138 138
139 local object = { mechanism = "DIGEST-MD5", realm = realm, password_handler = password_handler} 139 local object = { mechanism = "DIGEST-MD5", realm = realm, password_handler = password_handler};
140 140
141 --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
142 object.nonce = generate_uuid() 142 object.nonce = generate_uuid();
143 object.step = 0 143 object.step = 0;
144 object.nonce_count = {} 144 object.nonce_count = {};
145 145
146 function object.feed(self, message) 146 function object.feed(self, message)
147 self.step = self.step + 1 147 self.step = self.step + 1;
148 if (self.step == 1) then 148 if (self.step == 1) then
149 local challenge = serialize({ nonce = object.nonce, 149 local challenge = serialize({ nonce = object.nonce,
150 qop = "auth", 150 qop = "auth",
151 charset = "utf-8", 151 charset = "utf-8",
152 algorithm = "md5-sess", 152 algorithm = "md5-sess",
153 realm = self.realm}); 153 realm = self.realm});
154 return "challenge", challenge 154 return "challenge", challenge;
155 elseif (self.step == 2) then 155 elseif (self.step == 2) then
156 local response = parse(message) 156 local response = parse(message);
157 -- check for replay attack 157 -- check for replay attack
158 if response["nc"] then 158 if response["nc"] then
159 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
160 end 160 end
161 161
162 -- check for username, it's REQUIRED by RFC 2831 162 -- check for username, it's REQUIRED by RFC 2831
163 if not response["username"] then 163 if not response["username"] then
164 return "failure", "malformed-request" 164 return "failure", "malformed-request";
165 end 165 end
166 self["username"] = response["username"] 166 self["username"] = response["username"];
167 167
168 -- check for nonce, ... 168 -- check for nonce, ...
169 if not response["nonce"] then 169 if not response["nonce"] then
170 return "failure", "malformed-request" 170 return "failure", "malformed-request";
171 else 171 else
172 -- check if it's the right nonce 172 -- check if it's the right nonce
173 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
174 end 174 end
175 175
184 184
185 local decoder; 185 local decoder;
186 if response["charset"] == nil then 186 if response["charset"] == nil then
187 decoder = utf8tolatin1ifpossible; 187 decoder = utf8tolatin1ifpossible;
188 elseif response["charset"] ~= "utf-8" then 188 elseif response["charset"] ~= "utf-8" then
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." 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.";
190 end 190 end
191 191
192 local domain = "" 192 local domain = "";
193 local protocol = "" 193 local protocol = "";
194 if response["digest-uri"] then 194 if response["digest-uri"] then
195 protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$") 195 protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$");
196 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
197 else 197 else
198 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."
199 end 199 end
200 200
201 --TODO maybe realm support 201 --TODO maybe realm support
202 self.username = response["username"] 202 self.username = response["username"];
203 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)
204 if Y == nil then return "failure", "not-authorized" 204 if Y == nil then return "failure", "not-authorized"
205 elseif Y == false then return "failure", "account-disabled" end 205 elseif Y == false then return "failure", "account-disabled" end
206 local A1 = ""; 206 local A1 = "";
207 if response.authzid then 207 if response.authzid then
214 else 214 else
215 A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; 215 A1 = Y..":"..response["nonce"]..":"..response["cnonce"];
216 end 216 end
217 local A2 = "AUTHENTICATE:"..protocol.."/"..domain; 217 local A2 = "AUTHENTICATE:"..protocol.."/"..domain;
218 218
219 local HA1 = md5(A1, true) 219 local HA1 = md5(A1, true);
220 local HA2 = md5(A2, true) 220 local HA2 = md5(A2, true);
221 221
222 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;
223 local response_value = md5(KD, true) 223 local response_value = md5(KD, true);
224 224
225 if response_value == response["response"] then 225 if response_value == response["response"] then
226 -- calculate rspauth 226 -- calculate rspauth
227 A2 = ":"..protocol.."/"..domain; 227 A2 = ":"..protocol.."/"..domain;
228 228
229 HA1 = md5(A1, true) 229 HA1 = md5(A1, true);
230 HA2 = md5(A2, true) 230 HA2 = md5(A2, true);
231 231
232 KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 232 KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2
233 local rspauth = md5(KD, true) 233 local rspauth = md5(KD, true);
234 self.authenticated = true 234 self.authenticated = true;
235 return "challenge", serialize({rspauth = rspauth}) 235 return "challenge", serialize({rspauth = rspauth});
236 else 236 else
237 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."
238 end 238 end
239 elseif self.step == 3 then 239 elseif self.step == 3 then
240 if self.authenticated ~= nil then return "success" 240 if self.authenticated ~= nil then return "success"
241 else return "failure", "malformed-request" end 241 else return "failure", "malformed-request" end
242 end 242 end
243 end 243 end
244 return object 244 return object;
245 end 245 end
246 246
247 local function new_anonymous(realm, password_handler) 247 local function new_anonymous(realm, password_handler)
248 local object = { mechanism = "ANONYMOUS", realm = realm, password_handler = password_handler} 248 local object = { mechanism = "ANONYMOUS", realm = realm, password_handler = password_handler}
249 function object.feed(self, message) 249 function object.feed(self, message)