From: Tim Otten Date: Thu, 26 Mar 2015 23:50:41 +0000 (-0700) Subject: CRM-16173 - CIVICRM_CXN_VERIFY => CIVICRM_CXN_CA, CIVICRM_CXN_VERIFY_APPMETA X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=9ae2d27b9565e867291135f075d95ddb49bf620c;p=civicrm-core.git CRM-16173 - CIVICRM_CXN_VERIFY => CIVICRM_CXN_CA, CIVICRM_CXN_VERIFY_APPMETA The CA may take one of three values - CiviRootCA, CiviTestRootCA, or none. In the future, this may help with staging and private-beta apps. --- diff --git a/CRM/Cxn/BAO/Cxn.php b/CRM/Cxn/BAO/Cxn.php index 4f6fa2f06e..434ffa0561 100644 --- a/CRM/Cxn/BAO/Cxn.php +++ b/CRM/Cxn/BAO/Cxn.php @@ -26,6 +26,8 @@ +--------------------------------------------------------------------+ */ +use Civi\Cxn\Rpc\Constants; + /** * * @package CRM @@ -64,4 +66,71 @@ class CRM_Cxn_BAO_Cxn extends CRM_Cxn_DAO_Cxn { return $appMeta; } + /** + * Parse the CIVICRM_CXN_CA constant. It may have the following + * values: + * - 'CiviRootCA'|undefined -- Use the production civicrm.org root CA + * - 'CiviTestRootCA' -- Use the test civicrm.org root CA + * - 'none' -- Do not perform any certificate verification. + * + * This constant is emphatically *not* exposed through Civi's "Settings" + * system (or any other runtime-editable datastore). Manipulating + * this setting can expose the system to man-in-the-middle attacks, + * and allowing runtime manipulation would create a new vector + * for escalating privileges. This setting must only be manipulated + * by developers and sysadmins who already have full privileges + * to edit the source. + * + * @return string|NULL + * The PEM-encoded root certificate. NULL if verification is disabled. + * @throws CRM_Core_Exception + */ + public static function createCACert() { + if (!defined('CIVICRM_CXN_CA') || CIVICRM_CXN_CA === 'CiviRootCA') { + $file = Constants::getCert(); + } + elseif (CIVICRM_CXN_CA === 'CiviTestRootCA') { + $file = Constants::getTestCert(); + } + elseif (CIVICRM_CXN_CA === 'none') { + return NULL; + } + else { + throw new \CRM_Core_Exception("CIVICRM_CXN_CA is invalid."); + } + + $content = file_get_contents($file); + if (empty($content)) { + // Fail hard. Returning an empty value is not acceptable. + throw new \CRM_Core_Exception("Error loading CA certificate: $file"); + } + return $content; + } + + /** + * Determine if this site's security policy allows connecting + * to apps based on untrusted metadata. + * + * @return bool + * TRUE if application metadata must be verified. + */ + public static function isAppMetaVerified() { + if (defined('CIVICRM_CXN_VERIFY_APPMETA')) { + return CIVICRM_CXN_VERIFY_APPMETA; + } + elseif (!defined('CIVICRM_CXN_CA')) { + return TRUE; + } + else { + return !in_array(CIVICRM_CXN_CA, array('CiviTestRootCA', 'none')); + } + } + + public static function createRegistrationClient() { + $cxnStore = new \CRM_Cxn_CiviCxnStore(); + $client = new \Civi\Cxn\Rpc\RegistrationClient(self::createCACert(), $cxnStore, \CRM_Cxn_BAO_Cxn::getSiteCallbackUrl()); + $client->setLog(new \CRM_Utils_SystemLogger()); + return $client; + } + } diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index 93d5bdce0e..627f1a18c9 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -101,7 +101,7 @@ class Container { '\Civi\Cxn\Rpc\RegistrationClient', array() )) - ->setFactoryService(self::SELF)->setFactoryMethod('createRegistrationClient'); + ->setFactoryClass('CRM_Cxn_BAO_Cxn')->setFactoryMethod('createRegistrationClient'); // Expose legacy singletons as services in the container. $singletons = array( @@ -140,9 +140,9 @@ class Container { $dispatcher->addListener('DAO::post-update', array('\CRM_Core_BAO_RecurringEntity', 'triggerUpdate')); $dispatcher->addListener('DAO::post-delete', array('\CRM_Core_BAO_RecurringEntity', 'triggerDelete')); $dispatcher->addListener('hook_civicrm_unhandled_exception', array( - 'CRM_Core_LegacyErrorHandler', - 'handleException', - )); + 'CRM_Core_LegacyErrorHandler', + 'handleException', + )); return $dispatcher; } @@ -218,10 +218,4 @@ class Container { return $kernel; } - public function createRegistrationClient() { - $cxnStore = new \CRM_Cxn_CiviCxnStore(); - $client = new \Civi\Cxn\Rpc\RegistrationClient(NULL, $cxnStore, \CRM_Cxn_BAO_Cxn::getSiteCallbackUrl()); - $client->setLog(new \CRM_Utils_SystemLogger()); - return $client; - } } diff --git a/api/v3/Cxn.php b/api/v3/Cxn.php index f70f1f35c1..29c53b3c95 100644 --- a/api/v3/Cxn.php +++ b/api/v3/Cxn.php @@ -33,7 +33,7 @@ */ function civicrm_api3_cxn_register($params) { if (empty($params['appMeta']) && !empty($params['appMetaUrl'])) { - if (defined('CIVICRM_CXN_VERIFY') && !CIVICRM_CXN_VERIFY) { + if (!CRM_Cxn_BAO_Cxn::isAppMetaVerified()) { list ($status, $json) = CRM_Utils_HttpClient::singleton()->get($params['appMetaUrl']); if (CRM_Utils_HttpClient::STATUS_OK != $status) { throw new API_Exception("Failed to download appMeta."); @@ -43,8 +43,8 @@ function civicrm_api3_cxn_register($params) { else { // Note: The metadata includes a cert, but the details aren't signed. // This is very useful in testing/development. In ordinary usage, we - // rely on a general signature for the full batch of all metadata. - throw new API_Exception('This site is configured to only use verified metadata.'); + // rely on civicrm.org to sign the metadata for all apps en masse. + throw new API_Exception('This site is configured to only connect to applications with verified metadata.'); } }