ud-mailgate: fix gpg result usage
[mirror/userdir-ldap.git] / userdir_gpg.py
index 273ac47..abe1708 100644 (file)
@@ -52,6 +52,9 @@ CleanCutOff = 7*24*60*60;
 AgeCutOff = 4*24*60*60;
 FutureCutOff = 3*24*60*60;
 
+def ClearKeyrings():
+   del GPGKeyRings[:]
+
 # Set the keyrings, the input is a list of keyrings
 def SetKeyrings(Rings):
    for x in Rings:
@@ -254,11 +257,20 @@ def GPGWriteFilter(Program,Options,Message):
 # It is best if the recipient is specified using the hex key fingerprint
 # of the target, ie 0x64BE1319CCF6D393BF87FF9358A6D4EE
 def GPGEncrypt(Message,To,PGP2):
+   Error = "KeyringError"
    # Encrypt using the PGP5 block encoding and with the PGP5 option set.
    # This will handle either RSA or DSA/DH asymetric keys.
    # In PGP2 compatible mode IDEA and rfc1991 encoding are used so that
    # PGP2 can read the result. RSA keys do not need PGP2 to be set, as GPG
    # can read a message encrypted with blowfish and RSA.
+   searchkey = GPGKeySearch(To);
+   if len(searchkey) == 0:
+      raise Error, "No key found matching %s"%(To);
+   elif len(searchkey) > 1:
+      raise Error, "Multiple keys found matching %s"%(To);
+   if searchkey[0][4].find("E") < 0:
+      raise Error, "Key %s has no encryption capability - are all encryption subkeys expired or revoked?  Are there any encryption subkeys?"%(To);
+
    if PGP2 == 0:
       try:
          Res = None;
@@ -343,7 +355,15 @@ def GPGCheckSig(Message):
               GoodSig = 1;
            KeyID = Split[2];
            Owner = ' '.join(Split[3:])
-           
+           # If this message is signed with a subkey which has not yet
+           # expired, GnuPG will say GOODSIG here, even if the primary
+           # key already has expired.  This came up in discussion of
+           # bug #489225.  GPGKeySearch only returns non-expired keys.
+           Verify = GPGKeySearch(KeyID);
+           if len(Verify) == 0:
+              GoodSig = 0
+              Why = "Key has expired (no unexpired key found in keyring matching %s)"%(KeyId);
+
         # Bad signature response
         if Split[1] == "BADSIG":
            GoodSig = 0;
@@ -406,7 +426,7 @@ def GPGCheckSig(Message):
       # A gpg failure is an automatic bad signature
       if Exit[1] != 0 and Why == None:
          GoodSig = 0;
-         Why = "GPG execution failed " + str(Exit[0]);
+         Why = "GPG execution returned non-zero exit status: " + str(Exit[1]);
 
       if GoodSig == 0 and (Why == None or len(Why) == 0):
          Why = "Checking Failed";
@@ -422,10 +442,32 @@ def GPGCheckSig(Message):
          Res[1].close();
          Res[2].close();
 
+class GPGCheckSig2:
+       def __init__(self, msg):
+               res = GPGCheckSig(msg)
+               self.why = res[0]
+               self.sig_info = res[1]
+               self.key_info = res[2]
+               self.text = res[3]
+
+               self.ok = self.why is None
+
+               self.sig_id = self.sig_info[0]
+               self.sig_date = self.sig_info[1]
+               self.sig_fpr = self.sig_info[2]
+
+               self.key_id = self.key_info[0]
+               self.key_fpr = self.key_info[1]
+               self.key_owner = self.key_info[2]
+
+               self.is_pgp2 = self.key_info[4]
+
 # Search for keys given a search pattern. The pattern is passed directly
 # to GPG for processing. The result is a list of tuples of the form:
 #   (KeyID,KeyFinger,Owner,Length)
 # Which is similar to the key identification tuple output by GPGChecksig
+#
+# Do not return keys where the primary key has expired
 def GPGKeySearch(SearchCriteria):
    Args = [GPGPath] + GPGBasicOptions + GPGKeyRings + GPGSearchOptions + \
           [SearchCriteria," 2> /dev/null"]
@@ -433,6 +475,8 @@ def GPGKeySearch(SearchCriteria):
    Result = [];
    Owner = "";
    KeyID = "";
+   Capabilities = ""
+   Expired = None;
    Hits = {};
 
    dir = os.path.expanduser("~/.gnupg")
@@ -453,14 +497,17 @@ def GPGKeySearch(SearchCriteria):
          if Split[0] == 'pub':
             KeyID = Split[4];
             Owner = Split[9];
-            Length = int(Split[2]);
+            Length = int(Split[2])
+            Capabilities = Split[11]
+            Expired = Split[1] == 'e'
 
          # 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) );
+            if not Expired:
+               Result.append( (KeyID,Split[9],Owner,Length,Capabilities) );
    finally:
       if Strm != None:
          Strm.close();
@@ -541,3 +588,6 @@ class ReplayCache:
       else:
          self.DB[Key] = str(int(Sig[1]));
         
+# vim:set et:
+# vim:set ts=3:
+# vim:set shiftwidth=3: