/**
  * When option value is updated
  * do same checks as plugin itself does
  * and log if we match something
  */
 function on_option_limit_login_lockouts_total($value)
 {
     global $limit_login_just_lockedout;
     if (!$limit_login_just_lockedout) {
         return $value;
     }
     $ip = limit_login_get_address();
     $whitelisted = is_limit_login_ip_whitelisted($ip);
     $retries = get_option('limit_login_retries');
     if (!is_array($retries)) {
         $retries = array();
     }
     if (isset($retries[$ip]) && $retries[$ip] / limit_login_option('allowed_retries') % limit_login_option('notify_email_after') != 0) {
         // $this->notice( "user locked out but don't log" );
         //return;
     }
     /* Format message. First current lockout duration */
     $lockout_type = "";
     if (!isset($retries[$ip])) {
         /* longer lockout */
         $lockout_type = "longer";
         $count = limit_login_option('allowed_retries') * limit_login_option('allowed_lockouts');
         $lockouts = limit_login_option('allowed_lockouts');
         $time = round(limit_login_option('long_duration') / 3600);
         #$when = sprintf( _n( '%d hour', '%d hours', $time, "Logger: Plugin Limit Login Attempts", 'limit-login-attempts' ), $time );
     } else {
         /* normal lockout */
         $lockout_type = "normal";
         $count = $retries[$ip];
         $lockouts = floor($count / limit_login_option('allowed_retries'));
         $time = round(limit_login_option('lockout_duration') / 60);
         //$when = sprintf( _n( '%d minute', '%d minutes', $time, 'limit-login-attempts' ), $time );
     }
     if ($whitelisted) {
         // $subject = __( "Failed login attempts from whitelisted IP", 'limit-login-attempts' );
         $message_key = "failed_login_whitelisted";
     } else {
         // $subject = __( "Too many failed login attempts", 'limit-login-attempts' );
         $message_key = "failed_login";
     }
     $this->noticeMessage($message_key, array("_initiator" => SimpleLoggerLogInitiators::WEB_USER, "value" => $value, "limit_login_just_lockedout" => $limit_login_just_lockedout, "count" => $count, "time" => $time, "lockouts" => $lockouts, "ip" => $ip, "lockout_type" => $lockout_type));
     return $value;
 }
示例#2
0
 public static function authenticateFilter($authUser, $username, $passwd)
 {
     wfConfig::inc('totalLoginHits');
     //The total hits to wp-login.php including logins, logouts and just hits.
     $IP = wfUtils::getIP();
     $secEnabled = wfConfig::get('loginSecurityEnabled');
     if ($secEnabled && !self::getLog()->isWhitelisted($IP) && wfConfig::get('isPaid')) {
         $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
         if (isset($twoFactorUsers) && is_array($twoFactorUsers) && sizeof($twoFactorUsers) > 0) {
             $userDat = isset($_POST['wordfence_userDat']) ? $_POST['wordfence_userDat'] : false;
             if (is_object($userDat) && get_class($authUser) == 'WP_User') {
                 //Valid username and password either with or without the 'wf...' code. Users is now logged in at this point.
                 if (isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor']) {
                     //user entered a valid user and password with ' wf....' appended
                     foreach ($twoFactorUsers as &$t) {
                         if ($t[0] == $userDat->ID && $t[3] == 'activated') {
                             if ($_POST['wordfence_authFactor'] == $t[2] && $t[4] > time()) {
                                 // Set this 2FA code to expire in 30 seconds (for other plugins hooking into the auth process)
                                 $t[4] = time() + 30;
                                 wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
                             } else {
                                 if ($_POST['wordfence_authFactor'] == $t[2]) {
                                     $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                                     try {
                                         $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $t[1]));
                                         if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                                             break;
                                             //Let them sign in without two factor
                                         }
                                         if (isset($codeResult['ok']) && $codeResult['ok']) {
                                             $t[2] = $codeResult['code'];
                                             $t[4] = time() + 1800;
                                             //30 minutes until code expires
                                             wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
                                             //save the code the user needs to enter and return an error.
                                             self::$authError = new WP_Error('twofactor_required', __('<strong>CODE EXPIRED. CHECK YOUR PHONE:</strong> The code you entered has expired. Codes are only valid for 30 minutes for security reasons. We have sent you a new code. Please sign in using your username and your password followed by a space and the new code we sent you.'));
                                             return self::$authError;
                                         } else {
                                             break;
                                             //No new code was received. Let them sign in with the expired code.
                                         }
                                     } catch (Exception $e) {
                                         // Couldn't connect to noc1, let them sign in since the password was correct.
                                         break;
                                     }
                                 } else {
                                     //Bad code, so cancel the login and return an error to user.
                                     self::$authError = new WP_Error('twofactor_required', __('<strong>INVALID CODE</strong>: You need to enter your password followed by a space and the code we sent to your phone. The code should start with \'wf\' and should be four characters. e.g. wfAB12. In this case you would enter your password as: \'mypassword wfAB12\' without quotes.'));
                                     return self::$authError;
                                 }
                             }
                         }
                         //No user matches and has TF activated so let user sign in.
                     }
                 } else {
                     //valid login with no code entered
                     foreach ($twoFactorUsers as &$t) {
                         if ($t[0] == $userDat->ID && $t[3] == 'activated') {
                             //Yup, enabled, so lets send the code
                             $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                             try {
                                 $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $t[1]));
                                 if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                                     break;
                                     //Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up.
                                 }
                             } catch (Exception $e) {
                                 // Couldn't connect to noc1, let them sign in since the password was correct.
                                 break;
                             }
                             if (isset($codeResult['ok']) && $codeResult['ok']) {
                                 $t[2] = $codeResult['code'];
                                 $t[4] = time() + 1800;
                                 //30 minutes until code expires
                                 wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
                                 //save the code the user needs to enter and return an error.
                                 if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
                                     $retries = get_option('limit_login_retries', array());
                                     $ip = limit_login_get_address();
                                     if (!is_array($retries)) {
                                         $retries = array();
                                     }
                                     if (isset($retries[$ip]) && is_int($retries[$ip])) {
                                         $retries[$ip]--;
                                     } else {
                                         $retries[$ip] = 0;
                                     }
                                     update_option('limit_login_retries', $retries);
                                 }
                                 self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space and the code to the end of your password.'));
                                 return self::$authError;
                             } else {
                                 //oops, our API returned an error.
                                 break;
                                 //Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems.
                             }
                         }
                         //User is not present in two factor list or is not activated. Sign in without twofactor.
                     }
                     //Two facto users is empty. Sign in without two factor.
                 }
             }
         }
     }
     if (self::getLog()->isWhitelisted($IP)) {
         return $authUser;
     }
     if (wfConfig::get('other_WFNet') && is_wp_error($authUser) && ($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'incorrect_password')) {
         if ($maxBlockTime = self::wfsnIsBlocked($IP, 'brute')) {
             self::getLog()->blockIP($IP, "Blocked by Wordfence Security Network", true, false, $maxBlockTime);
             $secsToGo = wfConfig::get('blockedTime');
             self::getLog()->do503($secsToGo, "Blocked by Wordfence Security Network");
         }
     }
     if ($secEnabled) {
         if (is_wp_error($authUser) && $authUser->get_error_code() == 'invalid_username') {
             if ($blacklist = wfConfig::get('loginSec_userBlacklist')) {
                 $users = explode("\n", wfUtils::cleanupOneEntryPerLine($blacklist));
                 foreach ($users as $user) {
                     if (strtolower($username) == strtolower($user)) {
                         self::getLog()->blockIP($IP, "Blocked by login security setting.");
                         $secsToGo = wfConfig::get('blockedTime');
                         self::getLog()->do503($secsToGo, "Blocked by login security setting.");
                         break;
                     }
                 }
             }
             if (wfConfig::get('loginSec_lockInvalidUsers')) {
                 if (strlen($username) > 0 && preg_match('/[^\\r\\s\\n\\t]+/', $username)) {
                     self::lockOutIP($IP, "Used an invalid username '" . $username . "' to try to sign in.");
                 }
                 require 'wfLockedOut.php';
             }
         }
         $tKey = 'wflginfl_' . bin2hex(wfUtils::inet_pton($IP));
         if (is_wp_error($authUser) && ($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'incorrect_password')) {
             $tries = get_transient($tKey);
             if ($tries) {
                 $tries++;
             } else {
                 $tries = 1;
             }
             if ($tries >= wfConfig::get('loginSec_maxFailures')) {
                 self::lockOutIP($IP, "Exceeded the maximum number of login failures which is: " . wfConfig::get('loginSec_maxFailures') . ". The last username they tried to sign in with was: '" . $username . "'");
                 require 'wfLockedOut.php';
             }
             set_transient($tKey, $tries, wfConfig::get('loginSec_countFailMins') * 60);
         } else {
             if (get_class($authUser) == 'WP_User') {
                 delete_transient($tKey);
                 //reset counter on success
             }
         }
     }
     if (is_wp_error($authUser)) {
         if ($authUser->get_error_code() == 'invalid_username') {
             self::getLog()->logLogin('loginFailInvalidUsername', 1, $username);
         } else {
             self::getLog()->logLogin('loginFailValidUsername', 1, $username);
         }
     }
     if (is_wp_error($authUser) && ($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'incorrect_password') && wfConfig::get('loginSec_maskLoginErrors')) {
         return new WP_Error('incorrect_password', sprintf(__('<strong>ERROR</strong>: The username or password you entered is incorrect. <a href="%2$s" title="Password Lost and Found">Lost your password</a>?'), $username, wp_lostpassword_url()));
     }
     return $authUser;
 }
function limit_login_option_page()
{
    limit_login_cleanup();
    if (!current_user_can('manage_options')) {
        wp_die('Sorry, but you do not have permissions to change settings.');
    }
    /* Make sure post was from this page */
    if (count($_POST) > 0) {
        check_admin_referer('limit-login-attempts-options');
    }
    /* Should we clear log? */
    if (isset($_POST['clear_log'])) {
        delete_option('limit_login_logged');
        echo '<div id="message" class="updated fade"><p>' . __('Cleared IP log', 'limit-login-attempts') . '</p></div>';
    }
    /* Should we reset counter? */
    if (isset($_POST['reset_total'])) {
        update_option('limit_login_lockouts_total', 0);
        echo '<div id="message" class="updated fade"><p>' . __('Reset lockout count', 'limit-login-attempts') . '</p></div>';
    }
    /* Should we restore current lockouts? */
    if (isset($_POST['reset_current'])) {
        update_option('limit_login_lockouts', array());
        echo '<div id="message" class="updated fade"><p>' . __('Cleared current lockouts', 'limit-login-attempts') . '</p></div>';
    }
    /* Should we update options? */
    if (isset($_POST['update_options'])) {
        global $limit_login_options;
        $limit_login_options['client_type'] = $_POST['client_type'];
        $limit_login_options['allowed_retries'] = $_POST['allowed_retries'];
        $limit_login_options['lockout_duration'] = $_POST['lockout_duration'] * 60;
        $limit_login_options['valid_duration'] = $_POST['valid_duration'] * 3600;
        $limit_login_options['allowed_lockouts'] = $_POST['allowed_lockouts'];
        $limit_login_options['long_duration'] = $_POST['long_duration'] * 3600;
        $limit_login_options['notify_email_after'] = $_POST['email_after'];
        $limit_login_options['cookies'] = isset($_POST['cookies']) && $_POST['cookies'] == '1';
        $v = array();
        if (isset($_POST['lockout_notify_log'])) {
            $v[] = 'log';
        }
        if (isset($_POST['lockout_notify_email'])) {
            $v[] = 'email';
        }
        $limit_login_options['lockout_notify'] = implode(',', $v);
        limit_login_sanitize_variables();
        limit_login_update_options();
        echo '<div id="message" class="updated fade"><p>' . __('Options changed', 'limit-login-attempts') . '</p></div>';
    }
    $lockouts_total = get_option('limit_login_lockouts_total', 0);
    $lockouts = get_option('limit_login_lockouts');
    $lockouts_now = is_array($lockouts) ? count($lockouts) : 0;
    $cookies_yes = limit_login_option('cookies') ? ' checked ' : '';
    $cookies_no = limit_login_option('cookies') ? '' : ' checked ';
    $client_type = limit_login_option('client_type');
    $client_type_direct = $client_type == LIMIT_LOGIN_DIRECT_ADDR ? ' checked ' : '';
    $client_type_proxy = $client_type == LIMIT_LOGIN_PROXY_ADDR ? ' checked ' : '';
    $client_type_guess = limit_login_guess_proxy();
    if ($client_type_guess == LIMIT_LOGIN_DIRECT_ADDR) {
        $client_type_message = sprintf(__('It appears the site is reached directly (from your IP: %s)', 'limit-login-attempts'), limit_login_get_address(LIMIT_LOGIN_DIRECT_ADDR));
    } else {
        $client_type_message = sprintf(__('It appears the site is reached through a proxy server (proxy IP: %s, your IP: %s)', 'limit-login-attempts'), limit_login_get_address(LIMIT_LOGIN_DIRECT_ADDR), limit_login_get_address(LIMIT_LOGIN_PROXY_ADDR));
    }
    $client_type_message .= '<br />';
    $client_type_warning = '';
    if ($client_type != $client_type_guess) {
        $faq = 'http://wordpress.org/extend/plugins/limit-login-attempts/faq/';
        $client_type_warning = '<br /><br />' . sprintf(__('<strong>Current setting appears to be invalid</strong>. Please make sure it is correct. Further information can be found <a href="%s" title="FAQ">here</a>', 'limit-login-attempts'), $faq);
    }
    $v = explode(',', limit_login_option('lockout_notify'));
    $log_checked = in_array('log', $v) ? ' checked ' : '';
    $email_checked = in_array('email', $v) ? ' checked ' : '';
    ?>
	<div class="wrap">
	  <h2><?php 
    echo __('Limit Login Attempts Settings', 'limit-login-attempts');
    ?>
</h2>
	  <h3><?php 
    echo __('Statistics', 'limit-login-attempts');
    ?>
</h3>
	  <form action="options-general.php?page=limit-login-attempts" method="post">
		<?php 
    wp_nonce_field('limit-login-attempts-options');
    ?>
	    <table class="form-table">
		  <tr>
			<th scope="row" valign="top"><?php 
    echo __('Total lockouts', 'limit-login-attempts');
    ?>
</th>
			<td>
			  <?php 
    if ($lockouts_total > 0) {
        ?>
			  <input name="reset_total" value="<?php 
        echo __('Reset Counter', 'limit-login-attempts');
        ?>
" type="submit" />
			  <?php 
        echo sprintf(_n('%d lockout since last reset', '%d lockouts since last reset', $lockouts_total, 'limit-login-attempts'), $lockouts_total);
        ?>
			  <?php 
    } else {
        echo __('No lockouts yet', 'limit-login-attempts');
    }
    ?>
			</td>
		  </tr>
		  <?php 
    if ($lockouts_now > 0) {
        ?>
		  <tr>
			<th scope="row" valign="top"><?php 
        echo __('Active lockouts', 'limit-login-attempts');
        ?>
</th>
			<td>
			  <input name="reset_current" value="<?php 
        echo __('Restore Lockouts', 'limit-login-attempts');
        ?>
" type="submit" />
			  <?php 
        echo sprintf(__('%d IP is currently blocked from trying to log in', 'limit-login-attempts'), $lockouts_now);
        ?>
 
			</td>
		  </tr>
		  <?php 
    }
    ?>
		</table>
	  </form>
	  <h3><?php 
    echo __('Options', 'limit-login-attempts');
    ?>
</h3>
	  <form action="options-general.php?page=limit-login-attempts" method="post">
		<?php 
    wp_nonce_field('limit-login-attempts-options');
    ?>
	    <table class="form-table">
		  <tr>
			<th scope="row" valign="top"><?php 
    echo __('Lockout', 'limit-login-attempts');
    ?>
</th>
			<td>
			  <input type="text" size="3" maxlength="4" value="<?php 
    echo limit_login_option('allowed_retries');
    ?>
" name="allowed_retries" /> <?php 
    echo __('allowed retries', 'limit-login-attempts');
    ?>
 <br />
			  <input type="text" size="3" maxlength="4" value="<?php 
    echo limit_login_option('lockout_duration') / 60;
    ?>
" name="lockout_duration" /> <?php 
    echo __('minutes lockout', 'limit-login-attempts');
    ?>
 <br />
			  <input type="text" size="3" maxlength="4" value="<?php 
    echo limit_login_option('allowed_lockouts');
    ?>
" name="allowed_lockouts" /> <?php 
    echo __('lockouts increase lockout time to', 'limit-login-attempts');
    ?>
 <input type="text" size="3" maxlength="4" value="<?php 
    echo limit_login_option('long_duration') / 3600;
    ?>
" name="long_duration" /> <?php 
    echo __('hours', 'limit-login-attempts');
    ?>
 <br />
			  <input type="text" size="3" maxlength="4" value="<?php 
    echo limit_login_option('valid_duration') / 3600;
    ?>
" name="valid_duration" /> <?php 
    echo __('hours until retries are reset', 'limit-login-attempts');
    ?>
			</td>
		  </tr>
		  <tr>
			<th scope="row" valign="top"><?php 
    echo __('Site connection', 'limit-login-attempts');
    ?>
</th>
			<td>
			  <?php 
    echo $client_type_message;
    ?>
			  <label>
				<input type="radio" name="client_type" 
					   <?php 
    echo $client_type_direct;
    ?>
 value="<?php 
    echo LIMIT_LOGIN_DIRECT_ADDR;
    ?>
" /> 
					   <?php 
    echo __('Direct connection', 'limit-login-attempts');
    ?>
 
			  </label>
			  <label>
				<input type="radio" name="client_type" 
					   <?php 
    echo $client_type_proxy;
    ?>
 value="<?php 
    echo LIMIT_LOGIN_PROXY_ADDR;
    ?>
" /> 
				  <?php 
    echo __('From behind a reversy proxy', 'limit-login-attempts');
    ?>
			  </label>
			  <?php 
    echo $client_type_warning;
    ?>
			</td>
		  </tr>
		  <tr>
			<th scope="row" valign="top"><?php 
    echo __('Handle cookie login', 'limit-login-attempts');
    ?>
</th>
			<td>
			  <label><input type="radio" name="cookies" <?php 
    echo $cookies_yes;
    ?>
 value="1" /> <?php 
    echo __('Yes', 'limit-login-attempts');
    ?>
</label> <label><input type="radio" name="cookies" <?php 
    echo $cookies_no;
    ?>
 value="0" /> <?php 
    echo __('No', 'limit-login-attempts');
    ?>
</label>
			</td>
		  </tr>
		  <tr>
			<th scope="row" valign="top"><?php 
    echo __('Notify on lockout', 'limit-login-attempts');
    ?>
</th>
			<td>
			  <input type="checkbox" name="lockout_notify_log" <?php 
    echo $log_checked;
    ?>
 value="log" /> <?php 
    echo __('Log IP', 'limit-login-attempts');
    ?>
<br />
			  <input type="checkbox" name="lockout_notify_email" <?php 
    echo $email_checked;
    ?>
 value="email" /> <?php 
    echo __('Email to admin after', 'limit-login-attempts');
    ?>
 <input type="text" size="3" maxlength="4" value="<?php 
    echo limit_login_option('notify_email_after');
    ?>
" name="email_after" /> <?php 
    echo __('lockouts', 'limit-login-attempts');
    ?>
			</td>
		  </tr>
		</table>
		<p class="submit">
		  <input name="update_options" value="<?php 
    echo __('Change Options', 'limit-login-attempts');
    ?>
" type="submit" />
		</p>
	  </form>
	  <?php 
    $log = get_option('limit_login_logged');
    if (is_array($log) && count($log) > 0) {
        ?>
	  <h3><?php 
        echo __('Lockout log', 'limit-login-attempts');
        ?>
</h3>
	  <form action="options-general.php?page=limit-login-attempts" method="post">
		<?php 
        wp_nonce_field('limit-login-attempts-options');
        ?>
		<input type="hidden" value="true" name="clear_log" />
		<p class="submit">
		  <input name="submit" value="<?php 
        echo __('Clear Log', 'limit-login-attempts');
        ?>
" type="submit" />
		</p>
	  </form>
	  <style type="text/css" media="screen">
		.limit-login-log th {
			font-weight: bold;
		}
		.limit-login-log td, .limit-login-log th {
			padding: 1px 5px 1px 5px;
		}
		td.limit-login-ip {
			font-family:  "Courier New", Courier, monospace;
			vertical-align: top;
		}
		td.limit-login-max {
			width: 100%;
		}
	  </style>
	  <div class="limit-login-log">
		<table class="form-table">
		  <?php 
        limit_login_show_log($log);
        ?>
		</table>
	  </div>
	  <?php 
    }
    /* if showing $log */
    ?>

	</div>	
	<?php 
}
 public static function authenticateFilter($authUser, $username, $passwd)
 {
     wfConfig::inc('totalLoginHits');
     //The total hits to wp-login.php including logins, logouts and just hits.
     $IP = wfUtils::getIP();
     $secEnabled = wfConfig::get('loginSecurityEnabled');
     $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
     $userDat = isset($_POST['wordfence_userDat']) ? $_POST['wordfence_userDat'] : false;
     $checkTwoFactor = $secEnabled && !self::getLog()->isWhitelisted($IP) && wfConfig::get('isPaid') && isset($twoFactorUsers) && is_array($twoFactorUsers) && sizeof($twoFactorUsers) > 0 && is_object($userDat) && get_class($userDat) == 'WP_User';
     if ($checkTwoFactor) {
         $twoFactorRecord = false;
         $hasActivatedTwoFactorUser = false;
         foreach ($twoFactorUsers as &$t) {
             if ($t[3] == 'activated') {
                 $userID = $t[0];
                 $testUser = get_user_by('ID', $userID);
                 if (is_object($testUser) && wfUtils::isAdmin($testUser)) {
                     $hasActivatedTwoFactorUser = true;
                 }
                 if ($userID == $userDat->ID) {
                     $twoFactorRecord =& $t;
                 }
             }
         }
         if (isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor'] && $twoFactorRecord) {
             //User authenticated with name and password, 2FA code ready to check
             $userID = $userDat->ID;
             if (get_class($authUser) == 'WP_User' && $authUser->ID == $userID) {
                 //Do nothing. This is the code path the old method of including the code in the password field will take -- since we already have a valid $authUser, skip the nonce verification portion
             } else {
                 if (isset($_POST['wordfence_twoFactorNonce'])) {
                     $twoFactorNonce = preg_replace('/[^a-f0-9]/i', '', $_POST['wordfence_twoFactorNonce']);
                     if (!self::verifyTwoFactorIntermediateValues($userID, $twoFactorNonce)) {
                         self::$authError = new WP_Error('twofactor_required', __('<strong>VERIFICATION FAILED</strong>: Two factor authentication verification failed. Please try again.'));
                         return self::processBruteForceAttempt(self::$authError, $username, $passwd);
                     }
                 } else {
                     //Code path for old method, invalid password the second time
                     self::$authError = $authUser;
                     if (is_wp_error(self::$authError) && (self::$authError->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'invalid_email' || self::$authError->get_error_code() == 'incorrect_password' || $authUser->get_error_code() == 'authentication_failed') && wfConfig::get('loginSec_maskLoginErrors')) {
                         self::$authError = new WP_Error('incorrect_password', sprintf(__('<strong>ERROR</strong>: The username or password you entered is incorrect. <a href="%2$s" title="Password Lost and Found">Lost your password</a>?'), $username, wp_lostpassword_url()));
                     }
                     return self::processBruteForceAttempt(self::$authError, $username, $passwd);
                 }
             }
             if (isset($twoFactorRecord[5])) {
                 //New method TOTP
                 $mode = $twoFactorRecord[5];
                 $code = preg_replace('/[^a-f0-9]/i', '', $_POST['wordfence_authFactor']);
                 $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                 try {
                     $codeResult = $api->call('twoFactorTOTP_verify', array(), array('totpid' => $twoFactorRecord[6], 'code' => $code, 'mode' => $mode));
                     if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                         //No longer a paid key, let them sign in without two factor
                     } else {
                         if (isset($codeResult['ok']) && $codeResult['ok']) {
                             //Everything's good, let the sign in continue
                         } else {
                             if (get_class($authUser) == 'WP_User' && $authUser->ID == $userID) {
                                 //Using the old method of appending the code to the password
                                 if ($mode == 'authenticator') {
                                     self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: Please sign in again and add a space, the letters <code>wf</code>, and the code from your authenticator app to the end of your password (e.g., <code>wf123456</code>).'));
                                 } else {
                                     self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: Please sign in again and add a space, the letters <code>wf</code>, and the code sent to your phone to the end of your password (e.g., <code>wf123456</code>).'));
                                 }
                             } else {
                                 $loginNonce = wfWAFUtils::random_bytes(20);
                                 if ($loginNonce === false) {
                                     //Should never happen but is technically possible
                                     self::$authError = new WP_Error('twofactor_required', __('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.'));
                                     return self::$authError;
                                 }
                                 $loginNonce = bin2hex($loginNonce);
                                 update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
                                 update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
                                 if ($mode == 'authenticator') {
                                     self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: You need to enter the code generated by your authenticator app. The code should be a six digit number (e.g., 123456).') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                 } else {
                                     self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: You need to enter the code generated sent to your phone. The code should be a six digit number (e.g., 123456).') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                 }
                             }
                             return self::processBruteForceAttempt(self::$authError, $username, $passwd);
                         }
                     }
                 } catch (Exception $e) {
                     if (self::isDebugOn()) {
                         error_log('TOTP validation error: ' . $e->getMessage());
                     }
                 }
                 // Couldn't connect to noc1, let them sign in since the password was correct.
             } else {
                 //Old method phone authentication
                 $authFactor = $_POST['wordfence_authFactor'];
                 if (strlen($authFactor) == 4) {
                     $authFactor = 'wf' . $authFactor;
                 }
                 if ($authFactor == $twoFactorRecord[2] && $twoFactorRecord[4] > time()) {
                     // Set this 2FA code to expire in 30 seconds (for other plugins hooking into the auth process)
                     $twoFactorRecord[4] = time() + 30;
                     wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
                 } else {
                     if ($authFactor == $twoFactorRecord[2]) {
                         $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                         try {
                             $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $twoFactorRecord[1]));
                             if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                                 //No longer a paid key, let them sign in without two factor
                             } else {
                                 if (isset($codeResult['ok']) && $codeResult['ok']) {
                                     $twoFactorRecord[2] = $codeResult['code'];
                                     $twoFactorRecord[4] = time() + 1800;
                                     //30 minutes until code expires
                                     wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
                                     //save the code the user needs to enter and return an error.
                                     $loginNonce = wfWAFUtils::random_bytes(20);
                                     if ($loginNonce === false) {
                                         //Should never happen but is technically possible
                                         self::$authError = new WP_Error('twofactor_required', __('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.'));
                                         return self::$authError;
                                     }
                                     $loginNonce = bin2hex($loginNonce);
                                     update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
                                     update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
                                     self::$authError = new WP_Error('twofactor_required', __('<strong>CODE EXPIRED. CHECK YOUR PHONE:</strong> The code you entered has expired. Codes are only valid for 30 minutes for security reasons. We have sent you a new code. Please sign in using your username, password, and the new code we sent you.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                     return self::$authError;
                                 }
                             }
                             //else: No new code was received. Let them sign in with the expired code.
                         } catch (Exception $e) {
                             // Couldn't connect to noc1, let them sign in since the password was correct.
                         }
                     } else {
                         //Bad code, so cancel the login and return an error to user.
                         $loginNonce = wfWAFUtils::random_bytes(20);
                         if ($loginNonce === false) {
                             //Should never happen but is technically possible
                             self::$authError = new WP_Error('twofactor_required', __('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.'));
                             return self::$authError;
                         }
                         $loginNonce = bin2hex($loginNonce);
                         update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
                         update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
                         self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: You need to enter your password and the code we sent to your phone. The code should start with \'wf\' and should be four characters (e.g., wfAB12).') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                         return self::processBruteForceAttempt(self::$authError, $username, $passwd);
                     }
                 }
             }
             delete_user_meta($userDat->ID, '_wf_twoFactorNonce');
             delete_user_meta($userDat->ID, '_wf_twoFactorNonceTime');
             $authUser = $userDat;
             //Log in as the user we saved in the wp_authenticate action
         } else {
             if (get_class($authUser) == 'WP_User') {
                 //User authenticated with name and password, prompt for the 2FA code
                 //Verify at least one administrator has 2FA enabled
                 $requireAdminTwoFactor = $hasActivatedTwoFactorUser && wfConfig::get('loginSec_requireAdminTwoFactor');
                 if ($twoFactorRecord) {
                     if ($twoFactorRecord[0] == $userDat->ID && $twoFactorRecord[3] == 'activated') {
                         //Yup, enabled, so require the code
                         $loginNonce = wfWAFUtils::random_bytes(20);
                         if ($loginNonce === false) {
                             //Should never happen but is technically possible, allow login
                             $requireAdminTwoFactor = false;
                         } else {
                             $loginNonce = bin2hex($loginNonce);
                             update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
                             update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
                             if (isset($twoFactorRecord[5])) {
                                 //New method TOTP authentication
                                 if ($twoFactorRecord[5] == 'authenticator') {
                                     if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
                                         $retries = get_option('limit_login_retries', array());
                                         $ip = limit_login_get_address();
                                         if (!is_array($retries)) {
                                             $retries = array();
                                         }
                                         if (isset($retries[$ip]) && is_int($retries[$ip])) {
                                             $retries[$ip]--;
                                         } else {
                                             $retries[$ip] = 0;
                                         }
                                         update_option('limit_login_retries', $retries);
                                     }
                                     $allowSeparatePrompt = ini_get('output_buffering') > 0;
                                     if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
                                         self::$authError = new WP_Error('twofactor_required', __('<strong>CODE REQUIRED</strong>: Please check your authenticator app for the current code. Enter it below to sign in.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                         return self::$authError;
                                     } else {
                                         self::$authError = new WP_Error('twofactor_required', __('<strong>CODE REQUIRED</strong>: Please check your authenticator app for the current code. Please sign in again and add a space, the letters <code>wf</code>, and the code to the end of your password (e.g., <code>wf123456</code>).'));
                                         return self::$authError;
                                     }
                                 } else {
                                     //Phone TOTP
                                     $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                                     try {
                                         $codeResult = $api->call('twoFactorTOTP_sms', array(), array('totpid' => $twoFactorRecord[6]));
                                         if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                                             $requireAdminTwoFactor = false;
                                             //Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up.
                                         } else {
                                             if (isset($codeResult['ok']) && $codeResult['ok']) {
                                                 if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
                                                     $retries = get_option('limit_login_retries', array());
                                                     $ip = limit_login_get_address();
                                                     if (!is_array($retries)) {
                                                         $retries = array();
                                                     }
                                                     if (isset($retries[$ip]) && is_int($retries[$ip])) {
                                                         $retries[$ip]--;
                                                     } else {
                                                         $retries[$ip] = 0;
                                                     }
                                                     update_option('limit_login_retries', $retries);
                                                 }
                                                 $allowSeparatePrompt = ini_get('output_buffering') > 0;
                                                 if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
                                                     self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Enter it below to sign in.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                                     return self::$authError;
                                                 } else {
                                                     self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space, the letters <code>wf</code>, and the code to the end of your password (e.g., <code>wf123456</code>).'));
                                                     return self::$authError;
                                                 }
                                             } else {
                                                 //oops, our API returned an error.
                                                 $requireAdminTwoFactor = false;
                                                 //Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems.
                                             }
                                         }
                                     } catch (Exception $e) {
                                         if (self::isDebugOn()) {
                                             error_log('TOTP SMS error: ' . $e->getMessage());
                                         }
                                         $requireAdminTwoFactor = false;
                                         // Couldn't connect to noc1, let them sign in since the password was correct.
                                     }
                                 }
                             } else {
                                 //Old method phone authentication
                                 $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                                 try {
                                     $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $twoFactorRecord[1]));
                                     if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                                         $requireAdminTwoFactor = false;
                                         //Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up.
                                     } else {
                                         if (isset($codeResult['ok']) && $codeResult['ok']) {
                                             $twoFactorRecord[2] = $codeResult['code'];
                                             $twoFactorRecord[4] = time() + 1800;
                                             //30 minutes until code expires
                                             wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
                                             //save the code the user needs to enter and return an error.
                                             if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
                                                 $retries = get_option('limit_login_retries', array());
                                                 $ip = limit_login_get_address();
                                                 if (!is_array($retries)) {
                                                     $retries = array();
                                                 }
                                                 if (isset($retries[$ip]) && is_int($retries[$ip])) {
                                                     $retries[$ip]--;
                                                 } else {
                                                     $retries[$ip] = 0;
                                                 }
                                                 update_option('limit_login_retries', $retries);
                                             }
                                             $allowSeparatePrompt = ini_get('output_buffering') > 0;
                                             if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
                                                 self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Enter it below to sign in.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                                 return self::$authError;
                                             } else {
                                                 self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space and the code to the end of your password (e.g., <code>wfABCD</code>).'));
                                                 return self::$authError;
                                             }
                                         } else {
                                             //oops, our API returned an error.
                                             $requireAdminTwoFactor = false;
                                             //Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems.
                                         }
                                     }
                                 } catch (Exception $e) {
                                     $requireAdminTwoFactor = false;
                                     // Couldn't connect to noc1, let them sign in since the password was correct.
                                 }
                             }
                             //end: Old method phone authentication
                         }
                     }
                 }
                 if ($requireAdminTwoFactor && wfUtils::isAdmin($authUser)) {
                     $username = $authUser->user_login;
                     self::getLog()->logLogin('loginFailValidUsername', 1, $username);
                     wordfence::alert("Admin Login Blocked", "A user with username \"{$username}\" who has administrator access tried to sign in to your WordPress site. Access was denied because all administrator accounts are required to have Cellphone Sign-in enabled but this account does not.", wfUtils::getIP());
                     self::$authError = new WP_Error('twofactor_disabled_required', __('<strong>Cellphone Sign-in Required</strong>: Cellphone Sign-in is required for all administrator accounts. Please contact the site administrator to enable it for your account.'));
                     return self::$authError;
                 }
                 //User is not configured for two factor. Sign in without two factor.
             }
         }
     }
     //End: if ($checkTwoFactor)
     return self::processBruteForceAttempt($authUser, $username, $passwd);
 }
示例#5
0
function limit_login_retries_remaining_msg()
{
    $ip = limit_login_get_address();
    $retries = get_option('limit_login_retries');
    $valid = get_option('limit_login_retries_valid');
    /* Should we show retries remaining? */
    if (!is_array($retries) || !is_array($valid)) {
        /* no retries at all */
        return '';
    }
    if (!isset($retries[$ip]) || !isset($valid[$ip]) || time() > $valid[$ip]) {
        /* no: no valid retries */
        return '';
    }
    if ($retries[$ip] % limit_login_option('allowed_retries') == 0) {
        /* no: already been locked out for these retries */
        return '';
    }
    $remaining = max(limit_login_option('allowed_retries') - $retries[$ip] % limit_login_option('allowed_retries'), 0);
    return sprintf(_n("<strong>%d</strong> attempt remaining.", "<strong>%d</strong> attempts remaining.", $remaining, 'limit-login-attempts'), $remaining);
}