['regexp' => '/^[a-zA-Z0-9]+$/']]); $hash = db_fetch_row_prepared('SELECT * FROM user_auth_reset_hashes WHERE hash = ? AND expiry > NOW() LIMIT 1', [$user_hash]); if (!$hash || $hash['hash'] != $user_hash) { $errorMessage = "" . __('Incorrect resetlink hash') . ''; $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 = ?) AND realm = 0 AND enabled = "on" AND password_change = "on" LIMIT 1', [$identity, $identity]); } if (cacti_sizeof($user) && $user['email_address'] != '') { $hash = generate_hash(); if (auth_reset_token_replace($user['id'], $hash, (int) 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 = ['', '', '']; $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('ERROR: Unable to replace password reset token for user %s', $user['username']), false, 'AUTH'); } } else { cacti_log(sprintf('NOTE: Reset password request for unknown user "%s" from IP %s', db_qstr($identity), get_client_addr()), false, 'SYSTEM'); } $errorMessage = "" . __('Reset password token was sent. Check your mailbox') . ''; $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 = ? AND expiry > NOW() LIMIT 1', [$user_hash]); if (!$hash || $hash['hash'] != $user_hash) { $errorMessage = "" . __('Incorrect resetlink hash') . ''; $action = 'formidentity'; break; } $user = db_fetch_row_prepared('SELECT * FROM user_auth WHERE id = ? AND realm = 0 AND enabled = "on"', [$hash['user_id']]); if (!cacti_sizeof($user)) { $errorMessage = "" . __('Password reset is not available for this account.') . ''; $action = 'formidentity'; break; } // 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 = "$error"; $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 = "" . __('Your new passwords do not match, please retype.') . ''; $action = 'formreset'; break; } // Check new password does not match stored password if (compat_password_verify($password, $user['password'])) { $errorMessage = "" . __('Your new password cannot be the same as the old password. Please try again.') . ''; $action = 'formreset'; break; } // If password isn't blank, password change is good to go if ($password != '') { db_check_password_length(); if (!db_begin_transaction()) { $errorMessage = "" . __('Unable to securely consume the password reset link.') . ''; $action = 'formidentity'; break; } $locked_user = db_fetch_cell_prepared('SELECT id FROM user_auth WHERE id = ? AND realm = 0 AND enabled = "on" FOR UPDATE', [$user['id']]); $locked_token_user = db_fetch_cell_prepared('SELECT user_id FROM user_auth_reset_hashes WHERE user_id = ? AND hash = ? AND expiry > NOW() FOR UPDATE', [$user['id'], $user_hash]); if ((int) $locked_user !== (int) $user['id'] || (int) $locked_token_user !== (int) $user['id']) { db_rollback_transaction(); $errorMessage = "" . __('Incorrect resetlink hash') . ''; $action = 'formidentity'; break; } $reset_ok = true; if (read_config_option('secpass_expirepass') > 0) { $reset_ok = db_execute_prepared("UPDATE user_auth SET lastchange = ? WHERE id = ? AND realm = 0 AND enabled = 'on'", [time(), $user['id']]) !== false; } $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); $reset_ok = $reset_ok && db_execute_prepared("UPDATE user_auth SET password_history = ? WHERE id = ? AND realm = 0 AND enabled = 'on'", [$h, $user['id']]) !== false; } $reset_ok = $reset_ok && db_execute_prepared('INSERT IGNORE INTO user_log (username, result, time, ip) VALUES (?, 3, NOW(), ?)', [$user['username'], get_client_addr()]) !== false; $reset_ok = $reset_ok && db_execute_prepared("UPDATE user_auth SET must_change_password = '', password = ? WHERE id = ?", [compat_password_hash($password, PASSWORD_DEFAULT), $user['id']]) !== false; $reset_ok = $reset_ok && db_execute_prepared('DELETE FROM user_auth_reset_hashes WHERE user_id = ?', [$user['id']]) !== false; $reset_ok = $reset_ok && db_execute_prepared('DELETE FROM user_auth_cache WHERE user_id = ?', [$user['id']]) !== false; $reset_ok = $reset_ok && db_execute_prepared('DELETE FROM sessions WHERE user_id = ?', [$user['id']]) !== false; if (!$reset_ok || !db_commit_transaction()) { db_rollback_transaction(); $errorMessage = "" . __('Unable to securely consume the password reset link.') . ''; $action = 'formidentity'; break; } 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 = "" . __('Password requirements include:') . '
'; $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 != '' ? '
' : '') . __('Must include mixed case'); } if (read_config_option('secpass_reqnum') == 'on') { $secpass_body .= ($secpass_body != '' ? '
' : '') . __('Must include at least 1 number'); } if (read_config_option('secpass_reqspec') == 'on') { $secpass_body .= ($secpass_body != '' ? '
' : '') . __('Must include at least 1 special character'); } if (read_config_option('secpass_history') != '0') { $secpass_body .= ($secpass_body != '' ? '
' : '') . __('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); ?> '> '> $(function() { if ($('#identity').length) { $('#identity').focus(); } else { $('#password').focus(); } /* clear passwords */ $('#password').val(''); $('#password_confirm').val(''); }); ");