WordPress.org

Make WordPress Core

Ticket #39309: 39309.2.diff

File 39309.2.diff, 8.5 KB (added by dd32, 9 months ago)

39309.diff with minor coding standards whitespace violations fixed

  • src/wp-admin/includes/class-wp-upgrader.php

    diff --git a/src/wp-admin/includes/class-wp-upgrader.php b/src/wp-admin/includes/class-wp-upgrader.php
    index d8433b4516..7fbecbae0d 100644
    a b public function download_package( $package ) { 
    275275
    276276                $this->skin->feedback( 'downloading_package', $package );
    277277
    278                 $download_file = download_url( $package );
     278                $download_file = download_url( $package, 300, true );
    279279
    280                 if ( is_wp_error( $download_file ) ) {
     280                if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) {
    281281                        return new WP_Error( 'download_failed', $this->strings['download_failed'], $download_file->get_error_message() );
    282282                }
    283283
    public function run( $options ) { 
    731731                 * of the file if the package is a local file)
    732732                 */
    733733                $download = $this->download_package( $options['package'] );
     734
     735                // Allow for signature soft-fail.
     736                // WARNING: This may be removed in the future.
     737                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                        );
     748
     749                        // Pretend this error didn't happen.
     750                        $download = $download->get_error_data( 'softfail-filename' );
     751                }
     752
    734753                if ( is_wp_error( $download ) ) {
    735754                        $this->skin->error( $download );
    736755                        $this->skin->after();
  • src/wp-admin/includes/file.php

    diff --git a/src/wp-admin/includes/file.php b/src/wp-admin/includes/file.php
    index 03f6e3c7b6..0e30dfdcd4 100644
    a b function wp_handle_sideload( &$file, $overrides = false, $time = null ) { 
    965965 * Please note that the calling function must unlink() the file.
    966966 *
    967967 * @since 2.5.0
     968 * @since 5.2.0 Signature Verification with SoftFail was added.
    968969 *
    969  * @param string $url  The URL of the file to download.
    970  * @param int $timeout The timeout for the request to download the file. Default 300 seconds.
     970 * @param string $url                The URL of the file to download.
     971 * @param int    $timeout            The timeout for the request to download the file. Default 300 seconds.
     972 * @param bool   $signature_softfail Whether to allow Signature Verification to softfail. Default true.
    971973 * @return string|WP_Error Filename on success, WP_Error on failure.
    972974 */
    973 function download_url( $url, $timeout = 300 ) {
     975function download_url( $url, $timeout = 300, $signature_softfail = true ) {
    974976        //WARNING: The file is not automatically deleted, The script must unlink() the file.
    975977        if ( ! $url ) {
    976978                return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) );
    function download_url( $url, $timeout = 300 ) { 
    10341036                }
    10351037        }
    10361038
     1039        /**
     1040         * Filters the list of hosts which should have Signature Verification attempted on.
     1041         *
     1042         * @since 5.2.0
     1043         *
     1044         * @param array List of hostnames.
     1045         */
     1046        $signed_hostnames       = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) );
     1047        $signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true );
     1048
     1049        // Perform the valiation
     1050        if ( $signature_verification ) {
     1051                $signature = wp_remote_retrieve_header( $response, 'x-content-signature' );
     1052                if ( ! $signature ) {
     1053                        // Retrieve signatures from a file if the header wasn't included.
     1054                        // WordPress.org stores signatures at $package_url.sig
     1055                        $signature_request = wp_safe_remote_get( $url . '.sig' );
     1056                        if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) {
     1057                                $signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) );
     1058                        }
     1059                }
     1060
     1061                // Perform the checks.
     1062                $signature_verification = verify_file_signature( $tmpfname, $signature, basename( parse_url( $url, PHP_URL_PATH ) ) );
     1063        }
     1064
     1065        if ( is_wp_error( $signature_verification ) ) {
     1066                if (
     1067                        /**
     1068                         * Filters whether Signature Verification failures should be allowed to soft fail.
     1069                         *
     1070                         * WARNING: This may be removed from a future release.
     1071                         *
     1072                         * @since 5.2.0
     1073                         *
     1074                         * @param bool   $signature_softfail If a softfail is allowed.
     1075                         * @param string $url                The url being accessed.
     1076                         */
     1077                        apply_filters( 'wp_signature_softfail', $signature_softfail, $url )
     1078                ) {
     1079                        $signature_verification->add_data( $tmpfname, 'softfail-filename' );
     1080                } else {
     1081                        // Hard-fail.
     1082                        unlink( $tmpfname );
     1083                }
     1084
     1085                return $signature_verification;
     1086        }
     1087
    10371088        return $tmpfname;
    10381089}
    10391090
    function verify_file_md5( $filename, $expected_md5 ) { 
    10661117        return new WP_Error( 'md5_mismatch', sprintf( __( 'The checksum of the file (%1$s) does not match the expected checksum value (%2$s).' ), bin2hex( $file_md5 ), bin2hex( $expected_raw_md5 ) ) );
    10671118}
    10681119
     1120/**
     1121 * Verifies the contents of a file against its ED25519 signature.
     1122 *
     1123 * @since 5.2.0
     1124 *
     1125 * @param string       $filename            The file to validate.
     1126 * @param string|array $signatures          A Signature provided for the file.
     1127 * @param string       $filename_for_errors A friendly filename for errors. Optional.
     1128 *
     1129 * @return bool|WP_Error true on success, false if verificaiton not attempted, or WP_Error describing an error condition.
     1130 */
     1131function verify_file_signature( $filename, $signatures, $filename_for_errors = false ) {
     1132        if ( ! $filename_for_errors ) {
     1133                $filename_for_errors = wp_basename( $filename );
     1134        }
     1135
     1136        // Check we can process signatures.
     1137        if ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) || ! in_array( 'sha384', array_map( 'strtolower', hash_algos() ) ) ) {
     1138                return new WP_Error(
     1139                        'signature_verification_unsupported',
     1140                        sprintf(
     1141                                /* translators: 1: The filename of the package. */
     1142                                __( 'The authenticity of %1$s could not be verified as signature verification is unavailable on this system.' ),
     1143                                '<span class="code">' . esc_html( $filename_for_errors ) . '</span>'
     1144                        ),
     1145                        ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' )
     1146                );
     1147        }
     1148
     1149        if ( ! $signatures ) {
     1150                return new WP_Error(
     1151                        'signature_verification_no_signature',
     1152                        sprintf(
     1153                                /* translators: 1: The filename of the package. */
     1154                                __( 'The authenticity of %1$s could not be verified as no signature was found.' ),
     1155                                '<span class="code">' . esc_html( $filename_for_errors ) . '</span>'
     1156                        )
     1157                );
     1158        }
     1159
     1160        $trusted_keys = wp_trusted_keys();
     1161        $file_hash    = hash_file( 'sha384', $filename, true );
     1162
     1163        mbstring_binary_safe_encoding();
     1164
     1165        foreach ( (array) $signatures as $signature ) {
     1166                $signature_raw = base64_decode( $signature );
     1167
     1168                // Ensure only valid-length signatures are considered.
     1169                if ( SODIUM_CRYPTO_SIGN_BYTES !== strlen( $signature_raw ) ) {
     1170                        continue;
     1171                }
     1172
     1173                foreach ( (array) $trusted_keys as $key ) {
     1174                        $key_raw = base64_decode( $key );
     1175
     1176                        // Only pass valid public keys through.
     1177                        if ( SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES !== strlen( $key_raw ) ) {
     1178                                continue;
     1179                        }
     1180
     1181                        if ( sodium_crypto_sign_verify_detached( $signature_raw, $file_hash, $key_raw ) ) {
     1182                                reset_mbstring_encoding();
     1183                                return true;
     1184                        }
     1185                }
     1186        }
     1187
     1188        reset_mbstring_encoding();
     1189
     1190        return new WP_Error(
     1191                'signature_verification_failed',
     1192                sprintf(
     1193                        /* translators: 1: The filename of the package. */
     1194                        __( 'The authenticity of %1$s could not be verified.' ),
     1195                        '<span class="code">' . esc_html( $filename_for_errors ) . '</span>'
     1196                ),
     1197                // Error data helpful for debugging:
     1198                array(
     1199                        'filename'   => $filename_for_errors,
     1200                        'keys'       => $trusted_keys,
     1201                        'signatures' => $signatures,
     1202                        'hash'       => bin2hex( $file_hash ),
     1203                )
     1204        );
     1205}
     1206
     1207/**
     1208 * Retrieve the list of signing keys trusted by WordPress.
     1209 *
     1210 * @since 5.2.0
     1211 *
     1212 * @return array List of hex-encoded Signing keys.
     1213 */
     1214function wp_trusted_keys() {
     1215        $trusted_keys = array(
     1216                // TODO: Fill with WordPress.org keys x2.
     1217                'fRPyrxb/MvVLbdsYi+OOEv4xc+Eqpsj+kkAS6gNOkI0=', // WordPress.org Test Key #1
     1218        );
     1219
     1220        /**
     1221         * Filter the valid Signing keys used to verify the contents of files.
     1222         *
     1223         * @since 5.2.0
     1224         *
     1225         * @param array $trusted_keys The trusted keys that may sign packages.
     1226         */
     1227        return apply_filters( 'wp_trusted_keys', $trusted_keys );
     1228}
     1229
    10691230/**
    10701231 * Unzips a specified ZIP file to a location on the filesystem via the WordPress
    10711232 * Filesystem Abstraction.