[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [bridgedb/master] 5463 - Adds GPG clearsign to email distributor.
commit ca5398470108b8f9444b5c0f823411eb735dcfd0
Author: aagbsn <aagbsn@xxxxxxxx>
Date: Wed Jun 20 05:55:49 2012 -0700
5463 - Adds GPG clearsign to email distributor.
Two new configuration options are added to bridgedb.conf:
EMAIL_GPG_SIGNING_ENABLED
EMAIL_GPG_SIGNING_KEY
The former may be either True or False, and the latter must
point to the ascii-armored key file. The keyfile must not be
passphrase protected.
The gpgme library will add the secret key to the secret key ring of
user who runs BridgeDB.
---
README | 16 ++++++++--
bridgedb.conf | 4 ++
lib/bridgedb/Main.py | 2 +
lib/bridgedb/Server.py | 79 +++++++++++++++++++++++++++++++++++++++++++----
4 files changed, 91 insertions(+), 10 deletions(-)
diff --git a/README b/README
index 6ab1545..baca007 100644
--- a/README
+++ b/README
@@ -20,10 +20,11 @@ To set up:
- A recaptcha.net account is required.
- Install these required packages:
- Debian: apt-get install python-recaptcha python-beautifulsoup
- - Python: pip install recaptcha-client BeautifulSoup
+ python-gpgme
+ - Python: pip install recaptcha-client BeautifulSoup pygpgme
- Others: http://pypi.python.org/pypi/recaptcha-client
- : http://pypi.python.org/pypi/BeautifulSoup
-
+ http://pypi.python.org/pypi/BeautifulSoup
+ http://pypi.python.org/pypi/pygpgme
To re-generate and update the i18n files (in case translated strings
have changed in BridgeDB):
@@ -67,6 +68,15 @@ To indicate which bridges are blocked:
- If this file is present, bridgedb will filter blocked bridges from responses
- For GeoIP support make sure to install Maxmind GeoIP
+To sign emails with gpg:
+ - Add these two options to your bridgedb.conf:
+
+ EMAIL_GPG_SIGNING_ENABLED, EMAIL_GPG_SIGNING_KEY
+
+ The former may be either True or False, and the latter must
+ point to the ascii-armored key file. The keyfile must not be
+ passphrase protected.
+
To update the SQL schema:
- Install sqlite3:
- Debian: apt-get install sqlite3
diff --git a/bridgedb.conf b/bridgedb.conf
index f044ee6..2b97d78 100644
--- a/bridgedb.conf
+++ b/bridgedb.conf
@@ -146,6 +146,10 @@ EMAIL_N_BRIDGES_PER_ANSWER=3
# once we have the vidalia/tor interaction fixed for everbody.
EMAIL_INCLUDE_FINGERPRINTS=False
+# Configuration options for GPG signed messages
+EMAIL_GPG_SIGNING_ENABLED = False
+EMAIL_GPG_SIGNING_KEY = ''
+
#==========
# Options related to unallocated bridges.
diff --git a/lib/bridgedb/Main.py b/lib/bridgedb/Main.py
index 69a4761..977f37a 100644
--- a/lib/bridgedb/Main.py
+++ b/lib/bridgedb/Main.py
@@ -97,6 +97,8 @@ CONFIG = Conf(
EMAIL_INCLUDE_FINGERPRINTS = False,
EMAIL_SMTP_HOST="127.0.0.1",
EMAIL_SMTP_PORT=25,
+ EMAIL_GPG_SIGNING_ENABLED = False,
+ EMAIL_GPG_SIGNING_KEY = "bridgedb-gpg.sec",
RESERVED_SHARE=2,
diff --git a/lib/bridgedb/Server.py b/lib/bridgedb/Server.py
index 8ce9ce2..5c4641c 100644
--- a/lib/bridgedb/Server.py
+++ b/lib/bridgedb/Server.py
@@ -6,7 +6,7 @@
This module implements the web and email interfaces to the bridge database.
"""
-from cStringIO import StringIO
+from StringIO import StringIO
import MimeWriter
import rfc822
import time
@@ -31,12 +31,15 @@ from random import randint
from bridgedb.Raptcha import Raptcha
import base64
import textwrap
+
from ipaddr import IPv4Address, IPv6Address
from bridgedb.Dist import BadEmail, TooSoonEmail, IgnoreEmail
from bridgedb.Filters import filterBridgesByIP6
from bridgedb.Filters import filterBridgesByIP4
from bridgedb.Filters import filterBridgesByTransport
+
+import gpgme
try:
import GeoIP
@@ -458,7 +461,8 @@ def getMailResponse(lines, ctx):
# Compose a warning email
# MAX_EMAIL_RATE is in seconds, convert to hours
body = buildSpamWarningTemplate(t) % (bridgedb.Dist.MAX_EMAIL_RATE / 3600)
- return composeEmail(ctx.fromAddr, clientAddr, subject, body, msgID)
+ return composeEmail(ctx.fromAddr, clientAddr, subject, body, msgID,
+ gpgContext=ctx.gpgContext)
except IgnoreEmail, e:
logging.info("Got a mail too frequently; ignoring %r: %s.",
@@ -483,7 +487,8 @@ def getMailResponse(lines, ctx):
body = buildMessageTemplate(t) % answer
# Generate the message.
- return composeEmail(ctx.fromAddr, clientAddr, subject, body, msgID)
+ return composeEmail(ctx.fromAddr, clientAddr, subject, body, msgID,
+ gpgContext=ctx.gpgContext)
def buildMessageTemplate(t):
msg_template = t.gettext(I18n.BRIDGEDB_TEXT[5]) + "\n\n" \
@@ -575,6 +580,9 @@ class MailContext:
# The number of bridges to send for each email.
self.N = cfg.EMAIL_N_BRIDGES_PER_ANSWER
+ # Initialize a gpg context or set to None for backward compatibliity.
+ self.gpgContext = getGPGContext(cfg)
+
self.cfg = cfg
class MailMessage:
@@ -690,7 +698,9 @@ def getCCFromRequest(request):
return path.lower()
return None
-def composeEmail(fromAddr, clientAddr, subject, body, msgID=False):
+def composeEmail(fromAddr, clientAddr, subject, body, msgID=False,
+ gpgContext=None):
+
f = StringIO()
w = MimeWriter.MimeWriter(f)
w.addheader("From", fromAddr)
@@ -702,10 +712,65 @@ def composeEmail(fromAddr, clientAddr, subject, body, msgID=False):
w.addheader("In-Reply-To", msgID)
w.addheader("Date", twisted.mail.smtp.rfc822date())
mailbody = w.startbody("text/plain")
- mailbody.write(body)
- f.seek(0)
- logging.debug(f.readlines())
+ # gpg-clearsign messages
+ if gpgContext:
+ signature = StringIO()
+ plaintext = StringIO(body)
+ sigs = gpgContext.sign(plaintext, signature, gpgme.SIG_MODE_CLEAR)
+ if (len(sigs) != 1):
+ logging.warn('Failed to sign message!')
+ signature.seek(0)
+ [mailbody.write(l) for l in signature]
+ else:
+ mailbody.write(body)
+
f.seek(0)
logging.info("Email looks good; we should send an answer.")
return clientAddr, f
+
+def getGPGContext(cfg):
+ """ Returns a gpgme Context() with the signers initialized by the keyfile
+ specified by the option EMAIL_GPG_SIGNING_KEY in bridgedb.conf, or None
+ if the option was not enabled or unable to initialize.
+
+ The key should not be protected by a passphrase.
+ """
+ try:
+ # must have enabled signing and specified a key file
+ if not cfg.EMAIL_GPG_SIGNING_ENABLED or not cfg.EMAIL_GPG_SIGNING_KEY:
+ return None
+ except AttributeError:
+ return None
+
+ try:
+ # import the key
+ keyfile = open(cfg.EMAIL_GPG_SIGNING_KEY)
+ logging.debug("Opened GPG Keyfile %s" % cfg.EMAIL_GPG_SIGNING_KEY)
+ ctx = gpgme.Context()
+ result = ctx.import_(keyfile)
+
+ assert len(result.imports) == 1
+ fingerprint = result.imports[0][0]
+ keyfile.close()
+ logging.debug("GPG Key with fingerprint %s imported" % fingerprint)
+
+ ctx.armor = True
+ ctx.signers = [ctx.get_key(fingerprint)]
+ assert len(ctx.signers) == 1
+
+ # make sure we can sign
+ message = StringIO('Test')
+ signature = StringIO()
+ new_sigs = ctx.sign(message, signature, gpgme.SIG_MODE_CLEAR)
+ assert len(new_sigs) == 1
+
+ # return the ctx
+ return ctx
+
+ except IOError, e:
+ # exit noisily if keyfile not found
+ exit(e)
+ except AssertionError:
+ # exit noisily if key does not pass tests
+ exit('Invalid GPG Signing Key')
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits