#!/usr/bin/env python # -*- mode: python -*- # Copyright (c) 2000-2001 Jason Gunthorpe # Copyright (c) 2001 Ryan Murray # Copyright (c) 2003 James Troup # Copyright (c) 2004-2005 Joey Schulze # Copyright (c) 2008,2009 Peter Palfrader # # 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. # This script is an interactive way to manipulate fields in the LDAP directory. # When run it connects to the directory using the current users ID and fetches # all the attributes for the first machine. It then formats them nicely and # allows the user to change them. # # Usage: userinfo -a -u -c -r # -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 time, os, pwd, sys, getopt, ldap, crypt, readline, copy; from tempfile import mktemp from os import O_CREAT, O_EXCL, O_WRONLY from userdir_ldap import *; RootMode = 0; AttrInfo = {"description": ["Machine Descr.", 1], "hostname": ["Host names", 2], "status": ["Status", 3], "l": ["Location", 4], "sponsor": ["Sponsors", 5], "distribution": ["Distribution", 6], "access": ["Access", 7], "admin": ["Admin", 8], "architecture": ["Architecture", 9], "machine": ["Machine Hardware", 10], "memory": ["Memory", 11], "disk": ["Disk", 12], "physicalHost": ["Physical Host", 13], "sshRSAHostKey": ["SSH Host Keys", 14], "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)"], "status": ["Blank if Up, explaination if not"], "l": ["Physical location"], "sponsor": ["Sponsors and their URLs"], "distribution": ["The distribution version"], "access": ["all, developer only, restricted"], "admin": ["Admin email address"], "architecture": ["Debian Architecture string"], "machine": ["Hardware description"], "memory": ["Installed RAM"], "disk": ["Disk Space, RAID levels, etc"], "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 = {}; for at in AttrInfo.keys(): if (AttrInfo[at][1] != 0): 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",""); 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 last modified on"), time.strftime("%a %d/%m/%Y %X UTC",Time), print "by",ldap.explode_dn(GetAttr(Attrs,"modifiersName"),1)[0]; 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); # Display all of the attributes in a numbered list def ShowAttrs(Attrs): print; PrintModTime(Attrs); for at in Attrs[1].keys(): if AttrInfo.has_key(at): if AttrInfo[at][1] == 0: print " %-18s:" % (AttrInfo[at][0]), for x in Attrs[1][at]: print "'%s'" % (x), print; else: OrderedIndex[AttrInfo[at][1]][1] = Attrs[1][at]; 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; 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 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? "); # Empty string if (NewValue == ""): print "Leaving unchanged."; return; # Single space designates delete, trap the delete error if (NewValue == " "): print "Deleting.",; try: l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,None)]); except ldap.NO_SUCH_ATTRIBUTE: pass; 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; def MultiChangeAttr(Attrs,Attr): # Make sure that we have an entry if not Attrs[1].has_key(Attr): Attrs[1][Attr] = []; Attrs[1][Attr].sort(); print "Old values: ",Attrs[1][Attr]; Mode = raw_input("[D]elete or [A]dd? ").upper() if (Mode != 'D' and Mode != 'A'): return; NewValue = raw_input("Value? "); # Empty string if (NewValue == ""): print "Leaving unchanged."; return; # Delete if (Mode == "D"): print "Deleting.",; try: l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,NewValue)]); except ldap.NO_SUCH_ATTRIBUTE: print "Failed"; 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; def CalcTempFile(): unique = 0 while unique == 0: name = mktemp() try: fd = os.open(name, O_CREAT | O_EXCL | O_WRONLY, 0600) except OSError: continue os.close(fd) unique = 1 return name # Main program starts here User = pwd.getpwuid(os.getuid())[0]; BindUser = User; ListMode = 0 FingerPrints = 0 Host = None # Process options 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; elif (switch == '-a'): BindUser = val; elif (switch == '-r'): RootMode = 1; elif (switch == '-n'): BindUser = ""; elif (switch == '-l'): BindUser = ""; ListMode = 1 elif (switch == '-f'): BindUser = ""; FingerPrints = 1 if (BindUser != ""): l = passwdAccessLDAP(BaseDn, BindUser) else: l = connectLDAP() l.simple_bind_s("","") 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() tmpfile = CalcTempFile() 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']: tmp = open(tmpfile, 'w') tmp.write(key + '\n') tmp.close() fp = os.popen('/usr/bin/ssh-keygen -l -f ' + tmpfile, "r") input = fp.readline() fp.close() fingerprint = input.split(' ') print "%s %s root@%s" % (fingerprint[0], fingerprint[1], host) os.unlink(tmpfile) sys.exit(0) HostDn = "host=" + Host + "," + HostBaseDn; # Query the server for all of the attributes Attrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host); if len(Attrs) == 0: print "Host",Host,"was not found."; sys.exit(0); # repeatedly show the account configuration while(1): ShowAttrs(Attrs[0]); if (BindUser == ""): 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"; # Prompt Response = raw_input("Change? "); if (Response == "x" or Response == "X" or Response == "q" or Response == "quit" or Response == "exit"): break; # Change who we are looking at if (Response == 'u' or Response == 'U'): NewHost = raw_input("Host? "); if 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 + "," + 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? "); if 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? "); if NewHost == "": continue; Dn = "host=" + NewHost + "," + HostBaseDn; l.add_s(Dn,[("host", NewHost), ("hostname", NewHostName), ("objectClass", ("top", "debianServer"))]); # Switch 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 + "," + HostBaseDn; OrderedIndex = copy.deepcopy(OrigOrderedIndex); continue; # Handle changing an arbitary value if (Response == "a"): Attr = raw_input("Attr? "); ChangeAttr(Attrs[0],Attr); continue; if (Response == 'd'): Really = raw_input("Really (type yes)? "); if Really != 'yes': continue; print "Deleting",HostDn; l.delete_s(HostDn); continue; # Convert the integer response try: ID = int(Response); if (not OrderedIndex.has_key(ID) or (ID > 100 and RootMode == 0)): raise ValueError; except ValueError: 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]);