Changeset 44954
- Timestamp:
- 03/21/2019 05:48:46 AM (6 years ago)
- Location:
- trunk/src/wp-admin/includes
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/includes/class-wp-upgrader.php
r42787 r44954 276 276 $this->skin->feedback( 'downloading_package', $package ); 277 277 278 $download_file = download_url( $package );279 280 if ( is_wp_error( $download_file ) ) {278 $download_file = download_url( $package, 300, true ); 279 280 if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) { 281 281 return new WP_Error( 'download_failed', $this->strings['download_failed'], $download_file->get_error_message() ); 282 282 } … … 732 732 */ 733 733 $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 734 753 if ( is_wp_error( $download ) ) { 735 754 $this->skin->error( $download ); -
trunk/src/wp-admin/includes/file.php
r44824 r44954 966 966 * 967 967 * @since 2.5.0 968 * 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. 968 * @since 5.2.0 Signature Verification with SoftFail was added. 969 * 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. 971 973 * @return string|WP_Error Filename on success, WP_Error on failure. 972 974 */ 973 function download_url( $url, $timeout = 300 ) {975 function download_url( $url, $timeout = 300, $signature_softfail = true ) { 974 976 //WARNING: The file is not automatically deleted, The script must unlink() the file. 975 977 if ( ! $url ) { … … 1035 1037 } 1036 1038 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 1037 1088 return $tmpfname; 1038 1089 } … … 1065 1116 1066 1117 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 ) ) ); 1118 } 1119 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 */ 1131 function 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 */ 1214 function wp_trusted_keys() { 1215 $trusted_keys = array(); 1216 1217 if ( time() < 1617235200 ) { 1218 // WordPress.org Key #1 - This key is only valid before April 1st, 2021. 1219 $trusted_keys[] = 'fRPyrxb/MvVLbdsYi+OOEv4xc+Eqpsj+kkAS6gNOkI0='; 1220 } 1221 1222 // TODO: Add key #2 with longer expiration. 1223 1224 /** 1225 * Filter the valid Signing keys used to verify the contents of files. 1226 * 1227 * @since 5.2.0 1228 * 1229 * @param array $trusted_keys The trusted keys that may sign packages. 1230 */ 1231 return apply_filters( 'wp_trusted_keys', $trusted_keys ); 1067 1232 } 1068 1233
Note: See TracChangeset
for help on using the changeset viewer.