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