| @@ -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 | { |