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