From 3e442a0f7d49fc8eed45002841d84d9080473cc9 Mon Sep 17 00:00:00 2001 From: Mark Weiman Date: Wed, 9 Nov 2016 18:54:37 -0500 Subject: Remove all usage of UNIX_TIMESTAMP in web interface UNIX_TIMESTAMP is not part of the SQL standard. Instead, all usage in the web interface is changed to use PHP's time() function. Signed-off-by: Mark Weiman Signed-off-by: Lukas Fleischer --- web/lib/acctfuncs.inc.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'web/lib/acctfuncs.inc.php') diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php index 172b9621..6dcf91d1 100644 --- a/web/lib/acctfuncs.inc.php +++ b/web/lib/acctfuncs.inc.php @@ -543,7 +543,7 @@ function try_login() { $new_sid = new_sid(); $q = "INSERT INTO Sessions (UsersID, SessionID, LastUpdateTS)" - ." VALUES (" . $userID . ", '" . $new_sid . "', UNIX_TIMESTAMP())"; + ." VALUES (" . $userID . ", '" . $new_sid . "', " . strval(time()) . ")"; $result = $dbh->exec($q); /* Query will fail if $new_sid is not unique. */ @@ -560,7 +560,7 @@ function try_login() { return array('SID' => $new_sid, 'error' => $login_error); } - $q = "UPDATE Users SET LastLogin = UNIX_TIMESTAMP(), "; + $q = "UPDATE Users SET LastLogin = " . strval(time()) . ", "; $q.= "LastLoginIPAddress = " . $dbh->quote($_SERVER['REMOTE_ADDR']) . " "; $q.= "WHERE ID = $userID"; $dbh->exec($q); @@ -638,7 +638,7 @@ function valid_username($user) { function open_user_proposals($user) { $dbh = DB::connect(); $q = "SELECT * FROM TU_VoteInfo WHERE User = " . $dbh->quote($user) . " "; - $q.= "AND End > UNIX_TIMESTAMP()"; + $q.= "AND End > " . strval(time()); $result = $dbh->query($q); return ($result->fetchColumn() ? true : false); @@ -665,7 +665,7 @@ function add_tu_proposal($agenda, $user, $votelength, $quorum, $submitteruid) { $q = "INSERT INTO TU_VoteInfo (Agenda, User, Submitted, End, Quorum, "; $q.= "SubmitterID, ActiveTUs) VALUES "; $q.= "(" . $dbh->quote($agenda) . ", " . $dbh->quote($user) . ", "; - $q.= "UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + " . $dbh->quote($votelength); + $q.= strval(time()) . ", " . strval(time()) . " + " . $dbh->quote($votelength); $q.= ", " . $dbh->quote($quorum) . ", " . $submitteruid . ", "; $q.= $active_tus . ")"; $result = $dbh->exec($q); @@ -978,7 +978,7 @@ function clear_expired_sessions() { $dbh = DB::connect(); $timeout = config_get_int('options', 'login_timeout'); - $q = "DELETE FROM Sessions WHERE LastUpdateTS < (UNIX_TIMESTAMP() - " . $timeout . ")"; + $q = "DELETE FROM Sessions WHERE LastUpdateTS < (" . strval(time()) . " - " . $timeout . ")"; $dbh->query($q); return; @@ -1086,7 +1086,7 @@ function last_votes_list() { $q = "SELECT UserID, MAX(VoteID) AS LastVote FROM TU_Votes, "; $q .= "TU_VoteInfo, Users WHERE TU_VoteInfo.ID = TU_Votes.VoteID AND "; - $q .= "TU_VoteInfo.End < UNIX_TIMESTAMP() AND "; + $q .= "TU_VoteInfo.End < " . strval(time()) . " AND "; $q .= "Users.ID = TU_Votes.UserID AND (Users.AccountTypeID = 2 OR Users.AccountTypeID = 4) "; $q .= "GROUP BY UserID ORDER BY LastVote DESC, UserName ASC"; $result = $dbh->query($q); -- cgit v1.2.3-24-g4f1b From 608c48309084e4048d8226c3f7e363b240248040 Mon Sep 17 00:00:00 2001 From: Mark Weiman Date: Fri, 20 Jan 2017 01:16:39 -0500 Subject: Add user set timezones Currently, aurweb displays all dates and times in UTC time. This patch adds a capability for each logged in user to set their preferred timezone. Implements FS#48729. Signed-off-by: Mark Weiman Signed-off-by: Lukas Fleischer --- web/lib/acctfuncs.inc.php | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'web/lib/acctfuncs.inc.php') diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php index 6dcf91d1..b7288143 100644 --- a/web/lib/acctfuncs.inc.php +++ b/web/lib/acctfuncs.inc.php @@ -1,5 +1,4 @@ quote($salt); $R = $dbh->quote($R); $L = $dbh->quote($L); + $TZ = $dbh->quote($TZ); $HP = $dbh->quote($HP); $I = $dbh->quote($I); $K = $dbh->quote(str_replace(" ", "", $K)); $q = "INSERT INTO Users (AccountTypeID, Suspended, "; $q.= "InactivityTS, Username, Email, Passwd, Salt, "; - $q.= "RealName, LangPreference, Homepage, IRCNick, PGPKey) "; - $q.= "VALUES (1, 0, 0, $U, $E, $P, $salt, $R, $L, "; + $q.= "RealName, LangPreference, Timezone, Homepage, IRCNick, PGPKey) "; + $q.= "VALUES (1, 0, 0, $U, $E, $P, $salt, $R, $L, $TZ "; $q.= "$HP, $I, $K)"; $result = $dbh->exec($q); if (!$result) { @@ -347,6 +356,7 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="" } $q.= ", RealName = " . $dbh->quote($R); $q.= ", LangPreference = " . $dbh->quote($L); + $q.= ", Timezone = " . $dbh->quote($TZ); $q.= ", Homepage = " . $dbh->quote($HP); $q.= ", IRCNick = " . $dbh->quote($I); $q.= ", PGPKey = " . $dbh->quote(str_replace(" ", "", $K)); @@ -359,6 +369,13 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="" $ssh_key_result = account_set_ssh_keys($UID, $ssh_keys, $ssh_fingerprints); + if (isset($_COOKIE["AURTZ"]) && ($_COOKIE["AURTZ"] != $TZ)) { + /* set new cookie for timezone */ + $timeout = intval(config_get("options", "persistent_cookie_timeout")); + $cookie_time = time() + $timeout; + setcookie("AURTZ", $TZ, $cookie_time, "/"); + } + if ($result === false || $ssh_key_result === false) { $message = __("No changes were made to the account, %s%s%s.", "", htmlspecialchars($U,ENT_QUOTES), ""); -- cgit v1.2.3-24-g4f1b From 7ff50701903017b3b97ca3ca176e923769cdad43 Mon Sep 17 00:00:00 2001 From: Mark Weiman Date: Fri, 20 Jan 2017 01:16:40 -0500 Subject: Update cookie for language setting when editing user information Currently, when a user edits their language setting from the edit user form, the changes aren't reflected until the user either lets the original cookie expire, deletes the cookie manually, or changes the language a second time via the dropdown menu on the top of the page. This patch makes the language cookie get updated when it is changed from the edit user form. Signed-off-by: Mark Weiman Signed-off-by: Lukas Fleischer --- web/lib/acctfuncs.inc.php | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'web/lib/acctfuncs.inc.php') diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php index b7288143..08dbc671 100644 --- a/web/lib/acctfuncs.inc.php +++ b/web/lib/acctfuncs.inc.php @@ -376,6 +376,13 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="" setcookie("AURTZ", $TZ, $cookie_time, "/"); } + if (isset($_COOKIE["AURLANG"]) && ($_COOKIE["AURLANG"] != $L)) { + /* set new cookie for language */ + $timeout = intval(config_get("options", "persistent_cookie_timeout")); + $cookie_time = time() + $timeout; + setcookie("AURLANG", $L, $cookie_time, "/"); + } + if ($result === false || $ssh_key_result === false) { $message = __("No changes were made to the account, %s%s%s.", "", htmlspecialchars($U,ENT_QUOTES), ""); -- cgit v1.2.3-24-g4f1b From 70db022aa8287c57a2ee03328ae893ba8b83b192 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Wed, 25 Jan 2017 08:37:48 +0100 Subject: Store banned IP addresses as plain text Inspired by commit 32c8d0c (Store last login address as plain text, 2016-03-13). Signed-off-by: Lukas Fleischer --- web/lib/acctfuncs.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'web/lib/acctfuncs.inc.php') diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php index 08dbc671..b3cf6122 100644 --- a/web/lib/acctfuncs.inc.php +++ b/web/lib/acctfuncs.inc.php @@ -621,7 +621,7 @@ function try_login() { function is_ipbanned() { $dbh = DB::connect(); - $q = "SELECT * FROM Bans WHERE IPAddress = " . $dbh->quote(ip2long($_SERVER['REMOTE_ADDR'])); + $q = "SELECT * FROM Bans WHERE IPAddress = " . $dbh->quote($_SERVER['REMOTE_ADDR']); $result = $dbh->query($q); return ($result->fetchColumn() ? true : false); -- cgit v1.2.3-24-g4f1b From 29a48708bb7c3e00e80275a6b898f557f63dff69 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Fri, 24 Feb 2017 19:52:28 +0100 Subject: Use bcrypt to hash passwords Replace the default hash function used for storing passwords by password_hash() which internally uses bcrypt. Legacy MD5 hashes are still supported and are immediately converted to the new format when a user logs in. Since big parts of the authentication system needed to be rewritten in this context, this patch also includes some simplification and refactoring of all code related to password checking and resetting. Fixes FS#52297. Signed-off-by: Lukas Fleischer --- web/lib/acctfuncs.inc.php | 144 +++++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 84 deletions(-) (limited to 'web/lib/acctfuncs.inc.php') diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php index b3cf6122..d0a7ff94 100644 --- a/web/lib/acctfuncs.inc.php +++ b/web/lib/acctfuncs.inc.php @@ -272,13 +272,12 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="" if ($TYPE == "new") { /* Create an unprivileged user. */ - $salt = generate_salt(); if (empty($P)) { $send_resetkey = true; $email = $E; } else { $send_resetkey = false; - $P = salted_hash($P, $salt); + $P = password_hash($P, PASSWORD_DEFAULT); } $U = $dbh->quote($U); $E = $dbh->quote($E); @@ -291,9 +290,9 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="" $I = $dbh->quote($I); $K = $dbh->quote(str_replace(" ", "", $K)); $q = "INSERT INTO Users (AccountTypeID, Suspended, "; - $q.= "InactivityTS, Username, Email, Passwd, Salt, "; + $q.= "InactivityTS, Username, Email, Passwd , "; $q.= "RealName, LangPreference, Timezone, Homepage, IRCNick, PGPKey) "; - $q.= "VALUES (1, 0, 0, $U, $E, $P, $salt, $R, $L, $TZ "; + $q.= "VALUES (1, 0, 0, $U, $E, $P, $R, $L, $TZ "; $q.= "$HP, $I, $K)"; $result = $dbh->exec($q); if (!$result) { @@ -350,9 +349,8 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="" $q.= ", HideEmail = 0"; } if ($P) { - $salt = generate_salt(); - $hash = salted_hash($P, $salt); - $q .= ", Passwd = '$hash', Salt = '$salt'"; + $hash = password_hash($P, PASSWORD_DEFAULT); + $q .= ", Passwd = " . $dbh->quote($hash); } $q.= ", RealName = " . $dbh->quote($R); $q.= ", LangPreference = " . $dbh->quote($L); @@ -528,19 +526,24 @@ function try_login() { if (user_suspended($userID)) { $login_error = __('Account suspended'); return array('SID' => '', 'error' => $login_error); - } elseif (passwd_is_empty($userID)) { - $login_error = __('Your password has been reset. ' . - 'If you just created a new account, please ' . - 'use the link from the confirmation email ' . - 'to set an initial password. Otherwise, ' . - 'please request a reset key on the %s' . - 'Password Reset%s page.', '', - ''); - return array('SID' => '', 'error' => $login_error); - } elseif (!valid_passwd($userID, $_REQUEST['passwd'])) { - $login_error = __("Bad username or password."); - return array('SID' => '', 'error' => $login_error); + } + + switch (check_passwd($userID, $_REQUEST['passwd'])) { + case -1: + $login_error = __('Your password has been reset. ' . + 'If you just created a new account, please ' . + 'use the link from the confirmation email ' . + 'to set an initial password. Otherwise, ' . + 'please request a reset key on the %s' . + 'Password Reset%s page.', '', + ''); + return array('SID' => '', 'error' => $login_error); + case 0: + $login_error = __("Bad username or password."); + return array('SID' => '', 'error' => $login_error); + case 1: + break; } $logged_in = 0; @@ -736,18 +739,18 @@ function send_resetkey($email, $welcome=false) { /** * Change a user's password in the database if reset key and e-mail are correct * - * @param string $hash New MD5 hash of a user's password - * @param string $salt New salt for the user's password + * @param string $password The new password * @param string $resetkey Code e-mailed to a user to reset a password * @param string $email E-mail address of the user resetting their password * * @return string|void Redirect page if successful, otherwise return error message */ -function password_reset($hash, $salt, $resetkey, $email) { +function password_reset($password, $resetkey, $email) { + $hash = password_hash($password, PASSWORD_DEFAULT); + $dbh = DB::connect(); - $q = "UPDATE Users "; - $q.= "SET Passwd = '$hash', "; - $q.= "Salt = '$salt', "; + $q = "UPDATE Users SET "; + $q.= "Passwd = " . $dbh->quote($hash) . ", "; $q.= "ResetKey = '' "; $q.= "WHERE ResetKey != '' "; $q.= "AND ResetKey = " . $dbh->quote($resetkey) . " "; @@ -778,75 +781,48 @@ function good_passwd($passwd) { /** * Determine if the password is correct and salt it if it hasn't been already * - * @param string $userID The user ID to check the password against + * @param int $user_id The user ID to check the password against * @param string $passwd The password the visitor sent * - * @return bool True if password was correct and properly salted, otherwise false + * @return int Positive if password is correct, negative if password is unset */ -function valid_passwd($userID, $passwd) { +function check_passwd($user_id, $passwd) { $dbh = DB::connect(); - if ($passwd == "") { - return false; - } - /* Get salt for this user. */ - $salt = get_salt($userID); - if ($salt) { - $q = "SELECT ID FROM Users "; - $q.= "WHERE ID = " . $userID . " "; - $q.= "AND Passwd = " . $dbh->quote(salted_hash($passwd, $salt)); - $result = $dbh->query($q); - if (!$result) { - return false; - } - - $row = $result->fetch(PDO::FETCH_NUM); - return ($row[0] > 0); - } else { - /* Check password without using salt. */ - $q = "SELECT ID FROM Users "; - $q.= "WHERE ID = " . $userID . " "; - $q.= "AND Passwd = " . $dbh->quote(md5($passwd)); - $result = $dbh->query($q); - if (!$result) { - return false; - } - - $row = $result->fetch(PDO::FETCH_NUM); - if (!$row[0]) { - return false; - } + /* Get password hash and salt. */ + $q = "SELECT Passwd, Salt FROM Users WHERE ID = " . intval($user_id); + $result = $dbh->query($q); + if (!$result) { + return 0; + } + $row = $result->fetch(PDO::FETCH_ASSOC); + if (!$row) { + return 0; + } + $hash = $row['Passwd']; + $salt = $row['Salt']; + if (!$hash) { + return -1; + } - /* 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; + /* Verify the password hash. */ + if (!password_verify($passwd, $hash)) { + /* Invalid password, fall back to MD5. */ + if (md5($salt . $passwd) != $hash) { + return 0; } - - return true; } -} -/** - * Determine if a user's password is empty - * - * @param string $uid The user ID to check for an empty password - * - * @return bool True if the user's password is empty, otherwise false - */ -function passwd_is_empty($uid) { - $dbh = DB::connect(); - - $q = "SELECT * FROM Users WHERE ID = " . $dbh->quote($uid) . " "; - $q .= "AND Passwd = " . $dbh->quote(''); - $result = $dbh->query($q); + /* Password correct, migrate the hash if necessary. */ + if (password_needs_rehash($hash, PASSWORD_DEFAULT)) { + $hash = password_hash($passwd, PASSWORD_DEFAULT); - if ($result->fetchColumn()) { - return true; - } else { - return false; + $q = "UPDATE Users SET Passwd = " . $dbh->quote($hash) . " "; + $q.= "WHERE ID = " . intval($user_id); + $dbh->query($q); } + + return 1; } /** -- cgit v1.2.3-24-g4f1b