3

I can't seem to change a users password using the ldap3 python module against an OpenLDAP server. A similar question has been asked before but that's specific to Active Directory.

What I've tried:

from ldap3.extend.standard.modifyPassword import ModifyPassword
from ldap3.utils.hashed import hashed
password = hashed(HASHED_SALTED_SHA, password)
# or..
password = '{SASL}theuser@domain.com'
modify = ModifyPassword(
    connection, user.entry_get_dn(), new_password=password)
resp = modify.send()
print(modify.result)
{'referrals': None, 'result': 0, 'description': 'success', 'type': 'extendedResp', 'message': '', 'responseName': None, 'new_password': None, 'dn': '', 'responseValue': None}

The description says success, but the password isn't actually changed.

I've also tried to send a modify replace message:

def modify_user_password(self, user, password):
    dn = user.entry_get_dn()
    hashed_password = hashed(HASHED_SALTED_SHA, 'MyStupidPassword')
    changes = {
        'userPassword': [(MODIFY_REPLACE, [hashed_password])]
    }
    logger.debug('dn: ' + dn)
    logger.debug('changes: ' + str(changes))
    success = self.engage_conn.modify(dn, changes=changes)
    if success:
        logger.debug('Changed password for: %s', dn)
        print(self.engage_conn.result)
    else:
        logger.warn('Unable to change password for %s', dn)
        logger.debug(str(self.engage_conn.result))
        raise ValueError('stop')

The connection is not an SSL connection. The answer to the AD question requires that the connection be over SSL. Is this also a requirement for OpenLDAP?

Edit:

After changing the dn to user.entry_get_dn() the code seemed to work about 90% of the time. After running these tests again today it appears that it now works consistently. I'm going to chalk this up to not viewing fresh data in my directory browser.

Community
  • 1
  • 1
Josh Smeaton
  • 47,939
  • 24
  • 129
  • 164
  • You should not hash the oassword yourself. Let the server do it. – user207421 Sep 06 '16 at 00:44
  • @EJP if I let the server try to take care of it the result is a plain text password. There may be server side settings to fix this, but it's not enabled in my situation. – Josh Smeaton Sep 06 '16 at 02:42
  • Then you've misconfigured the server. – user207421 Sep 06 '16 at 03:53
  • @EJP Possibly - it's not a server I'm configuring. One of our requirements is to be able to set the password to `{SASL}username@domain.com` for delegated authentication. Would the server hashing prevent this from working correctly? I'll raise this with my coworker configuring the server so we can test regardless. Thanks. – Josh Smeaton Sep 06 '16 at 04:08
  • You need to review the requirement. The server should be responsible for all hashing and all password comparisons. That's what it's for, and that's what it's good at. This function need not and should not be distributed across clients. – user207421 Sep 06 '16 at 10:23
  • We're hosting what is essentially an LDAP proxy. We synchronise the usernames, and use SASL to authenticate against a specific backend (our customers LDAP). We neither host nor control the password. **A** server is responsible for comparing, it's just not ours. What I'm implementing here is a workaround for when we lose connection to their site in BCP scenarios. Set a temporary password locally so they can get working, then flip back to SASL once normal operation is restored. – Josh Smeaton Sep 06 '16 at 11:11

1 Answers1

1

Changing the password seems to work as described in the docs and shown in the edit of my question above. For future reference, this code seems to work:

from ldap3 import (
    HASHED_SALTED_SHA, MODIFY_REPLACE
)
from ldap3.utils.hashed import hashed

def modify_user_password(self, user, password):
    dn = user.entry_get_dn()
    hashed_password = hashed(HASHED_SALTED_SHA, password)
    changes = {
        'userPassword': [(MODIFY_REPLACE, [hashed_password])]
    }
    success = self.connection.modify(dn, changes=changes)
    if not success:
        print('Unable to change password for %s' % dn)
        print(self.connection.result)
        raise ValueError('Unable to change password')

To clarify a few things:

  1. This is connecting to an OpenLDAP server (with multiple databases)
  2. There is NO SSL here. We plan on implementing SSL but this works without it.
Josh Smeaton
  • 47,939
  • 24
  • 129
  • 164
  • 3
    Ldap3 includes a specific function to change the userPassword. Look at the connection.extend.standard.modify_password() method. – cannatag Oct 29 '16 at 14:20