| @@ -9,9 +9,9 @@ | ||
| 9 | 9 | |
| 10 | 10 | /** |
| 11 | 11 | * Implementation of Vanilla 2.0.18.1 Converter |
| 12 | 12 | * |
| 13 | - * @since 2.3.0 bbPress (r4717) | |
| 13 | + * @since 2.3.0 bbPress (r4718) | |
| 14 | 14 | * |
| 15 | 15 | * @link Codex Docs https://codex.bbpress.org/import-forums/vanilla |
| 16 | 16 | */ |
| 17 | 17 | class Vanilla extends BBP_Converter_Base { |
| @@ -468,14 +468,30 @@ | ||
| 468 | 468 | ); |
| 469 | 469 | |
| 470 | 470 | // Store old user password (Stored in usermeta) |
| 471 | 471 | $this->field_map[] = array( |
| 472 | + 'from_tablename' => 'User', | |
| 473 | + 'from_fieldname' => 'Password', | |
| 474 | + 'to_type' => 'user', | |
| 475 | + 'to_fieldname' => '_bbp_password', | |
| 476 | + 'callback_method' => 'callback_savepass' | |
| 477 | + ); | |
| 478 | + | |
| 479 | + // Password hash method (Used by the password callback above) | |
| 480 | + $this->field_map[] = array( | |
| 472 | 481 | 'from_tablename' => 'User', |
| 473 | - 'from_fieldname' => 'Password', | |
| 482 | + 'from_fieldname' => 'HashMethod', | |
| 474 | 483 | 'to_type' => 'user', |
| 475 | - 'to_fieldname' => '_bbp_password' | |
| 484 | + 'to_fieldname' => '' | |
| 476 | 485 | ); |
| 477 | 486 | |
| 487 | + // User password verify class (Stored in usermeta for verifying password) | |
| 488 | + $this->field_map[] = array( | |
| 489 | + 'to_type' => 'user', | |
| 490 | + 'to_fieldname' => '_bbp_class', | |
| 491 | + 'default' => 'Vanilla' | |
| 492 | + ); | |
| 493 | + | |
| 478 | 494 | // User name. |
| 479 | 495 | $this->field_map[] = array( |
| 480 | 496 | 'from_tablename' => 'User', |
| 481 | 497 | 'from_fieldname' => 'Name', |
| @@ -589,20 +605,80 @@ | ||
| 589 | 605 | return $count; |
| 590 | 606 | } |
| 591 | 607 | |
| 592 | 608 | /** |
| 593 | - * This method is to save the salt and password together. That | |
| 594 | - * way when we authenticate it we can get it out of the database | |
| 595 | - * as one value. Array values are auto sanitized by WordPress. | |
| 609 | + * Store Vanilla's password hash with its hash method. | |
| 610 | + * | |
| 611 | + * WordPress unslashes metadata before storage, so pre-slash the source value | |
| 612 | + * to preserve legacy plaintext passwords containing quotes or backslashes. | |
| 613 | + * | |
| 614 | + * @param string $field Password hash or legacy plaintext password. | |
| 615 | + * @param array $row Source database row. | |
| 616 | + * @return array|bool Password metadata, or false if invalid. | |
| 596 | 617 | */ |
| 597 | 618 | public function callback_savepass( $field, $row ) { |
| 598 | - return false; | |
| 619 | + if ( ! is_string( $field ) ) { | |
| 620 | + return false; | |
| 621 | + } | |
| 622 | + | |
| 623 | + $method = isset( $row['HashMethod'] ) && is_string( $row['HashMethod'] ) | |
| 624 | + ? $row['HashMethod'] | |
| 625 | + : ''; | |
| 626 | + | |
| 627 | + return wp_slash( | |
| 628 | + array( | |
| 629 | + 'hash' => $field, | |
| 630 | + 'method' => $method, | |
| 631 | + ) | |
| 632 | + ); | |
| 599 | 633 | } |
| 600 | 634 | |
| 601 | 635 | /** |
| 602 | - * This method is to take the pass out of the database and compare | |
| 603 | - * to a pass the user has typed in. | |
| 636 | + * Check a password against Vanilla 2 authentication metadata. | |
| 637 | + * | |
| 638 | + * Vanilla 2.0.18.1 generated portable phpass hashes, while retaining | |
| 639 | + * compatibility with legacy MD5 hashes and plaintext passwords. | |
| 640 | + * | |
| 641 | + * @param string $password Password in plain text. | |
| 642 | + * @param string $serialized_pass Serialized authentication metadata. | |
| 643 | + * @return bool Whether the password is correct. | |
| 604 | 644 | */ |
| 605 | 645 | public function authenticate_pass( $password, $serialized_pass ) { |
| 606 | - return false; | |
| 646 | + | |
| 647 | + // Unserialize the password, with safeguards | |
| 648 | + $pass_array = $this->unserialize_pass( $serialized_pass ); | |
| 649 | + | |
| 650 | + // Bail if missing or invalid values | |
| 651 | + if ( ! is_string( $password ) || ! is_array( $pass_array ) || ! isset( $pass_array['hash'], $pass_array['method'] ) || ! is_string( $pass_array['hash'] ) || ! is_string( $pass_array['method'] ) ) { | |
| 652 | + return false; | |
| 653 | + } | |
| 654 | + | |
| 655 | + // This converter targets Vanilla's native password formats only | |
| 656 | + if ( '' !== $pass_array['method'] && 0 !== strcasecmp( 'Vanilla', $pass_array['method'] ) ) { | |
| 657 | + return false; | |
| 658 | + } | |
| 659 | + | |
| 660 | + $hash = $pass_array['hash']; | |
| 661 | + | |
| 662 | + if ( '' === $hash || '*' === $hash ) { | |
| 663 | + return false; | |
| 664 | + } | |
| 665 | + | |
| 666 | + // Vanilla used phpass for hashes beginning with "$" or "_" | |
| 667 | + if ( '$' === $hash[0] || '_' === $hash[0] ) { | |
| 668 | + require_once ABSPATH . WPINC . '/class-phpass.php'; | |
| 669 | + | |
| 670 | + $hasher = new PasswordHash( 8, true ); | |
| 671 | + | |
| 672 | + return $hasher->CheckPassword( $password, $hash ); | |
| 673 | + } | |
| 674 | + | |
| 675 | + if ( empty( $password ) ) { | |
| 676 | + return false; | |
| 677 | + } | |
| 678 | + | |
| 679 | + $plain_matches = hash_equals( $hash, $password ); | |
| 680 | + $md5_matches = hash_equals( $hash, md5( $password ) ); | |
| 681 | + | |
| 682 | + return $plain_matches || $md5_matches; | |
| 607 | 683 | } |
| 608 | 684 | } |