From b4ba73c8b61d4af5282dd88216a71323c967e0e6 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 28 Feb 2010 22:23:03 +0100 Subject: util.sasl: Abstracting out the hash function used since SCRAM is independent of it. Adding scram-{mech} authentication backend support. --- util/sasl/scram.lua | 132 +++++++++++++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 57 deletions(-) (limited to 'util/sasl') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 103e8a90..43b97c55 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -82,77 +82,95 @@ local function validate_username(username) return username; end -local function scram_sha_1(self, message) - if not self.state then self["state"] = {} end +local function scram_gen(hash_name, H_f, HMAC_f) + local function scram_hash(self, message) + if not self.state then self["state"] = {} end - if not self.state.name then - -- we are processing client_first_message - local client_first_message = message; - self.state["client_first_message"] = client_first_message; - self.state["name"] = client_first_message:match("n=(.+),r=") - self.state["clientnonce"] = client_first_message:match("r=([^,]+)") - - if not self.state.name or not self.state.clientnonce then - return "failure", "malformed-request"; - end - - self.state.name = validate_username(self.state.name); if not self.state.name then - log("debug", "Username violates either SASLprep or contains forbidden character sequences.") - return "failure", "malformed-request", "Invalid username."; - end + -- we are processing client_first_message + local client_first_message = message; + self.state["client_first_message"] = client_first_message; + self.state["name"] = client_first_message:match("n=(.+),r=") + self.state["clientnonce"] = client_first_message:match("r=([^,]+)") - self.state["servernonce"] = generate_uuid(); - self.state["salt"] = generate_uuid(); + if not self.state.name or not self.state.clientnonce then + return "failure", "malformed-request"; + end - local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..default_i; - self.state["server_first_message"] = server_first_message; - return "challenge", server_first_message - else - if type(message) ~= "string" then return "failure", "malformed-request" end - -- we are processing client_final_message - local client_final_message = message; + self.state.name = validate_username(self.state.name); + if not self.state.name then + log("debug", "Username violates either SASLprep or contains forbidden character sequences.") + return "failure", "malformed-request", "Invalid username."; + end - self.state["proof"] = client_final_message:match("p=(.+)"); - self.state["nonce"] = client_final_message:match("r=(.+),p="); - self.state["channelbinding"] = client_final_message:match("c=(.+),r="); - if not self.state.proof or not self.state.nonce or not self.state.channelbinding then - return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message."; - end + self.state["servernonce"] = generate_uuid(); + + -- retreive credentials + if self.profile.plain then + password, state = self.profile.plain(self.state.name, self.realm) + if state == nil then return "failure", "not-authorized" + elseif state == false then return "failure", "account-disabled" end + + password = saslprep(password); + if not password then + log("debug", "Password violates SASLprep."); + return "failure", "not-authorized", "Invalid password." + end + self.state.salt = generate_uuid(); + self.state.iteration_count = default_i; + self.state.salted_password = Hi(HMAC_f, password, self.state.salt, default_i); + elseif self.profile["scram-"..hash_name] then + salted_password, iteration_count, salt, state = self.profile["scram-"..hash_name](self.state.name, self.realm); + if state == nil then return "failure", "not-authorized" + elseif state == false then return "failure", "account-disabled" end + + self.state.salted_password = salted_password; + self.state.iteration_count = iteration_count; + self.state.salt = salt + end - local password, state; - if self.profile.plain then - password, state = self.profile.plain(self.state.name, self.realm) - if state == nil then return "failure", "not-authorized" - elseif state == false then return "failure", "account-disabled" end - password = saslprep(password); - if not password then - log("debug", "Password violates SASLprep."); - return "failure", "not-authorized", "Invalid password." + local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..self.state.iteration_count; + self.state["server_first_message"] = server_first_message; + return "challenge", server_first_message + else + if type(message) ~= "string" then return "failure", "malformed-request" end + -- we are processing client_final_message + local client_final_message = message; + + self.state["proof"] = client_final_message:match("p=(.+)"); + self.state["nonce"] = client_final_message:match("r=(.+),p="); + self.state["channelbinding"] = client_final_message:match("c=(.+),r="); + if not self.state.proof or not self.state.nonce or not self.state.channelbinding then + return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message."; end - end - local SaltedPassword = Hi(hmac_sha1, password, self.state.salt, default_i) - local ClientKey = hmac_sha1(SaltedPassword, "Client Key") - local ServerKey = hmac_sha1(SaltedPassword, "Server Key") - local StoredKey = sha1(ClientKey) - local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") - local ClientSignature = hmac_sha1(StoredKey, AuthMessage) - local ClientProof = binaryXOR(ClientKey, ClientSignature) - local ServerSignature = hmac_sha1(ServerKey, AuthMessage) + local SaltedPassword = self.state.salted_password; + local ClientKey = HMAC_f(SaltedPassword, "Client Key") + local ServerKey = HMAC_f(SaltedPassword, "Server Key") + local StoredKey = H_f(ClientKey) + local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") + local ClientSignature = HMAC_f(StoredKey, AuthMessage) + local ClientProof = binaryXOR(ClientKey, ClientSignature) + local ServerSignature = HMAC_f(ServerKey, AuthMessage) - if base64.encode(ClientProof) == self.state.proof then - local server_final_message = "v="..base64.encode(ServerSignature); - self["username"] = self.state.name; - return "success", server_final_message; - else - return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated."; + if base64.encode(ClientProof) == self.state.proof then + local server_final_message = "v="..base64.encode(ServerSignature); + self["username"] = self.state.name; + return "success", server_final_message; + else + return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated."; + end end end + return scram_hash; end function init(registerMechanism) - registerMechanism("SCRAM-SHA-1", {"plain"}, scram_sha_1); + local function registerSCRAMMechanism(hash_name, hash, hmac_hash) + registerMechanism("SCRAM-"..hash_name, {"plain", "scram-"..(hash_name:lower())}, scram_gen(hash_name:lower(), hash, hmac_hash)); + end + + registerSCRAMMechanism("SHA-1", sha1, hmac_sha1); end return _M; \ No newline at end of file -- cgit v1.2.3 From cb3a0cad21e9598fb4e9ed69a4294135da755aa6 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 28 Feb 2010 22:32:12 +0100 Subject: util.sasl: Moving SASL authentication backends documentation to the mechanism files. --- util/sasl/digest-md5.lua | 15 +++++++++++++++ util/sasl/plain.lua | 15 +++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'util/sasl') diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index 5b8f5c8a..3d6a3e1e 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -29,6 +29,21 @@ module "digest-md5" --========================= --SASL DIGEST-MD5 according to RFC 2831 +--[[ +Supported Authentication Backends + +digest-md5: + function(username, domain, realm, encoding) -- domain and realm are usually the same; for some broken + -- implementations it's not + return digesthash, state; + end + +digest-md5-test: + function(username, domain, realm, encoding, digesthash) + return true or false, state; + end +]] + local function digest(self, message) --TODO complete support for authzid diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index ae5c777a..43bb239f 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -19,6 +19,21 @@ module "plain" -- ================================ -- SASL PLAIN according to RFC 4616 + +--[[ +Supported Authentication Backends + +plain: + function(username, realm) + return password, state; + end + +plain-test: + function(username, realm, password) + return true or false, state; + end +]] + local function plain(self, message) if not message then return "failure", "malformed-request"; -- cgit v1.2.3 From 1146d48bd64689a2675364b881d59bb88e3a0323 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 28 Feb 2010 22:40:05 +0100 Subject: util.sasl.anonymous: Adding documentation on anonymous authentication backend. --- util/sasl/anonymous.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'util/sasl') diff --git a/util/sasl/anonymous.lua b/util/sasl/anonymous.lua index 65650294..d1e7d437 100644 --- a/util/sasl/anonymous.lua +++ b/util/sasl/anonymous.lua @@ -20,6 +20,16 @@ module "anonymous" --========================= --SASL ANONYMOUS according to RFC 4505 + +--[[ +Supported Authentication Backends + +anonymous: + function(username, realm) + return true; --for normal usage just return true; if you don't like the supplied username you can return false. + end +]] + local function anonymous(self, message) local username; repeat -- cgit v1.2.3 From ceb748bd96b32e76092518c302cb62452508cbdb Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 28 Feb 2010 22:42:53 +0100 Subject: util.sasl.scram: Adding documentation on SCRAM authentication backend. --- util/sasl/scram.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'util/sasl') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 43b97c55..1ceeae47 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -28,6 +28,16 @@ module "scram" --========================= --SASL SCRAM-SHA-1 according to draft-ietf-sasl-scram-10 + +--[[ +Supported Authentication Backends + +scram-{MECH}: + function(username, realm) + return salted_password, iteration_count, salt, state; + end +]] + local default_i = 4096 local function bp( b ) -- cgit v1.2.3 From 965aa9476a77678ed1a7c2ea58954eebd6c6bb9b Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 28 Feb 2010 22:50:25 +0100 Subject: util.sasl.plain: Adding plain_hashed authentication backend support. --- util/sasl/plain.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'util/sasl') diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 43bb239f..6369face 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -32,6 +32,11 @@ plain-test: function(username, realm, password) return true or false, state; end + +plain-hashed: + function(username, realm) + return hashed_password, hash_function, state; + end ]] local function plain(self, message) @@ -61,6 +66,10 @@ local function plain(self, message) if correct_password == password then correct = true; else correct = false; end elseif self.profile.plain_test then correct, state = self.profile.plain_test(authentication, self.realm, password); + elseif self.profile.plain_hashed then + local hashed_password, hash_f; + hashed_password, hash_f, state = self.profile.plain_hashed(authentication, self.realm); + if hashed_password == hash_f(password) then correct = true; else correct = false; end end self.username = authentication @@ -76,7 +85,7 @@ local function plain(self, message) end function init(registerMechanism) - registerMechanism("PLAIN", {"plain", "plain_test"}, plain); + registerMechanism("PLAIN", {"plain", "plain_test", "plain_hashed"}, plain); end return _M; -- cgit v1.2.3 From 263683d8c2bbb471ac3527d0bc106064c834d045 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 28 Feb 2010 22:58:43 +0100 Subject: util.sasl.scram: Adjusting authentication backend name to conform with the style already used by the plain module. --- util/sasl/scram.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util/sasl') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 1ceeae47..b813137a 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -129,7 +129,7 @@ local function scram_gen(hash_name, H_f, HMAC_f) self.state.salt = generate_uuid(); self.state.iteration_count = default_i; self.state.salted_password = Hi(HMAC_f, password, self.state.salt, default_i); - elseif self.profile["scram-"..hash_name] then + elseif self.profile["scram_"..hash_name] then salted_password, iteration_count, salt, state = self.profile["scram-"..hash_name](self.state.name, self.realm); if state == nil then return "failure", "not-authorized" elseif state == false then return "failure", "account-disabled" end @@ -177,7 +177,7 @@ end function init(registerMechanism) local function registerSCRAMMechanism(hash_name, hash, hmac_hash) - registerMechanism("SCRAM-"..hash_name, {"plain", "scram-"..(hash_name:lower())}, scram_gen(hash_name:lower(), hash, hmac_hash)); + registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hash_name:lower())}, scram_gen(hash_name:lower(), hash, hmac_hash)); end registerSCRAMMechanism("SHA-1", sha1, hmac_sha1); -- cgit v1.2.3 From 3a972b1742c28b7ef9cb5f76ed3bc69f752b5899 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 12 Mar 2010 18:37:51 +0100 Subject: util.sasl: 2009 -> 2010 in copyright header. --- util/sasl/anonymous.lua | 2 +- util/sasl/digest-md5.lua | 2 +- util/sasl/plain.lua | 2 +- util/sasl/scram.lua | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'util/sasl') diff --git a/util/sasl/anonymous.lua b/util/sasl/anonymous.lua index d1e7d437..f3e31a7f 100644 --- a/util/sasl/anonymous.lua +++ b/util/sasl/anonymous.lua @@ -1,5 +1,5 @@ -- sasl.lua v0.4 --- Copyright (C) 2008-2009 Tobias Markmann +-- Copyright (C) 2008-2010 Tobias Markmann -- -- All rights reserved. -- diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index 3d6a3e1e..73b310bc 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -1,5 +1,5 @@ -- sasl.lua v0.4 --- Copyright (C) 2008-2009 Tobias Markmann +-- Copyright (C) 2008-2010 Tobias Markmann -- -- All rights reserved. -- diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 6369face..2abbc53a 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -1,5 +1,5 @@ -- sasl.lua v0.4 --- Copyright (C) 2008-2009 Tobias Markmann +-- Copyright (C) 2008-2010 Tobias Markmann -- -- All rights reserved. -- diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index b813137a..4875731f 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -1,5 +1,5 @@ -- sasl.lua v0.4 --- Copyright (C) 2008-2009 Tobias Markmann +-- Copyright (C) 2008-2010 Tobias Markmann -- -- All rights reserved. -- -- cgit v1.2.3