X-Git-Url: https://git.adam-barratt.org.uk/?a=blobdiff_plain;f=ud-generate;h=850bb51191fcd74f37b0d2205cfc06406e7b23c1;hb=032921f6a9454a411d72cf859aaef6dabdf8a2c9;hp=e6f67f3183fc044a5469a9364cc6d3c7361ef2bd;hpb=6fd20b860bb8d0ed91852d5bbccd2e21eeb9e48b;p=mirror%2Fuserdir-ldap.git diff --git a/ud-generate b/ud-generate index e6f67f3..850bb51 100755 --- a/ud-generate +++ b/ud-generate @@ -2,575 +2,1125 @@ # -*- mode: python -*- # Generates passwd, shadow and group files from the ldap directory. -import string, re, time, ldap, getopt, sys, os, pwd, posix, socket; -from userdir_ldap import *; - -PasswdAttrs = None; -GroupIDMap = {}; -Allowed = None; -CurrentHost = ""; - -EmailCheck = re.compile("^([^ <>@]+@[^ ,<>@]+)?$"); -BSMTPCheck = re.compile(".*mx 0 klecker\.debian\.org\..*",re.DOTALL); +# Copyright (c) 2000-2001 Jason Gunthorpe +# Copyright (c) 2003-2004 James Troup +# Copyright (c) 2004-2005,7 Joey Schulze +# Copyright (c) 2001-2007 Ryan Murray +# Copyright (c) 2008,2009,2010 Peter Palfrader +# Copyright (c) 2008 Andreas Barth +# Copyright (c) 2008 Mark Hymers +# Copyright (c) 2008 Luk Claes +# Copyright (c) 2008 Thomas Viehmann +# Copyright (c) 2009 Stephen Gran +# Copyright (c) 2010 Helmut Grohne +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import string, re, time, ldap, getopt, sys, os, pwd, posix, socket, base64, sha, shutil, errno, tarfile, grp +from userdir_ldap import * +from userdir_exceptions import * +import UDLdap +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +global Allowed +global CurrentHost + +if os.getuid() == 0: + sys.stderr.write("You should probably not run ud-generate as root.\n") + sys.exit(1) + +DebianUsers = None +GroupIDMap = {} +SubGroupMap = {} +Allowed = None +CurrentHost = "" + +UUID_FORMAT = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' + +EmailCheck = re.compile("^([^ <>@]+@[^ ,<>@]+)?$") +BSMTPCheck = re.compile(".*mx 0 (master)\.debian\.org\..*",re.DOTALL) +PurposeHostField = re.compile(r".*\[\[([\*\-]?[a-z0-9.\-]*)(?:\|.*)?\]\]") +IsV6Addr = re.compile("^[a-fA-F0-9:]+$") +IsDebianHost = re.compile(ConfModule.dns_hostmatch) +isSSHFP = re.compile("^\s*IN\s+SSHFP") DNSZone = ".debian.net" +Keyrings = ConfModule.sync_keyrings.split(":") + +def safe_makedirs(dir): + try: + os.makedirs(dir) + except OSError, e: + if e.errno == errno.EEXIST: + pass + else: + raise e + +def safe_rmtree(dir): + try: + shutil.rmtree(dir) + except OSError, e: + if e.errno == errno.ENOENT: + pass + else: + raise e def Sanitize(Str): - return string.translate(Str,string.maketrans("\n\r\t","$$$")); + return Str.translate(string.maketrans("\n\r\t", "$$$")) + +def DoLink(From, To, File): + try: + posix.remove(To + File) + except: + pass + posix.link(From + File, To + File) + +def IsRetired(account): + """ + Looks for accountStatus in the LDAP record and tries to + match it against one of the known retired statuses + """ + + status = account['accountStatus'] + + line = status.split() + status = line[0] + + if status == "inactive": + return True -def DoLink(From,To,File): - try: posix.remove(To+File); - except: pass; - posix.link(From+File,To+File); + elif status == "memorial": + return True + + elif status == "retiring": + # We'll give them a few extra days over what we said + age = 6 * 31 * 24 * 60 * 60 + try: + return (time.time() - time.mktime(time.strptime(line[1], "%Y-%m-%d"))) > age + except IndexError: + return False + except ValueError: + return False + + return False + +#def IsGidDebian(account): +# return account['gidNumber'] == 800 # See if this user is in the group list -def IsInGroup(DnRecord): - global Allowed,CurrentHost; - if Allowed == None: - return 1; +def IsInGroup(account): + if Allowed is None: + return True # See if the primary group is in the list - if Allowed.has_key(GetAttr(DnRecord,"gidnumber")) != 0: - return 1; + if str(account['gidNumber']) in Allowed: return True # Check the host based ACL - if DnRecord[1].has_key("allowedhosts") != 0: - for I in DnRecord[1]["allowedhosts"]: - if CurrentHost == I: - return 1; + if 'allowedHost' in account and CurrentHost in account['allowedHost']: return True # See if there are supplementary groups - if DnRecord[1].has_key("supplementarygid") == 0: - return 0; + if not 'supplementaryGid' in account: return False - # Check the supplementary groups - for I in DnRecord[1]["supplementarygid"]: - if Allowed.has_key(I): - return 1; - return 0; + supgroups=[] + addGroups(supgroups, account['supplementaryGid'], account['uid']) + for g in supgroups: + if Allowed.has_key(g): + return True + return False -def Die(File,F,Fdb): +def Die(File, F, Fdb): if F != None: - F.close(); + F.close() if Fdb != None: - Fdb.close(); - try: os.remove(File + ".tmp"); - except: pass; - try: os.remove(File + ".tdb.tmp"); - except: pass; - -def Done(File,F,Fdb): - if F != None: - F.close(); - os.rename(File + ".tmp",File); - if Fdb != None: - Fdb.close(); - os.rename(File + ".tdb.tmp",File+".tdb"); - -# Generate the password list -def GenPasswd(l,File,HomePrefix): - F = None; - Fdb = None; - try: - F = open(File + ".tmp","w"); - Fdb = open(File + ".tdb.tmp","w"); + Fdb.close() + try: + os.remove(File + ".tmp") + except: + pass + try: + os.remove(File + ".tdb.tmp") + except: + pass + +def Done(File, F, Fdb): + if F != None: + F.close() + os.rename(File + ".tmp", File) + if Fdb != None: + Fdb.close() + os.rename(File + ".tdb.tmp", File + ".tdb") - # Fetch all the users - global PasswdAttrs; - if PasswdAttrs == None: - raise "No Users"; - - I = 0; - for x in PasswdAttrs: - if x[1].has_key("uidnumber") == 0 or IsInGroup(x) == 0: - continue; - - # Do not let people try to buffer overflow some busted passwd parser. - if len(GetAttr(x,"gecos")) > 100 or len(GetAttr(x,"loginshell")) > 50: - continue; - - Line = "%s:x:%s:%s:%s:%s%s:%s\n" % (GetAttr(x,"uid"),\ - GetAttr(x,"uidnumber"),GetAttr(x,"gidnumber"),\ - GetAttr(x,"gecos"),HomePrefix,GetAttr(x,"uid"),\ - GetAttr(x,"loginshell")); - F.write(Line); - Fdb.write("0%u %s" % (I,Line)); - Fdb.write(".%s %s" % (GetAttr(x,"uid"),Line)); - Fdb.write("=%s %s" % (GetAttr(x,"uidnumber"),Line)); - I = I + 1; - - # Oops, something unspeakable happened. - except: - Die(File,F,Fdb); - raise; - Done(File,F,Fdb); +# Generate the password list +def GenPasswd(accounts, File, HomePrefix, PwdMarker): + F = None + try: + F = open(File + ".tdb.tmp", "w") + + userlist = {} + i = 0 + for a in accounts: + if not IsInGroup(a): continue + + # Do not let people try to buffer overflow some busted passwd parser. + if len(a['gecos']) > 100 or len(a['loginShell']) > 50: continue + + userlist[a['uid']] = a['gidNumber'] + line = "%s:%s:%d:%d:%s:%s%s:%s" % ( + a['uid'], + PwdMarker, + a['uidNumber'], + a['gidNumber'], + a['gecos'], + HomePrefix, a['uid'], + a['loginShell']) + line = Sanitize(line) + "\n" + F.write("0%u %s" % (i, line)) + F.write(".%s %s" % (a['uid'], line)) + F.write("=%d %s" % (a['uidNumber'], line)) + i = i + 1 + + # Oops, something unspeakable happened. + except: + Die(File, None, F) + raise + Done(File, None, F) + + # Return the list of users so we know which keys to export + return userlist # Generate the shadow list -def GenShadow(l,File): - F = None; - Fdb = None; - try: - OldMask = os.umask(0077); - F = open(File + ".tmp","w",0600); - Fdb = open(File + ".tdb.tmp","w",0600); - os.umask(OldMask); +def GenShadow(accounts, File): + F = None + try: + OldMask = os.umask(0077) + F = open(File + ".tdb.tmp", "w", 0600) + os.umask(OldMask) + + i = 0 + for a in accounts: + Pass = '*' + if not IsInGroup(a): continue + + # If the account is locked, mark it as such in shadow + # See Debian Bug #308229 for why we set it to 1 instead of 0 + if not a.pw_active(): ShadowExpire = '1' + elif 'shadowExpire' in a: ShadowExpire = str(a['shadowExpire']) + else: ShadowExpire = '' + + values = [] + values.append(a['uid']) + values.append(a.get_password()) + for key in 'shadowLastChange', 'shadowMin', 'shadowMax', 'shadowWarning', 'shadowInactive': + if key in a: values.append(a[key]) + else: values.append('') + values.append(ShadowExpire) + line = ':'.join(values)+':' + line = Sanitize(line) + "\n" + F.write("0%u %s" % (i, line)) + F.write(".%s %s" % (a['uid'], line)) + i = i + 1 + + # Oops, something unspeakable happened. + except: + Die(File, None, F) + raise + Done(File, None, F) + +# Generate the sudo passwd file +def GenShadowSudo(accounts, File, untrusted): + F = None + try: + OldMask = os.umask(0077) + F = open(File + ".tmp", "w", 0600) + os.umask(OldMask) + + for a in accounts: + Pass = '*' + if not IsInGroup(a): continue + + if 'sudoPassword' in a: + for entry in a['sudoPassword']: + Match = re.compile('^('+UUID_FORMAT+') (confirmed:[0-9a-f]{40}|unconfirmed) ([a-z0-9.,*]+) ([^ ]+)$').match(entry) + if Match == None: + continue + uuid = Match.group(1) + status = Match.group(2) + hosts = Match.group(3) + cryptedpass = Match.group(4) + + if status != 'confirmed:'+make_passwd_hmac('password-is-confirmed', 'sudo', a['uid'], uuid, hosts, cryptedpass): + continue + for_all = hosts == "*" + for_this_host = CurrentHost in hosts.split(',') + if not (for_all or for_this_host): + continue + # ignore * passwords for untrusted hosts, but copy host specific passwords + if for_all and untrusted: + continue + Pass = cryptedpass + if for_this_host: # this makes sure we take a per-host entry over the for-all entry + break + if len(Pass) > 50: + Pass = '*' + + Line = "%s:%s" % (a['uid'], Pass) + Line = Sanitize(Line) + "\n" + F.write("%s" % (Line)) + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) +# Generate the shadow list +def GenSSHShadow(accounts): # Fetch all the users - global PasswdAttrs; - if PasswdAttrs == None: - raise "No Users"; - - I = 0; - for x in PasswdAttrs: - if x[1].has_key("uidnumber") == 0 or IsInGroup(x) == 0: - continue; - - Pass = GetAttr(x,"userpassword"); - if Pass[0:7] != "{crypt}" or len(Pass) > 50: - Pass = '*'; - else: - Pass = Pass[7:]; - Line = "%s:%s:%s:%s:%s:%s:%s:%s:" % (GetAttr(x,"uid"),\ - Pass,GetAttr(x,"shadowlastchange"),\ - GetAttr(x,"shadowmin"),GetAttr(x,"shadowmax"),\ - GetAttr(x,"shadowwarning"),GetAttr(x,"shadowinactive"),\ - GetAttr(x,"shadowexpire")); - Line = Sanitize(Line) + "\n"; - F.write(Line); - Fdb.write("0%u %s" % (I,Line)); - Fdb.write(".%s %s" % (GetAttr(x,"uid"),Line)); - I = I + 1; - - # Oops, something unspeakable happened. - except: - Die(File,F,Fdb); - raise; - Done(File,F,Fdb); + userfiles = [] -# Generate the shadow list -def GenSSHShadow(l,File): - F = None; - Fdb = None; - try: - OldMask = os.umask(0077); - F = open(File + ".tmp","w",0600); - Fdb = os.popen("cdbmake %s.cdb %s.cdb.tmp"%(File,File),"w"); - os.umask(OldMask); + safe_rmtree(os.path.join(GlobalDir, 'userkeys')) + safe_makedirs(os.path.join(GlobalDir, 'userkeys')) - # Fetch all the users - global PasswdAttrs; - if PasswdAttrs == None: - raise "No Users"; - - I = 0; - for x in PasswdAttrs: - if x[1].has_key("uidnumber") == 0 or \ - x[1].has_key("sshrsaauthkey") == 0: - continue; - for I in x[1]["sshrsaauthkey"]: - User = GetAttr(x,"uid"); - Line = "%s: %s" %(User,I); - Line = Sanitize(Line) + "\n"; - F.write(Line); - Fdb.write("+%d,%d:%s->%s\n"%(len(User),len(I),User,I)); - Fdb.write("\n"); - # Oops, something unspeakable happened. - except: - Die(File,F,Fdb); - raise; - if Fdb.close() != None: - raise "cdbmake gave an error"; - Done(File,F,None); + for a in accounts: + if not 'sshRSAAuthKey' in a: continue + + F = None + try: + OldMask = os.umask(0077) + File = os.path.join(GlobalDir, 'userkeys', a['uid']) + F = open(File + ".tmp", "w", 0600) + os.umask(OldMask) + + for I in a['sshRSAAuthKey']: + MultipleLine = "%s" % I + MultipleLine = Sanitize(MultipleLine) + "\n" + F.write(MultipleLine) + + Done(File, F, None) + userfiles.append(os.path.basename(File)) + + # Oops, something unspeakable happened. + except IOError: + Die(File, F, None) + # As neither masterFileName nor masterFile are defined at any point + # this will raise a NameError. + Die(masterFileName, masterFile, None) + raise + + return userfiles + +def GenSSHtarballs(userlist, SSHFiles, grouprevmap, target): + OldMask = os.umask(0077) + tf = tarfile.open(name=os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), mode='w:gz') + os.umask(OldMask) + for f in userlist.keys(): + if f not in SSHFiles: + continue + # If we're not exporting their primary group, don't export + # the key and warn + grname = None + if userlist[f] in grouprevmap.keys(): + grname = grouprevmap[userlist[f]] + else: + try: + if int(userlist[f]) <= 100: + # In these cases, look it up in the normal way so we + # deal with cases where, for instance, users are in group + # users as their primary group. + grname = grp.getgrgid(userlist[f])[0] + except Exception, e: + pass + + if grname is None: + 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]) + continue + + to = tf.gettarinfo(os.path.join(GlobalDir, 'userkeys', f), f) + # These will only be used where the username doesn't + # exist on the target system for some reason; hence, + # in those cases, the safest thing is for the file to + # be owned by root but group nobody. This deals with + # the bloody obscure case where the group fails to exist + # whilst the user does (in which case we want to avoid + # ending up with a file which is owned user:root to avoid + # a fairly obvious attack vector) + to.uid = 0 + to.gid = 65534 + # Using the username / groupname fields avoids any need + # to give a shit^W^W^Wcare about the UIDoffset stuff. + to.uname = f + to.gname = grname + to.mode = 0400 + + contents = file(os.path.join(GlobalDir, 'userkeys', f)).read() + lines = [] + for line in contents.splitlines(): + if line.startswith("allowed_hosts=") and ' ' in line: + machines, line = line.split('=', 1)[1].split(' ', 1) + if CurrentHost not in machines.split(','): + continue # skip this key + lines.append(line) + if not lines: + continue # no keys for this host + contents = "\n".join(lines) + "\n" + to.size = len(contents) + tf.addfile(to, StringIO(contents)) + + tf.close() + os.rename(os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), target) + +# add a list of groups to existing groups, +# including all subgroups thereof, recursively. +# basically this proceduces the transitive hull of the groups in +# addgroups. +def addGroups(existingGroups, newGroups, uid): + for group in newGroups: + # if it's a @host, split it and verify it's on the current host. + s = group.split('@', 1) + if len(s) == 2 and s[1] != CurrentHost: + continue + group = s[0] + + # let's see if we handled this group already + if group in existingGroups: + continue + + if not GroupIDMap.has_key(group): + print "Group", group, "does not exist but", uid, "is in it" + continue + + existingGroups.append(group) + + if SubGroupMap.has_key(group): + addGroups(existingGroups, SubGroupMap[group], uid) # Generate the group list -def GenGroup(l,File): - F = None; - Fdb = None; - try: - F = open(File + ".tmp","w"); - Fdb = open(File + ".tdb.tmp","w"); - - # Generate the GroupMap - GroupMap = {}; - for x in GroupIDMap.keys(): - GroupMap[x] = []; - - # Fetch all the users - global PasswdAttrs; - if PasswdAttrs == None: - raise "No Users"; - - # Sort them into a list of groups having a set of users - for x in PasswdAttrs: - if x[1].has_key("uidnumber") == 0 or IsInGroup(x) == 0: - continue; - if x[1].has_key("supplementarygid") == 0: - continue; - - for I in x[1]["supplementarygid"]: - if GroupMap.has_key(I): - GroupMap[I].append(GetAttr(x,"uid")); - else: - print "Group does not exist ",I,"but",GetAttr(x,"uid"),"is in it"; - - # Output the group file. - Counter = 0; - for x in GroupMap.keys(): - if GroupIDMap.has_key(x) == 0: - continue; - Line = "%s:x:%u:" % (x,GroupIDMap[x]); - Comma = ''; - for I in GroupMap[x]: - Line = Line + ("%s%s" % (Comma,I)); - Comma = ','; - Line = Sanitize(Line) + "\n"; - F.write(Line); - Fdb.write("0%u %s" % (Counter,Line)); - Fdb.write(".%s %s" % (x,Line)); - Fdb.write("=%u %s" % (GroupIDMap[x],Line)); - Counter = Counter + 1; - - # Oops, something unspeakable happened. - except: - Die(File,F,Fdb); - raise; - Done(File,F,Fdb); +def GenGroup(accounts, File): + grouprevmap = {} + F = None + try: + F = open(File + ".tdb.tmp", "w") + + # Generate the GroupMap + GroupMap = {} + for x in GroupIDMap.keys(): + GroupMap[x] = [] + GroupHasPrimaryMembers = {} + + # Sort them into a list of groups having a set of users + for a in accounts: + GroupHasPrimaryMembers[ a['gidNumber'] ] = True + if not IsInGroup(a): continue + if not 'supplementaryGid' in a: continue + + supgroups=[] + addGroups(supgroups, a['supplementaryGid'], a['uid']) + for g in supgroups: + GroupMap[g].append(a['uid']) + + # Output the group file. + J = 0 + for x in GroupMap.keys(): + if GroupIDMap.has_key(x) == 0: + continue + + if len(GroupMap[x]) == 0 and GroupIDMap[x] not in GroupHasPrimaryMembers: + continue + + grouprevmap[GroupIDMap[x]] = x + + Line = "%s:x:%u:" % (x, GroupIDMap[x]) + Comma = '' + for I in GroupMap[x]: + Line = Line + ("%s%s" % (Comma, I)) + Comma = ',' + Line = Sanitize(Line) + "\n" + F.write("0%u %s" % (J, Line)) + F.write(".%s %s" % (x, Line)) + F.write("=%u %s" % (GroupIDMap[x], Line)) + J = J + 1 + + # Oops, something unspeakable happened. + except: + Die(File, None, F) + raise + Done(File, None, F) + + return grouprevmap -# Generate the email forwarding list -def GenForward(l,File): - F = None; - Fdb = None; - try: - OldMask = os.umask(0022); - F = open(File + ".tmp","w",0644); - os.umask(OldMask); +def CheckForward(accounts): + for a in accounts: + if not 'emailForward' in a: continue - # Fetch all the users - global PasswdAttrs; - if PasswdAttrs == None: - raise "No Users"; - - # Write out the email address for each user - for x in PasswdAttrs: - if x[1].has_key("emailforward") == 0 or IsInGroup(x) == 0: - continue; - - # Do not allow people to try to buffer overflow busted parsers - if len(GetAttr(x,"emailforward")) > 200: - continue; - # Check the forwarding address - if EmailCheck.match(GetAttr(x,"emailforward")) == None: - continue; - Line = "%s: %s" % (GetAttr(x,"uid"),GetAttr(x,"emailforward")); - Line = Sanitize(Line) + "\n"; - F.write(Line); - - # Oops, something unspeakable happened. - except: - Die(File,F,Fdb); - raise; - Done(File,F,Fdb); - -def GenAllForward(l,File): - Fdb = None; - try: - OldMask = os.umask(0022); - Fdb = os.popen("cdbmake %s %s.tmp"%(File,File),"w"); - os.umask(OldMask); + delete = False - # Fetch all the users - global PasswdAttrs; - if PasswdAttrs == None: - raise "No Users"; - - # Write out the email address for each user - for x in PasswdAttrs: - if x[1].has_key("emailforward") == 0: - continue; - + if not IsInGroup(a): delete = True # Do not allow people to try to buffer overflow busted parsers - Forward = GetAttr(x,"emailforward"); - if len(Forward) > 200: - continue; - + elif len(a['emailForward']) > 200: delete = True # Check the forwarding address - if EmailCheck.match(Forward) == None: - continue; - - User = GetAttr(x,"uid"); - Fdb.write("+%d,%d:%s->%s\n"%(len(User),len(Forward),User,Forward)); - Fdb.write("\n"); - # Oops, something unspeakable happened. - except: - Fdb.close(); - raise; - if Fdb.close() != None: - raise "cdbmake gave an error"; - -# Generate the anon XEarth marker file -def GenMarkers(l,File): - F = None; - Fdb = None; - try: - F = open(File + ".tmp","w"); - Fdb = None; - - # Fetch all the users - global PasswdAttrs; - if PasswdAttrs == None: - raise "No Users"; - - # Write out the position for each user - for x in PasswdAttrs: - if x[1].has_key("latitude") == 0 or x[1].has_key("longitude") == 0: - continue; - try: - Line = "%8s %8s \"\""%(DecDegree(GetAttr(x,"latitude"),1),DecDegree(GetAttr(x,"longitude"),1)); - Line = Sanitize(Line) + "\n"; - F.write(Line); - except: - pass; - - # Oops, something unspeakable happened. - except: - Die(File,F,Fdb); - raise; - Done(File,F,Fdb); - -# Generate the debian-private subscription list -def GenPrivate(l,File): - F = None; - Fdb = None; - try: - F = open(File + ".tmp","w"); - Fdb = None; + elif EmailCheck.match(a['emailForward']) is None: delete = True - # Fetch all the users - global PasswdAttrs; - if PasswdAttrs == None: - raise "No Users"; - - # Write out the position for each user - for x in PasswdAttrs: - if x[1].has_key("privatesub") == 0: - continue; - - # If the account is locked, do not write it - if (string.find(GetAttr(x,"userpassword"),"*LK*") != -1): - continue; - - # If the account has no PGP key, do not write it - if x[1].has_key("keyfingerprint") == 0: - continue; + if delete: + a.delete_mailforward() - # Must be in the Debian group (yuk, hard coded for now) - if GetAttr(x,"gidnumber") != "800": - continue; +# Generate the email forwarding list +def GenForward(accounts, File): + F = None + try: + OldMask = os.umask(0022) + F = open(File + ".tmp", "w", 0644) + os.umask(OldMask) + + for a in accounts: + if not 'emailForward' in a: continue + Line = "%s: %s" % (a['uid'], a['emailForward']) + Line = Sanitize(Line) + "\n" + F.write(Line) + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +def GenCDB(accounts, File, key): + Fdb = None + try: + OldMask = os.umask(0022) + Fdb = os.popen("cdbmake %s %s.tmp"%(File, File), "w") + os.umask(OldMask) + + # Write out the email address for each user + for a in accounts: + if not key in a: continue + value = a[key] + user = a['uid'] + Fdb.write("+%d,%d:%s->%s\n" % (len(user), len(value), user, value)) + + Fdb.write("\n") + # Oops, something unspeakable happened. + except: + Fdb.close() + raise + if Fdb.close() != None: + raise "cdbmake gave an error" + +# Generate the anon XEarth marker file +def GenMarkers(accounts, File): + F = None + try: + F = open(File + ".tmp", "w") + + # Write out the position for each user + for a in accounts: + if not ('latitude' in a and 'longitude' in a): continue + try: + Line = "%8s %8s \"\""%(a.latitude_dec(True), a.longitude_dec(True)) + Line = Sanitize(Line) + "\n" + F.write(Line) + except: + pass + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) - try: - Line = "%s"%(GetAttr(x,"privatesub")); - Line = Sanitize(Line) + "\n"; - F.write(Line); - except: - pass; - - # Oops, something unspeakable happened. - except: - Die(File,F,Fdb); - raise; - Done(File,F,Fdb); +# Generate the debian-private subscription list +def GenPrivate(accounts, File): + F = None + try: + F = open(File + ".tmp", "w") + + # Write out the position for each user + for a in accounts: + if not a.is_active_user(): continue + if not 'privateSub' in a: continue + try: + Line = "%s"%(a['privateSub']) + Line = Sanitize(Line) + "\n" + F.write(Line) + except: + pass + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +# Generate a list of locked accounts +def GenDisabledAccounts(accounts, File): + F = None + try: + F = open(File + ".tmp", "w") + disabled_accounts = [] + + # Fetch all the users + for a in accounts: + if a.pw_active(): continue + Line = "%s:%s" % (a['uid'], "Account is locked") + disabled_accounts.append(a) + F.write(Sanitize(Line) + "\n") + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + return disabled_accounts + +# Generate the list of local addresses that refuse all mail +def GenMailDisable(accounts, File): + F = None + try: + F = open(File + ".tmp", "w") + + for a in accounts: + if not 'mailDisableMessage' in a: continue + Line = "%s: %s"%(a['uid'], a['mailDisableMessage']) + Line = Sanitize(Line) + "\n" + F.write(Line) + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +# Generate a list of uids that should have boolean affects applied +def GenMailBool(accounts, File, key): + F = None + try: + F = open(File + ".tmp", "w") + + for a in accounts: + if not key in a: continue + if not a[key] == 'TRUE': continue + Line = "%s"%(a['uid']) + Line = Sanitize(Line) + "\n" + F.write(Line) + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +# Generate a list of hosts for RBL or whitelist purposes. +def GenMailList(accounts, File, key): + F = None + try: + F = open(File + ".tmp", "w") + + if key == "mailWhitelist": validregex = re.compile('^[-\w.]+(/[\d]+)?$') + else: validregex = re.compile('^[-\w.]+$') + + for a in accounts: + if not key in a: continue + + filtered = filter(lambda z: validregex.match(z), a[key]) + if len(filtered) == 0: continue + if key == "mailRHSBL": filtered = map(lambda z: z+"/$sender_address_domain", filtered) + line = a['uid'] + ': ' + ' : '.join(filtered) + line = Sanitize(line) + "\n" + F.write(line) + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +def isRoleAccount(account): + return 'debianRoleAccount' in account['objectClass'] # Generate the DNS Zone file -def GenDNS(l,File,BSMTPFile,HomePrefix): - F = None; - FB = None; - try: - F = open(File + ".tmp","w"); - FB = open(BSMTPFile + ".tmp","w"); - - # Fetch all the users - global PasswdAttrs; - if PasswdAttrs == None: - raise "No Users"; - - # Write out the zone file entry for each user - for x in PasswdAttrs: - if x[1].has_key("dnszoneentry") == 0: - continue; - try: - F.write("; %s\n"%(EmailAddress(x))); - for z in x[1]["dnszoneentry"]: - Split = string.split(string.lower(z)); - if string.lower(Split[1]) == 'in': - for y in range(0,len(Split)): - if Split[y] == "$": - Split[y] = "\n\t"; - Line = string.join(Split," ") + "\n"; - F.write(Line); - - Host = Split[0] + DNSZone; - if BSMTPCheck.match(Line) != None: - F.write("; Has BSMTP\n"); - FB.write("%s: user=%s group=Debian file=%s%s/bsmtp/%s\n"%(Host, - GetAttr(x,"uid"),HomePrefix,GetAttr(x,"uid"),Host)); - - # Write some identification information - if string.lower(Split[2]) == "a": - Line = "%s IN TXT \"%s\"\n"%(Split[0],EmailAddress(x)); - for y in x[1]["keyfingerprint"]: - Line = Line + "%s IN TXT \"PGP %s\"\n"%(Split[0],FormatPGPKey(y)); - F.write(Line); - else: - Line = "; Err %s"%(str(Split)); - F.write(Line); - - F.write("\n"); - except: - F.write("; Errors\n"); - pass; - - # Oops, something unspeakable happened. - except: - Die(File,F,None); - Die(BSMTPFile,FB,None); - raise; - Done(File,F,None); - Done(BSMTPFile,FB,None); - -# Generate the shadow list -def GenSSHKnown(l,File): - F = None; - Fdb = None; - try: - OldMask = os.umask(0022); - F = open(File + ".tmp","w",0644); - Fdb = os.popen("cdbmake %s.cdb %s.cdb.tmp"%(File,File),"w"); - os.umask(OldMask); - - # Fetch all the hosts - HostKeys = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"sshrsahostkey=*",\ - ["hostname","sshrsahostkey"]); - - if HostKeys == None: - raise "No Hosts"; - - I = 0; - for x in HostKeys: - if x[1].has_key("hostname") == 0 or \ - x[1].has_key("sshrsahostkey") == 0: - continue; - Host = GetAttr(x,"hostname"); - SHost = string.find(Host,"."); - for I in x[1]["sshrsahostkey"]: - if SHost == None: - Line = "%s,%s %s" %(Host,gethostbyname(Host),I); +def GenDNS(accounts, File): + F = None + try: + F = open(File + ".tmp", "w") + + # Fetch all the users + RRs = {} + + # Write out the zone file entry for each user + for a in accounts: + if not 'dnsZoneEntry' in a: continue + if not a.is_active_user() and not isRoleAccount(a): continue + + try: + F.write("; %s\n"%(a.email_address())) + for z in a["dnsZoneEntry"]: + Split = z.lower().split() + if Split[1].lower() == 'in': + for y in range(0, len(Split)): + if Split[y] == "$": + Split[y] = "\n\t" + Line = " ".join(Split) + "\n" + F.write(Line) + + Host = Split[0] + DNSZone + if BSMTPCheck.match(Line) != None: + F.write("; Has BSMTP\n") + + # Write some identification information + if not RRs.has_key(Host): + if Split[2].lower() in ["a", "aaaa"]: + Line = "%s IN TXT \"%s\"\n"%(Split[0], a.email_address()) + for y in a["keyFingerPrint"]: + Line = Line + "%s IN TXT \"PGP %s\"\n"%(Split[0], FormatPGPKey(y)) + F.write(Line) + RRs[Host] = 1 + else: + Line = "; Err %s"%(str(Split)) + F.write(Line) + + F.write("\n") + except Exception, e: + F.write("; Errors:\n") + for line in str(e).split("\n"): + F.write("; %s\n"%(line)) + pass + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +def ExtractDNSInfo(x): + + TTLprefix="\t" + if 'dnsTTL' in x[1]: + TTLprefix="%s\t"%(x[1]["dnsTTL"][0]) + + DNSInfo = [] + if x[1].has_key("ipHostNumber"): + for I in x[1]["ipHostNumber"]: + if IsV6Addr.match(I) != None: + DNSInfo.append("%sIN\tAAAA\t%s" % (TTLprefix, I)) else: - Line = "%s,%s,%s %s" %(Host,Host[0:SHost],gethostbyname(Host),I); - Line = Sanitize(Line) + "\n"; - F.write(Line); - Fdb.write("+%d,%d:%s->%s\n"%(len(Host),len(I),Host,I)); - Fdb.write("\n"); - # Oops, something unspeakable happened. - except: - Die(File,F,Fdb); - raise; - if Fdb.close() != None: - raise "cdbmake gave an error"; - Done(File,F,None); - + DNSInfo.append("%sIN\tA\t%s" % (TTLprefix, I)) + + Algorithm = None + + if 'sshRSAHostKey' in x[1]: + for I in x[1]["sshRSAHostKey"]: + Split = I.split() + if Split[0] == 'ssh-rsa': + Algorithm = 1 + if Split[0] == 'ssh-dss': + Algorithm = 2 + if Algorithm == None: + continue + Fingerprint = sha.new(base64.decodestring(Split[1])).hexdigest() + DNSInfo.append("%sIN\tSSHFP\t%u 1 %s" % (TTLprefix, Algorithm, Fingerprint)) + + if 'architecture' in x[1]: + Arch = GetAttr(x, "architecture") + Mach = "" + if x[1].has_key("machine"): + Mach = " " + GetAttr(x, "machine") + DNSInfo.append("%sIN\tHINFO\t\"%s%s\" \"%s\"" % (TTLprefix, Arch, Mach, "Debian GNU/Linux")) + + if x[1].has_key("mXRecord"): + for I in x[1]["mXRecord"]: + DNSInfo.append("%sIN\tMX\t%s" % (TTLprefix, I)) + + return DNSInfo + +# Generate the DNS records +def GenZoneRecords(File): + F = None + try: + F = open(File + ".tmp", "w") + + # Fetch all the hosts + global HostAttrs + + for x in HostAttrs: + if x[1].has_key("hostname") == 0: + continue + + if IsDebianHost.match(GetAttr(x, "hostname")) is None: + continue + + DNSInfo = ExtractDNSInfo(x) + start = True + for Line in DNSInfo: + if start == True: + Line = "%s.\t%s" % (GetAttr(x, "hostname"), Line) + start = False + else: + Line = "\t\t\t%s" % (Line) + + F.write(Line + "\n") + + # this would write sshfp lines for services on machines + # but we can't yet, since some are cnames and we'll make + # an invalid zonefile + # + # for i in x[1].get("purpose", []): + # m = PurposeHostField.match(i) + # if m: + # m = m.group(1) + # # we ignore [[*..]] entries + # if m.startswith('*'): + # continue + # if m.startswith('-'): + # m = m[1:] + # if m: + # if not m.endswith(HostDomain): + # continue + # if not m.endswith('.'): + # m = m + "." + # for Line in DNSInfo: + # if isSSHFP.match(Line): + # Line = "%s\t%s" % (m, Line) + # F.write(Line + "\n") + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +# Generate the BSMTP file +def GenBSMTP(accounts, File, HomePrefix): + F = None + try: + F = open(File + ".tmp", "w") + + # Write out the zone file entry for each user + for a in accounts: + if not 'dnsZoneEntry' in a: continue + if not a.is_active_user(): continue + + try: + for z in a["dnsZoneEntry"]: + Split = z.lower().split() + if Split[1].lower() == 'in': + for y in range(0, len(Split)): + if Split[y] == "$": + Split[y] = "\n\t" + Line = " ".join(Split) + "\n" + + Host = Split[0] + DNSZone + if BSMTPCheck.match(Line) != None: + F.write("%s: user=%s group=Debian file=%s%s/bsmtp/%s\n"%(Host, + a['uid'], HomePrefix, a['uid'], Host)) + + except: + F.write("; Errors\n") + pass + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +def HostToIP(Host, mapped=True): + + IPAdresses = [] + + if Host[1].has_key("ipHostNumber"): + for addr in Host[1]["ipHostNumber"]: + IPAdresses.append(addr) + if IsV6Addr.match(addr) is None and mapped == "True": + IPAdresses.append("::ffff:"+addr) + + return IPAdresses + +# Generate the ssh known hosts file +def GenSSHKnown(File, mode=None): + F = None + try: + OldMask = os.umask(0022) + F = open(File + ".tmp", "w", 0644) + os.umask(OldMask) + + global HostAttrs + + for x in HostAttrs: + if x[1].has_key("hostname") == 0 or \ + x[1].has_key("sshRSAHostKey") == 0: + continue + Host = GetAttr(x, "hostname") + HostNames = [ Host ] + if Host.endswith(HostDomain): + HostNames.append(Host[:-(len(HostDomain) + 1)]) + + # in the purpose field [[host|some other text]] (where some other text is optional) + # makes a hyperlink on the web thing. we now also add these hosts to the ssh known_hosts + # file. But so that we don't have to add everything we link we can add an asterisk + # and say [[*... to ignore it. In order to be able to add stuff to ssh without + # http linking it we also support [[-hostname]] entries. + for i in x[1].get("purpose", []): + m = PurposeHostField.match(i) + if m: + m = m.group(1) + # we ignore [[*..]] entries + if m.startswith('*'): + continue + if m.startswith('-'): + m = m[1:] + if m: + HostNames.append(m) + if m.endswith(HostDomain): + HostNames.append(m[:-(len(HostDomain) + 1)]) + + for I in x[1]["sshRSAHostKey"]: + if mode and mode == 'authorized_keys': + hosts = HostToIP(x) + if 'sshdistAuthKeysHost' in x[1]: + hosts += x[1]['sshdistAuthKeysHost'] + 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) + else: + Line = "%s %s" %(",".join(HostNames + HostToIP(x, False)), I) + Line = Sanitize(Line) + "\n" + F.write(Line) + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +# Generate the debianhosts file (list of all IP addresses) +def GenHosts(File): + F = None + try: + OldMask = os.umask(0022) + F = open(File + ".tmp", "w", 0644) + os.umask(OldMask) + + seen = set() + + global HostAttrs + + for x in HostAttrs: + + if IsDebianHost.match(GetAttr(x, "hostname")) is None: + continue + + if not 'ipHostNumber' in x[1]: + continue + + addrs = x[1]["ipHostNumber"] + for addr in addrs: + if addr not in seen: + seen.add(addr) + addr = Sanitize(addr) + "\n" + F.write(addr) + + # Oops, something unspeakable happened. + except: + Die(File, F, None) + raise + Done(File, F, None) + +def GenKeyrings(OutDir): + for k in Keyrings: + shutil.copy(k, OutDir) # Connect to the ldap server -l = ldap.open(LDAPServer); -F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r"); -Pass = string.split(string.strip(F.readline())," "); -F.close(); -l.simple_bind_s("uid="+Pass[0]+","+BaseDn,Pass[1]); +l = connectLDAP() +# for testing purposes it's sometimes useful to pass username/password +# via the environment +if 'UD_CREDENTIALS' in os.environ: + Pass = os.environ['UD_CREDENTIALS'].split() +else: + F = open(PassDir + "/pass-" + pwd.getpwuid(os.getuid())[0], "r") + Pass = F.readline().strip().split(" ") + F.close() +l.simple_bind_s("uid=" + Pass[0] + "," + BaseDn, Pass[1]) # Fetch all the groups -GroupIDMap = {}; -Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"gid=*",\ - ["gid","gidnumber"]); +GroupIDMap = {} +Attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "gid=*",\ + ["gid", "gidNumber", "subGroup"]) -# Generate the GroupMap and GroupIDMap +# Generate the SubGroupMap and GroupIDMap for x in Attrs: - if x[1].has_key("gidnumber") == 0: - continue; - GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidnumber"][0]); + if x[1].has_key("accountStatus") and x[1]['accountStatus'] == "disabled": + continue + if x[1].has_key("gidNumber") == 0: + continue + GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidNumber"][0]) + if x[1].has_key("subGroup") != 0: + SubGroupMap.setdefault(x[1]["gid"][0], []).extend(x[1]["subGroup"]) # Fetch all the users -PasswdAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=*",\ - ["uid","uidnumber","gidnumber","supplementarygid",\ - "gecos","loginshell","userpassword","shadowlastchange",\ - "shadowmin","shadowmax","shadowwarning","shadowinactive", - "shadowexpire","emailforward","latitude","longitude",\ - "allowedhosts","sshrsaauthkey","dnszoneentry","cn","sn",\ - "keyfingerprint","privatesub"]); - -# Open the control file -if len(sys.argv) == 1: - F = open(GenerateConf,"r"); -else: - F = open(sys.argv[1],"r") +passwd_attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "(&(uid=*)(!(uidNumber=0)))",\ + ["uid", "uidNumber", "gidNumber", "supplementaryGid",\ + "gecos", "loginShell", "userPassword", "shadowLastChange",\ + "shadowMin", "shadowMax", "shadowWarning", "shadowInactive", + "shadowExpire", "emailForward", "latitude", "longitude",\ + "allowedHost", "sshRSAAuthKey", "dnsZoneEntry", "cn", "sn",\ + "keyFingerPrint", "privateSub", "mailDisableMessage",\ + "mailGreylisting", "mailCallout", "mailRBL", "mailRHSBL",\ + "mailWhitelist", "sudoPassword", "objectClass", "accountStatus",\ + "mailContentInspectionAction"]) + +if passwd_attrs is None: + raise UDEmptyList, "No Users" +accounts = map(lambda x: UDLdap.Account(x[0], x[1]), passwd_attrs) +accounts.sort(lambda x,y: cmp(x['uid'].lower(), y['uid'].lower())) + +# Fetch all the hosts +HostAttrs = l.search_s(HostBaseDn, ldap.SCOPE_ONELEVEL, "objectClass=debianServer",\ + ["hostname", "sshRSAHostKey", "purpose", "allowedGroups", "exportOptions",\ + "mXRecord", "ipHostNumber", "dnsTTL", "machine", "architecture"]) + +if HostAttrs == None: + raise UDEmptyList, "No Hosts" + +HostAttrs.sort(lambda x, y: cmp((GetAttr(x, "hostname")).lower(), (GetAttr(y, "hostname")).lower())) + +# override globaldir for testing +if 'UD_GENERATEDIR' in os.environ: + GenerateDir = os.environ['UD_GENERATEDIR'] # Generate global things -GlobalDir = GenerateDir+"/"; -GenSSHShadow(l,GlobalDir+"ssh-rsa-shadow"); -GenAllForward(l,GlobalDir+"mail-forward.cdb"); -GenMarkers(l,GlobalDir+"markers"); -GenPrivate(l,GlobalDir+"debian-private"); -GenSSHKnown(l,GlobalDir+"ssh_known_hosts"); +GlobalDir = GenerateDir + "/" +accounts_disabled = GenDisabledAccounts(accounts, GlobalDir + "disabled-accounts") + +accounts = filter(lambda x: not IsRetired(x), accounts) +#accounts_DDs = filter(lambda x: IsGidDebian(x), accounts) + +CheckForward(accounts) + +GenMailDisable(accounts, GlobalDir + "mail-disable") +GenCDB(accounts, GlobalDir + "mail-forward.cdb", 'emailForward') +GenCDB(accounts, GlobalDir + "mail-contentinspectionaction.cdb", 'mailContentInspectionAction') +GenPrivate(accounts, GlobalDir + "debian-private") +GenSSHKnown(GlobalDir+"authorized_keys", 'authorized_keys') +GenMailBool(accounts, GlobalDir + "mail-greylist", "mailGreylisting") +GenMailBool(accounts, GlobalDir + "mail-callout", "mailCallout") +GenMailList(accounts, GlobalDir + "mail-rbl", "mailRBL") +GenMailList(accounts, GlobalDir + "mail-rhsbl", "mailRHSBL") +GenMailList(accounts, GlobalDir + "mail-whitelist", "mailWhitelist") +GenKeyrings(GlobalDir) # Compatibility. -GenForward(l,GlobalDir+"forward-alias"); - -while(1): - Line = F.readline(); - if Line == "": - break; - Line = string.strip(Line); - if Line == "": - continue; - if Line[0] == '#': - continue; - - Split = string.split(Line," "); - OutDir = GenerateDir + '/' + Split[0] + '/'; - try: os.mkdir(OutDir); - except: pass; +GenForward(accounts, GlobalDir + "forward-alias") + +accounts = filter(lambda a: not a in accounts_disabled, accounts) + +SSHFiles = GenSSHShadow(accounts) +GenMarkers(accounts, GlobalDir + "markers") +GenSSHKnown(GlobalDir + "ssh_known_hosts") +GenHosts(GlobalDir + "debianhosts") + +for host in HostAttrs: + if not "hostname" in host[1]: + continue + + CurrentHost = host[1]['hostname'][0] + OutDir = GenerateDir + '/' + CurrentHost + '/' + try: + os.mkdir(OutDir) + except: + pass # Get the group list and convert any named groups to numerics - GroupList = {}; - ExtraList = {}; - for I in Split[2:]: - if I[0] == '[': - ExtraList[I] = None; - continue; - GroupList[I] = None; - if GroupIDMap.has_key(I): - GroupList[str(GroupIDMap[I])] = None; - - global Allowed,CurrentHost; - Allowed = GroupList; - CurrentHost = Split[0]; - - sys.stdout.flush(); - GenPasswd(l,OutDir+"passwd",Split[1]); - sys.stdout.flush(); - GenGroup(l,OutDir+"group"); - GenShadow(l,OutDir+"shadow"); - - # Link in global things - DoLink(GlobalDir,OutDir,"ssh-rsa-shadow"); - DoLink(GlobalDir,OutDir,"ssh-rsa-shadow.cdb"); - DoLink(GlobalDir,OutDir,"markers"); - DoLink(GlobalDir,OutDir,"mail-forward.cdb"); - DoLink(GlobalDir,OutDir,"ssh_known_hosts"); + GroupList = {} + for groupname in AllowedGroupsPreload.strip().split(" "): + GroupList[groupname] = True + if 'allowedGroups' in host[1]: + for groupname in host[1]['allowedGroups']: + GroupList[groupname] = True + for groupname in GroupList.keys(): + if groupname in GroupIDMap: + GroupList[str(GroupIDMap[groupname])] = True + + ExtraList = {} + if 'exportOptions' in host[1]: + for extra in host[1]['exportOptions']: + ExtraList[extra.upper()] = True + + Allowed = GroupList + if Allowed == {}: + Allowed = None + + DoLink(GlobalDir, OutDir, "debianhosts") + DoLink(GlobalDir, OutDir, "ssh_known_hosts") + DoLink(GlobalDir, OutDir, "disabled-accounts") + + sys.stdout.flush() + if 'NOPASSWD' in ExtraList: + userlist = GenPasswd(accounts, OutDir + "passwd", HomePrefix, "*") + else: + userlist = GenPasswd(accounts, OutDir + "passwd", HomePrefix, "x") + sys.stdout.flush() + grouprevmap = GenGroup(accounts, OutDir + "group") + GenShadowSudo(accounts, OutDir + "sudo-passwd", ('UNTRUSTED' in ExtraList) or ('NOPASSWD' in ExtraList)) + + # Now we know who we're allowing on the machine, export + # the relevant ssh keys + GenSSHtarballs(userlist, SSHFiles, grouprevmap, os.path.join(OutDir, 'ssh-keys.tar.gz')) + + if not 'NOPASSWD' in ExtraList: + GenShadow(accounts, OutDir + "shadow") + + # Link in global things + if not 'NOMARKERS' in ExtraList: + DoLink(GlobalDir, OutDir, "markers") + DoLink(GlobalDir, OutDir, "mail-forward.cdb") + DoLink(GlobalDir, OutDir, "mail-contentinspectionaction.cdb") + DoLink(GlobalDir, OutDir, "mail-disable") + DoLink(GlobalDir, OutDir, "mail-greylist") + DoLink(GlobalDir, OutDir, "mail-callout") + DoLink(GlobalDir, OutDir, "mail-rbl") + DoLink(GlobalDir, OutDir, "mail-rhsbl") + DoLink(GlobalDir, OutDir, "mail-whitelist") + GenCDB(filter(lambda x: IsInGroup(x), accounts), OutDir + "user-forward.cdb", 'emailForward') + GenCDB(filter(lambda x: IsInGroup(x), accounts), OutDir + "batv-tokens.cdb", 'bATVToken') + GenCDB(filter(lambda x: IsInGroup(x), accounts), OutDir + "default-mail-options.cdb", 'mailDefaultOptions') # Compatibility. - DoLink(GlobalDir,OutDir,"forward-alias"); - - if ExtraList.has_key("[DNS]"): - GenDNS(l,OutDir+"dns-zone",OutDir+"bsmtp",Split[1]); - - if ExtraList.has_key("[PRIVATE]"): - DoLink(GlobalDir,OutDir,"debian-private"); + DoLink(GlobalDir, OutDir, "forward-alias") + + if 'DNS' in ExtraList: + GenDNS(accounts, OutDir + "dns-zone") + GenZoneRecords(OutDir + "dns-sshfp") + + if 'AUTHKEYS' in ExtraList: + DoLink(GlobalDir, OutDir, "authorized_keys") + + if 'BSMTP' in ExtraList: + GenBSMTP(accounts, OutDir + "bsmtp", HomePrefix) + + if 'PRIVATE' in ExtraList: + DoLink(GlobalDir, OutDir, "debian-private") + + if 'KEYRING' in ExtraList: + for k in Keyrings: + DoLink(GlobalDir, OutDir, os.path.basename(k)) + else: + for k in Keyrings: + try: + posix.remove(OutDir + os.path.basename(k)) + except: + pass + +# vim:set et: +# vim:set ts=3: +# vim:set shiftwidth=3: