| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Authentication and account functions | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  | use Base32\Base32; | 
					
						
							|  |  |  | use OTPHP\TOTP; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | use LdapTools\LdapManager; | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  | use LdapTools\Object\LdapObjectType; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  | $ldap = new LdapManager($ldap_config); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | ////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //                           Account handling                                 //
 | 
					
						
							|  |  |  | ////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |  * Add a user to the system.  /!\ Assumes input is OK /!\ | 
					
						
							|  |  |  |  * @param string $username Username, saved in lowercase. | 
					
						
							|  |  |  |  * @param string $password Password, will be hashed before saving. | 
					
						
							|  |  |  |  * @param string $realname User's real legal name | 
					
						
							|  |  |  |  * @param string $email User's email address. | 
					
						
							|  |  |  |  * @param string $phone1 Phone number #1
 | 
					
						
							|  |  |  |  * @param string $phone2 Phone number #2
 | 
					
						
							|  |  |  |  * @param string $type Account type | 
					
						
							|  |  |  |  * @return int The new user's ID number in the database. | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | function adduser($username, $password, $realname, $email = null, $phone1 = "", $phone2 = "", $type) { | 
					
						
							|  |  |  |     global $database; | 
					
						
							|  |  |  |     $database->insert('accounts', [ | 
					
						
							|  |  |  |         'username' => strtolower($username), | 
					
						
							|  |  |  |         'password' => (is_null($password) ? null : encryptPassword($password)), | 
					
						
							|  |  |  |         'realname' => $realname, | 
					
						
							|  |  |  |         'email' => $email, | 
					
						
							|  |  |  |         'phone1' => $phone1, | 
					
						
							|  |  |  |         'phone2' => $phone2, | 
					
						
							|  |  |  |         'acctstatus' => 1, | 
					
						
							|  |  |  |         'accttype' => $type | 
					
						
							|  |  |  |     ]); | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |     //var_dump($database->error());
 | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     return $database->id(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Change the password for the current user. | 
					
						
							|  |  |  |  * @global $database $database | 
					
						
							|  |  |  |  * @global LdapManager $ldap | 
					
						
							|  |  |  |  * @param string $old The current password | 
					
						
							|  |  |  |  * @param string $new The new password | 
					
						
							|  |  |  |  * @param string $new2 New password again | 
					
						
							|  |  |  |  * @param [string] $error If the function returns false, this will have an array  | 
					
						
							|  |  |  |  * with a message ID from `lang/messages.php` and (depending on the message) an  | 
					
						
							|  |  |  |  * extra string for that message. | 
					
						
							|  |  |  |  * @return boolean true if the password is changed, else false | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function change_password($old, $new, $new2, &$error) { | 
					
						
							|  |  |  |     global $database, $ldap; | 
					
						
							|  |  |  |     // make sure the new password isn't the same as the current one
 | 
					
						
							|  |  |  |     if ($old == $new) { | 
					
						
							|  |  |  |         $error = ["passwords_same"]; | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Make sure the new passwords are the same
 | 
					
						
							|  |  |  |     if ($new != $new2) { | 
					
						
							|  |  |  |         $error = ["new_password_mismatch"]; | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // check the current password
 | 
					
						
							|  |  |  |     $login_ok = authenticate_user($_SESSION['username'], $old, $errmsg, $errcode); | 
					
						
							|  |  |  |     // Allow login if the error is due to expired password
 | 
					
						
							|  |  |  |     if (!$login_ok && ($errcode == LdapTools\Connection\ADResponseCodes::ACCOUNT_PASSWORD_EXPIRED || $errcode == LdapTools\Connection\ADResponseCodes::ACCOUNT_PASSWORD_MUST_CHANGE)) { | 
					
						
							|  |  |  |         $login_ok = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if ($login_ok) { | 
					
						
							|  |  |  |         // Check the new password and make sure it's not stupid
 | 
					
						
							|  |  |  |         require_once __DIR__ . "/worst_passwords.php"; | 
					
						
							|  |  |  |         $passrank = checkWorst500List($new); | 
					
						
							|  |  |  |         if ($passrank !== FALSE) { | 
					
						
							|  |  |  |             $error = ["password_500", $passrank]; | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (strlen($new) < MIN_PASSWORD_LENGTH) { | 
					
						
							|  |  |  |             $error = ["weak_password"]; | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Figure out how to change the password, then do it
 | 
					
						
							|  |  |  |         $acctloc = account_location($_SESSION['username']); | 
					
						
							|  |  |  |         if ($acctloc == "LOCAL") { | 
					
						
							| 
									
										
										
										
											2017-06-24 23:35:07 -06:00
										 |  |  |             $database->update('accounts', ['password' => encryptPassword($new), 'acctstatus' => 1], ['uid' => $_SESSION['uid']]); | 
					
						
							|  |  |  |             $_SESSION['password'] = $new; | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  |             insertAuthLog(3, $_SESSION['uid']); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } else if ($acctloc == "LDAP") { | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |                 $repository = $ldap->getRepository(LdapObjectType::USER); | 
					
						
							|  |  |  |                 $user = $repository->findOneByUsername($_SESSION['username']); | 
					
						
							| 
									
										
										
										
											2017-06-24 23:35:07 -06:00
										 |  |  |                 $user->setPassword($new); | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  |                 $user->setpasswordMustChange(false); | 
					
						
							|  |  |  |                 $ldap->persist($user); | 
					
						
							| 
									
										
										
										
											2017-06-24 23:35:07 -06:00
										 |  |  |                 $database->update('accounts', ['acctstatus' => 1], ['uid' => $_SESSION['uid']]); | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  |                 insertAuthLog(3, $_SESSION['uid']); | 
					
						
							| 
									
										
										
										
											2017-06-24 23:35:07 -06:00
										 |  |  |                 $_SESSION['password'] = $new; | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  |                 return true; | 
					
						
							|  |  |  |             } catch (\Exception $e) { | 
					
						
							|  |  |  |                 // Stupid password complexity BS error
 | 
					
						
							|  |  |  |                 if (strpos($e->getMessage(), "DSID-031A11E5") !== FALSE) { | 
					
						
							|  |  |  |                     $error = ["password_complexity"]; | 
					
						
							|  |  |  |                     return false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 $error = ["ldap_error", $e->getMessage()]; | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $error = ["account_state_error"]; | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     $error = ["old_password_mismatch"]; | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Get where a user's account actually is. | 
					
						
							|  |  |  |  * @param string $username | 
					
						
							|  |  |  |  * @return string "LDAP", "LOCAL", "LDAP_ONLY", or "NONE". | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  | function account_location($username) { | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     global $database; | 
					
						
							|  |  |  |     $username = strtolower($username); | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |     $user_exists = user_exists_local($username); | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     if (!$user_exists && !LDAP_ENABLED) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if ($user_exists) { | 
					
						
							|  |  |  |         $userinfo = $database->select('accounts', ['password'], ['username' => $username])[0]; | 
					
						
							|  |  |  |         // if password empty, it's an LDAP user
 | 
					
						
							|  |  |  |         if (is_empty($userinfo['password']) && LDAP_ENABLED) { | 
					
						
							|  |  |  |             return "LDAP"; | 
					
						
							|  |  |  |         } else if (is_empty($userinfo['password']) && !LDAP_ENABLED) { | 
					
						
							|  |  |  |             return "NONE"; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return "LOCAL"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |         if (user_exists_ldap($username)) { | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |             return "LDAP_ONLY"; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return "NONE"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Checks the given credentials against the database. | 
					
						
							|  |  |  |  * @param string $username | 
					
						
							|  |  |  |  * @param string $password | 
					
						
							|  |  |  |  * @return boolean True if OK, else false | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  | function authenticate_user($username, $password, &$errormsg, &$errorcode) { | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     global $database; | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |     global $ldap; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     $username = strtolower($username); | 
					
						
							|  |  |  |     if (is_empty($username) || is_empty($password)) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     $loc = account_location($username, $password); | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  |     switch ($loc) { | 
					
						
							|  |  |  |         case "LOCAL": | 
					
						
							|  |  |  |             $hash = $database->select('accounts', ['password'], ['username' => $username, "LIMIT" => 1])[0]['password']; | 
					
						
							|  |  |  |             return (comparePassword($password, $hash)); | 
					
						
							|  |  |  |         case "LDAP": | 
					
						
							|  |  |  |             return authenticate_user_ldap($username, $password, $errormsg, $errorcode) === TRUE; | 
					
						
							|  |  |  |         case "LDAP_ONLY": | 
					
						
							|  |  |  |             // Authenticate with LDAP and create database account
 | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |                 if (authenticate_user_ldap($username, $password, $errormsg, $errorcode) === TRUE) { | 
					
						
							|  |  |  |                     $user = $ldap->getRepository('user')->findOneByUsername($username); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     adduser($user->getUsername(), null, $user->getName(), ($user->hasEmailAddress() ? $user->getEmailAddress() : null), "", "", 2); | 
					
						
							|  |  |  |                     return true; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } catch (Exception $e) { | 
					
						
							|  |  |  |                 $errormsg = $e->getMessage(); | 
					
						
							|  |  |  |                 return false; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  |         default: | 
					
						
							| 
									
										
										
										
											2017-05-07 13:02:18 -06:00
										 |  |  |             return false; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  | function user_exists($username) { | 
					
						
							| 
									
										
										
										
											2017-07-03 03:00:23 -06:00
										 |  |  |     return account_location(strtolower($username)) !== "NONE"; | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Check if a username exists in the local database. | 
					
						
							|  |  |  |  * @param String $username | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  | function user_exists_local($username) { | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     global $database; | 
					
						
							|  |  |  |     $username = strtolower($username); | 
					
						
							|  |  |  |     return $database->has('accounts', ['username' => $username, "LIMIT" => QUERY_LIMIT]); | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Get the account status: NORMAL, TERMINATED, LOCKED_OR_DISABLED, | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |  * CHANGE_PASSWORD, ALERT_ON_ACCESS, or OTHER | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |  * @global $database $database | 
					
						
							|  |  |  |  * @param string $username | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |  * @param string $password | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |  * @return string | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-05-07 13:02:18 -06:00
										 |  |  | function get_account_status($username, &$error) { | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     global $database; | 
					
						
							|  |  |  |     $username = strtolower($username); | 
					
						
							|  |  |  |     $loc = account_location($username); | 
					
						
							|  |  |  |     if ($loc == "LOCAL") { | 
					
						
							|  |  |  |         $statuscode = $database->select('accounts', [ | 
					
						
							|  |  |  |                     '[>]acctstatus' => [ | 
					
						
							|  |  |  |                         'acctstatus' => 'statusid' | 
					
						
							|  |  |  |                     ] | 
					
						
							|  |  |  |                         ], [ | 
					
						
							|  |  |  |                     'accounts.acctstatus', | 
					
						
							|  |  |  |                     'acctstatus.statuscode' | 
					
						
							|  |  |  |                         ], [ | 
					
						
							|  |  |  |                     'username' => $username, | 
					
						
							|  |  |  |                     "LIMIT" => 1 | 
					
						
							|  |  |  |                         ] | 
					
						
							|  |  |  |                 )[0]['statuscode']; | 
					
						
							|  |  |  |         return $statuscode; | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |     } else if ($loc == "LDAP" || $loc == "LDAP_ONLY") { | 
					
						
							| 
									
										
										
										
											2017-05-07 13:02:18 -06:00
										 |  |  |         return get_account_status_ldap($username, $error); | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         // account isn't setup properly
 | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |         return "OTHER"; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-25 00:43:33 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Check if the given username has the given permission (or admin access) | 
					
						
							|  |  |  |  * @global $database $database | 
					
						
							|  |  |  |  * @param string $username | 
					
						
							|  |  |  |  * @param string $permcode | 
					
						
							|  |  |  |  * @return boolean TRUE if the user has the permission (or admin access), else FALSE | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function account_has_permission($username, $permcode) { | 
					
						
							|  |  |  |     global $database; | 
					
						
							|  |  |  |     return $database->has('assigned_permissions', [ | 
					
						
							|  |  |  |                 '[>]accounts' => [ | 
					
						
							|  |  |  |                     'uid' => 'uid' | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |                 '[>]permissions' => [ | 
					
						
							|  |  |  |                     'permid' => 'permid' | 
					
						
							|  |  |  |                 ] | 
					
						
							| 
									
										
										
										
											2017-05-28 23:45:50 -06:00
										 |  |  |                     ], ['AND' => ['OR' => ['permcode #code' => $permcode, 'permcode #admin' => 'ADMIN'], 'username' => $username]]) === TRUE; | 
					
						
							| 
									
										
										
										
											2017-05-25 00:43:33 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | ////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //                              Login handling                                //
 | 
					
						
							|  |  |  | ////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Setup $_SESSION values to log in a user | 
					
						
							|  |  |  |  * @param string $username | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | function doLoginUser($username, $password) { | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  |     global $database; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     $username = strtolower($username); | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  |     $userinfo = $database->select('accounts', ['email', 'uid', 'realname'], ['username' => $username])[0]; | 
					
						
							|  |  |  |     $_SESSION['username'] = $username; | 
					
						
							|  |  |  |     $_SESSION['uid'] = $userinfo['uid']; | 
					
						
							|  |  |  |     $_SESSION['email'] = $userinfo['email']; | 
					
						
							|  |  |  |     $_SESSION['realname'] = $userinfo['realname']; | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  |     $_SESSION['password'] = $password; // needed for accessing data in other apps
 | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  |     $_SESSION['loggedin'] = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Send an alert email to the system admin | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  * Used when an account with the status ALERT_ON_ACCESS logs in | 
					
						
							|  |  |  |  * @param String $username the account username | 
					
						
							| 
									
										
										
										
											2017-06-21 21:01:02 -06:00
										 |  |  |  * @return Mixed TRUE if successful, error string if not | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-06-23 15:53:09 -06:00
										 |  |  | function sendLoginAlertEmail($username, $appname = SITE_TITLE) { | 
					
						
							| 
									
										
										
										
											2017-06-21 21:01:02 -06:00
										 |  |  |     if (is_empty(ADMIN_EMAIL) || filter_var(ADMIN_EMAIL, FILTER_VALIDATE_EMAIL) === FALSE) { | 
					
						
							| 
									
										
										
										
											2017-07-03 03:00:23 -06:00
										 |  |  |         return "invalid_to_email"; | 
					
						
							| 
									
										
										
										
											2017-06-21 21:01:02 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (is_empty(FROM_EMAIL) || filter_var(FROM_EMAIL, FILTER_VALIDATE_EMAIL) === FALSE) { | 
					
						
							| 
									
										
										
										
											2017-07-03 03:00:23 -06:00
										 |  |  |         return "invalid_from_email"; | 
					
						
							| 
									
										
										
										
											2017-06-21 21:01:02 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-03 03:00:23 -06:00
										 |  |  |     $username = strtolower($username); | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2017-06-21 21:01:02 -06:00
										 |  |  |     $mail = new PHPMailer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (DEBUG) { | 
					
						
							|  |  |  |         $mail->SMTPDebug = 2; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (USE_SMTP) { | 
					
						
							|  |  |  |         $mail->isSMTP(); | 
					
						
							|  |  |  |         $mail->Host = SMTP_HOST; | 
					
						
							|  |  |  |         $mail->SMTPAuth = SMTP_AUTH; | 
					
						
							|  |  |  |         $mail->Username = SMTP_USER; | 
					
						
							|  |  |  |         $mail->Password = SMTP_PASS; | 
					
						
							|  |  |  |         $mail->SMTPSecure = SMTP_SECURE; | 
					
						
							|  |  |  |         $mail->Port = SMTP_PORT; | 
					
						
							|  |  |  |         if (SMTP_ALLOW_INVALID_CERTIFICATE) { | 
					
						
							|  |  |  |             $mail->SMTPOptions = array( | 
					
						
							|  |  |  |                 'ssl' => array( | 
					
						
							|  |  |  |                     'verify_peer' => false, | 
					
						
							|  |  |  |                     'verify_peer_name' => false, | 
					
						
							|  |  |  |                     'allow_self_signed' => true | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $mail->setFrom(FROM_EMAIL, 'Account Alerts'); | 
					
						
							|  |  |  |     $mail->addAddress(ADMIN_EMAIL, "System Admin"); | 
					
						
							|  |  |  |     $mail->isHTML(false); | 
					
						
							|  |  |  |     $mail->Subject = lang("admin alert email subject", false); | 
					
						
							| 
									
										
										
										
											2017-06-23 15:48:45 -06:00
										 |  |  |     $mail->Body = lang2("admin alert email message", ["username" => $username, "datetime" => date("Y-m-d H:i:s"), "ipaddr" => getClientIP(), "appname" => $appname], false); | 
					
						
							| 
									
										
										
										
											2017-06-21 21:01:02 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!$mail->send()) { | 
					
						
							|  |  |  |         return $mail->ErrorInfo; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return TRUE; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 14:29:22 -06:00
										 |  |  | function insertAuthLog($type, $uid = null, $data = "") { | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     global $database; | 
					
						
							| 
									
										
										
										
											2017-05-01 14:29:22 -06:00
										 |  |  |     // find IP address
 | 
					
						
							| 
									
										
										
										
											2017-06-16 18:40:12 -06:00
										 |  |  |     $ip = getClientIP(); | 
					
						
							| 
									
										
										
										
											2017-05-22 02:58:03 -06:00
										 |  |  |     $database->insert("authlog", ['logtime' => date("Y-m-d H:i:s"), 'logtype' => $type, 'uid' => $uid, 'ip' => $ip, 'otherdata' => $data]); | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function verifyReCaptcha($response) { | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |         $client = new GuzzleHttp\Client(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $client | 
					
						
							|  |  |  |                 ->request('POST', "https://www.google.com/recaptcha/api/siteverify", [ | 
					
						
							|  |  |  |             'form_params' => [ | 
					
						
							|  |  |  |                 'secret' => RECAPTCHA_SECRET_KEY, | 
					
						
							|  |  |  |                 'response' => $response | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($response->getStatusCode() != 200) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $resp = json_decode($response->getBody(), TRUE); | 
					
						
							|  |  |  |         if ($resp['success'] === true) { | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } catch (Exception $e) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //                              LDAP handling                                 //
 | 
					
						
							|  |  |  | ////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Checks the given credentials against the LDAP server. | 
					
						
							|  |  |  |  * @param string $username | 
					
						
							|  |  |  |  * @param string $password | 
					
						
							|  |  |  |  * @return mixed True if OK, else false or the error code from the server | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  | function authenticate_user_ldap($username, $password, &$errormsg, &$errorcode) { | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |     global $ldap; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     if (is_empty($username) || is_empty($password)) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     $username = strtolower($username); | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |         $msg = ""; | 
					
						
							|  |  |  |         $code = 0; | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |         if ($ldap->authenticate($username, $password, $msg, $code) === TRUE) { | 
					
						
							|  |  |  |             $errormsg = $msg; | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  |             $errorcode = $code; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |             return true; | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |             $errormsg = $msg; | 
					
						
							| 
									
										
										
										
											2017-05-13 16:17:58 -06:00
										 |  |  |             $errorcode = $code; | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |             return $msg; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2017-05-07 13:02:18 -06:00
										 |  |  |         $errormsg = $e->getMessage(); | 
					
						
							|  |  |  |         return $e->getMessage(); | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Check if a username exists on the LDAP server. | 
					
						
							|  |  |  |  * @global type $ldap_config | 
					
						
							|  |  |  |  * @param type $username | 
					
						
							|  |  |  |  * @return boolean true if yes, else false | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  | function user_exists_ldap($username) { | 
					
						
							|  |  |  |     global $ldap; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     try { | 
					
						
							|  |  |  |         $username = strtolower($username); | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |         $lqb = $ldap->buildLdapQuery(); | 
					
						
							|  |  |  |         $result = $lqb->fromUsers() | 
					
						
							|  |  |  |                 ->where(['username' => $username]) | 
					
						
							|  |  |  |                 ->getLdapQuery() | 
					
						
							|  |  |  |                 ->getResult(); | 
					
						
							|  |  |  |         if (count($result) > 0) { | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } catch (Exception $e) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-07 13:02:18 -06:00
										 |  |  | function get_account_status_ldap($username, &$error) { | 
					
						
							| 
									
										
										
										
											2017-05-06 23:19:22 -06:00
										 |  |  |     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"; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2017-05-07 13:02:18 -06:00
										 |  |  |         $error = $e->getMessage(); | 
					
						
							|  |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | //                          2-factor authentication                           //
 | 
					
						
							|  |  |  | ////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Check if a user has TOTP setup | 
					
						
							|  |  |  |  * @global $database $database | 
					
						
							|  |  |  |  * @param string $username | 
					
						
							|  |  |  |  * @return boolean true if TOTP secret exists, else false | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function userHasTOTP($username) { | 
					
						
							|  |  |  |     global $database; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     $username = strtolower($username); | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  |     $secret = $database->select('accounts', 'authsecret', ['username' => $username])[0]; | 
					
						
							|  |  |  |     if (is_empty($secret)) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2017-04-16 02:05:18 -06:00
										 |  |  |  * Generate a TOTP secret for the given user. | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  |  * @param string $username | 
					
						
							|  |  |  |  * @return string OTP provisioning URI (for generating a QR code) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function newTOTP($username) { | 
					
						
							|  |  |  |     global $database; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     $username = strtolower($username); | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  |     $secret = random_bytes(20); | 
					
						
							|  |  |  |     $encoded_secret = Base32::encode($secret); | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     $userdata = $database->select('accounts', ['email', 'authsecret', 'realname'], ['username' => $username])[0]; | 
					
						
							|  |  |  |     $totp = new TOTP((is_null($userdata['email']) ? $userdata['realname'] : $userdata['email']), $encoded_secret); | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  |     $totp->setIssuer(SYSTEM_NAME); | 
					
						
							|  |  |  |     return $totp->getProvisioningUri(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-16 02:05:18 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Save a TOTP secret for the user. | 
					
						
							|  |  |  |  * @global $database $database | 
					
						
							|  |  |  |  * @param string $username | 
					
						
							|  |  |  |  * @param string $secret | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function saveTOTP($username, $secret) { | 
					
						
							|  |  |  |     global $database; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     $username = strtolower($username); | 
					
						
							| 
									
										
										
										
											2017-04-16 02:05:18 -06:00
										 |  |  |     $database->update('accounts', ['authsecret' => $secret], ['username' => $username]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Verify a TOTP multiauth code | 
					
						
							|  |  |  |  * @global $database | 
					
						
							|  |  |  |  * @param string $username | 
					
						
							|  |  |  |  * @param int $code | 
					
						
							|  |  |  |  * @return boolean true if it's legit, else false | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function verifyTOTP($username, $code) { | 
					
						
							|  |  |  |     global $database; | 
					
						
							| 
									
										
										
										
											2017-04-29 02:35:49 -06:00
										 |  |  |     $username = strtolower($username); | 
					
						
							| 
									
										
										
										
											2017-04-14 21:40:24 -06:00
										 |  |  |     $userdata = $database->select('accounts', ['email', 'authsecret'], ['username' => $username])[0]; | 
					
						
							|  |  |  |     if (is_empty($userdata['authsecret'])) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     $totp = new TOTP(null, $userdata['authsecret']); | 
					
						
							|  |  |  |     return $totp->verify($code); | 
					
						
							|  |  |  | } |