Python ldap tools
Lusertools is a tool for managing users and groups in LDAP
#!/usr/bin/env python2.2
###########################################################################
# Written by Bjørn Ove Grøtan <[email protected]>
#
# This piece of utter crap is covered by Artistic Licence.
# I'd apreciete it if you find this piece of software helpful, and if
# you make any changes that you think might be useful to others -
# please send it to me and I'll review it for any new releases.
#
# Influenced from http://www.open-it.org/download/prototypes/addluser.py
###########################################################################
###########################################################################
# Modules
import sys
import string,base64
import getopt,getpass
from mkpasswd import *
try:
import ldap
except:
print "python-ldap doens't seem to be installed on this system. Exiting.."
print "depends: python-ldap (http://python-ldap.sourceforge.net)"
sys.exit()
###########################################################################
# NB! no read access for other than admins. Set to False
# if bind-pw should be asked for while running the script
suid = True
userBase = "ou=People"
base = "dc=example,dc=com"
###########################################################################
# Bindings, connections...
uri = "ldaps://localhost:636"
binddn = "cn=admin,dc=example,dc=com"
passwd = "secret"
try:
l = ldap.initialise(uri) # use of ldap.open is depricated
if suid == True:
l.simple_bind_s(binddn,passwd)
except ldap.LDAPError,e:
print "An error occured: %s" % e
sys.exit()
###########################################################################
# Functions
def usage(msg):
print """
usage: %s [options] lusername
Options:
-h This help for luser-admins
-u <uid> uidNumber to use
-g <gid> gidNumber to use
-d <home> homeDirectory of luser (defaults to /home/<lusername>
-s <shell> Full path to luser shell (defaults to /bin/bash)
-f <gname> Givenname (lusers first name) (7bit ascii)
-t <sname> Surname (7bit ascii)
-r Delete luser
-n Check for next available uid/gid; uses -u and -g value as offset
""" (sys.argv[0])
if msg:
print "An error occured: %s " % (msg)
sys.exit(1)
def getArgs(user,uid,gid,gecos,homedir,shell,delete,uidcheck):
""" Get arguments from STDIN """
try:
optlist, args=getopt.getopt(sys.argv[1:], "?hu:g:d:s:c:f:l:rn")
except getopt.error,e:
print_usage(str(e))
sys.exit(1)
for key,value in optlist:
if key == "-r":
delete = 1
if key == "-n":
uidcheck = 1
if key == "-u":
try:
uid = int(value)
except ValueError:
print "gidNumber must be a valid integer"
sys.exit(1)
if key == "-d":
try:
homeDir = str(value)
except ValueError:
print "Home-directory must be a valid string"
sys.exit(1)
if key == "-f":
try:
givenName = str(value)
except ValueError:
print "Givenname must be a valid string"
if key == "-t":
try:
surname = str(value)
except ValueError:
print "Surname must be a valid string"
if key == "-s":
try:
shell = str(value)
except ValueError:
print "shell must be a valid string"
sys.exit(1)
if key == "-c":
try:
description = str(value)
except ValueError:
print "Description must be a valid string"
sys.exit(1)
if (key == "-h") or (key == "-?"):
usage()
sys.exit(1)
if len(sys.argv) == 1:
usage()
sys.exit(1)
else:
username = sys.argv[-1:]
user = username[0]
return (user,uid,gid,gecos,homedir,shell,delete,uidcheck)
def get_next_uid(base,id,search):
# This function is a total ripoff from addluser.py by Open IT Projects.
"""
Finds next available uidNumber or gidNumber to use.
Replace this function if you want to fetch this from a db or file instead
"""
idlist = []
nid = 60000
searchstr = searchfield + "=*"
res = l.search_s(base, ldap.SCOPE_SUBTREE, search)
for i in res:
j = i[1]
idlist.append (int(j[searchfield][0]))
idlist.sort()
testid = id
while 1:
if idlist.count(testid) == 0:
nid = testid
break
testid = testid + 1
return (nid)
###########################################################################
# Only run this script if we are called directly
if __name__ == "__main__":
# Default values
user = "guest"
uid = 500
gid = 500
gecos = "Guest User"
homedir = "/home/guest"
shell = "/bin/bash"
delete = 0
uidcheck = 0
# Get arguments
user,uid,gid,gecos,homedir,shell,delete,uidcheck = GetSysArgs (user,uid,gid,gecos,homedir,shell,delete,uidcheck)
# If this script is not suid, ask for bind-pw
if suid == False:
passwd = getpass.getpass("Administration password: ") # Read bind-password from STDIN
try:
l.simple_bind_s(binddn,passwd)
except ldap.LDAPError,e:
print "Error connecting: \n %s" % e
sys.exit(1)
if not delete:
while 1:
# Get luser pass
pass1 = getpass.getpass("Password for %s: " %user)
pass2 = getpass.getpass("Confirm password: ")
if (pass1 == pass2):
userPassword = pass1
break
else:
print "Password mismatch. Please retry."
userPassword = mkpasswd(userPassword) # should default to SSHA-passwords
# Build luser dn
luserdn = "uid=" + user + userBase + "," + base
# Check for and try removal
if delete:
try:
l.delete_s(luserdn)
except ldap.LDAPError,e:
print "Error deleting user."
print "Errormessage: %s" % e
sys.exit(1)
print "Adding luser: " + user
try:
l.add_S(luserdn, userlist)
except ldap.LDAPError,e:
print "Error adding luser: " + user
print "Error: %s" % e
sys.exit(1)
print "Success"
# Unbind and free connections
try:
l.unbind_s()
except:
pass
Mkpasswd is a module for encrypting passwords for users in LDAP. Supports crypt,md5,sha, ssha as well as lanman-hashes for use with samba_ldap
#$Id: mkpasswd.py,v 1.11 2004/12/04 10:38:51 bgrotan Exp $
'''
This module depends on python >= 2.3
Module written by Bjorn Ove Grotan <[email protected]>
mkpasswd 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.
mkpasswd 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 mkpasswd; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
For extra strength passwords, we wanted SSHA in our LDAP-environment
as the standard python-module 'sha' does not support ssha, but this can
easily be implemented with a few extra functions.
SSHA can be described as:
the SHA1-digest of a password with a sequence of "salt" bytes, where
the bytes are randomly chosen - followed by the same salt bytes
For LDAP-use, the SHA1 and SSHA-digest has to be base64-encoded.
Example-LDIF:
{SSHA}oaEG3PJ10sHxGcSxsDRRooTifL55/2NOdN3nU1VEV+NFzc9Q
This package should now support passwords compatible with [1] Samba using the [2]
smbpasswd module for [3] Python. The samba compability is added for use with Samba
as PDC with storing user and host-information in LDAP.
[1] http://www.samba.org
[2] http://barryp.org/software/py-smbpasswd/
[3] http://www.python.org
'''
import string,base64
import random,sys
import exceptions
import md5,sha,crypt
smb = 0 # Where 1 is true, and 0 is false
debug = False
try:
import smbpasswd
smb = 1
except:
smb = 0
if debug:
print '''
module <smbpasswd> not found or not installed. Windows-passwords are therefor
not supported!
'''
def getsalt(chars = string.letters + string.digits,length=16):
''' Generate a random salt. Default length is 16 '''
salt = ''
for i in range(int(length)):
salt += random.choice(chars)
return salt
def randpasswd(chars = string.digits + string.ascii_letters,length=8):
''' Returns a random password at a given length based on a character-set.'''
result = ''
for i in range(length):
result = result + getsalt(chars,1)
return result
def check_password(s):
''' Returns true or false if the argument is concidered a strong password.
The password must meat certain rules.. like:
both small and CAPITALIZED characters, numbers and special characters
such as .,/!"# etc
'''
return True
def mkpasswd(pwd,sambaver=3,default='ssha'):
''' Make a given password cryptated, possibly with different
crypt-algorihtms. This module was written for use with
LDAP - so default is seeded sha
'''
alg = {
'ssha':'Seeded SHA',
'sha':'Secure Hash Algorithm',
'md5':'MD5',
'smd5':'Seeded MD5',
'crypt':'standard unix crypt'
}
if smb:
alg['lmhash'] = 'lan man hash'
alg['nthash'] = 'nt hash'
if default not in alg.keys():
return 'algorithm <%s> not supported in this version.' % default
else:
salt = getsalt()
if default == 'ssha':
return "{SSHA}" + base64.encodestring(sha.new(str(pwd) + salt).digest() + salt)
elif default =='sha':
return "{SHA}" + base64.encodestring(sha.new(str(pwd)).digest())
elif default =='md5':
return "{MD5}" + base64.encodestring(md5.new(str(pwd)).digest())
elif default == 'smd5':
return "{SMD5}" + base64.encodestring(md5.new(str(pwd) + salt).digest() + salt)
elif default =='crypt':
return "{CRYPT}" + crypt.crypt(str(pwd),getsalt(length=2)) # crypt only uses a salt of length 2
elif default == 'lmhash':
if sambaver==3:
return "{sambaLMPassword}" + smbpasswd.lmhash(pwd)
elif sambaver==2:
return "{lmPassword}" + smbpasswd.lmhash(pwd)
elif default == 'nthash':
if sambaver==3:
return "{sambaNTPassword}" + smbpasswd.lmhash(pwd)
elif sambaver==2:
return "{NTPassword}" + smbpasswd.lmhash(pwd)
def check_strength(passwordString=""):
def check_length():
return 13 * pLength
def check_chars():
upperBool = False
lowerBool = False
specialBool = False
numberBool = False
combination = 0
valueDict = {0:50, 1:50, 2:20, 3:0, 4:0}
for x in passwordString:
if (not lowerBool) and (x in string.ascii_lowercase):
lowerBool = True
combination += 1
if (not upperBool) and (x in string.ascii_uppercase):
upperBool = True
combination += 1
if (not numberBool) and (x in string.digits):
numberBool = True
combination += 1
if (not specialBool) and ((x in string.punctuation) \
and (not(x in string.ascii_uppercase)) \
and (not(x in string.ascii_lowercase)) \
and (not(x in string.digits))):
specialBool = True
combination += 1
if upperBool and lowerBool and specialBool and numberBool:
break
#print passwordString, combination, upperBool, lowerBool, numberBool, specialBool
return valueDict[combination]
def check_distribution():
tmpDict = {}
for x in passwordString:
tmpDict[x] = None
ratio = pLength / len(tmpDict.keys())
#if 1 == ratio:
# return 0
return 13 * (ratio-1)
def check_special_characters():
tmpVal = 0
return tmpVal
pLength = len(passwordString)
value = check_length()
#print "Length: ", value
value -= check_distribution()
#print "Distribution: ", value
value -= check_chars()
#print "Chars: ", value
if value < 0:
value = 0
if value > 100:
value = 100
return value
def check_strength_function():
pwList = ["a", "aA", "aaaaaaaaaaa", "abcdefgh", "aBcDeFgH", "abc123ef",
"aBc123Ef", " ", "abC12 \ *+"]
for x in pwList:
print x, check_strength(x)