How to bind (authenticate) a user with ldap3 in python3 - python

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.

Related

Error - UNWILLING_TO_PERFORM - while change user password in AD ldap using python code

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.

How to properly deal with Celery and Azure Storage queues containing a '/' character in the access key?

I am creating a Celery application using the syntax
celery_app = Celery(
my_config_name,
backend=my_backend,
broker=my_broker,
)
For my_broker, I am using the address of an Azure storage account that reads
my_broker = "azurestoragequeues://:jrIEoHgyi7y8L7dJ+0CeYGnR9rHLwzMRsKzrmUle7ZL8OA/EaO5aBxqEYSB2VPXJ2v4C58D==#myazurequeue"
and note that the password bit contains a '/' (that's the corresponding access key that can be read also in the Azure portal).
If I run my code, I get
ValueError: invalid literal for int() with base 10: 'jrIEoHgyi7y8L7dJ+0CeYGnR9rHLwzMRsKzrmUle7ZL8OA'
which is the same error that I get with
from kombu.utils.url import parse_url
parse_url(my_broker)
One fix that seems to work is something along the lines of
from kombu.utils.url import safequote
my_broker_fixed = "azurestoragequeues://:" + safequote(str.replace(my_broker, "azurestoragequeues://:", ""), safe = "=#")
which can encode the '/' character in the access key only (but not in the rest of the address).
This also parses fine when firing parse_url(my_broker) as it yields
{'hostname': 'myazurequeue',
'password': 'jrIEoHgyi7y8L7dJ+0CeYGnR9rHLwzMRsKzrmUle7ZL8OA/EaO5aBxqEYSB2VPXJ2v4C58D==',
'port': None,
'transport': 'azurestoragequeues',
'userid': None,
'virtual_host': None}
This seems to be OK as a workaround, but is there a better fix?
The password is the part of the Transport connection that needs to be safely quoted.
Store the username, password, hostname for the connection as environment variables and read those in your application.
from kombu.utils.url import safequote
TRANSPORT_USER = os.getenv('TRANSPORT_USER'),
TRANSPORT_PASS = safequote(os.getenv('TRANSPORT_PASS')),
TRANSPORT_HOST = os.getenv('TRANSPORT_HOST')
my_broker = f'azurestoragequeues://{TRANSPORT_USER}:{TRANSPORT_PASS}#{TRANSPORT_HOST}'

Password Change - LDAP3 - Python - Raspberry Pi

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?
.

Adding AD group with python-ldap

I am trying to create group within AD with no success, below is my code:
import ldap
import ldap.modlist as modlist
LDAPserver = 'hidden'
LDAPlogin = 'hidden'
LDAPpassword = 'hidden'
l = ldap.initialize('ldap://' + LDAPserver)
l.simple_bind_s(LDAPlogin, LDAPpassword)
dn = 'OU=someunit,OU=someunit,OU=someunit,OU=someunit,DC=my-company,DC=local'
attrs = {}
attrs['objectclass'] = ['top','Group']
attrs['cn'] = 'group name to be created'
attrs['description'] = 'Test Group'
ldif = modlist.addModlist(attrs)
l.add_s(dn,ldif)
l.unbind_s()
Following snippet gives me an error:
ldap.INSUFFICIENT_ACCESS: {'info': '00000005: SecErr: DSID-031521D0, problem 4003
(INSUFF_ACCESS_RIGHTS), data 0\n', 'desc': 'Insufficient access'}
However, using same credentials I can create group with some UI tools like LDAP Admin
so I suppose that I have proper permissions to create groups, but still no success with python-ldap.
I can also query existing groups and fetch its members via script.
I believe that the problem is in my attributes, maybe Active Directory need some different values to be inserted into attrs variable. AD is running under Win Server 2012 R2.
Any help would be appreciated :)
The dn should actually be CN=<groupname> + base_dn, so in your case something like
dn = 'CN=groupname,OU=someunit,OU=someunit,OU=someunit,OU=someunit,DC=my-company,DC=local'
Please try to replace LDAPlogin to a full bind dn value such as
"cn=hidden,dc=example,dc=com"

Changing userPassword in OpenLDAP using ldap3 library

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.

Categories

Resources