Catch the case where attributes that are not declared as an array value have more...
[mirror/userdir-ldap.git] / ud-generate
index 2836f5c..303f9f5 100755 (executable)
@@ -6,12 +6,13 @@
 #   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 Peter Palfrader <peter@palfrader.org>
+#   Copyright (c) 2008,2009,2010 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>
 #   Copyright (c) 2008 Thomas Viehmann <tv@beamnet.de>
 #   Copyright (c) 2009 Stephen Gran <steve@lobefin.net>
+#   Copyright (c) 2010 Helmut Grohne <helmut@subdivi.de>
 #
 #   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
 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)
+
 PasswdAttrs = None
 DebianUsers = None
 DisabledUsers = []
@@ -49,6 +59,7 @@ 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(":")
 
@@ -338,6 +349,8 @@ def GenSSHShadow():
       # 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
 
@@ -385,7 +398,20 @@ def GenSSHtarballs(userlist, SSHFiles, grouprevmap, target):
       to.uname = f
       to.gname = grname
       to.mode  = 0400
-      tf.addfile(to, file(os.path.join(GlobalDir, 'userkeys', f)))
+
+      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)
@@ -555,10 +581,11 @@ def GenMarkers(File):
      
       # 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:
+         a = UDLdap.Account(x[0], x[1])
+         if not ('latitude' in a and 'longitude' in a):
             continue
          try:
-            Line = "%8s %8s \"\""%(DecDegree(GetAttr(x, "latitude"), 1), DecDegree(GetAttr(x, "longitude"), 1))
+            Line = "%8s %8s \"\""%(a.latitude_dec(True), a.longitude_dec(True))
             Line = Sanitize(Line) + "\n"
             F.write(Line)
          except:
@@ -758,29 +785,9 @@ def GenDNS(File):
    try:
       F = open(File + ".tmp", "w")
      
-#      global HostAttrs
-#
-#      for x in HostAttrs:
-#         if x[1].has_key("hostname") == 0 or \
-#            x[1].has_key("architecture") == 0 or\
-#            x[1].has_key("sshRSAHostKey") == 0:
-#            continue
-#
-#         if IsDebianHost.match(GetAttr(x, "hostname")) is not 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")
-
       # Fetch all the users
       global PasswdAttrs
+      RRs = {}
      
       # Write out the zone file entry for each user
       for x in PasswdAttrs:
@@ -806,11 +813,13 @@ def GenDNS(File):
                      F.write("; Has BSMTP\n")
      
                   # Write some identification information
-                  if Split[2].lower() == "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)
+                  if not RRs.has_key(Host):
+                     if Split[2].lower() in ["a", "aaaa"]:
+                        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)
+                        RRs[Host] = 1
                else:
                   Line = "; Err %s"%(str(Split))
                   F.write(Line)
@@ -894,6 +903,29 @@ def GenZoneRecords(File):
 
             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)
@@ -997,7 +1029,6 @@ def GenSSHKnown(File, mode=None):
                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)
-               #Line = 'command="rsync --server --sender -pr . /var/cache/userdir-ldap/hosts/%s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding %s' % (Host,I)
             else:
                Line = "%s %s" %(",".join(HostNames + HostToIP(x, False)), I)
             Line = Sanitize(Line) + "\n"
@@ -1047,9 +1078,14 @@ def GenKeyrings(OutDir):
 
 # Connect to the ldap server
 l = connectLDAP()
-F = open(PassDir + "/pass-" + pwd.getpwuid(os.getuid())[0], "r")
-Pass = F.readline().strip().split(" ")
-F.close()
+# 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
@@ -1094,6 +1130,10 @@ if HostAttrs == None:
 
 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 + "/"
 GenDisabledAccounts(GlobalDir + "disabled-accounts")