# Copyright (c) 2003-2004 James Troup <troup@debian.org>
# Copyright (c) 2004-2005,7 Joey Schulze <joey@infodrom.org>
# Copyright (c) 2001-2007 Ryan Murray <rmurray@debian.org>
-# Copyright (c) 2008,2009,2010 Peter Palfrader <peter@palfrader.org>
+# Copyright (c) 2008,2009,2010,2011 Peter Palfrader <peter@palfrader.org>
# Copyright (c) 2008 Andreas Barth <aba@not.so.argh.org>
# Copyright (c) 2008 Mark Hymers <mhy@debian.org>
# Copyright (c) 2008 Luk Claes <luk@debian.org>
# 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
+import string, re, time, ldap, optparse, sys, os, pwd, posix, socket, base64, hashlib, shutil, errno, tarfile, grp
+import lockfile
from userdir_ldap import *
from userdir_exceptions import *
import UDLdap
isSSHFP = re.compile("^\s*IN\s+SSHFP")
DNSZone = ".debian.net"
Keyrings = ConfModule.sync_keyrings.split(":")
+GitoliteSSHRestrictions = getattr(ConfModule, "gitolitesshrestrictions", None)
+
def safe_makedirs(dir):
try:
else:
raise e
+def get_lock(fn, wait=5*60, max_age=3600*6):
+ try:
+ stat = os.stat(fn + '.lock')
+ if stat.st_mtime < time.time() - max_age:
+ sys.stderr.write("Removing stale lock %s"%(fn + '.lock'))
+ os.unlink(fn + '.lock')
+ except OSError, error:
+ if error.errno == errno.ENOENT:
+ pass
+ else:
+ raise
+
+ lock = lockfile.FileLock(fn)
+ try:
+ lock.acquire(timeout=wait)
+ except lockfile.LockTimeout:
+ return None
+
+ return lock
+
+
def Sanitize(Str):
return Str.translate(string.maketrans("\n\r\t", "$$$"))
raise
Done(File, F, None)
+# Generate the sudo passwd file
+def GenSSHGitolite(accounts, File):
+ F = None
+ try:
+ OldMask = os.umask(0022)
+ F = open(File + ".tmp", "w", 0600)
+ os.umask(OldMask)
+
+ if not GitoliteSSHRestrictions is None and GitoliteSSHRestrictions != "":
+ for a in accounts:
+ if not 'sshRSAAuthKey' in a: continue
+
+ User = a['uid']
+ prefix = GitoliteSSHRestrictions.replace('@@USER@@', User)
+ for I in a["sshRSAAuthKey"]:
+ if I.startswith('ssh-'):
+ line = "%s %s"%(prefix, I)
+ else:
+ line = "%s,%s"%(prefix, I)
+ line = Sanitize(line) + "\n"
+ F.write(line)
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File, F, None)
+ raise
+ Done(File, F, None)
+
# Generate the shadow list
def GenSSHShadow(global_dir, accounts):
# Fetch all the users
return userfiles
+# Generate the webPassword list
+def GenWebPassword(accounts, File):
+ F = None
+ try:
+ OldMask = os.umask(0077)
+ F = open(File, "w", 0600)
+ os.umask(OldMask)
+
+ for a in accounts:
+ if not 'webPassword' in a: continue
+ if not a.pw_active(): continue
+
+ Pass = str(a['webPassword'])
+ Line = "%s:%s" % (a['uid'], Pass)
+ Line = Sanitize(Line) + "\n"
+ F.write("%s" % (Line))
+
+ except:
+ Die(File, None, F)
+ raise
+
def GenSSHtarballs(global_dir, userlist, SSHFiles, grouprevmap, target):
OldMask = os.umask(0077)
tf = tarfile.open(name=os.path.join(global_dir, 'ssh-keys-%s.tar.gz' % CurrentHost), mode='w:gz')
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"]:
else:
Line = "; Err %s"%(str(Split))
F.write(Line)
-
+
F.write("\n")
except Exception, e:
F.write("; Errors:\n")
Algorithm = 2
if Algorithm == None:
continue
- Fingerprint = sha.new(base64.decodestring(Split[1])).hexdigest()
+ Fingerprint = hashlib.new('sha1', base64.decodestring(Split[1])).hexdigest()
DNSInfo.append("%sIN\tSSHFP\t%u 1 %s" % (TTLprefix, Algorithm, Fingerprint))
if 'architecture' in x[1]:
raise
Done(File, F, None)
+def replaceTree(src, dst_basedir):
+ bn = os.path.basename(src)
+ dst = os.path.join(dst_basedir, bn)
+ safe_rmtree(dst)
+ shutil.copytree(src, dst)
+
def GenKeyrings(OutDir):
for k in Keyrings:
- shutil.copy(k, OutDir)
+ if os.path.isdir(k):
+ replaceTree(k, OutDir)
+ else:
+ shutil.copy(k, OutDir)
def get_accounts(ldap_conn):
# Fetch all the users
- passwd_attrs = ldap_conn.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "(&(uid=*)(!(uidNumber=0)))",\
+ passwd_attrs = ldap_conn.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "(&(uid=*)(!(uidNumber=0))(objectClass=shadowAccount))",\
["uid", "uidNumber", "gidNumber", "supplementaryGid",\
"gecos", "loginShell", "userPassword", "shadowLastChange",\
"shadowMin", "shadowMax", "shadowWarning", "shadowInactive",
"keyFingerPrint", "privateSub", "mailDisableMessage",\
"mailGreylisting", "mailCallout", "mailRBL", "mailRHSBL",\
"mailWhitelist", "sudoPassword", "objectClass", "accountStatus",\
- "mailContentInspectionAction"])
+ "mailContentInspectionAction", "webPassword"])
if passwd_attrs is None:
raise UDEmptyList, "No Users"
GenMailList(accounts, global_dir + "mail-rbl", "mailRBL")
GenMailList(accounts, global_dir + "mail-rhsbl", "mailRHSBL")
GenMailList(accounts, global_dir + "mail-whitelist", "mailWhitelist")
+ GenWebPassword(accounts, global_dir + "web-passwords")
GenKeyrings(global_dir)
# Compatibility.
GenMarkers(accounts, global_dir + "markers")
GenSSHKnown(host_attrs, global_dir + "ssh_known_hosts")
GenHosts(host_attrs, global_dir + "debianhosts")
+ GenSSHGitolite(accounts, global_dir + "ssh-gitolite")
GenDNS(accounts, global_dir + "dns-zone")
GenZoneRecords(host_attrs, global_dir + "dns-sshfp")
if 'PRIVATE' in ExtraList:
DoLink(global_dir, OutDir, "debian-private")
+ if 'GITOLITE' in ExtraList:
+ DoLink(global_dir, OutDir, "ssh-gitolite")
+
+ if 'WEB-PASSWORDS' in ExtraList:
+ DoLink(global_dir, OutDir, "web-passwords")
+
if 'KEYRING' in ExtraList:
for k in Keyrings:
- DoLink(global_dir, OutDir, os.path.basename(k))
+ bn = os.path.basename(k)
+ if os.path.isdir(k):
+ src = os.path.join(global_dir, bn)
+ replaceTree(src, OutDir)
+ else:
+ DoLink(global_dir, OutDir, bn)
else:
for k in Keyrings:
try:
- posix.remove(OutDir + os.path.basename(k))
+ bn = os.path.basename(k)
+ target = os.path.join(OutDir, bn)
+ if os.path.isdir(target):
+ safe_rmtree(dst)
+ else:
+ posix.remove(target)
except:
pass
+ DoLink(global_dir, OutDir, "last_update.trace")
+
+
+def getLastLDAPChangeTime(l):
+ mods = l.search_s('cn=log',
+ ldap.SCOPE_ONELEVEL,
+ '(&(&(!(reqMod=activity-from*))(!(reqMod=activity-pgp*)))(|(reqType=add)(reqType=delete)(reqType=modify)(reqType=modrdn)))',
+ ['reqEnd'])
+
+ last = 0
+
+ # Sort the list by reqEnd
+ sorted_mods = sorted(mods, key=lambda mod: mod[1]['reqEnd'][0].split('.')[0])
+ # Take the last element in the array
+ last = sorted_mods[-1][1]['reqEnd'][0].split('.')[0]
+
+ return last
+
+def getLastBuildTime():
+ cache_last_mod = 0
+
+ try:
+ fd = open(os.path.join(GenerateDir, "last_update.trace"), "r")
+ cache_last_mod=fd.read().split()
+ try:
+ cache_last_mod = cache_last_mod[0]
+ except IndexError:
+ pass
+ fd.close()
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ pass
+ else:
+ raise e
+
+ return cache_last_mod
+
+
+
+
+parser = optparse.OptionParser()
+parser.add_option("-g", "--generatedir", dest="generatedir", metavar="DIR",
+ help="Output directory.")
+parser.add_option("-f", "--force", dest="force", action="store_true",
+ help="Force generation, even if not update to LDAP has happened.")
+
+(options, args) = parser.parse_args()
+if len(args) > 0:
+ parser.print_help()
+ sys.exit(1)
+
l = make_ldap_conn()
+if options.generatedir is not None:
+ GenerateDir = os.environ['UD_GENERATEDIR']
+elif 'UD_GENERATEDIR' in os.environ:
+ GenerateDir = os.environ['UD_GENERATEDIR']
+
+ldap_last_mod = getLastLDAPChangeTime(l)
+cache_last_mod = getLastBuildTime()
+need_update = ldap_last_mod > cache_last_mod
+
+if not options.force and not need_update:
+ fd = open(os.path.join(GenerateDir, "last_update.trace"), "w")
+ fd.write("%s\n%s\n" % (ldap_last_mod, int(time.time())))
+ fd.close()
+ sys.exit(0)
+
# Fetch all the groups
GroupIDMap = {}
attrs = l.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "gid=*",\
if x[1].has_key("subGroup") != 0:
SubGroupMap.setdefault(x[1]["gid"][0], []).extend(x[1]["subGroup"])
-# override globaldir for testing
-if 'UD_GENERATEDIR' in os.environ:
- GenerateDir = os.environ['UD_GENERATEDIR']
+lock = None
+try:
+ lockf = os.path.join(GenerateDir, 'ud-generate.lock')
+ lock = get_lock( lockf )
+ if lock is None:
+ sys.stderr.write("Could not acquire lock %s.\n"%(lockf))
+ sys.exit(1)
+
+ tracefd = open(os.path.join(GenerateDir, "last_update.trace"), "w")
+ generate_all(GenerateDir, l)
+ tracefd.write("%s\n%s\n" % (ldap_last_mod, int(time.time())))
+ tracefd.close()
+
+finally:
+ if lock is not None:
+ lock.release()
-generate_all(GenerateDir, l)
# vim:set et:
# vim:set ts=3: