commit 5595c9a5384d2ce1ec2e6420192254221ce7b774 Author: Isis Lovecruft isis@torproject.org Date: Sat Jun 7 02:41:15 2014 +0000
Add rudimentary (email_addr,gpg_key_fingerprint) whitelist feature.
This serves two purposes. It allows arbitrary whitelisting of certain email addresses for testing purposes, and once #9332 is fully implemented, will allow encrypted emails to be sent to these whitelisted addresses.
* FIXES part of #9332. --- bridgedb.conf | 8 +++++++- lib/bridgedb/email/autoresponder.py | 28 +++++++++++++++++++++++----- lib/bridgedb/email/server.py | 9 +++++++++ lib/bridgedb/test/email_helpers.py | 3 +++ 4 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/bridgedb.conf b/bridgedb.conf index eac6a34..85c86f1 100644 --- a/bridgedb.conf +++ b/bridgedb.conf @@ -15,11 +15,14 @@ # for details. # :copyright: (c) 2007-2013 The Tor Project, Inc. # (c) 2007-2013, all sentient entities within the AUTHORS file -# :version: 0.0.8 +# :version: 0.0.9 #=============================================================================== # # CHANGELOG: # ~~~~~~~~~~ +# Changes in version 0.0.9 - 2014-06-06 +# * ADD EMAIL_WHITELIST setting. +# # Changes in version 0.0.8 - 2014-05-14 # * CHANGE RECAPTCHA_PRIV_KEY to RECAPTCHA_SEC_KEY. # @@ -305,6 +308,9 @@ EMAIL_DOMAIN_MAP = {"mail.google.com": "gmail.com", EMAIL_DOMAIN_RULES = {'gmail.com': ["ignore_dots", "dkim"], 'yahoo.com': ["dkim"]}
+# A mapping of whitelisted email addresses to GnuPG key fingerprints: +EMAIL_WHITELIST = {} + # If there are any IPs in this list, only allow incoming connections from # those IPs. EMAIL_RESTRICT_IPS = [] diff --git a/lib/bridgedb/email/autoresponder.py b/lib/bridgedb/email/autoresponder.py index 9be7e7d..2556da9 100644 --- a/lib/bridgedb/email/autoresponder.py +++ b/lib/bridgedb/email/autoresponder.py @@ -433,15 +433,27 @@ class SMTPAutoresponder(smtp.SMTPClient): if not addrHeader: logging.warn("No Sender header on incoming mail.") else: + client = None try: - client = smtp.Address(addr.normalizeEmail( + normalized = addr.normalizeEmail( addrHeader, self.incoming.context.domainMap, - self.incoming.context.domainRules)) - except (addr.UnsupportedDomain, addr.BadEmail, - smtp.AddressError) as error: + self.incoming.context.domainRules) + client = smtp.Address(normalized) + except (addr.UnsupportedDomain, addr.BadEmail) as error: logging.warn(error) - else: + # Check if it was one of the whitelisted addresses, because + # then it would make sense that it couldn't be canonicalized: + try: client = smtp.Address(addrHeader) + except smtp.AddressError as error: pass + else: + if str(client) in self.incoming.context.whitelist.keys(): + logging.debug("Email address was whitelisted: %s." + % str(client)) + except smtp.AddressError as error: + logging.warn(error) + + if client: clients.append(client) return clients
@@ -594,6 +606,9 @@ class SMTPAutoresponder(smtp.SMTPClient): return False
logging.debug("Canonicalizing client email domain...") + # Allow whitelisted addresses through the canonicalization check: + if str(client) in self.incoming.context.whitelist.keys(): + canonicalFromEmail = client.domain # The client's address was already checked to see if it came from a # supported domain and is a valid email address in :meth:`getMailTo`, # so we should just be able to re-extract the canonical domain safely @@ -606,6 +621,9 @@ class SMTPAutoresponder(smtp.SMTPClient): # ``From:`` header should match: if self.incoming.canonicalFromSMTP != canonicalFromEmail: logging.error("SMTP/Email canonical domain mismatch!") + logging.debug("Canonical domain mismatch: %s != %s" + % (self.incoming.canonicalFromSMTP, + canonicalFromEmail)) return False
domainRules = self.incoming.context.domainRules.get( diff --git a/lib/bridgedb/email/server.py b/lib/bridgedb/email/server.py index 7c86395..62d3bff 100644 --- a/lib/bridgedb/email/server.py +++ b/lib/bridgedb/email/server.py @@ -91,6 +91,7 @@ class MailServerContext(object): self.domainRules = config.EMAIL_DOMAIN_RULES or {} self.domainMap = config.EMAIL_DOMAIN_MAP or {} self.canon = self.buildCanonicalDomainMap() + self.whitelist = config.EMAIL_WHITELIST or {}
self.gpgContext = getGPGContext(config)
@@ -276,6 +277,14 @@ class SMTPIncomingDelivery(smtp.SMTP): error. """ try: + if str(origin) in self.context.whitelist.keys(): + logging.warn("Got SMTP 'MAIL FROM:' whitelisted address: %s." + % str(origin)) + # We need to be certain later that when the fromCanonicalSMTP + # domain is checked again the email 'From:' canonical domain, + # that we allow whitelisted addresses through the check. + self.fromCanonicalSMTP = origin.domain + return origin if ((origin.domain == self.context.hostname) or (origin.domain == smtp.DNSNAME)): self.fromCanonicalSMTP = origin.domain diff --git a/lib/bridgedb/test/email_helpers.py b/lib/bridgedb/test/email_helpers.py index 59b3aa8..f171419 100644 --- a/lib/bridgedb/test/email_helpers.py +++ b/lib/bridgedb/test/email_helpers.py @@ -37,6 +37,7 @@ EMAIL_DOMAIN_RULES = { 'localhost': [], } EMAIL_DOMAINS = ["gmail.com", "example.com", "localhost"] +EMAIL_WHITELIST = {'white@list.ed': 'ABCD1234ABCD1234ABCD1234ABCD1234ABCD1234'} EMAIL_USERNAME = "bridges" EMAIL_SMTP_HOST = "127.0.0.1" EMAIL_SMTP_PORT = 25 @@ -54,6 +55,7 @@ EMAIL_GPG_SIGNING_KEY = %s EMAIL_DOMAIN_MAP = %s EMAIL_DOMAIN_RULES = %s EMAIL_DOMAINS = %s +EMAIL_WHITELIST = %s EMAIL_USERNAME = %s EMAIL_SMTP_HOST = %s EMAIL_SMTP_PORT = %s @@ -69,6 +71,7 @@ EMAIL_PORT = %s repr(EMAIL_DOMAIN_MAP), repr(EMAIL_DOMAIN_RULES), repr(EMAIL_DOMAINS), + repr(EMAIL_WHITELIST), repr(EMAIL_USERNAME), repr(EMAIL_SMTP_HOST), repr(EMAIL_SMTP_PORT),