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():
430 # Fetch all the users
433 # Sort them into a list of groups having a set of users
434 for x in PasswdAttrs:
435 uid = GetAttr(x, "uid")
436 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
438 if x[1].has_key("supplementaryGid") == 0:
442 addGroups(supgroups, x[1]["supplementaryGid"], uid)
444 GroupMap[g].append(uid)
446 # Output the group file.
448 for x in GroupMap.keys():
449 grouprevmap[GroupIDMap[x]] = x
450 if GroupIDMap.has_key(x) == 0:
452 Line = "%s:x:%u:" % (x, GroupIDMap[x])
454 for I in GroupMap[x]:
455 Line = Line + ("%s%s" % (Comma, I))
457 Line = Sanitize(Line) + "\n"
458 F.write("0%u %s" % (J, Line))
459 F.write(".%s %s" % (x, Line))
460 F.write("=%u %s" % (GroupIDMap[x], Line))
463 # Oops, something unspeakable happened.
473 for x in DebianUsers:
474 if x[1].has_key("emailForward") == 0:
478 x[1].pop("emailForward")
481 # Do not allow people to try to buffer overflow busted parsers
482 if len(GetAttr(x, "emailForward")) > 200:
483 x[1].pop("emailForward")
486 # Check the forwarding address
487 if EmailCheck.match(GetAttr(x, "emailForward")) == None:
488 x[1].pop("emailForward")
490 # Generate the email forwarding list
491 def GenForward(File):
494 OldMask = os.umask(0022)
495 F = open(File + ".tmp", "w", 0644)
498 # Fetch all the users
501 # Write out the email address for each user
502 for x in DebianUsers:
503 if x[1].has_key("emailForward") == 0:
506 Line = "%s: %s" % (GetAttr(x, "uid"), GetAttr(x, "emailForward"))
507 Line = Sanitize(Line) + "\n"
510 # Oops, something unspeakable happened.
516 def GenCDB(File, Key):
519 OldMask = os.umask(0022)
520 Fdb = os.popen("cdbmake %s %s.tmp"%(File, File), "w")
523 # Fetch all the users
526 # Write out the email address for each user
527 for x in DebianUsers:
530 Value = GetAttr(x, Key)
531 User = GetAttr(x, "uid")
532 Fdb.write("+%d,%d:%s->%s\n" % (len(User), len(Value), User, Value))
535 # Oops, something unspeakable happened.
539 if Fdb.close() != None:
540 raise "cdbmake gave an error"
542 # Generate the anon XEarth marker file
543 def GenMarkers(File):
546 F = open(File + ".tmp", "w")
548 # Fetch all the users
551 # Write out the position for each user
552 for x in DebianUsers:
553 if x[1].has_key("latitude") == 0 or x[1].has_key("longitude") == 0:
556 Line = "%8s %8s \"\""%(DecDegree(GetAttr(x, "latitude"), 1), DecDegree(GetAttr(x, "longitude"), 1))
557 Line = Sanitize(Line) + "\n"
562 # Oops, something unspeakable happened.
568 # Generate the debian-private subscription list
569 def GenPrivate(File):
572 F = open(File + ".tmp", "w")
574 # Fetch all the users
577 # Write out the position for each user
578 for x in DebianUsers:
579 if x[1].has_key("privateSub") == 0:
582 # If the account has no PGP key, do not write it
583 if x[1].has_key("keyFingerPrint") == 0:
587 Line = "%s"%(GetAttr(x, "privateSub"))
588 Line = Sanitize(Line) + "\n"
593 # Oops, something unspeakable happened.
599 # Generate a list of locked accounts
600 def GenDisabledAccounts(File):
603 F = open(File + ".tmp", "w")
605 # Fetch all the users
610 for x in PasswdAttrs:
611 if x[1].has_key("uidNumber") == 0:
614 Pass = GetAttr(x, "userPassword")
616 # *LK* is the reference value for a locked account
617 # password starting with ! is also a locked account
618 if Pass.find("*LK*") != -1 or Pass.startswith("!"):
619 # Format is <login>:<reason>
620 Line = "%s:%s" % (GetAttr(x, "uid"), "Account is locked")
621 DisabledUsers.append(x)
624 F.write(Sanitize(Line) + "\n")
627 # Oops, something unspeakable happened.
633 # Generate the list of local addresses that refuse all mail
634 def GenMailDisable(File):
637 F = open(File + ".tmp", "w")
639 # Fetch all the users
642 for x in DebianUsers:
645 if x[1].has_key("mailDisableMessage"):
646 Reason = GetAttr(x, "mailDisableMessage")
651 Line = "%s: %s"%(GetAttr(x, "uid"), Reason)
652 Line = Sanitize(Line) + "\n"
657 # Oops, something unspeakable happened.
663 # Generate a list of uids that should have boolean affects applied
664 def GenMailBool(File, Key):
667 F = open(File + ".tmp", "w")
669 # Fetch all the users
672 for x in DebianUsers:
675 if x[1].has_key(Key) == 0:
678 if GetAttr(x, Key) != "TRUE":
682 Line = "%s"%(GetAttr(x, "uid"))
683 Line = Sanitize(Line) + "\n"
688 # Oops, something unspeakable happened.
694 # Generate a list of hosts for RBL or whitelist purposes.
695 def GenMailList(File, Key):
698 F = open(File + ".tmp", "w")
700 # Fetch all the users
703 for x in DebianUsers:
706 if x[1].has_key(Key) == 0:
713 if Key == "mailWhitelist":
714 if re.match('^[-\w.]+(/[\d]+)?$', z) == None:
717 if re.match('^[-\w.]+$', z) == None:
721 Line = GetAttr(x, "uid")
725 if Key == "mailRHSBL":
726 Line += "/$sender_address_domain"
729 Line = Sanitize(Line) + "\n"
734 # Oops, something unspeakable happened.
740 def isRoleAccount(pwEntry):
741 if not pwEntry.has_key("objectClass"):
742 raise "pwEntry has no objectClass"
743 oc = pwEntry['objectClass']
745 i = oc.index('debianRoleAccount')
750 # Generate the DNS Zone file
754 F = open(File + ".tmp", "w")
758 # for x in HostAttrs:
759 # if x[1].has_key("hostname") == 0 or \
760 # x[1].has_key("architecture") == 0 or\
761 # x[1].has_key("sshRSAHostKey") == 0:
764 # if IsDebianHost.match(GetAttr(x, "hostname")) is not None:
767 # DNSInfo = ExtractDNSInfo(x)
769 # for Line in DNSInfo:
771 # Line = "%s.\t%s" % (GetAttr(x, "hostname"), Line)
774 # Line = "\t\t\t%s" % (Line)
775 # F.write(Line + "\n")
777 # Fetch all the users
780 # Write out the zone file entry for each user
781 for x in PasswdAttrs:
782 if x[1].has_key("dnsZoneEntry") == 0:
785 # If the account has no PGP key, do not write it
786 if x[1].has_key("keyFingerPrint") == 0 and not isRoleAccount(x[1]):
789 F.write("; %s\n"%(EmailAddress(x)))
790 for z in x[1]["dnsZoneEntry"]:
791 Split = z.lower().split()
792 if Split[1].lower() == 'in':
793 for y in range(0, len(Split)):
796 Line = " ".join(Split) + "\n"
799 Host = Split[0] + DNSZone
800 if BSMTPCheck.match(Line) != None:
801 F.write("; Has BSMTP\n")
803 # Write some identification information
804 if Split[2].lower() == "a":
805 Line = "%s IN TXT \"%s\"\n"%(Split[0], EmailAddress(x))
806 for y in x[1]["keyFingerPrint"]:
807 Line = Line + "%s IN TXT \"PGP %s\"\n"%(Split[0], FormatPGPKey(y))
810 Line = "; Err %s"%(str(Split))
815 F.write("; Errors\n")
818 # Oops, something unspeakable happened.
824 def ExtractDNSInfo(x):
828 TTLprefix="%s\t"%(x[1]["dnsTTL"][0])
831 if x[1].has_key("ipHostNumber"):
832 for I in x[1]["ipHostNumber"]:
833 if IsV6Addr.match(I) != None:
834 DNSInfo.append("%sIN\tAAAA\t%s" % (TTLprefix, I))
836 DNSInfo.append("%sIN\tA\t%s" % (TTLprefix, I))
838 Host = GetAttr(x, "hostname")
839 Arch = GetAttr(x, "architecture")
842 for I in x[1]["sshRSAHostKey"]:
844 if Split[0] == 'ssh-rsa':
846 if Split[0] == 'ssh-dss':
848 if Algorithm == None:
850 Fingerprint = sha.new(base64.decodestring(Split[1])).hexdigest()
851 DNSInfo.append("%sIN\tSSHFP\t%u 1 %s" % (TTLprefix, Algorithm, Fingerprint))
854 if x[1].has_key("machine"):
855 Mach = " " + GetAttr(x, "machine")
856 DNSInfo.append("%sIN\tHINFO\t\"%s%s\" \"%s\"" % (TTLprefix, Arch, Mach, "Debian GNU/Linux"))
858 if x[1].has_key("mXRecord"):
859 for I in x[1]["mXRecord"]:
860 DNSInfo.append("%sIN\tMX\t%s" % (TTLprefix, I))
864 # Generate the DNS records
865 def GenZoneRecords(File):
868 F = open(File + ".tmp", "w")
870 # Fetch all the hosts
874 if x[1].has_key("hostname") == 0 or \
875 x[1].has_key("architecture") == 0 or\
876 x[1].has_key("sshRSAHostKey") == 0:
879 if IsDebianHost.match(GetAttr(x, "hostname")) is None:
882 DNSInfo = ExtractDNSInfo(x)
886 Line = "%s.\t%s" % (GetAttr(x, "hostname"), Line)
889 Line = "\t\t\t%s" % (Line)
893 # Oops, something unspeakable happened.
899 # Generate the BSMTP file
900 def GenBSMTP(File, HomePrefix):
903 F = open(File + ".tmp", "w")
905 # Fetch all the users
908 # Write out the zone file entry for each user
909 for x in DebianUsers:
910 if x[1].has_key("dnsZoneEntry") == 0:
913 # If the account has no PGP key, do not write it
914 if x[1].has_key("keyFingerPrint") == 0:
917 for z in x[1]["dnsZoneEntry"]:
918 Split = z.lower().split()
919 if Split[1].lower() == 'in':
920 for y in range(0, len(Split)):
923 Line = " ".join(Split) + "\n"
925 Host = Split[0] + DNSZone
926 if BSMTPCheck.match(Line) != None:
927 F.write("%s: user=%s group=Debian file=%s%s/bsmtp/%s\n"%(Host,
928 GetAttr(x, "uid"), HomePrefix, GetAttr(x, "uid"), Host))
931 F.write("; Errors\n")
934 # Oops, something unspeakable happened.
940 def HostToIP(Host, mapped=True):
944 if Host[1].has_key("ipHostNumber"):
945 for addr in Host[1]["ipHostNumber"]:
946 IPAdresses.append(addr)
947 if IsV6Addr.match(addr) is None and mapped == "True":
948 IPAdresses.append("::ffff:"+addr)
952 # Generate the ssh known hosts file
953 def GenSSHKnown(File, mode=None):
956 OldMask = os.umask(0022)
957 F = open(File + ".tmp", "w", 0644)
963 if x[1].has_key("hostname") == 0 or \
964 x[1].has_key("sshRSAHostKey") == 0:
966 Host = GetAttr(x, "hostname")
968 if Host.endswith(HostDomain):
969 HostNames.append(Host[:-(len(HostDomain) + 1)])
971 # in the purpose field [[host|some other text]] (where some other text is optional)
972 # makes a hyperlink on the web thing. we now also add these hosts to the ssh known_hosts
973 # file. But so that we don't have to add everything we link we can add an asterisk
974 # and say [[*... to ignore it. In order to be able to add stuff to ssh without
975 # http linking it we also support [[-hostname]] entries.
976 for i in x[1].get("purpose", []):
977 m = PurposeHostField.match(i)
980 # we ignore [[*..]] entries
981 if m.startswith('*'):
983 if m.startswith('-'):
987 if m.endswith(HostDomain):
988 HostNames.append(m[:-(len(HostDomain) + 1)])
990 for I in x[1]["sshRSAHostKey"]:
991 if mode and mode == 'authorized_keys':
993 if 'sshdistAuthKeysHost' in x[1]:
994 hosts += x[1]['sshdistAuthKeysHost']
995 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)
996 #Line = 'command="rsync --server --sender -pr . /var/cache/userdir-ldap/hosts/%s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding %s' % (Host,I)
998 Line = "%s %s" %(",".join(HostNames + HostToIP(x, False)), I)
999 Line = Sanitize(Line) + "\n"
1001 # Oops, something unspeakable happened.
1007 # Generate the debianhosts file (list of all IP addresses)
1011 OldMask = os.umask(0022)
1012 F = open(File + ".tmp", "w", 0644)
1021 if IsDebianHost.match(GetAttr(x, "hostname")) is None:
1024 if not 'ipHostNumber' in x[1]:
1027 addrs = x[1]["ipHostNumber"]
1029 if addr not in seen:
1031 addr = Sanitize(addr) + "\n"
1034 # Oops, something unspeakable happened.
1040 def GenKeyrings(OutDir):
1042 shutil.copy(k, OutDir)
1044 # Connect to the ldap server
1046 F = open(PassDir + "/pass-" + pwd.getpwuid(os.getuid())[0], "r")
1047 Pass = F.readline().strip().split(" ")
1049 l.simple_bind_s("uid=" + Pass[0] + "," + BaseDn, Pass[1])
1051 # Fetch all the groups
1053 Attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "gid=*",\
1054 ["gid", "gidNumber", "subGroup"])
1056 # Generate the SubGroupMap and GroupIDMap
1058 if x[1].has_key("accountStatus") and x[1]['accountStatus'] == "disabled":
1060 if x[1].has_key("gidNumber") == 0:
1062 GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidNumber"][0])
1063 if x[1].has_key("subGroup") != 0:
1064 SubGroupMap.setdefault(x[1]["gid"][0], []).extend(x[1]["subGroup"])
1066 # Fetch all the users
1067 PasswdAttrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "uid=*",\
1068 ["uid", "uidNumber", "gidNumber", "supplementaryGid",\
1069 "gecos", "loginShell", "userPassword", "shadowLastChange",\
1070 "shadowMin", "shadowMax", "shadowWarning", "shadowInactive",
1071 "shadowExpire", "emailForward", "latitude", "longitude",\
1072 "allowedHost", "sshRSAAuthKey", "dnsZoneEntry", "cn", "sn",\
1073 "keyFingerPrint", "privateSub", "mailDisableMessage",\
1074 "mailGreylisting", "mailCallout", "mailRBL", "mailRHSBL",\
1075 "mailWhitelist", "sudoPassword", "objectClass", "accountStatus",\
1076 "mailContentInspectionAction"])
1078 if PasswdAttrs is None:
1079 raise UDEmptyList, "No Users"
1081 PasswdAttrs.sort(lambda x, y: cmp((GetAttr(x, "uid")).lower(), (GetAttr(y, "uid")).lower()))
1083 # Fetch all the hosts
1084 HostAttrs = l.search_s(HostBaseDn, ldap.SCOPE_ONELEVEL, "objectClass=debianServer",\
1085 ["hostname", "sshRSAHostKey", "purpose", "allowedGroups", "exportOptions",\
1086 "mXRecord", "ipHostNumber", "dnsTTL", "machine", "architecture"])
1088 if HostAttrs == None:
1089 raise UDEmptyList, "No Hosts"
1091 HostAttrs.sort(lambda x, y: cmp((GetAttr(x, "hostname")).lower(), (GetAttr(y, "hostname")).lower()))
1093 # Generate global things
1094 GlobalDir = GenerateDir + "/"
1095 GenDisabledAccounts(GlobalDir + "disabled-accounts")
1097 PasswdAttrs = filter(lambda x: not IsRetired(x), PasswdAttrs)
1098 #DebianUsers = filter(lambda x: IsGidDebian(x), PasswdAttrs)
1099 DebianUsers = PasswdAttrs
1103 GenMailDisable(GlobalDir + "mail-disable")
1104 GenCDB(GlobalDir + "mail-forward.cdb", 'emailForward')
1105 GenCDB(GlobalDir + "mail-contentinspectionaction.cdb", 'mailContentInspectionAction')
1106 GenPrivate(GlobalDir + "debian-private")
1107 GenSSHKnown(GlobalDir+"authorized_keys", 'authorized_keys')
1108 GenMailBool(GlobalDir + "mail-greylist", "mailGreylisting")
1109 GenMailBool(GlobalDir + "mail-callout", "mailCallout")
1110 GenMailList(GlobalDir + "mail-rbl", "mailRBL")
1111 GenMailList(GlobalDir + "mail-rhsbl", "mailRHSBL")
1112 GenMailList(GlobalDir + "mail-whitelist", "mailWhitelist")
1113 GenKeyrings(GlobalDir)
1116 GenForward(GlobalDir + "forward-alias")
1118 PasswdAttrs = filter(lambda x: not x in DisabledUsers, PasswdAttrs)
1120 SSHFiles = GenSSHShadow()
1121 GenMarkers(GlobalDir + "markers")
1122 GenSSHKnown(GlobalDir + "ssh_known_hosts")
1123 GenHosts(GlobalDir + "debianhosts")
1125 for host in HostAttrs:
1126 if not "hostname" in host[1]:
1129 CurrentHost = host[1]['hostname'][0]
1130 OutDir = GenerateDir + '/' + CurrentHost + '/'
1136 # Get the group list and convert any named groups to numerics
1138 for groupname in AllowedGroupsPreload.strip().split(" "):
1139 GroupList[groupname] = True
1140 if 'allowedGroups' in host[1]:
1141 for groupname in host[1]['allowedGroups']:
1142 GroupList[groupname] = True
1143 for groupname in GroupList.keys():
1144 if groupname in GroupIDMap:
1145 GroupList[str(GroupIDMap[groupname])] = True
1148 if 'exportOptions' in host[1]:
1149 for extra in host[1]['exportOptions']:
1150 ExtraList[extra.upper()] = True
1156 DoLink(GlobalDir, OutDir, "debianhosts")
1157 DoLink(GlobalDir, OutDir, "ssh_known_hosts")
1158 DoLink(GlobalDir, OutDir, "disabled-accounts")
1161 if 'NOPASSWD' in ExtraList:
1162 userlist = GenPasswd(OutDir + "passwd", HomePrefix, "*")
1164 userlist = GenPasswd(OutDir + "passwd", HomePrefix, "x")
1166 grouprevmap = GenGroup(OutDir + "group")
1167 GenShadowSudo(OutDir + "sudo-passwd", ('UNTRUSTED' in ExtraList) or ('NOPASSWD' in ExtraList))
1169 # Now we know who we're allowing on the machine, export
1170 # the relevant ssh keys
1171 GenSSHtarballs(userlist, SSHFiles, grouprevmap, os.path.join(OutDir, 'ssh-keys.tar.gz'))
1173 if not 'NOPASSWD' in ExtraList:
1174 GenShadow(OutDir + "shadow")
1176 # Link in global things
1177 if not 'NOMARKERS' in ExtraList:
1178 DoLink(GlobalDir, OutDir, "markers")
1179 DoLink(GlobalDir, OutDir, "mail-forward.cdb")
1180 DoLink(GlobalDir, OutDir, "mail-contentinspectionaction.cdb")
1181 DoLink(GlobalDir, OutDir, "mail-disable")
1182 DoLink(GlobalDir, OutDir, "mail-greylist")
1183 DoLink(GlobalDir, OutDir, "mail-callout")
1184 DoLink(GlobalDir, OutDir, "mail-rbl")
1185 DoLink(GlobalDir, OutDir, "mail-rhsbl")
1186 DoLink(GlobalDir, OutDir, "mail-whitelist")
1189 DoLink(GlobalDir, OutDir, "forward-alias")
1191 if 'DNS' in ExtraList:
1192 GenDNS(OutDir + "dns-zone")
1193 GenZoneRecords(OutDir + "dns-sshfp")
1195 if 'AUTHKEYS' in ExtraList:
1196 DoLink(GlobalDir, OutDir, "authorized_keys")
1198 if 'BSMTP' in ExtraList:
1199 GenBSMTP(OutDir + "bsmtp", HomePrefix)
1201 if 'PRIVATE' in ExtraList:
1202 DoLink(GlobalDir, OutDir, "debian-private")
1204 if 'KEYRING' in ExtraList:
1206 DoLink(GlobalDir, OutDir, os.path.basename(k))
1210 posix.remove(OutDir + os.path.basename(k))
1216 # vim:set shiftwidth=3: