Sync with -common tree again
[mirror/userdir-ldap.git] / ud-generate
1 #!/usr/bin/env python
2 # -*- mode: python -*-
3 # Generates passwd, shadow and group files from the ldap directory.
4
5 #   Copyright (c) 2000-2001  Jason Gunthorpe <jgg@debian.org>
6 #   Copyright (c) 2003-2004  James Troup <troup@debian.org>
7 #   Copyright (c) 2004-2005,7  Joey Schulze <joey@infodrom.org>
8 #   Copyright (c) 2001-2007  Ryan Murray <rmurray@debian.org>
9 #
10 #   This program is free software; you can redistribute it and/or modify
11 #   it under the terms of the GNU General Public License as published by
12 #   the Free Software Foundation; either version 2 of the License, or
13 #   (at your option) any later version.
14 #
15 #   This program is distributed in the hope that it will be useful,
16 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 #   GNU General Public License for more details.
19 #
20 #   You should have received a copy of the GNU General Public License
21 #   along with this program; if not, write to the Free Software
22 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 import string, re, time, ldap, getopt, sys, os, pwd, posix, socket, base64, sha
25 from userdir_ldap import *;
26
27 global Allowed;
28 global CurrentHost;
29
30 PasswdAttrs = None;
31 GroupIDMap = {};
32 Allowed = None;
33 CurrentHost = "";
34
35 EmailCheck = re.compile("^([^ <>@]+@[^ ,<>@]+)?$");
36 BSMTPCheck = re.compile(".*mx 0 (gluck)\.debian\.org\..*",re.DOTALL);
37 DNSZone = ".debian.net"
38
39 def Sanitize(Str):
40   return Str.translate(string.maketrans("\n\r\t","$$$"))
41
42 def DoLink(From,To,File):
43    try: posix.remove(To+File);
44    except: pass;
45    posix.link(From+File,To+File);
46
47 # See if this user is in the group list
48 def IsInGroup(DnRecord):
49   if Allowed == None:
50      return 1;
51
52   # See if the primary group is in the list
53   if Allowed.has_key(GetAttr(DnRecord,"gidNumber")) != 0:
54      return 1;
55
56   # Check the host based ACL
57   if DnRecord[1].has_key("allowedHost") != 0:
58      for I in DnRecord[1]["allowedHost"]:
59         if CurrentHost == I:
60            return 1;
61
62   # See if there are supplementary groups
63   if DnRecord[1].has_key("supplementaryGid") == 0:
64      return 0;
65
66   # Check the supplementary groups
67   for I in DnRecord[1]["supplementaryGid"]:
68      if Allowed.has_key(I):
69         return 1;
70   return 0;
71
72 def Die(File,F,Fdb):
73    if F != None:
74       F.close();
75    if Fdb != None:
76       Fdb.close();
77    try: os.remove(File + ".tmp");
78    except: pass;
79    try: os.remove(File + ".tdb.tmp");
80    except: pass;
81
82 def Done(File,F,Fdb):
83   if F != None:
84     F.close();
85     os.rename(File + ".tmp",File);
86   if Fdb != None:
87     Fdb.close();
88     os.rename(File + ".tdb.tmp",File+".tdb");
89   
90 # Generate the password list
91 def GenPasswd(l,File,HomePrefix,PwdMarker,UidShift):
92   F = None;
93   try:
94    F = open(File + ".tdb.tmp","w");
95
96    # Fetch all the users
97    global PasswdAttrs;
98    if PasswdAttrs == None:
99       raise "No Users";
100
101    I = 0;
102    for x in PasswdAttrs:
103       if x[1].has_key("uidNumber") == 0 or IsInGroup(x) == 0:
104          continue;
105
106       # Do not let people try to buffer overflow some busted passwd parser.
107       if len(GetAttr(x,"gecos")) > 100 or len(GetAttr(x,"loginShell")) > 50:
108          continue;
109
110       Line = "%s:%s:%s:%s:%s:%s%s:%s" % (GetAttr(x,"uid"),\
111               PwdMarker,\
112               GetAttr(x,"uidNumber"),GetAttr(x,"gidNumber"),\
113               GetAttr(x,"gecos"),HomePrefix,GetAttr(x,"uid"),\
114               GetAttr(x,"loginShell"));
115
116       Line = Sanitize(Line) + "\n";
117       F.write("0%u %s" % (I,Line));
118       F.write(".%s %s" % (GetAttr(x,"uid"),Line));
119       F.write("=%s %s" % ((int(GetAttr(x,"uidNumber"))+UidShift),Line));
120       I = I + 1;
121
122   # Oops, something unspeakable happened.
123   except:
124    Die(File,None,F);
125    raise;
126   Done(File,None,F);
127
128 # Generate the shadow list
129 def GenShadow(l,File):
130   F = None;
131   try:
132    OldMask = os.umask(0077);
133    F = open(File + ".tdb.tmp","w",0600);
134    os.umask(OldMask);
135
136    # Fetch all the users
137    global PasswdAttrs;
138    if PasswdAttrs == None:
139       raise "No Users";
140
141    I = 0;
142    for x in PasswdAttrs:
143       if x[1].has_key("uidNumber") == 0 or IsInGroup(x) == 0:
144          continue;
145          
146       Pass = GetAttr(x,"userPassword");
147       if Pass[0:7] != "{crypt}" or len(Pass) > 50:
148          Pass = '*';
149       else:
150          Pass = Pass[7:];
151
152       # If the account is locked, mark it as such in shadow
153       # See Debian Bug #308229 for why we set it to 1 instead of 0
154       if (GetAttr(x,"userPassword").find("*LK*") != -1) \
155           or GetAttr(x,"userPassword").startswith("!"):
156          ShadowExpire = '1'
157       else:
158          ShadowExpire = GetAttr(x,"shadowexpire")
159
160       Line = "%s:%s:%s:%s:%s:%s:%s:%s:" % (GetAttr(x,"uid"),\
161               Pass,GetAttr(x,"shadowLastChange"),\
162               GetAttr(x,"shadowMin"),GetAttr(x,"shadowMax"),\
163               GetAttr(x,"shadowWarning"),GetAttr(x,"shadowinactive"),\
164               ShadowExpire);
165       Line = Sanitize(Line) + "\n";
166       F.write("0%u %s" % (I,Line));
167       F.write(".%s %s" % (GetAttr(x,"uid"),Line));
168       I = I + 1;
169
170   # Oops, something unspeakable happened.
171   except:
172    Die(File,None,F);
173    raise;
174   Done(File,None,F);
175
176 # Generate the shadow list
177 def GenSSHShadow(l,File):
178   F = None;
179   try:
180    OldMask = os.umask(0077);
181    F = open(File + ".tmp","w",0600);
182    os.umask(OldMask);
183
184    # Fetch all the users
185    global PasswdAttrs;
186    if PasswdAttrs == None:
187       raise "No Users";
188
189    for x in PasswdAttrs:
190       # If the account is locked, do not write it.
191       # This is a partial stop-gap. The ssh also needs to change this
192       # to ignore ~/.ssh/authorized* files.
193       if (GetAttr(x,"userPassword").find("*LK*") != -1) \
194              or GetAttr(x,"userPassword").startswith("!"):
195          continue;
196
197       if x[1].has_key("uidNumber") == 0 or \
198          x[1].has_key("sshRSAAuthKey") == 0:
199          continue;
200       for I in x[1]["sshRSAAuthKey"]:
201          User = GetAttr(x,"uid");
202          Line = "%s: %s" %(User,I);
203          Line = Sanitize(Line) + "\n";
204          F.write(Line);
205   # Oops, something unspeakable happened.
206   except:
207    Die(File,F,None);
208    raise;
209   Done(File,F,None);
210
211 # Generate the group list
212 def GenGroup(l,File,UidShift):
213   F = None;
214   try:
215    F = open(File + ".tdb.tmp","w");
216
217    # Generate the GroupMap
218    GroupMap = {};
219    for x in GroupIDMap.keys():
220       GroupMap[x] = [];
221       
222    # Fetch all the users
223    global PasswdAttrs;
224    if PasswdAttrs == None:
225       raise "No Users";
226
227    # Sort them into a list of groups having a set of users
228    for x in PasswdAttrs:
229       if x[1].has_key("uidNumber") == 0 or IsInGroup(x) == 0:
230          continue;
231       if x[1].has_key("supplementaryGid") == 0:
232          continue;
233          
234       for I in x[1]["supplementaryGid"]:
235          if GroupMap.has_key(I):
236             GroupMap[I].append(GetAttr(x,"uid"));
237          else:
238             print "Group does not exist ",I,"but",GetAttr(x,"uid"),"is in it";
239             
240    # Output the group file.
241    J = 0;
242    for x in GroupMap.keys():
243       if GroupIDMap.has_key(x) == 0:
244          continue;
245       NewGid = int(GroupIDMap[x]);
246       if NewGid >= 500:
247          NewGid = NewGid + UidShift
248       Line = "%s:x:%u:" % (x,NewGid)
249       Comma = '';
250       for I in GroupMap[x]:
251         Line = Line + ("%s%s" % (Comma,I));
252         Comma = ',';
253       Line = Sanitize(Line) + "\n";
254       F.write("0%u %s" % (J,Line));
255       F.write(".%s %s" % (x,Line));
256       F.write("=%u %s" % (NewGid,Line));
257       J = J + 1;
258       
259   # Oops, something unspeakable happened.
260   except:
261    Die(File,None,F);
262    raise;
263   Done(File,None,F);
264
265 # Generate the email forwarding list
266 def GenForward(l,File):
267   F = None;
268   try:
269    OldMask = os.umask(0022);
270    F = open(File + ".tmp","w",0644);
271    os.umask(OldMask);
272
273    # Fetch all the users
274    global PasswdAttrs;
275    if PasswdAttrs == None:
276       raise "No Users";
277
278    # Write out the email address for each user
279    for x in PasswdAttrs:
280       if x[1].has_key("emailForward") == 0 or IsInGroup(x) == 0:
281          continue;
282       
283       # Do not allow people to try to buffer overflow busted parsers
284       if len(GetAttr(x,"emailForward")) > 200:
285          continue;
286
287       # Check the forwarding address
288       if EmailCheck.match(GetAttr(x,"emailForward")) == None:
289          continue;
290       Line = "%s: %s" % (GetAttr(x,"uid"),GetAttr(x,"emailForward"));
291       Line = Sanitize(Line) + "\n";
292       F.write(Line);
293       
294   # Oops, something unspeakable happened.
295   except:
296    Die(File,F,None);
297    raise;
298   Done(File,F,None);
299
300 def GenAllForward(l,File):
301   Fdb = None;
302   try:
303    OldMask = os.umask(0022);
304    Fdb = os.popen("cdbmake %s %s.tmp"%(File,File),"w");
305    os.umask(OldMask);
306
307    # Fetch all the users
308    global PasswdAttrs;
309    if PasswdAttrs == None:
310       raise "No Users";
311
312    # Write out the email address for each user
313    for x in PasswdAttrs:
314       if x[1].has_key("emailForward") == 0:
315          continue;
316       
317       # Do not allow people to try to buffer overflow busted parsers
318       Forward = GetAttr(x,"emailForward");
319       if len(Forward) > 200:
320          continue;
321
322       # Check the forwarding address
323       if EmailCheck.match(Forward) == None:
324          continue;
325          
326       User = GetAttr(x,"uid");
327       Fdb.write("+%d,%d:%s->%s\n"%(len(User),len(Forward),User,Forward));
328    Fdb.write("\n");
329   # Oops, something unspeakable happened.
330   except:
331     Fdb.close();
332     raise;
333   if Fdb.close() != None:
334     raise "cdbmake gave an error";
335
336 # Generate the anon XEarth marker file 
337 def GenMarkers(l,File):
338   F = None;
339   try:
340    F = open(File + ".tmp","w");
341
342    # Fetch all the users
343    global PasswdAttrs;
344    if PasswdAttrs == None:
345       raise "No Users";
346
347    # Write out the position for each user
348    for x in PasswdAttrs:
349       if x[1].has_key("latitude") == 0 or x[1].has_key("longitude") == 0:
350          continue;       
351       try:
352          Line = "%8s %8s \"\""%(DecDegree(GetAttr(x,"latitude"),1),DecDegree(GetAttr(x,"longitude"),1));
353          Line = Sanitize(Line) + "\n";
354          F.write(Line);
355       except:
356          pass;
357       
358   # Oops, something unspeakable happened.
359   except:
360    Die(File,F,None);
361    raise;
362   Done(File,F,None);
363
364 # Generate the debian-private subscription list
365 def GenPrivate(l,File):
366   F = None;
367   try:
368    F = open(File + ".tmp","w");
369
370    # Fetch all the users
371    global PasswdAttrs;
372    if PasswdAttrs == None:
373       raise "No Users";
374
375    # Write out the position for each user
376    for x in PasswdAttrs:
377       if x[1].has_key("privateSub") == 0:
378          continue;
379
380       # If the account is locked, do not write it
381       if (GetAttr(x,"userPassword").find("*LK*") != -1) \
382              or GetAttr(x,"userPassword").startswith("!"):
383          continue;
384
385       # If the account has no PGP key, do not write it
386       if x[1].has_key("keyFingerPrint") == 0:
387          continue;
388
389       # Must be in the Debian group (yuk, hard coded for now)
390       if GetAttr(x,"gidNumber") != "800":
391          continue;
392
393       try:
394          Line = "%s"%(GetAttr(x,"privateSub"));
395          Line = Sanitize(Line) + "\n";
396          F.write(Line);
397       except:
398          pass;
399       
400   # Oops, something unspeakable happened.
401   except:
402    Die(File,F,None);
403    raise;
404   Done(File,F,None);
405
406 # Generate a list of locked accounts
407 def GenDisabledAccounts(l,File):
408   F = None;
409   try:
410    F = open(File + ".tmp","w");
411
412    # Fetch all the users
413    global PasswdAttrs;
414    if PasswdAttrs == None:
415       raise "No Users";
416
417    I = 0;
418    for x in PasswdAttrs:
419       if x[1].has_key("uidNumber") == 0:
420          continue;
421          
422       Pass = GetAttr(x,"userPassword");
423       Line = ""
424       # *LK* is the reference value for a locked account
425       # password starting with ! is also a locked account
426       if Pass.find("*LK*") != -1 or Pass.startswith("!"):
427          # Format is <login>:<reason>
428          Line = "%s:%s" % (GetAttr(x,"uid"), "Account is locked")
429
430       if Line != "":
431          F.write(Sanitize(Line) + "\n")
432
433   # Oops, something unspeakable happened.
434   except:
435    Die(File,F,None);
436    raise;
437   Done(File,F,None);
438
439 # Generate the list of local addresses that refuse all mail
440 def GenMailDisable(l,File):
441   F = None;
442   try:
443    F = open(File + ".tmp","w");
444
445    # Fetch all the users
446    global PasswdAttrs;
447    if PasswdAttrs == None:
448       raise "No Users";
449
450    for x in PasswdAttrs:
451       Reason = None
452       
453       # If the account is locked, disable incoming mail
454       if (GetAttr(x,"userPassword").find("*LK*") != -1):
455          if GetAttr(x,"uid") == "luther":
456             continue
457          else:
458             Reason = "user account locked"
459       else:
460          if x[1].has_key("mailDisableMessage"):
461             Reason = GetAttr(x,"mailDisableMessage")
462          else:
463             continue
464
465       # Must be in the Debian group (yuk, hard coded for now)
466       if GetAttr(x,"gidNumber") != "800":
467          continue;
468
469       try:
470          Line = "%s: %s"%(GetAttr(x,"uid"),Reason);
471          Line = Sanitize(Line) + "\n";
472          F.write(Line);
473       except:
474          pass;
475       
476   # Oops, something unspeakable happened.
477   except:
478    Die(File,F,None);
479    raise;
480   Done(File,F,None);
481
482 # Generate a list of uids that should have boolean affects applied
483 def GenMailBool(l,File,Key):
484   F = None;
485   try:
486    F = open(File + ".tmp","w");
487
488    # Fetch all the users
489    global PasswdAttrs;
490    if PasswdAttrs == None:
491       raise "No Users";
492
493    for x in PasswdAttrs:
494       Reason = None
495       
496       if x[1].has_key(Key) == 0:
497          continue
498
499       # Must be in the Debian group (yuk, hard coded for now)
500       if GetAttr(x,"gidNumber") != "800":
501          continue
502
503       if GetAttr(x,Key) != "TRUE":
504          continue
505
506       try:
507          Line = "%s"%(GetAttr(x,"uid"));
508          Line = Sanitize(Line) + "\n";
509          F.write(Line);
510       except:
511          pass;
512       
513   # Oops, something unspeakable happened.
514   except:
515    Die(File,F,None);
516    raise;
517   Done(File,F,None);
518
519 # Generate a list of hosts for RBL or whitelist purposes.
520 def GenMailList(l,File,Key):
521   F = None;
522   try:
523    F = open(File + ".tmp","w");
524
525    # Fetch all the users
526    global PasswdAttrs;
527    if PasswdAttrs == None:
528       raise "No Users";
529
530    for x in PasswdAttrs:
531       Reason = None
532       
533       if x[1].has_key(Key) == 0:
534          continue
535
536       # Must be in the Debian group (yuk, hard coded for now)
537       if GetAttr(x,"gidNumber") != "800":
538          continue
539
540       try:
541          found = 0
542          Line = None
543          for z in x[1][Key]:
544              if Key == "mailWhitelist":
545                  if re.match('^[-\w.]+(/[\d]+)?$',z) == None:
546                      continue
547              else:
548                  if re.match('^[-\w.]+$',z) == None:
549                      continue
550              if found == 0:
551                  found = 1
552                  Line = GetAttr(x,"uid")
553              else:
554                  Line += " "
555              Line += ": " + z
556              if Key == "mailRHSBL":
557                  Line += "/$sender_address_domain"
558
559          if Line != None:
560              Line = Sanitize(Line) + "\n";
561              F.write(Line);
562       except:
563          pass;
564       
565   # Oops, something unspeakable happened.
566   except:
567    Die(File,F,None);
568    raise;
569   Done(File,F,None);
570
571 # Generate the DNS Zone file
572 def GenDNS(l,File,HomePrefix):
573   F = None;
574   try:
575    F = open(File + ".tmp","w");
576    
577    # Fetch all the users
578    global PasswdAttrs;
579    if PasswdAttrs == None:
580       raise "No Users";
581
582    # Write out the zone file entry for each user
583    for x in PasswdAttrs:
584       if x[1].has_key("dnsZoneEntry") == 0:
585          continue;
586
587       # If the account has no PGP key, do not write it
588       if x[1].has_key("keyFingerPrint") == 0:
589          continue;
590       try:
591          F.write("; %s\n"%(EmailAddress(x)));
592          for z in x[1]["dnsZoneEntry"]:
593             Split = z.lower().split()
594             if Split[1].lower() == 'in':
595                for y in range(0,len(Split)):
596                   if Split[y] == "$":
597                      Split[y] = "\n\t";
598                Line = " ".join(Split) + "\n";
599                F.write(Line);
600                
601                Host = Split[0] + DNSZone;
602                if BSMTPCheck.match(Line) != None:
603                    F.write("; Has BSMTP\n");
604                                
605                # Write some identification information
606                if Split[2].lower() == "a":
607                   Line = "%s IN TXT \"%s\"\n"%(Split[0],EmailAddress(x));
608                   for y in x[1]["keyFingerPrint"]:
609                      Line = Line + "%s IN TXT \"PGP %s\"\n"%(Split[0],FormatPGPKey(y));
610                   F.write(Line);
611             else:
612                Line = "; Err %s"%(str(Split));
613                F.write(Line);
614
615          F.write("\n");
616       except:
617          F.write("; Errors\n");
618          pass;
619       
620   # Oops, something unspeakable happened.
621   except:
622    Die(File,F,None);
623    raise;
624   Done(File,F,None);
625
626 # Generate the DNS SSHFP records
627 def GenSSHFP(l,File,HomePrefix):
628   F = None
629   try:
630    F = open(File + ".tmp","w")
631    
632    # Fetch all the hosts
633    global HostAttrs
634    if HostAttrs == None:
635       raise "No Hosts"
636
637    for x in HostAttrs:
638       if x[1].has_key("hostname") == 0 or \
639          x[1].has_key("sshRSAHostKey") == 0:
640          continue
641       Host = GetAttr(x,"hostname");
642       Algorithm = None
643       for I in x[1]["sshRSAHostKey"]:
644          Split = I.split()
645          if Split[0] == 'ssh-rsa':
646             Algorithm = 1
647          if Split[0] == 'ssh-dss':
648             Algorithm = 2
649          if Algorithm == None:
650             continue
651          Fingerprint = sha.new(base64.decodestring(Split[1])).hexdigest()
652          Line = "%s. IN SSHFP %u 1 %s" % (Host,Algorithm,Fingerprint)
653          Line = Sanitize(Line) + "\n"
654          F.write(Line)
655   # Oops, something unspeakable happened.
656   except:
657    Die(File,F,None)
658    raise;
659   Done(File,F,None)
660
661 # Generate the BSMTP file
662 def GenBSMTP(l,File,HomePrefix):
663   F = None;
664   try:
665    F = open(File + ".tmp","w");
666    
667    # Fetch all the users
668    global PasswdAttrs;
669    if PasswdAttrs == None:
670       raise "No Users";
671
672    # Write out the zone file entry for each user
673    for x in PasswdAttrs:
674       if x[1].has_key("dnsZoneEntry") == 0:
675          continue;
676
677       # If the account has no PGP key, do not write it
678       if x[1].has_key("keyFingerPrint") == 0:
679          continue;
680       try:
681          for z in x[1]["dnsZoneEntry"]:
682             Split = z.lower().split()
683             if Split[1].lower() == 'in':
684                for y in range(0,len(Split)):
685                   if Split[y] == "$":
686                      Split[y] = "\n\t";
687                Line = " ".join(Split) + "\n";
688                
689                Host = Split[0] + DNSZone;
690                if BSMTPCheck.match(Line) != None:
691                    F.write("%s: user=%s group=Debian file=%s%s/bsmtp/%s\n"%(Host,
692                                GetAttr(x,"uid"),HomePrefix,GetAttr(x,"uid"),Host));
693                                
694       except:
695          F.write("; Errors\n");
696          pass;
697       
698   # Oops, something unspeakable happened.
699   except:
700    Die(File,F,None);
701    raise;
702   Done(File,F,None);
703
704 # Generate the ssh known hosts file
705 def GenSSHKnown(l,File):
706   F = None;
707   try:
708    OldMask = os.umask(0022);
709    F = open(File + ".tmp","w",0644);
710    os.umask(OldMask);
711
712    global HostAttrs
713    if HostAttrs == None:
714       raise "No Hosts";
715    
716    for x in HostAttrs:
717       if x[1].has_key("hostname") == 0 or \
718          x[1].has_key("sshRSAHostKey") == 0:
719          continue;
720       Host = GetAttr(x,"hostname");
721       SHost = Host.find(".")
722       for I in x[1]["sshRSAHostKey"]:
723          if SHost == None:
724             Line = "%s,%s %s" %(Host,socket.gethostbyname(Host),I);
725          else:
726             Line = "%s,%s,%s %s" %(Host,Host[0:SHost],socket.gethostbyname(Host),I);
727          Line = Sanitize(Line) + "\n";
728          F.write(Line);
729   # Oops, something unspeakable happened.
730   except:
731    Die(File,F,None);
732    raise;
733   Done(File,F,None);
734
735 # Generate the debianhosts file (list of all IP addresses)
736 def GenHosts(l,File):
737   F = None;
738   try:
739    OldMask = os.umask(0022);
740    F = open(File + ".tmp","w",0644);
741    os.umask(OldMask);
742
743    # Fetch all the hosts
744    HostNames = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"hostname=*",\
745                 ["hostname"]);
746    
747    if HostNames == None:
748       raise "No Hosts";
749
750    for x in HostNames:
751       if x[1].has_key("hostname") == 0:
752          continue;
753       Host = GetAttr(x,"hostname");
754       try:
755         Addr = socket.gethostbyname(Host);
756         F.write(Addr + "\n");
757       except:
758         pass
759   # Oops, something unspeakable happened.
760   except:
761    Die(File,F,None);
762    raise;
763   Done(File,F,None);
764
765 # Connect to the ldap server
766 l = ldap.open(LDAPServer);
767 F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r");
768 Pass = F.readline().strip().split(" ")
769 F.close();
770 l.simple_bind_s("uid="+Pass[0]+","+BaseDn,Pass[1]);
771
772 # Fetch all the groups
773 GroupIDMap = {};
774 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"gid=*",\
775                   ["gid","gidNumber"]);
776
777 # Generate the GroupMap and GroupIDMap
778 for x in Attrs:
779    if x[1].has_key("gidNumber") == 0:
780       continue;
781    GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidNumber"][0]);
782
783 # Fetch all the users
784 PasswdAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=*",\
785                 ["uid","uidNumber","gidNumber","supplementaryGid",\
786                  "gecos","loginShell","userPassword","shadowLastChange",\
787                  "shadowMin","shadowMax","shadowWarning","shadowinactive",
788                  "shadowexpire","emailForward","latitude","longitude",\
789                  "allowedHost","sshRSAAuthKey","dnsZoneEntry","cn","sn",\
790                  "keyFingerPrint","privateSub","mailDisableMessage",\
791                  "mailGreylisting","mailCallout","mailRBL","mailRHSBL",\
792                  "mailWhitelist"]);
793 # Fetch all the hosts
794 HostAttrs    = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"sshRSAHostKey=*",\
795                 ["hostname","sshRSAHostKey"]);
796
797 # Open the control file
798 if len(sys.argv) == 1:
799    F = open(GenerateConf,"r");
800 else:
801    F = open(sys.argv[1],"r")
802
803 # Generate global things
804 GlobalDir = GenerateDir+"/";
805 GenSSHShadow(l,GlobalDir+"ssh-rsa-shadow");
806 GenAllForward(l,GlobalDir+"mail-forward.cdb");
807 GenMarkers(l,GlobalDir+"markers");
808 GenPrivate(l,GlobalDir+"debian-private");
809 GenDisabledAccounts(l,GlobalDir+"disabled-accounts");
810 GenSSHKnown(l,GlobalDir+"ssh_known_hosts");
811 GenHosts(l,GlobalDir+"debianhosts");
812 GenMailDisable(l,GlobalDir+"mail-disable");
813 GenMailBool(l,GlobalDir+"mail-greylist","mailGreylisting");
814 GenMailBool(l,GlobalDir+"mail-callout","mailCallout");
815 GenMailList(l,GlobalDir+"mail-rbl","mailRBL");
816 GenMailList(l,GlobalDir+"mail-rhsbl","mailRHSBL");
817 GenMailList(l,GlobalDir+"mail-whitelist","mailWhitelist");
818
819 # Compatibility.
820 GenForward(l,GlobalDir+"forward-alias");
821    
822 while(1):
823    Line = F.readline();
824    if Line == "":
825       break;
826    Line = Line.strip()
827    if Line == "":
828       continue;
829    if Line[0] == '#':
830       continue;
831
832    Split = Line.split(" ")
833    OutDir = GenerateDir + '/' + Split[0] + '/';
834    try: os.mkdir(OutDir);
835    except: pass;
836
837    # Get the group list and convert any named groups to numerics
838    GroupList = {};
839    ExtraList = {};
840    for I in Split[2:]:
841       if I[0] == '[':
842          ExtraList[I] = None;
843          continue;
844       GroupList[I] = None;
845       if GroupIDMap.has_key(I):
846          GroupList[str(GroupIDMap[I])] = None;
847
848    Allowed = GroupList;
849    if Allowed == {}:
850      Allowed = None
851    CurrentHost = Split[0];
852
853    DoLink(GlobalDir,OutDir,"ssh-rsa-shadow");
854    DoLink(GlobalDir,OutDir,"debianhosts");
855    DoLink(GlobalDir,OutDir,"ssh_known_hosts");
856    DoLink(GlobalDir,OutDir,"disabled-accounts")
857
858    sys.stdout.flush();
859    UidShift = 0
860    if ExtraList.has_key("[UIDSHIFT]"): UidShift=1000
861    if ExtraList.has_key("[NOPASSWD]"):
862       GenPasswd(l,OutDir+"passwd",Split[1], "*", UidShift);
863    else:
864       GenPasswd(l,OutDir+"passwd",Split[1], "x", UidShift);
865    sys.stdout.flush();
866    GenGroup(l,OutDir+"group", UidShift);
867    if ExtraList.has_key("[UNTRUSTED]"):
868         continue;
869    if not ExtraList.has_key("[NOPASSWD]"):
870      GenShadow(l,OutDir+"shadow");
871         
872    # Link in global things   
873    DoLink(GlobalDir,OutDir,"markers");
874    DoLink(GlobalDir,OutDir,"mail-forward.cdb");
875    DoLink(GlobalDir,OutDir,"mail-disable");
876    DoLink(GlobalDir,OutDir,"mail-greylist");
877    DoLink(GlobalDir,OutDir,"mail-callout");
878    DoLink(GlobalDir,OutDir,"mail-rbl");
879    DoLink(GlobalDir,OutDir,"mail-rhsbl");
880    DoLink(GlobalDir,OutDir,"mail-whitelist");
881
882    # Compatibility.
883    DoLink(GlobalDir,OutDir,"forward-alias");
884
885    if ExtraList.has_key("[DNS]"):
886       GenDNS(l,OutDir+"dns-zone",Split[1]);
887       GenSSHFP(l,OutDir+"dns-sshfp",Split[1])
888       
889    if ExtraList.has_key("[BSMTP]"):
890       GenBSMTP(l,OutDir+"bsmtp",Split[1])
891
892    if ExtraList.has_key("[PRIVATE]"):
893       DoLink(GlobalDir,OutDir,"debian-private")