ud-generate: deal with users without loginShell
[mirror/userdir-ldap.git] / ud-host
diff --git a/ud-host b/ud-host
index c3bf07d..79a9bd1 100755 (executable)
--- a/ud-host
+++ b/ud-host
@@ -4,7 +4,8 @@
 #   Copyright (c) 2000-2001  Jason Gunthorpe <jgg@debian.org>
 #   Copyright (c) 2001       Ryan Murray <rmurray@debian.org>
 #   Copyright (c) 2003       James Troup <troup@debian.org>
-#   Copyright (c) 2004       Joey Schulze <joey@infodrom.org>
+#   Copyright (c) 2004-2005  Joey Schulze <joey@infodrom.org>
+#   Copyright (c) 2008,2009  Peter Palfrader <peter@palfrader.org>
 #
 #   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
 #    -a    Set the authentication user (the user whose password you are
 #          going to enter)
 #    -h    Set the host to display
+#    -l    list all hosts and their status
+#    -f    list all SSH fingerprints
 
-import string, time, os, pwd, sys, getopt, ldap, crypt, whrandom, readline, copy;
-from userdir_ldap import *;
+import time, os, pwd, sys, getopt, ldap, crypt, readline, copy, subprocess
+from os import O_CREAT, O_EXCL, O_WRONLY
+from userdir_ldap import *
 
-RootMode = 0;
+RootMode = 0
 AttrInfo = {"description": ["Machine Descr.", 1],
             "hostname": ["Host names", 2],
             "status": ["Status", 3],
@@ -46,8 +50,17 @@ AttrInfo = {"description": ["Machine Descr.", 1],
             "machine": ["Machine Hardware", 10],
             "memory": ["Memory", 11],
             "disk": ["Disk", 12],
+            "physicalHost": ["Physical Host", 13],
             "sshRSAHostKey": ["SSH Host Keys", 14],
-            "bandwidth": ["Bandwidth", 15]};
+            "bandwidth": ["Bandwidth", 15],
+            "purpose": ["Purposes", 16],
+            "allowedGroups": ["Groups", 17],
+            "exportOptions": ["Export-Opts", 18],
+            "ipHostNumber": ["IP Address", 19],
+            "mXRecord": ["MXRecord", 20],
+            "dnsTTL": ["dnsTTL", 21],
+            "sshdistAuthKeysHost": ["extra authkeys ip", 22],
+            }
 
 AttrPrompt = {"description": ["Purpose of the machine"],
               "hostname": ["The hostnames for the box (ipv4/ipv6)"],
@@ -61,35 +74,44 @@ AttrPrompt = {"description": ["Purpose of the machine"],
               "machine": ["Hardware description"],
               "memory": ["Installed RAM"],
               "disk": ["Disk Space, RAID levels, etc"],
-             "sshRSAHostKey": ["A copy of /etc/ssh/ssh_*host_key.pub"],
-              "bandwidth": ["Available outbound"]};
+              "physicalHost": ["The box hosting this virtual server"],
+              "sshRSAHostKey": ["A copy of /etc/ssh/ssh_*host_key.pub"],
+              "bandwidth": ["Available outbound"],
+              "purpose": ["The purposes of this host"],
+              "allowedGroups": ["allowed Groups on this host"],
+              "exportOptions": ["additional export options"],
+              "ipHostNumber": ["IP Addresses(es) of the machine"],
+              "mXRecord": ["Mail Exchanger for this machine"],
+              "dnsTTL": ["dns TTL value"],
+              "sshdistAuthKeysHost": ["additional hosts for sshdist's authkeys file"],
+              }
 
 # Create a map of IDs to desc,value,attr
-OrderedIndex = {};
+OrderedIndex = {}
 for at in AttrInfo.keys():
    if (AttrInfo[at][1] != 0):
-      OrderedIndex[AttrInfo[at][1]] = [AttrInfo[at][0], "", at];
-OrigOrderedIndex = copy.deepcopy(OrderedIndex);
+      OrderedIndex[AttrInfo[at][1]] = [AttrInfo[at][0], "", at]
+OrigOrderedIndex = copy.deepcopy(OrderedIndex)
 
 # Print out the automatic time stamp information
 def PrintModTime(Attrs):
-   Stamp = GetAttr(Attrs,"modifyTimestamp","");
+   Stamp = GetAttr(Attrs,"modifyTimestamp","")
    if len(Stamp) >= 13:
       Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
-              int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
+              int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1)
       print "%-24s:" % ("Record last modified on"), time.strftime("%a %d/%m/%Y %X UTC",Time),
-      print "by",ldap.explode_dn(GetAttr(Attrs,"modifiersName"),1)[0];
+      print "by",ldap.explode_dn(GetAttr(Attrs,"modifiersName"),1)[0]
 
-   Stamp = GetAttr(Attrs,"createTimestamp","");
+   Stamp = GetAttr(Attrs,"createTimestamp","")
    if len(Stamp) >= 13:
       Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
-              int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
-      print "%-24s:" % ("Record created on"), time.strftime("%a %d/%m/%Y %X UTC",Time);
+              int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1)
+      print "%-24s:" % ("Record created on"), time.strftime("%a %d/%m/%Y %X UTC",Time)
 
 # Display all of the attributes in a numbered list
 def ShowAttrs(Attrs):
-   print;
-   PrintModTime(Attrs);
+   print
+   PrintModTime(Attrs)
 
    for at in Attrs[1].keys():
       if AttrInfo.has_key(at):
@@ -97,203 +119,268 @@ def ShowAttrs(Attrs):
             print "      %-18s:" % (AttrInfo[at][0]),
            for x in Attrs[1][at]:
               print "'%s'" % (x),
-            print;
+            print
          else:
-            OrderedIndex[AttrInfo[at][1]][1] = Attrs[1][at];
+            OrderedIndex[AttrInfo[at][1]][1] = Attrs[1][at]
 
-   Keys = OrderedIndex.keys();
-   Keys.sort();
+   Keys = OrderedIndex.keys()
+   Keys.sort()
    for at in Keys:
       if at < 100 or RootMode != 0:
          print " %3u) %-18s: " % (at,OrderedIndex[at][0]),
          for x in OrderedIndex[at][1]:
             print "'%s'" % (re.sub('[\n\r]','?',x)),
-         print;
+         print
+
+def Overview(Attrs):
+   """Display a one-line overview for a given host"""
+   for i in ['host','architecture','distribution','access','status']:
+      if i not in Attrs[1].keys():
+         Attrs[1][i] = ['']
+   print "%-12s  %-10s  %-38s  %-25s %s" % (\
+      Attrs[1]['host'][0], \
+      Attrs[1]['architecture'][0], \
+      Attrs[1]['distribution'][0], \
+      Attrs[1]['access'][0], \
+      Attrs[1]['status'][0])
 
 # Change a single attribute
 def ChangeAttr(Attrs,Attr):
-   if (Attr == "sponsor" or Attr == "hostname" or Attr == "sshRSAHostKey"):
-      return MultiChangeAttr(Attrs,Attr);
+   if (Attr in ["sponsor", "sshRSAHostKey", "purpose", "allowedGroups", "exportOptions", "ipHostNumber", "mXRecord", "sshdistAuthKeysHost"]):
+      return MultiChangeAttr(Attrs,Attr)
 
-   print "Old value: '%s'" % (GetAttr(Attrs,Attr,""));
-   print "Press enter to leave unchanged and a single space to set to empty";
-   NewValue = raw_input("New? ");
+   print "Old value: '%s'" % (GetAttr(Attrs,Attr,""))
+   print "Press enter to leave unchanged and a single space to set to empty"
+   NewValue = raw_input("New? ")
 
    # Empty string
    if (NewValue == ""):
-      print "Leaving unchanged.";
-      return;
+      print "Leaving unchanged."
+      return
 
    # Single space designates delete, trap the delete error
    if (NewValue == " "):
-      print "Deleting.",;
+      print "Deleting.",
       try:
-         l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,None)]);
+         l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,None)])
       except ldap.NO_SUCH_ATTRIBUTE:
-         pass;
+         pass
 
-      print;
-      Attrs[1][Attr] = [""];
-      return;
+      print
+      Attrs[1][Attr] = [""]
+      return
 
    # Set a new value
-   print "Setting.",;
-   l.modify_s(HostDn,[(ldap.MOD_REPLACE,Attr,NewValue)]);
-   Attrs[1][Attr] = [NewValue];
-   print;
+   print "Setting.",
+   l.modify_s(HostDn,[(ldap.MOD_REPLACE,Attr,NewValue)])
+   Attrs[1][Attr] = [NewValue]
+   print
 
 def MultiChangeAttr(Attrs,Attr):
    # Make sure that we have an entry
    if not Attrs[1].has_key(Attr):
-      Attrs[1][Attr] = [];
+      Attrs[1][Attr] = []
 
-   Attrs[1][Attr].sort();
-   print "Old values: ",Attrs[1][Attr];
+   Attrs[1][Attr].sort()
+   print "Old values: ",Attrs[1][Attr]
 
-   Mode = string.upper(raw_input("[D]elete or [A]dd? "));
+   Mode = raw_input("[D]elete or [A]dd? ").upper()
    if (Mode != 'D' and Mode != 'A'):
-      return;
+      return
 
-   NewValue = raw_input("Value? ");
+   NewValue = raw_input("Value? ")
    # Empty string
    if (NewValue == ""):
-      print "Leaving unchanged.";
-      return;
+      print "Leaving unchanged."
+      return
 
    # Delete
    if (Mode == "D"):
-      print "Deleting.",;
+      print "Deleting.",
       try:
-         l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,NewValue)]);
+         l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,NewValue)])
       except ldap.NO_SUCH_ATTRIBUTE:
-         print "Failed";
+         print "Failed"
 
-      print;
-      Attrs[1][Attr].remove(NewValue);
-      return;
+      print
+      Attrs[1][Attr].remove(NewValue)
+      return
 
    # Set a new value
-   print "Setting.",;
-   l.modify_s(HostDn,[(ldap.MOD_ADD,Attr,NewValue)]);
-   Attrs[1][Attr].append(NewValue);
-   print;
+   print "Setting.",
+   l.modify_s(HostDn,[(ldap.MOD_ADD,Attr,NewValue)])
+   Attrs[1][Attr].append(NewValue)
+   print
+
 
 # Main program starts here
-User = pwd.getpwuid(os.getuid())[0];
-BindUser = User;
+User = pwd.getpwuid(os.getuid())[0]
+BindUser = User
+ListMode = 0
+FingerPrints = 0
+Host = None
 # Process options
-(options, arguments) = getopt.getopt(sys.argv[1:], "nh:a:r")
+try:
+   (options, arguments) = getopt.getopt(sys.argv[1:], "nh:a:rlf")
+except getopt.GetoptError, data:
+   print data
+   sys.exit(1)
+
 for (switch, val) in options:
    if (switch == '-h'):
-      Host = val;
+      Host = val
    elif (switch == '-a'):
-      BindUser = val;
+      BindUser = val
    elif (switch == '-r'):
-      RootMode = 1;
+      RootMode = 1
    elif (switch == '-n'):
-      BindUser = "";
+      BindUser = ""
+   elif (switch == '-l'):
+      BindUser = ""
+      ListMode = 1
+   elif (switch == '-f'):
+      BindUser = ""
+      FingerPrints = 1
 
 if (BindUser != ""):
-   l = passwdAccessLDAP(LDAPServer, BaseDn, BindUser)
+   l = passwdAccessLDAP(BaseDn, BindUser)
 else:
-   l = ldap.open(LDAPServer);
+   l = connectLDAP()
    l.simple_bind_s("","")
 
-HBaseDn = "ou=hosts,dc=debian,dc=org";
-HostDn = "host=" + Host + "," + HBaseDn;
+if ListMode == 1:
+   Attrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=*")
+   hosts = []
+   for hAttrs in Attrs:
+      hosts.append(hAttrs[1]['host'][0])
+   hosts.sort()
+
+   print "%-12s  %-10s  %-38s  %-25s %s" % ("Host name","Arch","Distribution","Access","Status")
+   print "-"*115
+   for host in hosts:
+      for hAttrs in Attrs:
+         if host == hAttrs[1]['host'][0]:
+            Overview(hAttrs)
+   sys.exit(0)
+elif FingerPrints == 1:
+   if Host is not None:
+      Attrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host)
+   else:
+      Attrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=*")
+   hosts = []
+   for hAttrs in Attrs:
+      hosts.append(hAttrs[1]['host'][0])
+   hosts.sort()
+
+   for host in hosts:
+      for hAttrs in Attrs:
+         if host == hAttrs[1]['host'][0]:
+            if 'sshRSAHostKey' in hAttrs[1].keys():
+               for key in hAttrs[1]['sshRSAHostKey']:
+                  keygen = subprocess.Popen(['/usr/bin/ssh-keygen', '-l', '-f', '-'],
+                                            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+                  keygen.stdin.write(key + '\n')
+                  keygen.stdin.close()
+                  input = keygen.stdout.readline()
+                  keygen.wait()
+                  fingerprint = input.split(' ')
+                  print "%s %s root@%s" % (fingerprint[0], fingerprint[1], host)
+   sys.exit(0)
+
+HostDn = "host=" + Host + "," + HostBaseDn
 
 # Query the server for all of the attributes
-Attrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host);
+Attrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host)
 if len(Attrs) == 0:
-   print "Host",Host,"was not found.";
-   sys.exit(0);
+   print "Host",Host,"was not found."
+   sys.exit(0)
 
 # repeatedly show the account configuration
 while(1):
-   ShowAttrs(Attrs[0]);
+   ShowAttrs(Attrs[0])
    if (BindUser == ""):
-      sys.exit(0);
+      sys.exit(0)
 
    if RootMode == 1:
-      print "   a) Arbitary Change";
-   print "   n) New Host";
-   print "   d) Delete Host";
-   print "   u) Switch Hosts";
-   print "   x) Exit";
+      print "   a) Arbitary Change"
+   print "   n) New Host"
+   print "   d) Delete Host"
+   print "   u) Switch Hosts"
+   print "   x) Exit"
 
    # Prompt
-   Response = raw_input("Change? ");
+   Response = raw_input("Change? ")
    if (Response == "x" or Response == "X" or Response == "q" or
        Response == "quit" or Response == "exit"):
-      break;
+      break
 
    # Change who we are looking at
    if (Response == 'u' or Response == 'U'):
-      NewHost = raw_input("Host? ");
+      NewHost = raw_input("Host? ")
       if NewHost == "":
-         continue;
-      NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
+         continue
+      NAttrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost)
       if len(NAttrs) == 0:
-         print "Host",NewHost,"was not found.";
-         continue;
-      Attrs = NAttrs;
-      Host = NewHost;
-      HostDn = "host=" + Host + "," + HBaseDn;
-      OrderedIndex = copy.deepcopy(OrigOrderedIndex);
-      continue;
+         print "Host",NewHost,"was not found."
+         continue
+      Attrs = NAttrs
+      Host = NewHost
+      HostDn = "host=" + Host + "," + HostBaseDn
+      OrderedIndex = copy.deepcopy(OrigOrderedIndex)
+      continue
 
    # Create a new entry and change to it Change who we are looking at
    if (Response == 'n' or Response == 'N'):
-      NewHost = raw_input("Host? ");
+      NewHost = raw_input("Host? ")
       if NewHost == "":
-         continue;
-      NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
+         continue
+      NAttrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost)
       if len(NAttrs) != 0:
-         print "Host",NewHost,"already exists.";
-         continue;
-      NewHostName = raw_input("Hostname? ");
+         print "Host",NewHost,"already exists."
+         continue
+      NewHostName = raw_input("Hostname? ")
       if NewHost == "":
-         continue;
-      Dn = "host=" + NewHost + "," + HBaseDn;
+         continue
+      Dn = "host=" + NewHost + "," + HostBaseDn
       l.add_s(Dn,[("host", NewHost),
                   ("hostname", NewHostName),
-                  ("objectClass", ("top", "debianServer"))]);
+                  ("objectClass", ("top", "debianServer"))])
 
       # Switch
-      NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
+      NAttrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost)
       if len(NAttrs) == 0:
-         print "Host",NewHost,"was not found.";
-         continue;
-      Attrs = NAttrs;
-      Host = NewHost;
-      HostDn = "host=" + Host + "," + HBaseDn;
-      OrderedIndex = copy.deepcopy(OrigOrderedIndex);
-      continue;
+         print "Host",NewHost,"was not found."
+         continue
+      Attrs = NAttrs
+      Host = NewHost
+      HostDn = "host=" + Host + "," + HostBaseDn
+      OrderedIndex = copy.deepcopy(OrigOrderedIndex)
+      continue
 
    # Handle changing an arbitary value
    if (Response == "a"):
-      Attr = raw_input("Attr? ");
-      ChangeAttr(Attrs[0],Attr);
-      continue;
+      Attr = raw_input("Attr? ")
+      ChangeAttr(Attrs[0],Attr)
+      continue
 
    if (Response == 'd'):
-      Really = raw_input("Really (type yes)? ");
+      Really = raw_input("Really (type yes)? ")
       if Really != 'yes':
-         continue;
-      print "Deleting",HostDn;
-      l.delete_s(HostDn);
-      continue;
+         continue
+      print "Deleting",HostDn
+      l.delete_s(HostDn)
+      continue
 
    # Convert the integer response
    try:
-      ID = int(Response);
+      ID = int(Response)
       if (not OrderedIndex.has_key(ID) or (ID > 100 and RootMode == 0)):
-         raise ValueError;
+         raise ValueError
    except ValueError:
-      print "Invalid";
-      continue;
+      print "Invalid"
+      continue
 
    # Print the what to do prompt
-   print "Changing LDAP entry '%s' (%s)" % (OrderedIndex[ID][0],OrderedIndex[ID][2]);
-   print AttrPrompt[OrderedIndex[ID][2]][0];
-   ChangeAttr(Attrs[0],OrderedIndex[ID][2]);
+   print "Changing LDAP entry '%s' (%s)" % (OrderedIndex[ID][0],OrderedIndex[ID][2])
+   print AttrPrompt[OrderedIndex[ID][2]][0]
+   ChangeAttr(Attrs[0],OrderedIndex[ID][2])