Fix LDAP password changing, rewrite a lot of LDAP code to work better

V2_Rewrite
Skylar Ittner 7 years ago
parent ca3da67929
commit cd9ebb461e

@ -8,6 +8,14 @@ use LdapTools\Object\LdapObjectType;
require_once __DIR__ . "/required.php"; require_once __DIR__ . "/required.php";
// If the user presses Sign Out but we're not logged in anymore,
// we don't want to show a nasty error.
if ($VARS['action'] == 'signout' && $_SESSION['loggedin'] != true) {
session_destroy();
header('Location: index.php');
die("Logged out (session was expired anyways).");
}
dieifnotloggedin(); dieifnotloggedin();
require_once __DIR__ . "/lib/login.php"; require_once __DIR__ . "/lib/login.php";
@ -16,9 +24,9 @@ require_once __DIR__ . "/lib/worst_passwords.php";
function returnToSender($msg, $arg = "") { function returnToSender($msg, $arg = "") {
global $VARS; global $VARS;
if ($arg == "") { if ($arg == "") {
header("Location: home.php?page=" . urlencode($VARS['source']) . "&msg=" . $msg); header("Location: home.php?page=" . urlencode($VARS['source']) . "&msg=$msg");
} else { } else {
header("Location: home.php?page=" . urlencode($VARS['source']) . "&msg=$msg&arg=$arg"); header("Location: home.php?page=" . urlencode($VARS['source']) . "&msg=$msg&arg=" . urlencode($arg));
} }
die(); die();
} }
@ -30,7 +38,10 @@ switch ($VARS['action']) {
header('Location: index.php'); header('Location: index.php');
die("Logged out."); die("Logged out.");
case "chpasswd": case "chpasswd":
if ($_SESSION['password'] == $VARS['oldpass']) { if ($VARS['oldpass'] == $VARS['newpass']) {
returnToSender("passwords_same");
}
if (authenticate_user($_SESSION['username'], $VARS['oldpass'])) {
if ($VARS['newpass'] == $VARS['conpass']) { if ($VARS['newpass'] == $VARS['conpass']) {
$passrank = checkWorst500List($VARS['newpass']); $passrank = checkWorst500List($VARS['newpass']);
if ($passrank !== FALSE) { if ($passrank !== FALSE) {
@ -48,15 +59,26 @@ switch ($VARS['action']) {
insertAuthLog(3, $_SESSION['uid']); insertAuthLog(3, $_SESSION['uid']);
returnToSender("password_updated"); returnToSender("password_updated");
} else if ($acctloc == "LDAP") { } else if ($acctloc == "LDAP") {
$ldapManager = new LdapManager($ldap_config); /* $ldap_config_domain
$repository = $ldapManager->getRepository(LdapObjectType::USER); ->setUsername($_SESSION['username'])
$user = $repository->findOneByUsername($_SESSION['username']); ->setPassword($VARS['oldpass']); */
$user->setPassword($VARS['newpass']);
try { try {
//echo "0";
$ldapManager = new LdapManager($ldap_config);
//echo "1";
$repository = $ldapManager->getRepository(LdapObjectType::USER);
//echo "2";
$user = $repository->findOneByUsername($_SESSION['username']);
//echo "3";
$user->setPassword($VARS['newpass']);
//echo "4";
$ldapManager->persist($user); $ldapManager->persist($user);
//echo "5";
insertAuthLog(3, $_SESSION['uid']); insertAuthLog(3, $_SESSION['uid']);
$_SESSION['password'] = $VARS['newpass'];
returnToSender("password_updated"); returnToSender("password_updated");
} catch (\Exception $e) { } catch (\Exception $e) {
echo $e->getMessage();
returnToSender("ldap_error", $e->getMessage()); returnToSender("ldap_error", $e->getMessage());
} }
} else { } else {

@ -40,17 +40,33 @@ switch ($VARS['action']) {
exit(json_encode(["status" => "OK"])); exit(json_encode(["status" => "OK"]));
break; break;
case "auth": case "auth":
if (authenticate_user($VARS['username'], $VARS['password'])) { $errmsg = "";
if (authenticate_user($VARS['username'], $VARS['password'], $errmsg)) {
insertAuthLog(12, null, "Username: " . $VARS['username'] . ", Key: " . getCensoredKey()); insertAuthLog(12, null, "Username: " . $VARS['username'] . ", Key: " . getCensoredKey());
exit(json_encode(["status" => "OK", "msg" => lang("login successful", false)])); exit(json_encode(["status" => "OK", "msg" => lang("login successful", false)]));
} else { } else {
insertAuthLog(13, null, "Username: " . $VARS['username'] . ", Key: " . getCensoredKey()); insertAuthLog(13, $uid, "Username: " . $VARS['username'] . ", Key: " . getCensoredKey());
if (!is_empty($errmsg)) {
exit(json_encode(["status" => "ERROR", "msg" => lang2("ldap error", ['error' => $errmsg], false)]));
}
if (user_exists($VARS['username'])) {
switch (get_account_status($VARS['username'])) {
case "LOCKED_OR_DISABLED":
exit(json_encode(["status" => "ERROR", "msg" => lang("account locked", false)]));
case "TERMINATED":
exit(json_encode(["status" => "ERROR", "msg" => lang("account terminated", false)]));
case "CHANGE_PASSWORD":
exit(json_encode(["status" => "ERROR", "msg" => lang("password expired", false)]));
default:
exit(json_encode(["status" => "ERROR", "msg" => lang("account state error", false)]));
}
}
exit(json_encode(["status" => "ERROR", "msg" => lang("login incorrect", false)])); exit(json_encode(["status" => "ERROR", "msg" => lang("login incorrect", false)]));
} }
break; break;
case "userinfo": case "userinfo":
if (!is_empty($VARS['username'])) { if (!is_empty($VARS['username'])) {
if (user_exists($VARS['username'])) { if (user_exists_local($VARS['username'])) {
$data = $database->select("accounts", ["uid", "username", "realname (name)", "email", "phone" => ["phone1 (1)", "phone2 (2)"]], ["username" => $VARS['username']])[0]; $data = $database->select("accounts", ["uid", "username", "realname (name)", "email", "phone" => ["phone1 (1)", "phone2 (2)"]], ["username" => $VARS['username']])[0];
exit(json_encode(["status" => "OK", "data" => $data])); exit(json_encode(["status" => "OK", "data" => $data]));
} else { } else {
@ -76,7 +92,7 @@ switch ($VARS['action']) {
exit(json_encode(["status" => "OK", "exists" => false])); exit(json_encode(["status" => "OK", "exists" => false]));
} }
} }
if (user_exists($VARS['username'])) { if (user_exists_local($VARS['username'])) {
exit(json_encode(["status" => "OK", "exists" => true])); exit(json_encode(["status" => "OK", "exists" => true]));
} else { } else {
exit(json_encode(["status" => "OK", "exists" => false])); exit(json_encode(["status" => "OK", "exists" => false]));
@ -101,7 +117,8 @@ switch ($VARS['action']) {
exit(json_encode(["status" => "OK", "account" => get_account_status($VARS['username'])])); exit(json_encode(["status" => "OK", "account" => get_account_status($VARS['username'])]));
case "login": case "login":
// simulate a login, checking account status and alerts // simulate a login, checking account status and alerts
if (authenticate_user($VARS['username'], $VARS['password'])) { $errmsg = "";
if (authenticate_user($VARS['username'], $VARS['password'], $errmsg)) {
$uid = $database->select('accounts', 'uid', ['username' => $VARS['username']])[0]; $uid = $database->select('accounts', 'uid', ['username' => $VARS['username']])[0];
switch (get_account_status($VARS['username'])) { switch (get_account_status($VARS['username'])) {
case "LOCKED_OR_DISABLED": case "LOCKED_OR_DISABLED":
@ -126,6 +143,9 @@ switch ($VARS['action']) {
} }
} else { } else {
insertAuthLog(5, null, "Username: " . $VARS['username'] . ", Key: " . getCensoredKey()); insertAuthLog(5, null, "Username: " . $VARS['username'] . ", Key: " . getCensoredKey());
if (!is_empty($errmsg)) {
exit(json_encode(["status" => "ERROR", "msg" => lang2("ldap error", ['error' => $errmsg], false)]));
}
exit(json_encode(["status" => "ERROR", "msg" => lang("login incorrect", false)])); exit(json_encode(["status" => "ERROR", "msg" => lang("login incorrect", false)]));
} }
break; break;
@ -142,8 +162,8 @@ switch ($VARS['action']) {
exit(json_encode(["status" => "ERROR", "msg" => lang("user does not exist", false), "user" => $VARS['manager']])); exit(json_encode(["status" => "ERROR", "msg" => lang("user does not exist", false), "user" => $VARS['manager']]));
} }
} else { } else {
if (user_exists($VARS['manager'])) { if (user_exists_local($VARS['manager'])) {
if (user_exists($VARS['employee'])) { if (user_exists_local($VARS['employee'])) {
$managerid = $database->select('accounts', 'uid', ['username' => $VARS['manager']]); $managerid = $database->select('accounts', 'uid', ['username' => $VARS['manager']]);
$employeeid = $database->select('accounts', 'uid', ['username' => $VARS['employee']]); $employeeid = $database->select('accounts', 'uid', ['username' => $VARS['employee']]);
} else { } else {

@ -6,7 +6,7 @@ $APPS["change_password"]["title"] = "Change Password";
$APPS["change_password"]["icon"] = "key"; $APPS["change_password"]["icon"] = "key";
$APPS["change_password"]["content"] = <<<CONTENTEND $APPS["change_password"]["content"] = <<<CONTENTEND
<form action="action.php" method="POST"> <form action="action.php" method="POST">
<input type="password" class="form-control" name="oldpass" placeholder="Old password" /> <input type="password" class="form-control" name="oldpass" placeholder="Current password" />
<input type="password" class="form-control" name="newpass" placeholder="New password" /> <input type="password" class="form-control" name="newpass" placeholder="New password" />
<input type="password" class="form-control" name="conpass" placeholder="New password (again)" /> <input type="password" class="form-control" name="conpass" placeholder="New password (again)" />
<input type="hidden" name="action" value="chpasswd" /> <input type="hidden" name="action" value="chpasswd" />

@ -3,12 +3,18 @@ require_once __DIR__ . "/required.php";
require_once __DIR__ . "/lib/login.php"; require_once __DIR__ . "/lib/login.php";
// if we're logged in, we don't need to be here.
if ($_SESSION['loggedin']) {
header('Location: home.php');
}
/* Authenticate user */ /* Authenticate user */
$userpass_ok = false; $userpass_ok = false;
$multiauth = false; $multiauth = false;
if ($VARS['progress'] == "1") { if ($VARS['progress'] == "1") {
if (!RECAPTCHA_ENABLED || (RECAPTCHA_ENABLED && verifyReCaptcha($VARS['g-recaptcha-response']))) { if (!RECAPTCHA_ENABLED || (RECAPTCHA_ENABLED && verifyReCaptcha($VARS['g-recaptcha-response']))) {
if (authenticate_user($VARS['username'], $VARS['password'])) { $autherror = "";
if (user_exists($VARS['username'])) {
switch (get_account_status($VARS['username'])) { switch (get_account_status($VARS['username'])) {
case "LOCKED_OR_DISABLED": case "LOCKED_OR_DISABLED":
$alert = lang("account locked", false); $alert = lang("account locked", false);
@ -18,6 +24,7 @@ if ($VARS['progress'] == "1") {
break; break;
case "CHANGE_PASSWORD": case "CHANGE_PASSWORD":
$alert = lang("password expired", false); $alert = lang("password expired", false);
break;
case "NORMAL": case "NORMAL":
$userpass_ok = true; $userpass_ok = true;
break; break;
@ -27,17 +34,27 @@ if ($VARS['progress'] == "1") {
break; break;
} }
if ($userpass_ok) { if ($userpass_ok) {
$_SESSION['passok'] = true; // stop logins using only username and authcode if (authenticate_user($VARS['username'], $VARS['password'], $autherror)) {
if (userHasTOTP($VARS['username'])) { $_SESSION['passok'] = true; // stop logins using only username and authcode
$multiauth = true; if (userHasTOTP($VARS['username'])) {
$multiauth = true;
} else {
doLoginUser($VARS['username'], $VARS['password']);
insertAuthLog(1, $_SESSION['uid']);
header('Location: home.php');
die("Logged in, go to home.php");
}
} else { } else {
doLoginUser($VARS['username'], $VARS['password']); if (!is_empty($autherror)) {
insertAuthLog(1, $_SESSION['uid']); $alert = $autherror;
header('Location: home.php'); insertAuthLog(2, null, "Username: " . $VARS['username']);
die("Logged in, go to home.php"); } else {
$alert = lang("login incorrect", false);
insertAuthLog(2, null, "Username: " . $VARS['username']);
}
} }
} }
} else { } else { // User does not exist anywhere
$alert = lang("login incorrect", false); $alert = lang("login incorrect", false);
insertAuthLog(2, null, "Username: " . $VARS['username']); insertAuthLog(2, null, "Username: " . $VARS['username']);
} }
@ -71,6 +88,7 @@ if ($VARS['progress'] == "1") {
<title><?php echo SITE_TITLE; ?></title> <title><?php echo SITE_TITLE; ?></title>
<link href="static/css/bootstrap.min.css" rel="stylesheet"> <link href="static/css/bootstrap.min.css" rel="stylesheet">
<link href="static/css/font-awesome.min.css" rel="stylesheet">
<link href="static/css/app.css" rel="stylesheet"> <link href="static/css/app.css" rel="stylesheet">
<?php if (RECAPTCHA_ENABLED) { ?> <?php if (RECAPTCHA_ENABLED) { ?>
<script src='https://www.google.com/recaptcha/api.js'></script> <script src='https://www.google.com/recaptcha/api.js'></script>
@ -93,7 +111,7 @@ if ($VARS['progress'] == "1") {
if (!is_empty($alert)) { if (!is_empty($alert)) {
?> ?>
<div class="alert alert-danger"> <div class="alert alert-danger">
<?php echo $alert; ?> <i class="fa fa-fw fa-exclamation-triangle"></i> <?php echo $alert; ?>
</div> </div>
<?php <?php
} }

@ -42,4 +42,6 @@ define("STRINGS", [
"user does not exist" => "User does not exist.", "user does not exist" => "User does not exist.",
"captcha error" => "There was a problem with the CAPTCHA (robot test). Try again.", "captcha error" => "There was a problem with the CAPTCHA (robot test). Try again.",
"home" => "Home", "home" => "Home",
"ldap error" => "LDAP error: {error}",
"old and new passwords match" => "Your current and new passwords are the same."
]); ]);

@ -40,5 +40,9 @@ define("MESSAGES", [
"ldap_error" => [ "ldap_error" => [
"string" => "ldap server error", "string" => "ldap server error",
"type" => "danger" "type" => "danger"
],
"passwords_same" => [
"string" => "old and new passwords match",
"type" => "danger"
] ]
]); ]);

@ -6,8 +6,11 @@
use Base32\Base32; use Base32\Base32;
use OTPHP\TOTP; use OTPHP\TOTP;
use LdapTools\LdapManager; use LdapTools\LdapManager;
use LdapTools\Object\LdapObjectType;
use LdapTools\Connection\ADResponseCodes; use LdapTools\Connection\ADResponseCodes;
$ldap = new LdapManager($ldap_config);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Account handling // // Account handling //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -35,7 +38,7 @@ function adduser($username, $password, $realname, $email = null, $phone1 = "", $
'acctstatus' => 1, 'acctstatus' => 1,
'accttype' => $type 'accttype' => $type
]); ]);
var_dump($database->error()); //var_dump($database->error());
return $database->id(); return $database->id();
} }
@ -44,10 +47,10 @@ function adduser($username, $password, $realname, $email = null, $phone1 = "", $
* @param string $username * @param string $username
* @return string "LDAP", "LOCAL", "LDAP_ONLY", or "NONE". * @return string "LDAP", "LOCAL", "LDAP_ONLY", or "NONE".
*/ */
function account_location($username, $password) { function account_location($username) {
global $database; global $database;
$username = strtolower($username); $username = strtolower($username);
$user_exists = user_exists($username); $user_exists = user_exists_local($username);
if (!$user_exists && !LDAP_ENABLED) { if (!$user_exists && !LDAP_ENABLED) {
return false; return false;
} }
@ -62,7 +65,7 @@ function account_location($username, $password) {
return "LOCAL"; return "LOCAL";
} }
} else { } else {
if (user_exists_ldap($username, $password)) { if (user_exists_ldap($username)) {
return "LDAP_ONLY"; return "LDAP_ONLY";
} else { } else {
return "NONE"; return "NONE";
@ -76,9 +79,9 @@ function account_location($username, $password) {
* @param string $password * @param string $password
* @return boolean True if OK, else false * @return boolean True if OK, else false
*/ */
function authenticate_user($username, $password) { function authenticate_user($username, $password, &$errormsg) {
global $database; global $database;
global $ldap_config; global $ldap;
$username = strtolower($username); $username = strtolower($username);
if (is_empty($username) || is_empty($password)) { if (is_empty($username) || is_empty($password)) {
return false; return false;
@ -90,11 +93,11 @@ function authenticate_user($username, $password) {
$hash = $database->select('accounts', ['password'], ['username' => $username, "LIMIT" => 1])[0]['password']; $hash = $database->select('accounts', ['password'], ['username' => $username, "LIMIT" => 1])[0]['password'];
return (comparePassword($password, $hash)); return (comparePassword($password, $hash));
} else if ($loc == "LDAP") { } else if ($loc == "LDAP") {
return authenticate_user_ldap($username, $password) === TRUE; return authenticate_user_ldap($username, $password, $errormsg) === TRUE;
} else if ($loc == "LDAP_ONLY") { } else if ($loc == "LDAP_ONLY") {
try { try {
if (authenticate_user_ldap($username, $password) === TRUE) { if (authenticate_user_ldap($username, $password) === TRUE) {
$user = (new LdapManager($ldap_config))->getRepository('user')->findOneByUsername($username); $user = $ldap->getRepository('user')->findOneByUsername($username);
//var_dump($user); //var_dump($user);
adduser($user->getUsername(), null, $user->getName(), ($user->hasEmailAddress() ? $user->getEmailAddress() : null), "", "", 2); adduser($user->getUsername(), null, $user->getName(), ($user->hasEmailAddress() ? $user->getEmailAddress() : null), "", "", 2);
return true; return true;
@ -109,11 +112,15 @@ function authenticate_user($username, $password) {
} }
} }
function user_exists($username) {
return account_location($username) !== "NONE";
}
/** /**
* Check if a username exists in the local database. * Check if a username exists in the local database.
* @param String $username * @param String $username
*/ */
function user_exists($username) { function user_exists_local($username) {
global $database; global $database;
$username = strtolower($username); $username = strtolower($username);
return $database->has('accounts', ['username' => $username, "LIMIT" => QUERY_LIMIT]); return $database->has('accounts', ['username' => $username, "LIMIT" => QUERY_LIMIT]);
@ -121,9 +128,10 @@ function user_exists($username) {
/** /**
* Get the account status: NORMAL, TERMINATED, LOCKED_OR_DISABLED, * Get the account status: NORMAL, TERMINATED, LOCKED_OR_DISABLED,
* CHANGE_PASSWORD, or ALERT_ON_ACCESS * CHANGE_PASSWORD, ALERT_ON_ACCESS, or OTHER
* @global $database $database * @global $database $database
* @param string $username * @param string $username
* @param string $password
* @return string * @return string
*/ */
function get_account_status($username) { function get_account_status($username) {
@ -144,12 +152,11 @@ function get_account_status($username) {
] ]
)[0]['statuscode']; )[0]['statuscode'];
return $statuscode; return $statuscode;
} else if ($loc == "LDAP") { } else if ($loc == "LDAP" || $loc == "LDAP_ONLY") {
// TODO: Read actual account status from AD servers return get_account_status_ldap($username);
return "NORMAL";
} else { } else {
// account isn't setup properly // account isn't setup properly
return "LOCKED_OR_DISABLED"; return "OTHER";
} }
} }
@ -244,20 +251,21 @@ function verifyReCaptcha($response) {
* @param string $password * @param string $password
* @return mixed True if OK, else false or the error code from the server * @return mixed True if OK, else false or the error code from the server
*/ */
function authenticate_user_ldap($username, $password) { function authenticate_user_ldap($username, $password, &$errormsg) {
global $ldap_config; global $ldap;
if (is_empty($username) || is_empty($password)) { if (is_empty($username) || is_empty($password)) {
return false; return false;
} }
$username = strtolower($username); $username = strtolower($username);
try { try {
$ldapManager = new LdapManager($ldap_config);
$msg = ""; $msg = "";
$code = 0; $code = 0;
if ($ldapManager->authenticate($username, $password, $msg, $code) === TRUE) { if ($ldap->authenticate($username, $password, $msg, $code) === TRUE) {
$errormsg = $msg;
return true; return true;
} else { } else {
return $code; $errormsg = $msg;
return $msg;
} }
} catch (Exception $e) { } catch (Exception $e) {
sendError("LDAP error: " . $e->getMessage()); sendError("LDAP error: " . $e->getMessage());
@ -270,40 +278,67 @@ function authenticate_user_ldap($username, $password) {
* @param type $username * @param type $username
* @return boolean true if yes, else false * @return boolean true if yes, else false
*/ */
function user_exists_ldap($username, $password) { function user_exists_ldap($username) {
global $ldap_config; global $ldap;
try { try {
$ldap = new LdapManager($ldap_config);
$username = strtolower($username); $username = strtolower($username);
if (!$ldap->authenticate($username, $password, $message, $code)) { $lqb = $ldap->buildLdapQuery();
switch ($code) { $result = $lqb->fromUsers()
case ADResponseCodes::ACCOUNT_INVALID: ->where(['username' => $username])
return false; ->getLdapQuery()
case ADResponseCodes::ACCOUNT_CREDENTIALS_INVALID: ->getResult();
return true; if (count($result) > 0) {
case ADResponseCodes::ACCOUNT_RESTRICTIONS: return true;
return true; }
case ADResponseCodes::ACCOUNT_RESTRICTIONS_TIME: return false;
return true; } catch (Exception $e) {
case ADResponseCodes::ACCOUNT_RESTRICTIONS_DEVICE: return false;
return true; }
case ADResponseCodes::ACCOUNT_PASSWORD_EXPIRED: }
return true;
case ADResponseCodes::ACCOUNT_DISABLED: function get_account_status_ldap($username) {
return true; global $ldap;
case ADResponseCodes::ACCOUNT_CONTEXT_IDS: try {
return true; $username = strtolower($username);
case ADResponseCodes::ACCOUNT_EXPIRED: $normal = $ldap->buildLdapQuery()
return false; ->fromUsers()
case ADResponseCodes::ACCOUNT_PASSWORD_MUST_CHANGE: ->where(['enabled' => true, 'passwordMustChange' => false, 'locked' => false, 'disabled' => false, 'username' => $username])
return true; ->getLdapQuery()
case ADResponseCodes::ACCOUNT_LOCKED: ->getResult();
return true; if (count($normal) == 1) {
default: return "NORMAL";
return false; }
} $disabled = $ldap->buildLdapQuery()
->fromUsers()
->where(['disabled' => true, 'username' => $username])
->getLdapQuery()
->getResult();
$locked = $ldap->buildLdapQuery()
->fromUsers()
->where(['locked' => true, 'username' => $username])
->getLdapQuery()
->getResult();
if (count($disabled) == 1 || count($locked) == 1) {
return "LOCKED_OR_DISABLED";
}
$passwordExpired = $ldap->buildLdapQuery()
->fromUsers()
->where(['passwordMustChange' => true, 'username' => $username])
->getLdapQuery()
->getResult();
if (count($passwordExpired) == 1) {
return "CHANGE_PASSWORD";
}
$other = $ldap->buildLdapQuery()
->fromUsers()
->where(['username' => $username])
->getLdapQuery()
->getResult();
if (count($other) == 0) {
return false;
} else {
return "OTHER";
} }
return true;
} catch (Exception $e) { } catch (Exception $e) {
sendError("LDAP error: " . $e->getMessage()); sendError("LDAP error: " . $e->getMessage());
} }

Loading…
Cancel
Save