X-Git-Url: https://git.adam-barratt.org.uk/?p=mirror%2Fuserdir-ldap.git;a=blobdiff_plain;f=ud-info;h=3db064800f8eb03d51f6642d81af4f3bb8690d00;hp=a99a3ebc55fc8ab730145f27ebb745c987768ccd;hb=HEAD;hpb=e9fb8e4029022dfae6792f8dcc680792bc431c2e diff --git a/ud-info b/ud-info index a99a3eb..3db0648 100755 --- a/ud-info +++ b/ud-info @@ -17,7 +17,30 @@ # -r Enable 'root' functions, do this if your uid has access to # restricted variables. -import string, time, os, pwd, sys, getopt, ldap, crypt, whrandom, readline, copy; +# Copyright (c) 1999-2001 Jason Gunthorpe +# Copyright (c) 2004-2005,7,8 Joey Schulze +# Copyright (c) 2001-2006 Ryan Murray +# Copyright (c) 2008,2009 Peter Palfrader +# Copyright (c) 2008 Martin Zobel-Helas +# Copyright (c) 2008 Marc 'HE' Brockschmidt +# Copyright (c) 2008 Mark Hymers +# Copyright (c) 2008 Thomas Viehmann +# +# 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 time, os, pwd, sys, getopt, ldap, crypt, readline, copy, getpass from userdir_ldap import *; RootMode = 0; @@ -27,49 +50,77 @@ AttrInfo = {"cn": ["First Name", 101], "c": ["Country Code",1], "l": ["Locality",2], "ou": ["Membership",0], - "facsimiletelephonenumber": ["Fax Phone Number",3], - "telephonenumber": ["Phone Number",4], - "postaladdress": ["Mailing Address",5], - "postalcode": ["Postal Code",6], + "facsimileTelephoneNumber": ["Fax Phone Number",3], + "telephoneNumber": ["Phone Number",4], + "postalAddress": ["Mailing Address",5], + "postalCode": ["Postal Code",6], "uid": ["Unix User ID",0], - "loginshell": ["Unix Shell",7], - "supplementarygid": ["Unix Groups",0], - "allowedhosts": ["Host ACL",0], + "loginShell": ["Unix Shell",7], + "supplementaryGid": ["Unix Groups",0], + "allowedHost": ["Host ACL",0], "member": ["LDAP Group",0], - "emailforward": ["Email Forwarding",8], - "ircnick": ["IRC Nickname",9], - "onvacation": ["Vacation Message",10], - "labeledurl": ["Home Page",11], + "emailForward": ["Email Forwarding",8], + "ircNick": ["IRC Nickname",9], + "onVacation": ["Vacation Message",10], + "labeledURI": ["Home Page",11], "latitude": ["Latitude",12], "longitude": ["Longitude",13], - "icqUIN": ["ICQ UIN",14], - "comment": ["Comment",115], - "userpassword": ["Crypted Password",116], - "dnszoneentry": ["d.net Entry",117]}; + "icqUin": ["ICQ UIN",14], + "jabberJID": ["Jabber ID",15], + "privateSub": ["Debian-Private",16], + "birthDate": ["Date of Birth",18], + "mailDisableMessage": ["Mail Disabled",19], + "mailGreylisting": ["Mail Greylisting",20], + "mailCallout": ["Mail Callouts",21], + "mailRBL": ["Mail RBLs",22], + "mailRHSBL": ["Mail RHSBLs",23], + "mailWhitelist": ["Mail Whitelist",24], + "mailContentInspectionAction": ["mail C-I Action",25], + "VoIP": ["VoIP Address",26], + "comment": ["Comment",116], + "userPassword": ["Crypted Password",117], + "dnsZoneEntry": ["d.net Entry",118], + "accountStatus": ["DD status",301], + "accountComment": ["DD status comment",302], + }; AttrPrompt = {"cn": ["Common name or first name"], "mn": ["Middle name (or initial if it ends in a dot)"], "sn": ["Surname or last name"], "c": ["ISO 2 letter country code, such as US, DE, etc"], "l": ["City name, State/Provice (Locality)\n e.g. Dallas, Texas"], - "facsimiletelephonenumber": ["Fax phone number, with area code and country code"], - "telephonenumber": ["Voice phone number"], - "postaladdress": ["Complete mailing address including postal codes and country designations\nSeperate lines using a $ character"], - "postalcode": ["Postal Code or Zip Code"], - "loginshell": ["Login shell with full path (no check is done for validity)"], - "emailforward": ["EMail address to send all mail to or blank to disable"], - "ircnick": ["IRC nickname if you use IRC"], - "onvacation": ["A message if on vaction, indicating the time of departure and return"], - "userpassword": ["The users Crypt'd password"], + "facsimileTelephoneNumber": ["Fax phone number, with area code and country code"], + "telephoneNumber": ["Voice phone number"], + "postalAddress": ["Complete mailing address including postal codes and country designations\nSeperate lines using a $ character"], + "postalCode": ["Postal Code or Zip Code"], + "loginShell": ["Login shell with full path (no check is done for validity)"], + "emailForward": ["EMail address to send all mail to or blank to disable"], + "ircNick": ["IRC nickname if you use IRC"], + "onVacation": ["A message if on vaction, indicating the time of departure and return"], + "userPassword": ["The users Crypt'd password"], "comment": ["Admin Comment about the account"], - "supplementarygid": ["Groups the user is in"], - "allowedhosts": ["Grant access to certain hosts"], + "supplementaryGid": ["Groups the user is in"], + "allowedHost": ["Grant access to certain hosts"], + "privateSub": ["Debian-Private mailing list subscription"], + "birthDate": ["Date of Birth (YYYYMMDD)"], + "mailDisableMessage": ["Error message to return via SMTP"], + "mailGreylisting": ["SMTP Greylisting (TRUE/FALSE)"], + "mailCallout": ["SMTP Callouts (TRUE/FALSE)"], + "mailRBL": ["SMTP time RBL lists"], + "mailRHSBL": ["SMTP time RHSBL lists"], + "mailWhitelist": ["SMTP time whitelist from other checks"], + "mailContentInspectionAction": ["Content Inspection Action (reject, blackhole, markup)"], "member": ["LDAP Group Member for slapd ACLs"], "latitude": ["XEarth latitude in ISO 6709 format - see /usr/share/zoneinfo/zone.tab or etak.com"], "longitude": ["XEarth latitude in ISO 6709 format - see /usr/share/zoneinfo/zone.tab or etak.com"], - "dnszoneentry": ["DNS Zone fragment associated this this user"], - "labeledurl": ["Web home page"], - "icqUIN": ["ICQ UIN Number"]}; + "dnsZoneEntry": ["DNS Zone fragment associated this this user"], + "labeledURI": ["Web home page"], + "jabberJID": ["Jabber ID"], + "icqUin": ["ICQ UIN Number"], + "VoIP": ["VoIP Address"], + "accountStatus": ["DD status"], + "accountComment": ["DD status comment"], + }; # Create a map of IDs to desc,value,attr OrderedIndex = {}; @@ -78,14 +129,18 @@ for at in AttrInfo.keys(): OrderedIndex[AttrInfo[at][1]] = [AttrInfo[at][0], "", at]; OrigOrderedIndex = copy.deepcopy(OrderedIndex); +for id in OrderedIndex: + if not AttrPrompt.has_key( OrderedIndex[id][2] ): + print "Warning: no AttrPrompt for %s"%(id) + # Show shadow information def PrintShadow(Attrs): - Changed = int(GetAttr(Attrs,"shadowlastchange","0")); - MinDays = int(GetAttr(Attrs,"shadowmin","0")); - MaxDays = int(GetAttr(Attrs,"shadowmax","0")); - WarnDays = int(GetAttr(Attrs,"shadowwarning","0")); - InactDays = int(GetAttr(Attrs,"shadowinactive","0")); - Expire = int(GetAttr(Attrs,"shadowexpire","0")); + Changed = int(GetAttr(Attrs,"shadowLastChange","0")); + MinDays = int(GetAttr(Attrs,"shadowMin","0")); + MaxDays = int(GetAttr(Attrs,"shadowMax","0")); + WarnDays = int(GetAttr(Attrs,"shadowWarning","0")); + InactDays = int(GetAttr(Attrs,"shadowInactive","0")); + Expire = int(GetAttr(Attrs,"shadowExpire","0")); print "%-24s:" % ("Password last changed"), print time.strftime("%a %d/%m/%Y %Z",time.localtime(Changed*24*60*60)); @@ -97,14 +152,14 @@ def PrintShadow(Attrs): # 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); 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); @@ -112,10 +167,10 @@ def PrintModTime(Attrs): # Print the PGP key for a user def PrintKeys(Attrs): - if Attrs[1].has_key("keyfingerprint") == 0: + if Attrs[1].has_key("keyFingerPrint") == 0: return; First = 0; - for x in Attrs[1]["keyfingerprint"]: + for x in Attrs[1]["keyFingerPrint"]: if First == 0: print "%-24s:" % ("PGP/GPG Key Fingerprints"), First = 1; @@ -125,12 +180,12 @@ def PrintKeys(Attrs): # Print the SSH RSA Authentication keys for a user def PrintSshRSAKeys(Attrs): - if Attrs[1].has_key("sshrsaauthkey") == 0: + if Attrs[1].has_key("sshRSAAuthKey") == 0: return; First = 0; - for x in Attrs[1]["sshrsaauthkey"]: + for x in Attrs[1]["sshRSAAuthKey"]: if First == 0: - print "%-24s:" % ("SSH RSA Auth Keys"), + print "%-24s:" % ("SSH Auth Keys"), First = 1; else: print "%-24s:" % (""), @@ -153,7 +208,7 @@ def ShowAttrs(Attrs): for x in Attrs[1][at]: print "'%s'" % (x), if at == "uid": - print "(id=%s, gid=%s)" % (GetAttr(Attrs,"uidnumber","-1"),GetAttr(Attrs,"gidnumber","-1")), + print "(id=%s, gid=%s)" % (GetAttr(Attrs,"uidNumber","-1"),GetAttr(Attrs,"gidNumber","-1")), print; else: OrderedIndex[AttrInfo[at][1]][1] = Attrs[1][at]; @@ -162,15 +217,16 @@ def ShowAttrs(Attrs): Keys.sort(); for at in Keys: if at < 100 or RootMode != 0: - print " %3u) %-18s: " % (at,OrderedIndex[at][0]), + print " %3u) %-19s: " % (at,OrderedIndex[at][0]), for x in OrderedIndex[at][1]: print "'%s'" % (re.sub('[\n\r]','?',x)), print; # Change a single attribute def ChangeAttr(Attrs,Attr): - if (Attr == "supplementarygid" or Attr == "allowedhosts" or \ - Attr == "member" or Attr == "dnszoneentry"): + if (Attr == "supplementaryGid" or Attr == "allowedHost" or \ + Attr == "member" or Attr == "dnsZoneEntry" or Attr == "mailWhitelist" or \ + Attr == "mailRBL" or Attr == "mailRHSBL"): return MultiChangeAttr(Attrs,Attr); print "Old value: '%s'" % (GetAttr(Attrs,Attr,"")); @@ -194,6 +250,16 @@ def ChangeAttr(Attrs,Attr): Attrs[1][Attr] = [""]; return; + if (Attr == "mailGreylisting" or Attr == "mailCallout"): + if (NewValue.lower() != "true" and NewValue.lower() != "false"): + if (NewValue == "1"): NewValue = "true" + else: + if (NewValue == "0"): NewValue = "false" + else: + print "Need a boolean value" + return + NewValue = NewValue.upper() + # Set a new value print "Setting.",; l.modify_s(UserDn,[(ldap.MOD_REPLACE,Attr,NewValue)]); @@ -208,7 +274,7 @@ def MultiChangeAttr(Attrs,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; @@ -236,11 +302,30 @@ def MultiChangeAttr(Attrs,Attr): Attrs[1][Attr].append(NewValue); print; +def Lock(UserDn, Attrs, DisableMail=True): + shadowLast = str(int(time.time()/24/60/60)); + recs = [ + (ldap.MOD_REPLACE,"userPassword","{crypt}*LK*"), + (ldap.MOD_REPLACE,"shadowLastChange",shadowLast), + (ldap.MOD_REPLACE,"shadowExpire","1")]; + if DisableMail: + recs.append( (ldap.MOD_REPLACE,"mailDisableMessage","account locked") ) + Attrs[0][1]["mailDisableMessage"] = ["account locked"]; + l.modify_s(UserDn,recs); + Attrs[0][1]["userPassword"] = ["{crypt}*LK*"]; + Attrs[0][1]["shadowLastChange"] = [shadowLast]; + Attrs[0][1]["shadowExpire"] = ["1"]; + # Main program starts here User = pwd.getpwuid(os.getuid())[0]; BindUser = User; # Process options -(options, arguments) = getopt.getopt(sys.argv[1:], "nu:c:a:r") +try: + (options, arguments) = getopt.getopt(sys.argv[1:], "nu:c:a:r") +except getopt.GetoptError, data: + print data + sys.exit(1) + for (switch, val) in options: if (switch == '-u'): User = val; @@ -261,22 +346,27 @@ if (BindUser != User): print "as '" + BindUser + "'"; else: print; -if (BindUser != ""): - Password = getpass(BindUser + "'s password: "); # Connect to the ldap server -l = ldap.open(LDAPServer); -UserDn = "uid=" + BindUser + "," + BaseDn; +l = connectLDAP() +UserDn = "uid=" + User + "," + BaseDn if (BindUser != ""): - l.simple_bind_s(UserDn,Password); + Password = getpass.getpass(BindUser + "'s password: ") + BindUserDn = "uid=" + BindUser + "," + BaseDn else: - l.simple_bind_s("",""); -UserDn = "uid=" + User + "," + BaseDn; + Password = "" + BindUserDn = "" +try: + l.simple_bind_s(BindUserDn,Password) +except ldap.LDAPError,e: + print >> sys.stderr, "LDAP error:", e.args[0]['desc'] + print >> sys.stderr, " ", e.args[0]['info'] + sys.exit(1) # Enable changing of supplementary gid's if (RootMode == 1): # Items that root can edit - list = ["supplementarygid","allowedhosts","member"]; + list = ["supplementaryGid","allowedHost","member"]; Count = 0; for x in list: AttrInfo[x][1] = 200 + Count; @@ -298,6 +388,9 @@ while(1): if RootMode == 1: print " a) Arbitary Change"; + print " r) retire developer"; + print " R) Randomize Password"; + print " L) Lock account and disable mail"; print " p) Change Password"; print " u) Switch Users"; print " x) Exit"; @@ -329,28 +422,108 @@ while(1): print "contain spaces and other special characters. No checking is done on the"; print "strength of the passwords so pick good ones please!"; - Pass1 = getpass(User + "'s new password: "); - Pass2 = getpass(User + "'s new password again: "); + Pass1 = getpass.getpass(User + "'s new password: ") + Pass2 = getpass.getpass(User + "'s new password again: ") if Pass1 != Pass2: print "Passwords did not match"; raw_input("Press a key"); continue; - # Hash it telling glibc to use the MD5 algorithm - if you dont have - # glibc then just change Salt = "$1$" to Salt = ""; - SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/."; - Salt = "$1$"; - for x in range(0,10): - Salt = Salt + SaltVals[whrandom.randint(0,len(SaltVals)-1)]; - Pass = crypt.crypt(Pass1,Salt); - if len(Pass) < 14: - print "Caution! MD5 Password hashing failed, not changing password!"; + try: + Pass = HashPass(Pass1); + except: + print "%s: %s\n" %(sys.exc_type,sys.exc_value); raw_input("Press a key"); continue; print "Setting password.."; Pass = "{crypt}" + Pass; - l.modify_s(UserDn,[(ldap.MOD_REPLACE,"userpassword",Pass)]); + shadowLast = str(int(time.time()/24/60/60)); + l.modify_s(UserDn,[(ldap.MOD_REPLACE,"userPassword",Pass), + (ldap.MOD_REPLACE,"shadowLastChange",shadowLast)]); + Attrs[0][1]["userPassword"] = [Pass]; + Attrs[0][1]["shadowLastChange"] = [shadowLast]; + continue; + + # retire DD + if Response == 'r' and RootMode == 1: + if Attrs[0][1].has_key("accountStatus") == 0: + curStatus = "" + else: + curStatus = Attrs[0][1]["accountStatus"][0] + if Attrs[0][1].has_key("accountComment") == 0: + curComment = "" + else: + curComment = Attrs[0][1]["accountComment"][0] + print "\n\nCurrent status is %s"%curStatus + print "Current comment is %s\n"%curComment + + print "Set account to:" + print " 1) retiring (lock account but do not disable mail):" + print " 2) inactive (removed/emeritus/... - lock account and disable mail):" + print " 3) memorial (lock account and disable mail):" + print " 4) active (do not change other settings, you will have to deal with them)" + print " q) return (no change)" + Resp = raw_input("Action? ") + if Resp == "1" or Resp == "2": + Lock(UserDn, Attrs, Resp == "2") + if Resp == "1": + newstatus = "retiring %s"%(time.strftime("%Y-%m-%d")) + else: + newstatus = "inactive %s"%(time.strftime("%Y-%m-%d")) + l.modify_s(UserDn,[(ldap.MOD_REPLACE,"accountStatus",newstatus)]) + Attrs[0][1]["accountStatus"] = [newstatus] + + Resp2 = raw_input("Optional RT ticket number? ") + if (Resp2 != ''): + comment = "RT#%s"%(Resp2) + l.modify_s(UserDn,[(ldap.MOD_REPLACE,"accountComment",comment)]) + Attrs[0][1]["accountComment"] = [comment] + elif Resp == "3": + Lock(UserDn, Attrs) + newstatus = "memorial" + l.modify_s(UserDn,[(ldap.MOD_REPLACE,"accountStatus",newstatus)]) + Attrs[0][1]["accountStatus"] = [newstatus] + elif Resp == "4": + newstatus = "active" + l.modify_s(UserDn,[(ldap.MOD_REPLACE,"accountStatus",newstatus)]) + Attrs[0][1]["accountStatus"] = [newstatus] + + continue; + + + # Randomize password + if Response == 'R' and RootMode == 1: + Resp = raw_input("Randomize Users Password? [no/yes]"); + if Resp != "yes": + continue; + + # Generate a random password + try: + Password = GenPass(); + Pass = HashPass(Password); + except: + print "%s: %s\n" %(sys.exc_type,sys.exc_value); + raw_input("Press a key"); + continue; + + print "Setting password.."; + Pass = "{crypt}" + Pass; + shadowLast = str(int(time.time()/24/60/60)); + l.modify_s(UserDn,[(ldap.MOD_REPLACE,"userPassword",Pass), + (ldap.MOD_REPLACE,"shadowLastChange",shadowLast)]); + Attrs[0][1]["userPassword"] = [Pass]; + Attrs[0][1]["shadowLastChange"] = [shadowLast]; + continue; + + # Lock account + if Response == 'L' and RootMode == 1: + Resp = raw_input("Really lock account? [no/yes]"); + if Resp != "yes": + continue; + + print "Setting password.."; + Lock(UserDn, Attrs) continue; # Handle changing an arbitary value