Commit 71597847 authored by Julien Cristau's avatar Julien Cristau

Delete unmaintained/gpgwrapper. 8 years of non-maintenance ought to be enough.

parent 6602e1c9
......@@ -20,6 +20,7 @@ userdir-ldap (0.3.97) UNRELEASED; urgency=medium
* Use "foo is None" instead of "foo == None".
* Use "foo is not None" instead of "foo != None".
* Stop using string exceptions. They were removed in python 2.6.
* Delete unmaintained/gpgwrapper. 8 years of non-maintenance ought to be enough.
-- Peter Palfrader <weasel@debian.org> Sat, 06 Apr 2019 22:04:34 +0200
......
#!/usr/bin/env python
# -*- mode: python -*-
#
# Check and decode PGP signed emails.
# This script implements a wrapper around another program. It takes a mail
# on stdin and processes off a PGP signature, verifying it and seperating
# out the checked plaintext. It then invokes a sub process and feeds it
# the verified plain text and sets environment vairables indicating the
# result of the PGP check. If PGP checking fails then the subprocess is
# is never run and a bounce message is generated. The wrapper can understand
# PGP-MIME and all signatures supported by GPG. It completely decodes
# PGP-MIME before running the subprocess. It also can do optional
# anti-replay checking on the signatures.
#
# If enabled it can also do LDAP checking to determine the uniq UID owner
# of the key.
#
# Options:
# -r Replay cache file, if unset replay checking is disabled
# -e Bounce error message template file, if unset very ugly bounces are
# made
# -k Colon seperated list of keyrings to use
# -a Reply to address (mail daemon administrator)
# -d LDAP search base DN
# -l LDAP server
# -m Email address to use when prettying up LDAP_EMAIL
#
# It exports the following environment variables:
# LDAP_EMAIL="Adam Di Carlo <aph@debian.org>"
# LDAP_UID="aph"
# PGP_FINGERPRINT="E21E5D13FAD42A54F1AA5A00D801CE55"
# PGP_KEYID="8FFC405EFD5A67CD"
# PGP_KEYNAME="Adam Di Carlo <aph@debian.org> "
# SENDER (from mailer - envelope sender for bounces)
# REPLYTO (generated from message headers)
#
# Typical Debian invokation may look like:
# ./gpgwrapper -k /usr/share/keyrings/debian-keyring.gpg:/usr/share/keyrings/debian-keyring.pgp \
# -d ou=users,dc=debian,dc=org -l db.debian.org \
# -m debian.org -a admin@db.debian.org \
# -e /etc/userdir-ldap/templtes/error-reply -- test.sh
import sys, traceback, time, os;
import pwd, getopt;
from userdir_gpg import *;
EX_TEMPFAIL = 75;
EX_PERMFAIL = 65; # EX_DATAERR
Error = 'Message Error';
ReplyTo = "admin@db";
# Configuration
ReplayCacheFile = None;
ErrorTemplate = None;
LDAPDn = None;
LDAPServer = None;
EmailAppend = "";
# Safely get an attribute from a tuple representing a dn and an attribute
# list. It returns the first attribute if there are multi.
def GetAttr(DnRecord,Attribute,Default = ""):
try:
return DnRecord[1][Attribute][0];
except IndexError:
return Default;
except KeyError:
return Default;
return Default;
# Return a printable email address from the attributes.
def EmailAddress(DnRecord):
cn = GetAttr(DnRecord,"cn");
sn = GetAttr(DnRecord,"sn");
uid = GetAttr(DnRecord,"uid");
if cn == "" and sn == "":
return "<" + uid + "@" + EmailAppend + ">";
return cn + " " + sn + " <" + uid + "@" + EmailAppend + ">"
# Match the key fingerprint against an LDAP directory
def CheckLDAP(FingerPrint):
import ldap;
# Connect to the ldap server
global ErrTyp, ErrMsg;
ErrType = EX_TEMPFAIL;
ErrMsg = "An error occured while performing the LDAP lookup";
global l;
l = connectLDAP(LDAPServer);
l.simple_bind_s("","");
# Search for the matching key fingerprint
Attrs = l.search_s(LDAPDn,ldap.SCOPE_ONELEVEL,"keyfingerprint=" + FingerPrint);
if len(Attrs) == 0:
raise Error, "Key not found"
if len(Attrs) != 1:
raise Error, "Oddly your key fingerprint is assigned to more than one account.."
os.environ["LDAP_UID"] = GetAttr(Attrs[0],"uid");
os.environ["LDAP_EMAIL"] = EmailAddress(Attrs[0]);
# Start of main program
# Process options
(options, arguments) = getopt.getopt(sys.argv[1:], "r:e:k:a:d:l:m:");
for (switch, val) in options:
if (switch == '-r'):
ReplayCacheFile = val;
elif (switch == '-e'):
ErrorTemplate = val;
elif (switch == '-k'):
SetKeyrings(val.split(":"));
elif (switch == '-a'):
ReplyTo = val;
elif (switch == '-d'):
LDAPDn = val;
elif (switch == '-l'):
LDAPServer = val;
elif (switch == '-m'):
EmailAppend = val;
# Drop messages from a mailer daemon. (empty sender)
if os.environ.has_key('SENDER') == 0 or len(os.environ['SENDER']) == 0:
sys.exit(0);
ErrMsg = "Indeterminate Error";
ErrType = EX_TEMPFAIL;
try:
# Startup the replay cache
ErrType = EX_TEMPFAIL;
if ReplayCacheFile is not 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);
Msg = GetClearSig(Email);
ErrMsg = "Message is not PGP signed:"
if Msg[0].find("-----BEGIN PGP SIGNED MESSAGE-----") == -1:
raise Error, "No PGP signature";
# Check the signature
ErrMsg = "Unable to check the signature or the signature was invalid:";
Res = GPGCheckSig(Msg[0]);
if Res[0] is not None:
raise Error, Res[0];
if Res[3] is None:
raise Error, "Null signature text";
# Extract the plain message text in the event of mime encoding
global PlainText;
ErrMsg = "Problem stripping MIME headers from the decoded message"
if Msg[1] == 1:
try:
Index = Res[3].index("\n\n") + 2;
except ValueError:
Index = Res[3].index("\n\r\n") + 3;
PlainText = Res[3][Index:];
else:
PlainText = Res[3];
# Check the signature against the replay cache
if ReplayCacheFile is not None:
ErrMsg = "The replay cache rejected your message. Check your clock!";
Rply = RC.Check(Res[1]);
if Rply is not None:
raise Error, Rply;
RC.Add(Res[1]);
# Do LDAP stuff
if LDAPDn is not None:
CheckLDAP(Res[2][1]);
# Determine the sender address
ErrType = EX_PERMFAIL;
ErrMsg = "A problem occured while trying to formulate the reply";
Sender = Email.getheader("Reply-To");
if Sender is None:
Sender = Email.getheader("From");
if Sender is None:
raise Error, "Unable to determine the sender's address";
# Setup the environment
ErrType = EX_TEMPFAIL;
ErrMsg = "Problem calling the child process"
os.environ["PGP_KEYID"] = Res[2][0];
os.environ["PGP_FINGERPRINT"] = Res[2][1];
os.environ["PGP_KEYNAME"] = Res[2][2];
os.environ["REPLYTO"] = Sender;
# Invoke the child
Child = os.popen(" ".join(arguments),"w");
Child.write(PlainText);
if Child.close() is not None:
raise Error, "Child gave a non-zero return code";
except:
# Error Reply Header
Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
ErrReplyHead = "To: %s\nReply-To: %s\nDate: %s\n" % (os.environ['SENDER'],ReplyTo,Date);
# Error Body
Subst = {};
Subst["__ERROR__"] = ErrMsg;
Subst["__ADMIN__"] = ReplyTo;
Trace = "==> %s: %s\n" %(sys.exc_type,sys.exc_value);
List = traceback.extract_tb(sys.exc_traceback);
if len(List) >= 1:
Trace = Trace + "Python Stack Trace:\n";
for x in List:
Trace = Trace + " %s %s:%u: %s\n" %(x[2],x[0],x[1],x[3]);
Subst["__TRACE__"] = Trace;
# Try to send the bounce
try:
if ErrorTemplate is not None:
ErrReply = TemplateSubst(Subst,open(ErrorTemplate,"r").read());
else:
ErrReply = "\n"+str(Subst)+"\n";
Child = os.popen("/usr/sbin/sendmail -t","w");
Child.write(ErrReplyHead);
Child.write(ErrReply);
if Child.close() is not None:
raise Error, "Sendmail gave a non-zero return code";
except:
sys.exit(EX_TEMPFAIL);
if ErrType != EX_PERMFAIL:
sys.exit(ErrType);
sys.exit(0);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment