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.
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:
This is connecting to an OpenLDAP server (with multiple databases)
There is NO SSL here. We plan on implementing SSL but this works without it.
Related
I am creating a simple python function to change the user password. I have tested my AD set up, able to search the user and get correct response but when try to run l.modify_s, I get the below error. AD user has the required permissions. Not sure why am I getting this error.
Any help will be great. Please let me know if you need any more information or code as well to understand the issue better.
"errorType": "**UNWILLING_TO_PERFORM**",
"errorMessage": "{'info': u'0000001F: SvcErr: DSID-031A12D2, problem 5003 (WILL_NOT_PERFORM), data 0\\n', 'msgid': 3, 'msgtype': 103, 'result': 53, 'desc': u'Server is unwilling to perform', 'ctrls': []}"
}```
Please find my code below
``` import ldap
import os
import boto3
import random
import string
from base64 import b64decode
import ldap
def lambda_handler(event, context):
try:
cert = os.path.join('/Users/marsh79/Downloads', 'Serverssl.cer')
print "My cert is", cert
# LDAP connection initialization
l = ldap.initialize('ldap://example.corp.com')
# Set LDAP protocol version used
l.protocol_version = ldap.VERSION3
#Force cert validation
l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
# Set path name of file containing all trusted CA certificates
l.set_option(ldap.OPT_X_TLS_CACERTFILE, cert)
# Force libldap to create a new SSL context (must be last TLS option!)
l.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
bind = l.simple_bind_s("admin#corp.example.com", "secret_pass")
base = "OU=Enterprise,OU=Users,OU=corp,DC=corp,DC=example,DC=com"
criteria = "(objectClass=user)"
attributes = ['distinguishedName']
result = l.search_s(base, ldap.SCOPE_SUBTREE, criteria, attributes)
results = [entry for dn, entry in result if isinstance(entry, dict)]
new_password='secretpass_new'
unicode_pass = unicode('\"' + new_password + '\"', 'iso-8859-1')
password_value = unicode_pass.encode('utf-16-le')
add_pass = [(ldap.MOD_REPLACE, 'unicodePwd', [password_value])]
print "My result distinguishedName1:", results[0]['distinguishedName'][0]
print "My result distinguishedName2:", results[1]['distinguishedName'][0]
l.modify_s(results[0]['distinguishedName'][0],add_pass)
print results
finally:
l.unbind()
I have checked multiple things
Password complexity is good
Enabled secured ldap on my AD server and tested this using ldp.exe and I can connect using port 636
I am able to run this code if I just need to search the user. I get the search results.
But when I try to modify the password, it breaks and my head is just throwing up to work out where it is going wrong :X
I'm not a Python programmer, but I know how AD and LDAP works. It's probably still not connected via LDAPS. From examples I've seen online, you might need to specify ldaps://:
l = ldap.initialize('ldaps://<server name>.corp.example.com')
Or possibly the port as well:
l = ldap.initialize('ldaps://<server name>.corp.example.com:636')
You don't need to supply the cert file on the client side, but the issuer of the certificate on the server must be trusted by the client computer. I guess that's what you're trying to do with cert. But you may not have to. Try without that and see what happens. If you're running this on Windows, it may use the Trusted Certificate Store from Windows itself and it should work as long as the server isn't using a self-signed cert.
I'm trying to access the social site, minds.com via this python api by by installing the module locally with python3 setup.py install && pipenv run python and following the instructions to log in.
However, I get this error message when trying to authenticate:
(For some reason, Stackoverflow doesn't allow me to post the python error-log becuse it isn't indented properly, so here it is https://pastebin.com/0sWa1hmY)
The code, which seems to be called from the python api looks like this:
minds/api.py
# -*- coding: utf-8 -*-
from pprint import pprint
from requests.utils import dict_from_cookiejar, cookiejar_from_dict
from minds.connections import XSRFSession
from minds.exceptions import AuthenticationError
from minds.profile import Profile
from minds.utils import add_url_kwargs
from minds.endpoints import *
from minds.sections import NewsfeedAPI, ChannelAPI, NotificationsAPI,
PostingAPI, InteractAPI
class Minds(NewsfeedAPI, ChannelAPI, NotificationsAPI, PostingAPI, ...):
_xsrf_retries = 5
def __init__(self, profile: Profile = None, login=True):
self.con = XSRFSession()
self.profile = profile
if self.profile:
if profile.cookie:
self.con.cookies = cookiejar_from_dict(profile.cookie)
if profile.proxy:
self.con.proxies = {'https': profile.proxy, 'http':\
profile.proxy}
self._establish_xsrf()
if self.profile and login and not self.is_authenticated:
if profile.username and profile.password:
self.authenticate(profile.username, profile.password)
(...)
def authenticate(self, username, password, save_profile=True) -> dict:
"""
Authenticate current instance with given user
:param: save_profile: whether to save profile locally
"""
auth = {
'username': username,
'password': password
}
resp = self.con.post(AUTHENTICATE_URL, json=auth)
self.user = resp.json()
if self.user['status'] == 'failed':
raise AuthenticationError("Couldn't log in with the ...")
self.guid = self.user['user']['guid']
if save_profile:
Profile(
username=username,
password=password,
cookie=self.get_cookies(),
proxy=self.con.proxies.get('https'),
).save()
return resp.json()
The python API doesn't seem to be maintained, but I think minds.com newly uses jsonwebtokens for authentication. Is the something missing from the api to be jwt-enabled?
After compare to browser request again and again. The reason why occurred error is you gave a wrong content-type, but actually you need to send as json data (it seems website is making joke). So you need to specify headers["content-type"] = "text/plain". Otherwise website will response 500.
Note: To avoid broad discussion, i can only answer this error.
Change the code to below, but there is only one line differs from source code :).
def authenticate(self, username, password, save_profile=True) -> dict:
"""
Authenticate current instance with given user
:param: save_profile: whether to save profile locally
"""
auth = {
'username': username,
'password': password
}
self.con.headers["content-type"] = "text/plain" ####
resp = self.con.post(AUTHENTICATE_URL, json=auth)
self.user = resp.json()
if self.user['status'] == 'failed':
raise AuthenticationError("Couldn't log in with the given credentials")
self.guid = self.user['user']['guid']
if save_profile:
Profile(
username=username,
password=password,
cookie=self.get_cookies(),
proxy=self.con.proxies.get('https'),
).save()
return resp.json()
I'm on the very last section of my password changer, and for some reason it just won't change the password. It's connecting to the AD server fine (checked event logs), there are no errors when trying it, but for some reason the password won't actually change.
Here's the connection code:
server= Server("DCNAME", port = 636, use_ssl = True)
connection= Connection(server, user='DOMAIN\\USER', password='PASSWORD', authentication=NTLM , auto_bind=True)
And here's the password changing code:
dn = "cn = {0}, ou= Users, dc=DC, dc=local".format(user_name.get())
connection.extend.microsoft.modify_password(dn, new_password=user_password.get())
All together should work like this:
User inputs email --> Sent otp --> Put in username (stored in user_name entry in tkinter) --> Enter password sent to their email (stored in user_password entry in tkinter) --> change password
Does anyone know why it won't change the password in AD?
Thanks in advance!
EDIT: Just added the ssl encryption when connecting to the server, but still not changing password
EDIT2: Made it print the connection results and get this back:
{'result': 32, 'description': 'noSuchObject', 'dn': 'OU=Users,DC=DC,DC=local', 'message': "0000208D: NameErr: DSID-0310020A, problem 2001 (NO_OBJECT), data 0, best match of:\n\t'OU=Users,DC=DC,DC=local'\n\x00", 'referrals': None, 'type': 'modifyResponse'}
Am I right in saying it's completely ignoring the CN?
Have you tried without the blanks after the equal signs in the dn?
.
I have a Windows 2012 R2 server and a LDAP server on it. I wrote a python script to modify the password of user (the user, who isn't admin, want to modify is own password. I have an other function which modify the password when you're admin, but I don't want to set a password, but modify it). This is a sample of my code :
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
ld = ldap.initialize('ldaps://XXX:636')
ld.simple_bind_s('XXXXXXXX#ad2012.local', 'ZZZZZ')
new = {'unicodePwd':[str('"YYYYY"').decode('utf8').encode('utf-16-le')]}
old = {'unicodePwd':[str('"ZZZZZ"').decode('utf8').encode('utf-16-le')]}
ldif = modlist.modifyModlist(old, new)
ld.modify_s('A DN',ldif)
But when I run it, I have an error (on the last line):
{'info': '00000056: AtrErr: DSID-03191083, #1:\n\t0: 00000056: DSID-03191083, problem 1005 (CONSTRAINT_ATT_TYPE), data 0, Att 9005a (unicodePwd)\n', 'desc': 'Constraint violation'}
I checked the error :
"The specified network password is not correct."
But, I use the same password to connect to user to change it.
I tried with(out) double quote, with(out) utf-8, enter a real bad old password... but nothing change, I always have the same error
If someone could help me, thanks in advance.
I achieved to change the password, here is the code :
#!/usr/bin/env python
#coding:utf-8
import ldap
import ldap.modlist as modlist
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
ld = ldap.initialize('ldaps://XXX:636')
ld.simple_bind_s('XXX#ad2012.local', 'XXX')
newpassword = 'YYY'
oldpassword = 'ZZZ'
newpassword = unicode('\"' + newpassword + '\"').encode('utf-16-le')
oldpassword = unicode('\"' + oldpassword + '\"').encode('utf-16-le')
pass_mod = [(ldap.MOD_DELETE, 'unicodePwd', [oldpassword]), (ldap.MOD_ADD, 'unicodePwd', [newpassword])]
result = ld.modify_s('A DN', pass_mod)
I hope it will help someone ! :D
I'm trying to update some code to python3, using ldap3 version '0.9.7.4'.
(https://pypi.python.org/pypi/ldap3)
Previously, I used python-ldap with python2 to authenticate a user like this:
import ldap
address = "ldap://HOST:389"
con = ldap.initialize(address)
base_dn = "ourDN=jjj"
con.protocol_version = ldap.VERSION3
search_filter = "(uid=USERNAME)"
result = con.search_s(base_dn, ldap.SCOPE_SUBTREE, search_filter, None)
user_dn = result[0][0] # get the user DN
con.simple_bind_s(user_dn, "PASSWORD")
This properly returns (97, [], 2, []) on correct password, and raises ldap.INVALID_CREDENTIALS on a bind attempt using an incorrect password.
Using ldap3 in python3 I'm doing the following:
from ldap3 import Server, Connection, AUTH_SIMPLE, STRATEGY_SYNC, ALL
s = Server(HOST, port=389, get_info=ALL)
c = Connection(s, authentication=AUTH_SIMPLE, user=user_dn, password=PASSWORD, check_names=True, lazy=False, client_strategy=STRATEGY_SYNC, raise_exceptions=True)
c.open()
c.bind()
It's raising the following exception:
ldap3.core.exceptions.LDAPInvalidCredentialsResult: LDAPInvalidCredentialsResult - 49 - invalidCredentials - [{'dn': '', 'message': '', 'type': 'bindResponse', 'result': 0, 'saslCreds': 'None', 'description': 'success', 'referrals': None}]
I'm using the user_dn value returned by python2's ldap search, since this appears to be working in python2.
How can I get this to bind properly using ldap3 in python3?
(One thing strange, I noticed, is that the ldap3's LDAPInvalidCredentialsResult includes 'description': 'success'. I'm guessing this just means response successfully recieved...)
I'm the author of ldap3, please set raise_exceptions=False in the Connection definition and check the connection.result after the bind. You should get the reason why your bind() is unsuccessful.
Confirm that your DN doesn't need to escape a comma using backslash \.
My organization gives users a CN of "last name, first name", so my DN needed to be "CN=Doe\, Jane, OU=xyz, ..., DC=abc, DC=com"
I realized this by using Active Directory Explorer to navigate to my user object, r-click > view properties to see the distinguished name. I ran into this invalid credential error when using the DN that AD Explorer displays in its Path breadcrumb which omits the escape character.