Login LockDown - Version 1.6

Version Description

Download this release

Release Info

Developer mvandemar
Plugin Icon 128x128 Login LockDown
Version 1.6
Comparing to
See all releases

Code changes from version 1.5 to 1.6

Files changed (3) hide show
  1. loginlockdown.php +179 -121
  2. readme.txt +45 -4
  3. version.txt +1 -1
loginlockdown.php CHANGED
@@ -2,24 +2,31 @@
2
  /*
3
  Plugin Name: Login LockDown
4
  Plugin URI: http://www.bad-neighborhood.com/
5
- Version: v1.5
6
  Author: Michael VanDeMar
7
  Description: Adds some extra security to WordPress by restricting the rate at which failed logins can be re-attempted from a given IP range. Distributed through <a href="http://www.bad-neighborhood.com/" target="_blank">Bad Neighborhood</a>.
8
  */
9
 
10
  /*
11
- * Change Log
 
 
 
 
 
 
 
12
  *
13
  * ver. 1.5 17-Sep-2009
14
  * - implemented wp_nonce security in the options and lockdown release forms in the admin screen
15
- * - fixed a security hole with an improperly escaped SQL query
16
- * - encoded certain outputs in the admin panel using esc_attr() to prevent XSS attacks
17
- * - fixed an issue with the 'Lockout Invalid Usernames' option not functioning as intended
18
  *
19
  * ver. 1.4 29-Aug-2009
20
  * - removed erroneous error affecting WP 2.8+
21
- * - fixed activation error caused by customizing the location of the wp-content folder
22
- * - added in the option to mask which specific login error (invalid username or invalid password) was generated
23
  * - added in the option to lock out failed login attempts even if the username doesn't exist
24
  *
25
  * ver. 1.3 23-Feb-2009
@@ -52,7 +59,7 @@ Description: Adds some extra security to WordPress by restricting the rate at wh
52
  | |
53
  | Login LockDown - added security measures to WordPress intended to |
54
  | inhibit or reduce brute force password discovery. |
55
- | Copyright (C) 2007 - 2009, Michael VanDeMar, |
56
  | http://www.bad-neighborhood.com |
57
  | All rights reserved. |
58
  | |
@@ -80,6 +87,7 @@ $loginlockdownOptions = get_loginlockdownOptions();
80
 
81
  function loginLockdown_install() {
82
  global $wpdb;
 
83
  $table_name = $wpdb->prefix . "login_fails";
84
 
85
  if( $wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name ) {
@@ -91,9 +99,8 @@ function loginLockdown_install() {
91
  PRIMARY KEY (`login_attempt_ID`)
92
  );";
93
 
94
- require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
95
  dbDelta($sql);
96
- add_option("loginlockdown_db1_version", $loginlockdown_db_version);
97
  }
98
 
99
  $table_name = $wpdb->prefix . "lockdowns";
@@ -108,10 +115,13 @@ function loginLockdown_install() {
108
  PRIMARY KEY (`lockdown_ID`)
109
  );";
110
 
111
- require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
112
  dbDelta($sql);
113
- add_option("loginlockdown_db2_version", $loginlockdown_db_version);
114
  }
 
 
 
 
115
  }
116
 
117
  function countFails($username = "") {
@@ -121,24 +131,33 @@ function countFails($username = "") {
121
  $ip = $_SERVER['REMOTE_ADDR'];
122
  $class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
123
 
124
- $numFails = $wpdb->get_var("SELECT COUNT(login_attempt_ID) FROM $table_name " .
125
  "WHERE login_attempt_date + INTERVAL " .
126
  $loginlockdownOptions['retries_within'] . " MINUTE > now() AND " .
127
- "login_attempt_IP LIKE '" . $wpdb->escape($class_c) . "%'");
 
 
 
128
  return $numFails;
129
  }
130
 
131
  function incrementFails($username = "") {
132
- global $wpdb;
133
  global $loginlockdownOptions;
134
  $table_name = $wpdb->prefix . "login_fails";
135
  $ip = $_SERVER['REMOTE_ADDR'];
136
 
137
  $username = sanitize_user($username);
138
- $user = get_userdatabylogin($username);
139
  if ( $user || "yes" == $loginlockdownOptions['lockout_invalid_usernames'] ) {
 
 
 
 
 
140
  $insert = "INSERT INTO " . $table_name . " (user_id, login_attempt_date, login_attempt_IP) " .
141
- "VALUES ('" . $user->ID . "', now(), '" . $wpdb->escape($ip) . "')";
 
142
  $results = $wpdb->query($insert);
143
  }
144
  }
@@ -150,11 +169,17 @@ function lockDown($username = "") {
150
  $ip = $_SERVER['REMOTE_ADDR'];
151
 
152
  $username = sanitize_user($username);
153
- $user = get_userdatabylogin($username);
154
  if ( $user || "yes" == $loginlockdownOptions['lockout_invalid_usernames'] ) {
 
 
 
 
 
155
  $insert = "INSERT INTO " . $table_name . " (user_id, lockdown_date, release_date, lockdown_IP) " .
156
- "VALUES ('" . $user->ID . "', now(), date_add(now(), INTERVAL " .
157
- $loginlockdownOptions['lockout_length'] . " MINUTE), '" . $wpdb->escape($ip) . "')";
 
158
  $results = $wpdb->query($insert);
159
  }
160
  }
@@ -165,9 +190,12 @@ function isLockedDown() {
165
  $ip = $_SERVER['REMOTE_ADDR'];
166
  $class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
167
 
168
- $stillLocked = $wpdb->get_var("SELECT user_id FROM $table_name " .
169
  "WHERE release_date > now() AND " .
170
- "lockdown_IP LIKE '" . $wpdb->escape($class_c) . "%'");
 
 
 
171
 
172
  return $stillLocked;
173
  }
@@ -186,9 +214,11 @@ function get_loginlockdownOptions() {
186
  $loginlockdownAdminOptions = array(
187
  'max_login_retries' => 3,
188
  'retries_within' => 5,
189
- 'lockout_length' => 60,
190
- 'lockout_invalid_usernames' => 'no',
191
- 'mask_login_errors' => 'no');
 
 
192
  $loginlockdownOptions = get_option("loginlockdownAdminOptions");
193
  if ( !empty($loginlockdownOptions) ) {
194
  foreach ( $loginlockdownOptions as $key => $option ) {
@@ -202,13 +232,13 @@ function get_loginlockdownOptions() {
202
  function print_loginlockdownAdminPage() {
203
  global $wpdb;
204
  $table_name = $wpdb->prefix . "lockdowns";
205
- $loginlockdownAdminOptions = get_loginlockdownOptions();
206
-
207
  if (isset($_POST['update_loginlockdownSettings'])) {
208
-
209
- //wp_nonce check
210
- check_admin_referer('login-lockdown_update-options');
211
-
212
  if (isset($_POST['ll_max_login_retries'])) {
213
  $loginlockdownAdminOptions['max_login_retries'] = $_POST['ll_max_login_retries'];
214
  }
@@ -224,21 +254,26 @@ function print_loginlockdownAdminPage() {
224
  if (isset($_POST['ll_mask_login_errors'])) {
225
  $loginlockdownAdminOptions['mask_login_errors'] = $_POST['ll_mask_login_errors'];
226
  }
 
 
 
227
  update_option("loginlockdownAdminOptions", $loginlockdownAdminOptions);
228
  ?>
229
  <div class="updated"><p><strong><?php _e("Settings Updated.", "loginlockdown");?></strong></p></div>
230
  <?php
231
  }
232
- if (isset($_POST['release_lockdowns'])) {
233
-
234
- //wp_nonce check
235
- check_admin_referer('login-lockdown_release-lockdowns');
236
 
237
  if (isset($_POST['releaseme'])) {
238
  $released = $_POST['releaseme'];
239
  foreach ( $released as $release_id ) {
240
- $results = $wpdb->query("UPDATE $table_name SET release_date = now() " .
241
- "WHERE lockdown_ID = " . $wpdb->escape($release_id) . "");
 
 
242
  }
243
  }
244
  update_option("loginlockdownAdminOptions", $loginlockdownAdminOptions);
@@ -248,37 +283,50 @@ function print_loginlockdownAdminPage() {
248
  }
249
  $dalist = listLockedDown();
250
  ?>
251
- <div class=wrap>
252
- <form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
253
- <?php
254
- if ( function_exists('wp_nonce_field') )
255
- wp_nonce_field('login-lockdown_update-options');
256
  ?>
257
  <h2><?php _e('Login LockDown Options', 'loginlockdown') ?></h2>
258
  <h3><?php _e('Max Login Retries', 'loginlockdown') ?></h3>
259
- <input type="text" name="ll_max_login_retries" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['max_login_retries']); ?>">
 
260
  <h3><?php _e('Retry Time Period Restriction (minutes)', 'loginlockdown') ?></h3>
261
- <input type="text" name="ll_retries_within" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['retries_within']); ?>">
 
262
  <h3><?php _e('Lockout Length (minutes)', 'loginlockdown') ?></h3>
263
- <input type="text" name="ll_lockout_length" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['lockout_length']); ?>">
 
264
  <h3><?php _e('Lockout Invalid Usernames?', 'loginlockdown') ?></h3>
265
- <input type="radio" name="ll_lockout_invalid_usernames" value="yes" <?php if( $loginlockdownAdminOptions['lockout_invalid_usernames'] == "yes" ) echo "checked"; ?>>&nbsp;Yes&nbsp;&nbsp;&nbsp;<input type="radio" name="ll_lockout_invalid_usernames" value="no" <?php if( $loginlockdownAdminOptions['lockout_invalid_usernames'] == "no" ) echo "checked"; ?>>&nbsp;No
 
266
  <h3><?php _e('Mask Login Errors?', 'loginlockdown') ?></h3>
267
- <input type="radio" name="ll_mask_login_errors" value="yes" <?php if( $loginlockdownAdminOptions['mask_login_errors'] == "yes" ) echo "checked"; ?>>&nbsp;Yes&nbsp;&nbsp;&nbsp;<input type="radio" name="ll_mask_login_errors" value="no" <?php if( $loginlockdownAdminOptions['mask_login_errors'] == "no" ) echo "checked"; ?>>&nbsp;No
 
 
 
 
 
 
 
 
 
268
  <div class="submit">
269
- <input type="submit" name="update_loginlockdownSettings" value="<?php _e('Update Settings', 'loginlockdown') ?>" /></div>
270
  </form>
271
  <br />
272
- <form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
273
- <?php
274
- if ( function_exists('wp_nonce_field') )
275
- wp_nonce_field('login-lockdown_release-lockdowns');
276
  ?>
277
  <h3><?php _e('Currently Locked Out', 'loginlockdown') ?></h3>
278
  <?php
279
  $num_lockedout = count($dalist);
280
  if( 0 == $num_lockedout ) {
281
- echo "<p>No current IP blocks locked out.</p>";
282
  } else {
283
  foreach ( $dalist as $key => $option ) {
284
  ?>
@@ -288,7 +336,7 @@ if ( function_exists('wp_nonce_field') )
288
  }
289
  ?>
290
  <div class="submit">
291
- <input type="submit" name="release_lockdowns" value="<?php _e('Release Selected', 'loginlockdown') ?>" /></div>
292
  </form>
293
  </div>
294
  <?php
@@ -296,105 +344,115 @@ if ( function_exists('wp_nonce_field') )
296
 
297
  function loginlockdown_ap() {
298
  if ( function_exists('add_options_page') ) {
299
- add_options_page('Login LockDown', 'Login LockDown', 9, basename(__FILE__), 'print_loginlockdownAdminPage');
300
  }
301
  }
302
 
303
  function ll_credit_link(){
304
- echo "<p>Login form protected by <a href='http://www.bad-neighborhood.com/login-lockdown.html'>Login LockDown</a>.<br /><br /><br /></p>";
 
 
 
 
 
 
 
 
 
 
305
  }
306
 
307
  //Actions and Filters
308
  if ( isset($loginlockdown_db_version) ) {
309
  //Actions
310
  add_action('admin_menu', 'loginlockdown_ap');
311
- if(!defined('WP_PLUGIN_DIR')){
312
  define('WP_PLUGIN_DIR', ABSPATH . 'wp-content/plugins');
313
  }
314
- $activatestr = str_replace(WP_PLUGIN_DIR . "/", "activate_", __FILE__);
315
  add_action($activatestr, 'loginLockdown_install');
316
- add_action('login_form', 'll_credit_link');
317
-
318
- remove_filter('authenticate', 'wp_authenticate_username_password', 20, 3);
319
  add_filter('authenticate', 'll_wp_authenticate_username_password', 20, 3);
320
  //Filters
321
- //Functions
322
- function ll_wp_authenticate_username_password($user, $username, $password) {
323
- if ( is_a($user, 'WP_User') ) { return $user; }
324
-
325
- if ( empty($username) || empty($password) ) {
326
- $error = new WP_Error();
327
-
328
- if ( empty($username) )
329
- $error->add('empty_username', __('<strong>ERROR</strong>: The username field is empty.'));
330
-
331
- if ( empty($password) )
332
- $error->add('empty_password', __('<strong>ERROR</strong>: The password field is empty.'));
333
-
334
- return $error;
335
- }
336
-
337
- $userdata = get_userdatabylogin($username);
338
-
339
- if ( !$userdata ) {
340
- return new WP_Error('invalid_username', sprintf(__('<strong>ERROR</strong>: Invalid username. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
341
- }
342
-
343
- $userdata = apply_filters('wp_authenticate_user', $userdata, $password);
344
- if ( is_wp_error($userdata) ) {
345
- return $userdata;
346
- }
347
-
348
- if ( !wp_check_password($password, $userdata->user_pass, $userdata->ID) ) {
349
- return new WP_Error('incorrect_password', sprintf(__('<strong>ERROR</strong>: Incorrect password. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
350
- }
351
-
352
- $user = new WP_User($userdata->ID);
353
- return $user;
354
- }
355
-
356
 
357
  if ( !function_exists('wp_authenticate') ) :
358
  function wp_authenticate($username, $password) {
359
  global $wpdb, $error;
360
- global $loginlockdownOptions;
361
-
362
- $username = sanitize_user($username);
363
- $password = trim($password);
364
-
365
  if ( "" != isLockedDown() ) {
366
  return new WP_Error('incorrect_password', "<strong>ERROR</strong>: We're sorry, but this IP range has been blocked due to too many recent " .
367
  "failed login attempts.<br /><br />Please try again later.");
368
  }
369
-
370
- $user = apply_filters('authenticate', null, $username, $password);
371
-
372
- if ( $user == null ) {
373
- // TODO what should the error message be? (Or would these even happen?)
374
- // Only needed if all authentication handlers fail to return anything.
375
- $user = new WP_Error('authentication_failed', __('<strong>ERROR</strong>: Invalid username or incorrect password.'));
376
- }
377
-
378
- $ignore_codes = array('empty_username', 'empty_password');
379
-
380
- if (is_wp_error($user) && !in_array($user->get_error_code(), $ignore_codes) ) {
381
  incrementFails($username);
382
  if ( $loginlockdownOptions['max_login_retries'] <= countFails($username) ) {
383
  lockDown($username);
384
  return new WP_Error('incorrect_password', __("<strong>ERROR</strong>: We're sorry, but this IP range has been blocked due to too many recent " .
385
  "failed login attempts.<br /><br />Please try again later."));
386
- }
387
- if ( 'yes' == $loginlockdownOptions['mask_login_errors'] ) {
388
- return new WP_Error('authentication_failed', sprintf(__('<strong>ERROR</strong>: Invalid username or incorrect password. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
389
  } else {
390
- do_action('wp_login_failed', $username);
391
- }
392
- }
393
-
394
  return $user;
395
  }
396
  endif;
397
  }
398
 
399
 
400
- ?>
2
  /*
3
  Plugin Name: Login LockDown
4
  Plugin URI: http://www.bad-neighborhood.com/
5
+ Version: v1.6
6
  Author: Michael VanDeMar
7
  Description: Adds some extra security to WordPress by restricting the rate at which failed logins can be re-attempted from a given IP range. Distributed through <a href="http://www.bad-neighborhood.com/" target="_blank">Bad Neighborhood</a>.
8
  */
9
 
10
  /*
11
+ == Change Log ==
12
+ *
13
+ * ver. 1.6 7-Mar-2014
14
+ * - cleaned up deprecated functions
15
+ * - fixed bug with invalid property on a non-object when locking out invalid usernames
16
+ * - fixed utilization of $wpdb->prepare
17
+ * - added more descriptive help text to each of the options
18
+ * - added the ability to remove the "Login form protected by Login LockDown." message from within the dashboard
19
  *
20
  * ver. 1.5 17-Sep-2009
21
  * - implemented wp_nonce security in the options and lockdown release forms in the admin screen
22
+ * - fixed a security hole with an improperly escaped SQL query
23
+ * - encoded certain outputs in the admin panel using esc_attr() to prevent XSS attacks
24
+ * - fixed an issue with the 'Lockout Invalid Usernames' option not functioning as intended
25
  *
26
  * ver. 1.4 29-Aug-2009
27
  * - removed erroneous error affecting WP 2.8+
28
+ * - fixed activation error caused by customizing the location of the wp-content folder
29
+ * - added in the option to mask which specific login error (invalid username or invalid password) was generated
30
  * - added in the option to lock out failed login attempts even if the username doesn't exist
31
  *
32
  * ver. 1.3 23-Feb-2009
59
  | |
60
  | Login LockDown - added security measures to WordPress intended to |
61
  | inhibit or reduce brute force password discovery. |
62
+ | Copyright (C) 2007 - 2014, Michael VanDeMar, |
63
  | http://www.bad-neighborhood.com |
64
  | All rights reserved. |
65
  | |
87
 
88
  function loginLockdown_install() {
89
  global $wpdb;
90
+ global $loginlockdown_db_version;
91
  $table_name = $wpdb->prefix . "login_fails";
92
 
93
  if( $wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name ) {
99
  PRIMARY KEY (`login_attempt_ID`)
100
  );";
101
 
102
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
103
  dbDelta($sql);
 
104
  }
105
 
106
  $table_name = $wpdb->prefix . "lockdowns";
115
  PRIMARY KEY (`lockdown_ID`)
116
  );";
117
 
118
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
119
  dbDelta($sql);
 
120
  }
121
+ add_option("loginlockdown_db_version", "1.0", "", "no");
122
+ // added in 1.6, cleanup from previously improperly set db versions
123
+ delete_option( "loginlockdown_db1_version" );
124
+ delete_option( "loginlockdown_db2_version" );
125
  }
126
 
127
  function countFails($username = "") {
131
  $ip = $_SERVER['REMOTE_ADDR'];
132
  $class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
133
 
134
+ $numFailsquery = "SELECT COUNT(login_attempt_ID) FROM $table_name " .
135
  "WHERE login_attempt_date + INTERVAL " .
136
  $loginlockdownOptions['retries_within'] . " MINUTE > now() AND " .
137
+ "login_attempt_IP LIKE '%s'";
138
+ $numFailsquery = $wpdb->prepare( $numFailsquery, $class_c . "%");
139
+
140
+ $numFails = $wpdb->get_var($numFailsquery);
141
  return $numFails;
142
  }
143
 
144
  function incrementFails($username = "") {
145
+ global $wpdb;
146
  global $loginlockdownOptions;
147
  $table_name = $wpdb->prefix . "login_fails";
148
  $ip = $_SERVER['REMOTE_ADDR'];
149
 
150
  $username = sanitize_user($username);
151
+ $user = get_user_by('login',$username);
152
  if ( $user || "yes" == $loginlockdownOptions['lockout_invalid_usernames'] ) {
153
+ if ( $user === false ) {
154
+ $user_id = -1;
155
+ } else {
156
+ $user_id = $user->ID;
157
+ }
158
  $insert = "INSERT INTO " . $table_name . " (user_id, login_attempt_date, login_attempt_IP) " .
159
+ "VALUES ('" . $user_id . "', now(), '%s')";
160
+ $insert = $wpdb->prepare( $insert, $ip );
161
  $results = $wpdb->query($insert);
162
  }
163
  }
169
  $ip = $_SERVER['REMOTE_ADDR'];
170
 
171
  $username = sanitize_user($username);
172
+ $user = get_user_by('login',$username);
173
  if ( $user || "yes" == $loginlockdownOptions['lockout_invalid_usernames'] ) {
174
+ if ( $user === false ) {
175
+ $user_id = -1;
176
+ } else {
177
+ $user_id = $user->ID;
178
+ }
179
  $insert = "INSERT INTO " . $table_name . " (user_id, lockdown_date, release_date, lockdown_IP) " .
180
+ "VALUES ('" . $user_id . "', now(), date_add(now(), INTERVAL " .
181
+ $loginlockdownOptions['lockout_length'] . " MINUTE), '%s')";
182
+ $insert = $wpdb->prepare( $insert, $ip );
183
  $results = $wpdb->query($insert);
184
  }
185
  }
190
  $ip = $_SERVER['REMOTE_ADDR'];
191
  $class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
192
 
193
+ $stillLockedquery = "SELECT user_id FROM $table_name " .
194
  "WHERE release_date > now() AND " .
195
+ "lockdown_IP LIKE %s";
196
+ $stillLockedquery = $wpdb->prepare($stillLockedquery,$class_c . "%");
197
+
198
+ $stillLocked = $wpdb->get_var($stillLockedquery);
199
 
200
  return $stillLocked;
201
  }
214
  $loginlockdownAdminOptions = array(
215
  'max_login_retries' => 3,
216
  'retries_within' => 5,
217
+ 'lockout_length' => 60,
218
+ 'lockout_invalid_usernames' => 'no',
219
+ 'mask_login_errors' => 'no',
220
+ 'show_credit_link' => 'yes'
221
+ );
222
  $loginlockdownOptions = get_option("loginlockdownAdminOptions");
223
  if ( !empty($loginlockdownOptions) ) {
224
  foreach ( $loginlockdownOptions as $key => $option ) {
232
  function print_loginlockdownAdminPage() {
233
  global $wpdb;
234
  $table_name = $wpdb->prefix . "lockdowns";
235
+ $loginlockdownAdminOptions = get_loginlockdownOptions();
236
+
237
  if (isset($_POST['update_loginlockdownSettings'])) {
238
+
239
+ //wp_nonce check
240
+ check_admin_referer('login-lockdown_update-options');
241
+
242
  if (isset($_POST['ll_max_login_retries'])) {
243
  $loginlockdownAdminOptions['max_login_retries'] = $_POST['ll_max_login_retries'];
244
  }
254
  if (isset($_POST['ll_mask_login_errors'])) {
255
  $loginlockdownAdminOptions['mask_login_errors'] = $_POST['ll_mask_login_errors'];
256
  }
257
+ if (isset($_POST['ll_show_credit_link'])) {
258
+ $loginlockdownAdminOptions['show_credit_link'] = $_POST['ll_show_credit_link'];
259
+ }
260
  update_option("loginlockdownAdminOptions", $loginlockdownAdminOptions);
261
  ?>
262
  <div class="updated"><p><strong><?php _e("Settings Updated.", "loginlockdown");?></strong></p></div>
263
  <?php
264
  }
265
+ if (isset($_POST['release_lockdowns'])) {
266
+
267
+ //wp_nonce check
268
+ check_admin_referer('login-lockdown_release-lockdowns');
269
 
270
  if (isset($_POST['releaseme'])) {
271
  $released = $_POST['releaseme'];
272
  foreach ( $released as $release_id ) {
273
+ $releasequery = "UPDATE $table_name SET release_date = now() " .
274
+ "WHERE lockdown_ID = '%d'";
275
+ $releasequery = $wpdb->prepare($releasequery,$release_id);
276
+ $results = $wpdb->query($releasequery);
277
  }
278
  }
279
  update_option("loginlockdownAdminOptions", $loginlockdownAdminOptions);
283
  }
284
  $dalist = listLockedDown();
285
  ?>
286
+ <div class="wrap" style="width>
287
+ <form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
288
+ <?php
289
+ if ( function_exists('wp_nonce_field') )
290
+ wp_nonce_field('login-lockdown_update-options');
291
  ?>
292
  <h2><?php _e('Login LockDown Options', 'loginlockdown') ?></h2>
293
  <h3><?php _e('Max Login Retries', 'loginlockdown') ?></h3>
294
+ <p>Number of failed login attempts within the "Retry Time Period Restriction" (defined below) needed to trigger a LockDown.</p>
295
+ <p><input type="text" name="ll_max_login_retries" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['max_login_retries']); ?>"></p>
296
  <h3><?php _e('Retry Time Period Restriction (minutes)', 'loginlockdown') ?></h3>
297
+ <p>Amount of time that determines the rate at which failed login attempts are allowed before a LockDown occurs.</p>
298
+ <p><input type="text" name="ll_retries_within" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['retries_within']); ?>"></p>
299
  <h3><?php _e('Lockout Length (minutes)', 'loginlockdown') ?></h3>
300
+ <p>How long a particular IP block will be locked out for once a LockDown has been triggered.</p>
301
+ <p><input type="text" name="ll_lockout_length" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['lockout_length']); ?>"></p>
302
  <h3><?php _e('Lockout Invalid Usernames?', 'loginlockdown') ?></h3>
303
+ <p>By default Login LockDown will not trigger if an attempt is made to log in using a username that does not exist. You can override this behavior here.</p>
304
+ <p><input type="radio" name="ll_lockout_invalid_usernames" value="yes" <?php if( $loginlockdownAdminOptions['lockout_invalid_usernames'] == "yes" ) echo "checked"; ?>>&nbsp;Yes&nbsp;&nbsp;&nbsp;<input type="radio" name="ll_lockout_invalid_usernames" value="no" <?php if( $loginlockdownAdminOptions['lockout_invalid_usernames'] == "no" ) echo "checked"; ?>>&nbsp;No</p>
305
  <h3><?php _e('Mask Login Errors?', 'loginlockdown') ?></h3>
306
+ <p>WordPress will normally display distinct messages to the user depending on whether they try and log in with an invalid username, or with a
307
+ valid username but the incorrect password. Toggling this option will hide why the login failed.</p>
308
+ <p><input type="radio" name="ll_mask_login_errors" value="yes" <?php if( $loginlockdownAdminOptions['mask_login_errors'] == "yes" ) echo "checked"; ?>>&nbsp;Yes&nbsp;&nbsp;&nbsp;<input type="radio" name="ll_mask_login_errors" value="no" <?php if( $loginlockdownAdminOptions['mask_login_errors'] == "no" ) echo "checked"; ?>>&nbsp;No</p>
309
+ <h3><?php _e('Show Credit Link?', 'loginlockdown') ?></h3>
310
+ <p>By default, Login LockDown will display the following message on the login form:<br />
311
+ <blockquote>Login form protected by <a href='http://www.bad-neighborhood.com/login-lockdown.html'>Login LockDown</a>.</blockquote>
312
+ This helps others know about the plugin so they can protect their blogs as well if they like. However, you can disable this message if you prefer.</p>
313
+ <input type="radio" name="ll_show_credit_link" value="yes" <?php if( $loginlockdownAdminOptions['show_credit_link'] == "yes" || $loginlockdownAdminOptions['show_credit_link'] == "" ) echo "checked"; ?>>&nbsp;Yes, display the credit link.<br />
314
+ <input type="radio" name="ll_show_credit_link" value="shownofollow" <?php if( $loginlockdownAdminOptions['show_credit_link'] == "shownofollow" ) echo "checked"; ?>>&nbsp;Display the credit link, but add "rel='nofollow'" (ie. do not pass any link juice).<br />
315
+ <input type="radio" name="ll_show_credit_link" value="no" <?php if( $loginlockdownAdminOptions['show_credit_link'] == "no" ) echo "checked"; ?>>&nbsp;No, do not display the credit link.<br />
316
  <div class="submit">
317
+ <input type="submit" class="button button-primary" name="update_loginlockdownSettings" value="<?php _e('Update Settings', 'loginlockdown') ?>" /></div>
318
  </form>
319
  <br />
320
+ <form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
321
+ <?php
322
+ if ( function_exists('wp_nonce_field') )
323
+ wp_nonce_field('login-lockdown_release-lockdowns');
324
  ?>
325
  <h3><?php _e('Currently Locked Out', 'loginlockdown') ?></h3>
326
  <?php
327
  $num_lockedout = count($dalist);
328
  if( 0 == $num_lockedout ) {
329
+ echo "<p>No IP blocks currently locked out.</p>";
330
  } else {
331
  foreach ( $dalist as $key => $option ) {
332
  ?>
336
  }
337
  ?>
338
  <div class="submit">
339
+ <input type="submit" class="button button-primary" name="release_lockdowns" value="<?php _e('Release Selected', 'loginlockdown') ?>" /></div>
340
  </form>
341
  </div>
342
  <?php
344
 
345
  function loginlockdown_ap() {
346
  if ( function_exists('add_options_page') ) {
347
+ add_options_page('Login LockDown', 'Login LockDown', 'manage_options', basename(__FILE__), 'print_loginlockdownAdminPage');
348
  }
349
  }
350
 
351
  function ll_credit_link(){
352
+ global $loginlockdownOptions;
353
+ $thispage = "http://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
354
+ $homepage = get_option( "home" );
355
+ $showcreditlink = $loginlockdownOptions['show_credit_link'];
356
+ $relnofollow = "rel='nofollow'";
357
+ if ( $showcreditlink != "shownofollow" && ($thispage == $homepage || $thispage == $homepage . "/" || substr($_SERVER["REQUEST_URI"], strlen($_SERVER["REQUEST_URI"]) - 12) == "wp-login.php") ) {
358
+ $relnofollow = "";
359
+ }
360
+ if ( $showcreditlink != "no" ) {
361
+ echo "<p>Login form protected by <a href='http://www.bad-neighborhood.com/login-lockdown.html' $relnofollow>Login LockDown</a>.<br /><br /><br /></p>";
362
+ }
363
  }
364
 
365
  //Actions and Filters
366
  if ( isset($loginlockdown_db_version) ) {
367
  //Actions
368
  add_action('admin_menu', 'loginlockdown_ap');
369
+ if(!defined('WP_PLUGIN_DIR')){
370
  define('WP_PLUGIN_DIR', ABSPATH . 'wp-content/plugins');
371
  }
372
+ $activatestr = str_replace(WP_PLUGIN_DIR . "/", "activate_", __FILE__);
373
  add_action($activatestr, 'loginLockdown_install');
374
+ add_action('login_form', 'll_credit_link');
375
+
376
+ remove_filter('authenticate', 'wp_authenticate_username_password', 20, 3);
377
  add_filter('authenticate', 'll_wp_authenticate_username_password', 20, 3);
378
  //Filters
379
+ //Functions
380
+ function ll_wp_authenticate_username_password($user, $username, $password) {
381
+ if ( is_a($user, 'WP_User') ) { return $user; }
382
+
383
+ if ( empty($username) || empty($password) ) {
384
+ $error = new WP_Error();
385
+
386
+ if ( empty($username) )
387
+ $error->add('empty_username', __('<strong>ERROR</strong>: The username field is empty.'));
388
+
389
+ if ( empty($password) )
390
+ $error->add('empty_password', __('<strong>ERROR</strong>: The password field is empty.'));
391
+
392
+ return $error;
393
+ }
394
+
395
+ $userdata = get_user_by('login',$username);
396
+
397
+ if ( !$userdata ) {
398
+ return new WP_Error('invalid_username', sprintf(__('<strong>ERROR</strong>: Invalid username. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
399
+ }
400
+
401
+ $userdata = apply_filters('wp_authenticate_user', $userdata, $password);
402
+ if ( is_wp_error($userdata) ) {
403
+ return $userdata;
404
+ }
405
+
406
+ if ( !wp_check_password($password, $userdata->user_pass, $userdata->ID) ) {
407
+ return new WP_Error('incorrect_password', sprintf(__('<strong>ERROR</strong>: Incorrect password. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
408
+ }
409
+
410
+ $user = new WP_User($userdata->ID);
411
+ return $user;
412
+ }
413
+
414
 
415
  if ( !function_exists('wp_authenticate') ) :
416
  function wp_authenticate($username, $password) {
417
  global $wpdb, $error;
418
+ global $loginlockdownOptions;
419
+
420
+ $username = sanitize_user($username);
421
+ $password = trim($password);
422
+
423
  if ( "" != isLockedDown() ) {
424
  return new WP_Error('incorrect_password', "<strong>ERROR</strong>: We're sorry, but this IP range has been blocked due to too many recent " .
425
  "failed login attempts.<br /><br />Please try again later.");
426
  }
427
+
428
+ $user = apply_filters('authenticate', null, $username, $password);
429
+
430
+ if ( $user == null ) {
431
+ // TODO what should the error message be? (Or would these even happen?)
432
+ // Only needed if all authentication handlers fail to return anything.
433
+ $user = new WP_Error('authentication_failed', __('<strong>ERROR</strong>: Invalid username or incorrect password.'));
434
+ }
435
+
436
+ $ignore_codes = array('empty_username', 'empty_password');
437
+
438
+ if (is_wp_error($user) && !in_array($user->get_error_code(), $ignore_codes) ) {
439
  incrementFails($username);
440
  if ( $loginlockdownOptions['max_login_retries'] <= countFails($username) ) {
441
  lockDown($username);
442
  return new WP_Error('incorrect_password', __("<strong>ERROR</strong>: We're sorry, but this IP range has been blocked due to too many recent " .
443
  "failed login attempts.<br /><br />Please try again later."));
444
+ }
445
+ if ( 'yes' == $loginlockdownOptions['mask_login_errors'] ) {
446
+ return new WP_Error('authentication_failed', sprintf(__('<strong>ERROR</strong>: Invalid username or incorrect password. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
447
  } else {
448
+ do_action('wp_login_failed', $username);
449
+ }
450
+ }
451
+
452
  return $user;
453
  }
454
  endif;
455
  }
456
 
457
 
458
+ ?>
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === Login LockDown ===
2
  Developer: Michael VanDeMar (michael@endlesspoetry.com)
3
  Tags: security, login
4
- Requires at least: 2.5
5
- Tested up to: 2.8.4
6
- Stable Tag: 1.5
7
 
8
  Limits the number of login attempts from a given IP range within a certain time period.
9
 
@@ -22,4 +22,45 @@ via the Options panel. Admisitrators can release locked out IP ranges manually f
22
  2. Activate the plugin in the Plugin options.
23
  3. Customize the settings from the Options panel, if desired.
24
 
25
- Enjoy.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  === Login LockDown ===
2
  Developer: Michael VanDeMar (michael@endlesspoetry.com)
3
  Tags: security, login
4
+ Requires at least: 3.6
5
+ Tested up to: 3.8.1
6
+ Stable Tag: 1.6
7
 
8
  Limits the number of login attempts from a given IP range within a certain time period.
9
 
22
  2. Activate the plugin in the Plugin options.
23
  3. Customize the settings from the Options panel, if desired.
24
 
25
+ Enjoy.
26
+
27
+ == Change Log ==
28
+
29
+ ver. 1.6 7-Mar-2014
30
+
31
+ - cleaned up deprecated functions
32
+ - fixed bug with invalid property on a non-object when locking out invalid usernames
33
+ - fixed utilization of $wpdb->prepare
34
+ - added more descriptive help text to each of the options
35
+ - added the ability to remove the "Login form protected by Login LockDown." message from within the dashboard
36
+
37
+ ver. 1.5 17-Sep-2009
38
+
39
+ - implemented wp_nonce security in the options and lockdown release forms in the admin screen
40
+ - fixed a security hole with an improperly escaped SQL query
41
+ - encoded certain outputs in the admin panel using esc_attr() to prevent XSS attacks
42
+ - fixed an issue with the 'Lockout Invalid Usernames' option not functioning as intended
43
+
44
+ ver. 1.4 29-Aug-2009
45
+
46
+ - removed erroneous error affecting WP 2.8+
47
+ - fixed activation error caused by customizing the location of the wp-content folder
48
+ - added in the option to mask which specific login error (invalid username or invalid password) was generated
49
+ - added in the option to lock out failed login attempts even if the username doesn't exist
50
+
51
+ ver. 1.3 23-Feb-2009
52
+ - adjusted positioning of plugin byline
53
+ - allowed for dynamic location of plugin files
54
+
55
+ ver. 1.2 15-Jun-2008
56
+
57
+ - now compatible with WordPress 2.5 and up only
58
+
59
+ ver. 1.1 01-Sep-2007
60
+
61
+ - revised time query to MySQL 4.0 compatability
62
+
63
+ ver. 1.0 29-Aug-2007
64
+
65
+ - released
66
+
version.txt CHANGED
@@ -1 +1 @@
1
- 1.5
1
+ 1.6