def is_local_message(sender, recipient):
    '''
        Determine if the message is from the localhost.

        >>> sender = 'test@localhost'
        >>> recipient = '*****@*****.**'
        >>> is_local_message(sender, recipient)
        True
        >>> sender = '*****@*****.**'
        >>> recipient = 'test@localhost'
        >>> is_local_message(sender, recipient)
        True
        >>> sender = None
        >>> recipient = '*****@*****.**'
        >>> is_local_message(sender, recipient)
        False
        >>> sender = None
        >>> recipient = 'test@localhost'
        >>> is_local_message(sender, recipient)
        True
    '''

    def is_local_host_domain(user):
        is_text = isinstance(user, str)
        return user is not None and is_text and user.endswith('@localhost')

    return  (is_local_host_domain(sender) or
             is_local_host_domain(recipient) or
             (email_in_domain(sender) and email_in_domain(recipient)))
Exemple #2
0
def is_local_message(sender, recipient):
    '''
        Determine if the message is from the localhost.

        >>> sender = 'test@localhost'
        >>> recipient = '*****@*****.**'
        >>> is_local_message(sender, recipient)
        True
        >>> sender = '*****@*****.**'
        >>> recipient = 'test@localhost'
        >>> is_local_message(sender, recipient)
        True
        >>> sender = None
        >>> recipient = '*****@*****.**'
        >>> is_local_message(sender, recipient)
        False
        >>> sender = None
        >>> recipient = 'test@localhost'
        >>> is_local_message(sender, recipient)
        True
    '''
    def is_local_host_domain(user):
        is_text = isinstance(user, str)
        return user is not None and is_text and user.endswith('@localhost')

    return (is_local_host_domain(sender) or is_local_host_domain(recipient)
            or (email_in_domain(sender) and email_in_domain(recipient)))
Exemple #3
0
    def reject_message(self, error_message, message=None):
        '''
            Reject a message that had an unexpected error and return the to address.

            >>> # This message will fail if testing on dev system
            >>> to_address = get_admin_email()
            >>> filter = Filter('root', 'root', 'bad message')
            >>> filter.reject_message('Unknown message') == to_address
            True
        '''
        try:
            if message is None:
                message = self.out_message

            if email_in_domain(self.sender):
                to_address = self.sender
                subject = i18n('Undelivered Mail: Unable to send message')
            elif email_in_domain(self.recipient):
                to_address = self.recipient
                subject = i18n('Error: Unable to receive message')
            else:
                to_address = get_admin_email()
                subject = i18n('Message rejected.')

            notice = '{}'.format(error_message)
            if message is not None:
                notice += '\n\n===================\n{}'.format(message)
            notify_user(to_address, subject, notice)
        except:
            raise

        return to_address
Exemple #4
0
    def reject_message(self, error_message, message=None):
        '''
            Reject a message that had an unexpected error and return the to address.

            >>> # This message will fail if testing on dev system
            >>> to_address = get_admin_email()
            >>> filter = Filter('root', 'root', 'bad message')
            >>> filter.reject_message('Unknown message') == to_address
            True
        '''
        try:
            if message is None:
                message = self.out_message

            if email_in_domain(self.sender):
                to_address = self.sender
                subject = i18n('Undelivered Mail: Unable to send message')
            elif email_in_domain(self.recipient):
                to_address = self.recipient
                subject = i18n('Error: Unable to receive message')
            else:
                to_address = get_admin_email()
                subject = i18n('Message rejected.')

            notice = '{}'.format(error_message)
            if message is not None:
                notice += '\n\n===================\n{}'.format(message)
            notify_user(to_address, subject, notice)
        except:
            raise

        return to_address
def bounce_message(original_message, user, subject, error_message):
    '''
        Bounce a message that a local user.

        Test extreme case
        >>> bounce_message(None, None, None, None)
        False
    '''

    notified_user = False

    try:
        log_message(error_message)

        if user is None:
            log_message('unable to bounce message without a user email address')
        elif email_in_domain(user):
            message = '{}\n\n===================\n{}'.format(
              error_message, original_message)
            notified_user = notify_user(user, subject, message)
            log_message('sent note to {} about error.'.format(user))
        else:
            log_message('unable to send note to {} about error.'.format(user))
    except:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')

    return notified_user
def drop_message(original_message, recipient, subject, error_message):
    '''
        Drop a message that we shouldn't process from a remote user.

        Test extreme case
        >>> drop_message(None, None, None, None)
        False
    '''

    notified_user = False

    try:
        log_message(error_message)

        if recipient is None:
            log_message('unable to notify recipient about dropped message')
        elif email_in_domain(recipient):
            message = '{}\n\n===================\n{}'.format(
              error_message, original_message)
            notified_user = notify_user(recipient, subject, message)
            log_message('sent note to {} about error.'.format(recipient))
        else:
            log_message('unable to send note to {} about error.'.format(recipient))
    except:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')

    return notified_user
def get_passcode(email, encryption_name):
    '''
        Get the passcode for the encryption program for the contact.
        The email can be an RFC address or just the email address.

        >>> len(get_passcode('*****@*****.**', 'GPG')) > 0
        True
        >>> get_passcode('*****@*****.**', 'TestBC') is None
        True
    '''

    passcode = None

    try:
        if email_in_domain(email):
            address = get_email(email)
            user_key = get(address, encryption_name)
            if user_key:
                passcode = user_key.passcode
                if passcode and len(passcode) > 0:
                    log_message("private {} key configured for {}".format(encryption_name, email))
                else:
                    log_message('{} does not have a {} private key configured'.format(email, encryption_name))
            else:
                log_message('{} does not have a matching contact'.format(email))
        else:
            log_message('{} not part of managed domain so no private user key'.format(email))
    except Exception:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')

    return passcode
    def save(self):
        '''
            Save the fingerprint in the database.

            Test extreme case.
            >>> set_fingerprint_class = SetFingerprint(None)
            >>> set_fingerprint_class.save()
            False
        '''

        self.result_ok, self.crypto_name, self.email, self.key_plugin = prep_sync(self.contacts_encryption)
        if self.result_ok:
            try:
                # the contact's crypto record must exist or we'll get into an infinite loop
                # because whenever we add a contact's encryption for an email in our supported domain,
                # then this class is activated so we can complete the configuration
                if self.contacts_encryption is None:
                    self.result_ok = False
                    log_message('no contact encryption record for {}'.format(self.email))
                else:
                    fingerprint, expiration_date = self.key_plugin.get_fingerprint(self.email)
                    if fingerprint is None:
                        self.result_ok = False
                        log_message('no {} fingerprint found for {}'.format(self.crypto_name, self.email))
                    else:
                        self.result_ok = True
                        try:
                            fingerprint = format_fingerprint(fingerprint)
                            if (self.contacts_encryption.fingerprint is not None and
                                self.contacts_encryption.fingerprint != fingerprint):
                                log_message('replaced old fingerprint {} with {} for {}'.format(
                                    self.contacts_encryption.fingerprint, fingerprint, self.email))

                            # if we created the key, then be sure it's verified
                            if utils.email_in_domain(self.email):
                                if self.contacts_encryption.source is None and create_private_keys():
                                    self.contacts_encryption.source = AUTO_GENERATED
                                if self.contacts_encryption.source == AUTO_GENERATED:
                                    self.contacts_encryption.verified = True

                            self.contacts_encryption.fingerprint = fingerprint
                            self.contacts_encryption.save()
                            log_message('set {} fingerprint for {} in database: {}'.format(
                                self.crypto_name, self.email, fingerprint))
                        except IntegrityError as ie:
                            self.result_ok = False
                            if 'insert or update on table "mail_contactscrypto" violates foreign key constraint' in str(ie):
                                log_message('{} crypto key no longer exists for {}'.format(self.crypto_name, self.email))
                            else:
                                raise
            except Exception as exception:
                record_exception()
                log_message('EXCEPTION - see syr.exception.log for details')
                self.result_ok = False

        return self.result_ok
def post_save_contacts_crypto(sender, **kwargs):
    '''
        Process the contact's encryption record after it's saved.

        After crypto record saved:
  * if record for managed domain:
    * if no user_key record:
      * create user_key record
      * create gpg key
      * get gpg fingerprint
      * update crypto record with fingerprint
    * if user_key record, but no fingerprint in crypto record:
      * get gpg fingerprint
      * update crypto record with fingerprint
  * if no fingerprint in crypto record
    * get gpg fingerprint
    * update crypto record with fingerprint

    '''

    if TESTS_RUNNING:
        log_message('tests running so no post save processing')
    else:
        created = kwargs['created']
        contacts_encryption = kwargs['instance']
        email = contacts_encryption.contact.email
        encryption_name = contacts_encryption.encryption_software.name
        fingerprint = contacts_encryption.fingerprint
        log_message("starting post save for {} contact's {} crypto".format(email, encryption_name))

        if email_in_domain(email):
            if created:
                from goodcrypto.mail.message.utils import sync_private_key_via_queue

                log_message('starting to add private {} user key for {}'.format(encryption_name, email))
                if contacts_encryption.source is None:
                    contacts_encryption.source = AUTO_GENERATED
                sync_private_key_via_queue(contacts_encryption)
            elif fingerprint is None:
                from goodcrypto.mail.message.utils import sync_fingerprint_via_queue

                log_message('setting private {} fingerprint for {}'.format(encryption_name, email))
                sync_fingerprint_via_queue(contacts_encryption)
            else:
                log_message("{} already has {} fingerprint: {}".format(email, encryption_name, fingerprint))
        elif fingerprint is None:
            from goodcrypto.mail.message.utils import sync_fingerprint_via_queue

            log_message('setting {} fingerprint for {}'.format(encryption_name, email))
            sync_fingerprint_via_queue(contacts_encryption)
        else:
            log_message('{} already has {} crypto software defined'.format(email, encryption_name))

        log_message("finished post save for {} contact's {} crypto".format(email, encryption_name))
    def _get_recipient_encryption_software(self):
        '''
            Get the software to decrypt a message for the recipient (internal use only).

            If the user doesn't have a key, then configure one and return None.
        '''

        encryption_software = None

        if self.crypto_message is None:
            self.log_message("missing crypto_message".format(
                self.crypto_message))
        else:
            try:
                from_user = self.crypto_message.smtp_sender()
                to_user = self.crypto_message.smtp_recipient()
                encryption_software = contacts.get_encryption_names(to_user)
                if len(encryption_software) > 0:
                    self.log_message(
                        "encryption software: {}".format(encryption_software))
                elif email_in_domain(
                        to_user) and options.create_private_keys():
                    add_private_key(to_user,
                                    encryption_software=encryption_software)
                    self.log_message(
                        "started to create a new {} key for {}".format(
                            encryption_software, to_user))
                else:
                    self.log_message(
                        "no encryption software for {}".format(to_user))
                    self.crypto_message.add_error_tag_once(
                        i18n(
                            'Message appears encrypted, but {email} does not use any known encryption'
                            .format(email=to_user)))
                    report_unable_to_decrypt(
                        to_user,
                        self.crypto_message.get_email_message().to_string())
            except CryptoException as crypto_exception:
                raise CryptoException(crypto_exception.value)
            except Exception as IOError:
                utils.log_crypto_exception(MessageException(format_exc()))
                record_exception()
                self.log_message(
                    'EXCEPTION - see syr.exception.log for details')
                try:
                    self.crypto_message.add_error_tag_once(
                        SERIOUS_ERROR_PREFIX)
                except Exception:
                    record_exception()
                    self.log_message(
                        'EXCEPTION - see syr.exception.log for details')

        return encryption_software
Exemple #11
0
def make_key(line, domain, crypto_name):
    ''' Make a private key if its in the domain.'''

    user_name, email = parse_address(line)
    if email is None:
        print('{} is not a valid email address'.format(line))
    elif email_in_domain(email):
        if user_name is None or len(user_name.strip()) <= 0:
            full_address = email
        else:
            full_address = '{} <{}>'.format(user_name, email)
        contacts.add(full_address, crypto_name, source=AUTO_GENERATED)
    else:
        print('{} not in the domain: {}'.format(line, domain))
def make_key(line, domain, crypto_name):
    ''' Make a private key if its in the domain.'''

    user_name, email = parse_address(line)
    if email is None:
        print('{} is not a valid email address'.format(line))
    elif email_in_domain(email):
        if user_name is None or len(user_name.strip()) <= 0:
            full_address = email
        else:
            full_address = '{} <{}>'.format(user_name, email)
        contacts.add(full_address, crypto_name, source=AUTO_GENERATED)
    else:
        print('{} not in the domain: {}'.format(line, domain))
    def configure(self):
        '''
            Add a crypto key pair.

            >>> # In honor of David Fifield, developer and co-inventor of Flash Proxy.
            >>> from time import sleep
            >>> email = '*****@*****.**'
            >>> crypto_name = 'GPG'
            >>> contact = contacts.add(email, crypto_name)
            >>> contact != None
            True
            >>> contacts_crypto = contacts.get_contacts_crypto(email, crypto_name)
            >>> sync_db_key_class = SyncPrivateKey(contacts_crypto)
            >>> sync_db_key_class.configure()
            True
            >>> contacts.delete(email)
            True
            >>> sync_db_key_class.key_plugin.delete(email)
            True
        '''

        try:
            ok, self.crypto_name, self.email, self.key_plugin = prep_sync(
                self.contacts_encryption)
            self.result_ok = ok and email_in_domain(self.email)
            if self.result_ok and utils.ok_to_modify_key(
                    self.crypto_name, self.key_plugin):
                log_message('starting SyncPrivateKey.configure for {}'.format(
                    self.email))
                # if there's a matching private key
                if self._need_new_crypto_key():
                    self.result_ok = self._add_crypto_key()
                else:
                    self.result_ok = self._validate_passcode()
                    if self.result_ok:
                        self._save_fingerprint()
            else:
                log_message('{} not ok to configure private key'.format(
                    self.email))
                self.result_ok = False
        except Exception as exception:
            record_exception()
            log_message('EXCEPTION - see syr.exception.log for details')
            self.result_ok = False

        log_message('finished SyncPrivateKey.configure for {} ok: {}'.format(
            self.email, self.result_ok))

        return self.result_ok
Exemple #14
0
    def possibly_needs_encryption(self):
        '''
            Determine if the message might need to be encrypted or not.

            It could need encrytion if the sender has the same domain as
            the domain defined in goodcrypto mail server's options. If we're not using
            an SMTP proxy, then it never needs encryption if the message is going
            to and from a user with the domain defined in goodcrypto mail server's options.
        '''

        maybe_needs_encryption = email_in_domain(self.sender)

        self.log_message('possibly needs encryption: {}'.format(maybe_needs_encryption))

        return maybe_needs_encryption
    def get_public_key_header(self, from_user):
        '''
            Get the public key header lines.

            >>> from goodcrypto_tests.mail.message_utils import get_plain_message_name
            >>> from goodcrypto.oce.test_constants import EDWARD_LOCAL_USER
            >>> auto_exchange = options.auto_exchange_keys()
            >>> options.set_auto_exchange_keys(True)
            >>> filename = get_plain_message_name('basic.txt')
            >>> with open(filename) as input_file:
            ...     crypto_message = CryptoMessage(email_message=EmailMessage(input_file))
            ...     key_block = crypto_message.get_public_key_header(EDWARD_LOCAL_USER)
            ...     key_block is not None
            ...     len(key_block) > 0
            True
            True
            >>> options.set_auto_exchange_keys(auto_exchange)
        '''

        header_lines = []
        if options.auto_exchange_keys():
            encryption_software_list = contacts.get_encryption_names(from_user)

            # if no crypto and we're creating keys, then do so now
            if (len(encryption_software_list) <= 0
                    and email_in_domain(from_user)
                    and options.create_private_keys()):

                add_private_key(from_user)
                self.log_message(
                    "started to create a new key for {}".format(from_user))
                encryption_software_list = contacts.get_encryption_names(
                    from_user)

            if len(encryption_software_list) > 0:
                self.log_message(
                    "getting header with public keys for {}: {}".format(
                        from_user, encryption_software_list))

                for encryption_software in encryption_software_list:
                    key_block = self.create_public_key_block(
                        encryption_software, from_user)
                    if len(key_block) > 0:
                        header_lines += key_block
        else:
            self.log_message("Warning: auto-exchange of keys is not active")

        return header_lines
Exemple #16
0
    def possibly_needs_encryption(self):
        '''
            Determine if the message might need to be encrypted or not.

            It could need encrytion if the sender has the same domain as
            the domain defined in goodcrypto mail server's options. If we're not using
            an SMTP proxy, then it never needs encryption if the message is going
            to and from a user with the domain defined in goodcrypto mail server's options.
        '''

        maybe_needs_encryption = email_in_domain(self.sender)

        self.log_message(
            'possibly needs encryption: {}'.format(maybe_needs_encryption))

        return maybe_needs_encryption
    def _is_ready_for_search(self):
        '''
            Verify that we're ready to search for this key.

            Test extreme case.
            >>> srk_class = SearchKeyserver(None, None, None, None)
            >>> srk_class._is_ready_for_search()
            False
        '''

        ready = False
        try:
            ready = (self.email is not None
                     and self.encryption_name is not None
                     and self.keyserver is not None
                     and self.user_initiated_search is not None
                     and not email_in_domain(self.email))

            if ready:
                self.key_plugin = KeyFactory.get_crypto(
                    self.encryption_name,
                    crypto_software.get_key_classname(self.encryption_name))
                ready = self.key_plugin is not None

            if ready:
                # make sure we don't already have crypto defined for this user
                contacts_crypto = contacts.get_contacts_crypto(
                    self.email, self.encryption_name)
                if contacts_crypto is None or contacts_crypto.fingerprint is None:
                    fingerprint, expiration = self.key_plugin.get_fingerprint(
                        self.email)
                    if fingerprint is not None:
                        ready = False
                        self.log_message(
                            '{} public key exists for {}: {}'.format(
                                self.encryption_name, self.email, fingerprint))
                else:
                    ready = False
                    self.log_message('crypto for {} already defined'.format(
                        self.email))

        except Exception as exception:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')
            ready = False

        return ready
    def get_public_key_header(self, from_user):
        '''
            Get the public key header lines.

            >>> from goodcrypto_tests.mail.message_utils import get_plain_message_name
            >>> from goodcrypto.oce.test_constants import EDWARD_LOCAL_USER
            >>> auto_exchange = options.auto_exchange_keys()
            >>> options.set_auto_exchange_keys(True)
            >>> filename = get_plain_message_name('basic.txt')
            >>> with open(filename) as input_file:
            ...     crypto_message = CryptoMessage(email_message=EmailMessage(input_file))
            ...     key_block = crypto_message.get_public_key_header(EDWARD_LOCAL_USER)
            ...     key_block is not None
            ...     len(key_block) > 0
            True
            True
            >>> options.set_auto_exchange_keys(auto_exchange)
        '''

        header_lines = []
        if options.auto_exchange_keys():
            encryption_software_list = contacts.get_encryption_names(from_user)

            # if no crypto and we're creating keys, then do so now
            if (len(encryption_software_list) <= 0 and
                email_in_domain(from_user) and
                options.create_private_keys()):

                add_private_key(from_user)
                self.log_message("started to create a new key for {}".format(from_user))
                encryption_software_list = contacts.get_encryption_names(from_user)

            if len(encryption_software_list) > 0:
                self.log_message("getting header with public keys for {}: {}".format(
                   from_user, encryption_software_list))

                for encryption_software in encryption_software_list:
                    key_block = self.create_public_key_block(encryption_software, from_user)
                    if len(key_block) > 0:
                        header_lines += key_block
        else:
            self.log_message("Warning: auto-exchange of keys is not active")

        return header_lines
    def configure(self):
        '''
            Add a crypto key pair.

            >>> # In honor of David Fifield, developer and co-inventor of Flash Proxy.
            >>> from time import sleep
            >>> email = '*****@*****.**'
            >>> crypto_name = 'GPG'
            >>> contact = contacts.add(email, crypto_name)
            >>> contact != None
            True
            >>> contacts_crypto = contacts.get_contacts_crypto(email, crypto_name)
            >>> sync_db_key_class = SyncPrivateKey(contacts_crypto)
            >>> sync_db_key_class.configure()
            True
            >>> contacts.delete(email)
            True
            >>> sync_db_key_class.key_plugin.delete(email)
            True
        '''

        try:
            ok, self.crypto_name, self.email, self.key_plugin = prep_sync(self.contacts_encryption)
            self.result_ok = ok and email_in_domain(self.email)
            if self.result_ok and utils.ok_to_modify_key(self.crypto_name, self.key_plugin):
                log_message('starting SyncPrivateKey.configure for {}'.format(self.email))
                # if there's a matching private key
                if self._need_new_crypto_key():
                    self.result_ok = self._add_crypto_key()
                else:
                    self.result_ok = self._validate_passcode()
                    if self.result_ok:
                        self._save_fingerprint()
            else:
                log_message('{} not ok to configure private key'.format(self.email))
                self.result_ok = False
        except Exception as exception:
            record_exception()
            log_message('EXCEPTION - see syr.exception.log for details')
            self.result_ok = False

        log_message('finished SyncPrivateKey.configure for {} ok: {}'.format(self.email, self.result_ok))

        return self.result_ok
    def _is_ready_for_search(self):
        '''
            Verify that we're ready to search for this key.

            Test extreme case.
            >>> srk_class = SearchKeyserver(None, None, None, None)
            >>> srk_class._is_ready_for_search()
            False
        '''

        ready = False
        try:
            ready = (self.email is not None and
                     self.encryption_name is not None and
                     self.keyserver is not None and
                     self.user_initiated_search is not None and
                     not email_in_domain(self.email))

            if ready:
                self.key_plugin = KeyFactory.get_crypto(
                   self.encryption_name, crypto_software.get_key_classname(self.encryption_name))
                ready = self.key_plugin is not None

            if ready:
                # make sure we don't already have crypto defined for this user
                contacts_crypto = contacts.get_contacts_crypto(self.email, self.encryption_name)
                if contacts_crypto is None or contacts_crypto.fingerprint is None:
                    fingerprint, expiration = self.key_plugin.get_fingerprint(self.email)
                    if fingerprint is not None:
                        ready = False
                        self.log_message('{} public key exists for {}: {}'.format(
                            self.encryption_name, self.email, fingerprint))
                else:
                    ready = False
                    self.log_message('crypto for {} already defined'.format(self.email))

        except Exception as exception:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')
            ready = False

        return ready
def add_private_key(email, encryption_software=None):
    '''
        Add a private key if it doesn't exist.

        Creating a key takes minutes so a separate process handles it so no return code.

        >>> add_private_key(None)
    '''

    try:
        # only add private keys for members of the domain
        if email_in_domain(email):
            if options.create_private_keys():
                if encryption_software is None or len(encryption_software) <= 0:
                    encryption_software = CryptoFactory.DEFAULT_ENCRYPTION_NAME

                user_key = user_keys.get(email, encryption_software)
                if user_key is None:
                    contacts_crypto = contacts.get_contacts_crypto(email, encryption_name=encryption_software)
                    if contacts_crypto is None:
                        # a private user key will automatically be created
                        # when the contact's crypto record is created after the contact is created
                        contacts.add(email, encryption_software, source=AUTO_GENERATED)
                        log_message('add {} key for {}'.format(encryption_software, email))
                    else:
                        log_message('adding private {} user key for {}'.format(encryption_software, email))
                        sync_private_key_via_queue(contacts_crypto)

                elif user_key.contacts_encryption.fingerprint is None:
                    log_message('setting private {} key fingerprint for {}'.format(encryption_software, email))
                    sync_fingerprint_via_queue(user_key.contacts_encryption)
                else:
                    log_message('{} already has crypto software defined'.format(email))
            else:
                log_message('creating private key disabled so no key created for {}'.format(email))
        else:
            log_message('{} not a member of {}'.format(email, get_domain()))

    except Exception:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')
Exemple #22
0
    def _get_recipient_encryption_software(self):
        '''
            Get the software to decrypt a message for the recipient (internal use only).

            If the user doesn't have a key, then configure one and return None.
        '''

        encryption_software = None

        if self.crypto_message is None:
            self.log_message("missing crypto_message".format(self.crypto_message))
        else:
            try:
                from_user = self.crypto_message.smtp_sender()
                to_user = self.crypto_message.smtp_recipient()
                encryption_software = contacts.get_encryption_names(to_user)
                if len(encryption_software) > 0:
                    self.log_message("encryption software: {}".format(encryption_software))
                elif email_in_domain(to_user) and options.create_private_keys():
                    add_private_key(to_user, encryption_software=encryption_software)
                    self.log_message("started to create a new {} key for {}".format(encryption_software, to_user))
                else:
                    self.log_message("no encryption software for {}".format(to_user))
                    self.crypto_message.add_error_tag_once(
                        i18n('Message appears encrypted, but {email} does not use any known encryption'.format(email=to_user)))
                    report_unable_to_decrypt(to_user, self.crypto_message.get_email_message().to_string())
            except CryptoException as crypto_exception:
                raise CryptoException(crypto_exception.value)
            except Exception as IOError:
                utils.log_crypto_exception(MessageException(format_exc()))
                record_exception()
                self.log_message('EXCEPTION - see syr.exception.log for details')
                try:
                    self.crypto_message.add_error_tag_once(SERIOUS_ERROR_PREFIX)
                except Exception:
                    record_exception()
                    self.log_message('EXCEPTION - see syr.exception.log for details')

        return encryption_software
Exemple #23
0
def get_passcode(email, encryption_name):
    '''
        Get the passcode for the encryption program for the contact.
        The email can be an RFC address or just the email address.

        >>> len(get_passcode('*****@*****.**', 'GPG')) > 0
        True
        >>> get_passcode('*****@*****.**', 'TestBC') is None
        True
    '''

    passcode = None

    try:
        if email_in_domain(email):
            address = get_email(email)
            user_key = get(address, encryption_name)
            if user_key:
                passcode = user_key.passcode
                if passcode and len(passcode) > 0:
                    log_message("private {} key configured for {}".format(
                        encryption_name, email))
                else:
                    log_message(
                        '{} does not have a {} private key configured'.format(
                            email, encryption_name))
            else:
                log_message(
                    '{} does not have a matching contact'.format(email))
        else:
            log_message(
                '{} not part of managed domain so no private user key'.format(
                    email))
    except Exception:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')

    return passcode
    def save(self):
        '''
            Save the fingerprint in the database.

            Test extreme case.
            >>> set_fingerprint_class = SetFingerprint(None)
            >>> set_fingerprint_class.save()
            False
        '''

        self.result_ok, self.crypto_name, self.email, self.key_plugin = prep_sync(
            self.contacts_encryption)
        if self.result_ok:
            try:
                # the contact's crypto record must exist or we'll get into an infinite loop
                # because whenever we add a contact's encryption for an email in our supported domain,
                # then this class is activated so we can complete the configuration
                if self.contacts_encryption is None:
                    self.result_ok = False
                    log_message('no contact encryption record for {}'.format(
                        self.email))
                else:
                    fingerprint, expiration_date = self.key_plugin.get_fingerprint(
                        self.email)
                    if fingerprint is None:
                        self.result_ok = False
                        log_message('no {} fingerprint found for {}'.format(
                            self.crypto_name, self.email))
                    else:
                        self.result_ok = True
                        try:
                            fingerprint = format_fingerprint(fingerprint)
                            if (self.contacts_encryption.fingerprint
                                    is not None
                                    and self.contacts_encryption.fingerprint !=
                                    fingerprint):
                                log_message(
                                    'replaced old fingerprint {} with {} for {}'
                                    .format(
                                        self.contacts_encryption.fingerprint,
                                        fingerprint, self.email))

                            # if we created the key, then be sure it's verified
                            if utils.email_in_domain(self.email):
                                if self.contacts_encryption.source is None and create_private_keys(
                                ):
                                    self.contacts_encryption.source = AUTO_GENERATED
                                if self.contacts_encryption.source == AUTO_GENERATED:
                                    self.contacts_encryption.verified = True

                            self.contacts_encryption.fingerprint = fingerprint
                            self.contacts_encryption.save()
                            log_message(
                                'set {} fingerprint for {} in database: {}'.
                                format(self.crypto_name, self.email,
                                       fingerprint))
                        except IntegrityError as ie:
                            self.result_ok = False
                            if 'insert or update on table "mail_contactscrypto" violates foreign key constraint' in str(
                                    ie):
                                log_message(
                                    '{} crypto key no longer exists for {}'.
                                    format(self.crypto_name, self.email))
                            else:
                                raise
            except Exception as exception:
                record_exception()
                log_message('EXCEPTION - see syr.exception.log for details')
                self.result_ok = False

        return self.result_ok
def is_key_ok(email, encryption_name):
    '''
        Throws a CryptoException if the email address does not have a crypto key, or
        the key has expired, or the key's fingerprint does not match the fingerprint in the database.

        >>> from goodcrypto.oce.test_constants import EDWARD_LOCAL_USER_ADDR
        >>> ok, __, active = is_key_ok(EDWARD_LOCAL_USER_ADDR, KeyFactory.DEFAULT_ENCRYPTION_NAME)
        >>> ok
        True
        >>> active
        True

        # In honor of Georg Koppen, works on Tor Browser, Torbutton, and our build automation.
        >>> email = 'Georg <*****@*****.**>'
        >>> try:
        ...     is_key_ok(email, KeyFactory.DEFAULT_ENCRYPTION_NAME)
        ...     fail()
        ... except CryptoException as crypto_exception:
        ...     crypto_exception.__str__() == 'There is no key for Georg <*****@*****.**>.'
        True
    '''

    # we use NO_FINGERPRINT_IN_DB a few times because we don't want to get too technical

    key_ok = verified = active = False
    encryption_software = crypto_software.get(encryption_name)
    if encryption_software is None:
        # this should never happen, but better be prepared
        log_message('no database entry for {}'.format(email))
        raise CryptoException(
            i18n(
                NO_FINGERPRINT_IN_DB.format(encryption=encryption_name,
                                            email=email)))
    else:
        key_crypto = KeyFactory.get_crypto(encryption_name,
                                           encryption_software.classname)
        if key_crypto is None:
            # this should never happen, but better to be prepared
            log_message('no plugin for {} with classname: {}'.format(
                encryption_name, encryption_software.classname))
            raise CryptoException(
                i18n(
                    NO_FINGERPRINT_IN_DB.format(encryption=encryption_name,
                                                email=email)))
        else:
            # see if the crypto key exists
            crypto_fingerprint, expiration = key_crypto.get_fingerprint(email)
            if crypto_fingerprint is None:
                message = i18n(
                    'There is no key for {email}.'.format(email=email))
                log_message(message)
                raise CryptoException(message)

            # if the key has expired, then raise an error
            if expiration is not None and key_crypto.fingerprint_expired(
                    expiration):
                message = i18n('The key for {email} expired on {date}.'.format(
                    email=email, date=expiration))
                log_message(message)
                raise CryptoException(message)

            database_fingerprint, verified, active = get_fingerprint(
                email, encryption_name)
            # if there isn't a fingerprint, then try to save the crypto fingerprint
            if database_fingerprint is None or len(
                    database_fingerprint.strip()) <= 0:
                contacts_encryption = get_contacts_crypto(
                    email, encryption_name=encryption_name)
                if contacts_encryption is not None and contacts_encryption.fingerprint is None:
                    database_fingerprint = crypto_fingerprint
                    contacts_encryption.fingerprint = database_fingerprint
                    if email_in_domain(
                            email) and contacts.crypto.source is None:
                        contacts.crypto.source = constants.AUTO_GENERATED
                    contacts_encryption.save()
                    log_message('updated {} fingerprint for {}'.format(
                        encryption_name, email))

            if database_fingerprint is None or len(
                    database_fingerprint.strip()) <= 0:
                error_message = i18n(
                    NO_FINGERPRINT_IN_DB.format(encryption=encryption_name,
                                                email=email))
                log_message(error_message)
                raise CryptoException(error_message)
            else:
                # finally verify the fingerprints agree
                if (strip_fingerprint(database_fingerprint).lower() ==
                        strip_fingerprint(crypto_fingerprint).lower()):
                    key_ok = True
                else:
                    message = i18n(
                        'The fingerprint for {email} does not match the saved fingerprint.'
                        .format(email=email))
                    log_message('email address: {}'.format(email))
                    log_message('  database fingerprint: {}'.format(
                        database_fingerprint.lower()))
                    log_message('  crypto fingerprint: {}'.format(
                        crypto_fingerprint.lower()))
                    log_message(message)
                    raise CryptoException(message)

            log_message(
                '{} fingerprints agree and key has not expired for {}'.format(
                    encryption_name, email))

    return key_ok, verified, active
def add(email, encryption_program, fingerprint=None, passcode=None, source=None):
    '''
        Add a contact and related settings.

        >>> # In honor of Thomas Drake, a whistleblower about Trailblazer, a NSA mass surveillance project.
        >>> email = '*****@*****.**'
        >>> encryption_software = crypto_software.get(KeyFactory.DEFAULT_ENCRYPTION_NAME)
        >>> contact = add(email, KeyFactory.DEFAULT_ENCRYPTION_NAME)
        >>> contact.email
        '*****@*****.**'
        >>> contact.user_name
        'Thomas'
        >>> address = contact.email
        >>> address = '*****@*****.**'
        >>> contacts_crypto = ContactsCrypto.objects.get(
        ...    contact=contact, encryption_software=encryption_software)
        >>> contacts_crypto is not None
        True
        >>> x = contact.delete()
        >>> contact = add(None, encryption_software)
        >>> contact is None
        True
        >>> contact = add(email, None)
        >>> contact.email = '*****@*****.**'
        >>> contact.user_name = 'Thomas'
        >>> get_contacts_crypto(email)
        []
        >>> x = contact.delete()
        >>> contact = add(None, None)
        >>> contact is None
        True
        >>> contact = add('*****@*****.**', None)
        >>> contact.email = '*****@*****.**'
        >>> contact.user_name = 'test.com domain key (system use only)'
        >>> x = contact.delete()
    '''

    try:
        new_contact = True
        user_name, email_address = parse_address(email)
        if email_address is None:
            contact = None
        else:
            try:
                contact = Contact.objects.get(email=email_address)
                new_contact = False

                # update the user name if it's been given and it differs from the name in the DB
                if user_name is not None and contact.user_name != user_name:
                    contact.user_name = user_name
                    contact.save()
                    log_message('updated {} user name to {}'.format(email_address, user_name))
            except Contact.DoesNotExist:
                log_message('creating a contact for {}'.format(email_address))
                try:
                    if user_name is None or len(user_name.strip()) <= 0:
                        from goodcrypto.mail.message.metadata import is_metadata_address

                        user_name = email_address
                        i = user_name.find('@')

                        # handle domain keys specially
                        if is_metadata_address(email_address):
                            if i > 0:
                                email_domain = user_name[i+1:]
                            user_name = '{} domain key (system use only)'.format(email_domain)
                        else:
                            if i > 0:
                                user_name = user_name[:i]
                            user_name = user_name.replace('.', ' ').replace('-', ' ').replace('_', ' ')
                            user_name = capwords(user_name)
                except:
                    pass

                contact = Contact.objects.create(email=email_address, user_name=user_name)
            except Exception:
                record_exception()
                log_message('EXCEPTION - see syr.exception.log for details')
                contact = None

        if encryption_program is None:
            log_message("no encryption software defined so not creating contact's crytpo record for {}".format(email))
        else:
            # add a corresponding record for the contact's crypto program
            encryption_software = crypto_software.get(encryption_program)
            if contact is None or encryption_software is None:
                log_message('no contact and/or encryption software defined')
            else:
                try:
                    contacts_crypto = ContactsCrypto.objects.get(
                      contact=contact, encryption_software=encryption_software)
                    if (fingerprint is not None and
                        strip_fingerprint(contacts_crypto.fingerprint) != strip_fingerprint(fingerprint)):
                        contacts_crypto.fingerprint = format_fingerprint(fingerprint)
                        if email_in_domain(email):
                            if contacts.crypto.source is None:
                                contacts.crypto.source = constants.AUTO_GENERATED
                            if contacts_crypto.source == constants.AUTO_GENERATED:
                                contacts_crypto.verified = True
                        contacts_crypto.save()
                except ContactsCrypto.DoesNotExist:
                    # if the contact existed without any contact crypto, but was set
                    # to never encrypt and now we have a key, then change the
                    # outbound encrypt policy to the default
                    if (not new_contact and
                        contact.outbound_encrypt_policy == constants.NEVER_ENCRYPT_OUTBOUND):
                        contact.outbound_encrypt_policy = constants.DEFAULT_OUTBOUND_ENCRYPT_POLICY
                        contact.save()

                    contacts_crypto = add_contacts_crypto(contact, encryption_software,
                        fingerprint=fingerprint, source=source)

                    log_message("created {} crypto record for {} with {} fingerprint: {}".format(
                        encryption_software, email, fingerprint, contacts_crypto is not None))
                except:
                    record_exception()
                    log_message('EXCEPTION - see syr.exception.log for details')

    except Exception:
        contact = None
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')

    return contact
def _import_key_add_contact(keyblock, user_name, possible_fingerprint, passcode, id_fingerprint_pairs, plugin):
    '''
        Import keys and create associated contact records.

    '''
    def update_contact(contact, crypto_name, fingerprint):
        fingerprint_ok = True
        try:
            if (user_name is not None and
                (contact.user_name is None or len(contact.user_name.strip()) <= 0)):
                contact.user_name = user_name.strip()
                contact.save()
                log_message('updated user name')
            else:
                log_message('user name: {}'.format(user_name))
                log_message('contact user name: {}'.format(contact.user_name))
            if possible_fingerprint is not None and len(possible_fingerprint.strip()) > 0:
                if strip_fingerprint(possible_fingerprint).lower() == strip_fingerprint(fingerprint).lower():
                    contacts_crypto = contacts.get_contacts_crypto(user_id, plugin.get_name())
                    contacts_crypto.verified = True
                    contacts_crypto.save()
                    log_message('verified fingerprint')
                else:
                    fingerprint_ok = False
                    log_message('possible fingerprint: {}'.format(strip_fingerprint(possible_fingerprint).lower()))
                    log_message('imported fingerprint: {}'.format(strip_fingerprint(fingerprint).lower()))
        except:
            record_exception()
            log_message('EXCEPTION - see syr.exception.log for details')

        return  fingerprint_ok


    result_ok = True
    fingerprint_ok = True
    status = i18n('Imported key:')

    if plugin is None:
        result_ok = False
        fingerprint_ok = False
    else:
        result_ok = plugin.import_public(keyblock, id_fingerprint_pairs)
        log_message('imported key: {}'.format(result_ok))

    if result_ok:
        crypto_name = plugin.get_name()

        local_users = []
        for (user_id, fingerprint) in id_fingerprint_pairs:
            if email_in_domain(user_id):
                local_users.append(user_id)

            if user_name is not None:
                full_email = '{} <{}>'.format(user_name, user_id)
            else:
                full_email = user_id
            contact = contacts.add(full_email, crypto_name, passcode=passcode, source=MANUALLY_IMPORTED)
            if contact is None:
                log_message('unable to add contact for {}'.format(user_id))
            else:
                if not update_contact(contact, plugin.get_name(), fingerprint):
                    fingerprint_ok = False
                status += ' {}'.format(user_id)

        if len(local_users) > 0:
            for local_user in local_users:
                if not plugin.is_passcode_valid(local_user, passcode, key_exists=True):
                    result_ok = False
                    status = ('The passcode is not correct for the imported key for {email}').format(email=user_id)
                    log_message(status)
                    break
            for (user_id, __) in id_fingerprint_pairs:
                if not result_ok:
                    contacts.delete(user_id)
                    log_message('deleted {}'.format(user_id))
    else:
        result_ok = False
        status = KEYBLOCK_INVALID

    log_message(status)

    return result_ok, status, fingerprint_ok
def import_key_now(encryption_name, keyblock, user_name, possible_fingerprint, passcode):
    ''' Import if key is ok and doesn't exist. '''

    fingerprint_ok = True
    id_fingerprint_pairs = []

    if encryption_name is None or keyblock is None:
        result_ok = False
        fingerprint_ok = False
        status = MISSING_DATA_STATUS
        log_message('crypto: {} / keyblock is None: {}'.format(
           encryption_name, keyblock is None))
    else:
        encryption_software = crypto_software.get(encryption_name)
        plugin = KeyFactory.get_crypto(encryption_software.name, encryption_software.classname)
        if plugin is None:
            result_ok = False
            status = ('GoodCrypto does not currently support {encryption}').format(
                encryption=encryption_software.name)
            log_message('no plugin for {} with classname: {}'.format(
                encryption_software.name, encryption_software.classname))
        else:
            id_fingerprint_pairs = plugin.get_id_fingerprint_pairs(keyblock)
            if id_fingerprint_pairs is None or len(id_fingerprint_pairs) <= 0:
                result_ok = False
                status = KEYBLOCK_INVALID
            else:
                result_ok = True
                for (user_id, fingerprint) in id_fingerprint_pairs:
                    if email_in_domain(user_id):
                        if passcode is None or len(passcode.strip()) <= 0:
                            result_ok = False
                            status = ('You must include the passcode when importing a key for {email}').format(email=user_id)
                            break

                    if result_ok:
                        # make sure we don't already have crypto defined for this user
                        contacts_crypto = contacts.get_contacts_crypto(user_id, encryption_name)
                        if contacts_crypto is None or contacts_crypto.fingerprint is None:
                            fingerprint, expiration = plugin.get_fingerprint(user_id)
                            if fingerprint is not None:
                                log_message('{} key exists for {}: {}'.format(
                                    encryption_name, user_id, fingerprint))
                                result_ok = False
                        else:
                            result_ok = False

                        if not result_ok:
                            status = ('A {encryption_name} key already exists for {email}. If you have a new key, then delete the Contact and then try importing the key again.').format(
                                encryption_name=encryption_name, email=user_id)
                            break

                # import the key if this is a new contact
                if result_ok:
                    log_message('importing keys for {}'.format(id_fingerprint_pairs))
                    result_ok, status, fingerprint_ok = _import_key_add_contact(
                        keyblock, user_name, possible_fingerprint, passcode, id_fingerprint_pairs, plugin)
                else:
                    log_message('unable to import keys for {}'.format(id_fingerprint_pairs))

    log_message("Imported public {} key ok: {}".format(encryption_name, result_ok))
    log_message("    Status: {}".format(status))

    return result_ok, status, fingerprint_ok, id_fingerprint_pairs
def is_key_ok(email, encryption_name):
    '''
        Throws a CryptoException if the email address does not have a crypto key, or
        the key has expired, or the key's fingerprint does not match the fingerprint in the database.

        >>> from goodcrypto.oce.test_constants import EDWARD_LOCAL_USER_ADDR
        >>> ok, __, active = is_key_ok(EDWARD_LOCAL_USER_ADDR, KeyFactory.DEFAULT_ENCRYPTION_NAME)
        >>> ok
        True
        >>> active
        True

        # In honor of Georg Koppen, works on Tor Browser, Torbutton, and our build automation.
        >>> email = 'Georg <*****@*****.**>'
        >>> try:
        ...     is_key_ok(email, KeyFactory.DEFAULT_ENCRYPTION_NAME)
        ...     fail()
        ... except CryptoException as crypto_exception:
        ...     crypto_exception.__str__() == 'There is no key for Georg <*****@*****.**>.'
        True
    '''

    # we use NO_FINGERPRINT_IN_DB a few times because we don't want to get too technical

    key_ok = verified = active = False
    encryption_software = crypto_software.get(encryption_name)
    if encryption_software is None:
        # this should never happen, but better be prepared
        log_message('no database entry for {}'.format(email))
        raise CryptoException(i18n(NO_FINGERPRINT_IN_DB.format(encryption=encryption_name, email=email)))
    else:
        key_crypto = KeyFactory.get_crypto(encryption_name, encryption_software.classname)
        if key_crypto is None:
            # this should never happen, but better to be prepared
            log_message('no plugin for {} with classname: {}'.format(
                encryption_name, encryption_software.classname))
            raise CryptoException(i18n(NO_FINGERPRINT_IN_DB.format(encryption=encryption_name, email=email)))
        else:
            # see if the crypto key exists
            crypto_fingerprint, expiration = key_crypto.get_fingerprint(email)
            if crypto_fingerprint is None:
                message = i18n('There is no key for {email}.'.format(email=email))
                log_message(message)
                raise CryptoException(message)

            # if the key has expired, then raise an error
            if expiration is not None and key_crypto.fingerprint_expired(expiration):
                message = i18n('The key for {email} expired on {date}.'.format(email=email, date=expiration))
                log_message(message)
                raise CryptoException(message)

            database_fingerprint, verified, active = get_fingerprint(email, encryption_name)
            # if there isn't a fingerprint, then try to save the crypto fingerprint
            if database_fingerprint is None or len(database_fingerprint.strip()) <= 0:
                contacts_encryption = get_contacts_crypto(email, encryption_name=encryption_name)
                if contacts_encryption is not None and contacts_encryption.fingerprint is None:
                    database_fingerprint = crypto_fingerprint
                    contacts_encryption.fingerprint = database_fingerprint
                    if email_in_domain(email) and contacts.crypto.source is None:
                        contacts.crypto.source = constants.AUTO_GENERATED
                    contacts_encryption.save()
                    log_message('updated {} fingerprint for {}'.format(encryption_name, email))

            if database_fingerprint is None or len(database_fingerprint.strip()) <= 0:
                error_message = i18n(NO_FINGERPRINT_IN_DB.format(encryption=encryption_name, email=email))
                log_message(error_message)
                raise CryptoException(error_message)
            else:
                # finally verify the fingerprints agree
                if (strip_fingerprint(database_fingerprint).lower() ==
                    strip_fingerprint(crypto_fingerprint).lower()):
                    key_ok = True
                else:
                    message = i18n('The fingerprint for {email} does not match the saved fingerprint.'.format(
                        email=email))
                    log_message('email address: {}'.format(email))
                    log_message('  database fingerprint: {}'.format(database_fingerprint.lower()))
                    log_message('  crypto fingerprint: {}'.format(crypto_fingerprint.lower()))
                    log_message(message)
                    raise CryptoException(message)

            log_message('{} fingerprints agree and key has not expired for {}'.format(
              encryption_name, email))

    return key_ok, verified, active
Exemple #30
0
def import_key_now(encryption_name, keyblock, user_name, possible_fingerprint,
                   passcode):
    ''' Import if key is ok and doesn't exist. '''

    fingerprint_ok = True
    id_fingerprint_pairs = []

    if encryption_name is None or keyblock is None:
        result_ok = False
        fingerprint_ok = False
        status = MISSING_DATA_STATUS
        log_message('crypto: {} / keyblock is None: {}'.format(
            encryption_name, keyblock is None))
    else:
        encryption_software = crypto_software.get(encryption_name)
        plugin = KeyFactory.get_crypto(encryption_software.name,
                                       encryption_software.classname)
        if plugin is None:
            result_ok = False
            status = (
                'GoodCrypto does not currently support {encryption}').format(
                    encryption=encryption_software.name)
            log_message('no plugin for {} with classname: {}'.format(
                encryption_software.name, encryption_software.classname))
        else:
            id_fingerprint_pairs = plugin.get_id_fingerprint_pairs(keyblock)
            if id_fingerprint_pairs is None or len(id_fingerprint_pairs) <= 0:
                result_ok = False
                status = KEYBLOCK_INVALID
            else:
                result_ok = True
                for (user_id, fingerprint) in id_fingerprint_pairs:
                    if email_in_domain(user_id):
                        if passcode is None or len(passcode.strip()) <= 0:
                            result_ok = False
                            status = (
                                'You must include the passcode when importing a key for {email}'
                            ).format(email=user_id)
                            break

                    if result_ok:
                        # make sure we don't already have crypto defined for this user
                        contacts_crypto = contacts.get_contacts_crypto(
                            user_id, encryption_name)
                        if contacts_crypto is None or contacts_crypto.fingerprint is None:
                            fingerprint, expiration = plugin.get_fingerprint(
                                user_id)
                            if fingerprint is not None:
                                log_message('{} key exists for {}: {}'.format(
                                    encryption_name, user_id, fingerprint))
                                result_ok = False
                        else:
                            result_ok = False

                        if not result_ok:
                            status = (
                                'A {encryption_name} key already exists for {email}. If you have a new key, then delete the Contact and then try importing the key again.'
                            ).format(encryption_name=encryption_name,
                                     email=user_id)
                            break

                # import the key if this is a new contact
                if result_ok:
                    log_message(
                        'importing keys for {}'.format(id_fingerprint_pairs))
                    result_ok, status, fingerprint_ok = _import_key_add_contact(
                        keyblock, user_name, possible_fingerprint, passcode,
                        id_fingerprint_pairs, plugin)
                else:
                    log_message('unable to import keys for {}'.format(
                        id_fingerprint_pairs))

    log_message("Imported public {} key ok: {}".format(encryption_name,
                                                       result_ok))
    log_message("    Status: {}".format(status))

    return result_ok, status, fingerprint_ok, id_fingerprint_pairs
def post_save_contacts_crypto(sender, **kwargs):
    '''
        Process the contact's encryption record after it's saved.

        After crypto record saved:
  * if record for managed domain:
    * if no user_key record:
      * create user_key record
      * create gpg key
      * get gpg fingerprint
      * update crypto record with fingerprint
    * if user_key record, but no fingerprint in crypto record:
      * get gpg fingerprint
      * update crypto record with fingerprint
  * if no fingerprint in crypto record
    * get gpg fingerprint
    * update crypto record with fingerprint

    '''

    if TESTS_RUNNING:
        log_message('tests running so no post save processing')
    else:
        created = kwargs['created']
        contacts_encryption = kwargs['instance']
        email = contacts_encryption.contact.email
        encryption_name = contacts_encryption.encryption_software.name
        fingerprint = contacts_encryption.fingerprint
        log_message("starting post save for {} contact's {} crypto".format(
            email, encryption_name))

        if email_in_domain(email):
            if created:
                from goodcrypto.mail.message.utils import sync_private_key_via_queue

                log_message(
                    'starting to add private {} user key for {}'.format(
                        encryption_name, email))
                if contacts_encryption.source is None:
                    contacts_encryption.source = AUTO_GENERATED
                sync_private_key_via_queue(contacts_encryption)
            elif fingerprint is None:
                from goodcrypto.mail.message.utils import sync_fingerprint_via_queue

                log_message('setting private {} fingerprint for {}'.format(
                    encryption_name, email))
                sync_fingerprint_via_queue(contacts_encryption)
            else:
                log_message("{} already has {} fingerprint: {}".format(
                    email, encryption_name, fingerprint))
        elif fingerprint is None:
            from goodcrypto.mail.message.utils import sync_fingerprint_via_queue

            log_message('setting {} fingerprint for {}'.format(
                encryption_name, email))
            sync_fingerprint_via_queue(contacts_encryption)
        else:
            log_message('{} already has {} crypto software defined'.format(
                email, encryption_name))

        log_message("finished post save for {} contact's {} crypto".format(
            email, encryption_name))
def add(email,
        encryption_program,
        fingerprint=None,
        passcode=None,
        source=None):
    '''
        Add a contact and related settings.

        >>> # In honor of Thomas Drake, a whistleblower about Trailblazer, a NSA mass surveillance project.
        >>> email = '*****@*****.**'
        >>> encryption_software = crypto_software.get(KeyFactory.DEFAULT_ENCRYPTION_NAME)
        >>> contact = add(email, KeyFactory.DEFAULT_ENCRYPTION_NAME)
        >>> contact.email
        '*****@*****.**'
        >>> contact.user_name
        'Thomas'
        >>> address = contact.email
        >>> address = '*****@*****.**'
        >>> contacts_crypto = ContactsCrypto.objects.get(
        ...    contact=contact, encryption_software=encryption_software)
        >>> contacts_crypto is not None
        True
        >>> x = contact.delete()
        >>> contact = add(None, encryption_software)
        >>> contact is None
        True
        >>> contact = add(email, None)
        >>> contact.email = '*****@*****.**'
        >>> contact.user_name = 'Thomas'
        >>> get_contacts_crypto(email)
        []
        >>> x = contact.delete()
        >>> contact = add(None, None)
        >>> contact is None
        True
        >>> contact = add('*****@*****.**', None)
        >>> contact.email = '*****@*****.**'
        >>> contact.user_name = 'test.com domain key (system use only)'
        >>> x = contact.delete()
    '''

    try:
        new_contact = True
        user_name, email_address = parse_address(email)
        if email_address is None:
            contact = None
        else:
            try:
                contact = Contact.objects.get(email=email_address)
                new_contact = False

                # update the user name if it's been given and it differs from the name in the DB
                if user_name is not None and contact.user_name != user_name:
                    contact.user_name = user_name
                    contact.save()
                    log_message('updated {} user name to {}'.format(
                        email_address, user_name))
            except Contact.DoesNotExist:
                log_message('creating a contact for {}'.format(email_address))
                try:
                    if user_name is None or len(user_name.strip()) <= 0:
                        from goodcrypto.mail.message.metadata import is_metadata_address

                        user_name = email_address
                        i = user_name.find('@')

                        # handle domain keys specially
                        if is_metadata_address(email_address):
                            if i > 0:
                                email_domain = user_name[i + 1:]
                            user_name = '{} domain key (system use only)'.format(
                                email_domain)
                        else:
                            if i > 0:
                                user_name = user_name[:i]
                            user_name = user_name.replace('.', ' ').replace(
                                '-', ' ').replace('_', ' ')
                            user_name = capwords(user_name)
                except:
                    pass

                contact = Contact.objects.create(email=email_address,
                                                 user_name=user_name)
            except Exception:
                record_exception()
                log_message('EXCEPTION - see syr.exception.log for details')
                contact = None

        if encryption_program is None:
            log_message(
                "no encryption software defined so not creating contact's crytpo record for {}"
                .format(email))
        else:
            # add a corresponding record for the contact's crypto program
            encryption_software = crypto_software.get(encryption_program)
            if contact is None or encryption_software is None:
                log_message('no contact and/or encryption software defined')
            else:
                try:
                    contacts_crypto = ContactsCrypto.objects.get(
                        contact=contact,
                        encryption_software=encryption_software)
                    if (fingerprint is not None
                            and strip_fingerprint(contacts_crypto.fingerprint)
                            != strip_fingerprint(fingerprint)):
                        contacts_crypto.fingerprint = format_fingerprint(
                            fingerprint)
                        if email_in_domain(email):
                            if contacts.crypto.source is None:
                                contacts.crypto.source = constants.AUTO_GENERATED
                            if contacts_crypto.source == constants.AUTO_GENERATED:
                                contacts_crypto.verified = True
                        contacts_crypto.save()
                except ContactsCrypto.DoesNotExist:
                    # if the contact existed without any contact crypto, but was set
                    # to never encrypt and now we have a key, then change the
                    # outbound encrypt policy to the default
                    if (not new_contact and contact.outbound_encrypt_policy
                            == constants.NEVER_ENCRYPT_OUTBOUND):
                        contact.outbound_encrypt_policy = constants.DEFAULT_OUTBOUND_ENCRYPT_POLICY
                        contact.save()

                    contacts_crypto = add_contacts_crypto(
                        contact,
                        encryption_software,
                        fingerprint=fingerprint,
                        source=source)

                    log_message(
                        "created {} crypto record for {} with {} fingerprint: {}"
                        .format(encryption_software, email, fingerprint,
                                contacts_crypto is not None))
                except:
                    record_exception()
                    log_message(
                        'EXCEPTION - see syr.exception.log for details')

    except Exception:
        contact = None
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')

    return contact
Exemple #33
0
def _import_key_add_contact(keyblock, user_name, possible_fingerprint,
                            passcode, id_fingerprint_pairs, plugin):
    '''
        Import keys and create associated contact records.

    '''
    def update_contact(contact, crypto_name, fingerprint):
        fingerprint_ok = True
        try:
            if (user_name is not None
                    and (contact.user_name is None
                         or len(contact.user_name.strip()) <= 0)):
                contact.user_name = user_name.strip()
                contact.save()
                log_message('updated user name')
            else:
                log_message('user name: {}'.format(user_name))
                log_message('contact user name: {}'.format(contact.user_name))
            if possible_fingerprint is not None and len(
                    possible_fingerprint.strip()) > 0:
                if strip_fingerprint(possible_fingerprint).lower(
                ) == strip_fingerprint(fingerprint).lower():
                    contacts_crypto = contacts.get_contacts_crypto(
                        user_id, plugin.get_name())
                    contacts_crypto.verified = True
                    contacts_crypto.save()
                    log_message('verified fingerprint')
                else:
                    fingerprint_ok = False
                    log_message('possible fingerprint: {}'.format(
                        strip_fingerprint(possible_fingerprint).lower()))
                    log_message('imported fingerprint: {}'.format(
                        strip_fingerprint(fingerprint).lower()))
        except:
            record_exception()
            log_message('EXCEPTION - see syr.exception.log for details')

        return fingerprint_ok

    result_ok = True
    fingerprint_ok = True
    status = i18n('Imported key:')

    if plugin is None:
        result_ok = False
        fingerprint_ok = False
    else:
        result_ok = plugin.import_public(keyblock, id_fingerprint_pairs)
        log_message('imported key: {}'.format(result_ok))

    if result_ok:
        crypto_name = plugin.get_name()

        local_users = []
        for (user_id, fingerprint) in id_fingerprint_pairs:
            if email_in_domain(user_id):
                local_users.append(user_id)

            if user_name is not None:
                full_email = '{} <{}>'.format(user_name, user_id)
            else:
                full_email = user_id
            contact = contacts.add(full_email,
                                   crypto_name,
                                   passcode=passcode,
                                   source=MANUALLY_IMPORTED)
            if contact is None:
                log_message('unable to add contact for {}'.format(user_id))
            else:
                if not update_contact(contact, plugin.get_name(), fingerprint):
                    fingerprint_ok = False
                status += ' {}'.format(user_id)

        if len(local_users) > 0:
            for local_user in local_users:
                if not plugin.is_passcode_valid(
                        local_user, passcode, key_exists=True):
                    result_ok = False
                    status = (
                        'The passcode is not correct for the imported key for {email}'
                    ).format(email=user_id)
                    log_message(status)
                    break
            for (user_id, __) in id_fingerprint_pairs:
                if not result_ok:
                    contacts.delete(user_id)
                    log_message('deleted {}'.format(user_id))
    else:
        result_ok = False
        status = KEYBLOCK_INVALID

    log_message(status)

    return result_ok, status, fingerprint_ok