try to properly handle some more mime stuff.
authorPeter Palfrader <peter@palfrader.org>
Sat, 21 May 2011 12:52:28 +0000 (14:52 +0200)
committerPeter Palfrader <peter@palfrader.org>
Sat, 21 May 2011 12:52:28 +0000 (14:52 +0200)
- use email module instead of deprecated mimetools and multifile modules
- changes: sigcheck ud-echelon ud-mailgate userdir_gpg.py
- move ud-echelon and sigcheck to GPGCheckSig2 interface.

debian/changelog
sigcheck
ud-echelon
ud-mailgate
userdir_gpg.py

index 8ad08cd..874c268 100644 (file)
@@ -17,8 +17,12 @@ userdir-ldap (0.3.7X) Xnstable; urgency=low
     - Do not mess with sudo passwords if nothing changed.
   * templates/change-reply: say a word about subjects in mail to admin@db.
   * move gpgwrapper to unmaintained/ - it is now using obsolete interfaces.
+  * try to properly handle some more mime stuff.
+    - use email module instead of deprecated mimetools and multifile modules
+    - changes: sigcheck ud-echelon ud-mailgate userdir_gpg.py
+  * move ud-echelon and sigcheck to GPGCheckSig2 interface.
 
- -- Peter Palfrader <weasel@debian.org>  Sat, 21 May 2011 14:49:52 +0200
+ -- Peter Palfrader <weasel@debian.org>  Sat, 21 May 2011 14:50:32 +0200
 
 userdir-ldap (0.3.78) unstable; urgency=low
 
index 1ba2a8d..fc16d1f 100755 (executable)
--- a/sigcheck
+++ b/sigcheck
@@ -26,6 +26,7 @@
 
 import sys, traceback, time, os;
 import pwd, getopt;
+import email, email.parser
 from userdir_gpg import *;
 
 EX_TEMPFAIL = 75;
@@ -124,17 +125,16 @@ try:
    if ReplayCacheFile != None:
       ErrMsg = "Failed to initialize the replay cache:";
       RC = ReplayCache(ReplayCacheFile);
-      RC.Clean();
-   
+
    # Get the email 
    ErrType = EX_PERMFAIL;
    ErrMsg = "Failed to understand the email or find a signature:";
-   Email = mimetools.Message(sys.stdin,0);
-   MsgID = Email.getheader("Message-ID");
+   email = email.parser.Parser().parse(sys.stdin);
+   MsgID = email["Message-ID"]
+
    print "Inspecting message %s"%MsgID;
    verbmsg("Processing message %s" % MsgID)
-   Msg = GetClearSig(Email,1);
-   # print Msg
+   Msg = GetClearSig(email,1);
    if AllowMIME == 0 and Msg[1] != 0:
       raise Error, "PGP/MIME disallowed";
   
@@ -144,34 +144,28 @@ try:
    
    # Check the signature
    ErrMsg = "Unable to check the signature or the signature was invalid:";
-   Res = GPGCheckSig(Msg[0]);
+   pgp = GPGCheckSig2(Msg[0])
 
-   if Res[0] != None:
-      raise Error, Res[0];
-      
-   if Res[3] == None:
-      raise Error, "Null signature text";
+   if not pgp.ok:
+      raise UDFormatError, pgp.why
+   if pgp.text is None:
+      raise UDFormatError, "Null signature text"
 
    # Check the signature against the replay cache
    if ReplayCacheFile != None:
-      ErrMsg = "The replay cache rejected your message. Check your clock!";
-      Rply = RC.Check(Res[1]);
-      if Rply != None:
-         raise Error, Rply;
-      RC.Add(Res[1]);
-      RC.close();
+      RC.process(pgp.sig_info)
 
    # Do LDAP stuff
    if LDAPDn != None:
-      CheckLDAP(Res[2][1]);
-         
+      CheckLDAP(pgp.key_fpr)
+
    ErrMsg = "Verifying message:";
    if Phrases != None:
       F = open(Phrases,"r");
       while 1:
          Line = F.readline();
          if Line == "": break;
-         if Res[3].find(Line.strip()) == -1:
+         if pgp.text.find(Line.strip()) == -1:
              raise Error,"Phrase '%s' was not found" % (Line.strip())
       
 except:
index 6c07a0a..2c0f984 100755 (executable)
@@ -2,6 +2,7 @@
 # -*- mode: python -*-
 import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, os, getopt;
 import pwd
+import email, email.parser
 from userdir_gpg import *;
 from userdir_ldap import *;
 
@@ -10,10 +11,10 @@ EX_PERMFAIL = 65;      # EX_DATAERR
 Debug = None;
 
 # Try to extract a key fingerprint from a PGP siged message
-def TryGPG(Email):
+def TryGPG(email):
    # Try to get a pgp text
    try:
-      Msg = GetClearSig(Email);
+      Msg = GetClearSig(email);
    except:
       # Log an exception.. but continue. This is to deal with 'sort of' 
       # PGP-MIME things
@@ -25,26 +26,26 @@ def TryGPG(Email):
    if Msg[0].find("-----BEGIN PGP SIGNED MESSAGE-----") == -1:
       return None;
 
-   Res = GPGCheckSig(Msg[0]);
+   pgp = GPGCheckSig2(Msg[0]);
 
    # Failed to find a matching sig
-   if Res[0] != None:
-      S = "%s: %s -> PGP Checking failed '%s': %s %s\n" %(Now,MsgID,Email.getheader("From"),str(Res[0]),str(Res[2]));
+   if not pgp.ok:
+      S = "%s: %s -> PGP Checking failed '%s': %s %s\n" %(Now,MsgID,email["From"],str(pgp.why),str(pgp.key_info));
       ErrLog.write(S);
       return None;
       
    # Search for the matching key fingerprint
-   Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + Res[2][1]);
+   Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + pgp.key_fpr);
    if len(Attrs) == 0:
       return None;
    if len(Attrs) != 1:
       raise Error, "Oddly your key fingerprint is assigned to more than one account.."
    
-   return (Attrs[0][1]["uid"][0],"PGP",FormatPGPKey(Res[2][1]));
+   return (Attrs[0][1]["uid"][0],"PGP",FormatPGPKey(pgp.key_fpr));
 
 # Try to guess the name from the email address
-def TryMatcher(Email):
-   Sender = Email.getheader("From");
+def TryMatcher(email):
+   Sender = email["From"];
    if Sender == None:
       return None;
       
@@ -87,9 +88,9 @@ try:
    # Get the email 
    ErrType = EX_PERMFAIL;
    ErrMsg = "Failed to understand the email or find a signature:";
-   Email = mimetools.Message(sys.stdin,0);
-   MsgID = Email.getheader("Message-ID");
-   
+   email = email.parser.Parser().parse(sys.stdin);
+   MsgID = email["Message-ID"]
+
    # Connect to the ldap server
    ErrType = EX_TEMPFAIL;
    ErrMsg = "An error occured while performing the LDAP lookup";
@@ -106,14 +107,15 @@ try:
    # Try to decode
    ErrType = EX_TEMPFAIL;
    ErrMsg = "An error occured while trying GPG decoding";
-   User = TryGPG(Email);
+   User = TryGPG(email);
    if User == None:
       ErrMsg = "An error occured while trying Matcher decoding";
-      User = TryMatcher(Email);
+      User = TryMatcher(email);
 
    # Get any mailing list information   
-   List = Email.getheader("X-Mailing-List");
-   if List == None:
+   if 'X-Mailing-List' in email:
+      List = email['X-Mailing-List']
+   else:
       List = "-";
 
    # Tada, write a log message
@@ -127,7 +129,7 @@ try:
       else:
          print Rec;
    else:
-      User = ("-","UKN",Email.getheader("From"));
+      User = ("-","UKN",email("From"));
       Msg = "[%s] \"%s\" \"%s\" \"%s\""%(Now,User[2],List,MsgID);
       MainLog.write("%s %s %s\n"%(User[0],User[1],Msg));
 
index 30c9514..8439037 100755 (executable)
@@ -10,6 +10,7 @@
 import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, os, commands
 import pwd, tempfile
 import subprocess
+import email, email.parser
 
 from userdir_gpg import *
 from userdir_ldap import *
@@ -751,8 +752,8 @@ try:
    # Get the email 
    ErrType = EX_PERMFAIL;
    ErrMsg = "Failed to understand the email or find a signature:";
-   Email = mimetools.Message(sys.stdin,0);
-   Msg = GetClearSig(Email);
+   email = email.parser.Parser().parse(sys.stdin);
+   Msg = GetClearSig(email);
 
    ErrMsg = "Message is not PGP signed:"
    if Msg[0].find("-----BEGIN PGP SIGNED MESSAGE-----") == -1 and \
@@ -773,11 +774,8 @@ try:
    global PlainText;
    ErrMsg = "Problem stripping MIME headers from the decoded message"
    if Msg[1] == 1:
-      try:
-         Index = pgp.text.index("\n\n") + 2
-      except ValueError:
-         Index = pgp.text.index("\n\r\n") + 3
-      PlainText = pgp.text[Index:]
+      e = email.parser.Parser().parsestr(pgp.text)
+      PlainText = e.get_payload(decode=True)
    else:
       PlainText = pgp.text
 
@@ -800,22 +798,13 @@ try:
 
    # Check the signature against the replay cache
    RC = ReplayCache(ReplayCacheFile);
-   RC.Clean();
-   ErrMsg = "The replay cache rejected your message. Check your clock!";
-   Rply = RC.Check(pgp.sig_info);
-   if Rply != None:
-      RC.close()
-      raise UDNotAllowedError, Rply;
-   RC.Add(pgp.sig_info);
-   RC.close()
+   RC.process(pgp.sig_info)
 
    # Determine the sender address
    ErrMsg = "A problem occured while trying to formulate the reply";
-   Sender = Email.getheader("Reply-To");
-   if Sender == None:
-      Sender = Email.getheader("From");
-   if Sender == None:
-      raise UDFormatError, "Unable to determine the sender's address";
+   Sender = email['Reply-To']
+   if not Sender: Sender = email['From']
+   if not Sender: raise UDFormatError, "Unable to determine the sender's address";
 
    # Formulate a reply
    Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
index 7a6862b..a6bfa55 100644 (file)
 #    packets so I can tell if a signature is made by pgp2 to enable the
 #    pgp2 encrypting mode.
 
-import mimetools, multifile, sys, StringIO, os, tempfile, re;
+import sys, StringIO, os, tempfile, re;
 import time, fcntl, anydbm
+import email, email.message
+
+from userdir_exceptions import *
 
 # General GPG options
 GPGPath = "gpg"
@@ -75,109 +78,78 @@ def SetKeyrings(Rings):
 # Paranoid will check the message text to make sure that all the plaintext is
 # in fact signed (bounded by a PGP packet)
 def GetClearSig(Msg,Paranoid = 0):
-   Error = 'MIME Error';
+   if not Msg.__class__ == email.message.Message:
+      raise RuntimeError, "GetClearSign() not called with a email.message.Message"
+
    # See if this is a MIME encoded multipart signed message
-   if Msg.gettype() == "multipart/signed":
-      Boundary = Msg.getparam("boundary");
-      if not Boundary:
-         raise Error, "multipart/* without a boundary parameter";
-
-      # Create the multipart handler. Regrettably their implementation
-      # Needs seeking..
-      SkMessage = StringIO.StringIO();
-      SkMessage.write(Msg.fp.read());
-      SkMessage.seek(0);
-      mf = multifile.MultiFile(SkMessage)
-      mf.push(Msg.getparam("boundary"));
-
-      # Check the first bit of the message..
-      if Paranoid != 0:
-         Pos = mf.tell();
-         while 1:
-             x = mf.readline();
-             if not x: break;
-             if len(x.strip()) != 0:
-                raise Error,"Unsigned text in message (at start)";
-         mf.seek(Pos);
-
-      # Get the first part of the multipart message
-      if not mf.next():
-         raise Error, "Invalid pgp/mime encoding [no section]";
-
-      # Get the part as a safe seekable stream
-      Signed = StringIO.StringIO();
-      Signed.write(mf.read());
-      InnerMsg = mimetools.Message(Signed);
-
-      # Make sure it is the right type
-      if InnerMsg.gettype() != "text/plain":
-         raise Error, "Invalid pgp/mime encoding [wrong plaintext type]";
-
-      # Get the next part of the multipart message
-      if not mf.next():
-         raise Error, "Invalid pgp/mime encoding [no section]";
-      InnerMsg = mimetools.Message(mf);
-      if InnerMsg.gettype() != "application/pgp-signature":
-         raise Error, "Invalid pgp/mime encoding [wrong signature type]";
-      Signature = ''.join(mf.readlines())
-
-      # Check the last bit of the message..
-      if Paranoid != 0:
-         mf.pop();
-         Pos = mf.tell();
-         while 1:
-             x = mf.readline();
-             if not x: break;
-             if len(x.strip()) != 0:
-                raise Error,"Unsigned text in message (at end)";
-         mf.seek(Pos);
+   if Msg.is_multipart():
+      if not Msg.get_content_type() == "multipart/signed":
+         raise UDFormatError, "Cannot handle multipart messages not of type multipart/signed";
+
+      if Paranoid:
+         if Msg.preamble is not None and Msg.preamble.strip() != "":
+            raise UDFormatError,"Unsigned text in message (at start)";
+         if Msg.epilogue is not None and Msg.epilogue.strip() != "":
+            raise UDFormatError,"Unsigned text in message (at end)";
+
+      payloads = Msg.get_payload()
+      if len(payloads) != 2:
+         raise UDFormatError, "multipart/signed message with number of payloads != 2";
+
+      (Signed, Signature) = payloads
+
+      if Signed.get_content_type() != "text/plain":
+         raise UDFormatError, "Invalid pgp/mime encoding [wrong plaintext type]";
+      if Signature.get_content_type() != "application/pgp-signature":
+         raise UDFormatError, "Invalid pgp/mime encoding [wrong signature type]";
 
       # Append the PGP boundary header and the signature text to re-form the
       # original signed block [needs to convert to \r\n]
       Output = "-----BEGIN PGP SIGNED MESSAGE-----\r\n";
       # Semi-evil hack to get the proper hash type inserted in the message
-      if Msg.getparam('micalg') != None:
-          Output = Output + "Hash: MD5,SHA1,%s\r\n"%(Msg.getparam('micalg')[4:].upper())
+      if Msg.get_param('micalg') != None:
+          Output = Output + "Hash: MD5,SHA1,%s\r\n"%(Msg.get_param('micalg')[4:].upper())
       Output = Output + "\r\n";
-      Output = Output + Signed.getvalue().replace("\n-","\n- -") + Signature
+      Output = Output + Signed.as_string().replace("\n-","\n- -") + "\n" + Signature.get_payload(decode=True)
       return (Output,1);
    else:
       if Paranoid == 0:
          # Just return the message body
-         return (''.join(Msg.fp.readlines()),0);
+         return (Msg.get_payload(decode=True), 0);
 
-      Body = "";
+      Body = [];
       State = 1;
-      for x in Msg.fp.readlines():
-          Body = Body + x;
-          Tmp = x.strip()
-          if len(Tmp) == 0:
+      for x in Msg.get_payload(decode=True).split('\n'):
+          Body.append(x)
+
+          if x == "":
              continue;
 
           # Leading up to the signature
           if State == 1:
-             if Tmp == "-----BEGIN PGP SIGNED MESSAGE-----":
+             if x == "-----BEGIN PGP SIGNED MESSAGE-----":
                 State = 2;
              else:
-                raise Error,"Unsigned text in message (at start)";
+                raise UDFormatError,"Unsigned text in message (at start)";
              continue;
 
           # In the signature plain text
           if State == 2:
-             if Tmp == "-----BEGIN PGP SIGNATURE-----":
+             if x == "-----BEGIN PGP SIGNATURE-----":
                 State = 3;
              continue;
 
           # In the signature
           if State == 3:
-             if Tmp == "-----END PGP SIGNATURE-----":
+             if x == "-----END PGP SIGNATURE-----":
                 State = 4;
              continue;
 
           # Past the end
           if State == 4:
-             raise Error,"Unsigned text in message (at end)";
-      return (Body,0);
+             raise UDFormatError,"Unsigned text in message (at end)";
+
+      return ("\n".join(Body), 0);
 
 # This opens GPG in 'write filter' mode. It takes Message and sends it
 # to GPGs standard input, pipes the standard output to a temp file along
@@ -546,6 +518,7 @@ class ReplayCache:
       self.CleanCutOff = CleanCutOff;
       self.AgeCutOff = AgeCutOff;
       self.FutureCutOff = FutureCutOff;
+      self.Clean()
 
    # Close the cache and lock
    def __del__(self):
@@ -588,6 +561,13 @@ class ReplayCache:
       else:
          self.DB[Key] = str(int(Sig[1]));
 
+   def process(self, sig_info):
+      r = self.Check(sig_info);
+      if r != None:
+         raise RuntimeError, "The replay cache rejected your message: %s."%(r);
+      self.Add(sig_info);
+      self.close();
+
 # vim:set et:
 # vim:set ts=3:
 # vim:set shiftwidth=3: