diff options
-rw-r--r-- | util/sasl.lua | 179 |
1 files changed, 88 insertions, 91 deletions
diff --git a/util/sasl.lua b/util/sasl.lua index 6fd45ce1..6b14c1b1 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -151,7 +151,7 @@ registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); --========================= --SASL DIGEST-MD5 according to RFC 2831 -local function new_digest_md5(realm, credentials_handler) +local function sasl_mechanism_digest_md5(self, message) --TODO complete support for authzid local function serialize(message) @@ -225,112 +225,109 @@ local function new_digest_md5(realm, credentials_handler) return message; end - local object = { mechanism = "DIGEST-MD5", realm = realm, credentials_handler = credentials_handler}; - - object.nonce = generate_uuid(); - object.step = 0; - object.nonce_count = {}; - - function object.feed(self, message) - self.step = self.step + 1; - if (self.step == 1) then - local challenge = serialize({ nonce = object.nonce, - qop = "auth", - charset = "utf-8", - algorithm = "md5-sess", - realm = self.realm}); - return "challenge", challenge; - elseif (self.step == 2) then - local response = parse(message); - -- check for replay attack - if response["nc"] then - if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end - end + if not self.nonce then + self.nonce = generate_uuid(); + self.step = 0; + self.nonce_count = {}; + end - -- check for username, it's REQUIRED by RFC 2831 - if not response["username"] then - return "failure", "malformed-request"; - end - self["username"] = response["username"]; + self.step = self.step + 1; + if (self.step == 1) then + local challenge = serialize({ nonce = object.nonce, + qop = "auth", + charset = "utf-8", + algorithm = "md5-sess", + realm = self.realm}); + return "challenge", challenge; + elseif (self.step == 2) then + local response = parse(message); + -- check for replay attack + if response["nc"] then + if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end + end - -- check for nonce, ... - if not response["nonce"] then - return "failure", "malformed-request"; - else - -- check if it's the right nonce - if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end - end + -- check for username, it's REQUIRED by RFC 2831 + if not response["username"] then + return "failure", "malformed-request"; + end + self["username"] = response["username"]; + + -- check for nonce, ... + if not response["nonce"] then + return "failure", "malformed-request"; + else + -- check if it's the right nonce + if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end + end - if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end - if not response["qop"] then response["qop"] = "auth" end + if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end + if not response["qop"] then response["qop"] = "auth" end - if response["realm"] == nil or response["realm"] == "" then - response["realm"] = ""; - elseif response["realm"] ~= self.realm then - return "failure", "not-authorized", "Incorrect realm value"; - end + if response["realm"] == nil or response["realm"] == "" then + response["realm"] = ""; + elseif response["realm"] ~= self.realm then + return "failure", "not-authorized", "Incorrect realm value"; + end - local decoder; - if response["charset"] == nil then - decoder = utf8tolatin1ifpossible; - elseif response["charset"] ~= "utf-8" then - 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."; - end + local decoder; + if response["charset"] == nil then + decoder = utf8tolatin1ifpossible; + elseif response["charset"] ~= "utf-8" then + 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."; + end - local domain = ""; - local protocol = ""; - if response["digest-uri"] then - protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); - if protocol == nil or domain == nil then return "failure", "malformed-request" end - else - return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." - end + local domain = ""; + local protocol = ""; + if response["digest-uri"] then + protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); + if protocol == nil or domain == nil then return "failure", "malformed-request" end + else + return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." + end - --TODO maybe realm support - self.username = response["username"]; - local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); - if Y == nil then return "failure", "not-authorized" - elseif Y == false then return "failure", "account-disabled" end - local A1 = ""; - if response.authzid then - if response.authzid == self.username.."@"..self.realm then - -- COMPAT - log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; - else - A1 = "?"; - end + --TODO maybe realm support + self.username = response["username"]; + local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); + if Y == nil then return "failure", "not-authorized" + elseif Y == false then return "failure", "account-disabled" end + local A1 = ""; + if response.authzid then + if response.authzid == self.username.."@"..self.realm then + -- COMPAT + log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); + A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; else - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; + A1 = "?"; end - local A2 = "AUTHENTICATE:"..protocol.."/"..domain; + else + A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; + end + local A2 = "AUTHENTICATE:"..protocol.."/"..domain; - local HA1 = md5(A1, true); - local HA2 = md5(A2, true); + local HA1 = md5(A1, true); + local HA2 = md5(A2, true); - local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; - local response_value = md5(KD, true); + local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; + local response_value = md5(KD, true); - if response_value == response["response"] then - -- calculate rspauth - A2 = ":"..protocol.."/"..domain; + if response_value == response["response"] then + -- calculate rspauth + A2 = ":"..protocol.."/"..domain; - HA1 = md5(A1, true); - HA2 = md5(A2, true); + HA1 = md5(A1, true); + HA2 = md5(A2, true); - KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 - local rspauth = md5(KD, true); - self.authenticated = true; - return "challenge", serialize({rspauth = rspauth}); - else - return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." - end - elseif self.step == 3 then - if self.authenticated ~= nil then return "success" - else return "failure", "malformed-request" end + KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 + local rspauth = md5(KD, true); + self.authenticated = true; + return "challenge", serialize({rspauth = rspauth}); + else + return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." end + elseif self.step == 3 then + if self.authenticated ~= nil then return "success" + else return "failure", "malformed-request" end end - return object; end return _M; |