[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[PATCH] adding --passphrase-fd to 'mixminion generate-surb'
Howdy all. I'm working on an anti-spam SMTP replacement called PETmail [1]
(which will be presented at CodeCon next month). I'm in the process of adding
mixminion support to it, and have a few small patches to make it easier for
my agent program to drive mixminion as a child process. The patches are
attached below.
The first is just to emit the version number and testing-warning on stderr
instead of stdout, so that they are not mixed up with the SURB or decoded
message being emitted on stdout. It looks like there is a --quiet argument
intended to silence these messages, but passing that argument causes a
failure later, as it doesn't appear in any of the getopt lists.
The second adds a --passphrase-fd option to the 'generate-surb' command, and
works just like gpg's option of the same name. This makes it possible to run
mixminion as a child process to generate a SURB, passing the keyring
passphrase in on a spare filehandle, perhaps fd 3. (PETmail will publish
these SURBs through a "Transport Server", described vaguely in the paper
referenced below, to accomplish the nymserver-like property of allowing
repeated access to the same anonymous recipient).
It also fixes what I think is a minor bug in
MixminionClient.generateReplyBlock, in which a failed password request (such
as one which hit the retry limit) leads to a cryptic exception inside a
crypto function instead of a more useful "bad password" UIError.
In addition, I am looking at adding something like a --surb-fd to 'mixmaster
send', which would make it possible to send messages through a SURB without
having to create a temporary file first. At the moment you can pass the
message body or the SURB in on stdin, but not both. I haven't investigated
how to actually implement this, though.. I suspect it will be trickier than
--passphrase-fd was.
cheers,
-Brian
[1]: http://www.lothar.com/tech/spam/index.html
Index: lib/mixminion/Main.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Main.py,v
retrieving revision 1.66
diff -u -r1.66 Main.py
--- lib/mixminion/Main.py 14 Dec 2003 01:43:38 -0000 1.66
+++ lib/mixminion/Main.py 15 Jan 2004 10:02:48 -0000
@@ -265,8 +265,8 @@
if args[1] not in ('unittests', 'benchmarks', 'version') and \
'--quiet' not in args and '-Q' not in args:
import mixminion
- print "Mixminion version %s" % mixminion.__version__
- print "This software is for testing purposes only."\
+ print >>sys.stderr, "Mixminion version %s" % mixminion.__version__
+ print >>sys.stderr, "This software is for testing purposes only."\
" Anonymity is not guaranteed."
# Read the 'common' module to get the UIError class. To simplify
Index: lib/mixminion/ClientMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientMain.py,v
retrieving revision 1.149
diff -u -r1.149 ClientMain.py
--- lib/mixminion/ClientMain.py 8 Jan 2004 22:35:24 -0000 1.149
+++ lib/mixminion/ClientMain.py 15 Jan 2004 10:07:53 -0000
@@ -194,7 +194,7 @@
# keys: A ClientKeyring object.
# queue: A ClientQueue object.
# surbLogFilename: The filename used by the SURB log.
- def __init__(self, conf):
+ def __init__(self, conf, password_fileno=None):
"""Create a new MixminionClient with a given configuration"""
self.config = conf
@@ -202,7 +202,7 @@
userdir = self.config['User']['UserDir']
createPrivateDir(userdir)
keyDir = os.path.join(userdir, "keys")
- self.pwdManager = mixminion.ClientUtils.CLIPasswordManager()
+ self.pwdManager = mixminion.ClientUtils.CLIPasswordManager(password_fileno)
self.keys = ClientKeyring(keyDir, self.pwdManager)
self.surbLogFilename = os.path.join(userdir, "surbs", "log")
@@ -308,6 +308,8 @@
"""
#XXXX write unit tests
key = self.keys.getSURBKey(name=name, create=1)
+ if not key:
+ raise UIError("unable to get SURB key")
exitType, exitInfo, _ = address.getRouting()
block = mixminion.BuildMessage.buildReplyBlock(
@@ -781,6 +783,7 @@
self.configFile = None
self.verbose = 0
self.download = None
+ self.password_fileno = None
self.path = None
self.nHops = None
@@ -848,6 +851,11 @@
self.lifetime = int(v)
except ValueError:
raise UsageError("%s expects an integer"%o)
+ elif o in ('--passphrase-fd',):
+ try:
+ self.password_fileno = int(v)
+ except ValueError:
+ raise UsageError("%s expects an integer"%o)
elif o in ('--queue',):
self.forceQueue = 1
elif o in ('--no-queue',):
@@ -890,7 +898,7 @@
if self.wantClient:
assert self.wantConfig
LOG.debug("Configuring client")
- self.client = MixminionClient(self.config)
+ self.client = MixminionClient(self.config, self.password_fileno)
if self.wantClientDirectory:
assert self.wantConfig
@@ -1564,6 +1572,8 @@
of ascii mode.
-n <N>, --count=<N> Generate <N> reply blocks. (Defaults to 1.)
--identity=<name> Specify a pseudonymous identity.
+ --passphrase-fd=<N> Read passphrase from file descriptor N instead
+ of asking on the console.
EXAMPLES:
Generate a reply block to deliver messages to the address given in
@@ -1591,7 +1601,7 @@
def generateSURB(cmd, args):
options, args = getopt.getopt(args, "hvf:D:t:H:P:o:bn:",
['help', 'verbose', 'config=', 'download-directory=',
- 'to=', 'hops=', 'path=', 'lifetime=',
+ 'to=', 'hops=', 'path=', 'lifetime=', 'passphrase-fd=',
'output=', 'binary', 'count=', 'identity='])
outputFile = '-'
Index: lib/mixminion/ClientUtils.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientUtils.py,v
retrieving revision 1.16
diff -u -r1.16 ClientUtils.py
--- lib/mixminion/ClientUtils.py 3 Jan 2004 07:35:23 -0000 1.16
+++ lib/mixminion/ClientUtils.py 15 Jan 2004 10:07:53 -0000
@@ -37,6 +37,8 @@
"""A PasswordManager keeps track of a set of named passwords, so that
a user never has to enter any password more than once. This is an
abstract class."""
+ do_retry = 1
+
## Fields
# passwords: map from password name to string value of the password.
def __init__(self):
@@ -83,6 +85,8 @@
if confirmFn(pwd):
self.passwords[name] = pwd
return pwd
+ if not self.do_retry:
+ break
maxTries -= 1
pmt = "Incorrect password. "+prompt
@@ -95,13 +99,29 @@
class CLIPasswordManager(PasswordManager):
"""Impementation of PasswordManager that asks for passwords from the
command line."""
- def __init__(self):
+ def __init__(self, password_fileno=None):
PasswordManager.__init__(self)
+ self.password_fileno = password_fileno
+ if self.password_fileno != None:
+ self.do_retry = 0
def _getPassword(self, name, prompt):
+ if self.password_fileno != None:
+ return getPassword_fd(self.password_fileno)
return getPassword_term(prompt)
def _getNewPassword(self, name, prompt):
+ if self.password_fileno != None:
+ return getPassword_fd(self.password_fileno)
return getNewPassword_term(prompt)
+def getPassword_fd(fileno):
+ pw = ""
+ while 1:
+ chunk = os.read(fileno, 1024) # read from --password-fd filehandle
+ if not chunk:
+ break
+ pw += chunk
+ return pw
+
def getPassword_term(prompt):
"""Read a password from the console, then return it. Use the string
'message' as a prompt."""