You are here

function login_security_validate in Login Security 7

Same name and namespace in other branches
  1. 8 login_security.module \login_security_validate()
  2. 5 login_security.module \login_security_validate()
  3. 6 login_security.module \login_security_validate()
  4. 2.x login_security.module \login_security_validate()

Implements hook_validate().

This functions does more than just validating, but it's main intention is to break the login form flow.

1 string reference to 'login_security_validate'
login_security_form_alter in ./login_security.module
Implements hook_form_alter().

File

./login_security.module, line 170
Login Security

Code

function login_security_validate($form, &$form_state) {

  // Sanitize user input.
  $name = $form_state['values']['name'];

  // Null username should not be tracked.
  if (!strlen($name)) {
    return;
  }

  // Expire old tracked entries.
  _login_security_remove_events();

  // Populate variables to be used in any module message or login operation.
  $variables = _login_security_get_variables_by_name($name);

  // First, check if administrator should be notified of unexpected login
  // activity.
  // Only process if configured threshold > 1.
  // see: http://drupal.org/node/583092.
  if ($variables['@activity_threshold']) {

    // Check if threshold has been reached.
    if ($variables['@tracking_current_count'] > $variables['@activity_threshold']) {

      // Check if admin has been already alerted.
      if (!variable_get('login_security_threshold_notified', LOGIN_SECURITY_THRESHOLD_NOTIFIED)) {

        // Mark alert status as notified and send the email.
        watchdog('login_security', 'Ongoing attack detected: Suspicious activity detected in login form submissions. Too many invalid login attempts threshold reached: currently @tracking_current_count events are tracked, and threshold is configured for @activity_threshold attempts.', $variables, WATCHDOG_WARNING);
        variable_set('login_security_threshold_notified', TRUE);

        // Submit email only if required.
        $login_activity_email_user = variable_get('login_security_login_activity_email_user', LOGIN_SECURITY_LOGIN_ACTIVITY_EMAIL_USER);
        if ($login_activity_email_user !== '') {
          $from = variable_get('site_mail', ini_get('sendmail_from'));
          $admin_mail = db_query_range("SELECT mail FROM {users} WHERE name = :name", 0, 1, array(
            ':name' => $login_activity_email_user,
          ))
            ->fetchField();
          $mail = drupal_mail('login_security', 'login_activity_notify', $admin_mail, language_default(), $variables, $from, TRUE);
        }
      }
    }
    elseif (variable_get('login_security_threshold_notified', TRUE) && $variables['@tracking_current_count'] < $variables['@activity_threshold'] / 3) {

      // Reset alert if currently tracked events is < threshold / 3.
      watchdog('login_security', 'Suspicious activity in login form submissions is no longer detected: currently @tracking_current_count events are being tracked, and threshold is configured for @activity_threshold maximum allowed attempts).', $variables, WATCHDOG_NOTICE);
      variable_set('login_security_threshold_notified', FALSE);
    }
  }

  // Check for host login attempts: Hard.
  if ($variables['@hard_block_attempts'] >= 1) {
    if ($variables['@ip_current_count'] >= $variables['@hard_block_attempts']) {

      // Block the host ip_address().
      login_user_block_ip($variables);
    }
  }

  // Check for user login attempts.
  if ($variables['@user_block_attempts'] >= 1) {
    if ($variables['@user_current_count'] >= $variables['@user_block_attempts']) {

      // Block the account $name.
      login_user_block_user_name($variables);
    }
  }

  // At this point, they're either logged in or not by Drupal core's abuse of
  // the validation hook to login users completely.
  global $user;

  // Login failed.
  $messages = drupal_get_messages('error', FALSE);
  if (!empty($messages['error'])) {
    $password_message = preg_grep("/<a href=\"\\/user\\/password\\?name={$name}\">Have you forgotten your password\\?<\\/a>/", $messages['error']);
    $block_message = preg_grep("/The username <em class=\"placeholder\">{$name}<\\/em> has not been activated or is blocked./", $messages['error']);
    if (!count($password_message) || !count($block_message)) {
      if (variable_get('login_security_disable_core_login_error', LOGIN_SECURITY_DISABLE_CORE_LOGIN_ERROR)) {

        // Resets the form error status so no form fields are highlighted in
        // red.
        $form_state['rebuild'] = TRUE;
        form_clear_error();

        // Removes "Sorry, unrecognized username or password. Have you
        // forgotten your password?" and "The username $name has not been
        // activated or is blocked.", and any other errors that might be
        // helpful to an attacker it should not reset the attempts message
        // because it is a warning, not an error.
        drupal_get_messages('error', TRUE);
      }

      // Should the user be advised about the remaining login attempts?
      $notice_user = variable_get('login_security_notice_attempts_available', LOGIN_SECURITY_NOTICE_ATTEMPTS_AVAILABLE);
      if ($notice_user == TRUE && $variables['@user_block_attempts'] > 0 && $variables['@user_block_attempts'] >= $variables['@user_current_count']) {
        $message_raw = variable_get('login_security_notice_attempts_message', LOGIN_SECURITY_NOTICE_ATTEMPTS_MESSAGE);

        // Simple flag that can be changed using hook_alter (see below).
        $display_block_attempts = TRUE;

        // Allow other module to change the flag, or even the message displayed,
        // with a custom logic.
        drupal_alter('login_security_display_block_attempts', $message_raw, $display_block_attempts, $variables['@user_current_count']);
        $message = array(
          'message' => $message_raw,
          'variables' => $variables,
        );

        // This loop is used instead of doing t() because t() can only
        // translate static strings, not variables.
        // Ignoring Coder because $variables is sanitized by
        // login_security_t().
        // See https://drupal.org/node/1743996#comment-6421246.
        // @ignore security_2
        $message = login_security_t($message['message'], $message['variables']);
        if ($display_block_attempts) {
          drupal_set_message($message, 'warning', TRUE);
        }
      }
    }
  }
}