Make WordPress Core

Changeset 45262


Ignore:
Timestamp:
04/24/2019 07:43:29 AM (7 years 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.