diff --git a/cron.py b/cron.py index 0232c342..49f3f19e 100644 --- a/cron.py +++ b/cron.py @@ -68,6 +68,7 @@ from app.models import ( PartnerUser, ApiToCookieToken, ) +from app.pgp_utils import load_public_key_and_check, PGPException from app.proton.utils import get_proton_partner from app.utils import sanitize_email from server import create_light_app @@ -741,6 +742,9 @@ def sanity_check(): LOG.d("check mailbox valid domain") check_mailbox_valid_domain() + LOG.d("check mailbox valid PGP keys") + check_mailbox_valid_pgp_keys() + LOG.d( """check if there's an email that starts with "\u200f" (right-to-left mark (RLM))""" ) @@ -765,7 +769,7 @@ def check_mailbox_valid_domain(): ) mailbox_ids = [e[0] for e in mailbox_ids] # iterate over id instead of mailbox directly - # as a mailbox can be deleted during the sleep time + # as a mailbox can be deleted in the meantime for mailbox_id in mailbox_ids: mailbox = Mailbox.get(mailbox_id) # a mailbox has been deleted @@ -821,6 +825,40 @@ def check_mailbox_valid_domain(): Session.commit() +def check_mailbox_valid_pgp_keys(): + mailbox_ids = ( + Session.query(Mailbox.id) + .filter( + Mailbox.verified.is_(True), + Mailbox.pgp_public_key.isnot(None), + Mailbox.disable_pgp.is_(False), + ) + .all() + ) + mailbox_ids = [e[0] for e in mailbox_ids] + # iterate over id instead of mailbox directly + # as a mailbox can be deleted in the meantime + for mailbox_id in mailbox_ids: + mailbox = Mailbox.get(mailbox_id) + # a mailbox has been deleted + if not mailbox: + continue + + try: + load_public_key_and_check(mailbox.pgp_public_key) + except PGPException: + LOG.i(f"{mailbox} PGP key invalid") + send_email( + mailbox.user.email, + f"Mailbox {mailbox.email}'s PGP Key is invalid", + render( + "transactional/invalid-mailbox-pgp-key.txt.jinja2", + mailbox=mailbox, + ), + retries=3, + ) + + def check_custom_domain(): LOG.d("Check verified domain for DNS issues") diff --git a/email_handler.py b/email_handler.py index da33caf5..b09f5975 100644 --- a/email_handler.py +++ b/email_handler.py @@ -162,7 +162,12 @@ from app.models import ( Notification, VerpType, ) -from app.pgp_utils import PGPException, sign_data_with_pgpy, sign_data +from app.pgp_utils import ( + PGPException, + sign_data_with_pgpy, + sign_data, + load_public_key_and_check, +) from app.utils import sanitize_email from init_app import load_pgp_public_keys from server import create_light_app @@ -502,7 +507,12 @@ def prepare_pgp_message( encrypted_data = pgp_utils.encrypt_file(BytesIO(msg_bytes), pgp_fingerprint) second.set_payload(encrypted_data) except PGPException: - LOG.w("Cannot encrypt using python-gnupg, use pgpy") + LOG.w( + "Cannot encrypt using python-gnupg, check if public key is valid and try with pgpy" + ) + # check if the public key is valid + load_public_key_and_check(public_key) + encrypted = pgp_utils.encrypt_file_with_pgpy(msg_bytes, public_key) second.set_payload(str(encrypted)) diff --git a/templates/emails/transactional/invalid-mailbox-pgp-key.txt.jinja2 b/templates/emails/transactional/invalid-mailbox-pgp-key.txt.jinja2 new file mode 100644 index 00000000..4a001d6f --- /dev/null +++ b/templates/emails/transactional/invalid-mailbox-pgp-key.txt.jinja2 @@ -0,0 +1,11 @@ +{% extends "base.txt.jinja2" %} + +{% block content %} + + We have detected that your mailbox {{ mailbox.email }}'s PGP key is invalid. + + A potential cause is the key is already expired. + + Please update the key so forwarded emails can be properly encrypted. + +{% endblock %}