Merge Tobias's fancy SASL branch->trunk

Wed, 05 May 2010 11:29:10 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Wed, 05 May 2010 11:29:10 +0100
changeset 2998
36c169ed1576
parent 2989
87c378c77be3 (current diff)
parent 2997
6ccaefea80ec (diff)
child 2999
9a8f942433c4

Merge Tobias's fancy SASL branch->trunk

net/server_select.lua file | annotate | diff | comparison | revisions
util/sasl.lua file | annotate | diff | comparison | revisions
util/sasl/digest-md5.lua file | annotate | diff | comparison | revisions
--- a/net/server_select.lua	Wed May 05 11:25:26 2010 +0100
+++ b/net/server_select.lua	Wed May 05 11:29:10 2010 +0100
@@ -508,6 +508,9 @@
 						out_put( "server.lua: ssl handshake done" )
 						handler.readbuffer = _readbuffer	-- when handshake is done, replace the handshake function with regular functions
 						handler.sendbuffer = _sendbuffer
+						out_put ("server.lua: compression used: "..tostring(client:compression()))
+						out_put ("server.lua: finished: "..tostring(client:getfinished()):sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"))
+						out_put ("server.lua: peer finished: "..tostring(client:getpeerfinished()):sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"))
 						_ = status and status( handler, "ssl-handshake-complete" )
 						_readlistlen = addsocket(_readlist, client, _readlistlen)
 						return true
--- a/util/sasl.lua	Wed May 05 11:25:26 2010 +0100
+++ b/util/sasl.lua	Wed May 05 11:29:10 2010 +0100
@@ -41,27 +41,6 @@
 state = false : disabled
 state = true : enabled
 state = nil : non-existant
-
-plain:
-	function(username, realm)
-		return password, state;
-	end
-
-plain-test:
-	function(username, realm, password)
-		return true or false, state;
-	end
-
-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 method = {};
--- a/util/sasl/anonymous.lua	Wed May 05 11:25:26 2010 +0100
+++ b/util/sasl/anonymous.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,5 +1,5 @@
 -- sasl.lua v0.4
--- Copyright (C) 2008-2009 Tobias Markmann
+-- Copyright (C) 2008-2010 Tobias Markmann
 --
 --    All rights reserved.
 --
@@ -20,6 +20,16 @@
 
 --=========================
 --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
--- a/util/sasl/digest-md5.lua	Wed May 05 11:25:26 2010 +0100
+++ b/util/sasl/digest-md5.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,5 +1,5 @@
 -- sasl.lua v0.4
--- Copyright (C) 2008-2009 Tobias Markmann
+-- Copyright (C) 2008-2010 Tobias Markmann
 --
 --    All rights reserved.
 --
@@ -29,6 +29,21 @@
 --=========================
 --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
 
--- a/util/sasl/plain.lua	Wed May 05 11:25:26 2010 +0100
+++ b/util/sasl/plain.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,5 +1,5 @@
 -- sasl.lua v0.4
--- Copyright (C) 2008-2009 Tobias Markmann
+-- Copyright (C) 2008-2010 Tobias Markmann
 --
 --    All rights reserved.
 --
@@ -19,6 +19,26 @@
 
 -- ================================
 -- 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
+	
+plain-hashed:
+	function(username, realm)
+		return hashed_password, hash_function, state;
+	end
+]]
+
 local function plain(self, message)
 	if not message then
 		return "failure", "malformed-request";
@@ -46,6 +66,10 @@
 		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
@@ -61,7 +85,7 @@
 end
 
 function init(registerMechanism)
-	registerMechanism("PLAIN", {"plain", "plain_test"}, plain);
+	registerMechanism("PLAIN", {"plain", "plain_test", "plain_hashed"}, plain);
 end
 
 return _M;
--- a/util/sasl/scram.lua	Wed May 05 11:25:26 2010 +0100
+++ b/util/sasl/scram.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,5 +1,5 @@
 -- sasl.lua v0.4
--- Copyright (C) 2008-2009 Tobias Markmann
+-- Copyright (C) 2008-2010 Tobias Markmann
 --
 --    All rights reserved.
 --
@@ -28,6 +28,16 @@
 
 --=========================
 --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 )
@@ -82,77 +92,95 @@
 	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 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
+			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
-		
-		self.state["servernonce"] = generate_uuid();
-		self.state["salt"] = generate_uuid();
+			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
 		
-		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["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
 		
-		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
+			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
 		
-		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 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.";
 			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)
-		
-		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
+	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

mercurial