So, I am trying to run a search with ldap, and trying to check if I am able to initialize it first. However, I keep getting the error {'desc': 'No such object'} I even tried wrong credentials but I don't even get to that error message. If someone could shed light on this, it'd be really helpful. Thanks in advance!
#!/usr/bin/python
import os, os.path
import subprocess as sp
import ldap
l = ldap.initialize('ldap://ldap.domain.com')
username = "uid=%s,ou=People,dc=domain,dc=com"
password = "password"
try:
l.protocol_version = ldap.VERSION3
l.simple_bind_s(username, password)
valid = True
except Exception, error:
print error
except ldap.INVALID_CREDENTIALS:
print "Your username or password is incorrect."
sys.exit(0)
I was getting the same error.
A quick and dirty fix to successfully bind is to change the username variable:
username = "user#domain.com"
the problem with your variable is that the %s is a string formatting syntax (which it borrows from C).
the proper use of that variable would look something like:
username = "uid=%s,ou=People,dc=domain,dc=com" %('insert-username-here')
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 have a list of emails(mine) that I want to test against a list of passwords(All valid and some none valid of course) using imaplib library. Whenever I test the program ordinarily like in the code below, it works perfectly no errors.
import sys
import imaplib
# connect to host using SSL
imap_host = 'imap.server.com'
imap_port = '993'
imap_user = 'username#email'
imap_pass = 'RightPassword'
imap = imaplib.IMAP4_SSL(imap_host, imap_port)
## login to server
try:
login = imap.login(imap_user, imap_pass)
if login:
print login
except imaplib.IMAP4.error as error:
print error
#
But whenever I run the code such as to parsing credentials through a function to handle the authentication protocols such as the following code below, I get an error saying
"LOGIN command error: BAD ['Missing \'"\'']".
I have tried all sort of things I could find using google and non seem to handle it properly.
"""
E-mail Tester
NB: This is for educational purpose only.
"""
import sys
import imaplib
EMAILS_FILE = open('email_list.txt', 'r')
PASSWORD_FILE = open('pass_list.txt', 'r')
SUCCESS_FILE = open('success.txt', 'a')
EMAILS_FILE_LIST = []
def set_check(_emails):
email = str(_emails)
PASSWORD_FILE.seek(0)
for passwords in PASSWORD_FILE:
password = str(passwords)
# connect to host using SSL
imap_host = 'imap.server.com'
imap_port = '993'
imap = imaplib.IMAP4_SSL(imap_host, imap_port)
## login to server
try:
# print "%s%s" % (email,password)
# print "I got here so far"
# sys.exit()
print "Testing <--> E-mail: %s - Password: %s" % (email, password)
login = imap.login("%s","%s" % (email, password))
if login:
print login
print "OK <---> E-mail: %s\nPassword: %s" % (email, password)
except imaplib.IMAP4.error as error:
print error
for emails in EMAILS_FILE:
EMAILS_FILE_LIST.append(emails)
for email_count in range(0, len(EMAILS_FILE_LIST)):
set_check(EMAILS_FILE_LIST[email_count])
I have tried all kind of suggestions I could find on the internet but non has worked thus far.
I expect imap.login to handle the authentication without the mysterious error output
"LOGIN command error: BAD ['Missing \'"\'']"
login = imap.login("%s","%s" % (email, password))
does not work. It throws an error in Python: TypeError: not all arguments converted during string formatting, because you're providing two strings to one %s.
Why don't you just use imap.login(email, password)? It has the same effect as what you're trying to do.
And what does your password file look like? What is it actually sending? Please provide the log line before it crashes. (anonymizing if necessary, but leaving any punctuation in for help diagnosing)
Okay, so I actually got this fixed by removing trail lines from my strings.
email = str(_emails).rstrip()
PASSWORD_FILE.seek(0)
for passwords in PASSWORD_FILE:
password = str(passwords).rstrip()
the error is caused by trail lines in the strings.
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 have modified the example python script:
service = 'passwd'
if len(sys.argv) == 3:
user = sys.argv[1]
password = sys.argv[2]
else:
print 'error'
auth = PAM.pam()
auth.start(service)
if user != None:
auth.set_item(PAM.PAM_USER, user)
auth.set_item(PAM.PAM_CONV, pam_conv)
try:
auth.authenticate()
auth.acct_mgmt()
except PAM.error, resp:
print 'Go away! (%s)' % resp
except:
print 'Internal error'
else:
print 'Good to go!'
This works, but asks me to input the password. I would like instead to verify the password which is passed as a parameter (sys.argv[2]). Documentation is non-existant, so how should I do it?
The example first lines are missing.
The provided 'pam_conv' function asks the password to the user.
You must define your own function returning a constant password:
def pam_conv(auth, query_list, userData):
return [(the_password,0)]
When I was finding solution for interactive password prompt, I only found this solution
python expect lib
I want to execute the following query in the ldap
ldapsearch -h hostname -b dc=ernet,dc=in -x "(&(uid=w2lame)(objectClass=posixAccount))" gidnumber
ldapsearch -h hostname -b dc=ernet,dc=in -x "(&(gidNumber=1234)(objectClass=posixGroup))" cn
And use the variables thus obtained. How can I do that?
While the accepted answer does in fact show a proper way to bind to an LDAP server I do feel it didn't answer the question holistically. Here is what I ended up implementing to grab the mail and department of a user. This somewhat blends the required attributes from the original question.
l = ldap.initialize('ldap://ldap.myserver.com:389')
binddn = "cn=myUserName,ou=GenericID,dc=my,dc=company,dc=com"
pw = "myPassword"
basedn = "ou=UserUnits,dc=my,dc=company,dc=com"
searchFilter = "(&(gidNumber=123456)(objectClass=posixAccount))"
searchAttribute = ["mail","department"]
#this will scope the entire subtree under UserUnits
searchScope = ldap.SCOPE_SUBTREE
#Bind to the server
try:
l.protocol_version = ldap.VERSION3
l.simple_bind_s(binddn, pw)
except ldap.INVALID_CREDENTIALS:
print "Your username or password is incorrect."
sys.exit(0)
except ldap.LDAPError, e:
if type(e.message) == dict and e.message.has_key('desc'):
print e.message['desc']
else:
print e
sys.exit(0)
try:
ldap_result_id = l.search(basedn, searchScope, searchFilter, searchAttribute)
result_set = []
while 1:
result_type, result_data = l.result(ldap_result_id, 0)
if (result_data == []):
break
else:
## if you are expecting multiple results you can append them
## otherwise you can just wait until the initial result and break out
if result_type == ldap.RES_SEARCH_ENTRY:
result_set.append(result_data)
print result_set
except ldap.LDAPError, e:
print e
l.unbind_s()
You probably want to use the ldap module. Code would look something like:
import ldap
l = ldap.initialize('ldap://ldapserver')
username = "uid=%s,ou=People,dc=mydotcom,dc=com" % username
password = "my password"
try:
l.protocol_version = ldap.VERSION3
l.simple_bind_s(username, password)
valid = True
except Exception, error:
print error
Here's an example generator for python-ldap.
The ldap_server is the object you get from ldap.initialize(). You will probably need to bind before calling this function, too, depending on what LDAP server you are using and what you are trying to query for. The base_dn and filter_ are similar to what you've got in your command line version. The limit is the maximum number of records returned.
def _ldap_list(ldap_server, base_dn, filter_, limit=0):
""" Generator: get a list of search results from LDAP asynchronously. """
ldap_attributes = ["*"] # List of attributes that you want to fetch.
result_id = ldap_server.search(base_dn, ldap.SCOPE_SUBTREE, filter_, ldap_attributes)
records = 0
while 1:
records += 1
if limit != 0 and records > limit:
break
try:
result_type, result_data = ldap_server.result(result_id, 0)
except ldap.NO_SUCH_OBJECT:
raise DirectoryError("Distinguished name (%s) does not exist." % base_dn)
if result_type == ldap.RES_SEARCH_ENTRY:
dn = result_data[0][0]
data = result_data[0][1]
yield dn, data
else:
break
Please keep in mind that interpolating user-provided values into your LDAP query is dangerous! It's a form of injection that allows a malicious user to change the meaning of the query. See: http://www.python-ldap.org/doc/html/ldap-filter.html
I cobbled this together this morning while skimming through the documentation of the ldap module. It can fulfil the requirements of the OP changing the filter and the other settings to his liking.
The documentation of the ldap module is pretty good if you understand the context (that's what took me a while). And the module is surprinsingly easy to use. We have a similar script written in bash using ldapserach that is at least 3 or 4 times longer and more complex to read.
This code accepts a partial search string (email, name, uid or part of it) and returns the results in LDIF format. The idea is to make it very simple to use for a very specific task and if possible without using flags so that my less skilled co-workers can find the relevant info quickly.
Note that this is written for an LDAP server that runs on a machine that is not accessible from outside our internal network and which is secured with 2FA authentication. It can, thus, safely accept anonymous queries. But adding user and password should be trivial.
#! /usr/bin/python3
### usearch
### searches in the LDAP database for part of a name, uid or email and returns mail, uid, and full name
import ldap
import argparse
import sys
import ldif
l = ldap.initialize('ldaps://your.fancy.server.url', bytes_mode=False)
basedn = "dc=foo,dc=bar,dc=baz"
## ARGPARSE stuff!!!
parser=argparse.ArgumentParser(
description ='searches the LDAP server',
usage='usearch PARTIAL_MATCH (email, name, username)',
formatter_class = argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('istr', help='searches stuffz')
parser.print_help
args = parser.parse_args(None if sys.argv[1:] else ['-h'])
str1 = args.istr
sfilter = "(|(sn=*{}*)(mail=*{}*)(uid=*{}*))".format(str1,str1,str1)
attributes = ["mail","uid","cn"]
scope = ldap.SCOPE_SUBTREE
r = l.search_s(basedn,scope,sfilter,attributes)
ldif_writer=ldif.LDIFWriter(sys.stdout)
for dn, entry in r:
ldif_writer.unparse(dn,entry)
And as I was at it, here a version with the ldap3 module. The argparse part is copy-pasted. This time the output is "human readable", instead of LDIF:
#! /usr/bin/python3
## usearch3
## LDAP3 version
import ldap3
import argparse
import sys
server = ldap3.Server('ldaps://foo.bar.baz')
conn = ldap3.Connection(server)
conn.bind()
basedn = 'dc=foobar,dc=dorq,dc=baz'
attribs = ['mail','uid','cn']
parser=argparse.ArgumentParser(
description ='searches the LDAP server and returns user, full name and email. Accepts any partial entry',
usage='usearch3 PARTIAL_MATCH (email, name, username)',
formatter_class = argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('istr', help='searches stuffz')
parser.print_help
args = parser.parse_args(None if sys.argv[1:] else ['-h'])
str1 = args.istr
sfilter = "(|(sn=*{}*)(mail=*{}*)(uid=*{}*))".format(str1,str1,str1)
conn.search(basedn,sfilter)
conn.search(basedn,sfilter,attributes = attribs)
leng = len(conn.entries)
for i in range(leng):
user = conn.entries[i].uid
fullname = conn.entries[i].cn
email = conn.entries[i].mail
print("user:\t{}\nname:\t{}\nemail:\t{}\n\n".format(user,fullname,email))
you can use the commands module, and the getoutput to parse the result of the ldap query:
from commands import getoutput
result = getoutput('ldapsearch -h hostname -b dc=ernet,dc=in -x "(&(uid=w2lame)(objectClass=posixAccount))"')
print result
you have to have ldapsearch binary installed in your system.