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


Built from https://develop.svn.wordpress.org/trunk@45262


git-svn-id: http://core.svn.wordpress.org/trunk@45071 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
tellyworth 2019-04-24 07:44:51 +00:00
parent df438d90a0
commit ed802428e3
4 changed files with 91 additions and 34 deletions

View File

@ -121,7 +121,7 @@ class Core_Upgrader extends WP_Upgrader {
return new WP_Error( 'locked', $this->strings['locked'] );
}
$download = $this->download_package( $current->packages->$to_download );
$download = $this->download_package( $current->packages->$to_download, true );
// Allow for signature soft-fail.
// WARNING: This may be removed in the future.

View File

@ -244,11 +244,12 @@ class WP_Upgrader {
*
* @since 2.8.0
*
* @param string $package The URI of the package. If this is the full path to an
* existing local file, it will be returned untouched.
* @param string $package The URI of the package. If this is the full path to an
* existing local file, it will be returned untouched.
* @param bool $check_signatures Whether to validate file signatures. Default false.
* @return string|WP_Error The full path to the downloaded package file, or a WP_Error object.
*/
public function download_package( $package ) {
public function download_package( $package, $check_signatures = false ) {
/**
* Filters whether to return the package.
@ -275,7 +276,7 @@ class WP_Upgrader {
$this->skin->feedback( 'downloading_package', $package );
$download_file = download_url( $package, 300, true );
$download_file = download_url( $package, 300, $check_signatures );
if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) {
return new WP_Error( 'download_failed', $this->strings['download_failed'], $download_file->get_error_message() );
@ -730,21 +731,25 @@ class WP_Upgrader {
* Download the package (Note, This just returns the filename
* of the file if the package is a local file)
*/
$download = $this->download_package( $options['package'] );
$download = $this->download_package( $options['package'], true );
// Allow for signature soft-fail.
// WARNING: This may be removed in the future.
if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
// Outout the failure error as a normal feedback, and not as an error:
$this->skin->feedback( $download->get_error_message() );
// Report this failure back to WordPress.org for debugging purposes.
wp_version_check(
array(
'signature_failure_code' => $download->get_error_code(),
'signature_failure_data' => $download->get_error_data(),
)
);
// Don't output the 'no signature could be found' failure message for now.
if ( 'signature_verification_no_signature' != $download->get_error_code() || WP_DEBUG ) {
// Outout the failure error as a normal feedback, and not as an error:
$this->skin->feedback( $download->get_error_message() );
// Report this failure back to WordPress.org for debugging purposes.
wp_version_check(
array(
'signature_failure_code' => $download->get_error_code(),
'signature_failure_data' => $download->get_error_data(),
)
);
}
// Pretend this error didn't happen.
$download = $download->get_error_data( 'softfail-filename' );

View File

@ -968,12 +968,12 @@ function wp_handle_sideload( &$file, $overrides = false, $time = null ) {
* @since 2.5.0
* @since 5.2.0 Signature Verification with SoftFail was added.
*
* @param string $url The URL of the file to download.
* @param int $timeout The timeout for the request to download the file. Default 300 seconds.
* @param bool $signature_softfail Whether to allow Signature Verification to softfail. Default true.
* @param string $url The URL of the file to download.
* @param int $timeout The timeout for the request to download the file. Default 300 seconds.
* @param bool $signature_verification Whether to perform Signature Verification. Default false.
* @return string|WP_Error Filename on success, WP_Error on failure.
*/
function download_url( $url, $timeout = 300, $signature_softfail = true ) {
function download_url( $url, $timeout = 300, $signature_verification = false ) {
//WARNING: The file is not automatically deleted, The script must unlink() the file.
if ( ! $url ) {
return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) );
@ -1037,25 +1037,53 @@ function download_url( $url, $timeout = 300, $signature_softfail = true ) {
}
}
/**
* Filters the list of hosts which should have Signature Verification attempted on.
*
* @since 5.2.0
*
* @param array List of hostnames.
*/
$signed_hostnames = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) );
$signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true );
// If the caller expects signature verification to occur, check to see if this URL supports it.
if ( $signature_verification ) {
/**
* Filters the list of hosts which should have Signature Verification attempteds on.
*
* @since 5.2.0
*
* @param array List of hostnames.
*/
$signed_hostnames = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) );
$signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true );
}
// Perform the valiation
// Perform signature valiation if supported.
if ( $signature_verification ) {
$signature = wp_remote_retrieve_header( $response, 'x-content-signature' );
if ( ! $signature ) {
// Retrieve signatures from a file if the header wasn't included.
// WordPress.org stores signatures at $package_url.sig
$signature_request = wp_safe_remote_get( $url . '.sig' );
if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) {
$signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) );
$signature_url = false;
$url_path = parse_url( $url, PHP_URL_PATH );
if ( substr( $url_path, -4 ) == '.zip' || substr( $url_path, -7 ) == '.tar.gz' ) {
$signature_url = str_replace( $url_path, $url_path . '.sig', $url );
}
/**
* Filter the URL where the signature for a file is located.
*
* @since 5.2
*
* @param false|string $signature_url The URL where signatures can be found for a file, or false if none are known.
* @param string $url The URL being verified.
*/
$signature_url = apply_filters( 'wp_signature_url', $signature_url, $url );
if ( $signature_url ) {
$signature_request = wp_safe_remote_get(
$signature_url,
array(
'limit_response_size' => 10 * 1024, // 10KB should be large enough for quite a few signatures.
)
);
if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) {
$signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) );
}
}
}
@ -1075,7 +1103,7 @@ function download_url( $url, $timeout = 300, $signature_softfail = true ) {
* @param bool $signature_softfail If a softfail is allowed.
* @param string $url The url being accessed.
*/
apply_filters( 'wp_signature_softfail', $signature_softfail, $url )
apply_filters( 'wp_signature_softfail', true, $url )
) {
$signature_verification->add_data( $tmpfname, 'softfail-filename' );
} else {
@ -1147,6 +1175,30 @@ function verify_file_signature( $filename, $signatures, $filename_for_errors = f
);
}
// Check for a edge-case affecting PHP Maths abilities
if (
! extension_loaded( 'sodium' ) &&
in_array( PHP_VERSION_ID, [ 70200, 70201, 70202 ], true ) &&
extension_loaded( 'opcache' )
) {
// 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.
// https://bugs.php.net/bug.php?id=75938
return new WP_Error(
'signature_verification_unsupported',
sprintf(
/* translators: 1: The filename of the package. */
__( 'The authenticity of %1$s could not be verified as signature verification is unavailable on this system.' ),
'<span class="code">' . esc_html( $filename_for_errors ) . '</span>'
),
array(
'php' => phpversion(),
'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ),
)
);
}
if ( ! $signatures ) {
return new WP_Error(
'signature_verification_no_signature',

View File

@ -13,7 +13,7 @@
*
* @global string $wp_version
*/
$wp_version = '5.2-beta3-45261';
$wp_version = '5.2-beta3-45262';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.