ud-mailgate: remove exception for münchen.debian.net
[mirror/userdir-ldap.git] / userdir_ldap.py
index 634c514..d47909d 100644 (file)
@@ -1,6 +1,8 @@
 #   Copyright (c) 1999-2000  Jason Gunthorpe <jgg@debian.org>
 #   Copyright (c) 2001-2003  Ryan Murray <rmurray@debian.org>
 #   Copyright (c) 2004-2005  Joey Schulze <joey@infodrom.org>
 #   Copyright (c) 1999-2000  Jason Gunthorpe <jgg@debian.org>
 #   Copyright (c) 2001-2003  Ryan Murray <rmurray@debian.org>
 #   Copyright (c) 2004-2005  Joey Schulze <joey@infodrom.org>
+#   Copyright (c) 2008 Peter Palfrader <peter@palfrader.org>
+#   Copyright (c) 2008 Thomas Viehmann <tv@beamnet.de>
 #
 #   This program is free software; you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
 #
 #   This program is free software; you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 # Some routines and configuration that are used by the ldap progams
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 # Some routines and configuration that are used by the ldap progams
-import termios, re, string, imp, ldap, sys, whrandom, crypt, rfc822;
+import crypt
+import getpass
+import hashlib
+import hmac
+import imp
+import ldap
+import os
+import pwd
+import re
+import rfc822
+import sys
+import termios
+
 import userdir_gpg
 
 try:
 import userdir_gpg
 
 try:
-   File = open("/etc/userdir-ldap/userdir-ldap.conf");
-except:
-   File = open("userdir-ldap.conf");
-ConfModule = imp.load_source("userdir_config","/etc/userdir-ldap.conf",File);
-File.close();
+   File = open("/etc/userdir-ldap/userdir-ldap.conf")
+except IOError:
+   File = open("userdir-ldap.conf")
+ConfModule = imp.load_source("userdir_config", "/etc/userdir-ldap.conf", File)
+File.close()
 
 # Cheap hack
 
 # Cheap hack
-BaseDn = ConfModule.basedn;
-HostBaseDn = ConfModule.hostbasedn;
-LDAPServer = ConfModule.ldaphost;
-EmailAppend = ConfModule.emailappend;
-AdminUser = ConfModule.adminuser;
-GenerateDir = ConfModule.generatedir;
-GenerateConf = ConfModule.generateconf;
-DefaultGID = ConfModule.defaultgid;
-TemplatesDir = ConfModule.templatesdir;
-PassDir = ConfModule.passdir;
-Ech_ErrorLog = ConfModule.ech_errorlog;
-Ech_MainLog = ConfModule.ech_mainlog;
+BaseDn = ConfModule.basedn
+HostBaseDn = ConfModule.hostbasedn
+LDAPServer = ConfModule.ldaphost
+EmailAppend = ConfModule.emailappend
+AdminUser = ConfModule.adminuser
+GenerateDir = ConfModule.generatedir
+AllowedGroupsPreload = ConfModule.allowedgroupspreload
+HomePrefix = ConfModule.homeprefix
+TemplatesDir = ConfModule.templatesdir
+PassDir = ConfModule.passdir
+Ech_ErrorLog = ConfModule.ech_errorlog
+Ech_MainLog = ConfModule.ech_mainlog
+HostDomain = getattr(ConfModule, "hostdomain", EmailAppend)
+
+try:
+   UseSSL = ConfModule.usessl
+except AttributeError:
+   UseSSL = False
+
+try:
+   BaseBaseDn = ConfModule.basebasedn
+except AttributeError:
+   BaseBaseDn = BaseDn
+
+try:
+   IgnoreUsersForUIDNumberGen = ConfModule.ignoreusersforuidnumbergen
+except AttributeError:
+   IgnoreUsersForUIDNumberGen = ['nobody']
+
 
 # Break up the keyring list
 
 # Break up the keyring list
-userdir_gpg.SetKeyrings(string.split(ConfModule.keyrings,":"));
+userdir_gpg.SetKeyrings(ConfModule.keyrings.split(":"))
 
 # This is a list of common last-name prefixes
 
 # This is a list of common last-name prefixes
-LastNamesPre = {"van": None, "von": None, "le": None, "de": None, "di": None};
+LastNamesPre = {"van": None, "von": None, "le": None, "de": None, "di": None}
 
 # This is a list of common groups on Debian hosts
 DebianGroups = {
 
 # This is a list of common groups on Debian hosts
 DebianGroups = {
-   "Debian": 800,
-   "guest": 60000,
-   "nogroup": 65534
-   }
+    "Debian": 800,
+    "guest": 60000,
+    "nogroup": 65534,
+}
+
+# ObjectClasses for different object types
+UserObjectClasses = ("top", "inetOrgPerson", "debianAccount", "shadowAccount", "debianDeveloper")
+RoleObjectClasses = ("top", "debianAccount", "shadowAccount", "debianRoleAccount")
+GroupObjectClasses = ("top", "debianGroup")
 
 # SSH Key splitting. The result is:
 # (options,size,modulous,exponent,comment)
 
 # 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+) (.+)$');
+SSHAuthSplit = re.compile('^(.* )?(\d+) (\d+) (\d+) ?(.+)$')
+SSH2AuthSplit = re.compile('^(.* )?ssh-(dss|rsa|ecdsa-sha2-nistp(?:256|384|521)|ed25519) ([a-zA-Z0-9=/+]+) ?(.+)$')
+AddressSplit = re.compile("(.*).*<([^@]*)@([^>]*)>")
 
 
-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.
 
 # 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 = ""):
+def GetAttr(DnRecord, Attribute, Default=""):
    try:
    try:
-      return DnRecord[1][Attribute][0];
+      return DnRecord[1][Attribute][0]
    except IndexError:
    except IndexError:
-      return Default;
+      return Default
    except KeyError:
    except KeyError:
-      return Default;
-   return Default;
+      return Default
+   return Default
+
 
 # Return a printable email address from the attributes.
 def EmailAddress(DnRecord):
 
 # Return a printable email address from the attributes.
 def EmailAddress(DnRecord):
-   cn = GetAttr(DnRecord,"cn");
-   sn = GetAttr(DnRecord,"sn");
-   uid = GetAttr(DnRecord,"uid");
+   cn = GetAttr(DnRecord, "cn")
+   sn = GetAttr(DnRecord, "sn")
+   uid = GetAttr(DnRecord, "uid")
    if cn == "" and sn == "":
    if cn == "" and sn == "":
-      return "<" + uid + "@" + EmailAppend + ">";
+      return "<" + uid + "@" + EmailAppend + ">"
    return cn + " " + sn + " <" + uid + "@" + EmailAppend + ">"
 
    return cn + " " + sn + " <" + uid + "@" + EmailAppend + ">"
 
+
 # Show a dump like ldapsearch
 def PrettyShow(DnRecord):
 # Show a dump like ldapsearch
 def PrettyShow(DnRecord):
-   Result = "";
-   List = DnRecord[1].keys();
-   List.sort();
+   Result = ""
+   List = DnRecord[1].keys()
+   List.sort()
    for x in List:
    for x in List:
-      Rec = DnRecord[1][x];
+      Rec = DnRecord[1][x]
       for i in Rec:
       for i in Rec:
-         Result = Result + "%s: %s\n" % (x,i);
-   return Result[:-1];
-
-# Function to prompt for a password 
-def getpass(prompt = "Password: "):
-   import termios, sys;
-   fd = sys.stdin.fileno();
-   old = termios.tcgetattr(fd);
-   new = termios.tcgetattr(fd);
-   new[3] = new[3] & ~termios.ECHO;          # lflags
-   try:
-      termios.tcsetattr(fd, termios.TCSADRAIN, new);
-      try:
-         passwd = raw_input(prompt);
-      except KeyboardInterrupt:
-         termios.tcsetattr(fd, termios.TCSADRAIN, old);
-         print
-         sys.exit(0)
-      except EOFError:
-         passwd = ""
-   finally:
-      termios.tcsetattr(fd, termios.TCSADRAIN, old);
-   print;
-   return passwd;
-
-def passwdAccessLDAP(LDAPServer, BaseDn, AdminUser):
+         Result = Result + "%s: %s\n" % (x, i)
+   return Result[:-1]
+
+
+def connectLDAP(server=None):
+   if server is None:
+      global LDAPServer
+      server = LDAPServer
+   lc = ldap.open(server)
+   global UseSSL
+   if UseSSL:
+      lc.start_tls_s()
+   return lc
+
+
+def passwdAccessLDAP(BaseDn, AdminUser):
    """
    Ask for the AdminUser's password and connect to the LDAP server.
    Returns the connection handle.
    """
    """
    Ask for the AdminUser's password and connect to the LDAP server.
    Returns the connection handle.
    """
-   print "Accessing LDAP directory as '" + AdminUser + "'";
+   print "Accessing LDAP directory as '" + AdminUser + "'"
    while (1):
    while (1):
-      Password = getpass(AdminUser + "'s password: ");
+      if 'LDAP_PASSWORD' in os.environ:
+          Password = os.environ['LDAP_PASSWORD']
+      else:
+          Password = getpass.getpass(AdminUser + "'s password: ")
 
       if len(Password) == 0:
          sys.exit(0)
 
 
       if len(Password) == 0:
          sys.exit(0)
 
-      l = ldap.open(LDAPServer);
-      UserDn = "uid=" + AdminUser + "," + BaseDn;
+      lc = connectLDAP()
+      UserDn = "uid=" + AdminUser + "," + BaseDn
 
       # Connect to the ldap server
       try:
 
       # Connect to the ldap server
       try:
-         l.simple_bind_s(UserDn,Password);
+         lc.simple_bind_s(UserDn, Password)
       except ldap.INVALID_CREDENTIALS:
       except ldap.INVALID_CREDENTIALS:
+         if 'LDAP_PASSWORD' in os.environ:
+             print "password in environment does not work"
+             del os.environ['LDAP_PASSWORD']
          continue
       break
          continue
       break
-   return l
+   return lc
+
 
 # Split up a name into multiple components. This tries to best guess how
 # to split up a name
 def NameSplit(Name):
 
 # Split up a name into multiple components. This tries to best guess how
 # to split up a name
 def NameSplit(Name):
-   Words = re.split(" ",string.strip(Name));
+   Words = re.split(" ", Name.strip())
 
    # Insert an empty middle name
 
    # Insert an empty middle name
-   if (len(Words) == 2):
-      Words.insert(1,"");
-   if (len(Words) < 2):
-      Words.append("");
+   if len(Words) == 2:
+      Words.insert(1, "")
+   if len(Words) < 2:
+      Words.append("")
 
    # Put a dot after any 1 letter words, must be an initial
 
    # Put a dot after any 1 letter words, must be an initial
-   for x in range(0,len(Words)):
+   for x in range(0, len(Words)):
       if len(Words[x]) == 1:
       if len(Words[x]) == 1:
-         Words[x] = Words[x] + '.';
+         Words[x] = Words[x] + '.'
 
    # If a word starts with a -, ( or [ we assume it marks the start of some
    # Non-name information and remove the remainder of the string
 
    # If a word starts with a -, ( or [ we assume it marks the start of some
    # Non-name information and remove the remainder of the string
-   for x in range(0,len(Words)):
-      if len(Words[x]) != 0 and (Words[x][0] == '-' or \
-          Words[x][0] == '(' or Words[x][0] == '['):
-         Words = Words[0:x];
-         break;
-        
+   for x in range(0, len(Words)):
+      if len(Words[x]) != 0 and (Words[x][0] == '-' or
+                                 Words[x][0] == '(' or Words[x][0] == '['):
+         Words = Words[0:x]
+         break
+
    # Merge any of the middle initials
    while len(Words) > 2 and len(Words[2]) == 2 and Words[2][1] == '.':
    # Merge any of the middle initials
    while len(Words) > 2 and len(Words[2]) == 2 and Words[2][1] == '.':
-      Words[1] = Words[1] +  Words[2];
-      del Words[2];
+      Words[1] = Words[1] + Words[2]
+      del Words[2]
 
    while len(Words) < 2:
 
    while len(Words) < 2:
-      Words.append('');
-   
+      Words.append('')
+
    # Merge any of the last name prefixes into one big last name
    # Merge any of the last name prefixes into one big last name
-   while LastNamesPre.has_key(string.lower(Words[-2])):
-      Words[-1] = Words[-2] + " " + Words[-1];
-      del Words[-2];
+   while Words[-2].lower() in LastNamesPre:
+      Words[-1] = Words[-2] + " " + Words[-1]
+      del Words[-2]
 
    # Fix up a missing middle name after lastname globbing
 
    # Fix up a missing middle name after lastname globbing
-   if (len(Words) == 2):
-      Words.insert(1,"");
+   if len(Words) == 2:
+      Words.insert(1, "")
 
    # If the name is multi-word then we glob them all into the last name and
    # do not worry about a middle name
    if (len(Words) > 3):
 
    # If the name is multi-word then we glob them all into the last name and
    # do not worry about a middle name
    if (len(Words) > 3):
-      Words[2] = string.join(Words[1:]);
-      Words[1] = "";
+      Words[2] = " ".join(Words[1:])
+      Words[1] = ""
+
+   return (Words[0].strip(), Words[1].strip(), Words[2].strip())
 
 
-   return (string.strip(Words[0]),string.strip(Words[1]),string.strip(Words[2]));
 
 # Compute a random password using /dev/urandom
 
 # Compute a random password using /dev/urandom
-def GenPass():   
+def GenPass():
    # Generate a 10 character random string
    # Generate a 10 character random string
-   SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/.";
-   Rand = open("/dev/urandom");
-   Password = "";
-   for i in range(0,15):
-      Password = Password + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)];
-   return Password;
+   SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/."
+   Rand = open("/dev/urandom")
+   Password = ""
+   for i in range(0, 15):
+      Password = Password + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)]
+   return Password
+
 
 # Compute the MD5 crypted version of the given password
 def HashPass(Password):
    # Hash it telling glibc to use the MD5 algorithm - if you dont have
    # glibc then just change Salt = "$1$" to Salt = "";
 
 # Compute the MD5 crypted version of the given password
 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 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/.";
-   Salt  = "$1$";
-   Rand = open("/dev/urandom");
-   for x in range(0,10):
-      Salt = Salt + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)];
-   Pass = crypt.crypt(Password,Salt);
+   SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/."
+   Salt = "$1$"
+   Rand = open("/dev/urandom")
+   for x in range(0, 10):
+      Salt = Salt + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)]
+   Pass = crypt.crypt(Password, Salt)
    if len(Pass) < 14:
    if len(Pass) < 14:
-      raise "Password Error", "MD5 password hashing failed, not changing the password!";
-   return Pass;
+      raise "Password Error", "MD5 password hashing failed, not changing the password!"
+   return Pass
+
 
 # Sync with the server, we count the number of async requests that are pending
 # and make sure result has been called that number of times
 
 # Sync with the server, we count the number of async requests that are pending
 # and make sure result has been called that number of times
-def FlushOutstanding(l,Outstanding,Fast=0):
+def FlushOutstanding(l, Outstanding, Fast=0):
    # Sync with the remote end
    if Fast == 0:
    # Sync with the remote end
    if Fast == 0:
-      print "Waiting for",Outstanding,"requests:",
+      print "Waiting for {} requests:".format(Outstanding),
    while (Outstanding > 0):
       try:
          if Fast == 0 or Outstanding > 50:
    while (Outstanding > 0):
       try:
          if Fast == 0 or Outstanding > 50:
-            sys.stdout.write(".",);
-            sys.stdout.flush();
-            if (l.result(ldap.RES_ANY,1) != (None,None)):
-               Outstanding = Outstanding - 1;
+            sys.stdout.write(".",)
+            sys.stdout.flush()
+            if (l.result(ldap.RES_ANY, 1) != (None, None)):
+               Outstanding = Outstanding - 1
          else:
          else:
-            if (l.result(ldap.RES_ANY,1,0) != (None,None)):
-               Outstanding = Outstanding - 1;
-           else:
-               break;
+            if (l.result(ldap.RES_ANY, 1, 0) != (None, None)):
+               Outstanding = Outstanding - 1
+            else:
+               break
       except ldap.TYPE_OR_VALUE_EXISTS:
       except ldap.TYPE_OR_VALUE_EXISTS:
-         Outstanding = Outstanding - 1;
+         Outstanding -= 1
       except ldap.NO_SUCH_ATTRIBUTE:
       except ldap.NO_SUCH_ATTRIBUTE:
-         Outstanding = Outstanding - 1;
+         Outstanding -= 1
       except ldap.NO_SUCH_OBJECT:
       except ldap.NO_SUCH_OBJECT:
-         Outstanding = Outstanding - 1;
+         Outstanding -= 1
    if Fast == 0:
    if Fast == 0:
-      print;
-   return Outstanding;
+      print
+   return Outstanding
+
 
 # Convert a lat/long attribute into Decimal degrees
 
 # Convert a lat/long attribute into Decimal degrees
-def DecDegree(Posn,Anon=0):
-  Parts = re.match('[-+]?(\d*)\\.?(\d*)',Posn).groups();
-  Val = string.atof(Posn);
+def DecDegree(Posn, Anon=0):
+  Parts = re.match('[-+]?(\d*)\\.?(\d*)', Posn).groups()
+  Val = float(Posn)
 
   if (abs(Val) >= 1806060.0):
 
   if (abs(Val) >= 1806060.0):
-     raise ValueError,"Too Big";
+      raise ValueError("Too Big")
 
   # Val is in DGMS
   if abs(Val) >= 18060.0 or len(Parts[0]) > 5:
 
   # Val is in DGMS
   if abs(Val) >= 18060.0 or len(Parts[0]) > 5:
-     Val = Val/100.0;
-     Secs = Val - long(Val);
-     Val = long(Val)/100.0;
-     Min = Val - long(Val);
-     Val = long(Val) + (Min*100.0 + Secs*100.0/60.0)/60.0;
+      Val = Val / 100.0
+      Secs = Val - long(Val)
+      Val = long(Val) / 100.0
+      Min = Val - long(Val)
+      Val = long(Val) + (Min * 100.0 + Secs * 100.0 / 60.0) / 60.0
 
   # Val is in DGM
   elif abs(Val) >= 180 or len(Parts[0]) > 3:
 
   # Val is in DGM
   elif abs(Val) >= 180 or len(Parts[0]) > 3:
-     Val = Val/100.0;
-     Min = Val - long(Val);
-     Val = long(Val) + Min*100.0/60.0;
-     
+     Val = Val / 100.0
+     Min = Val - long(Val)
+     Val = long(Val) + Min * 100.0 / 60.0
+
   if Anon != 0:
   if Anon != 0:
-      Str = "%3.2f"%(Val);
+      Str = "%3.2f" % (Val)
   else:
   else:
-      Str = str(Val);
+      Str = str(Val)
   if Val >= 0:
   if Val >= 0:
-     return "+" + Str;
-  return Str;
+     return "+" + Str
+  return Str
+
 
 def FormatSSH2Auth(Str):
 
 def FormatSSH2Auth(Str):
-   Match = SSH2AuthSplit.match(Str);
-   if Match == None:
-      return "<unknown format>";
-   G = Match.groups();
+   Match = SSH2AuthSplit.match(Str)
+   if Match is None:
+      return "<unknown format>"
+   G = Match.groups()
+
+   if G[0] is 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])
 
 
-   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):
 
 def FormatSSHAuth(Str):
-   Match = SSHAuthSplit.match(Str);
-   if Match == None:
-      return FormatSSH2Auth(Str);
-   G = Match.groups();
+   Match = SSHAuthSplit.match(Str)
+   if Match is None:
+      return FormatSSH2Auth(Str)
+   G = Match.groups()
 
    # No options
 
    # No options
-   if G[0] == None:
-      return "%s %s %s..%s %s"%(G[1],G[2],G[3][:8],G[3][-8:],G[4]);
-   return "%s %s %s %s..%s %s"%(G[0],G[1],G[2],G[3][:8],G[3][-8:],G[4]);
+   if G[0] is None:
+      return "%s %s %s..%s %s" % (G[1], G[2], G[3][:8], G[3][-8:], G[4])
+   return "%s %s %s %s..%s %s" % (G[0], G[1], G[2], G[3][:8], G[3][-8:], G[4])
+
 
 def FormatPGPKey(Str):
 
 def FormatPGPKey(Str):
-   Res = "";
+   Res = ""
 
    # PGP 2.x Print
    if (len(Str) == 32):
 
    # PGP 2.x Print
    if (len(Str) == 32):
-      I = 0;
-      while (I < len(Str)):
-         if I+2 == 32/2:
-            Res = "%s %s%s "%(Res,Str[I],Str[I+1]);
+      indent = 0
+      while (indent < len(Str)):
+         if indent == 32 / 2:
+            Res = "%s %s%s " % (Res, Str[indent], Str[indent + 1])
          else:
          else:
-            Res = "%s%s%s "%(Res,Str[I],Str[I+1]);
-         I = I + 2;
+            Res = "%s%s%s " % (Res, Str[indent], Str[indent + 1])
+         indent += 2
    elif (len(Str) == 40):
       # OpenPGP Print
    elif (len(Str) == 40):
       # OpenPGP Print
-      I = 0;
-      while (I < len(Str)):
-         if I+4 == 40/2:
-            Res = "%s %s%s%s%s "%(Res,Str[I],Str[I+1],Str[I+2],Str[I+3]);
+      indent = 0
+      while (indent < len(Str)):
+         if indent == 40 / 2:
+            Res = "%s %s%s%s%s " % (Res, Str[indent], Str[indent + 1], Str[indent + 2], Str[indent + 3])
          else:
          else:
-            Res = "%s%s%s%s%s "%(Res,Str[I],Str[I+1],Str[I+2],Str[I+3]);
-         I = I + 4;
+            Res = "%s%s%s%s%s " % (Res, Str[indent], Str[indent + 1], Str[indent + 2], Str[indent + 3])
+         indent += 4
    else:
    else:
-      Res = Str;
-   return string.strip(Res);
+      Res = Str
+   return Res.strip()
+
 
 # Take an email address and split it into 3 parts, (Name,UID,Domain)
 def SplitEmail(Addr):
    # Is not an email address at all
 
 # 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 Addr.find('@') == -1:
+      return (Addr, "", "")
+
+   Res1 = rfc822.AddrlistClass(Addr).getaddress()
    if len(Res1) != 1:
    if len(Res1) != 1:
-      return ("","",Addr);
-   Res1 = Res1[0];
-   if Res1[1] == None:
-      return (Res1[0],"","");
+      return ("", "", Addr)
+   Res1 = Res1[0]
+   if Res1[1] is 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.
 
    # 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],"@");
+   Res2 = Res1[1].split("@")
    if len(Res2) != 2:
    if len(Res2) != 2:
-      Match = AddressSplit.match(Addr);
-      if Match == None:
-         return ("","",Addr);
-      return Match.groups();
+      Match = AddressSplit.match(Addr)
+      if Match is None:
+         return ("", "", Addr)
+      return Match.groups()
+
+   return (Res1[0], Res2[0], Res2[1])
 
 
-   return (Res1[0],Res2[0],Res2[1]);
 
 # Convert the PGP name string to a uid value. The return is a tuple of
 
 # 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 
+# (uid,[message strings]). UnknownMpa is a hash from email to uid that
 # overrides normal searching.
 # overrides normal searching.
-def GetUID(l,Name,UnknownMap = {}):
+def GetUID(l, Name, UnknownMap={}):
    # Crack up the email address into a best guess first/middle/last name
    # Crack up the email address into a best guess first/middle/last name
-   (cn,mn,sn) = NameSplit(re.sub('["]','',Name[0]))
-   
+   (cn, mn, sn) = NameSplit(re.sub('["]', '', Name[0]))
+
    # Brackets anger the ldap searcher
    # Brackets anger the ldap searcher
-   cn = re.sub('[(")]','?',cn);
-   sn = re.sub('[(")]','?',sn);
+   cn = re.sub('[(")]', '?', cn)
+   sn = re.sub('[(")]', '?', sn)
 
    # First check the unknown map for the email address
 
    # 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]);
+   if Name[1] + '@' + Name[2] in UnknownMap:
+      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)
 
    # 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]);
+   if Name[2] in UnknownMap:
+      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)
 
    # 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]);
-  
+   if Name[0] in UnknownMap:
+      Stat = "unknown map hit for" + str(Name)
+      return (UnknownMap[Name[0]], [Stat])
+
    # Search for a possible first/last name hit
    try:
    # Search for a possible first/last name hit
    try:
-      Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(&(cn=%s)(sn=%s))"%(cn,sn),["uid"]);
+      Attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "(&(cn=%s)(sn=%s))" % (cn, sn), ["uid"])
    except ldap.FILTER_ERROR:
    except ldap.FILTER_ERROR:
-      Stat = "Filter failure: (&(cn=%s)(sn=%s))"%(cn,sn);
-      return (None,[Stat]);
+      Stat = "Filter failure: (&(cn=%s)(sn=%s))" % (cn, sn)
+      return (None, [Stat])
 
    # Try matching on the email address
 
    # Try matching on the email address
-   if (len(Attrs) != 1):
+   if len(Attrs) != 1:
       try:
       try:
-         Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"emailforward=%s"%(Name[2]),["uid"]);
+         Attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "emailforward=%s" % (Name[2]), ["uid"])
       except ldap.FILTER_ERROR:
       except ldap.FILTER_ERROR:
-        pass;
+          pass
 
    # Hmm, more than one/no return
 
    # Hmm, more than one/no return
-   if (len(Attrs) != 1):
+   if len(Attrs) != 1:
       # Key claims a local address
       if Name[2] == EmailAppend:
 
          # Pull out the record for the claimed user
       # 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"]);
+         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)'
 
          # 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
+         # Some people put their names backwards on their key too.. check that as well
          if len(Attrs) == 1 and \
          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]);
+            (sn.lower().find(Attrs[0][1]["sn"][0].lower()) != -1 or
+            cn.lower().find(Attrs[0][1]["sn"][0].lower()) != -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.
 
       # 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"]);
+      Attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "(sn~=%s)" % (sn,), ["uid", "sn", "cn"])
 
 
-      Stat = [];
+      Stat = []
       if len(Attrs) != 0:
       if len(Attrs) != 0:
-         Stat = ["None for %s"%(str(Name))];
+         Stat = ["None for %s" % (str(Name))]
       for x in Attrs:
       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);       
+         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:
    else:
-      return (Attrs[0][1]["uid"][0],None);
+      return (Attrs[0][1]["uid"][0], None)
+
+   return (None, None)
 
 
-   return (None,None);
 
 
-def Group2GID(name):
-   """Returns the numerical id of a common group"""
+def Group2GID(l, name):
+   """
+   Returns the numerical id of a common group
+   on error returns -1
+   """
    for g in DebianGroups.keys():
       if name == g:
          return DebianGroups[g]
    for g in DebianGroups.keys():
       if name == g:
          return DebianGroups[g]
-   return name
+
+   filter = "(gid=%s)" % name
+   res = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, filter, ["gidNumber"])
+   if res:
+      return int(GetAttr(res[0], "gidNumber"))
+
+   return -1
+
+
+def make_hmac(str):
+   if 'UD_HMAC_KEY' in os.environ:
+      HmacKey = os.environ['UD_HMAC_KEY']
+   else:
+      File = open(PassDir + "/key-hmac-" + pwd.getpwuid(os.getuid())[0], "r")
+      HmacKey = File.readline().strip()
+      File.close()
+   return hmac.new(HmacKey, str, hashlib.sha1).hexdigest()
+
+
+def make_passwd_hmac(status, purpose, uid, uuid, hosts, cryptedpass):
+   return make_hmac(':'.join([status, purpose, uid, uuid, hosts, cryptedpass]))