Files
cacti/auth_resetpassword.php
T

331 lines
11 KiB
PHP

<?php
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
| This code is designed, written, and maintained by the Cacti Group. See |
| about.php and/or the AUTHORS file for specific developer information. |
+-------------------------------------------------------------------------+
| http://www.cacti.net/ |
+-------------------------------------------------------------------------+
*/
require('./include/global.php');
set_default_action('formidentity');
$action = grv('action');
$errorMessage = '';
$return = CACTI_PATH_URL . 'index.php';
switch ($action) {
case 'checkpass': // check if inserted password match password complexivity only
$error = secpass_check_pass(gnrv('password'));
if ($error != '') {
print $error;
} else {
print 'ok';
}
exit;
case 'formreset': // check correct hash, if incorrect lets start again
$user_hash = gfrv('hash', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[a-zA-Z0-9]+$/']]);
$hash = db_fetch_row_prepared('SELECT *
FROM user_auth_reset_hashes
WHERE hash = ?
LIMIT 1',
[$user_hash]);
if (!$hash || $hash['hash'] != $user_hash) {
$errorMessage = "<span class='badpassword_message'>" . __('Incorrect resetlink hash') . '</span>';
$action = 'formidentity';
}
break;
case 'resetrequest': // try to find user in db. If yes and has email, send resetlink
$user = [];
$identity = gnrv('identity');
if (filter_var($identity, FILTER_VALIDATE_EMAIL) ||
gfrv('identity', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[a-zA-Z0-9]+$/']])) {
$user = db_fetch_row_prepared('SELECT id, username, email_address
FROM user_auth WHERE username = ? or email_address = ? LIMIT 1',
[$identity, $identity]);
}
if (cacti_sizeof($user) && $user['email_address'] != '') {
$hash = generate_hash();
db_execute_prepared('INSERT INTO user_auth_reset_hashes
(user_id, hash, expiry)
VALUES (?, ?, date_add(now(), interval ? minute))',
[$user['id'], $hash, read_config_option('secnotify_resetlink_timeout')]);
$replacement = [
read_config_option('base_url') . CACTI_PATH_URL,
$user['username'],
read_config_option('base_url') . CACTI_PATH_URL . 'auth_resetpassword.php?action=formreset&hash=' . $hash
];
$search = ['<CACTIURL>', '<USERNAME>', '<PWDRESETLINK>'];
$body = str_replace($search, $replacement, read_config_option('secnotify_chpass_message'));
send_mail($user['email_address'], null, read_config_option('secnotify_chpass_subject'), $body, [], [], true);
cacti_log(sprintf('NOTE: Reset password request for user %s from IP %s', $user['username'], get_client_addr()), false, 'SYSTEM');
} else {
cacti_log(sprintf('NOTE: Reset password request for unknown user "%s" from IP %s', db_qstr($identity), get_client_addr()), false, 'SYSTEM');
}
$errorMessage = "<span class='badpassword_message'>" . __('Reset password token was sent. Check your mailbox') . '</span>';
$action = 'formidentity';
break;
case 'resetpassword': // check and save new password
$user_hash = gfrv('hash', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[a-zA-Z0-9]+$/']]);
$hash = db_fetch_row_prepared('SELECT *
FROM user_auth_reset_hashes
WHERE hash = ?
LIMIT 1',
[$user_hash]);
if (!$hash || $hash['hash'] != $user_hash) {
$errorMessage = "<span class='badpassword_message'>" . __('Incorrect resetlink hash') . '</span>';
$action = 'formidentity';
}
$user = db_fetch_row_prepared('SELECT *
FROM user_auth
WHERE id = ?',
[$hash['user_id']]);
// Get passwords entered for change
$password = gnrv('password');
$password_confirm = gnrv('password_confirm');
// Secpass checking
$error = secpass_check_pass($password);
// Check new password passes basic checks
if ($error != 'ok') {
$errorMessage = "<span class='badpassword_message'>$error</span>";
$action = 'formreset';
break;
}
// Check user password history
if (!secpass_check_history($user['id'], $password)) {
$action = 'formreset';
break;
}
// Password and Confirmed password checks
if ($password !== $password_confirm) {
$errorMessage = "<span class='badpassword_message'>" . __('Your new passwords do not match, please retype.') . '</span>';
$action = 'formreset';
break;
}
// Check new password does not match stored password
if (compat_password_verify($password, $user['password'])) {
$errorMessage = "<span class='badpassword_message'>" . __('Your new password cannot be the same as the old password. Please try again.') . '</span>';
$action = 'formreset';
break;
}
// If password isn't blank, password change is good to go
if ($password != '') {
if (read_config_option('secpass_expirepass') > 0) {
db_execute_prepared("UPDATE user_auth
SET lastchange = ?
WHERE id = ?
AND realm = 0
AND enabled = 'on'",
[time(), $user['id']]);
}
$history = intval(read_config_option('secpass_history'));
if ($history > 0) {
$h = db_fetch_row_prepared("SELECT password, password_history
FROM user_auth
WHERE id = ?
AND realm = 0
AND enabled = 'on'",
[$user['id']]);
$op = $h['password'];
$h = explode('|', $h['password_history']);
while (cacti_count($h) > $history - 1) {
array_shift($h);
}
$h[] = $op;
$h = implode('|', $h);
db_execute_prepared("UPDATE user_auth
SET password_history = ?
WHERE id = ?
AND realm = 0
AND enabled = 'on'",
[$h, $user['id']]);
}
db_execute_prepared('INSERT IGNORE INTO user_log
(username, result, time, ip)
VALUES (?, 3, NOW(), ?)',
[$user['username'], get_client_addr()]);
db_check_password_length();
db_execute_prepared("UPDATE user_auth
SET must_change_password = '', password = ?
WHERE id = ?",
[compat_password_hash($password,PASSWORD_DEFAULT), $user['id']]);
db_execute_prepared('DELETE FROM user_auth_reset_hashes
WHERE hash = ?',
[$user_hash]);
raise_message('password_success');
header('Location: index.php');
exit;
} else {
$action = 'resetpassword';
$return = CACTI_PATH_URL . 'resetpassword.php';
}
break;
}
// show identity form or reset password form
if (api_plugin_hook_function('custom_password', OPER_MODE_NATIVE) == OPER_MODE_RESKIN) {
exit;
}
// Create tooltip for password complexity
$secpass_tooltip = "<span style='font-weight:normal;'>" . __('Password requirements include:') . '</span><br>';
$secpass_body = '';
if (read_config_option('secpass_minlen') > 0) {
$secpass_body .= __('Must be at least %d characters in length', read_config_option('secpass_minlen'));
}
if (read_config_option('secpass_reqmixcase') == 'on') {
$secpass_body .= ($secpass_body != '' ? '<br>' : '') . __('Must include mixed case');
}
if (read_config_option('secpass_reqnum') == 'on') {
$secpass_body .= ($secpass_body != '' ? '<br>' : '') . __('Must include at least 1 number');
}
if (read_config_option('secpass_reqspec') == 'on') {
$secpass_body .= ($secpass_body != '' ? '<br>' : '') . __('Must include at least 1 special character');
}
if (read_config_option('secpass_history') != '0') {
$secpass_body .= ($secpass_body != '' ? '<br>' : '') . __('Cannot be reused for %d password changes', read_config_option('secpass_history') + 1);
}
$secpass_tooltip .= $secpass_body;
$selectedTheme = get_selected_theme();
if ($action == 'formidentity') {
$title_message = __('Please enter your Cacti username or email address.');
} elseif ($action == 'formreset') {
$title_message = __('Please enter your new Cacti password.');
} else {
$title_message = __('Reset password problem.');
}
html_auth_header('reset_password', __('Reset Password'), __('Reset Password'), $title_message);
?>
<?php if ($action == 'formidentity') { ?>
<tr>
<td class='nowrap' colspan='2'>
<input type='text' class='ui-state-default ui-corner-all' id='identity' name='identity' size='25' maxlength='50'></td>
</tr>
<tr>
<td colspan='2' class='nowrap'>
<button type='submit' class='ui-button ui-corner-all ui-widget ui-state-active' value='send'><?php print __esc('Send reset token by email'); ?></button>
<button type='button' class='ui-button ui-corner-all ui-widget' onClick='document.location="<?php print $return; ?>"' value='return'><?php print __esc('Return'); ?></button>
</td>
</tr>
<tr>
<td colspan='2'>
<input type='hidden' name='action' value='resetrequest'>
<input type='hidden' name='hash' value='<?php print gnrv('hash'); ?>'>
</td>
</tr>
<?php }
if ($action == 'formreset') {?>
<tr>
<td><?php print __('New password'); ?></td>
<td class='nowrap'>
<input type='password' class='ui-state-default ui-corner-all' id='password' name='password' autocomplete='off' size='15' maxlength='25' placeholder='********'><?php print display_tooltip($secpass_tooltip); ?>
</td>
</tr>
<tr>
<td><?php print __('Confirm password'); ?></td>
<td class='nowrap'>
<input type='password' class='ui-state-default ui-corner-all' id='password_confirm' name='password_confirm' autocomplete='off' size='15' maxlength='25' placeholder='********'>
</td>
</tr>
<tr>
<td colspan='2' class='nowrap'>
<button type='submit' class='ui-button ui-corner-all ui-widget ui-state-active' value='save'><?php print __esc('Save'); ?></button>
<button type='button' class='ui-button ui-corner-all ui-widget' onClick='document.location="<?php print $return; ?>"' value='return'><?php print __esc('Return'); ?></button>
</td>
</tr>
<tr>
<td colspan='2'>
<input type='hidden' name='action' value='resetpassword'>
<input type='hidden' name='hash' value='<?php print gnrv('hash'); ?>'>
</td>
</tr>
<?php
}
$secpass_minlen = read_config_option('secpass_minlen');
html_auth_footer('reset_password', $errorMessage, "
<script type='text/javascript'>
$(function() {
if ($('#identity').length) {
$('#identity').focus();
} else {
$('#password').focus();
}
/* clear passwords */
$('#password').val('');
$('#password_confirm').val('');
});
</script>");