Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev/core#2258 - Read+write SMTP password using 'crypto.token' #19239

Merged
merged 5 commits into from
Jan 8, 2021

Conversation

totten
Copy link
Member

@totten totten commented Dec 19, 2020

Overview

This converts the smtpPassword subfield from CRM_Utils_Crypt to the crypto.token service, which means:

  • It supports more robust key-management.
  • When storing smtpPassword in SQL, the on-disk format is less ambiguous.
  • When storing smtpPassword in civicrm.settings.php, the on-disk format is less ambiguous and more forgiving.

See also: https://lab.civicrm.org/dev/core/-/issues/2258

(NOTE: This depends on #19236 an #19251.)

(NOTE: A few edits added to the description after-the-fact. It originally spoke of the old plaintext as, well, plaintext. It was actually Base64-encoded plaintext.)

Before

The format of the smtpPassword subfield depends on the available PHP/PECL extensions:

  • The field is Base64 plaintext... if and only if PHP has mcrypt disabled
  • The field is encrypted... if and only if PHP has mcrypt enabled

Additionally, note that:

  • There is no way to discern (based on the data) if a value like YWJjZGVm is random password or ciphertext. It remains legible only as long as both the PHP configuration and SITE_KEY stay the same.
  • There is no sensible way to rotate the encryption key.

After

The format of the smtpPassword subfield is specified by crypto.token which means:

  • The field is normal plaintext... if and only if the content begins with a printable character (not chr(2)).
  • The field is encrypted... if and only if the content begins with chr(2) (in which case the string looks like ^CTK?k=<keyid>&t=<ciphertext>)

Additionally, note that:

Technical Details: Review of upgrade scenarios

There are three variables which determine different upgrade scenarios:

  • Storage: Is the password stored in the DB (civicrm_settings) or in PHP (civicrm.settings.php)?
  • Old Crypto: Does the environment have mcrypt (ie the old crypto system) enabled?
  • New Crypto: Has the administrator pre-configured CIVICRM_CRED_KEYS (ie the new crypto system)?

Here are how the scenarios work out:

# Storage? Old crypto? New crypto? Comments
1 DB (civicrm_settings) No No smtpPassword starts as Base64 plain-text and ends as normal plain-text.
2 DB (civicrm_settings) No Yes smtpPassword converts from plain-text to new-encryption.
3 DB (civicrm_settings) Yes No smtpPassword converts from old-encryption to plain-text.
4 DB (civicrm_settings) Yes Yes smtpPassword converts from old-encryption to new-encryption.
5 PHP (civicrm.settings.php) No * smtpPassword was stored in PHP as plain-text, and the same plain-text will work post-upgrade. ⚠️ smtpPassword was stored in PHP with the old Base64 plain-text, and the upgrader cannot change it. After upgrading, you must manually edit civicrm.settings.php to set smtpPassword as normal plain-text.
6 PHP (civicrm.settings.php) Yes * ⚠️ smtpPassword was stored in PHP with the old encrypted format, and the upgrader cannot change it. After upgrading, you must manually edit civicrm.settings.php to set smtpPassword using plain-text or the new encryption format.

Scenarios 1-4 all end with a working/readable variant of smtpPassword. After upgrade, one has the option to change the crypto by updating CIVICRM_CRED_KEYS and calling System.rotateKeys.

Scenarios 5-6 are annoying, but there's not much we can do besides show a warning. However, I don't expect this to be common because it has always been tricky to understand/setup. If you have managed to figure it out before, then you're probably clever enough to sort out the migration - as long as you see the warning provided by the upgrader. (There are more comments in ThirtyFour.php alongside the relevant warning.)

Technical Details: Sysadmin usability

This patch incidentally makes it easier to set the SMTP password via civicrm.settings.php / $civicrm_settings.

$civicrm_setting['domain']['mailing_backend'] =  array(
        'outBound_option' => '0',
        'sendmail_path' => '',
        'sendmail_args' => '',
        'smtpServer' => 'localhost',
        'smtpPort' => '1025',
        'smtpAuth' => '1',
        'smtpUsername' => '',
        'smtpPassword' => '********',
      );

Compare:

  • With CRM_Utils_Crypt, the format of ******** was determined by the PECL configuration, which is usually out-of-sight/out-of-mind. Consequently, I would wager that the behavior seems unreliable/confusing to administrators -- on one deployment/server, ******** is interpreted as plaintext, and it works intuitively. On another, the ******** is interpreted as ciphertext, and it's very difficult to set correctly.
  • With crypto.token, the PECL configuration is not decisive. The sysadmin can put the smtpPassword as plain-text ('smtpPassword' => 'mySecret'), and that will work on any server. If they prefer to enter the override as an encrypted value, that works too ('smtpPassword' => chr(2).'CTK?k=...&t=...' -- although I think the audience for this would be quite limited).

@civibot
Copy link

civibot bot commented Dec 19, 2020

(Standard links)

@civibot civibot bot added the master label Dec 19, 2020
@totten
Copy link
Member Author

totten commented Dec 19, 2020

jenkins, test this please

@totten totten changed the title (WIP) dev/core#2258 - Read+write SMTP password using 'crypto.token' dev/core#2258 - Read+write SMTP password using 'crypto.token' Dec 21, 2020
@totten
Copy link
Member Author

totten commented Dec 21, 2020

This was "WIP" because it needed a mechanism to re-enable support for encryption. That is now available in #19251, so I've rebased and updated both title+description.

Of course, the PR still depends on others. (Recommended sequence of reading/merging: #19236 => #19251 => #19239.)

@seamuslee001
Copy link
Contributor

@totten can you rebase this please

$settings = self::findSmtpPasswords();
foreach ($settings as $setting) {
$value = unserialize($setting['value']);
$value['smtpPassword'] = CRM_Utils_Crypt::decrypt($value['smtpPassword']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you don't want to call crypto service and encrypt using the new system here?

Copy link
Member Author

@totten totten Jan 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For posterity, we discussed further at https://chat.civicrm.org/civicrm/pl/r8pbskapc7gybngdceqxg9sksh

Basically, we're evaluating trade-off between:

  • SITE_KEY as fallback crypto key
    • How: Register SITE_KEY as low-priority crypto-key for use with CRED. The upgrader uses this for initial encryption of smtpPassword
    • Pro: The smtpPassword is promptly (re)encrypted. No interregnum.
    • Con: Creates more edge-cases / complexity
  • Temporary decryption
    • How: The upgrader decrypts smtpPassword. After upgrade, sysadmin defines CRED_KEYS and runs rotateKey
    • Pro: Long-term, system is simpler to understand / has fewer edge-cases (for admin/dev/etc)
    • Con: There's an interregnum (between the upgrade and encryption) during which smtpPassword is stored as plaintext.

@totten totten force-pushed the master-crypt-smtp branch from ddf1456 to 9b645a5 Compare January 8, 2021 06:15
@seamuslee001
Copy link
Contributor

Based on discussion with Tim I'm ok with this now, MOP

totten added 5 commits January 7, 2021 23:04
Before
------

The format of the 'smtpPassword' subfield depends on the available PECL extensions:

* The field is ciphertext... if PHP has `mcrypt` enabled
* The field is plaintext... if PHP has `mcrypt` disabled

After
-----

The format of the `smtpPassword` subfield is specified by `crypto.token` which means:

* The field is ciphertext... if it begins with `chr(2)`
* The field is plaintext... if it begins with any printable character
@totten totten force-pushed the master-crypt-smtp branch from 9b645a5 to aa7a657 Compare January 8, 2021 08:24
@totten
Copy link
Member Author

totten commented Jan 8, 2021

Merging per MOP

@totten totten merged commit ebbf2a6 into civicrm:master Jan 8, 2021
@totten totten deleted the master-crypt-smtp branch January 8, 2021 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants