| @@ -7,11 +7,11 @@ | ||
| 7 | 7 | * @subpackage Converters |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | 10 | /** |
| 11 | - * Implementation of Invision Power Board v3.x converter. | |
| 11 | + * Implementation of Invision Power Board v3.1-3.4 converter. | |
| 12 | 12 | * |
| 13 | - * @since 2.3.0 bbPress (r4713) | |
| 13 | + * @since 2.1.0 bbPress (r3816) | |
| 14 | 14 | * |
| 15 | 15 | * @link Codex Docs https://codex.bbpress.org/import-forums/invision |
| 16 | 16 | */ |
| 17 | 17 | class Invision extends BBP_Converter_Base { |
| @@ -521,16 +521,15 @@ | ||
| 521 | 521 | return $count; |
| 522 | 522 | } |
| 523 | 523 | |
| 524 | 524 | /** |
| 525 | - * This method is to save the salt and password together. That | |
| 526 | - * way when we authenticate it we can get it out of the database | |
| 527 | - * as one value. Array values are auto sanitized by WordPress. | |
| 525 | + * Save the salt and password together. Pre-slash the salt because WordPress | |
| 526 | + * removes one layer of slashes when storing user metadata. | |
| 528 | 527 | */ |
| 529 | 528 | public function callback_savepass( $field, $row ) { |
| 530 | 529 | return array( |
| 531 | 530 | 'hash' => $field, |
| 532 | - 'salt' => $row['members_pass_salt'] | |
| 531 | + 'salt' => isset( $row['members_pass_salt'] ) ? wp_slash( (string) $row['members_pass_salt'] ) : '' | |
| 533 | 532 | ); |
| 534 | 533 | } |
| 535 | 534 | |
| 536 | 535 | /** |
| @@ -539,28 +538,55 @@ | ||
| 539 | 538 | */ |
| 540 | 539 | public function authenticate_pass( $password, $serialized_pass ) { |
| 541 | 540 | |
| 542 | 541 | // Unserialize the password, with safeguards |
| 543 | - $pass_array = unserialize( | |
| 544 | - $serialized_pass, | |
| 545 | - array( | |
| 546 | - 'allowed_classes' => false, | |
| 547 | - 'max_depth' => 1 | |
| 548 | - ) | |
| 549 | - ); | |
| 542 | + $pass_array = $this->unserialize_pass( $serialized_pass ); | |
| 550 | 543 | |
| 551 | - // Bail if missing values | |
| 552 | - if ( ! is_array( $pass_array ) || ! isset( $pass_array['hash'], $pass_array['salt'] ) ) { | |
| 544 | + // Bail if missing or invalid values | |
| 545 | + if ( ! is_string( $password ) || ! is_array( $pass_array ) || ! isset( $pass_array['hash'], $pass_array['salt'] ) || ! is_string( $pass_array['hash'] ) || ! is_string( $pass_array['salt'] ) ) { | |
| 553 | 546 | return false; |
| 554 | 547 | } |
| 555 | 548 | |
| 556 | - // Return comparison | |
| 557 | - return hash_equals( | |
| 558 | - $pass_array['hash'], | |
| 559 | - md5( md5( $pass_array['salt'] ) . md5( $this->to_char( $password ) ) ) | |
| 560 | - ); | |
| 549 | + $salt_hash = md5( $pass_array['salt'] ); | |
| 550 | + $hash = md5( $salt_hash . md5( $this->clean_password( $password ) ) ); | |
| 551 | + $old_hash = md5( $salt_hash . md5( $this->to_char( $password ) ) ); | |
| 552 | + | |
| 553 | + // Retain support for metadata compatible with the historical verifier | |
| 554 | + $hash_matches = hash_equals( $pass_array['hash'], $hash ); | |
| 555 | + $old_hash_matches = hash_equals( $pass_array['hash'], $old_hash ); | |
| 556 | + | |
| 557 | + return $hash_matches || $old_hash_matches; | |
| 561 | 558 | } |
| 562 | 559 | |
| 560 | + /** | |
| 561 | + * Apply IP.Board 3's password input cleaning. | |
| 562 | + * | |
| 563 | + * @param string $password Password in plain text. | |
| 564 | + * @return string Cleaned password. | |
| 565 | + */ | |
| 566 | + private function clean_password( $password ) { | |
| 567 | + $password = str_replace( ' ', ' ', $password ); | |
| 568 | + $password = str_replace( array( "\r\n", "\n\r", "\r" ), "\n", $password ); | |
| 569 | + $password = str_replace( '&', '&', $password ); | |
| 570 | + $password = str_replace( '<!--', '<!--', $password ); | |
| 571 | + $password = str_replace( '-->', '-->', $password ); | |
| 572 | + $password = str_ireplace( '<script', '<script', $password ); | |
| 573 | + $password = str_replace( '>', '>', $password ); | |
| 574 | + $password = str_replace( '<', '<', $password ); | |
| 575 | + $password = str_replace( '"', '"', $password ); | |
| 576 | + $password = str_replace( "\n", '<br />', $password ); | |
| 577 | + $password = str_replace( '$', '$', $password ); | |
| 578 | + $password = str_replace( '!', '!', $password ); | |
| 579 | + | |
| 580 | + return str_replace( "'", ''', $password ); | |
| 581 | + } | |
| 582 | + | |
| 583 | + /** | |
| 584 | + * Apply the historical bbPress Invision password transformation. | |
| 585 | + * | |
| 586 | + * @param string $input Password in plain text. | |
| 587 | + * @return string Transformed password. | |
| 588 | + */ | |
| 563 | 589 | private function to_char( $input ) { |
| 564 | 590 | $output = ''; |
| 565 | 591 | $length = strlen( $input ); |
| 566 | 592 | for ( $i = 0; $i < $length; $i++ ) { |