X-Git-Url: https://git.adam-barratt.org.uk/?a=blobdiff_plain;f=userdir_ldap.py;h=215d8e8e77d99a3356c315366c6331110e1616f1;hb=6597a93ddb54485b12420100c45dc83a42f70d8a;hp=f3f9ba7a306016c6407d6c9f7857b0d1284de95c;hpb=d34f4cb87a80d211f07fc1bfb2b9a630d1346e7c;p=mirror%2Fuserdir-ldap.git diff --git a/userdir_ldap.py b/userdir_ldap.py index f3f9ba7..215d8e8 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 termios, re, string, imp, ldap, sys, whrandom, crypt, rfc822; +import userdir_gpg try: File = open("/etc/userdir-ldap/userdir-ldap.conf"); @@ -10,7 +11,7 @@ File.close(); # Cheap hack BaseDn = ConfModule.basedn; -BaseDn = ConfModule.basedn; +HostBaseDn = ConfModule.hostbasedn; LDAPServer = ConfModule.ldaphost; EmailAppend = ConfModule.emailappend; AdminUser = ConfModule.adminuser; @@ -22,14 +23,20 @@ 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}; # SSH Key splitting. The result is: # (options,size,modulous,exponent,comment) SSHAuthSplit = re.compile('^(.* )?(\d+) (\d+) (\d+) ?(.+)$'); +SSH2AuthSplit = re.compile('^(.* )?ssh-(dss|rsa) ([a-zA-Z0-9=/+]+) ?(.+)$'); #'^([^\d](?:[^ "]+(?:".*")?)*)? ?(\d+) (\d+) (\d+) (.+)$'); +AddressSplit = re.compile("(.*).*<([^@]*)@([^>]*)>"); + # 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 = ""): @@ -63,16 +70,16 @@ def PrettyShow(DnRecord): # Function to prompt for a password def getpass(prompt = "Password: "): - import termios, TERMIOS, sys; + import termios, sys; fd = sys.stdin.fileno(); old = termios.tcgetattr(fd); new = termios.tcgetattr(fd); - new[3] = new[3] & ~TERMIOS.ECHO; # lflags + new[3] = new[3] & ~termios.ECHO; # lflags try: - termios.tcsetattr(fd, TERMIOS.TCSADRAIN, new); + termios.tcsetattr(fd, termios.TCSADRAIN, new); passwd = raw_input(prompt); finally: - termios.tcsetattr(fd, TERMIOS.TCSADRAIN, old); + termios.tcsetattr(fd, termios.TCSADRAIN, old); print; return passwd; @@ -101,10 +108,9 @@ def NameSplit(Name): break; # Merge any of the middle initials - if len(Words) > 2: - while len(Words[2]) == 2 and Words[2][1] == '.': - Words[1] = Words[1] + Words[2]; - del Words[2]; + while len(Words) > 2 and len(Words[2]) == 2 and Words[2][1] == '.': + Words[1] = Words[1] + Words[2]; + del Words[2]; while len(Words) < 2: Words.append(''); @@ -132,7 +138,7 @@ def GenPass(): SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/."; Rand = open("/dev/urandom"); Password = ""; - for i in range(0,10): + for i in range(0,15): Password = Password + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)]; return Password; @@ -140,10 +146,11 @@ def GenPass(): def HashPass(Password): # Hash it telling glibc to use the MD5 algorithm - if you dont have # glibc then just change Salt = "$1$" to Salt = ""; - SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/."; + SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/."; Salt = "$1$"; + Rand = open("/dev/urandom"); for x in range(0,10): - Salt = Salt + SaltVals[whrandom.randint(0,len(SaltVals)-1)]; + Salt = Salt + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)]; Pass = crypt.crypt(Password,Salt); if len(Pass) < 14: raise "Password Error", "MD5 password hashing failed, not changing the password!"; @@ -179,7 +186,7 @@ def FlushOutstanding(l,Outstanding,Fast=0): # Convert a lat/long attribute into Decimal degrees def DecDegree(Posn,Anon=0): - Parts = re.match('[+-]?(\d*)\\.?(\d*)?',Posn).groups(); + Parts = re.match('[-+]?(\d*)\\.?(\d*)',Posn).groups(); Val = string.atof(Posn); if (abs(Val) >= 1806060.0): @@ -207,10 +214,20 @@ def DecDegree(Posn,Anon=0): return "+" + Str; return Str; +def FormatSSH2Auth(Str): + Match = SSH2AuthSplit.match(Str); + if Match == None: + return ""; + G = Match.groups(); + + if G[0] == None: + return "ssh-%s %s..%s %s"%(G[1],G[2][:8],G[2][-8:],G[3]); + return "%s ssh-%s %s..%s %s"%(G[0],G[1],G[2][:8],G[2][-8:],G[3]); + def FormatSSHAuth(Str): Match = SSHAuthSplit.match(Str); if Match == None: - return ""; + return FormatSSH2Auth(Str); G = Match.groups(); # No options @@ -245,13 +262,98 @@ def FormatPGPKey(Str): # Take an email address and split it into 3 parts, (Name,UID,Domain) def SplitEmail(Addr): + # Is not an email address at all + if string.find(Addr,'@') == -1: + return (Addr,"",""); + Res1 = rfc822.AddrlistClass(Addr).getaddress(); if len(Res1) != 1: return ("","",Addr); Res1 = Res1[0]; if Res1[1] == None: return (Res1[0],"",""); + + # If there is no @ then the address was not parsed well. Try the alternate + # Parsing scheme. This is particularly important when scanning PGP keys. Res2 = string.split(Res1[1],"@"); if len(Res2) != 2: - return (Res1[0],"",Res1[1]); + Match = AddressSplit.match(Addr); + if Match == None: + return ("","",Addr); + return Match.groups(); + return (Res1[0],Res2[0],Res2[1]); + +# Convert the PGP name string to a uid value. The return is a tuple of +# (uid,[message strings]). UnknownMpa is a hash from email to uid that +# overrides normal searching. +def GetUID(l,Name,UnknownMap = {}): + # Crack up the email address into a best guess first/middle/last name + (cn,mn,sn) = NameSplit(re.sub('["]','',Name[0])) + + # Brackets anger the ldap searcher + cn = re.sub('[(")]','?',cn); + sn = re.sub('[(")]','?',sn); + + # First check the unknown map for the email address + if UnknownMap.has_key(Name[1] + '@' + Name[2]): + Stat = "unknown map hit for "+str(Name); + return (UnknownMap[Name[1] + '@' + Name[2]],[Stat]); + + # Then the cruft component (ie there was no email address to match) + if UnknownMap.has_key(Name[2]): + Stat = "unknown map hit for"+str(Name); + return (UnknownMap[Name[2]],[Stat]); + + # Then the name component (another ie there was no email address to match) + if UnknownMap.has_key(Name[0]): + Stat = "unknown map hit for"+str(Name); + return (UnknownMap[Name[0]],[Stat]); + + # Search for a possible first/last name hit + try: + Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(&(cn=%s)(sn=%s))"%(cn,sn),["uid"]); + except ldap.FILTER_ERROR: + Stat = "Filter failure: (&(cn=%s)(sn=%s))"%(cn,sn); + return (None,[Stat]); + + # Try matching on the email address + if (len(Attrs) != 1): + try: + Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"emailforward=%s"%(Name[2]),["uid"]); + except ldap.FILTER_ERROR: + pass; + + # Hmm, more than one/no return + if (len(Attrs) != 1): + # Key claims a local address + if Name[2] == EmailAppend: + + # Pull out the record for the claimed user + Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(uid=%s)"%(Name[1]),["uid","sn","cn"]); + + # We require the UID surname to be someplace in the key name, this + # deals with special purpose keys like 'James Troup (Alternate Debian key)' + # Some people put their names backwards on their key too.. check that as well + if len(Attrs) == 1 and \ + (string.find(string.lower(sn),string.lower(Attrs[0][1]["sn"][0])) != -1 or \ + string.find(string.lower(cn),string.lower(Attrs[0][1]["sn"][0])) != -1): + Stat = EmailAppend+" hit for "+str(Name); + return (Name[1],[Stat]); + + # Attempt to give some best guess suggestions for use in editing the + # override file. + Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(sn~=%s)"%(sn),["uid","sn","cn"]); + + Stat = []; + if len(Attrs) != 0: + Stat = ["None for %s"%(str(Name))]; + for x in Attrs: + Stat.append("But might be: %s %s <%s@debian.org>"%(x[1]["cn"][0],x[1]["sn"][0],x[1]["uid"][0])); + return (None,Stat); + else: + return (Attrs[0][1]["uid"][0],None); + + return (None,None); + +