3 # Generates passwd, shadow and group files from the ldap directory.
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 # Copyright (c) 2008 Mark Hymers <mhy@debian.org>
12 # Copyright (c) 2008 Luk Claes <luk@debian.org>
13 # Copyright (c) 2008 Thomas Viehmann <tv@beamnet.de>
14 # Copyright (c) 2009 Stephen Gran <steve@lobefin.net>
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 import string, re, time, ldap, getopt, sys, os, pwd, posix, socket, base64, sha, shutil, errno, tarfile, grp
31 from userdir_ldap import *
32 from userdir_exceptions import *
45 UUID_FORMAT = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
47 EmailCheck = re.compile("^([^ <>@]+@[^ ,<>@]+)?$")
48 BSMTPCheck = re.compile(".*mx 0 (master)\.debian\.org\..*",re.DOTALL)
49 PurposeHostField = re.compile(r".*\[\[([\*\-]?[a-z0-9.\-]*)(?:\|.*)?\]\]")
50 IsV6Addr = re.compile("^[a-fA-F0-9:]+$")
51 IsDebianHost = re.compile("[a-zA-Z0-9\.]+\.debian\.org$")
52 DNSZone = ".debian.net"
53 Keyrings = ConfModule.sync_keyrings.split(":")
55 def safe_makedirs(dir):
59 if e.errno == errno.EEXIST:
68 if e.errno == errno.ENOENT:
74 return Str.translate(string.maketrans("\n\r\t", "$$$"))
76 def DoLink(From, To, File):
78 posix.remove(To + File)
81 posix.link(From + File, To + File)
83 def IsRetired(DnRecord):
85 Looks for accountStatus in the LDAP record and tries to
86 match it against one of the known retired statuses
89 status = GetAttr(DnRecord, "accountStatus", None)
96 if status == "inactive":
99 elif status == "memorial":
102 elif status == "retiring":
103 # We'll give them a few extra days over what we said
104 age = 6 * 31 * 24 * 60 * 60
106 return (time.time() - time.mktime(time.strptime(line[1], "%Y-%m-%d"))) > age
116 return int(GetAttr(x, "gidNumber", 0)) == 800
120 # See if this user is in the group list
121 def IsInGroup(DnRecord):
125 # See if the primary group is in the list
126 if Allowed.has_key(GetAttr(DnRecord, "gidNumber")) != 0:
129 # Check the host based ACL
130 if DnRecord[1].has_key("allowedHost") != 0:
131 if CurrentHost in DnRecord[1]["allowedHost"]:
134 # See if there are supplementary groups
135 if DnRecord[1].has_key("supplementaryGid") == 0:
139 addGroups(supgroups, DnRecord[1]["supplementaryGid"], GetAttr(DnRecord, "uid"))
141 if Allowed.has_key(g):
145 def Die(File, F, Fdb):
151 os.remove(File + ".tmp")
155 os.remove(File + ".tdb.tmp")
159 def Done(File, F, Fdb):
162 os.rename(File + ".tmp", File)
165 os.rename(File + ".tdb.tmp", File + ".tdb")
167 # Generate the password list
168 def GenPasswd(File, HomePrefix, PwdMarker):
171 F = open(File + ".tdb.tmp", "w")
174 # Fetch all the users
178 for x in PasswdAttrs:
179 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
182 # Do not let people try to buffer overflow some busted passwd parser.
183 if len(GetAttr(x, "gecos")) > 100 or len(GetAttr(x, "loginShell")) > 50:
186 userlist[GetAttr(x, "uid")] = int(GetAttr(x, "gidNumber"))
187 Line = "%s:%s:%s:%s:%s:%s%s:%s" % (GetAttr(x, "uid"),\
189 GetAttr(x, "uidNumber"), GetAttr(x, "gidNumber"),\
190 GetAttr(x, "gecos"), HomePrefix, GetAttr(x, "uid"),\
191 GetAttr(x, "loginShell"))
193 Line = Sanitize(Line) + "\n"
194 F.write("0%u %s" % (I, Line))
195 F.write(".%s %s" % (GetAttr(x, "uid"), Line))
196 F.write("=%s %s" % (GetAttr(x, "uidNumber"), Line))
199 # Oops, something unspeakable happened.
205 # Return the list of users so we know which keys to export
208 # Generate the shadow list
212 OldMask = os.umask(0077)
213 F = open(File + ".tdb.tmp", "w", 0600)
216 # Fetch all the users
220 for x in PasswdAttrs:
221 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
224 Pass = GetAttr(x, "userPassword")
225 if Pass[0:7] != "{crypt}" or len(Pass) > 50:
230 # If the account is locked, mark it as such in shadow
231 # See Debian Bug #308229 for why we set it to 1 instead of 0
232 if (GetAttr(x, "userPassword").find("*LK*") != -1) \
233 or GetAttr(x, "userPassword").startswith("!"):
236 ShadowExpire = GetAttr(x, "shadowExpire")
238 Line = "%s:%s:%s:%s:%s:%s:%s:%s:" % (GetAttr(x, "uid"),\
239 Pass, GetAttr(x, "shadowLastChange"),\
240 GetAttr(x, "shadowMin"), GetAttr(x, "shadowMax"),\
241 GetAttr(x, "shadowWarning"), GetAttr(x, "shadowInactive"),\
243 Line = Sanitize(Line) + "\n"
244 F.write("0%u %s" % (I, Line))
245 F.write(".%s %s" % (GetAttr(x, "uid"), Line))
248 # Oops, something unspeakable happened.
254 # Generate the sudo passwd file
255 def GenShadowSudo(File, untrusted):
258 OldMask = os.umask(0077)
259 F = open(File + ".tmp", "w", 0600)
262 # Fetch all the users
265 for x in PasswdAttrs:
267 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
270 if x[1].has_key('sudoPassword'):
271 for entry in x[1]['sudoPassword']:
272 Match = re.compile('^('+UUID_FORMAT+') (confirmed:[0-9a-f]{40}|unconfirmed) ([a-z0-9.,*]+) ([^ ]+)$').match(entry)
275 uuid = Match.group(1)
276 status = Match.group(2)
277 hosts = Match.group(3)
278 cryptedpass = Match.group(4)
280 if status != 'confirmed:'+make_passwd_hmac('password-is-confirmed', 'sudo', x[1]['uid'][0], uuid, hosts, cryptedpass):
282 for_all = hosts == "*"
283 for_this_host = CurrentHost in hosts.split(',')
284 if not (for_all or for_this_host):
286 # ignore * passwords for untrusted hosts, but copy host specific passwords
287 if for_all and untrusted:
290 if for_this_host: # this makes sure we take a per-host entry over the for-all entry
295 Line = "%s:%s" % (GetAttr(x, "uid"), Pass)
296 Line = Sanitize(Line) + "\n"
297 F.write("%s" % (Line))
299 # Oops, something unspeakable happened.
305 # Generate the shadow list
307 # Fetch all the users
312 safe_rmtree(os.path.join(GlobalDir, 'userkeys'))
313 safe_makedirs(os.path.join(GlobalDir, 'userkeys'))
315 for x in PasswdAttrs:
317 if x[1].has_key("uidNumber") == 0 or \
318 x[1].has_key("sshRSAAuthKey") == 0:
321 User = GetAttr(x, "uid")
325 OldMask = os.umask(0077)
326 File = os.path.join(GlobalDir, 'userkeys', User)
327 F = open(File + ".tmp", "w", 0600)
330 for I in x[1]["sshRSAAuthKey"]:
331 MultipleLine = "%s" % I
332 MultipleLine = Sanitize(MultipleLine) + "\n"
333 F.write(MultipleLine)
336 userfiles.append(os.path.basename(File))
338 # Oops, something unspeakable happened.
341 Die(masterFileName, masterFile, None)
346 def GenSSHtarballs(userlist, SSHFiles, grouprevmap, target):
347 OldMask = os.umask(0077)
348 tf = tarfile.open(name=os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), mode='w:gz')
350 for f in userlist.keys():
351 if f not in SSHFiles:
353 # If we're not exporting their primary group, don't export
356 if userlist[f] in grouprevmap.keys():
357 grname = grouprevmap[userlist[f]]
360 if int(userlist[f]) <= 100:
361 # In these cases, look it up in the normal way so we
362 # deal with cases where, for instance, users are in group
363 # users as their primary group.
364 grname = grp.getgrgid(userlist[f])[0]
369 print "User %s is supposed to have their key exported to host %s but their primary group (gid: %d) isn't in LDAP" % (f, CurrentHost, userlist[f])
372 to = tf.gettarinfo(os.path.join(GlobalDir, 'userkeys', f), f)
373 # These will only be used where the username doesn't
374 # exist on the target system for some reason; hence,
375 # in those cases, the safest thing is for the file to
376 # be owned by root but group nobody. This deals with
377 # the bloody obscure case where the group fails to exist
378 # whilst the user does (in which case we want to avoid
379 # ending up with a file which is owned user:root to avoid
380 # a fairly obvious attack vector)
383 # Using the username / groupname fields avoids any need
384 # to give a shit^W^W^Wcare about the UIDoffset stuff.
388 tf.addfile(to, file(os.path.join(GlobalDir, 'userkeys', f)))
391 os.rename(os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), target)
393 # add a list of groups to existing groups,
394 # including all subgroups thereof, recursively.
395 # basically this proceduces the transitive hull of the groups in
397 def addGroups(existingGroups, newGroups, uid):
398 for group in newGroups:
399 # if it's a <group>@host, split it and verify it's on the current host.
400 s = group.split('@', 1)
401 if len(s) == 2 and s[1] != CurrentHost:
405 # let's see if we handled this group already
406 if group in existingGroups:
409 if not GroupIDMap.has_key(group):
410 print "Group", group, "does not exist but", uid, "is in it"
413 existingGroups.append(group)
415 if SubGroupMap.has_key(group):
416 addGroups(existingGroups, SubGroupMap[group], uid)
418 # Generate the group list
423 F = open(File + ".tdb.tmp", "w")
425 # Generate the GroupMap
427 for x in GroupIDMap.keys():
429 GroupHasPrimaryMembers = {}
431 # Fetch all the users
434 # Sort them into a list of groups having a set of users
435 for x in PasswdAttrs:
436 uid = GetAttr(x, "uid")
437 if 'gidNumber' in x[1]:
438 GroupHasPrimaryMembers[ int(x[1]["gidNumber"][0]) ] = True
439 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
441 if x[1].has_key("supplementaryGid") == 0:
445 addGroups(supgroups, x[1]["supplementaryGid"], uid)
447 GroupMap[g].append(uid)
449 # Output the group file.
451 for x in GroupMap.keys():
452 if GroupIDMap.has_key(x) == 0:
455 if len(GroupMap[x]) == 0 and GroupIDMap[x] not in GroupHasPrimaryMembers:
458 grouprevmap[GroupIDMap[x]] = x
460 Line = "%s:x:%u:" % (x, GroupIDMap[x])
462 for I in GroupMap[x]:
463 Line = Line + ("%s%s" % (Comma, I))
465 Line = Sanitize(Line) + "\n"
466 F.write("0%u %s" % (J, Line))
467 F.write(".%s %s" % (x, Line))
468 F.write("=%u %s" % (GroupIDMap[x], Line))
471 # Oops, something unspeakable happened.
481 for x in PasswdAttrs:
482 if x[1].has_key("emailForward") == 0:
486 x[1].pop("emailForward")
489 # Do not allow people to try to buffer overflow busted parsers
490 if len(GetAttr(x, "emailForward")) > 200:
491 x[1].pop("emailForward")
494 # Check the forwarding address
495 if EmailCheck.match(GetAttr(x, "emailForward")) == None:
496 x[1].pop("emailForward")
498 # Generate the email forwarding list
499 def GenForward(File):
502 OldMask = os.umask(0022)
503 F = open(File + ".tmp", "w", 0644)
506 # Fetch all the users
509 # Write out the email address for each user
510 for x in PasswdAttrs:
511 if x[1].has_key("emailForward") == 0:
514 Line = "%s: %s" % (GetAttr(x, "uid"), GetAttr(x, "emailForward"))
515 Line = Sanitize(Line) + "\n"
518 # Oops, something unspeakable happened.
524 def GenCDB(File, Users, Key):
527 OldMask = os.umask(0022)
528 Fdb = os.popen("cdbmake %s %s.tmp"%(File, File), "w")
531 # Write out the email address for each user
535 Value = GetAttr(x, Key)
536 User = GetAttr(x, "uid")
537 Fdb.write("+%d,%d:%s->%s\n" % (len(User), len(Value), User, Value))
540 # Oops, something unspeakable happened.
544 if Fdb.close() != None:
545 raise "cdbmake gave an error"
547 # Generate the anon XEarth marker file
548 def GenMarkers(File):
551 F = open(File + ".tmp", "w")
553 # Fetch all the users
556 # Write out the position for each user
557 for x in PasswdAttrs:
558 if x[1].has_key("latitude") == 0 or x[1].has_key("longitude") == 0:
561 Line = "%8s %8s \"\""%(DecDegree(GetAttr(x, "latitude"), 1), DecDegree(GetAttr(x, "longitude"), 1))
562 Line = Sanitize(Line) + "\n"
567 # Oops, something unspeakable happened.
573 # Generate the debian-private subscription list
574 def GenPrivate(File):
577 F = open(File + ".tmp", "w")
579 # Fetch all the users
582 # Write out the position for each user
583 for x in DebianDDUsers:
584 if x[1].has_key("privateSub") == 0:
587 # If the account has no PGP key, do not write it
588 if x[1].has_key("keyFingerPrint") == 0:
592 Line = "%s"%(GetAttr(x, "privateSub"))
593 Line = Sanitize(Line) + "\n"
598 # Oops, something unspeakable happened.
604 # Generate a list of locked accounts
605 def GenDisabledAccounts(File):
608 F = open(File + ".tmp", "w")
610 # Fetch all the users
615 for x in PasswdAttrs:
616 if x[1].has_key("uidNumber") == 0:
619 Pass = GetAttr(x, "userPassword")
621 # *LK* is the reference value for a locked account
622 # password starting with ! is also a locked account
623 if Pass.find("*LK*") != -1 or Pass.startswith("!"):
624 # Format is <login>:<reason>
625 Line = "%s:%s" % (GetAttr(x, "uid"), "Account is locked")
626 DisabledUsers.append(x)
629 F.write(Sanitize(Line) + "\n")
632 # Oops, something unspeakable happened.
638 # Generate the list of local addresses that refuse all mail
639 def GenMailDisable(File):
642 F = open(File + ".tmp", "w")
644 # Fetch all the users
647 for x in PasswdAttrs:
650 if x[1].has_key("mailDisableMessage"):
651 Reason = GetAttr(x, "mailDisableMessage")
656 Line = "%s: %s"%(GetAttr(x, "uid"), Reason)
657 Line = Sanitize(Line) + "\n"
662 # Oops, something unspeakable happened.
668 # Generate a list of uids that should have boolean affects applied
669 def GenMailBool(File, Key):
672 F = open(File + ".tmp", "w")
674 # Fetch all the users
677 for x in PasswdAttrs:
680 if x[1].has_key(Key) == 0:
683 if GetAttr(x, Key) != "TRUE":
687 Line = "%s"%(GetAttr(x, "uid"))
688 Line = Sanitize(Line) + "\n"
693 # Oops, something unspeakable happened.
699 # Generate a list of hosts for RBL or whitelist purposes.
700 def GenMailList(File, Key):
703 F = open(File + ".tmp", "w")
705 # Fetch all the users
708 for x in PasswdAttrs:
711 if x[1].has_key(Key) == 0:
718 if Key == "mailWhitelist":
719 if re.match('^[-\w.]+(/[\d]+)?$', z) == None:
722 if re.match('^[-\w.]+$', z) == None:
726 Line = GetAttr(x, "uid")
730 if Key == "mailRHSBL":
731 Line += "/$sender_address_domain"
734 Line = Sanitize(Line) + "\n"
739 # Oops, something unspeakable happened.
745 def isRoleAccount(pwEntry):
746 if not pwEntry.has_key("objectClass"):
747 raise "pwEntry has no objectClass"
748 oc = pwEntry['objectClass']
750 i = oc.index('debianRoleAccount')
755 # Generate the DNS Zone file
759 F = open(File + ".tmp", "w")
763 # for x in HostAttrs:
764 # if x[1].has_key("hostname") == 0 or \
765 # x[1].has_key("architecture") == 0 or\
766 # x[1].has_key("sshRSAHostKey") == 0:
769 # if IsDebianHost.match(GetAttr(x, "hostname")) is not None:
772 # DNSInfo = ExtractDNSInfo(x)
774 # for Line in DNSInfo:
776 # Line = "%s.\t%s" % (GetAttr(x, "hostname"), Line)
779 # Line = "\t\t\t%s" % (Line)
780 # F.write(Line + "\n")
782 # Fetch all the users
785 # Write out the zone file entry for each user
786 for x in PasswdAttrs:
787 if x[1].has_key("dnsZoneEntry") == 0:
790 # If the account has no PGP key, do not write it
791 if x[1].has_key("keyFingerPrint") == 0 and not isRoleAccount(x[1]):
794 F.write("; %s\n"%(EmailAddress(x)))
795 for z in x[1]["dnsZoneEntry"]:
796 Split = z.lower().split()
797 if Split[1].lower() == 'in':
798 for y in range(0, len(Split)):
801 Line = " ".join(Split) + "\n"
804 Host = Split[0] + DNSZone
805 if BSMTPCheck.match(Line) != None:
806 F.write("; Has BSMTP\n")
808 # Write some identification information
809 if Split[2].lower() == "a":
810 Line = "%s IN TXT \"%s\"\n"%(Split[0], EmailAddress(x))
811 for y in x[1]["keyFingerPrint"]:
812 Line = Line + "%s IN TXT \"PGP %s\"\n"%(Split[0], FormatPGPKey(y))
815 Line = "; Err %s"%(str(Split))
820 F.write("; Errors\n")
823 # Oops, something unspeakable happened.
829 def ExtractDNSInfo(x):
833 TTLprefix="%s\t"%(x[1]["dnsTTL"][0])
836 if x[1].has_key("ipHostNumber"):
837 for I in x[1]["ipHostNumber"]:
838 if IsV6Addr.match(I) != None:
839 DNSInfo.append("%sIN\tAAAA\t%s" % (TTLprefix, I))
841 DNSInfo.append("%sIN\tA\t%s" % (TTLprefix, I))
843 Host = GetAttr(x, "hostname")
844 Arch = GetAttr(x, "architecture")
847 for I in x[1]["sshRSAHostKey"]:
849 if Split[0] == 'ssh-rsa':
851 if Split[0] == 'ssh-dss':
853 if Algorithm == None:
855 Fingerprint = sha.new(base64.decodestring(Split[1])).hexdigest()
856 DNSInfo.append("%sIN\tSSHFP\t%u 1 %s" % (TTLprefix, Algorithm, Fingerprint))
859 if x[1].has_key("machine"):
860 Mach = " " + GetAttr(x, "machine")
861 DNSInfo.append("%sIN\tHINFO\t\"%s%s\" \"%s\"" % (TTLprefix, Arch, Mach, "Debian GNU/Linux"))
863 if x[1].has_key("mXRecord"):
864 for I in x[1]["mXRecord"]:
865 DNSInfo.append("%sIN\tMX\t%s" % (TTLprefix, I))
869 # Generate the DNS records
870 def GenZoneRecords(File):
873 F = open(File + ".tmp", "w")
875 # Fetch all the hosts
879 if x[1].has_key("hostname") == 0 or \
880 x[1].has_key("architecture") == 0 or\
881 x[1].has_key("sshRSAHostKey") == 0:
884 if IsDebianHost.match(GetAttr(x, "hostname")) is None:
887 DNSInfo = ExtractDNSInfo(x)
891 Line = "%s.\t%s" % (GetAttr(x, "hostname"), Line)
894 Line = "\t\t\t%s" % (Line)
898 # Oops, something unspeakable happened.
904 # Generate the BSMTP file
905 def GenBSMTP(File, HomePrefix):
908 F = open(File + ".tmp", "w")
910 # Fetch all the users
913 # Write out the zone file entry for each user
914 for x in PasswdAttrs:
915 if x[1].has_key("dnsZoneEntry") == 0:
918 # If the account has no PGP key, do not write it
919 if x[1].has_key("keyFingerPrint") == 0:
922 for z in x[1]["dnsZoneEntry"]:
923 Split = z.lower().split()
924 if Split[1].lower() == 'in':
925 for y in range(0, len(Split)):
928 Line = " ".join(Split) + "\n"
930 Host = Split[0] + DNSZone
931 if BSMTPCheck.match(Line) != None:
932 F.write("%s: user=%s group=Debian file=%s%s/bsmtp/%s\n"%(Host,
933 GetAttr(x, "uid"), HomePrefix, GetAttr(x, "uid"), Host))
936 F.write("; Errors\n")
939 # Oops, something unspeakable happened.
945 def HostToIP(Host, mapped=True):
949 if Host[1].has_key("ipHostNumber"):
950 for addr in Host[1]["ipHostNumber"]:
951 IPAdresses.append(addr)
952 if IsV6Addr.match(addr) is None and mapped == "True":
953 IPAdresses.append("::ffff:"+addr)
957 # Generate the ssh known hosts file
958 def GenSSHKnown(File, mode=None):
961 OldMask = os.umask(0022)
962 F = open(File + ".tmp", "w", 0644)
968 if x[1].has_key("hostname") == 0 or \
969 x[1].has_key("sshRSAHostKey") == 0:
971 Host = GetAttr(x, "hostname")
973 if Host.endswith(HostDomain):
974 HostNames.append(Host[:-(len(HostDomain) + 1)])
976 # in the purpose field [[host|some other text]] (where some other text is optional)
977 # makes a hyperlink on the web thing. we now also add these hosts to the ssh known_hosts
978 # file. But so that we don't have to add everything we link we can add an asterisk
979 # and say [[*... to ignore it. In order to be able to add stuff to ssh without
980 # http linking it we also support [[-hostname]] entries.
981 for i in x[1].get("purpose", []):
982 m = PurposeHostField.match(i)
985 # we ignore [[*..]] entries
986 if m.startswith('*'):
988 if m.startswith('-'):
992 if m.endswith(HostDomain):
993 HostNames.append(m[:-(len(HostDomain) + 1)])
995 for I in x[1]["sshRSAHostKey"]:
996 if mode and mode == 'authorized_keys':
998 if 'sshdistAuthKeysHost' in x[1]:
999 hosts += x[1]['sshdistAuthKeysHost']
1000 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(hosts), I)
1001 #Line = 'command="rsync --server --sender -pr . /var/cache/userdir-ldap/hosts/%s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding %s' % (Host,I)
1003 Line = "%s %s" %(",".join(HostNames + HostToIP(x, False)), I)
1004 Line = Sanitize(Line) + "\n"
1006 # Oops, something unspeakable happened.
1012 # Generate the debianhosts file (list of all IP addresses)
1016 OldMask = os.umask(0022)
1017 F = open(File + ".tmp", "w", 0644)
1026 if IsDebianHost.match(GetAttr(x, "hostname")) is None:
1029 if not 'ipHostNumber' in x[1]:
1032 addrs = x[1]["ipHostNumber"]
1034 if addr not in seen:
1036 addr = Sanitize(addr) + "\n"
1039 # Oops, something unspeakable happened.
1045 def GenKeyrings(OutDir):
1047 shutil.copy(k, OutDir)
1049 # Connect to the ldap server
1051 F = open(PassDir + "/pass-" + pwd.getpwuid(os.getuid())[0], "r")
1052 Pass = F.readline().strip().split(" ")
1054 l.simple_bind_s("uid=" + Pass[0] + "," + BaseDn, Pass[1])
1056 # Fetch all the groups
1058 Attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "gid=*",\
1059 ["gid", "gidNumber", "subGroup"])
1061 # Generate the SubGroupMap and GroupIDMap
1063 if x[1].has_key("accountStatus") and x[1]['accountStatus'] == "disabled":
1065 if x[1].has_key("gidNumber") == 0:
1067 GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidNumber"][0])
1068 if x[1].has_key("subGroup") != 0:
1069 SubGroupMap.setdefault(x[1]["gid"][0], []).extend(x[1]["subGroup"])
1071 # Fetch all the users
1072 PasswdAttrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "uid=*",\
1073 ["uid", "uidNumber", "gidNumber", "supplementaryGid",\
1074 "gecos", "loginShell", "userPassword", "shadowLastChange",\
1075 "shadowMin", "shadowMax", "shadowWarning", "shadowInactive",
1076 "shadowExpire", "emailForward", "latitude", "longitude",\
1077 "allowedHost", "sshRSAAuthKey", "dnsZoneEntry", "cn", "sn",\
1078 "keyFingerPrint", "privateSub", "mailDisableMessage",\
1079 "mailGreylisting", "mailCallout", "mailRBL", "mailRHSBL",\
1080 "mailWhitelist", "sudoPassword", "objectClass", "accountStatus",\
1081 "mailContentInspectionAction"])
1083 if PasswdAttrs is None:
1084 raise UDEmptyList, "No Users"
1086 PasswdAttrs.sort(lambda x, y: cmp((GetAttr(x, "uid")).lower(), (GetAttr(y, "uid")).lower()))
1088 # Fetch all the hosts
1089 HostAttrs = l.search_s(HostBaseDn, ldap.SCOPE_ONELEVEL, "objectClass=debianServer",\
1090 ["hostname", "sshRSAHostKey", "purpose", "allowedGroups", "exportOptions",\
1091 "mXRecord", "ipHostNumber", "dnsTTL", "machine", "architecture"])
1093 if HostAttrs == None:
1094 raise UDEmptyList, "No Hosts"
1096 HostAttrs.sort(lambda x, y: cmp((GetAttr(x, "hostname")).lower(), (GetAttr(y, "hostname")).lower()))
1098 # Generate global things
1099 GlobalDir = GenerateDir + "/"
1100 GenDisabledAccounts(GlobalDir + "disabled-accounts")
1102 PasswdAttrs = filter(lambda x: not IsRetired(x), PasswdAttrs)
1103 DebianDDUsers = filter(lambda x: IsGidDebian(x), PasswdAttrs)
1107 GenMailDisable(GlobalDir + "mail-disable")
1108 GenCDB(GlobalDir + "mail-forward.cdb", PasswdAttrs, 'emailForward')
1109 GenCDB(GlobalDir + "mail-contentinspectionaction.cdb", PasswdAttrs, 'mailContentInspectionAction')
1110 GenPrivate(GlobalDir + "debian-private")
1111 GenSSHKnown(GlobalDir+"authorized_keys", 'authorized_keys')
1112 GenMailBool(GlobalDir + "mail-greylist", "mailGreylisting")
1113 GenMailBool(GlobalDir + "mail-callout", "mailCallout")
1114 GenMailList(GlobalDir + "mail-rbl", "mailRBL")
1115 GenMailList(GlobalDir + "mail-rhsbl", "mailRHSBL")
1116 GenMailList(GlobalDir + "mail-whitelist", "mailWhitelist")
1117 GenKeyrings(GlobalDir)
1120 GenForward(GlobalDir + "forward-alias")
1122 PasswdAttrs = filter(lambda x: not x in DisabledUsers, PasswdAttrs)
1124 SSHFiles = GenSSHShadow()
1125 GenMarkers(GlobalDir + "markers")
1126 GenSSHKnown(GlobalDir + "ssh_known_hosts")
1127 GenHosts(GlobalDir + "debianhosts")
1129 for host in HostAttrs:
1130 if not "hostname" in host[1]:
1133 CurrentHost = host[1]['hostname'][0]
1134 OutDir = GenerateDir + '/' + CurrentHost + '/'
1140 # Get the group list and convert any named groups to numerics
1142 for groupname in AllowedGroupsPreload.strip().split(" "):
1143 GroupList[groupname] = True
1144 if 'allowedGroups' in host[1]:
1145 for groupname in host[1]['allowedGroups']:
1146 GroupList[groupname] = True
1147 for groupname in GroupList.keys():
1148 if groupname in GroupIDMap:
1149 GroupList[str(GroupIDMap[groupname])] = True
1152 if 'exportOptions' in host[1]:
1153 for extra in host[1]['exportOptions']:
1154 ExtraList[extra.upper()] = True
1160 DoLink(GlobalDir, OutDir, "debianhosts")
1161 DoLink(GlobalDir, OutDir, "ssh_known_hosts")
1162 DoLink(GlobalDir, OutDir, "disabled-accounts")
1165 if 'NOPASSWD' in ExtraList:
1166 userlist = GenPasswd(OutDir + "passwd", HomePrefix, "*")
1168 userlist = GenPasswd(OutDir + "passwd", HomePrefix, "x")
1170 grouprevmap = GenGroup(OutDir + "group")
1171 GenShadowSudo(OutDir + "sudo-passwd", ('UNTRUSTED' in ExtraList) or ('NOPASSWD' in ExtraList))
1173 # Now we know who we're allowing on the machine, export
1174 # the relevant ssh keys
1175 GenSSHtarballs(userlist, SSHFiles, grouprevmap, os.path.join(OutDir, 'ssh-keys.tar.gz'))
1177 if not 'NOPASSWD' in ExtraList:
1178 GenShadow(OutDir + "shadow")
1180 # Link in global things
1181 if not 'NOMARKERS' in ExtraList:
1182 DoLink(GlobalDir, OutDir, "markers")
1183 DoLink(GlobalDir, OutDir, "mail-forward.cdb")
1184 DoLink(GlobalDir, OutDir, "mail-contentinspectionaction.cdb")
1185 DoLink(GlobalDir, OutDir, "mail-disable")
1186 DoLink(GlobalDir, OutDir, "mail-greylist")
1187 DoLink(GlobalDir, OutDir, "mail-callout")
1188 DoLink(GlobalDir, OutDir, "mail-rbl")
1189 DoLink(GlobalDir, OutDir, "mail-rhsbl")
1190 DoLink(GlobalDir, OutDir, "mail-whitelist")
1191 GenCDB(OutDir + "user-forward.cdb", filter(lambda x: IsInGroup(x), PasswdAttrs), 'emailForward')
1192 GenCDB(OutDir + "batv-tokens.cdb", filter(lambda x: IsInGroup(x), PasswdAttrs), 'bATVToken')
1193 GenCDB(OutDir + "default-mail-options.cdb", filter(lambda x: IsInGroup(x), PasswdAttrs), 'mailDefaultOptions')
1196 DoLink(GlobalDir, OutDir, "forward-alias")
1198 if 'DNS' in ExtraList:
1199 GenDNS(OutDir + "dns-zone")
1200 GenZoneRecords(OutDir + "dns-sshfp")
1202 if 'AUTHKEYS' in ExtraList:
1203 DoLink(GlobalDir, OutDir, "authorized_keys")
1205 if 'BSMTP' in ExtraList:
1206 GenBSMTP(OutDir + "bsmtp", HomePrefix)
1208 if 'PRIVATE' in ExtraList:
1209 DoLink(GlobalDir, OutDir, "debian-private")
1211 if 'KEYRING' in ExtraList:
1213 DoLink(GlobalDir, OutDir, os.path.basename(k))
1217 posix.remove(OutDir + os.path.basename(k))
1223 # vim:set shiftwidth=3: