#!/usr/bin/python # addluser v.05 # created by Joe Little # provides useradd style additions to an LDAP directory service # This prototype is PublicDomain at this point (heh its a prototype!) # It represents one piece from an upcoming suite of tools, which combined # should be GPLed as a package. # please change the base_dn below before using. # It currently connects to localhost as the LDAP server (s/ localhost) # It also depends upon python-ldap (http://python-ldap.sourceforge.net) # # CHANGELOG # v.01 -- Initial release # v.02 -- added -r (remove) functionality, fixed default cn behavior # v.03 -- uid checking added. Use with -n flag. It will find the next # UID/GID after the uid and gid number provided by the user # via the command line, or after the default uid/gid number, # if no uid/gid is provided as an argument # Separately, it uses uidcheck to make sure there is no duplicate # UID in system. Skips GID check since having same GID is allowed # v.04 -- added SALT routine as provided by Luke Howard. Much better.. # v.05 -- changed importation to ldap instead of _ldap # Added password confirmation block to ensure correct user password import sys import getpass import crypt import ldap import whrandom base_dn = "dc=open-it,dc=org" #mail_domain = "open-it.org" login_dn = "cn=Manager," + base_dn # This is the ldap server, set to localhost by default. Currently NOT secure! ldap_host = "localhost" # def usage statment def print_usage(errormsg=''): print """ usage: %s [options] username Options: -h or -? Print out this message -u uid uid number to use -g gid gid number to use -d home home directory of user -s shell path of shell to use -c comment GECOS field entry -r remove user from LDAP (other options ignored) -n Check for next available uid/gid; uses -u and -g value as offset """ % (sys.argv[0]) if errormsg: print ' Error: %s' % (errormsg) sys.exit(1) def getsalt(): salt = "" for j in range(2): i = whrandom.randint(0,9) % 3 if i == 0 : i = (whrandom.randint(0,9) % 11) elif i == 1 : i = (whrandom.randint(0,9) % 25) elif i == 2 : i = (whrandom.randint(0,9) % 25) salt = salt + str(i) return (salt) # RFC2307 ldif creation def def BuildUser(user,uid,gid,gecos,homedir,shell,cn,userpass): user_ldif = [ ("uid", [user]), ("cn", [cn]), ("objectclass", ["account", "posixAccount", "shadowAccount", "top"]), ("shadowLastChange", ["12000"]), ("shadowMax", ["99999"]), ("shadowWarning", ["7"]), ("shadowInactive", ["-1"]), ("shadowExpire", ["-1"]), ("shadowFlag", ["123456789"]), ("userPassword", [userpass]), ("loginshell", [shell]), ("uidNumber", [str(uid)]), ("gidNumber", [str(gid)]), ("homeDirectory", [homedir]), ("gecos", [gecos]) ] return (user_ldif) # command line parsing def GetSysArgs(user,uid,gid,gecos,homedir,shell,delete,uidcheck): import getopt 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 'uid must be a number' sys.exit(1) if key == "-g": try: gid = int(value) except ValueError: print 'gid must be a number' 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 == "-s": try: shell = str(value) except ValueError: print 'shell must be a valid string' sys.exit(1) if key == "-c": try: gecos = str(value) except ValueError: print 'comment field must be a valid string' sys.exit(1) if (key == "-h") or (key == "-?"): print_usage() sys.exit(1) if len(sys.argv) == 1: print_usage() sys.exit(1) else: username = sys.argv[-1:] user = username[0] return ( user,uid,gid,gecos,homedir,shell,delete,uidcheck ) # checkuid function. connects with server and finds next available uidnumber # or gidnumber to use. Uses current uid/gid value (or default) as offset def check_id (base_dn, id, searchfield): idlist = [] nid = 60000 searchstr = searchfield + "=*" res = l.search_s(base_dn, ldap.SCOPE_SUBTREE, searchstr) 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) # actual main l = ldap.open(ldap_host) # default defs user = "guest" uid = 500 gid = 500 gecos = "Guest User" homedir = "/home/guest" shell = "/bin/bash" delete = 0 uidcheck = 0 # get command line arguments user,uid,gid,gecos,homedir,shell,delete,uidcheck = GetSysArgs (user,uid,gid,gecos,homedir,shell,delete,uidcheck) if not delete: while 1: # get user pass pass1 = getpass.getpass ("Password for %s: " %user) pass2 = getpass.getpass ("Confirm password: ") if (pass1 == pass2) : userpass_entry = pass1 break else: print "Passwords do not match. Please retry password." userpass = "{crypt}" + crypt.crypt(userpass_entry, getsalt()) # get password for root dn login_pw = getpass.getpass ("Password for %s: " % login_dn) # bind as root dn l.simple_bind_s (login_dn, login_pw) # if checking of uid select, establish new uid now if uidcheck: cuid = check_id (base_dn, uid, "uidNumber") uid = cuid cuid = check_id (base_dn, gid, "gidNumber") gid = cuid print "UID=%d GID=%d" % (uid,gid) # build ldif to add, if thats our operation if not delete: cn = user user_info = BuildUser (user,uid,gid,gecos,homedir,shell,cn,userpass) # check if UID is already in use on server if not uidcheck: cuid = check_id (base_dn, uid, "uidNumber") if (cuid != uid): print "UID already in use, consider using -n flag" sys.exit(1) # build user dn dn = "uid=" + user + ",ou=People," + base_dn # check for removal if delete: try: l.delete_s(dn) print "Deleted " + user sys.exit(0) except ldap.LDAPError: print "Error in deleting entry" print ' Error: %s' % (sys.exc_type) sys.exit(1) # add user otherwise print "Adding " + user try: l.add_s (dn, user_info) except ldap.LDAPError: print "error in creating entry" print ' Error: %s' % (sys.exc_type) sys.exit(1) print "Success" # end connection l.unbind()