diff --git a/action.php b/action.php index 13bd694..cac0cdc 100644 --- a/action.php +++ b/action.php @@ -8,6 +8,14 @@ use LdapTools\Object\LdapObjectType; 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(); require_once __DIR__ . "/lib/login.php"; @@ -16,9 +24,9 @@ require_once __DIR__ . "/lib/worst_passwords.php"; function returnToSender($msg, $arg = "") { global $VARS; if ($arg == "") { - header("Location: home.php?page=" . urlencode($VARS['source']) . "&msg=" . $msg); + header("Location: home.php?page=" . urlencode($VARS['source']) . "&msg=$msg"); } 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(); } @@ -30,7 +38,10 @@ switch ($VARS['action']) { header('Location: index.php'); die("Logged out."); 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']) { $passrank = checkWorst500List($VARS['newpass']); if ($passrank !== FALSE) { @@ -48,15 +59,26 @@ switch ($VARS['action']) { insertAuthLog(3, $_SESSION['uid']); returnToSender("password_updated"); } else if ($acctloc == "LDAP") { - $ldapManager = new LdapManager($ldap_config); - $repository = $ldapManager->getRepository(LdapObjectType::USER); - $user = $repository->findOneByUsername($_SESSION['username']); - $user->setPassword($VARS['newpass']); + /* $ldap_config_domain + ->setUsername($_SESSION['username']) + ->setPassword($VARS['oldpass']); */ 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); + //echo "5"; insertAuthLog(3, $_SESSION['uid']); + $_SESSION['password'] = $VARS['newpass']; returnToSender("password_updated"); } catch (\Exception $e) { + echo $e->getMessage(); returnToSender("ldap_error", $e->getMessage()); } } else { diff --git a/api.php b/api.php index adce940..f5a4ecf 100644 --- a/api.php +++ b/api.php @@ -40,17 +40,33 @@ switch ($VARS['action']) { exit(json_encode(["status" => "OK"])); break; 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()); exit(json_encode(["status" => "OK", "msg" => lang("login successful", false)])); } 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)])); } break; case "userinfo": 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]; exit(json_encode(["status" => "OK", "data" => $data])); } else { @@ -76,7 +92,7 @@ switch ($VARS['action']) { 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])); } else { 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'])])); case "login": // 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]; switch (get_account_status($VARS['username'])) { case "LOCKED_OR_DISABLED": @@ -126,6 +143,9 @@ switch ($VARS['action']) { } } else { 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)])); } break; @@ -142,8 +162,8 @@ switch ($VARS['action']) { exit(json_encode(["status" => "ERROR", "msg" => lang("user does not exist", false), "user" => $VARS['manager']])); } } else { - if (user_exists($VARS['manager'])) { - if (user_exists($VARS['employee'])) { + if (user_exists_local($VARS['manager'])) { + if (user_exists_local($VARS['employee'])) { $managerid = $database->select('accounts', 'uid', ['username' => $VARS['manager']]); $employeeid = $database->select('accounts', 'uid', ['username' => $VARS['employee']]); } else { diff --git a/apps/change_password.php b/apps/change_password.php index b330fad..bd447fb 100644 --- a/apps/change_password.php +++ b/apps/change_password.php @@ -6,7 +6,7 @@ $APPS["change_password"]["title"] = "Change Password"; $APPS["change_password"]["icon"] = "key"; $APPS["change_password"]["content"] = << - + diff --git a/index.php b/index.php index ba5ae1e..64308a2 100644 --- a/index.php +++ b/index.php @@ -3,12 +3,18 @@ require_once __DIR__ . "/required.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 */ $userpass_ok = false; $multiauth = false; if ($VARS['progress'] == "1") { 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'])) { case "LOCKED_OR_DISABLED": $alert = lang("account locked", false); @@ -18,6 +24,7 @@ if ($VARS['progress'] == "1") { break; case "CHANGE_PASSWORD": $alert = lang("password expired", false); + break; case "NORMAL": $userpass_ok = true; break; @@ -27,17 +34,27 @@ if ($VARS['progress'] == "1") { break; } if ($userpass_ok) { - $_SESSION['passok'] = true; // stop logins using only username and authcode - if (userHasTOTP($VARS['username'])) { - $multiauth = true; + if (authenticate_user($VARS['username'], $VARS['password'], $autherror)) { + $_SESSION['passok'] = true; // stop logins using only username and authcode + 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 { - doLoginUser($VARS['username'], $VARS['password']); - insertAuthLog(1, $_SESSION['uid']); - header('Location: home.php'); - die("Logged in, go to home.php"); + if (!is_empty($autherror)) { + $alert = $autherror; + insertAuthLog(2, null, "Username: " . $VARS['username']); + } else { + $alert = lang("login incorrect", false); + insertAuthLog(2, null, "Username: " . $VARS['username']); + } } } - } else { + } else { // User does not exist anywhere $alert = lang("login incorrect", false); insertAuthLog(2, null, "Username: " . $VARS['username']); } @@ -71,6 +88,7 @@ if ($VARS['progress'] == "1") { <?php echo SITE_TITLE; ?> + @@ -93,7 +111,7 @@ if ($VARS['progress'] == "1") { if (!is_empty($alert)) { ?>
- +
"User does not exist.", "captcha error" => "There was a problem with the CAPTCHA (robot test). Try again.", "home" => "Home", + "ldap error" => "LDAP error: {error}", + "old and new passwords match" => "Your current and new passwords are the same." ]); \ No newline at end of file diff --git a/lang/messages.php b/lang/messages.php index 764a66e..7b9d68f 100644 --- a/lang/messages.php +++ b/lang/messages.php @@ -40,5 +40,9 @@ define("MESSAGES", [ "ldap_error" => [ "string" => "ldap server error", "type" => "danger" + ], + "passwords_same" => [ + "string" => "old and new passwords match", + "type" => "danger" ] ]); diff --git a/lib/login.php b/lib/login.php index b68d958..2a121b1 100644 --- a/lib/login.php +++ b/lib/login.php @@ -6,8 +6,11 @@ use Base32\Base32; use OTPHP\TOTP; use LdapTools\LdapManager; +use LdapTools\Object\LdapObjectType; use LdapTools\Connection\ADResponseCodes; +$ldap = new LdapManager($ldap_config); + //////////////////////////////////////////////////////////////////////////////// // Account handling // //////////////////////////////////////////////////////////////////////////////// @@ -35,7 +38,7 @@ function adduser($username, $password, $realname, $email = null, $phone1 = "", $ 'acctstatus' => 1, 'accttype' => $type ]); - var_dump($database->error()); + //var_dump($database->error()); return $database->id(); } @@ -44,10 +47,10 @@ function adduser($username, $password, $realname, $email = null, $phone1 = "", $ * @param string $username * @return string "LDAP", "LOCAL", "LDAP_ONLY", or "NONE". */ -function account_location($username, $password) { +function account_location($username) { global $database; $username = strtolower($username); - $user_exists = user_exists($username); + $user_exists = user_exists_local($username); if (!$user_exists && !LDAP_ENABLED) { return false; } @@ -62,7 +65,7 @@ function account_location($username, $password) { return "LOCAL"; } } else { - if (user_exists_ldap($username, $password)) { + if (user_exists_ldap($username)) { return "LDAP_ONLY"; } else { return "NONE"; @@ -76,9 +79,9 @@ function account_location($username, $password) { * @param string $password * @return boolean True if OK, else false */ -function authenticate_user($username, $password) { +function authenticate_user($username, $password, &$errormsg) { global $database; - global $ldap_config; + global $ldap; $username = strtolower($username); if (is_empty($username) || is_empty($password)) { return false; @@ -90,11 +93,11 @@ function authenticate_user($username, $password) { $hash = $database->select('accounts', ['password'], ['username' => $username, "LIMIT" => 1])[0]['password']; return (comparePassword($password, $hash)); } else if ($loc == "LDAP") { - return authenticate_user_ldap($username, $password) === TRUE; + return authenticate_user_ldap($username, $password, $errormsg) === TRUE; } else if ($loc == "LDAP_ONLY") { try { 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); adduser($user->getUsername(), null, $user->getName(), ($user->hasEmailAddress() ? $user->getEmailAddress() : null), "", "", 2); 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. * @param String $username */ -function user_exists($username) { +function user_exists_local($username) { global $database; $username = strtolower($username); 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, - * CHANGE_PASSWORD, or ALERT_ON_ACCESS + * CHANGE_PASSWORD, ALERT_ON_ACCESS, or OTHER * @global $database $database * @param string $username + * @param string $password * @return string */ function get_account_status($username) { @@ -144,12 +152,11 @@ function get_account_status($username) { ] )[0]['statuscode']; return $statuscode; - } else if ($loc == "LDAP") { - // TODO: Read actual account status from AD servers - return "NORMAL"; + } else if ($loc == "LDAP" || $loc == "LDAP_ONLY") { + return get_account_status_ldap($username); } else { // account isn't setup properly - return "LOCKED_OR_DISABLED"; + return "OTHER"; } } @@ -244,20 +251,21 @@ function verifyReCaptcha($response) { * @param string $password * @return mixed True if OK, else false or the error code from the server */ -function authenticate_user_ldap($username, $password) { - global $ldap_config; +function authenticate_user_ldap($username, $password, &$errormsg) { + global $ldap; if (is_empty($username) || is_empty($password)) { return false; } $username = strtolower($username); try { - $ldapManager = new LdapManager($ldap_config); $msg = ""; $code = 0; - if ($ldapManager->authenticate($username, $password, $msg, $code) === TRUE) { + if ($ldap->authenticate($username, $password, $msg, $code) === TRUE) { + $errormsg = $msg; return true; } else { - return $code; + $errormsg = $msg; + return $msg; } } catch (Exception $e) { sendError("LDAP error: " . $e->getMessage()); @@ -270,40 +278,67 @@ function authenticate_user_ldap($username, $password) { * @param type $username * @return boolean true if yes, else false */ -function user_exists_ldap($username, $password) { - global $ldap_config; +function user_exists_ldap($username) { + global $ldap; try { - $ldap = new LdapManager($ldap_config); $username = strtolower($username); - if (!$ldap->authenticate($username, $password, $message, $code)) { - switch ($code) { - case ADResponseCodes::ACCOUNT_INVALID: - return false; - case ADResponseCodes::ACCOUNT_CREDENTIALS_INVALID: - return true; - case ADResponseCodes::ACCOUNT_RESTRICTIONS: - return true; - case ADResponseCodes::ACCOUNT_RESTRICTIONS_TIME: - return true; - case ADResponseCodes::ACCOUNT_RESTRICTIONS_DEVICE: - return true; - case ADResponseCodes::ACCOUNT_PASSWORD_EXPIRED: - return true; - case ADResponseCodes::ACCOUNT_DISABLED: - return true; - case ADResponseCodes::ACCOUNT_CONTEXT_IDS: - return true; - case ADResponseCodes::ACCOUNT_EXPIRED: - return false; - case ADResponseCodes::ACCOUNT_PASSWORD_MUST_CHANGE: - return true; - case ADResponseCodes::ACCOUNT_LOCKED: - return true; - default: - return false; - } + $lqb = $ldap->buildLdapQuery(); + $result = $lqb->fromUsers() + ->where(['username' => $username]) + ->getLdapQuery() + ->getResult(); + if (count($result) > 0) { + return true; + } + return false; + } catch (Exception $e) { + return false; + } +} + +function get_account_status_ldap($username) { + global $ldap; + try { + $username = strtolower($username); + $normal = $ldap->buildLdapQuery() + ->fromUsers() + ->where(['enabled' => true, 'passwordMustChange' => false, 'locked' => false, 'disabled' => false, 'username' => $username]) + ->getLdapQuery() + ->getResult(); + if (count($normal) == 1) { + return "NORMAL"; + } + $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) { sendError("LDAP error: " . $e->getMessage()); }