[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[minion-cvs] Add support for reading passphrases and SURBs from the ...



Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.mit.edu:/tmp/cvs-serv32580/lib/mixminion

Modified Files:
	ClientMain.py ClientUtils.py Main.py 
Log Message:
Add support for reading passphrases and SURBs from the arbitrary fds.

Also clean up some error messages.

Based on a patch submitted by Brian Warner.


Index: ClientMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientMain.py,v
retrieving revision 1.149
retrieving revision 1.150
diff -u -d -r1.149 -r1.150
--- ClientMain.py	8 Jan 2004 22:35:24 -0000	1.149
+++ ClientMain.py	15 Jan 2004 21:03:25 -0000	1.150
@@ -12,7 +12,7 @@
 import os
 import sys
 import time
-from types import ListType
+from types import IntType, StringType
 
 import mixminion.BuildMessage
 import mixminion.ClientUtils
@@ -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,10 @@
         userdir = self.config['User']['UserDir']
         createPrivateDir(userdir)
         keyDir = os.path.join(userdir, "keys")
-        self.pwdManager = mixminion.ClientUtils.CLIPasswordManager()
+        if password_fileno is None:
+            self.pwdManager = mixminion.ClientUtils.CLIPasswordManager()
+        else:
+            self.pwdManager = mixminion.ClientUtils.FDPasswordManager(password_fileno)
         self.keys = ClientKeyring(keyDir, self.pwdManager)
         self.surbLogFilename = os.path.join(userdir, "surbs", "log")
 
@@ -308,6 +311,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(
@@ -701,7 +706,7 @@
              -D | --download-directory : force/disable directory downloading.
           PATH-RELATED
              -t | --to : specify an exit address
-             -R | --reply-block : specify a reply block
+             -R | --reply-block | --reply-block-fd : specify a reply block
              -H | --hops : specify a path length
              -P | --path : specify a literal path.
           REPLY PATH ONLY
@@ -726,7 +731,7 @@
     #  nHops: number of hops, or None.
     #  address: exit address, or None.
     #  lifetime: SURB lifetime, or None.
-    #  replyBlockFiles: list of SURB filenames.
+    #  replyBlockFiles: list of SURB filenames. DOCDOC
     #  configFile: Filename of configuration file, or None.
     #  forceQueue: true if "--queue" is set.
     #  forceNoQueue: true if "--no-queue" is set.
@@ -781,12 +786,13 @@
         self.configFile = None
         self.verbose = 0
         self.download = None
+        self.password_fileno = None
 
         self.path = None
         self.nHops = None
         self.exitAddress = None
         self.lifetime = None
-        self.replyBlockFiles = []
+        self.replyBlockSources = [] #DOCDOC int is fd, str is filename
 
         self.forceQueue = None
         self.forceNoQueue = None
@@ -824,7 +830,12 @@
                     raise UsageError(str(e))
             elif o in ('-R', '--reply-block'):
                 assert wantForwardPath
-                self.replyBlockFiles.append(v)
+                self.replyBlockSources.append(v)
+            elif o == '--reply-block-fd':
+                try:
+                    self.replyBlockSources.append(int(v))
+                except ValueError:
+                    raise UIError("%s expects an integer"%o)
             elif o in ('-H', '--hops'):
                 assert wantForwardPath or wantReplyPath
                 if self.nHops is not None:
@@ -848,6 +859,12 @@
                     self.lifetime = int(v)
                 except ValueError:
                     raise UsageError("%s expects an integer"%o)
+            elif o in ('--passphrase-fd',):
+                #DOCDOC
+                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 +907,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
@@ -925,18 +942,25 @@
                 self.exitAddress = mixminion.ClientDirectory.parseAddress(address)
             except ParseError, e:
                 raise UIError("Error in SURBAddress: %s" % e)
-        elif self.exitAddress is None and self.replyBlockFiles == []:
+        elif self.exitAddress is None and self.replyBlockSources == []:
             raise UIError("No recipients specified; exiting. (Try using "
                           "-t <recipient-address>")
-        elif self.exitAddress is not None and self.replyBlockFiles:
+        elif self.exitAddress is not None and self.replyBlockSources:
             raise UIError("Cannot use both a recipient and a reply block")
-        elif self.replyBlockFiles:
+        elif self.replyBlockSources:
             useRB = 1
             surbs = []
-            for fn in self.replyBlockFiles:
-                if fn == '-':
+            for fn in self.replyBlockSources:
+                if isinstance(fn, IntType):
+                    f = os.fdopen(fn, 'rb')
+                    try:
+                        s = f.read()
+                    finally:
+                        f.close()
+                elif fn == '-':
                     s = sys.stdin.read()
                 else:
+                    assert isinstance(fn, StringType)
                     s = readFile(fn, 1)
                 try:
                     if stringContains(s,
@@ -1010,6 +1034,7 @@
                              packet, then deliver multiple fragmented packets
                              to the recipient instead of having the server
                              reassemble the message.
+  --reply-block-fd=<N>       DOCDOC
 %(extra)s
 
 EXAMPLES:
@@ -1076,10 +1101,10 @@
     # Parse and validate our options.
     options, args = getopt.getopt(args, "hvf:D:t:H:P:R:i:",
              ["help", "verbose", "config=", "download-directory=",
-              "to=", "hops=", "path=", "reply-block=",
+              "to=", "hops=", "path=", "reply-block=", "reply-block-fd=",
               "input=", "queue", "no-queue",
               "subject=", "from=", "in-reply-to=", "references=",
-              "deliver-fragments" ])
+              "deliver-fragments", ])
 
     if not options:
         sendUsageAndExit(cmd)
@@ -1124,7 +1149,7 @@
     except MixError, e:
         raise UIError("Invalid headers: %s"%e)
 
-    if inFile in (None, '-') and '-' in parser.replyBlockFiles:
+    if inFile in (None, '-') and '-' in parser.replyBlockSources:
         raise UIError(
             "Can't read both message and reply block from stdin")
 
@@ -1475,6 +1500,8 @@
                              overcompressed.
   -o <file>, --output=<file> Write the results to <file> rather than stdout.
   -i <file>, --input=<file>  Read the results from <file>.
+  --passphrase-fd=<N>        Read passphrase from file descriptor N instead
+                               of asking on the console.
 
 EXAMPLES:
   Decode message(s) stored in 'NewMail', writing the result to stdout.
@@ -1487,7 +1514,7 @@
     """[Entry point] Decode a message."""
     options, args = getopt.getopt(args, "hvf:o:Fi:",
           ['help', 'verbose', 'config=',
-           'output=', 'force', 'input='])
+           'output=', 'force', 'input=', 'passphrase-fd=',])
 
     outputFile = '-'
     inputFile = None
@@ -1564,6 +1591,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 +1620,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: ClientUtils.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientUtils.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- ClientUtils.py	3 Jan 2004 07:35:23 -0000	1.16
+++ ClientUtils.py	15 Jan 2004 21:03:26 -0000	1.17
@@ -39,6 +39,9 @@
        abstract class."""
     ## Fields
     # passwords: map from password name to string value of the password.
+    # do_retry: static field: should we keep asking for a password until
+    #    one is correct?
+    do_retry = 1
     def __init__(self):
         """Create a new PasswordManager"""
         self.passwords = {}
@@ -83,6 +86,8 @@
             if confirmFn(pwd):
                 self.passwords[name] = pwd
                 return pwd
+            if not self.do_retry:
+                break
             maxTries -= 1
             pmt = "Incorrect password. "+prompt
 
@@ -95,13 +100,37 @@
 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)
     def _getPassword(self, name, prompt):
         return getPassword_term(prompt)
     def _getNewPassword(self, name, prompt):
         return getNewPassword_term(prompt)
 
+class FDPasswordManager(PasswordManager):
+    """Impementation of PasswordManager that asks for passwords from a
+       specified fileno."""
+    do_retry = 0
+    def __init__(self, password_fileno=None):
+        PasswordManager.__init__(self)
+        self.password_fileno = password_fileno
+    def _getPassword(self, name, prompt):
+        return getPassword_fd(self.password_fileno)
+    def _getNewPassword(self, name, prompt):
+        return getPassword_fd(self.password_fileno)
+
+def getPassword_fd(fileno):
+    """Read a password from a specified fileno."""
+    pw = ""
+    while 1:
+        chunk = os.read(fileno, 1024) # read from --password-fd filehandle
+        if not chunk:
+            break
+        pw += chunk
+    # Strip trailing endline from password, if any.  
+    if pw.endswith("\n"): pw = pw[:-1]
+    return pw
+
 def getPassword_term(prompt):
     """Read a password from the console, then return it.  Use the string
        'message' as a prompt."""

Index: Main.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Main.py,v
retrieving revision 1.66
retrieving revision 1.67
diff -u -d -r1.66 -r1.67
--- Main.py	14 Dec 2003 01:43:38 -0000	1.66
+++ Main.py	15 Jan 2004 21:03:26 -0000	1.67
@@ -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