Use the common routine from userdir_ldap.py which asks for the
[mirror/userdir-ldap.git] / userdir_gpg.py
index 82e9ed9..1f3b1cb 100644 (file)
@@ -1,5 +1,18 @@
- #!/usr/bin/env python
-# -*- mode: python -*-
+#   Copyright (c) 1999-2001  Jason Gunthorpe <jgg@debian.org>
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 # GPG issues - 
 #  - gpgm with a status FD being fed keymaterial and other interesting
@@ -17,10 +30,10 @@ import rfc822, time, fcntl, FCNTL, anydbm
 
 # General GPG options
 GPGPath = "gpg"
-GPGBasicOptions = ["--no-options","--batch","--load-extension","rsa",\
+# "--load-extension","rsa",
+GPGBasicOptions = ["--no-options","--batch",
           "--no-default-keyring","--always-trust"];
-GPGKeyRings = ["--keyring","/usr/share/keyrings/debian-keyring.pgp",\
-               "--keyring","/usr/share/keyrings/debian-keyring.gpg"];
+GPGKeyRings = [];
 GPGSigOptions = ["--output","-"];
 GPGSearchOptions = ["--dry-run","--with-colons","--fingerprint"];
 GPGEncryptOptions = ["--output","-","--quiet","--always-trust",\
@@ -34,6 +47,12 @@ CleanCutOff = 7*24*60*60;
 AgeCutOff = 4*24*60*60;
 FutureCutOff = 3*24*60*60;
 
+# Set the keyrings, the input is a list of keyrings
+def SetKeyrings(Rings):
+   for x in Rings:
+      GPGKeyRings.append("--keyring");
+      GPGKeyRings.append(x);          
+
 # GetClearSig takes an un-seekable email message stream (mimetools.Message) 
 # and returns a standard PGP '---BEGIN PGP SIGNED MESSAGE---' bounded 
 # clear signed text.
@@ -44,7 +63,10 @@ FutureCutOff = 3*24*60*60;
 # present in the signature text! The return result is a tuple, the first
 # element is the text itself the second is a mime flag indicating if the
 # result should be mime processed after sig checking.
-def GetClearSig(Msg):
+#
+# 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';
    # See if this is a MIME encoded multipart signed message
    if Msg.gettype() == "multipart/signed":
@@ -60,6 +82,16 @@ def GetClearSig(Msg):
       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(string.strip(x)) != 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]";
@@ -68,7 +100,7 @@ def GetClearSig(Msg):
       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]";
@@ -81,13 +113,63 @@ def GetClearSig(Msg):
          raise Error, "Invalid pgp/mime encoding [wrong signature type]";
       Signature = string.joinfields(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(string.strip(x)) != 0:
+               raise Error,"Unsigned text in message (at end)";
+         mf.seek(Pos);
+      
       # 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\r\n" + Signed.getvalue() + Signature;
+      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"%(string.upper(Msg.getparam('micalg')[4:]));
+      Output = Output + "\r\n";
+      Output = Output +  string.replace(Signed.getvalue(),"\n-","\n- -") + Signature;
       return (Output,1);
    else:
-      # Just return the message body
-      return (string.joinfields(Msg.fp.readlines(),''),0);
+      if Paranoid == 0:
+         # Just return the message body
+         return (string.joinfields(Msg.fp.readlines(),''),0);
+     
+      Body = "";
+      State = 1;
+      for x in Msg.fp.readlines():
+         Body = Body + x;
+         Tmp = string.strip(x);
+         if len(Tmp) == 0:
+            continue;
+        
+         # Leading up to the signature
+         if State == 1:
+            if Tmp == "-----BEGIN PGP SIGNED MESSAGE-----":
+               State = 2;
+            else:
+               raise Error,"Unsigned text in message (at start)";
+            continue;
+        
+         # In the signature plain text
+         if State == 2:
+            if Tmp == "-----BEGIN PGP SIGNATURE-----":
+               State = 3;
+            continue;
+               
+         # In the signature
+         if State == 3:
+            if Tmp == "-----END PGP SIGNATURE-----":
+               State = 4;
+            continue;
+               
+          # Past the end
+         if State == 4:
+            raise Error,"Unsigned text in message (at end)";
+      return (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
@@ -264,16 +346,22 @@ def GPGCheckSig(Message):
             Why = "Verification of signature failed";
 
         # Bad signature response
-        if Split[1] == "ERRSIG" or Split[1] == "NO_PUBKEY":
+        if Split[1] == "ERRSIG":
            GoodSig = 0;
            KeyID = Split[2];
-           if Split[7] == '9':
+            if len(Split) <= 7:
+               Why = "GPG error, ERRSIG status tag is invalid";
+            elif Split[7] == '9':
                Why = "Unable to verify signature, signing key missing.";
             elif Split[7] == '4':
                Why = "Unable to verify signature, unknown packet format/key type";
            else:   
                Why = "Unable to verify signature, unknown reason";
 
+         if Split[1] == "NO_PUBKEY":
+           GoodSig = 0;
+            Why = "Unable to verify signature, signing key missing.";
+
         # Expired signature
         if Split[1] == "SIGEXPIRED":
            GoodSig = 0;
@@ -331,6 +419,7 @@ def GPGKeySearch(SearchCriteria):
    Result = [];
    Owner = "";
    KeyID = "";
+   Hits = {};
    try:
       Strm = os.popen(string.join(Args," "),"r");
       
@@ -349,6 +438,9 @@ def GPGKeySearch(SearchCriteria):
 
          # Output the key
          if Split[0] == 'fpr':
+            if Hits.has_key(Split[9]):
+               continue;
+            Hits[Split[9]] = None;
             Result.append( (KeyID,Split[9],Owner,Length) );
    finally:
       if Strm != None: