From 2525bf73603cb6487cfcea096e2dc347ad360394 Mon Sep 17 00:00:00 2001 From: jgg <> Date: Thu, 20 Jan 2000 05:33:58 +0000 Subject: [PATCH] posix -> os --- gpgwrapper | 238 ++++++++++++++++++++++++++++++++++++++++++++++++ ud-echelon | 4 +- ud-generate | 4 +- ud-gpgimport | 4 +- ud-gpgsigfetch | 4 +- ud-info | 4 +- ud-ldapshow | 12 +++ ud-mailgate | 23 +++-- ud-replicate | 3 +- ud-useradd | 10 +- ud-xearth | 2 +- userdir_gpg.py | 9 +- userdir_ldap.py | 4 + 13 files changed, 293 insertions(+), 28 deletions(-) create mode 100755 gpgwrapper diff --git a/gpgwrapper b/gpgwrapper new file mode 100755 index 0000000..8ab64da --- /dev/null +++ b/gpgwrapper @@ -0,0 +1,238 @@ +#!/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 " +# LDAP_UID="aph" +# PGP_FINGERPRINT="E21E5D13FAD42A54F1AA5A00D801CE55" +# PGP_KEYID="8FFC405EFD5A67CD" +# PGP_KEYNAME="Adam Di Carlo " +# 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 string, 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 = ldap.open(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'): + ErrorTempalte = val; + elif (switch == '-k'): + SetKeyrings(string.split(val,":")); + 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 != 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 string.find(Msg[0],"-----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] != None: + raise Error, Res[0]; + + if Res[3] == 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 = string.index(Res[3],"\n\n") + 2; + except ValueError: + Index = string.index(Res[3],"\n\r\n") + 3; + PlainText = Res[3][Index:]; + else: + PlainText = Res[3]; + + # 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]); + + # Do LDAP stuff + if LDAPDn != 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 == None: + Sender = Email.getheader("From"); + if Sender == 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(string.join(arguments," "),"w"); + Child.write(PlainText); + if Child.close() != 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 != 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() != 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); + diff --git a/ud-echelon b/ud-echelon index 6d4c38f..5e98afc 100755 --- a/ud-echelon +++ b/ud-echelon @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- mode: python -*- -import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, posix, getopt; +import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, os, getopt; import string, pwd from userdir_gpg import *; from userdir_ldap import *; @@ -96,7 +96,7 @@ try: global l; l = ldap.open(LDAPServer); if Debug == None: - F = open(PassDir+"/pass-"+pwd.getpwuid(posix.getuid())[0],"r"); + F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r"); AccessPass = string.split(string.strip(F.readline())," "); l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]); F.close(); diff --git a/ud-generate b/ud-generate index e27b6fa..792383e 100755 --- a/ud-generate +++ b/ud-generate @@ -2,7 +2,7 @@ # -*- mode: python -*- # Generates passwd, shadow and group files from the ldap directory. -import string, re, time, ldap, getopt, sys, os, posix, pwd; +import string, re, time, ldap, getopt, sys, os, pwd; from userdir_ldap import *; PasswdAttrs = None; @@ -336,7 +336,7 @@ def GenDNS(l,File): # Connect to the ldap server l = ldap.open(LDAPServer); -F = open(PassDir+"/pass-"+pwd.getpwuid(posix.getuid())[0],"r"); +F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r"); Pass = string.split(string.strip(F.readline())," "); F.close(); l.simple_bind_s("uid="+Pass[0]+","+BaseDn,Pass[1]); diff --git a/ud-gpgimport b/ud-gpgimport index 500f965..4b43bc7 100755 --- a/ud-gpgimport +++ b/ud-gpgimport @@ -14,7 +14,7 @@ # in the directory but not in the key ring will be removed from the # directory. -import string, re, time, ldap, getopt, sys, pwd, posix; +import string, re, time, ldap, getopt, sys, pwd, os; from userdir_ldap import *; from userdir_gpg import *; @@ -35,7 +35,7 @@ def LoadOverride(File): UnknownMap[Split[0]] = string.strip(Split[1]); # Process options -AdminUser = pwd.getpwuid(posix.getuid())[0]; +AdminUser = pwd.getpwuid(os.getuid())[0]; (options, arguments) = getopt.getopt(sys.argv[1:], "au:m:n") for (switch, val) in options: if (switch == '-u'): diff --git a/ud-gpgsigfetch b/ud-gpgsigfetch index 592b319..7e9ae13 100755 --- a/ud-gpgsigfetch +++ b/ud-gpgsigfetch @@ -1,12 +1,12 @@ #!/usr/bin/env python # -*- mode: python -*- -import string, re, time, ldap, getopt, sys, pwd, posix; +import string, re, time, ldap, getopt, sys, pwd, os; from userdir_gpg import *; Output = "extrakeys.gpg"; # Process options -AdminUser = pwd.getpwuid(posix.getuid())[0]; +AdminUser = pwd.getpwuid(os.getuid())[0]; (options, arguments) = getopt.getopt(sys.argv[1:], "o:") for (switch, val) in options: if (switch == '-o'): diff --git a/ud-info b/ud-info index ce06934..c77b2d2 100755 --- a/ud-info +++ b/ud-info @@ -17,7 +17,7 @@ # -r Enable 'root' functions, do this if your uid has access to # restricted variables. -import string, time, posix, pwd, sys, getopt, ldap, crypt, whrandom, readline, copy; +import string, time, os, pwd, sys, getopt, ldap, crypt, whrandom, readline, copy; from userdir_ldap import *; RootMode = 0; @@ -235,7 +235,7 @@ def MultiChangeAttr(Attrs,Attr): print; # Main program starts here -User = pwd.getpwuid(posix.getuid())[0]; +User = pwd.getpwuid(os.getuid())[0]; BindUser = User; # Process options (options, arguments) = getopt.getopt(sys.argv[1:], "nu:c:a:r") diff --git a/ud-ldapshow b/ud-ldapshow index 9899129..2b81549 100755 --- a/ud-ldapshow +++ b/ud-ldapshow @@ -83,6 +83,18 @@ if arguments[0] == "devcount": Count = Count + 1; print "There are",Count,"developers as of",time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time())); +if arguments[0] == "echelon": + Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,\ + "(&(|(activity-pgp=*)(activity-from=*))(&(keyfingerprint=*)(gidnumber=800)))",\ + ["activity-pgp","activity-from"]); + Count = 0; + PGPCount = 0; + for x in Attrs: + Count = Count + 1; + if x[1].has_key("activity-pgp"): + PGPCount = PGPCount + 1; + print "Echelon has seen",Count,"developers, with",PGPCount,"PGP confirms as of",time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time())); + if arguments[0] == "keystat": Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyfingerprint=*",\ ["keyfingerprint"]); diff --git a/ud-mailgate b/ud-mailgate index 586dddb..6e94c75 100755 --- a/ud-mailgate +++ b/ud-mailgate @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- mode: python -*- -import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, posix; +import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, os; import string, pwd from userdir_gpg import *; from userdir_ldap import *; @@ -234,7 +234,7 @@ def HandleChange(Reply,DnRecord,Key): # Connect to the ldap server l = ldap.open(LDAPServer); - F = open(PassDir+"/pass-"+pwd.getpwuid(posix.getuid())[0],"r"); + F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r"); AccessPass = string.split(string.strip(F.readline())," "); F.close(); @@ -301,12 +301,17 @@ def HandleChPass(Reply,DnRecord,Key): # Connect to the ldap server l = ldap.open(LDAPServer); - F = open(PassDir+"/pass-"+pwd.getpwuid(posix.getuid())[0],"r"); + F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r"); AccessPass = string.split(string.strip(F.readline())," "); F.close(); + l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]); + + # Check for a locked account + Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+GetAttr(DnRecord,"uid")); + if (string.find(GetAttr(Attrs[0],"userPassword"),"*LK*") != -1): + raise Error, "This account is locked"; # Modify the password - l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]); Rec = [(ldap.MOD_REPLACE,"userPassword","{crypt}"+Pass)]; Dn = "uid=" + GetAttr(DnRecord,"uid") + "," + BaseDn; l.modify_s(Dn,Rec); @@ -316,7 +321,7 @@ def HandleChPass(Reply,DnRecord,Key): # Start of main program # Drop messages from a mailer daemon. -if posix.environ.has_key('SENDER') == 0 or len(posix.environ['SENDER']) == 0: +if os.environ.has_key('SENDER') == 0 or len(os.environ['SENDER']) == 0: sys.exit(0); ErrMsg = "Indeterminate Error"; @@ -412,8 +417,8 @@ try: # Send the message through sendmail ErrMsg = "A problem occured while trying to send the reply"; - Child = posix.popen("/usr/sbin/sendmail -t","w"); -# Child = posix.popen("cat","w"); + Child = os.popen("/usr/sbin/sendmail -t","w"); +# Child = os.popen("cat","w"); Child.write(Reply); if Child.close() != None: raise Error, "Sendmail gave a non-zero return code"; @@ -421,7 +426,7 @@ try: 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" % (posix.environ['SENDER'],ReplyTo,Date); + ErrReplyHead = "To: %s\nReply-To: %s\nDate: %s\n" % (os.environ['SENDER'],ReplyTo,Date); # Error Body Subst = {}; @@ -441,7 +446,7 @@ except: try: ErrReply = TemplateSubst(Subst,open(TemplatesDir+"error-reply","r").read()); - Child = posix.popen("/usr/sbin/sendmail -t","w"); + Child = os.popen("/usr/sbin/sendmail -t","w"); Child.write(ErrReplyHead); Child.write(ErrReply); if Child.close() != None: diff --git a/ud-replicate b/ud-replicate index 371416d..34ee677 100755 --- a/ud-replicate +++ b/ud-replicate @@ -9,6 +9,7 @@ lockfile -r 1 -l 3600 lock > /dev/null 2>&1 trap "rm -f lock > /dev/null 2>&1" exit rsync -e ssh -rp sshdist@samosa:/var/cache/userdir-ldap/hosts/$HOST . > /dev/null 2>&1 makedb $HOST/passwd.tdb -o passwd.db > /dev/null 2>&1 -makedb $HOST/shadow.tdb -o shadow.db > /dev/null 2>&1 +(umask 027 && makedb $HOST/shadow.tdb -o shadow.db) > /dev/null 2>&1 +chown root.shadow shadow.db makedb $HOST/group.tdb -o group.db > /dev/null 2>&1 ln -sf $HOST/ssh-rsa-shadow . > /dev/null 2>&1 diff --git a/ud-useradd b/ud-useradd index dae1914..d1c6b79 100755 --- a/ud-useradd +++ b/ud-useradd @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- mode: python -*- -import string, re, time, ldap, getopt, sys, posix, pwd; +import string, re, time, ldap, getopt, sys, os, pwd; from userdir_ldap import *; from userdir_gpg import *; @@ -176,13 +176,13 @@ if Res != "yes": # Initialize the substitution Map Subst = {} Subst["__REALNAME__"] = FullName; -Subst["__WHOAMI__"] = pwd.getpwuid(posix.getuid())[0]; +Subst["__WHOAMI__"] = pwd.getpwuid(os.getuid())[0]; Subst["__DATE__"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time())); Subst["__LOGIN__"] = account; Subst["__PRIVATE__"] = privsub; Subst["__EMAIL__"] = email; Subst["__PASSWORD__"] = CryptedPass; -Subst["__LISTPASS__"] = string.strip(open(pwd.getpwuid(posix.getuid())[5]+"/.debian-lists_passwd","r").read()); +Subst["__LISTPASS__"] = string.strip(open(pwd.getpwuid(os.getuid())[5]+"/.debian-lists_passwd","r").read()); # Generate the LDAP request Rec = [(ldap.MOD_REPLACE,"uid",account), @@ -236,8 +236,8 @@ if privsub != " ": # Send the Welcome message print "Sending Welcome Email" Reply = TemplateSubst(Subst,open("templates/welcome-message-"+gidnumber,"r").read()); -Child = posix.popen("/usr/sbin/sendmail -t","w"); -#Child = posix.popen("cat","w"); +Child = os.popen("/usr/sbin/sendmail -t","w"); +#Child = os.popen("cat","w"); Child.write(Reply); if Child.close() != None: raise Error, "Sendmail gave a non-zero return code"; diff --git a/ud-xearth b/ud-xearth index 4f1773b..5b23044 100755 --- a/ud-xearth +++ b/ud-xearth @@ -14,7 +14,7 @@ # DGMS -> DD DDD + (MM + (SS.SSSSSS)/60)/60 # For Latitude + is North, for Longitude + is East -import string, re, time, ldap, getopt, sys, pwd, posix; +import string, re, time, ldap, getopt, sys, pwd, os; from userdir_ldap import *; Anon = 0; diff --git a/userdir_gpg.py b/userdir_gpg.py index e0f3feb..b181abe 100644 --- a/userdir_gpg.py +++ b/userdir_gpg.py @@ -19,8 +19,7 @@ import rfc822, time, fcntl, FCNTL, anydbm GPGPath = "gpg" GPGBasicOptions = ["--no-options","--batch","--load-extension","rsa",\ "--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 +33,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. diff --git a/userdir_ldap.py b/userdir_ldap.py index 0ea96a9..07bc6b1 100644 --- a/userdir_ldap.py +++ b/userdir_ldap.py @@ -1,5 +1,6 @@ # Some routines and configuration that are used by the ldap progams import termios, TERMIOS, re, string, imp, ldap, sys, whrandom, crypt, rfc822; +import userdir_gpg try: File = open("/etc/userdir-ldap/userdir-ldap.conf"); @@ -22,6 +23,9 @@ PassDir = ConfModule.passdir; Ech_ErrorLog = ConfModule.ech_errorlog; Ech_MainLog = ConfModule.ech_mainlog; +# Break up the keyring list +userdir_gpg.SetKeyrings(string.split(ConfModule.keyrings,":")); + # This is a list of common last-name prefixes LastNamesPre = {"van": None, "le": None, "de": None, "di": None}; -- 2.20.1