autodetect ssl
authordemeritcowboy <demeritcowboy@hotmail.com>
Wed, 26 Aug 2020 21:26:54 +0000 (17:26 -0400)
committerdemeritcowboy <demeritcowboy@hotmail.com>
Thu, 27 Aug 2020 14:27:56 +0000 (10:27 -0400)
setup/plugins/init/Backdrop.civi-setup.php
setup/plugins/init/Drupal.civi-setup.php
setup/plugins/init/Drupal8.civi-setup.php
setup/plugins/installFiles/InstallSettingsFile.civi-setup.php
setup/src/Setup/DrupalUtil.php
templates/CRM/common/civicrm.settings.php.template
tests/phpunit/Civi/Setup/DrupalUtilTest.php [new file with mode: 0644]

index 57e867f90c366b5816a3a5bfc67dac8182b23109..2320b88dace623244d904118b1d3147093f953bc 100644 (file)
@@ -39,11 +39,13 @@ if (!defined('CIVI_SETUP')) {
 
     // Compute DSN.
     global $databases;
+    $ssl_params = \Civi\Setup\DrupalUtil::guessSslParams($databases['default']['default']);
     $model->db = $model->cmsDb = array(
       'server' => \Civi\Setup\DbUtil::encodeHostPort($databases['default']['default']['host'], $databases['default']['default']['port'] ?: NULL),
       'username' => $databases['default']['default']['username'],
       'password' => $databases['default']['default']['password'],
       'database' => $databases['default']['default']['database'],
+      'ssl_params' => empty($ssl_params) ? NULL : $ssl_params,
     );
 
     // Compute URLs
index b24d7c164cba6e70d5b6b7ca8bd2ebbcc2c5bd74..d57ff432c21cfa4f80be5129073b00d7e4a9dceb 100644 (file)
@@ -37,11 +37,13 @@ if (!defined('CIVI_SETUP')) {
 
     // Compute DSN.
     global $databases;
+    $ssl_params = \Civi\Setup\DrupalUtil::guessSslParams($databases['default']['default']);
     $model->db = $model->cmsDb = array(
       'server' => \Civi\Setup\DbUtil::encodeHostPort($databases['default']['default']['host'], $databases['default']['default']['port'] ?: NULL),
       'username' => $databases['default']['default']['username'],
       'password' => $databases['default']['default']['password'],
       'database' => $databases['default']['default']['database'],
+      'ssl_params' => empty($ssl_params) ? NULL : $ssl_params,
     );
 
     // Compute cmsBaseUrl.
index a693cba92c4cd4adca82e8b711560b623f865709..22034e90895400b5d3cb81cb4617ee90b35ede6c 100644 (file)
@@ -40,11 +40,13 @@ if (!defined('CIVI_SETUP')) {
 
     // Compute DSN.
     $connectionOptions = \Drupal::database()->getConnectionOptions();
+    $ssl_params = \Civi\Setup\DrupalUtil::guessSslParams($connectionOptions);
     $model->db = $model->cmsDb = array(
       'server' => \Civi\Setup\DbUtil::encodeHostPort($connectionOptions['host'], $connectionOptions['port'] ?: NULL),
       'username' => $connectionOptions['username'],
       'password' => $connectionOptions['password'],
       'database' => $connectionOptions['database'],
+      'ssl_params' => empty($ssl_params) ? NULL : $ssl_params,
     );
 
     // Compute cmsBaseUrl.
index 29aedb2b073b2e66a765f4f6116555b5738c7591..5422292f32a5f5e41bfd8d5e33929cd139025bff 100644 (file)
@@ -71,6 +71,13 @@ if (!defined('CIVI_SETUP')) {
     $params['CMSdbPass'] = addslashes($m->cmsDb['password']);
     $params['CMSdbHost'] = addslashes($m->cmsDb['server']);
     $params['CMSdbName'] = addslashes($m->cmsDb['database']);
+    // The '&' prefix is awkward, but we don't know what's already in the file.
+    // At the time of writing, it has ?new_link=true. If that is removed,
+    // then need to update this.
+    // The PHP_QUERY_RFC3986 is important because PEAR::DB will interpret plus
+    // signs as a reference to its old DSN format and mangle the DSN, so we
+    // need to use %20 for spaces.
+    $params['CMSdbSSL'] = empty($m->cmsDb['ssl_params']) ? '' : addslashes('&' . http_build_query($m->cmsDb['ssl_params'], '', '&', PHP_QUERY_RFC3986));
     $params['siteKey'] = addslashes($m->siteKey);
 
     $extraSettings = array();
index 0f5ab95205b02ca0fc6893cb2e4f6b116821a59b..8d1e95bc2e7208156cb950b6fa26c975454d670d 100644 (file)
@@ -71,4 +71,58 @@ class DrupalUtil {
      */
   }
 
+  /**
+   * Guess if the CMS is using SSL for MySQL and what the corresponding
+   * parameters should be for PEAR::DB.
+   *
+   * Not all combinations will work. See the install docs for a list of known
+   * configurations that do. We don't enforce that here since we're just
+   * trying to guess a default based on what they already have.
+   *
+   * @param array $cmsDatabaseParams
+   *   The contents of the section from drupal's settings.php where it defines
+   *   the $database array, usually under 'default'.
+   * @return array
+   *   The corresponding guessed params for PEAR::DB.
+   */
+  public static function guessSslParams(array $cmsDatabaseParams):array {
+    // If the pdo-mysql extension isn't loaded or they have nothing in drupal
+    // config for pdo, then we're done. PDO isn't required for Civi, but note
+    // the references to PDO constants below would fail and they obviously
+    // wouldn't have them in drupal config then.
+    if (empty($cmsDatabaseParams['pdo']) || !extension_loaded('pdo_mysql')) {
+      return [];
+    }
+
+    $pdo = $cmsDatabaseParams['pdo'];
+
+    $pdo_map = [
+      \PDO::MYSQL_ATTR_SSL_CA => 'ca',
+      \PDO::MYSQL_ATTR_SSL_KEY => 'key',
+      \PDO::MYSQL_ATTR_SSL_CERT => 'cert',
+      \PDO::MYSQL_ATTR_SSL_CAPATH => 'capath',
+      \PDO::MYSQL_ATTR_SSL_CIPHER => 'cipher',
+    ];
+
+    $ssl_params = [];
+
+    // If they have one set in drupal config and it's a string, then copy
+    // it over verbatim.
+    foreach ($pdo_map as $pdo_name => $ssl_name) {
+      if (!empty($pdo[$pdo_name]) && is_string($pdo[$pdo_name])) {
+        $ssl_params[$ssl_name] = $pdo[$pdo_name];
+      }
+    }
+
+    // No client certificate or server verification, but want SSL. Return our
+    // made-up indicator ssl=1 that isn't a real mysqli option but which we
+    // recognize. It's possible they have other params set too which we pass
+    // along from above, but that may not be compatible but it's up to them.
+    if (($pdo[\PDO::MYSQL_ATTR_SSL_CA] ?? NULL) === TRUE && ($pdo[\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] ?? NULL) === FALSE) {
+      $ssl_params['ssl'] = 1;
+    }
+
+    return $ssl_params;
+  }
+
 }
index 3d0575569fb8dbccc87ac797befb0fcc48ca85e1..d66a63d632ecb2b5f2c7605c5ca314e0e4ab5b89 100644 (file)
@@ -73,7 +73,7 @@ if (!defined('CIVICRM_UF')) {
  *      define( 'CIVICRM_UF_DSN', 'mysql://cms_db_username:cms_db_password@db_server/cms_database?new_link=true');
  */
 if (!defined('CIVICRM_UF_DSN') && CIVICRM_UF !== 'UnitTests') {
-  define( 'CIVICRM_UF_DSN'           , 'mysql://%%CMSdbUser%%:%%CMSdbPass%%@%%CMSdbHost%%/%%CMSdbName%%?new_link=true');
+  define( 'CIVICRM_UF_DSN'           , 'mysql://%%CMSdbUser%%:%%CMSdbPass%%@%%CMSdbHost%%/%%CMSdbName%%?new_link=true%%CMSdbSSL%%');
 }
 
 // %%extraSettings%%
diff --git a/tests/phpunit/Civi/Setup/DrupalUtilTest.php b/tests/phpunit/Civi/Setup/DrupalUtilTest.php
new file mode 100644 (file)
index 0000000..adf606b
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+namespace Civi\Setup;
+
+/**
+ * Class DrupalUtilTest
+ * @package Civi\Setup
+ * @group headless
+ */
+class DrupalUtilTest extends \CiviUnitTestCase {
+
+  /**
+   * Test guessSslParams
+   * @dataProvider pdoParamsProvider
+   * @param array $input
+   * @param array $expected
+   */
+  public function testGuessSslParams(array $input, array $expected) {
+    $this->assertSame($expected, \Civi\Setup\DrupalUtil::guessSslParams($input));
+  }
+
+  /**
+   * Data provider for testGuessSslParams
+   * @return array
+   */
+  public function pdoParamsProvider():array {
+    return [
+      'empty' => [[], []],
+      'empty2' => [['pdo' => []], []],
+      'no client certificate, no server verification' => [
+        [
+          'pdo' => [
+            \PDO::MYSQL_ATTR_SSL_CA => TRUE,
+            \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => FALSE,
+          ],
+        ],
+        ['ssl' => 1],
+      ],
+      'typical client certificate with server verification' => [
+        [
+          'pdo' => [
+            \PDO::MYSQL_ATTR_SSL_CA => '/tmp/cacert.crt',
+            \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key',
+            \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt',
+          ],
+        ],
+        [
+          'ca' => '/tmp/cacert.crt',
+          'key' => '/tmp/my.key',
+          'cert' => '/tmp/cert.crt',
+        ],
+      ],
+      'client certificate, no server verification' => [
+        [
+          'pdo' => [
+            \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => FALSE,
+            \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key',
+            \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt',
+          ],
+        ],
+        [
+          'key' => '/tmp/my.key',
+          'cert' => '/tmp/cert.crt',
+        ],
+      ],
+      'self-signed client certificate with server verification' => [
+        [
+          'pdo' => [
+            \PDO::MYSQL_ATTR_SSL_CA => '/tmp/cert.crt',
+            \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key',
+            \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt',
+          ],
+        ],
+        [
+          'ca' => '/tmp/cert.crt',
+          'key' => '/tmp/my.key',
+          'cert' => '/tmp/cert.crt',
+        ],
+      ],
+      'Not sure what would happen in practice but is all the string params' => [
+        [
+          'pdo' => [
+            \PDO::MYSQL_ATTR_SSL_CA => '/tmp/cacert.crt',
+            \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key',
+            \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt',
+            \PDO::MYSQL_ATTR_SSL_CAPATH => '/tmp/cacert_folder',
+            \PDO::MYSQL_ATTR_SSL_CIPHER => 'aes',
+          ],
+        ],
+        [
+          'ca' => '/tmp/cacert.crt',
+          'key' => '/tmp/my.key',
+          'cert' => '/tmp/cert.crt',
+          'capath' => '/tmp/cacert_folder',
+          'cipher' => 'aes',
+        ],
+      ],
+      'Ditto, but showing extra ones should get ignored' => [
+        [
+          'pdo' => [
+            \PDO::MYSQL_ATTR_SSL_CA => '/tmp/cacert.crt',
+            \PDO::MYSQL_ATTR_SSL_KEY => '/tmp/my.key',
+            \PDO::MYSQL_ATTR_SSL_CERT => '/tmp/cert.crt',
+            \PDO::MYSQL_ATTR_SSL_CAPATH => '/tmp/cacert_folder',
+            \PDO::MYSQL_ATTR_SSL_CIPHER => 'aes',
+            'fourteen' => 'the number fourteen',
+          ],
+        ],
+        [
+          'ca' => '/tmp/cacert.crt',
+          'key' => '/tmp/my.key',
+          'cert' => '/tmp/cert.crt',
+          'capath' => '/tmp/cacert_folder',
+          'cipher' => 'aes',
+        ],
+      ],
+      "some windows paths shouldn't get mangled" => [
+        [
+          'pdo' => [
+            \PDO::MYSQL_ATTR_SSL_CA => 'C:/Program Files/MariaDB 10.3/data/cacert.crt',
+            \PDO::MYSQL_ATTR_SSL_KEY => 'C:/Program Files/MariaDB 10.3/data/my.key',
+            \PDO::MYSQL_ATTR_SSL_CERT => 'C:\\Program Files\\MariaDB 10.3\\data\\cert.crt',
+          ],
+        ],
+        [
+          'ca' => 'C:/Program Files/MariaDB 10.3/data/cacert.crt',
+          'key' => 'C:/Program Files/MariaDB 10.3/data/my.key',
+          'cert' => 'C:\\Program Files\\MariaDB 10.3\\data\\cert.crt',
+        ],
+      ],
+    ];
+  }
+
+}