adcc8593b05b1793784de457dbdfbc40d73c929e
[mirror/userdir-ldap.git] / userdir_ldap.py
1 # Some routines and configuration that are used by the ldap progams
2 import termios, TERMIOS, re, string, imp, ldap, sys, whrandom, crypt;
3
4 try:
5    File = open("/etc/userdir-ldap/userdir-ldap.conf");
6 except:
7    File = open("userdir-ldap.conf");
8 ConfModule = imp.load_source("userdir_config","/etc/userdir-ldap.conf",File);
9 File.close();
10
11 BaseDn = ConfModule.basedn;
12 BaseDn = ConfModule.basedn;
13 LDAPServer = ConfModule.ldaphost;
14 EmailAppend = ConfModule.emailappend;
15 AdminUser = ConfModule.adminuser;
16 GenerateDir = ConfModule.generatedir;
17 GenerateConf = ConfModule.generateconf;
18 DefaultGID = ConfModule.defaultgid;
19 TemplatesDir = ConfModule.templatesdir;
20 PassDir = ConfModule.passdir;
21
22 # This is a list of common last-name prefixes
23 LastNamesPre = {"van": None, "le": None, "de": None, "di": None};
24
25 # SSH Key splitting. The result is:
26 # (options,size,modulous,exponent,comment)
27 SSHAuthSplit = re.compile('^(.* )?(\d+) (\d+) (\d+) ?(.+)$');
28 #'^([^\d](?:[^ "]+(?:".*")?)*)? ?(\d+) (\d+) (\d+) (.+)$');
29
30 # Safely get an attribute from a tuple representing a dn and an attribute
31 # list. It returns the first attribute if there are multi.
32 def GetAttr(DnRecord,Attribute,Default = ""):
33    try:
34       return DnRecord[1][Attribute][0];
35    except IndexError:
36       return Default;
37    except KeyError:
38       return Default;
39    return Default;
40
41 # Return a printable email address from the attributes.
42 def EmailAddress(DnRecord):
43    cn = GetAttr(DnRecord,"cn");
44    sn = GetAttr(DnRecord,"sn");
45    uid = GetAttr(DnRecord,"uid");
46    if cn == "" and sn == "":
47       return "<" + uid + "@" + EmailAppend + ">";
48    return cn + " " + sn + " <" + uid + "@" + EmailAppend + ">"
49
50 # Show a dump like ldapsearch
51 def PrettyShow(DnRecord):
52    Result = "";
53    List = DnRecord[1].keys();
54    List.sort();
55    for x in List:
56       Rec = DnRecord[1][x];
57       for i in Rec:
58          Result = Result + "%s: %s\n" % (x,i);
59    return Result[:-1];
60
61 # Function to prompt for a password 
62 def getpass(prompt = "Password: "):
63    import termios, TERMIOS, sys;
64    fd = sys.stdin.fileno();
65    old = termios.tcgetattr(fd);
66    new = termios.tcgetattr(fd);
67    new[3] = new[3] & ~TERMIOS.ECHO;          # lflags
68    try:
69       termios.tcsetattr(fd, TERMIOS.TCSADRAIN, new);
70       passwd = raw_input(prompt);
71    finally:
72       termios.tcsetattr(fd, TERMIOS.TCSADRAIN, old);
73    print;
74    return passwd;
75
76 # Split up a name into multiple components. This tries to best guess how
77 # to split up a name
78 def NameSplit(Name):
79    Words = re.split(" ",string.strip(Name));
80
81    # Insert an empty middle name
82    if (len(Words) == 2):
83       Words.insert(1,"");
84    if (len(Words) < 2):
85       Words.append("");
86
87    # Put a dot after any 1 letter words, must be an initial
88    for x in range(0,len(Words)):
89       if len(Words[x]) == 1:
90          Words[x] = Words[x] + '.';
91
92    # If a word starts with a -, ( or [ we assume it marks the start of some
93    # Non-name information and remove the remainder of the string
94    for x in range(0,len(Words)):
95       if len(Words[x]) != 0 and (Words[x][0] == '-' or \
96           Words[x][0] == '(' or Words[x][0] == '['):
97          Words = Words[0:x];
98          break;
99          
100    # Merge any of the middle initials
101    if len(Words) > 2:
102       while len(Words[2]) == 2 and Words[2][1] == '.':
103          Words[1] = Words[1] +  Words[2];
104          del Words[2];
105
106    while len(Words) < 2:
107       Words.append('');
108    
109    # Merge any of the last name prefixes into one big last name
110    while LastNamesPre.has_key(string.lower(Words[-2])):
111       Words[-1] = Words[-2] + " " + Words[-1];
112       del Words[-2];
113
114    # Fix up a missing middle name after lastname globbing
115    if (len(Words) == 2):
116       Words.insert(1,"");
117
118    # If the name is multi-word then we glob them all into the last name and
119    # do not worry about a middle name
120    if (len(Words) > 3):
121       Words[2] = string.join(Words[1:]);
122       Words[1] = "";
123
124    return (string.strip(Words[0]),string.strip(Words[1]),string.strip(Words[2]));
125
126 # Compute a random password using /dev/urandom
127 def GenPass():   
128    # Generate a 10 character random string
129    SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/.";
130    Rand = open("/dev/urandom");
131    Password = "";
132    for i in range(0,10):
133       Password = Password + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)];
134    return Password;
135
136 # Compute the MD5 crypted version of the given password
137 def HashPass(Password):
138    # Hash it telling glibc to use the MD5 algorithm - if you dont have
139    # glibc then just change Salt = "$1$" to Salt = "";
140    SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/.";
141    Salt  = "$1$";
142    for x in range(0,10):
143       Salt = Salt + SaltVals[whrandom.randint(0,len(SaltVals)-1)];
144    Pass = crypt.crypt(Password,Salt);
145    if len(Pass) < 14:
146       raise "Password Error", "MD5 password hashing failed, not changing the password!";
147    return Pass;
148
149 # Sync with the server, we count the number of async requests that are pending
150 # and make sure result has been called that number of times
151 def FlushOutstanding(l,Outstanding,Fast=0):
152    # Sync with the remote end
153    if Fast == 0:
154       print "Waiting for",Outstanding,"requests:",
155    while (Outstanding > 0):
156       try:
157          if Fast == 0 or Outstanding > 50:
158             sys.stdout.write(".",);
159             sys.stdout.flush();
160             if (l.result(ldap.RES_ANY,1) != (None,None)):
161                Outstanding = Outstanding - 1;
162          else:
163             if (l.result(ldap.RES_ANY,1,0) != (None,None)):
164                Outstanding = Outstanding - 1;
165             else:
166                break;
167       except ldap.TYPE_OR_VALUE_EXISTS:
168          Outstanding = Outstanding - 1;
169       except ldap.NO_SUCH_ATTRIBUTE:
170          Outstanding = Outstanding - 1;
171       except ldap.NO_SUCH_OBJECT:
172          Outstanding = Outstanding - 1;
173    if Fast == 0:
174       print;
175    return Outstanding;
176
177 # Convert a lat/long attribute into Decimal degrees
178 def DecDegree(Posn,Anon=0):
179   Parts = re.match('[+-]?(\d*)\\.?(\d*)?',Posn).groups();
180   Val = string.atof(Posn);
181
182   if (abs(Val) >= 1806060.0):
183      raise ValueError,"Too Big";
184
185   # Val is in DGMS
186   if abs(Val) >= 18060.0 or len(Parts[0]) > 5:
187      Val = Val/100.0;
188      Secs = Val - long(Val);
189      Val = long(Val)/100.0;
190      Min = Val - long(Val);
191      Val = long(Val) + (Min*100.0 + Secs*100.0/60.0)/60.0;
192
193   # Val is in DGM
194   elif abs(Val) >= 180 or len(Parts[0]) > 3:
195      Val = Val/100.0;
196      Min = Val - long(Val);
197      Val = long(Val) + Min*100.0/60.0;
198      
199   if Anon != 0:
200       Str = "%3.2f"%(Val);
201   else:
202       Str = str(Val);
203   if Val >= 0:
204      return "+" + Str;
205   return Str;
206
207 def FormatSSHAuth(Str):
208    Match = SSHAuthSplit.match(Str);
209    if Match == None:
210       return "<unknown format>";
211    G = Match.groups();
212
213    # No options
214    if G[0] == None:
215       return "%s %s %s..%s %s"%(G[1],G[2],G[3][:8],G[3][-8:],G[4]);
216    return "%s %s %s %s..%s %s"%(G[0],G[1],G[2],G[3][:8],G[3][-8:],G[4]);
217
218 def FormatPGPKey(Str):
219    Res = "";
220
221    # PGP 2.x Print
222    if (len(Str) == 32):
223       I = 0;
224       while (I < len(Str)):
225          if I+2 == 32/2:
226             Res = "%s %s%s "%(Res,Str[I],Str[I+1]);
227          else:
228             Res = "%s%s%s "%(Res,Str[I],Str[I+1]);
229          I = I + 2;
230    elif (len(Str) == 40):
231       # OpenPGP Print
232       I = 0;
233       while (I < len(Str)):
234          if I+4 == 40/2:
235             Res = "%s %s%s%s%s "%(Res,Str[I],Str[I+1],Str[I+2],Str[I+3]);
236          else:
237             Res = "%s%s%s%s%s "%(Res,Str[I],Str[I+1],Str[I+2],Str[I+3]);
238          I = I + 4;
239    else:
240       Res = Str;
241    return Res;
242