def process_mail_creation(self, session, tid, data): user_id = data['user']['id'] # Do not spool emails if the receiver has opted out of ntfns for this tip. if not data['tip']['enable_notifications']: log.debug("Discarding emails for %s due to receiver's preference.", user_id) return # https://github.com/globaleaks/GlobaLeaks/issues/798 # TODO: the current solution is global and configurable only by the admin sent_emails = self.state.get_mail_counter(user_id) if sent_emails >= self.state.tenant_cache[ tid].notification.notification_threshold_per_hour: log.debug( "Discarding emails for receiver %s due to threshold already exceeded for the current hour", user_id) return self.state.increment_mail_counter(user_id) if sent_emails >= self.state.tenant_cache[ tid].notification.notification_threshold_per_hour: log.info( "Reached threshold of %d emails with limit of %d for receiver %s", sent_emails, self.state.tenant_cache[tid].notification. notification_threshold_per_hour, user_id, tid=tid) # simply changing the type of the notification causes # to send the notification_limit_reached data['type'] = u'receiver_notification_limit_reached' data['node'] = self.serialize_config(session, 'node', tid, data['user']['language']) if data['node']['mode'] != u'whistleblowing.it': data['notification'] = self.serialize_config( session, 'notification', tid, data['user']['language']) else: data['notification'] = self.serialize_config( session, 'notification', 1, data['user']['language']) subject, body = Templating().get_mail_subject_and_body(data) # If the receiver has encryption enabled encrypt the mail body if data['user']['pgp_key_public']: pgpctx = PGPContext(self.state.settings.tmp_path) fingerprint = pgpctx.load_key( data['user']['pgp_key_public'])['fingerprint'] body = pgpctx.encrypt_message(fingerprint, body) session.add( models.Mail({ 'address': data['user']['mail_address'], 'subject': subject, 'body': body, 'tid': tid, }))
def process_mail_creation(self, session, tid, data): user_id = data['user']['id'] language = data['user']['language'] # Do not spool emails if the receiver has disabled notifications if not data['user']['notification'] or ('tip' in data and not data['tip']['enable_notifications']): log.debug("Discarding emails for %s due to receiver's preference.", user_id) return data['node'] = self.serialize_config(session, 'node', tid, language) data['submission_statuses'] = db_get_submission_statuses(session, tid, language) if data['node']['mode'] == 'default': data['notification'] = self.serialize_config(session, 'notification', tid, language) else: data['notification'] = self.serialize_config(session, 'notification', 1, language) subject, body = Templating().get_mail_subject_and_body(data) # If the receiver has encryption enabled encrypt the mail body if data['user']['pgp_key_public']: pgpctx = PGPContext(self.state.settings.tmp_path) fingerprint = pgpctx.load_key(data['user']['pgp_key_public'])['fingerprint'] body = pgpctx.encrypt_message(fingerprint, body) session.add(models.Mail({ 'address': data['user']['mail_address'], 'subject': subject, 'body': body, 'tid': tid, }))
def test_read_expirations(self): pgpctx = PGPContext() self.assertEqual(pgpctx.load_key(helpers.PGPKEYS['VALID_PGP_KEY1_PRV'])['expiration'], datetime.utcfromtimestamp(0)) self.assertEqual(pgpctx.load_key(helpers.PGPKEYS['EXPIRED_PGP_KEY_PUB'])['expiration'], datetime.utcfromtimestamp(1391012793))
def encrypt_file_with_pgp(state, fd, key, fingerprint, dest_path): """ Encrypt the file for a specific key """ pgpctx = PGPContext(state.settings.tmp_path) pgpctx.load_key(key) pgpctx.encrypt_file(fingerprint, fd, dest_path)
def format_and_send_mail(self, session, tid, user_desc, template_vars): subject, body = Templating().get_mail_subject_and_body(template_vars) if user_desc.get('pgp_key_public', ''): pgpctx = PGPContext(self.settings.tmp_path) fingerprint = pgpctx.load_key(user_desc['pgp_key_public'])['fingerprint'] body = pgpctx.encrypt_message(fingerprint, body) db_schedule_email(session, tid, user_desc['mail_address'], subject, body)
def schedule_exception_email(self, exception_text, *args): from globaleaks.transactions import schedule_email if not hasattr(self.tenant_cache[1], 'notification'): log.err( "Error: Cannot send mail exception before complete initialization." ) return if self.exceptions_email_count >= self.settings.exceptions_email_hourly_limit: return exception_text = (exception_text % args) if args else exception_text sha256_hash = sha256(bytes(exception_text)) if sha256_hash not in self.exceptions: self.exceptions[sha256_hash] = 0 self.exceptions[sha256_hash] += 1 if self.exceptions[sha256_hash] > 5: log.err( "Exception mail suppressed for (%s) [reason: threshold exceeded]", sha256_hash) return self.exceptions_email_count += 1 mail_subject = "GlobaLeaks Exception" delivery_list = self.tenant_cache[ 1].notification.exception_delivery_list if self.settings.devel_mode: mail_subject += " [%s]" % self.settings.developer_name delivery_list = [("*****@*****.**", '')] mail_body = bytes("Platform: %s (%s)\nVersion: %s\n\n%s" \ % (self.tenant_cache[1].hostname, self.tenant_cache[1].onionservice, __version__, exception_text)) for mail_address, pgp_key_public in delivery_list: # Opportunisticly encrypt the mail body. NOTE that mails will go out # unencrypted if one address in the list does not have a public key set. if pgp_key_public: pgpctx = PGPContext(self.settings.tmp_path) fingerprint = pgpctx.load_key(pgp_key_public)['fingerprint'] mail_body = pgpctx.encrypt_message(fingerprint, mail_body) # avoid waiting for the notification to send and instead rely on threads to handle it schedule_email(1, mail_address, mail_subject, mail_body)
def format_and_send_mail(self, session, tid, user_desc, template_vars): subject, body = Templating().get_mail_subject_and_body(template_vars) if user_desc.get('pgp_key_public', ''): pgpctx = PGPContext(self.settings.tmp_path) fingerprint = pgpctx.load_key(user_desc['pgp_key_public'])['fingerprint'] body = pgpctx.encrypt_message(fingerprint, body) session.add(models.Mail({ 'address': user_desc['mail_address'], 'subject': subject, 'body': body, 'tid': tid, }))
def test_encrypt_message(self): fake_receiver_desc = { 'pgp_key_public': helpers.PGPKEYS['VALID_PGP_KEY1_PUB'], 'pgp_key_fingerprint': u'BFB3C82D1B5F6A94BDAC55C6E70460ABF9A4C8C1', 'username': u'*****@*****.**', } pgpctx = PGPContext() pgpctx.load_key(helpers.PGPKEYS['VALID_PGP_KEY1_PRV']) encrypted_body = pgpctx.encrypt_message(fake_receiver_desc['pgp_key_fingerprint'], self.secret_content) self.assertEqual(str(pgpctx.gnupg.decrypt(encrypted_body)), self.secret_content)
def process_mail_creation(self, session, tid, data): user_id = data['user']['id'] # Do not spool emails if the receiver has opted out of ntfns for this tip. if not data['tip']['enable_notifications']: log.debug("Discarding emails for %s due to receiver's preference.", user_id) return # https://github.com/globaleaks/GlobaLeaks/issues/798 # TODO: the current solution is global and configurable only by the admin sent_emails = self.state.get_mail_counter(user_id) if sent_emails >= self.state.tenant_cache[tid].notification.notification_threshold_per_hour: log.debug("Discarding emails for receiver %s due to threshold already exceeded for the current hour", user_id) return self.state.increment_mail_counter(user_id) if sent_emails >= self.state.tenant_cache[tid].notification.notification_threshold_per_hour: log.info("Reached threshold of %d emails with limit of %d for receiver %s", sent_emails, self.state.tenant_cache[tid].notification.notification_threshold_per_hour, user_id, tid=tid) # simply changing the type of the notification causes # to send the notification_limit_reached data['type'] = u'receiver_notification_limit_reached' data['node'] = self.serialize_config(session, 'node', tid, data['user']['language']) if data['node']['mode'] != u'whistleblowing.it': data['notification'] = self.serialize_config(session, 'notification', tid, data['user']['language']) else: data['notification'] = self.serialize_config(session, 'notification', 1, data['user']['language']) subject, body = Templating().get_mail_subject_and_body(data) # If the receiver has encryption enabled encrypt the mail body if data['user']['pgp_key_public']: pgpctx = PGPContext(self.state.settings.tmp_path) fingerprint = pgpctx.load_key(data['user']['pgp_key_public'])['fingerprint'] body = pgpctx.encrypt_message(fingerprint, body) session.add(models.Mail({ 'address': data['user']['mail_address'], 'subject': subject, 'body': body, 'tid': tid, }))
def fsops_pgp_encrypt(state, sf, key, fingerprint): """ Encrypt the file for a speficic key return path of encrypted file, length of the encrypted file """ pgpctx = PGPContext(state.settings.tmp_path) pgpctx.load_key(key) with sf.open('r') as f: encrypted_file_path = os.path.join(os.path.abspath(state.settings.attachments_path), "pgp_encrypted-%s" % generateRandomKey(16)) _, encrypted_file_size = pgpctx.encrypt_file(fingerprint, f, encrypted_file_path) return encrypted_file_path, encrypted_file_size
def schedule_exception_email(self, exception_text, *args): if not hasattr(self.tenant_cache[1], 'notification'): log.err("Error: Cannot send mail exception before complete initialization.") return if self.exceptions_email_count >= self.settings.exceptions_email_hourly_limit: return exception_text = (exception_text % args) if args else exception_text sha256_hash = sha256(exception_text.encode()) if sha256_hash not in self.exceptions: self.exceptions[sha256_hash] = 0 self.exceptions[sha256_hash] += 1 if self.exceptions[sha256_hash] > 5: log.err("Exception mail suppressed for (%s) [reason: threshold exceeded]", sha256_hash) return self.exceptions_email_count += 1 mail_subject = "GlobaLeaks Exception" delivery_list = self.tenant_cache[1].notification.exception_delivery_list mail_body = text_type("Platform: %s\nHost: %s (%s)\nVersion: %s\n\n%s" % (self.tenant_cache[1].name, self.tenant_cache[1].hostname, self.tenant_cache[1].onionservice, __version__, exception_text)) for mail_address, pgp_key_public in delivery_list: # Opportunisticly encrypt the mail body. NOTE that mails will go out # unencrypted if one address in the list does not have a public key set. if pgp_key_public: pgpctx = PGPContext(self.settings.tmp_path) fingerprint = pgpctx.load_key(pgp_key_public)['fingerprint'] mail_body = pgpctx.encrypt_message(fingerprint, mail_body) # avoid waiting for the notification to send and instead rely on threads to handle it tw(db_schedule_email, 1, mail_address, mail_subject, mail_body)
def parse_pgp_options(state, user, request): """ Used for parsing PGP key infos and fill related user configurations. """ pgp_key_public = request['pgp_key_public'] remove_key = request['pgp_key_remove'] k = None if not remove_key and pgp_key_public: pgpctx = PGPContext(state.settings.tmp_path) k = pgpctx.load_key(pgp_key_public) if k is not None: user.pgp_key_public = pgp_key_public user.pgp_key_fingerprint = k['fingerprint'] user.pgp_key_expiration = k['expiration'] else: user.pgp_key_public = '' user.pgp_key_fingerprint = '' user.pgp_key_expiration = datetime_null()
def parse_pgp_options(user, request): """ Used for parsing PGP key infos and fill related user configurations. """ pgp_key_public = request['pgp_key_public'] remove_key = request['pgp_key_remove'] k = None if not remove_key and pgp_key_public: pgpctx = PGPContext(State.settings.tmp_path) k = pgpctx.load_key(pgp_key_public) if k is not None: user.pgp_key_public = pgp_key_public user.pgp_key_fingerprint = k['fingerprint'] user.pgp_key_expiration = k['expiration'] else: user.pgp_key_public = '' user.pgp_key_fingerprint = '' user.pgp_key_expiration = datetime_null()
def test_encrypt_file(self): file_src = os.path.join(os.getcwd(), 'test_plaintext_file.txt') file_dst = os.path.join(os.getcwd(), 'test_encrypted_file.txt') fake_receiver_desc = { 'pgp_key_public': helpers.PGPKEYS['VALID_PGP_KEY1_PRV'], 'pgp_key_fingerprint': u'BFB3C82D1B5F6A94BDAC55C6E70460ABF9A4C8C1', 'username': u'*****@*****.**', } # these are the same lines used in delivery.py pgpctx = PGPContext() pgpctx.load_key(helpers.PGPKEYS['VALID_PGP_KEY1_PRV']) with open(file_src, 'w+') as f: f.write(self.secret_content) f.seek(0) pgpctx.encrypt_file(fake_receiver_desc['pgp_key_fingerprint'], f, file_dst) with open(file_dst, 'r') as f: self.assertEqual(str(pgpctx.gnupg.decrypt_file(f)), self.secret_content)