WordPress.org

Make WordPress Core

Changeset 45262


Ignore:
Timestamp:
04/24/2019 07:43:29 AM (6 months ago)
Author:
tellyworth
Message:

Upgrade/install: fix verification bugs and scale back signature checks.

This fixes several bugs in the signature verification code:
Disables signature checks on certain incompatible PHP versions that cause math errors when opcache is enabled;
Prevents a spurious URL and subsequent error when downloading a zip file with query arguments;
Prevents errors triggered by third-party upgrade scripts as per #46615;
Disables signature tests for Plugins, Themes, and Translations, leaving only core updates.

At the 5.2 release the API servers will only provide signatures for core update packages, which is why messages are suppressed for plugins and other package types. Signatures for those other items will become available later.

Props dd32.
See #39309, #46615

Location:
trunk/src/wp-admin/includes
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-core-upgrader.php

    r45046 r45262  
    122122        }
    123123
    124         $download = $this->download_package( $current->packages->$to_download );
     124        $download = $this->download_package( $current->packages->$to_download, true );
    125125
    126126        // Allow for signature soft-fail.
  • trunk/src/wp-admin/includes/class-wp-upgrader.php

    r44954 r45262  
    245245     * @since 2.8.0
    246246     *
    247      * @param string $package The URI of the package. If this is the full path to an
    248      *                        existing local file, it will be returned untouched.
     247     * @param string $package          The URI of the package. If this is the full path to an
     248     *                                 existing local file, it will be returned untouched.
     249     * @param bool   $check_signatures Whether to validate file signatures. Default false.
    249250     * @return string|WP_Error The full path to the downloaded package file, or a WP_Error object.
    250251     */
    251     public function download_package( $package ) {
     252    public function download_package( $package, $check_signatures = false ) {
    252253
    253254        /**
     
    276277        $this->skin->feedback( 'downloading_package', $package );
    277278
    278         $download_file = download_url( $package, 300, true );
     279        $download_file = download_url( $package, 300, $check_signatures );
    279280
    280281        if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) {
     
    731732         * of the file if the package is a local file)
    732733         */
    733         $download = $this->download_package( $options['package'] );
     734        $download = $this->download_package( $options['package'], true );
    734735
    735736        // Allow for signature soft-fail.
    736737        // WARNING: This may be removed in the future.
    737738        if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
    738             // Outout the failure error as a normal feedback, and not as an error:
    739             $this->skin->feedback( $download->get_error_message() );
    740 
    741             // Report this failure back to WordPress.org for debugging purposes.
    742             wp_version_check(
    743                 array(
    744                     'signature_failure_code' => $download->get_error_code(),
    745                     'signature_failure_data' => $download->get_error_data(),
    746                 )
    747             );
     739
     740            // Don't output the 'no signature could be found' failure message for now.
     741            if ( 'signature_verification_no_signature' != $download->get_error_code() || WP_DEBUG ) {
     742                // Outout the failure error as a normal feedback, and not as an error:
     743                $this->skin->feedback( $download->get_error_message() );
     744
     745                // Report this failure back to WordPress.org for debugging purposes.
     746                wp_version_check(
     747                    array(
     748                        'signature_failure_code' => $download->get_error_code(),
     749                        'signature_failure_data' => $download->get_error_data(),
     750                    )
     751                );
     752            }
    748753
    749754            // Pretend this error didn't happen.
  • trunk/src/wp-admin/includes/file.php

    r45232 r45262  
    969969 * @since 5.2.0 Signature Verification with SoftFail was added.
    970970 *
    971  * @param string $url                The URL of the file to download.
    972  * @param int    $timeout            The timeout for the request to download the file. Default 300 seconds.
    973  * @param bool   $signature_softfail Whether to allow Signature Verification to softfail. Default true.
     971 * @param string $url                    The URL of the file to download.
     972 * @param int    $timeout                The timeout for the request to download the file. Default 300 seconds.
     973 * @param bool   $signature_verification Whether to perform Signature Verification. Default false.
    974974 * @return string|WP_Error Filename on success, WP_Error on failure.
    975975 */
    976 function download_url( $url, $timeout = 300, $signature_softfail = true ) {
     976function download_url( $url, $timeout = 300, $signature_verification = false ) {
    977977    //WARNING: The file is not automatically deleted, The script must unlink() the file.
    978978    if ( ! $url ) {
     
    10381038    }
    10391039
    1040     /**
    1041      * Filters the list of hosts which should have Signature Verification attempted on.
    1042      *
    1043      * @since 5.2.0
    1044      *
    1045      * @param array List of hostnames.
    1046      */
    1047     $signed_hostnames       = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) );
    1048     $signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true );
    1049 
    1050     // Perform the valiation
     1040    // If the caller expects signature verification to occur, check to see if this URL supports it.
     1041    if ( $signature_verification ) {
     1042        /**
     1043         * Filters the list of hosts which should have Signature Verification attempteds on.
     1044         *
     1045         * @since 5.2.0
     1046         *
     1047         * @param array List of hostnames.
     1048         */
     1049        $signed_hostnames       = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) );
     1050        $signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true );
     1051    }
     1052
     1053    // Perform signature valiation if supported.
    10511054    if ( $signature_verification ) {
    10521055        $signature = wp_remote_retrieve_header( $response, 'x-content-signature' );
     
    10541057            // Retrieve signatures from a file if the header wasn't included.
    10551058            // WordPress.org stores signatures at $package_url.sig
    1056             $signature_request = wp_safe_remote_get( $url . '.sig' );
    1057             if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) {
    1058                 $signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) );
     1059
     1060            $signature_url = false;
     1061            $url_path      = parse_url( $url, PHP_URL_PATH );
     1062            if ( substr( $url_path, -4 ) == '.zip' || substr( $url_path, -7 ) == '.tar.gz' ) {
     1063                $signature_url = str_replace( $url_path, $url_path . '.sig', $url );
     1064            }
     1065
     1066            /**
     1067             * Filter the URL where the signature for a file is located.
     1068             *
     1069             * @since 5.2
     1070             *
     1071             * @param false|string $signature_url The URL where signatures can be found for a file, or false if none are known.
     1072             * @param string $url                 The URL being verified.
     1073             */
     1074            $signature_url = apply_filters( 'wp_signature_url', $signature_url, $url );
     1075
     1076            if ( $signature_url ) {
     1077                $signature_request = wp_safe_remote_get(
     1078                    $signature_url,
     1079                    array(
     1080                        'limit_response_size' => 10 * 1024, // 10KB should be large enough for quite a few signatures.
     1081                    )
     1082                );
     1083
     1084                if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) {
     1085                    $signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) );
     1086                }
    10591087            }
    10601088        }
     
    10761104             * @param string $url                The url being accessed.
    10771105             */
    1078             apply_filters( 'wp_signature_softfail', $signature_softfail, $url )
     1106            apply_filters( 'wp_signature_softfail', true, $url )
    10791107        ) {
    10801108            $signature_verification->add_data( $tmpfname, 'softfail-filename' );
     
    11461174            ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' )
    11471175        );
     1176    }
     1177
     1178    // Check for a edge-case affecting PHP Maths abilities
     1179    if (
     1180        ! extension_loaded( 'sodium' ) &&
     1181        in_array( PHP_VERSION_ID, [ 70200, 70201, 70202 ], true ) &&
     1182        extension_loaded( 'opcache' )
     1183    ) {
     1184        // Sodium_Compat isn't compatible with PHP 7.2.0~7.2.2 due to a bug in the PHP Opcache extension, bail early as it'll fail.
     1185        // https://bugs.php.net/bug.php?id=75938
     1186
     1187        return new WP_Error(
     1188            'signature_verification_unsupported',
     1189            sprintf(
     1190                /* translators: 1: The filename of the package. */
     1191                __( 'The authenticity of %1$s could not be verified as signature verification is unavailable on this system.' ),
     1192                '<span class="code">' . esc_html( $filename_for_errors ) . '</span>'
     1193            ),
     1194            array(
     1195                'php'    => phpversion(),
     1196                'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ),
     1197            )
     1198        );
     1199
    11481200    }
    11491201
Note: See TracChangeset for help on using the changeset viewer.