PluginProbe
Two Factor Authentication / 1.2.6
Two Factor Authentication v1.2.6
1.12.2 1.13.0 1.14.10 1.14.11 1.14.14 1.14.15 1.14.16 1.14.17 1.14.23 1.14.24 1.14.26 1.14.27 1.14.3 1.14.4 1.14.5 1.14.7 1.14.8 1.15.5 1.16.0 1.2.10 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 All 98 releases
← All changes | includes/class.TFA.php +28 -138 1.2.131.2.6 View file →
@@ -86,63 +86,12 @@
86 86
87 87 return $code;
88 88 }
89 89
90 - // Port over keys that were encrypted with mcrypt and its non-compliant padding scheme, so that if the site is ever migrated to a server without mcrypt, they can still be decrypted
91 - public function potentially_port_private_keys() {
92 90
93 - $simba_tfa_priv_key_format = get_site_option('simba_tfa_priv_key_format', false);
94 -
95 - $attempts = 0;
96 - $successes = 0;
97 -
98 - if ($simba_tfa_priv_key_format < 1 && function_exists('openssl_encrypt')) {
99 -
100 - error_log("TFA: Beginning attempt to port private key encryption over to openssl");
101 - global $wpdb;
102 - $sql = "SELECT user_id, meta_value FROM ".$wpdb->usermeta." WHERE meta_key = 'tfa_priv_key_64'";
103 -
104 - $user_results = $wpdb->get_results($sql);
105 -
106 - foreach ($user_results as $u) {
107 - $dec_openssl = $this->decryptString($u->meta_value, $u->user_id, true);
108 -
109 - $ported = false;
110 - if ('' == $dec_openssl) {
111 -
112 - $attempts++;
113 -
114 - $dec_default = $this->decryptString($u->meta_value, $u->user_id);
115 -
116 - if ('' != $dec_default) {
117 -
118 - $enc = $this->encryptString($dec_default, $u->user_id);
119 -
120 - if ($enc) {
121 -
122 - $ported = true;
123 - $successes++;
124 - update_user_meta($u->user_id, 'tfa_priv_key_64', $enc);
125 - }
126 - }
127 -
128 - }
129 -
130 - if ($ported) {
131 - error_log("TFA: Successfully ported the key for user with ID ".$u->user_id." over to openssl");
132 - } else {
133 - error_log("TFA: Failed to port the key for user with ID ".$u->user_id." over to openssl");
134 - }
135 - }
136 - if ($attempts == 0 || $successes > 0) update_site_option('simba_tfa_priv_key_format', 1);
137 -
138 - }
139 - }
140 -
141 91 public function getPrivateKeyPlain($enc, $user_ID)
142 92 {
143 93 $dec = $this->decryptString($enc, $user_ID);
144 - $this->potentially_port_private_keys();
145 94 return $dec;
146 95 }
147 96
148 97
@@ -147,9 +96,9 @@
147 96
148 97
149 98 public function getPanicCodesString($arr, $user_ID)
150 99 {
151 - if(!is_array($arr)) return '<em>'.__('No emergency codes left. Sorry.', 'two-factor-authentication').'</em>';
100 + if(!is_array($arr)) return '<em>'.__('No emergency codes left. Sorry.', SIMBA_TFA_TEXT_DOMAIN).'</em>';
152 101
153 102 $emergency_str = '';
154 103
155 104 foreach($arr as $p_code) {
@@ -157,9 +106,9 @@
157 106 }
158 107
159 108 $emergency_str = rtrim($emergency_str, ', ');
160 109
161 - $emergency_str = $emergency_str ? $emergency_str : '<em>'.__('No emergency codes left. Sorry.', 'two-factor-authentication').'</em>';
110 + $emergency_str = $emergency_str ? $emergency_str : '<em>'.__('No emergency codes left. Sorry.', SIMBA_TFA_TEXT_DOMAIN).'</em>';
162 111 return $emergency_str;
163 112 }
164 113
165 114 public function preAuth($params)
@@ -200,10 +149,8 @@
200 149
201 150 public function authUserFromLogin($params)
202 151 {
203 152
204 - $params = apply_filters('simbatfa_auth_user_from_login_params', $params);
205 -
206 153 global $simba_two_factor_authentication, $wpdb;
207 154
208 155 if(!$this->isCallerActive($params))
209 156 return true;
@@ -233,42 +180,29 @@
233 180
234 181 $account_age = time() - strtotime($user_registered);
235 182
236 183 if ($account_age > $requireafter) {
237 - return new WP_Error('tfa_required', apply_filters('simbatfa_notfa_forbidden_login', '<strong>'.__('Error:', 'two-factor-authentication').'</strong> '.__('The site owner has forbidden you to login without two-factor authentication. Please contact the site owner to re-gain access.', 'two-factor-authentication')));
184 + return new WP_Error('tfa_required', apply_filters('simbatfa_notfa_forbidden_login', '<strong>'.__('Error:', SIMBA_TFA_TEXT_DOMAIN).'</strong> '.__('The site owner has forbidden you to login without two-factor authentication. Please contact the site owner to re-gain access.', SIMBA_TFA_TEXT_DOMAIN)));
238 185 }
239 186
240 187 return true;
241 188 }
242 189
243 - $tfa_creds_user_id = !empty($params['creds_user_id']) ? $params['creds_user_id'] : $user_ID;
244 -
245 - if ($tfa_creds_user_id != $user_ID) {
246 -
247 - // Authenticating using a different user's credentials (e.g. https://wordpress.org/plugins/use-administrator-password/)
248 - // In this case, we require that different user to have TFA active - so that this mechanism can't be used to avoid TFA
249 -
250 - if(!$this->isActivatedForUser($tfa_creds_user_id) || !$this->isActivatedByUser($tfa_creds_user_id)) {
251 - return new WP_Error('tfa_required', apply_filters('simbatfa_notfa_forbidden_login_altuser', '<strong>'.__('Error:', 'two-factor-authentication').'</strong> '.__('You are attempting to log in to an account that has two-factor authentication enabled; this requires you to also have two-factor authentication enabled on the account whose credentials you are using.', 'two-factor-authentication')));
252 - }
253 -
254 - }
255 -
256 - $tfa_priv_key = get_user_meta($tfa_creds_user_id, 'tfa_priv_key_64', true);
257 -// $tfa_last_login = get_user_meta($tfa_creds_user_id, 'tfa_last_login', true); // Unused
258 - $tfa_last_pws_arr = get_user_meta($tfa_creds_user_id, 'tfa_last_pws', true);
190 + $tfa_priv_key = get_user_meta($user_ID, 'tfa_priv_key_64', true);
191 + $tfa_last_login = get_user_meta($user_ID, 'tfa_last_login', true);
192 + $tfa_last_pws_arr = get_user_meta($user_ID, 'tfa_last_pws', true);
259 193 $tfa_last_pws = @$tfa_last_pws_arr ? $tfa_last_pws_arr : array();
260 - $alg = $this->getUserAlgorithm($tfa_creds_user_id);
194 + $alg = $this->getUserAlgorithm($user_ID);
261 195
262 196 $current_time_window = intval(time()/30);
263 197
264 198 //Give the user 1,5 minutes time span to enter/retrieve the code
265 199 //Or check $this->check_forward_counter_window number of events if hotp
266 - $codes = $this->generateOTPsForLoginCheck($tfa_creds_user_id, $tfa_priv_key);
200 + $codes = $this->generateOTPsForLoginCheck($user_ID, $tfa_priv_key);
267 201
268 202 //A recently used code was entered.
269 203 //Not ok
270 - if(in_array($this->hash($user_code, $tfa_creds_user_id), $tfa_last_pws))
204 + if(in_array($this->hash($user_code, $user_ID), $tfa_last_pws))
271 205 return false;
272 206
273 207 $match = false;
274 208 foreach($codes as $index => $code)
@@ -283,9 +217,9 @@
283 217
284 218 //Check emergency codes
285 219 if(!$match)
286 220 {
287 - $emergency_codes = get_user_meta($tfa_creds_user_id, 'simba_tfa_emergency_codes_64', true);
221 + $emergency_codes = get_user_meta($user_ID, 'simba_tfa_emergency_codes_64', true);
288 222
289 223 if(!@$emergency_codes)
290 224 return $match;
291 225
@@ -290,9 +224,9 @@
290 224 return $match;
291 225
292 226 $dec = array();
293 227 foreach($emergency_codes as $emergency_code)
294 - $dec[] = trim($this->decryptString(trim($emergency_code), $tfa_creds_user_id));
228 + $dec[] = trim($this->decryptString(trim($emergency_code), $user_ID));
295 229
296 230 $in_array = array_search($user_code, $dec);
297 231 $match = $in_array !== false;
298 232
@@ -298,39 +232,39 @@
298 232
299 233 if($match)//Remove emergency code
300 234 {
301 235 array_splice($emergency_codes, $in_array, 1);
302 - update_user_meta($tfa_creds_user_id, 'simba_tfa_emergency_codes_64', $emergency_codes);
303 - do_action('simba_tfa_emergency_code_used', $tfa_creds_user_id, $emergency_codes);
236 + update_user_meta($user_ID, 'simba_tfa_emergency_codes_64', $emergency_codes);
237 + do_action('simba_tfa_emergency_code_used', $user_ID, $emergency_codes);
304 238 }
305 239
306 240 } else {
307 241 //Add the used code as well so it cant be used again
308 242 //Keep the two last codes
309 - $tfa_last_pws[] = $this->hash($user_code, $tfa_creds_user_id);
243 + $tfa_last_pws[] = $this->hash($user_code, $user_ID);
310 244 $nr_of_old_to_save = $alg == 'hotp' ? $this->check_forward_counter_window : $this->check_back_time_windows;
311 245
312 246 if(count($tfa_last_pws) > $nr_of_old_to_save)
313 247 array_splice($tfa_last_pws, 0, 1);
314 248
315 - update_user_meta($tfa_creds_user_id, 'tfa_last_pws', $tfa_last_pws);
249 + update_user_meta($user_ID, 'tfa_last_pws', $tfa_last_pws);
316 250 }
317 251
318 252 if($match)
319 253 {
320 254 //Save the time window when the last successful login took place
321 - update_user_meta($tfa_creds_user_id, 'tfa_last_login', $current_time_window);
255 + update_user_meta($user_ID, 'tfa_last_login', $current_time_window);
322 256
323 257 //Update the counter if HOTP was used
324 258 if($alg == 'hotp')
325 259 {
326 - $counter = $this->getUserCounter($tfa_creds_user_id);
260 + $counter = $this->getUserCounter($user_ID);
327 261
328 - $enc_new_counter = $this->encryptString($counter+1, $tfa_creds_user_id);
329 - update_user_meta($tfa_creds_user_id, 'tfa_hotp_counter', $enc_new_counter);
262 + $enc_new_counter = $this->encryptString($counter+1, $user_ID);
263 + update_user_meta($user_ID, 'tfa_hotp_counter', $enc_new_counter);
330 264
331 265 if($found_index > 10)
332 - update_user_meta($tfa_creds_user_id, 'tfa_hotp_off_sync', 1);
266 + update_user_meta($user_ID, 'tfa_hotp_off_sync', 1);
333 267 }
334 268 }
335 269
336 270 return $match;
@@ -474,81 +408,37 @@
474 408 return true;
475 409
476 410 return false;
477 411 }
478 -
479 - private function get_iv_size() {
480 - // mcrypt first, for backwards compatibility
481 - if (function_exists('mcrypt_get_iv_size')) {
482 - return mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
483 - } elseif (function_exists('openssl_cipher_iv_length')) {
484 - return openssl_cipher_iv_length('AES-128-CBC');
485 - }
486 - throw new Exception('One of the mcrypt or openssl PHP modules needs to be installed');
487 - }
488 -
489 - private function create_iv($iv_size) {
490 - if (function_exists('mcrypt_create_iv')) {
491 - return mcrypt_create_iv($iv_size, MCRYPT_RAND);
492 - } elseif (function_exists('openssl_random_pseudo_bytes')) {
493 - return openssl_random_pseudo_bytes($iv_size);
494 - }
495 - throw new Exception('One of the mcrypt or openssl PHP modules needs to be installed');
496 - }
497 -
498 - private function encrypt($key, $string, $iv) {
499 - // Prefer OpenSSL, because it uses correct padding, and its output can be decrypted by mcrypt - whereas, the converse is not true
500 - if (function_exists('openssl_encrypt')) {
501 - return openssl_encrypt($string, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
502 - } elseif (function_exists('mcrypt_encrypt')) {
503 - return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $iv);
504 - }
505 - throw new Exception('One of the mcrypt or openssl PHP modules needs to be installed');
506 - }
507 412
508 - private function decrypt($key, $enc, $iv, $force_openssl = false) {
509 - // Prefer mcrypt, because it can decrypt the output of both mcrypt_encrypt() and openssl_decrypt(), whereas (because of mcrypt_encrypt() using bad padding), the converse is not true
510 - if (function_exists('mcrypt_decrypt') && !$force_openssl) {
511 - return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $enc, MCRYPT_MODE_CBC, $iv);
512 - } elseif (function_exists('openssl_decrypt')) {
513 - $decrypted = openssl_decrypt($enc, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
514 - if (false === $decrypted && !$force_openssl) { error_log("TFA decryption failure: was your site migrated to a server without mcrypt? You may need to install mcrypt, or disable TFA, in order to successfully decrypt data that was previously encrypted with mcrypt."); }
515 - return $decrypted;
516 - }
517 - if ($force_openssl) return false;
518 - throw new Exception('One of the mcrypt or openssl PHP modules needs to be installed');
519 - }
520 -
521 413 public function encryptString($string, $salt_suffix)
522 414 {
523 415 $key = $this->hashAndBin($this->pw_prefix.$salt_suffix, $this->salt_prefix.$salt_suffix);
524 416
525 - $iv_size = $this->get_iv_size();
526 - $iv = $this->create_iv($iv_size);
417 + $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
418 + $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
527 419
528 - $enc = $this->encrypt($key, $string, $iv);
420 + $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $iv);
529 421
530 - if (false === $enc) return false;
531 -
532 422 $enc = $iv.$enc;
533 423 $enc_b64 = base64_encode($enc);
534 424 return $enc_b64;
535 425 }
536 426
537 - private function decryptString($enc_b64, $salt_suffix, $force_openssl = false)
427 + private function decryptString($enc_b64, $salt_suffix)
538 428 {
539 429 $key = $this->hashAndBin($this->pw_prefix.$salt_suffix, $this->salt_prefix.$salt_suffix);
540 430
541 - $iv_size = $this->get_iv_size();
431 + $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
542 432 $enc_conc = base64_decode($enc_b64);
543 433
544 434 $iv = substr($enc_conc, 0, $iv_size);
545 435 $enc = substr($enc_conc, $iv_size);
546 436
547 - $string = $this->decrypt($key, $enc, $iv, $force_openssl);
437 + $string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $enc, MCRYPT_MODE_CBC, $iv);
548 438
549 - // Remove padding bytes
550 - return rtrim($string, "\x00..\x1F");
439 + // Remove zeroed bytes
440 + return rtrim($string);
551 441 }
552 442
553 443 private function hashAndBin($pw, $salt)
554 444 {