- 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
import sys, traceback, time, os;
import pwd, getopt;
+import email, email.parser
from userdir_gpg import *;
EX_TEMPFAIL = 75;
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";
# 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:
# -*- 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 *;
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
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;
# 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";
# 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
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));
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 *
# 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 \
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
# 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()));
# 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"
# 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
self.CleanCutOff = CleanCutOff;
self.AgeCutOff = AgeCutOff;
self.FutureCutOff = FutureCutOff;
+ self.Clean()
# Close the cache and lock
def __del__(self):
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: