From e80ad5b544d14138542675f85d52ff8e1fb28d99 Mon Sep 17 00:00:00 2001
From: Denis <d.v.kobozev@gmail.com>
Date: Mon, 5 Apr 2010 09:41:30 -0400
Subject: [PATCH] Support for storing salted passwords

---
 support/schema/aur-schema.sql |    1 +
 web/html/passreset.php        |    5 +++-
 web/lib/acctfuncs.inc         |   58 ++++++++++++++++++++++++++--------------
 web/lib/aur.inc               |   31 ++++++++++++++++++++++
 4 files changed, 74 insertions(+), 21 deletions(-)

diff --git a/support/schema/aur-schema.sql b/support/schema/aur-schema.sql
index 67e20a7..250d405 100644
--- a/support/schema/aur-schema.sql
+++ b/support/schema/aur-schema.sql
@@ -26,6 +26,7 @@ CREATE TABLE Users (
 	Username CHAR(32) NOT NULL,
 	Email CHAR(64) NOT NULL,
 	Passwd CHAR(32) NOT NULL,
+	Salt CHAR(32) NOT NULL DEFAULT '',
 	ResetKey CHAR(32) NOT NULL DEFAULT '',
 	RealName CHAR(64) NOT NULL DEFAULT '',
 	LangPreference CHAR(5) NOT NULL DEFAULT 'en',
diff --git a/web/html/passreset.php b/web/html/passreset.php
index 6fbd1ca..0f98593 100644
--- a/web/html/passreset.php
+++ b/web/html/passreset.php
@@ -31,10 +31,13 @@ if (isset($_GET['resetkey'], $_POST['email'], $_POST['password'], $_POST['confir
 
 	if (empty($error)) {
 		$dbh = db_connect();
+		$salt = generate_salt();
+		$hash = salted_hash($password, $salt);
 		# The query below won't affect any records unless the ResetKey
 		# and Email combination is correct and ResetKey is nonempty
 		$q = "UPDATE Users
-		      SET Passwd = '".md5($password)."',
+		      SET Passwd = '$hash',
+		      Salt = '$salt',
 		      ResetKey = ''
 		      WHERE ResetKey != ''
 		      AND ResetKey = '".mysql_real_escape_string($resetkey)."'
diff --git a/web/lib/acctfuncs.inc b/web/lib/acctfuncs.inc
index 91b6249..9c172bb 100644
--- a/web/lib/acctfuncs.inc
+++ b/web/lib/acctfuncs.inc
@@ -265,17 +265,14 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="",
 	} else {
 		if ($TYPE == "new") {
 			# no errors, go ahead and create the unprivileged user
-
-			# md5hash the password
-			$P = md5($P);
-			$q = "INSERT INTO Users (AccountTypeID, Suspended, Username, Email, ";
-			$q.= "Passwd, RealName, LangPreference, IRCNick, NewPkgNotify) ";
-			$q.= "VALUES (1, 0, '".mysql_real_escape_string($U)."'";
-			$q.= ", '".mysql_real_escape_string($E)."'";
-			$q.= ", '".mysql_real_escape_string($P)."'";
-			$q.= ", '".mysql_real_escape_string($R)."'";
-			$q.= ", '".mysql_real_escape_string($L)."'";
-			$q.= ", '".mysql_real_escape_string($I)."'";
+			$salt = generate_salt();
+			$P = salted_hash($P, $salt);
+			$escaped = array_map(mysql_real_escape_string,
+				array($U, $E, $P, $salt, $R, $L, $I));
+			$q = "INSERT INTO Users (" .
+				"AccountTypeID, Suspended, Username, Email, Passwd, Salt" .
+				", RealName, LangPreference, IRCNick, NewPkgNotify) " .
+				"VALUES (1, 0, '" . implode("', '", $escaped) . "'";
 			if ($N) {
 				$q.= ", 1)";
 			} else {
@@ -298,7 +295,6 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="",
 		} else {
 			# no errors, go ahead and modify the user account
 
-			# md5 hash the password
 			$q = "UPDATE Users SET ";
 			$q.= "Username = '".mysql_real_escape_string($U)."'";
 			if ($T) {
@@ -311,7 +307,9 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="",
 			}
 			$q.= ", Email = '".mysql_real_escape_string($E)."'";
 			if ($P) {
-				$q.= ", Passwd = '".mysql_real_escape_string(md5($P))."'";
+				$salt = generate_salt();
+				$hash = salted_hash($P, $salt);
+				$q .= ", Passwd = '$hash', Salt = '$salt'";
 			}
 			$q.= ", RealName = '".mysql_real_escape_string($R)."'";
 			$q.= ", LangPreference = '".mysql_real_escape_string($L)."'";
@@ -738,14 +736,34 @@ function valid_passwd( $userID, $passwd )
 {
 	if ( strlen($passwd) > 0 ) {
 		$dbh = db_connect();
-		$q = "SELECT ID FROM Users".
-		  " WHERE ID = '$userID'" .
-		  " AND Passwd = '" . md5($passwd) . "'";
 
-		$result = mysql_fetch_row(db_query($q, $dbh));
-		if ($result[0]) {
-			# Is it the right password?
-			return true;
+		# get salt for this user
+		$salt = get_salt($userID);
+		if ($salt) {
+			# use salt
+			$passwd_q = "SELECT ID FROM Users" .
+				" WHERE ID = '$userID' AND Passwd = '" .
+				salted_hash($passwd, $salt) . "'";
+			$passwd_result = mysql_fetch_row(db_query($passwd_q, $dbh));
+			if ($passwd_result[0]) {
+				return true;
+			}
+		} else {
+			# check without salt
+			$nosalt_q = "SELECT ID FROM Users".
+				" WHERE ID = '$userID'" .
+				" AND Passwd = '" . md5($passwd) . "'";
+			$nosalt_result = mysql_fetch_row(db_query($nosalt_q, $dbh));
+			if ($nosalt_result[0]) {
+				# password correct, but salt it first
+				if (!save_salt($userID, $passwd)) {
+					trigger_error("Unable to salt user's password;" .
+						" ID $userID", E_USER_WARNING);
+					return false;
+				}
+
+				return true;
+			}
 		}
 	}
 	return false;
diff --git a/web/lib/aur.inc b/web/lib/aur.inc
index e0521ab..ec872b1 100644
--- a/web/lib/aur.inc
+++ b/web/lib/aur.inc
@@ -455,3 +455,34 @@ function mkurl($append) {
 
 	return substr($out, 5);
 }
+
+function get_salt($user_id)
+{
+	$dbh = db_connect();
+	$salt_q = "SELECT Salt FROM Users WHERE ID = '$user_id'"; 
+	$salt_result = mysql_fetch_row(db_query($salt_q, $dbh));
+	return $salt_result[0];
+}
+
+function save_salt($user_id, $passwd)
+{
+	$dbh = db_connect();
+	$salt = generate_salt();
+	$hash = salted_hash($passwd, $salt);
+	$salting_q = "UPDATE Users SET Salt = '$salt'" . 
+		", Passwd = '$hash' WHERE ID = '$user_id'";
+	return db_query($salting_q, $dbh);
+}
+
+function generate_salt()
+{
+	return md5(uniqid(rand(), true));
+}
+
+function salted_hash($passwd, $salt)
+{
+	if (strlen($salt) != 32) {
+		trigger_error('Salt does not look like an md5 hash', E_USER_WARNING);
+	}
+	return md5($salt . $passwd);
+}
-- 
1.7.0.3

