iThemes Sync - Version 2.1.4

Version Description

  • Enhancement: Added support for public-key signed requests to the plugin
Download this release

Release Info

Developer layotte
Plugin Icon 128x128 iThemes Sync
Version 2.1.4
Comparing to
See all releases

Code changes from version 2.1.3.1 to 2.1.4

Files changed (7) hide show
  1. functions.php +51 -0
  2. history.txt +2 -0
  3. init.php +1 -1
  4. lang/ithemes-sync.pot +2 -2
  5. public.key +1 -0
  6. readme.txt +4 -1
  7. request-handler.php +63 -24
functions.php CHANGED
@@ -1029,4 +1029,55 @@ class Ithemes_Sync_Functions {
1029
 
1030
  return false;
1031
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1032
  }
1029
 
1030
  return false;
1031
  }
1032
+
1033
+ /**
1034
+ * Checks if sodium library and methods we use are available
1035
+ * Also checks if sodium is fast enough on this system
1036
+ * If available: include the compatiability layer, core utilities, and Base64 UrlSafe classes
1037
+ *
1038
+ * @return bool
1039
+ */
1040
+ public static function is_sodium_available() {
1041
+ $requiredFiles = array(
1042
+ 'wp-includes/sodium_compat/src/Compat.php',
1043
+ 'wp-includes/sodium_compat/src/Core/Base64/UrlSafe.php',
1044
+ 'wp-includes/sodium_compat/src/Core/Util.php'
1045
+ );
1046
+
1047
+ foreach ( $requiredFiles as $file ) {
1048
+ if ( file_exists( ABSPATH . $file ) ) {
1049
+ require_once( ABSPATH . $file );
1050
+ } else {
1051
+ return false;
1052
+ }
1053
+ }
1054
+
1055
+ // Check for a edge-case affecting PHP Maths abilities
1056
+ // 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.
1057
+ if (
1058
+ ! extension_loaded( 'sodium' ) &&
1059
+ in_array( PHP_VERSION_ID, [ 70200, 70201, 70202 ], true ) &&
1060
+ extension_loaded( 'opcache' )
1061
+ ) {
1062
+ return false;
1063
+ }
1064
+
1065
+ // Verify runtime speed of Sodium_Compat is acceptable.
1066
+ if ( ! extension_loaded( 'sodium' ) && ! ParagonIE_Sodium_Compat::polyfill_is_fast() ) {
1067
+
1068
+ // Allow for an old version of Sodium_Compat being loaded before the bundled WordPress one.
1069
+ if ( method_exists( 'ParagonIE_Sodium_Compat', 'runtime_speed_test' ) ) {
1070
+ // Run `ParagonIE_Sodium_Compat::runtime_speed_test()` in optimized integer mode, as that's what is used for signing verifications.
1071
+ $old_fastMult = ParagonIE_Sodium_Compat::$fastMult;
1072
+ ParagonIE_Sodium_Compat::$fastMult = true;
1073
+ $sodium_compat_is_fast = ParagonIE_Sodium_Compat::runtime_speed_test( 100, 10 );
1074
+ ParagonIE_Sodium_Compat::$fastMult = $old_fastMult;
1075
+
1076
+ return $sodium_compat_is_fast;
1077
+ }
1078
+
1079
+ }
1080
+
1081
+ return true;
1082
+ }
1083
  }
history.txt CHANGED
@@ -228,3 +228,5 @@
228
  Bug Fix: Add nonce to authentication request
229
  2.1.3.1 - 2020-02-18 - Lew Ayotte
230
  Forgot to update the stable tag from last update
 
 
228
  Bug Fix: Add nonce to authentication request
229
  2.1.3.1 - 2020-02-18 - Lew Ayotte
230
  Forgot to update the stable tag from last update
231
+ 2.1.4 - 2020-03-16 - Josh Oakes
232
+ Enhancement: Added support for public-key signed requests to the plugin
init.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: iThemes Sync
4
  Plugin URI: http://ithemes.com/sync
5
  Description: Manage updates to your WordPress sites easily in one place.
6
  Author: iThemes
7
- Version: 2.1.3.1
8
  Author URI: http://ithemes.com/
9
  Domain Path: /lang/
10
  iThemes Package: ithemes-sync
4
  Plugin URI: http://ithemes.com/sync
5
  Description: Manage updates to your WordPress sites easily in one place.
6
  Author: iThemes
7
+ Version: 2.1.4
8
  Author URI: http://ithemes.com/
9
  Domain Path: /lang/
10
  iThemes Package: ithemes-sync
lang/ithemes-sync.pot CHANGED
@@ -2,9 +2,9 @@
2
  # This file is distributed under the same license as the iThemes Sync package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: iThemes Sync 2.1.3.1\n"
6
  "Report-Msgid-Bugs-To: http://ithemes.com/support/\n"
7
- "POT-Creation-Date: 2020-02-18 15:41:10+00:00\n"
8
  "PO-Revision-Date: 2020-MO-DA HO:MI+ZONE\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
2
  # This file is distributed under the same license as the iThemes Sync package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: iThemes Sync 2.1.4\n"
6
  "Report-Msgid-Bugs-To: http://ithemes.com/support/\n"
7
+ "POT-Creation-Date: 2020-03-17 14:06:40+00:00\n"
8
  "PO-Revision-Date: 2020-MO-DA HO:MI+ZONE\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
public.key ADDED
@@ -0,0 +1 @@
 
1
+ HW1bf2OWXmTKogDwNk76mLng2ULL8v7lUXpDLdqUMWE=
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: manage multiple Sites, backup, security, migrate, SEO, manage updates, adm
4
  Requires at least: 4.5
5
  Requires PHP: 5.6
6
  Tested up to: 5.4
7
- Stable tag: 2.1.3.1
8
  License: GPLv3 or later
9
  License URI: http://www.gnu.org/licenses/quick-guide-gplv3.html
10
 
@@ -87,6 +87,9 @@ Make steady, reliable income for WordPress maintenance with iThemes Sync Pro’s
87
 
88
  == Changelog ==
89
 
 
 
 
90
  = 2.1.3.1 =
91
  * Updating stable tag
92
 
4
  Requires at least: 4.5
5
  Requires PHP: 5.6
6
  Tested up to: 5.4
7
+ Stable tag: 2.1.4
8
  License: GPLv3 or later
9
  License URI: http://www.gnu.org/licenses/quick-guide-gplv3.html
10
 
87
 
88
  == Changelog ==
89
 
90
+ = 2.1.4 =
91
+ * Enhancement: Added support for public-key signed requests to the plugin
92
+
93
  = 2.1.3.1 =
94
  * Updating stable tag
95
 
request-handler.php CHANGED
@@ -35,6 +35,7 @@ Version History
35
 
36
 
37
  require_once( $GLOBALS['ithemes_sync_path'] . '/load-translations.php' );
 
38
 
39
  class Ithemes_Sync_Request_Handler {
40
  private $logs = array();
@@ -45,14 +46,40 @@ class Ithemes_Sync_Request_Handler {
45
 
46
  public function __construct() {
47
  $this->show_errors();
48
-
49
-
50
  if ( empty( $_POST['request'] ) ) {
51
  return;
52
  }
53
-
54
- $request = $_POST['request'];
55
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
57
  $request = stripslashes( $request );
58
  }
@@ -62,20 +89,9 @@ class Ithemes_Sync_Request_Handler {
62
  if ( ! is_array( $request ) ) {
63
  return;
64
  }
65
-
66
-
67
  $GLOBALS['ithemes_sync_request_handler'] = $this;
68
-
69
-
70
- add_action( 'ithemes-sync-add-log', array( $this, 'add_log' ), 10, 2 );
71
- add_action( 'shutdown', array( $this, 'handle_error' ) );
72
-
73
- add_action( 'ithemes_sync_verbs_registered', array( $this, 'handle_request' ) );
74
-
75
- require_once( $GLOBALS['ithemes_sync_path'] . '/api.php' );
76
- require_once( $GLOBALS['ithemes_sync_path'] . '/functions.php' );
77
- require_once( $GLOBALS['ithemes_sync_path'] . '/settings.php' );
78
-
79
  $this->options = $GLOBALS['ithemes-sync-settings']->get_options();
80
 
81
  $this->parse_request( $request );
@@ -277,15 +293,15 @@ class Ithemes_Sync_Request_Handler {
277
 
278
  public function send_response( $data ) {
279
  if ( is_wp_error( $data ) ) {
280
- foreach ( $data->get_error_codes() as $code )
281
- $response['errors'][$code] = $data->get_error_message( $code );
282
- }
283
- else {
284
  $response = array(
285
  'response' => $data,
286
  );
287
  }
288
-
289
  if ( ! empty( $this->logs ) ) {
290
  $response['logs'] = $this->logs;
291
  }
@@ -436,7 +452,7 @@ class Ithemes_Sync_Request_Handler {
436
 
437
  $this->logs[] = $log;
438
  }
439
-
440
  public function handle_error() {
441
  $this->send_response( new WP_Error( 'unhandled_request', 'This request was not handled by any registered verb. This was likely caused by a fatal error.' ) );
442
  }
@@ -463,6 +479,29 @@ class Ithemes_Sync_Request_Handler {
463
 
464
  return $json;
465
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  }
467
 
468
  new Ithemes_Sync_Request_Handler();
35
 
36
 
37
  require_once( $GLOBALS['ithemes_sync_path'] . '/load-translations.php' );
38
+ require_once( $GLOBALS['ithemes_sync_path'] . '/functions.php' );
39
 
40
  class Ithemes_Sync_Request_Handler {
41
  private $logs = array();
46
 
47
  public function __construct() {
48
  $this->show_errors();
49
+
 
50
  if ( empty( $_POST['request'] ) ) {
51
  return;
52
  }
53
+
54
+ require_once( $GLOBALS['ithemes_sync_path'] . '/api.php' );
55
+ require_once( $GLOBALS['ithemes_sync_path'] . '/functions.php' );
56
+ require_once( $GLOBALS['ithemes_sync_path'] . '/settings.php' );
57
+
58
+ add_action( 'ithemes-sync-add-log', array( $this, 'add_log' ), 10, 2 );
59
+ add_action( 'shutdown', array( $this, 'handle_error' ) );
60
+ add_action( 'ithemes_sync_verbs_registered', array( $this, 'handle_request' ) );
61
+
62
+ $request = $_POST['request'];
63
+
64
+ if ( !empty( $_POST['signature'] ) ) {
65
+
66
+ // Append success and failures to response
67
+ $sodium_available = Ithemes_Sync_Functions::is_sodium_available();
68
+
69
+ if ( $sodium_available && ! $this->verify_request_signature( $request, $_POST['signature'] ) ) {
70
+ // Sodium is available and verification failed
71
+ do_action( 'ithemes-sync-add-log', 'signature-verification', array( 'available' => true, 'verified' => false ) );
72
+
73
+ // $this->send_response( new WP_Error( 'request-signature-invalid', 'The request signature could not be verified' ) );
74
+ } elseif ( $sodium_available ) {
75
+ // Sodium available and signature was verified
76
+ do_action( 'ithemes-sync-add-log', 'signature-verification', array( 'available' => true, 'verified' => true ) );
77
+ } else {
78
+ // Sodium is not available
79
+ do_action( 'ithemes-sync-add-log', 'signature-verification', array( 'available' => false, 'verified' => false ) );
80
+ }
81
+ }
82
+
83
  if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
84
  $request = stripslashes( $request );
85
  }
89
  if ( ! is_array( $request ) ) {
90
  return;
91
  }
92
+
 
93
  $GLOBALS['ithemes_sync_request_handler'] = $this;
94
+
 
 
 
 
 
 
 
 
 
 
95
  $this->options = $GLOBALS['ithemes-sync-settings']->get_options();
96
 
97
  $this->parse_request( $request );
293
 
294
  public function send_response( $data ) {
295
  if ( is_wp_error( $data ) ) {
296
+ foreach ( $data->get_error_codes() as $code ) {
297
+ $response['errors'][ $code ] = $data->get_error_message( $code );
298
+ }
299
+ } else {
300
  $response = array(
301
  'response' => $data,
302
  );
303
  }
304
+
305
  if ( ! empty( $this->logs ) ) {
306
  $response['logs'] = $this->logs;
307
  }
452
 
453
  $this->logs[] = $log;
454
  }
455
+
456
  public function handle_error() {
457
  $this->send_response( new WP_Error( 'unhandled_request', 'This request was not handled by any registered verb. This was likely caused by a fatal error.' ) );
458
  }
479
 
480
  return $json;
481
  }
482
+
483
+ /**
484
+ * Determine if signature supplied in the request can be verified using the public key
485
+ *
486
+ * @param $request
487
+ * @param $signature
488
+ *
489
+ * @return bool
490
+ */
491
+ private function verify_request_signature( $request, $signature ) {
492
+
493
+ try {
494
+
495
+ $public_key = sodium_base642bin( file_get_contents( $GLOBALS['ithemes_sync_path'] . '/public.key' ), 5 );
496
+ $signature = sodium_base642bin( $signature, 5 );
497
+
498
+ } catch ( Exception $e ) {
499
+ return false;
500
+ }
501
+
502
+ return sodium_crypto_sign_verify_detached( $signature, $request, $public_key );
503
+ }
504
+
505
  }
506
 
507
  new Ithemes_Sync_Request_Handler();