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 DNSZone = ".debian.net"
51 Keyrings = ConfModule.sync_keyrings.split(":")
53 def safe_makedirs(dir):
57 if e.errno == errno.EEXIST:
66 if e.errno == errno.ENOENT:
72 return Str.translate(string.maketrans("\n\r\t", "$$$"))
74 def DoLink(From, To, File):
76 posix.remove(To + File)
79 posix.link(From + File, To + File)
81 def IsRetired(DnRecord):
83 Looks for accountStatus in the LDAP record and tries to
84 match it against one of the known retired statuses
87 status = GetAttr(DnRecord, "accountStatus", None)
94 if status == "inactive":
97 elif status == "memorial":
100 elif status == "retiring":
101 # We'll give them a few extra days over what we said
102 age = 6 * 31 * 24 * 60 * 60
104 return (time.time() - time.mktime(time.strptime(line[1], "%Y-%m-%d"))) > age
114 return int(GetAttr(x, "gidNumber", 0)) == 800
118 # See if this user is in the group list
119 def IsInGroup(DnRecord):
123 # See if the primary group is in the list
124 if Allowed.has_key(GetAttr(DnRecord, "gidNumber")) != 0:
127 # Check the host based ACL
128 if DnRecord[1].has_key("allowedHost") != 0:
129 if CurrentHost in DnRecord[1]["allowedHost"]:
132 # See if there are supplementary groups
133 if DnRecord[1].has_key("supplementaryGid") == 0:
137 addGroups(supgroups, DnRecord[1]["supplementaryGid"], GetAttr(DnRecord, "uid"))
139 if Allowed.has_key(g):
143 def Die(File, F, Fdb):
149 os.remove(File + ".tmp")
153 os.remove(File + ".tdb.tmp")
157 def Done(File, F, Fdb):
160 os.rename(File + ".tmp", File)
163 os.rename(File + ".tdb.tmp", File + ".tdb")
165 # Generate the password list
166 def GenPasswd(File, HomePrefix, PwdMarker):
169 F = open(File + ".tdb.tmp", "w")
172 # Fetch all the users
176 for x in PasswdAttrs:
177 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
180 # Do not let people try to buffer overflow some busted passwd parser.
181 if len(GetAttr(x, "gecos")) > 100 or len(GetAttr(x, "loginShell")) > 50:
184 userlist[GetAttr(x, "uid")] = int(GetAttr(x, "gidNumber"))
185 Line = "%s:%s:%s:%s:%s:%s%s:%s" % (GetAttr(x, "uid"),\
187 GetAttr(x, "uidNumber"), GetAttr(x, "gidNumber"),\
188 GetAttr(x, "gecos"), HomePrefix, GetAttr(x, "uid"),\
189 GetAttr(x, "loginShell"))
191 Line = Sanitize(Line) + "\n"
192 F.write("0%u %s" % (I, Line))
193 F.write(".%s %s" % (GetAttr(x, "uid"), Line))
194 F.write("=%s %s" % (GetAttr(x, "uidNumber"), Line))
197 # Oops, something unspeakable happened.
203 # Return the list of users so we know which keys to export
206 # Generate the shadow list
210 OldMask = os.umask(0077)
211 F = open(File + ".tdb.tmp", "w", 0600)
214 # Fetch all the users
218 for x in PasswdAttrs:
219 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
222 Pass = GetAttr(x, "userPassword")
223 if Pass[0:7] != "{crypt}" or len(Pass) > 50:
228 # If the account is locked, mark it as such in shadow
229 # See Debian Bug #308229 for why we set it to 1 instead of 0
230 if (GetAttr(x, "userPassword").find("*LK*") != -1) \
231 or GetAttr(x, "userPassword").startswith("!"):
234 ShadowExpire = GetAttr(x, "shadowExpire")
236 Line = "%s:%s:%s:%s:%s:%s:%s:%s:" % (GetAttr(x, "uid"),\
237 Pass, GetAttr(x, "shadowLastChange"),\
238 GetAttr(x, "shadowMin"), GetAttr(x, "shadowMax"),\
239 GetAttr(x, "shadowWarning"), GetAttr(x, "shadowInactive"),\
241 Line = Sanitize(Line) + "\n"
242 F.write("0%u %s" % (I, Line))
243 F.write(".%s %s" % (GetAttr(x, "uid"), Line))
246 # Oops, something unspeakable happened.
252 # Generate the sudo passwd file
253 def GenShadowSudo(File, untrusted):
256 OldMask = os.umask(0077)
257 F = open(File + ".tmp", "w", 0600)
260 # Fetch all the users
263 for x in PasswdAttrs:
265 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
268 if x[1].has_key('sudoPassword'):
269 for entry in x[1]['sudoPassword']:
270 Match = re.compile('^('+UUID_FORMAT+') (confirmed:[0-9a-f]{40}|unconfirmed) ([a-z0-9.,*]+) ([^ ]+)$').match(entry)
273 uuid = Match.group(1)
274 status = Match.group(2)
275 hosts = Match.group(3)
276 cryptedpass = Match.group(4)
278 if status != 'confirmed:'+make_passwd_hmac('password-is-confirmed', 'sudo', x[1]['uid'][0], uuid, hosts, cryptedpass):
280 for_all = hosts == "*"
281 for_this_host = CurrentHost in hosts.split(',')
282 if not (for_all or for_this_host):
284 # ignore * passwords for untrusted hosts, but copy host specific passwords
285 if for_all and untrusted:
288 if for_this_host: # this makes sure we take a per-host entry over the for-all entry
293 Line = "%s:%s" % (GetAttr(x, "uid"), Pass)
294 Line = Sanitize(Line) + "\n"
295 F.write("%s" % (Line))
297 # Oops, something unspeakable happened.
303 # Generate the shadow list
305 # Fetch all the users
310 safe_rmtree(os.path.join(GlobalDir, 'userkeys'))
311 safe_makedirs(os.path.join(GlobalDir, 'userkeys'))
313 for x in PasswdAttrs:
315 if x[1].has_key("uidNumber") == 0 or \
316 x[1].has_key("sshRSAAuthKey") == 0:
319 User = GetAttr(x, "uid")
323 OldMask = os.umask(0077)
324 File = os.path.join(GlobalDir, 'userkeys', User)
325 F = open(File + ".tmp", "w", 0600)
328 for I in x[1]["sshRSAAuthKey"]:
329 MultipleLine = "%s" % I
330 MultipleLine = Sanitize(MultipleLine) + "\n"
331 F.write(MultipleLine)
334 userfiles.append(os.path.basename(File))
336 # Oops, something unspeakable happened.
339 Die(masterFileName, masterFile, None)
344 def GenSSHtarballs(userlist, SSHFiles, grouprevmap, target):
345 OldMask = os.umask(0077)
346 tf = tarfile.open(name=os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), mode='w:gz')
348 for f in userlist.keys():
349 if f not in SSHFiles:
351 # If we're not exporting their primary group, don't export
354 if userlist[f] in grouprevmap.keys():
355 grname = grouprevmap[userlist[f]]
358 if int(userlist[f]) <= 100:
359 # In these cases, look it up in the normal way so we
360 # deal with cases where, for instance, users are in group
361 # users as their primary group.
362 grname = grp.getgrgid(userlist[f])[0]
367 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])
370 to = tf.gettarinfo(os.path.join(GlobalDir, 'userkeys', f), f)
371 # These will only be used where the username doesn't
372 # exist on the target system for some reason; hence,
373 # in those cases, the safest thing is for the file to
374 # be owned by root but group nobody. This deals with
375 # the bloody obscure case where the group fails to exist
376 # whilst the user does (in which case we want to avoid
377 # ending up with a file which is owned user:root to avoid
378 # a fairly obvious attack vector)
381 # Using the username / groupname fields avoids any need
382 # to give a shit^W^W^Wcare about the UIDoffset stuff.
386 tf.addfile(to, file(os.path.join(GlobalDir, 'userkeys', f)))
389 os.rename(os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), target)
391 # add a list of groups to existing groups,
392 # including all subgroups thereof, recursively.
393 # basically this proceduces the transitive hull of the groups in
395 def addGroups(existingGroups, newGroups, uid):
396 for group in newGroups:
397 # if it's a <group>@host, split it and verify it's on the current host.
398 s = group.split('@', 1)
399 if len(s) == 2 and s[1] != CurrentHost:
403 # let's see if we handled this group already
404 if group in existingGroups:
407 if not GroupIDMap.has_key(group):
408 print "Group", group, "does not exist but", uid, "is in it"
411 existingGroups.append(group)
413 if SubGroupMap.has_key(group):
414 addGroups(existingGroups, SubGroupMap[group], uid)
416 # Generate the group list
421 F = open(File + ".tdb.tmp", "w")
423 # Generate the GroupMap
425 for x in GroupIDMap.keys():
428 # Fetch all the users
431 # Sort them into a list of groups having a set of users
432 for x in PasswdAttrs:
433 uid = GetAttr(x, "uid")
434 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
436 if x[1].has_key("supplementaryGid") == 0:
440 addGroups(supgroups, x[1]["supplementaryGid"], uid)
442 GroupMap[g].append(uid)
444 # Output the group file.
446 for x in GroupMap.keys():
447 grouprevmap[GroupIDMap[x]] = x
448 if GroupIDMap.has_key(x) == 0:
450 Line = "%s:x:%u:" % (x, GroupIDMap[x])
452 for I in GroupMap[x]:
453 Line = Line + ("%s%s" % (Comma, I))
455 Line = Sanitize(Line) + "\n"
456 F.write("0%u %s" % (J, Line))
457 F.write(".%s %s" % (x, Line))
458 F.write("=%u %s" % (GroupIDMap[x], Line))
461 # Oops, something unspeakable happened.
471 for x in DebianUsers:
472 if x[1].has_key("emailForward") == 0:
476 x[1].pop("emailForward")
479 # Do not allow people to try to buffer overflow busted parsers
480 if len(GetAttr(x, "emailForward")) > 200:
481 x[1].pop("emailForward")
484 # Check the forwarding address
485 if EmailCheck.match(GetAttr(x, "emailForward")) == None:
486 x[1].pop("emailForward")
488 # Generate the email forwarding list
489 def GenForward(File):
492 OldMask = os.umask(0022)
493 F = open(File + ".tmp", "w", 0644)
496 # Fetch all the users
499 # Write out the email address for each user
500 for x in DebianUsers:
501 if x[1].has_key("emailForward") == 0:
504 Line = "%s: %s" % (GetAttr(x, "uid"), GetAttr(x, "emailForward"))
505 Line = Sanitize(Line) + "\n"
508 # Oops, something unspeakable happened.
514 def GenCDB(File, Key):
517 OldMask = os.umask(0022)
518 Fdb = os.popen("cdbmake %s %s.tmp"%(File, File), "w")
521 # Fetch all the users
524 # Write out the email address for each user
525 for x in DebianUsers:
528 Value = GetAttr(x, Key)
529 User = GetAttr(x, "uid")
530 Fdb.write("+%d,%d:%s->%s\n" % (len(User), len(Value), User, Value))
533 # Oops, something unspeakable happened.
537 if Fdb.close() != None:
538 raise "cdbmake gave an error"
540 # Generate the anon XEarth marker file
541 def GenMarkers(File):
544 F = open(File + ".tmp", "w")
546 # Fetch all the users
549 # Write out the position for each user
550 for x in DebianUsers:
551 if x[1].has_key("latitude") == 0 or x[1].has_key("longitude") == 0:
554 Line = "%8s %8s \"\""%(DecDegree(GetAttr(x, "latitude"), 1), DecDegree(GetAttr(x, "longitude"), 1))
555 Line = Sanitize(Line) + "\n"
560 # Oops, something unspeakable happened.
566 # Generate the debian-private subscription list
567 def GenPrivate(File):
570 F = open(File + ".tmp", "w")
572 # Fetch all the users
575 # Write out the position for each user
576 for x in DebianUsers:
577 if x[1].has_key("privateSub") == 0:
580 # If the account has no PGP key, do not write it
581 if x[1].has_key("keyFingerPrint") == 0:
585 Line = "%s"%(GetAttr(x, "privateSub"))
586 Line = Sanitize(Line) + "\n"
591 # Oops, something unspeakable happened.
597 # Generate a list of locked accounts
598 def GenDisabledAccounts(File):
601 F = open(File + ".tmp", "w")
603 # Fetch all the users
608 for x in PasswdAttrs:
609 if x[1].has_key("uidNumber") == 0:
612 Pass = GetAttr(x, "userPassword")
614 # *LK* is the reference value for a locked account
615 # password starting with ! is also a locked account
616 if Pass.find("*LK*") != -1 or Pass.startswith("!"):
617 # Format is <login>:<reason>
618 Line = "%s:%s" % (GetAttr(x, "uid"), "Account is locked")
619 DisabledUsers.append(x)
622 F.write(Sanitize(Line) + "\n")
625 # Oops, something unspeakable happened.
631 # Generate the list of local addresses that refuse all mail
632 def GenMailDisable(File):
635 F = open(File + ".tmp", "w")
637 # Fetch all the users
640 for x in DebianUsers:
643 if x[1].has_key("mailDisableMessage"):
644 Reason = GetAttr(x, "mailDisableMessage")
649 Line = "%s: %s"%(GetAttr(x, "uid"), Reason)
650 Line = Sanitize(Line) + "\n"
655 # Oops, something unspeakable happened.
661 # Generate a list of uids that should have boolean affects applied
662 def GenMailBool(File, Key):
665 F = open(File + ".tmp", "w")
667 # Fetch all the users
670 for x in DebianUsers:
673 if x[1].has_key(Key) == 0:
676 if GetAttr(x, Key) != "TRUE":
680 Line = "%s"%(GetAttr(x, "uid"))
681 Line = Sanitize(Line) + "\n"
686 # Oops, something unspeakable happened.
692 # Generate a list of hosts for RBL or whitelist purposes.
693 def GenMailList(File, Key):
696 F = open(File + ".tmp", "w")
698 # Fetch all the users
701 for x in DebianUsers:
704 if x[1].has_key(Key) == 0:
711 if Key == "mailWhitelist":
712 if re.match('^[-\w.]+(/[\d]+)?$', z) == None:
715 if re.match('^[-\w.]+$', z) == None:
719 Line = GetAttr(x, "uid")
723 if Key == "mailRHSBL":
724 Line += "/$sender_address_domain"
727 Line = Sanitize(Line) + "\n"
732 # Oops, something unspeakable happened.
738 def isRoleAccount(pwEntry):
739 if not pwEntry.has_key("objectClass"):
740 raise "pwEntry has no objectClass"
741 oc = pwEntry['objectClass']
743 i = oc.index('debianRoleAccount')
748 # Generate the DNS Zone file
749 def GenDNS(File, HomePrefix):
752 F = open(File + ".tmp", "w")
754 # Fetch all the users
757 # Write out the zone file entry for each user
758 for x in PasswdAttrs:
759 if x[1].has_key("dnsZoneEntry") == 0:
762 # If the account has no PGP key, do not write it
763 if x[1].has_key("keyFingerPrint") == 0 and not isRoleAccount(x[1]):
766 F.write("; %s\n"%(EmailAddress(x)))
767 for z in x[1]["dnsZoneEntry"]:
768 Split = z.lower().split()
769 if Split[1].lower() == 'in':
770 for y in range(0, len(Split)):
773 Line = " ".join(Split) + "\n"
776 Host = Split[0] + DNSZone
777 if BSMTPCheck.match(Line) != None:
778 F.write("; Has BSMTP\n")
780 # Write some identification information
781 if Split[2].lower() == "a":
782 Line = "%s IN TXT \"%s\"\n"%(Split[0], EmailAddress(x))
783 for y in x[1]["keyFingerPrint"]:
784 Line = Line + "%s IN TXT \"PGP %s\"\n"%(Split[0], FormatPGPKey(y))
787 Line = "; Err %s"%(str(Split))
792 F.write("; Errors\n")
795 # Oops, something unspeakable happened.
801 # Generate the DNS SSHFP records
802 def GenSSHFP(File, HomePrefix):
805 F = open(File + ".tmp", "w")
807 # Fetch all the hosts
809 if HostAttrs == None:
810 raise UDEmptyList, "No Hosts"
813 if x[1].has_key("hostname") == 0 or \
814 x[1].has_key("sshRSAHostKey") == 0:
816 Host = GetAttr(x, "hostname")
818 for I in x[1]["sshRSAHostKey"]:
820 if Split[0] == 'ssh-rsa':
822 if Split[0] == 'ssh-dss':
824 if Algorithm == None:
826 Fingerprint = sha.new(base64.decodestring(Split[1])).hexdigest()
827 Line = "%s. IN SSHFP %u 1 %s" % (Host, Algorithm, Fingerprint)
828 Line = Sanitize(Line) + "\n"
830 # Oops, something unspeakable happened.
836 # Generate the BSMTP file
837 def GenBSMTP(File, HomePrefix):
840 F = open(File + ".tmp", "w")
842 # Fetch all the users
845 # Write out the zone file entry for each user
846 for x in DebianUsers:
847 if x[1].has_key("dnsZoneEntry") == 0:
850 # If the account has no PGP key, do not write it
851 if x[1].has_key("keyFingerPrint") == 0:
854 for z in x[1]["dnsZoneEntry"]:
855 Split = z.lower().split()
856 if Split[1].lower() == 'in':
857 for y in range(0, len(Split)):
860 Line = " ".join(Split) + "\n"
862 Host = Split[0] + DNSZone
863 if BSMTPCheck.match(Line) != None:
864 F.write("%s: user=%s group=Debian file=%s%s/bsmtp/%s\n"%(Host,
865 GetAttr(x, "uid"), HomePrefix, GetAttr(x, "uid"), Host))
868 F.write("; Errors\n")
871 # Oops, something unspeakable happened.
881 if not Host in HostToIPCache:
884 IPAdressesT = list(set([ (a[0], a[4][0]) for a in socket.getaddrinfo(Host, None)]))
885 except socket.gaierror, (code):
889 if not IPAdressesT is None:
890 for addr in IPAdressesT:
891 if addr[0] == socket.AF_INET:
892 IPAdresses += [addr[1], "::ffff:"+addr[1]]
894 IPAdresses += [addr[1]]
895 HostToIPCache[Host] = IPAdresses
896 return HostToIPCache[Host]
898 # Generate the ssh known hosts file
899 def GenSSHKnown(File, mode=None):
902 OldMask = os.umask(0022)
903 F = open(File + ".tmp", "w", 0644)
907 if HostAttrs is None:
908 raise UDEmptyList, "No Hosts"
911 if x[1].has_key("hostname") == 0 or \
912 x[1].has_key("sshRSAHostKey") == 0:
914 Host = GetAttr(x, "hostname")
916 if Host.endswith(HostDomain):
917 HostNames.append(Host[:-(len(HostDomain) + 1)])
919 # in the purpose field [[host|some other text]] (where some other text is optional)
920 # makes a hyperlink on the web thing. we now also add these hosts to the ssh known_hosts
921 # file. But so that we don't have to add everything we link we can add an asterisk
922 # and say [[*... to ignore it. In order to be able to add stuff to ssh without
923 # http linking it we also support [[-hostname]] entries.
924 for i in x[1].get("purpose", []):
925 m = PurposeHostField.match(i)
928 # we ignore [[*..]] entries
929 if m.startswith('*'):
931 if m.startswith('-'):
935 if m.endswith(HostDomain):
936 HostNames.append(m[:-(len(HostDomain) + 1)])
938 for I in x[1]["sshRSAHostKey"]:
939 if mode and mode == 'authorized_keys':
940 #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)
941 Line = 'command="rsync --server --sender -pr . /var/cache/userdir-ldap/hosts/%s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding %s' % (Host,I)
943 Line = "%s %s" %(",".join(HostNames + HostToIP(Host)), I)
944 Line = Sanitize(Line) + "\n"
946 # Oops, something unspeakable happened.
952 # Generate the debianhosts file (list of all IP addresses)
953 def GenHosts(l, File):
956 OldMask = os.umask(0022)
957 F = open(File + ".tmp", "w", 0644)
960 # Fetch all the hosts
961 hostnames = l.search_s(HostBaseDn, ldap.SCOPE_ONELEVEL, "hostname=*",
964 if hostnames is None:
965 raise UDEmptyList, "No Hosts"
969 host = GetAttr(x, "hostname", None)
973 addrs += socket.getaddrinfo(host, None, socket.AF_INET)
977 addrs += socket.getaddrinfo(host, None, socket.AF_INET6)
981 for addrinfo in addrs:
982 if addrinfo[0] in (socket.AF_INET, socket.AF_INET6):
983 addr = addrinfo[4][0]
985 print >> F, addrinfo[4][0]
987 # Oops, something unspeakable happened.
993 def GenKeyrings(OutDir):
995 shutil.copy(k, OutDir)
997 # Connect to the ldap server
999 F = open(PassDir + "/pass-" + pwd.getpwuid(os.getuid())[0], "r")
1000 Pass = F.readline().strip().split(" ")
1002 l.simple_bind_s("uid=" + Pass[0] + "," + BaseDn, Pass[1])
1004 # Fetch all the groups
1006 Attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "gid=*",\
1007 ["gid", "gidNumber", "subGroup"])
1009 # Generate the SubGroupMap and GroupIDMap
1011 if x[1].has_key("gidNumber") == 0:
1013 GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidNumber"][0])
1014 if x[1].has_key("subGroup") != 0:
1015 SubGroupMap.setdefault(x[1]["gid"][0], []).extend(x[1]["subGroup"])
1017 # Fetch all the users
1018 PasswdAttrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "uid=*",\
1019 ["uid", "uidNumber", "gidNumber", "supplementaryGid",\
1020 "gecos", "loginShell", "userPassword", "shadowLastChange",\
1021 "shadowMin", "shadowMax", "shadowWarning", "shadowInactive",
1022 "shadowExpire", "emailForward", "latitude", "longitude",\
1023 "allowedHost", "sshRSAAuthKey", "dnsZoneEntry", "cn", "sn",\
1024 "keyFingerPrint", "privateSub", "mailDisableMessage",\
1025 "mailGreylisting", "mailCallout", "mailRBL", "mailRHSBL",\
1026 "mailWhitelist", "sudoPassword", "objectClass", "accountStatus",\
1027 "mailContentInspectionAction"])
1029 if PasswdAttrs is None:
1030 raise UDEmptyList, "No Users"
1032 # Fetch all the hosts
1033 HostAttrs = l.search_s(HostBaseDn, ldap.SCOPE_ONELEVEL, "sshRSAHostKey=*",\
1034 ["hostname", "sshRSAHostKey", "purpose"])
1036 # Open the control file
1037 if len(sys.argv) == 1:
1038 F = open(GenerateConf, "r")
1040 F = open(sys.argv[1], "r")
1042 # Generate global things
1043 GlobalDir = GenerateDir + "/"
1044 GenDisabledAccounts(GlobalDir + "disabled-accounts")
1046 PasswdAttrs = filter(lambda x: not IsRetired(x), PasswdAttrs)
1047 #DebianUsers = filter(lambda x: IsGidDebian(x), PasswdAttrs)
1048 DebianUsers = PasswdAttrs
1052 GenMailDisable(GlobalDir + "mail-disable")
1053 GenCDB(GlobalDir + "mail-forward.cdb", 'emailForward')
1054 GenCDB(GlobalDir + "mail-contentinspectionaction.cdb", 'mailContentInspectionAction')
1055 GenPrivate(GlobalDir + "debian-private")
1056 #GenSSHKnown(l,GlobalDir+"authorized_keys", 'authorized_keys')
1057 GenMailBool(GlobalDir + "mail-greylist", "mailGreylisting")
1058 GenMailBool(GlobalDir + "mail-callout", "mailCallout")
1059 GenMailList(GlobalDir + "mail-rbl", "mailRBL")
1060 GenMailList(GlobalDir + "mail-rhsbl", "mailRHSBL")
1061 GenMailList(GlobalDir + "mail-whitelist", "mailWhitelist")
1062 GenKeyrings(GlobalDir)
1065 GenForward(GlobalDir + "forward-alias")
1067 PasswdAttrs = filter(lambda x: not x in DisabledUsers, PasswdAttrs)
1069 SSHFiles = GenSSHShadow()
1070 GenMarkers(GlobalDir + "markers")
1071 GenSSHKnown(GlobalDir + "ssh_known_hosts")
1072 GenHosts(l, GlobalDir + "debianhosts")
1084 Split = Line.split(" ")
1085 OutDir = GenerateDir + '/' + Split[0] + '/'
1091 # Get the group list and convert any named groups to numerics
1099 if GroupIDMap.has_key(I):
1100 GroupList[str(GroupIDMap[I])] = None
1105 CurrentHost = Split[0]
1107 DoLink(GlobalDir, OutDir, "debianhosts")
1108 DoLink(GlobalDir, OutDir, "ssh_known_hosts")
1109 DoLink(GlobalDir, OutDir, "disabled-accounts")
1112 if ExtraList.has_key("[NOPASSWD]"):
1113 userlist = GenPasswd(OutDir + "passwd", Split[1], "*")
1115 userlist = GenPasswd(OutDir + "passwd", Split[1], "x")
1117 grouprevmap = GenGroup(OutDir + "group")
1118 GenShadowSudo(OutDir + "sudo-passwd", ExtraList.has_key("[UNTRUSTED]") or ExtraList.has_key("[NOPASSWD]"))
1120 # Now we know who we're allowing on the machine, export
1121 # the relevant ssh keys
1122 GenSSHtarballs(userlist, SSHFiles, grouprevmap, os.path.join(OutDir, 'ssh-keys.tar.gz'))
1124 if ExtraList.has_key("[UNTRUSTED]"):
1125 print "[UNTRUSTED] tag is obsolete and may be removed in the future."
1127 if not ExtraList.has_key("[NOPASSWD]"):
1128 GenShadow(OutDir + "shadow")
1130 # Link in global things
1131 if not ExtraList.has_key("[NOMARKERS]"):
1132 DoLink(GlobalDir, OutDir, "markers")
1133 DoLink(GlobalDir, OutDir, "mail-forward.cdb")
1134 DoLink(GlobalDir, OutDir, "mail-contentinspectionaction.cdb")
1135 DoLink(GlobalDir, OutDir, "mail-disable")
1136 DoLink(GlobalDir, OutDir, "mail-greylist")
1137 DoLink(GlobalDir, OutDir, "mail-callout")
1138 DoLink(GlobalDir, OutDir, "mail-rbl")
1139 DoLink(GlobalDir, OutDir, "mail-rhsbl")
1140 DoLink(GlobalDir, OutDir, "mail-whitelist")
1143 DoLink(GlobalDir, OutDir, "forward-alias")
1145 if ExtraList.has_key("[DNS]"):
1146 GenDNS(OutDir + "dns-zone", Split[1])
1147 GenSSHFP(OutDir + "dns-sshfp", Split[1])
1149 if ExtraList.has_key("[BSMTP]"):
1150 GenBSMTP(OutDir + "bsmtp", Split[1])
1152 if ExtraList.has_key("[PRIVATE]"):
1153 DoLink(GlobalDir, OutDir, "debian-private")
1155 if ExtraList.has_key("[KEYRING]"):
1157 DoLink(GlobalDir, OutDir, os.path.basename(k))
1161 posix.remove(OutDir + os.path.basename(k))
1167 # vim:set shiftwidth=3: