4

I am trying to make a Python script that will open an LDAP connection to a server running AD, take a search entry (in this case a name), search for that entry and change that users password to a randomly generated password (as well as set the option to change password on logon) and then send them an automated secure email containing the new temporary password.

So far I have been able to connect to the server, and search for a single DN which returns. The temporary password is being generated, and an email is being sent (although the password is not hashed, and the email is not secure yet). However, I cannot find any information on where to go from here.

I have found Change windows user password with python however I see that this does not play well with AD, and the other LDAP in Python documentation I have been finding seems to be outdated from 2.x and no longer works. The documentation for ldap3 (https://media.readthedocs.org/pdf/ldap3/stable/ldap3.pdf) also doesnt seem to really mention anything for it, and exhaustive Googling has been fruitless. I am new to this kind of programming having only low level or academic knowledge previously, so this has been a bit frustrating but Python is my strongest language.

----------------EDITED CODE TO CURRENT STATUS-----------------------

#Takes input for name which will be used for search criterion
zid = input("ZID: ")
zid = str(zid).lower()
print(zid)

#Binds session to the server and opens a connection
try:
    server = ldap3.Server('ldap://<IP_Address>', get_info=all)
    conn = ldap3.Connection(server, '%s@something.com' %zid, password = "<something>", auto_bind=True) 
    print("Successfully bound to server.\n")
except:
    print("Unsucessful initialization of <IP_Address>")
    try:
        server = ldap3.Server('ldap://<IP_Address>', get_info=all)
        conn = ldap3.Connection(server, '%s@something.com' %zid, password = "<something>", auto_bind=True) 
        print("Successfully bound to server.\n")
    except:
        print("Unsucessful initialization of <IP_Address>")
        try:
            server = ldap3.Server('ldap://<IP_Address>', get_info=all)
            conn = ldap3.Connection(server, '%s@something.com', password = "<something>", auto_bind=True) %zid 
            print("Successfully bound to server.\n")
        except:
            print("Unsucessful initialization of <IP_Address>")
            sys.exit(0)

#Searches and prints LDAP entries
try:
    base_dn = 'DC=<something>,DC=<something>,DC=<something>,DC=<something>,DC=com'
    zid_filter = '(sAMAccountName=%s)' %zid
    conn.search(base_dn, zid_filter, attributes=['mail'])

    #i.e. "DN: CN=<First Last>,OU=<something>, DC= <something>
    user_dn = str(conn.entries)

    #i.e. "CN=<First Last>"
    front = user_dn.find('C')
    back = user_dn.find(',')
    user_cn = user_dn[front:back]

    #i.e. "<First Last>"
    display_name = user_cn[3:]

    #i.e. "first.last@<something>.com"
    raw_email = str(conn.entries)
    front = raw_email.find('mail: ')
    back = raw_email.find('@<something>.com')
    user_email = raw_email[front + 6:back] + '@<something>.com'
except:
    print("Could not search entries")

#Generates random 12 digit alpha-numeric password
try:
    new_password = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(12))
    print(new_password)
    print("New password successfully generated")
except:
    print("New password could not be generated")


#Set and replace AD Password
try:
    conn.extend.microsoft.modify_password(user_dn, None, new_password)
    print ("Active Directory password was set successfully!")
except:
    print('Error setting AD password')
    sys.exit(0)

Any suggestions on how to get/set the user password and hash the password for security purposes during this whole ordeal? For the email I imagine I can force it to use HTTPS and that would be sufficient, but the connection to the server passing the new_password to I would like to secure.

Community
  • 1
  • 1
woundjuice
  • 41
  • 1
  • 1
  • 4
  • I also looked at http://stackoverflow.com/questions/20875708/how-can-i-change-password-for-domain-userwindows-active-directory-using-python?rq=1 however the server I am attempting to interact with is not using any certificates or SSL. – woundjuice Jun 16 '16 at 17:49

2 Answers2

4

ldap3 contains a specific method for changing the AD password, just add the following after you generated a new password:

dn = conn.entries[0].entry_get_dn()  # supposing you got back a single entry
conn.extend.microsoft.modify_password(dn, None, new_password)

This should properly encode the password and store it in AD.

Saikat
  • 14,222
  • 20
  • 104
  • 125
cannatag
  • 1,528
  • 11
  • 17
  • ok so my code has changed a bit since i posted, and i added a variable that stores a single users DN (which is what i assume you were telling me to do with dn = conn.entries[0].entry_get_dn()) so I will try the extend method today. – woundjuice Jun 20 '16 at 12:33
  • So i tried the conn.extend.microsoft.modify_password(dn, None, new_password) method modified slightly for my DN variable name... seems to be working as it does not give an error, however the password does not appear to be getting changed... i am confused how that can execute without throwing an exception, yet doesnt seem to be setting the password? Also, how to I manipulate the "change password on next logon" flag? I will edit my question with my current code block. – woundjuice Jun 20 '16 at 12:54
  • So after some more intense Googling ive been seeing that apparently AD wont actually change a password like this unless it is through an SSL connection or something? Is this accurate, and if so.... how can I enable this if the server is not using certificates or anything? :/ – woundjuice Jun 20 '16 at 19:40
  • 1
    Try with``use_ssl=True`` while defining the server object. – cannatag Jun 21 '16 at 03:40
  • ive tried the following with both specifying the port as well as not, with no luck: server = ldap3.Server('host.com', port = 636, use_ssl = True) conn = Connection(server, user='%s@host.com' %zid, password = "password", authentication=NTLM, auto_bind=True) – woundjuice Jun 21 '16 at 09:59
  • You can't change the password if the connection is not encrypted. You must configure the AD server to use a certificate. – cannatag Jul 09 '16 at 12:55
  • Is ssl also a requirement for using `ModifyPassword` on OpenLDAP? I should probably ask my own question for this, and still might. – Josh Smeaton Sep 05 '16 at 06:04
  • I've asked a specific question regarding openldap here: http://stackoverflow.com/questions/39325089/changing-userpassword-in-openldap-using-ldap3-library – Josh Smeaton Sep 05 '16 at 06:42
  • Please note that the [docs](https://ldap3.readthedocs.io/en/latest/microsoft.html) state that the format should actually be: `conn.extend.microsoft.modify_password(dn, new_password)` – Ryan Sep 24 '20 at 21:18
3

This code is working with Windows 2012 R2 AD:

First, install latest ldap3:

sudo pip3 install ldap

#!/usr/bin/python3

import ldap3

SERVER='127.0.0.1'
BASEDN="DC=domain,DC=com"
USER="user_domain_login_name@domain.com"
CURREENTPWD="current_password"
NEWPWD="new_password"

SEARCHFILTER='(&(userPrincipalName='+USER+')(objectClass=person))'

USER_DN=""
USER_CN=""

ldap_server = ldap3.Server(SERVER, get_info=ldap3.ALL)
conn = ldap3.Connection(ldap_server, USER, CURREENTPWD, auto_bind=True)
conn.start_tls()
print(conn)

conn.search(search_base = BASEDN,
         search_filter = SEARCHFILTER,
         search_scope = ldap3.SUBTREE,
         attributes = ['cn', 'givenName', 'userPrincipalName'],
         paged_size = 5)

for entry in conn.response:
    if entry.get("dn") and entry.get("attributes"):
        if entry.get("attributes").get("userPrincipalName"):
            if entry.get("attributes").get("userPrincipalName") == USER:
                USER_DN=entry.get("dn")
                USER_CN=entry.get("attributes").get("cn")

print("Found user:", USER_CN)

if USER_DN:
    print(USER_DN)
    print(ldap3.extend.microsoft.modifyPassword.ad_modify_password(conn, USER_DN, NEWPWD, CURREENTPWD,  controls=None))
else:
    print("User DN is missing!")
Saikat
  • 14,222
  • 20
  • 104
  • 125
Tamas Tobi
  • 76
  • 3
  • worked like a charm and tested on active directory server running windows 2012 – justsomeguy Jul 31 '19 at 21:42
  • This returns result successful {'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'type': 'modifyResponse'} But the password is not changed in AD server itself. – Shyamkkhadka Dec 16 '22 at 05:25