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 DNSZone = ".debian.net"
52 Keyrings = ConfModule.sync_keyrings.split(":")
54 def safe_makedirs(dir):
58 if e.errno == errno.EEXIST:
67 if e.errno == errno.ENOENT:
73 return Str.translate(string.maketrans("\n\r\t", "$$$"))
75 def DoLink(From, To, File):
77 posix.remove(To + File)
80 posix.link(From + File, To + File)
82 def IsRetired(DnRecord):
84 Looks for accountStatus in the LDAP record and tries to
85 match it against one of the known retired statuses
88 status = GetAttr(DnRecord, "accountStatus", None)
95 if status == "inactive":
98 elif status == "memorial":
101 elif status == "retiring":
102 # We'll give them a few extra days over what we said
103 age = 6 * 31 * 24 * 60 * 60
105 return (time.time() - time.mktime(time.strptime(line[1], "%Y-%m-%d"))) > age
115 return int(GetAttr(x, "gidNumber", 0)) == 800
119 # See if this user is in the group list
120 def IsInGroup(DnRecord):
124 # See if the primary group is in the list
125 if Allowed.has_key(GetAttr(DnRecord, "gidNumber")) != 0:
128 # Check the host based ACL
129 if DnRecord[1].has_key("allowedHost") != 0:
130 if CurrentHost in DnRecord[1]["allowedHost"]:
133 # See if there are supplementary groups
134 if DnRecord[1].has_key("supplementaryGid") == 0:
138 addGroups(supgroups, DnRecord[1]["supplementaryGid"], GetAttr(DnRecord, "uid"))
140 if Allowed.has_key(g):
144 def Die(File, F, Fdb):
150 os.remove(File + ".tmp")
154 os.remove(File + ".tdb.tmp")
158 def Done(File, F, Fdb):
161 os.rename(File + ".tmp", File)
164 os.rename(File + ".tdb.tmp", File + ".tdb")
166 # Generate the password list
167 def GenPasswd(File, HomePrefix, PwdMarker):
170 F = open(File + ".tdb.tmp", "w")
173 # Fetch all the users
177 for x in PasswdAttrs:
178 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
181 # Do not let people try to buffer overflow some busted passwd parser.
182 if len(GetAttr(x, "gecos")) > 100 or len(GetAttr(x, "loginShell")) > 50:
185 userlist[GetAttr(x, "uid")] = int(GetAttr(x, "gidNumber"))
186 Line = "%s:%s:%s:%s:%s:%s%s:%s" % (GetAttr(x, "uid"),\
188 GetAttr(x, "uidNumber"), GetAttr(x, "gidNumber"),\
189 GetAttr(x, "gecos"), HomePrefix, GetAttr(x, "uid"),\
190 GetAttr(x, "loginShell"))
192 Line = Sanitize(Line) + "\n"
193 F.write("0%u %s" % (I, Line))
194 F.write(".%s %s" % (GetAttr(x, "uid"), Line))
195 F.write("=%s %s" % (GetAttr(x, "uidNumber"), Line))
198 # Oops, something unspeakable happened.
204 # Return the list of users so we know which keys to export
207 # Generate the shadow list
211 OldMask = os.umask(0077)
212 F = open(File + ".tdb.tmp", "w", 0600)
215 # Fetch all the users
219 for x in PasswdAttrs:
220 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
223 Pass = GetAttr(x, "userPassword")
224 if Pass[0:7] != "{crypt}" or len(Pass) > 50:
229 # If the account is locked, mark it as such in shadow
230 # See Debian Bug #308229 for why we set it to 1 instead of 0
231 if (GetAttr(x, "userPassword").find("*LK*") != -1) \
232 or GetAttr(x, "userPassword").startswith("!"):
235 ShadowExpire = GetAttr(x, "shadowExpire")
237 Line = "%s:%s:%s:%s:%s:%s:%s:%s:" % (GetAttr(x, "uid"),\
238 Pass, GetAttr(x, "shadowLastChange"),\
239 GetAttr(x, "shadowMin"), GetAttr(x, "shadowMax"),\
240 GetAttr(x, "shadowWarning"), GetAttr(x, "shadowInactive"),\
242 Line = Sanitize(Line) + "\n"
243 F.write("0%u %s" % (I, Line))
244 F.write(".%s %s" % (GetAttr(x, "uid"), Line))
247 # Oops, something unspeakable happened.
253 # Generate the sudo passwd file
254 def GenShadowSudo(File, untrusted):
257 OldMask = os.umask(0077)
258 F = open(File + ".tmp", "w", 0600)
261 # Fetch all the users
264 for x in PasswdAttrs:
266 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
269 if x[1].has_key('sudoPassword'):
270 for entry in x[1]['sudoPassword']:
271 Match = re.compile('^('+UUID_FORMAT+') (confirmed:[0-9a-f]{40}|unconfirmed) ([a-z0-9.,*]+) ([^ ]+)$').match(entry)
274 uuid = Match.group(1)
275 status = Match.group(2)
276 hosts = Match.group(3)
277 cryptedpass = Match.group(4)
279 if status != 'confirmed:'+make_passwd_hmac('password-is-confirmed', 'sudo', x[1]['uid'][0], uuid, hosts, cryptedpass):
281 for_all = hosts == "*"
282 for_this_host = CurrentHost in hosts.split(',')
283 if not (for_all or for_this_host):
285 # ignore * passwords for untrusted hosts, but copy host specific passwords
286 if for_all and untrusted:
289 if for_this_host: # this makes sure we take a per-host entry over the for-all entry
294 Line = "%s:%s" % (GetAttr(x, "uid"), Pass)
295 Line = Sanitize(Line) + "\n"
296 F.write("%s" % (Line))
298 # Oops, something unspeakable happened.
304 # Generate the shadow list
306 # Fetch all the users
311 safe_rmtree(os.path.join(GlobalDir, 'userkeys'))
312 safe_makedirs(os.path.join(GlobalDir, 'userkeys'))
314 for x in PasswdAttrs:
316 if x[1].has_key("uidNumber") == 0 or \
317 x[1].has_key("sshRSAAuthKey") == 0:
320 User = GetAttr(x, "uid")
324 OldMask = os.umask(0077)
325 File = os.path.join(GlobalDir, 'userkeys', User)
326 F = open(File + ".tmp", "w", 0600)
329 for I in x[1]["sshRSAAuthKey"]:
330 MultipleLine = "%s" % I
331 MultipleLine = Sanitize(MultipleLine) + "\n"
332 F.write(MultipleLine)
335 userfiles.append(os.path.basename(File))
337 # Oops, something unspeakable happened.
340 Die(masterFileName, masterFile, None)
345 def GenSSHtarballs(userlist, SSHFiles, grouprevmap, target):
346 OldMask = os.umask(0077)
347 tf = tarfile.open(name=os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), mode='w:gz')
349 for f in userlist.keys():
350 if f not in SSHFiles:
352 # If we're not exporting their primary group, don't export
355 if userlist[f] in grouprevmap.keys():
356 grname = grouprevmap[userlist[f]]
359 if int(userlist[f]) <= 100:
360 # In these cases, look it up in the normal way so we
361 # deal with cases where, for instance, users are in group
362 # users as their primary group.
363 grname = grp.getgrgid(userlist[f])[0]
368 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])
371 to = tf.gettarinfo(os.path.join(GlobalDir, 'userkeys', f), f)
372 # These will only be used where the username doesn't
373 # exist on the target system for some reason; hence,
374 # in those cases, the safest thing is for the file to
375 # be owned by root but group nobody. This deals with
376 # the bloody obscure case where the group fails to exist
377 # whilst the user does (in which case we want to avoid
378 # ending up with a file which is owned user:root to avoid
379 # a fairly obvious attack vector)
382 # Using the username / groupname fields avoids any need
383 # to give a shit^W^W^Wcare about the UIDoffset stuff.
387 tf.addfile(to, file(os.path.join(GlobalDir, 'userkeys', f)))
390 os.rename(os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), target)
392 # add a list of groups to existing groups,
393 # including all subgroups thereof, recursively.
394 # basically this proceduces the transitive hull of the groups in
396 def addGroups(existingGroups, newGroups, uid):
397 for group in newGroups:
398 # if it's a <group>@host, split it and verify it's on the current host.
399 s = group.split('@', 1)
400 if len(s) == 2 and s[1] != CurrentHost:
404 # let's see if we handled this group already
405 if group in existingGroups:
408 if not GroupIDMap.has_key(group):
409 print "Group", group, "does not exist but", uid, "is in it"
412 existingGroups.append(group)
414 if SubGroupMap.has_key(group):
415 addGroups(existingGroups, SubGroupMap[group], uid)
417 # Generate the group list
422 F = open(File + ".tdb.tmp", "w")
424 # Generate the GroupMap
426 for x in GroupIDMap.keys():
429 # Fetch all the users
432 # Sort them into a list of groups having a set of users
433 for x in PasswdAttrs:
434 uid = GetAttr(x, "uid")
435 if x[1].has_key("uidNumber") == 0 or not IsInGroup(x):
437 if x[1].has_key("supplementaryGid") == 0:
441 addGroups(supgroups, x[1]["supplementaryGid"], uid)
443 GroupMap[g].append(uid)
445 # Output the group file.
447 for x in GroupMap.keys():
448 grouprevmap[GroupIDMap[x]] = x
449 if GroupIDMap.has_key(x) == 0:
451 Line = "%s:x:%u:" % (x, GroupIDMap[x])
453 for I in GroupMap[x]:
454 Line = Line + ("%s%s" % (Comma, I))
456 Line = Sanitize(Line) + "\n"
457 F.write("0%u %s" % (J, Line))
458 F.write(".%s %s" % (x, Line))
459 F.write("=%u %s" % (GroupIDMap[x], Line))
462 # Oops, something unspeakable happened.
472 for x in DebianUsers:
473 if x[1].has_key("emailForward") == 0:
477 x[1].pop("emailForward")
480 # Do not allow people to try to buffer overflow busted parsers
481 if len(GetAttr(x, "emailForward")) > 200:
482 x[1].pop("emailForward")
485 # Check the forwarding address
486 if EmailCheck.match(GetAttr(x, "emailForward")) == None:
487 x[1].pop("emailForward")
489 # Generate the email forwarding list
490 def GenForward(File):
493 OldMask = os.umask(0022)
494 F = open(File + ".tmp", "w", 0644)
497 # Fetch all the users
500 # Write out the email address for each user
501 for x in DebianUsers:
502 if x[1].has_key("emailForward") == 0:
505 Line = "%s: %s" % (GetAttr(x, "uid"), GetAttr(x, "emailForward"))
506 Line = Sanitize(Line) + "\n"
509 # Oops, something unspeakable happened.
515 def GenCDB(File, Key):
518 OldMask = os.umask(0022)
519 Fdb = os.popen("cdbmake %s %s.tmp"%(File, File), "w")
522 # Fetch all the users
525 # Write out the email address for each user
526 for x in DebianUsers:
529 Value = GetAttr(x, Key)
530 User = GetAttr(x, "uid")
531 Fdb.write("+%d,%d:%s->%s\n" % (len(User), len(Value), User, Value))
534 # Oops, something unspeakable happened.
538 if Fdb.close() != None:
539 raise "cdbmake gave an error"
541 # Generate the anon XEarth marker file
542 def GenMarkers(File):
545 F = open(File + ".tmp", "w")
547 # Fetch all the users
550 # Write out the position for each user
551 for x in DebianUsers:
552 if x[1].has_key("latitude") == 0 or x[1].has_key("longitude") == 0:
555 Line = "%8s %8s \"\""%(DecDegree(GetAttr(x, "latitude"), 1), DecDegree(GetAttr(x, "longitude"), 1))
556 Line = Sanitize(Line) + "\n"
561 # Oops, something unspeakable happened.
567 # Generate the debian-private subscription list
568 def GenPrivate(File):
571 F = open(File + ".tmp", "w")
573 # Fetch all the users
576 # Write out the position for each user
577 for x in DebianUsers:
578 if x[1].has_key("privateSub") == 0:
581 # If the account has no PGP key, do not write it
582 if x[1].has_key("keyFingerPrint") == 0:
586 Line = "%s"%(GetAttr(x, "privateSub"))
587 Line = Sanitize(Line) + "\n"
592 # Oops, something unspeakable happened.
598 # Generate a list of locked accounts
599 def GenDisabledAccounts(File):
602 F = open(File + ".tmp", "w")
604 # Fetch all the users
609 for x in PasswdAttrs:
610 if x[1].has_key("uidNumber") == 0:
613 Pass = GetAttr(x, "userPassword")
615 # *LK* is the reference value for a locked account
616 # password starting with ! is also a locked account
617 if Pass.find("*LK*") != -1 or Pass.startswith("!"):
618 # Format is <login>:<reason>
619 Line = "%s:%s" % (GetAttr(x, "uid"), "Account is locked")
620 DisabledUsers.append(x)
623 F.write(Sanitize(Line) + "\n")
626 # Oops, something unspeakable happened.
632 # Generate the list of local addresses that refuse all mail
633 def GenMailDisable(File):
636 F = open(File + ".tmp", "w")
638 # Fetch all the users
641 for x in DebianUsers:
644 if x[1].has_key("mailDisableMessage"):
645 Reason = GetAttr(x, "mailDisableMessage")
650 Line = "%s: %s"%(GetAttr(x, "uid"), Reason)
651 Line = Sanitize(Line) + "\n"
656 # Oops, something unspeakable happened.
662 # Generate a list of uids that should have boolean affects applied
663 def GenMailBool(File, Key):
666 F = open(File + ".tmp", "w")
668 # Fetch all the users
671 for x in DebianUsers:
674 if x[1].has_key(Key) == 0:
677 if GetAttr(x, Key) != "TRUE":
681 Line = "%s"%(GetAttr(x, "uid"))
682 Line = Sanitize(Line) + "\n"
687 # Oops, something unspeakable happened.
693 # Generate a list of hosts for RBL or whitelist purposes.
694 def GenMailList(File, Key):
697 F = open(File + ".tmp", "w")
699 # Fetch all the users
702 for x in DebianUsers:
705 if x[1].has_key(Key) == 0:
712 if Key == "mailWhitelist":
713 if re.match('^[-\w.]+(/[\d]+)?$', z) == None:
716 if re.match('^[-\w.]+$', z) == None:
720 Line = GetAttr(x, "uid")
724 if Key == "mailRHSBL":
725 Line += "/$sender_address_domain"
728 Line = Sanitize(Line) + "\n"
733 # Oops, something unspeakable happened.
739 def isRoleAccount(pwEntry):
740 if not pwEntry.has_key("objectClass"):
741 raise "pwEntry has no objectClass"
742 oc = pwEntry['objectClass']
744 i = oc.index('debianRoleAccount')
749 # Generate the DNS Zone file
753 F = open(File + ".tmp", "w")
755 # Fetch all the users
758 # Write out the zone file entry for each user
759 for x in PasswdAttrs:
760 if x[1].has_key("dnsZoneEntry") == 0:
763 # If the account has no PGP key, do not write it
764 if x[1].has_key("keyFingerPrint") == 0 and not isRoleAccount(x[1]):
767 F.write("; %s\n"%(EmailAddress(x)))
768 for z in x[1]["dnsZoneEntry"]:
769 Split = z.lower().split()
770 if Split[1].lower() == 'in':
771 for y in range(0, len(Split)):
774 Line = " ".join(Split) + "\n"
777 Host = Split[0] + DNSZone
778 if BSMTPCheck.match(Line) != None:
779 F.write("; Has BSMTP\n")
781 # Write some identification information
782 if Split[2].lower() == "a":
783 Line = "%s IN TXT \"%s\"\n"%(Split[0], EmailAddress(x))
784 for y in x[1]["keyFingerPrint"]:
785 Line = Line + "%s IN TXT \"PGP %s\"\n"%(Split[0], FormatPGPKey(y))
788 Line = "; Err %s"%(str(Split))
793 F.write("; Errors\n")
796 # Oops, something unspeakable happened.
802 # Generate the DNS SSHFP records
806 F = open(File + ".tmp", "w")
808 # Fetch all the hosts
810 if HostAttrs == None:
811 raise UDEmptyList, "No Hosts"
814 if x[1].has_key("hostname") == 0 or \
815 x[1].has_key("architecture") == 0 or\
816 x[1].has_key("sshRSAHostKey") == 0:
818 Host = GetAttr(x, "hostname")
819 Arch = GetAttr(x, "architecture")
822 for I in x[1]["sshRSAHostKey"]:
824 if Split[0] == 'ssh-rsa':
826 if Split[0] == 'ssh-dss':
828 if Algorithm == None:
830 Fingerprint = sha.new(base64.decodestring(Split[1])).hexdigest()
831 Line = "%s. IN SSHFP %u 1 %s" % (Host, Algorithm, Fingerprint)
832 Line = Sanitize(Line) + "\n"
836 if x[1].has_key("machine"):
837 Mach = " " + GetAttr(x, "machine")
838 Line = "%s. IN HINFO \"%s%s\" \"%s\"" % (Host, Arch, Mach, "Debian GNU/Linux")
839 Line = Sanitize(Line) + "\n"
842 if x[1].has_key("ipHostNumber"):
843 for I in x[1]["ipHostNumber"]:
844 if IsV6Addr.match(I) != None:
845 Line = "%s. IN AAAA %s" % (Host, I)
847 Line = "%s. IN A %s" % (Host, I)
848 Line = Sanitize(Line) + "\n"
851 if x[1].has_key("mXRecord"):
852 for I in x[1]["mXRecord"]:
853 Line = "%s. IN MX %s" % (Host, I)
854 Line = Sanitize(Line) + "\n"
857 # Oops, something unspeakable happened.
863 # Generate the BSMTP file
864 def GenBSMTP(File, HomePrefix):
867 F = open(File + ".tmp", "w")
869 # Fetch all the users
872 # Write out the zone file entry for each user
873 for x in DebianUsers:
874 if x[1].has_key("dnsZoneEntry") == 0:
877 # If the account has no PGP key, do not write it
878 if x[1].has_key("keyFingerPrint") == 0:
881 for z in x[1]["dnsZoneEntry"]:
882 Split = z.lower().split()
883 if Split[1].lower() == 'in':
884 for y in range(0, len(Split)):
887 Line = " ".join(Split) + "\n"
889 Host = Split[0] + DNSZone
890 if BSMTPCheck.match(Line) != None:
891 F.write("%s: user=%s group=Debian file=%s%s/bsmtp/%s\n"%(Host,
892 GetAttr(x, "uid"), HomePrefix, GetAttr(x, "uid"), Host))
895 F.write("; Errors\n")
898 # Oops, something unspeakable happened.
908 if not Host in HostToIPCache:
911 IPAdressesT = list(set([ (a[0], a[4][0]) for a in socket.getaddrinfo(Host, None)]))
912 except socket.gaierror, (code):
916 if not IPAdressesT is None:
917 for addr in IPAdressesT:
918 if addr[0] == socket.AF_INET:
919 IPAdresses += [addr[1], "::ffff:"+addr[1]]
921 IPAdresses += [addr[1]]
922 HostToIPCache[Host] = IPAdresses
923 return HostToIPCache[Host]
925 # Generate the ssh known hosts file
926 def GenSSHKnown(File, mode=None):
929 OldMask = os.umask(0022)
930 F = open(File + ".tmp", "w", 0644)
934 if HostAttrs is None:
935 raise UDEmptyList, "No Hosts"
938 if x[1].has_key("hostname") == 0 or \
939 x[1].has_key("sshRSAHostKey") == 0:
941 Host = GetAttr(x, "hostname")
943 if Host.endswith(HostDomain):
944 HostNames.append(Host[:-(len(HostDomain) + 1)])
946 # in the purpose field [[host|some other text]] (where some other text is optional)
947 # makes a hyperlink on the web thing. we now also add these hosts to the ssh known_hosts
948 # file. But so that we don't have to add everything we link we can add an asterisk
949 # and say [[*... to ignore it. In order to be able to add stuff to ssh without
950 # http linking it we also support [[-hostname]] entries.
951 for i in x[1].get("purpose", []):
952 m = PurposeHostField.match(i)
955 # we ignore [[*..]] entries
956 if m.startswith('*'):
958 if m.startswith('-'):
962 if m.endswith(HostDomain):
963 HostNames.append(m[:-(len(HostDomain) + 1)])
965 for I in x[1]["sshRSAHostKey"]:
966 if mode and mode == 'authorized_keys':
967 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(HostToIP(Host)), I)
968 #Line = 'command="rsync --server --sender -pr . /var/cache/userdir-ldap/hosts/%s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding %s' % (Host,I)
970 Line = "%s %s" %(",".join(HostNames + HostToIP(Host)), I)
971 Line = Sanitize(Line) + "\n"
973 # Oops, something unspeakable happened.
979 # Generate the debianhosts file (list of all IP addresses)
980 def GenHosts(l, File):
983 OldMask = os.umask(0022)
984 F = open(File + ".tmp", "w", 0644)
987 # Fetch all the hosts
988 hostnames = l.search_s(HostBaseDn, ldap.SCOPE_ONELEVEL, "hostname=*",
991 if hostnames is None:
992 raise UDEmptyList, "No Hosts"
996 host = GetAttr(x, "hostname", None)
1000 addrs += socket.getaddrinfo(host, None, socket.AF_INET)
1001 except socket.error:
1004 addrs += socket.getaddrinfo(host, None, socket.AF_INET6)
1005 except socket.error:
1008 for addrinfo in addrs:
1009 if addrinfo[0] in (socket.AF_INET, socket.AF_INET6):
1010 addr = addrinfo[4][0]
1011 if addr not in seen:
1012 print >> F, addrinfo[4][0]
1014 # Oops, something unspeakable happened.
1020 def GenKeyrings(OutDir):
1022 shutil.copy(k, OutDir)
1024 # Connect to the ldap server
1026 F = open(PassDir + "/pass-" + pwd.getpwuid(os.getuid())[0], "r")
1027 Pass = F.readline().strip().split(" ")
1029 l.simple_bind_s("uid=" + Pass[0] + "," + BaseDn, Pass[1])
1031 # Fetch all the groups
1033 Attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "gid=*",\
1034 ["gid", "gidNumber", "subGroup"])
1036 # Generate the SubGroupMap and GroupIDMap
1038 if x[1].has_key("gidNumber") == 0:
1040 GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidNumber"][0])
1041 if x[1].has_key("subGroup") != 0:
1042 SubGroupMap.setdefault(x[1]["gid"][0], []).extend(x[1]["subGroup"])
1044 # Fetch all the users
1045 PasswdAttrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "uid=*",\
1046 ["uid", "uidNumber", "gidNumber", "supplementaryGid",\
1047 "gecos", "loginShell", "userPassword", "shadowLastChange",\
1048 "shadowMin", "shadowMax", "shadowWarning", "shadowInactive",
1049 "shadowExpire", "emailForward", "latitude", "longitude",\
1050 "allowedHost", "sshRSAAuthKey", "dnsZoneEntry", "cn", "sn",\
1051 "keyFingerPrint", "privateSub", "mailDisableMessage",\
1052 "mailGreylisting", "mailCallout", "mailRBL", "mailRHSBL",\
1053 "mailWhitelist", "sudoPassword", "objectClass", "accountStatus",\
1054 "mailContentInspectionAction"])
1056 if PasswdAttrs is None:
1057 raise UDEmptyList, "No Users"
1059 # Fetch all the hosts
1060 HostAttrs = l.search_s(HostBaseDn, ldap.SCOPE_ONELEVEL, "objectClass=debianServer",\
1061 ["hostname", "sshRSAHostKey", "purpose", "allowedGroups", "exportOptions",\
1062 "mXRecord", "ipHostNumber", "machine", "architecture"])
1064 # Generate global things
1065 GlobalDir = GenerateDir + "/"
1066 GenDisabledAccounts(GlobalDir + "disabled-accounts")
1068 PasswdAttrs = filter(lambda x: not IsRetired(x), PasswdAttrs)
1069 #DebianUsers = filter(lambda x: IsGidDebian(x), PasswdAttrs)
1070 DebianUsers = PasswdAttrs
1074 GenMailDisable(GlobalDir + "mail-disable")
1075 GenCDB(GlobalDir + "mail-forward.cdb", 'emailForward')
1076 GenCDB(GlobalDir + "mail-contentinspectionaction.cdb", 'mailContentInspectionAction')
1077 GenPrivate(GlobalDir + "debian-private")
1078 GenSSHKnown(GlobalDir+"authorized_keys", 'authorized_keys')
1079 GenMailBool(GlobalDir + "mail-greylist", "mailGreylisting")
1080 GenMailBool(GlobalDir + "mail-callout", "mailCallout")
1081 GenMailList(GlobalDir + "mail-rbl", "mailRBL")
1082 GenMailList(GlobalDir + "mail-rhsbl", "mailRHSBL")
1083 GenMailList(GlobalDir + "mail-whitelist", "mailWhitelist")
1084 GenKeyrings(GlobalDir)
1087 GenForward(GlobalDir + "forward-alias")
1089 PasswdAttrs = filter(lambda x: not x in DisabledUsers, PasswdAttrs)
1091 SSHFiles = GenSSHShadow()
1092 GenMarkers(GlobalDir + "markers")
1093 GenSSHKnown(GlobalDir + "ssh_known_hosts")
1094 GenHosts(l, GlobalDir + "debianhosts")
1096 for host in HostAttrs:
1097 if not "hostname" in host[1]:
1100 CurrentHost = host[1]['hostname'][0]
1101 OutDir = GenerateDir + '/' + CurrentHost + '/'
1107 # Get the group list and convert any named groups to numerics
1109 for groupname in AllowedGroupsPreload.strip().split(" "):
1110 GroupList[groupname] = True
1111 if 'allowedGroups' in host[1]:
1112 for groupname in host[1]['allowedGroups']:
1113 GroupList[groupname] = True
1114 for groupname in GroupList.keys():
1115 if groupname in GroupIDMap:
1116 GroupList[str(GroupIDMap[groupname])] = True
1119 if 'exportOptions' in host[1]:
1120 for extra in host[1]['exportOptions']:
1121 ExtraList[extra.upper()] = True
1127 DoLink(GlobalDir, OutDir, "debianhosts")
1128 DoLink(GlobalDir, OutDir, "ssh_known_hosts")
1129 DoLink(GlobalDir, OutDir, "disabled-accounts")
1132 if 'NOPASSWD' in ExtraList:
1133 userlist = GenPasswd(OutDir + "passwd", HomePrefix, "*")
1135 userlist = GenPasswd(OutDir + "passwd", HomePrefix, "x")
1137 grouprevmap = GenGroup(OutDir + "group")
1138 GenShadowSudo(OutDir + "sudo-passwd", ('UNTRUSTED' in ExtraList) or ('NOPASSWD' in ExtraList))
1140 # Now we know who we're allowing on the machine, export
1141 # the relevant ssh keys
1142 GenSSHtarballs(userlist, SSHFiles, grouprevmap, os.path.join(OutDir, 'ssh-keys.tar.gz'))
1144 if not 'NOPASSWD' in ExtraList:
1145 GenShadow(OutDir + "shadow")
1147 # Link in global things
1148 if not 'NOMARKERS' in ExtraList:
1149 DoLink(GlobalDir, OutDir, "markers")
1150 DoLink(GlobalDir, OutDir, "mail-forward.cdb")
1151 DoLink(GlobalDir, OutDir, "mail-contentinspectionaction.cdb")
1152 DoLink(GlobalDir, OutDir, "mail-disable")
1153 DoLink(GlobalDir, OutDir, "mail-greylist")
1154 DoLink(GlobalDir, OutDir, "mail-callout")
1155 DoLink(GlobalDir, OutDir, "mail-rbl")
1156 DoLink(GlobalDir, OutDir, "mail-rhsbl")
1157 DoLink(GlobalDir, OutDir, "mail-whitelist")
1160 DoLink(GlobalDir, OutDir, "forward-alias")
1162 if 'DNS' in ExtraList:
1163 GenDNS(OutDir + "dns-zone")
1164 GenSSHFP(OutDir + "dns-sshfp")
1166 if 'AUTHKEYS' in ExtraList:
1167 DoLink(GlobalDir, OutDir, "authorized_keys")
1169 if 'BSMTP' in ExtraList:
1170 GenBSMTP(OutDir + "bsmtp", HomePrefix)
1172 if 'PRIVATE' in ExtraList:
1173 DoLink(GlobalDir, OutDir, "debian-private")
1175 if 'KEYRING' in ExtraList:
1177 DoLink(GlobalDir, OutDir, os.path.basename(k))
1181 posix.remove(OutDir + os.path.basename(k))
1187 # vim:set shiftwidth=3: