Basically trying to reset the user's password using LDAP python. I've gone through various posts here but no luck :(.
Tried using :
a) modify_s() - returns "No such object" every time. Tried with different user DN.
{'info': "0000208D: NameErr: DSID-0310020A, problem 2001 (NO_OBJECT), data 0, best match of:\n\t'DC=mydomain,DC=com'\n", 'matched': 'DC=mydomain,DC=com', 'desc': 'No such object'}
Here is the code Snippet:
def changePassword(userEmail, oldPassword, newPassword):
try:
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
ldap_client = ldap.initialize("ldap://127.0.01.1:389")
ldap_client.set_option(ldap.OPT_REFERRALS, 0)
ldap_client.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
ldap_client.set_option(ldap.OPT_X_TLS,ldap.OPT_X_TLS_DEMAND)
ldap_client.set_option( ldap.OPT_X_TLS_DEMAND, True )
ldap_client.set_option( ldap.OPT_DEBUG_LEVEL, 255 )
ldap_client.simple_bind_s(ADMIN_EMAIL, ADMIN_PASSWORD)
# Set AD password
#unicode_pass = unicode('\"' + newPassword + '\"', "iso-8859-1")
unicode_pass = newPassword
password_value = unicode_pass.encode("utf-16-le")
add_pass = [(ldap.MOD_REPLACE, 'unicodePwd', [password_value]),( ldap.MOD_REPLACE, 'unicodePwd', [password_value])]
# Replace password
try:
user_dn = 'CN=%s,DC=mydomain,DC=com' % username
ldap_client.modify_s(user_dn, add_pass)
print "Active Directory password for", username, \
"was set successfully!"
except ldap.LDAPError, e:
sys.stderr.write('Error setting AD password for: ' + username + '\n')
sys.stderr.write('Message: ' + str(e) + '\n')
ldap_client.unbind_s()
return 'SOME_PROBLEM'
ldap_client.unbind_s()
return 'AUTHENTICATED'
except ldap.INVALID_CREDENTIALS:
ldap_client.unbind()
return 'INVALID_CREDENTIALS'
except ldap.SERVER_DOWN:
return 'SERVER_UNAVAILABLE'
b) passwd(userEmail, oldPassword, newPassword). It gets executed well but password is not updated.
Need help in identifying the problem.
Reference Links:
Python+LDAP+SSL
python-ldap and Microsoft Active Directory: connect and delete user
how to set lockoutTime and password of a user of Active Directory
How can I change password for domain user(windows Active Directory) using Python?
https://groups.google.com/forum/#!topic/macromedia.coldfusion.security/Rq7xx15OeBs
http://www.grotan.com/ldap/python-ldap-samples.html#add
http://marcitland.blogspot.in/2011/02/python-active-directory-linux.html
https://snipt.net/Fotinakis/change-active-directory-password-via-ldap-modify-call/
I think below program helpful for you.. windows active directory use password attribute as unicode method
https://technet.microsoft.com/en-us/magazine/ff848710.aspx
import ldap
import ldap.modlist as modlist
import base64
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
l = ldap.initialize('ldaps://exam.local')
l.simple_bind_s('Administrator#exam.local', 'p#ssw0rd1')
dn="cn=map6,ou=Police,dc=exam,dc=local"
new_password='p#ssw0rd3'
unicode_pass = unicode('\"' + new_password + '\"', 'iso-8859-1')
print (unicode_pass)
password_value = unicode_pass.encode('utf-16-le')
add_pass = [(ldap.MOD_REPLACE, 'unicodePwd', [password_value])]
print (password_value)
l.modify_s(dn, add_pass)
l.modify_s(dn, add_pass)
l.unbind_s()
I had the very same issue and decided to ask on Server Fault. The answer I got helped me to figure out what was wrong in my code. To summarize, there is 2 diifferent methods to update an AD password: 1 for regular user updating his own password, and another 1 for administrator (or any account with sufficient access rights) resetting the password for another user.
Method 1: User update his own password
ad_server = "ldaps://ad.xxx_domain.com"
ad_dn = "CN={0},OU=Users,OU=AF,DC=xxx_domain,DC=com"
username = 'my_username'
old_pwd = 'the_old_pa55word'
new_pwd = 'the_new_pa55word'
cert = os.path.join('/path', "to", 'server_cert.cer')
# LDAP connection initialization
l = ldap.initialize(ad_server)
# 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(ad_dn.format(username), old_pwd)
# Now, perform the password update
oldpwd_utf16 = '"{0}"'.format(old_pwd).encode('utf-16-le')
newpwd_utf16 = '"{0}"'.format(new_pwd).encode('utf-16-le')
mod_list = [
(ldap.MOD_DELETE, "unicodePwd", oldpwd_utf16),
(ldap.MOD_ADD, "unicodePwd", newpwd_utf16),
]
l.modify_s(ad_dn.format(username), mod_list)
Method 2: Admin account update regular user's password
ad_server = "ldaps://ad.xxx_domain.com"
ad_dn = "CN={0},OU=Users,OU=AF,DC=xxx_domain,DC=com"
admin_username = "i_am_the_admin"
admin_password = "admin123"
username = 'my_username'
new_pwd = 'the_new_complicated_password'
cert = os.path.join('/path', "to", 'server_cert.cer')
# LDAP connection initialization
l = ldap.initialize(ad_server)
# 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 (as admin user)
l.simple_bind_s(ad_dn.format(admin_username), admin_password)
# Now, perform the password update
newpwd_utf16 = '"{0}"'.format(new_pwd).encode('utf-16-le')
mod_list = [
(ldap.MOD_REPLACE, "unicodePwd", newpwd_utf16),
]
l.modify_s(ad_dn.format(username), mod_list)
Please note that the second method needs to Bind with a different account (with sufficient rights) but allows to set the new password without re-typing the old one.
From what I can see is that you user_dn is not correctly set. Double check and ensure you full DN actually exists in the Directory Server. Check your username variable is correctly parsed (No newline or tab characters) and the Base DN is verified.
sys.stderr.write('Error setting AD password for: ' + username + '\n')
sys.stderr.write('DN: ' + user_dn + '\n')
sys.stderr.write('Message: ' + str(e) + '\n')
The error message is pretty clear that AD cannot find the object (DN) it wishes to modify **(NO_OBJECT)
{'info': "0000208D: NameErr: DSID-0310020A, problem 2001 (NO_OBJECT),
data 0, best match of:\n\t'DC=mydomain,DC=com'\n", 'matched':
'DC=mydomain,DC=com', 'desc': 'No such object'}
Related
I am working on adding agents to our RSA Authentication Manager (8.X) via PowerShell using the RSA Authentication Manager SDK. I am able to query existing agents without issue using the following code:
$searchAgent = New-Object rsaapi.SearchAgentsCommand
$searchAgent = ($CommandServer.executeCommand($loginCommand.sessionId,$searchAgent)).agents
However, when trying to add a new agent, I get "Cannot convert argument "in1", with value: "rsaapi.AgentDTO", for "executeCommand" to type "rsaapi.TargetableCommand": "Cannot convert the "rsaapi.AgentDTO" value of type "rsaapi.AgentDTO" to type "rsaapi.TargetableCommand"."" returned from the last line. Here is my code snippet:
$hostDTO = New-Object rsaapi.HostDTO
$hostDTO.name = $servername
$hostDTO.primaryIpAddress = $ipAddress
$agentDTO = New-Object rsaapi.AgentDTO
$agentDTO.name = $servername
$agentDTO.primaryAddress = $ipAddress
$agentDTO.agentType = 1
$agentDTO.offlineAuthDataRefreshRequired = $false
$agentDTO.restriction = $false
$agentDTO.securityDomainId = 'ims.securityDomainGUID'
$agentDTO.securityDomainName = 'securityDomainName'
$addAgent = New-Object rsaapi.AddAgentCommand
$addAgent = $CommandServer.executeCommand($loginCommand.sessionId,$agentDTO)
The AdminAPIDemo shows the Python code block below as how to add a new agent:
def createAgent(self, name, addr, alt, sdGuid):
# need a HostDTO to be set
host = HostDTO()
host.setName(name)
host.setPrimaryIpAddress(addr)
host.setSecurityDomainGuid(sdGuid)
host.setNotes("Created by AM Demo code")
# the agent to be created
agent = AgentDTO()
agent.setName(name)
agent.setHost(host)
agent.setPrimaryAddress(addr)
agent.setAlternateAddresses(alt)
agent.setSecurityDomainId(sdGuid)
agent.setAgentType(AgentConstants.STANDARD_AGENT)
agent.setRestriction(1) # only allow activated groups
agent.setEnabled(1)
agent.setOfflineAuthDataRefreshRequired(0)
agent.setNotes("Created by AM Demo code")
cmd = AddAgentCommand(agent)
try:
cmd.execute()
except DuplicateDataException:
print "ERROR: Agent " + name + " already exists."
sys.exit(2)
# return the created agents GUID for further linking
return cmd.getAgentGuid()
Any help would be greatly appreciated!!
Working code to add agents via PowerShell to the RSA Authentication Manager console:
$hostDTO = New-Object rsaapi.HostDTO
$hostDTO.name = $servername
$hostDTO.primaryIpAddress = $ipAddress
$agentDTO = New-Object rsaapi.AgentDTO
$agentDTO.name = $servername
$agentDTO.host = $hostDTO
$agentDTO.primaryAddress = $ipAddress
$agentDTO.agentType = 1
$agentDTO.offlineAuthDataRefreshRequired = $false
$agentDTO.restriction = $true
$agentDTO.securityDomainId = 'ims.securityDomainGUID'
$agentDTO.securityDomainName = 'securityDomainName'
$addAgent = New-Object rsaapi.AddAgentCommand
$addAgent.agentDTO = $agentDTO
$addAgent = $CommandServer.executeCommand($loginCommand.sessionId,$addAgent)
I'm writing a password manager program, and I need to store an encrypted string in a file. (The "service" variable) When the user needs to retrieve the information, they enter the same string as before. When I encrypt the same string, I get a different output. I've checked multiple times, and the string is the same. Anyone know of a fix for this?
Code I'm using to encrypt and write to the file:
def new_account(service, username, password, filepath, key):
# Encrypting the account details #
encrypted_service = key.encrypt(bytes(service, "utf-8"))
encrypted_username = key.encrypt(bytes(username, "utf-8"))
encrypted_password = key.encrypt(bytes(password, "utf-8"))
encrypted_service = encrypted_service.decode("utf-8")
encrypted_username = encrypted_username.decode("utf-8")
encrypted_password = encrypted_password.decode("utf-8")
password_file = open(f"{filepath}\passwords.txt", "a")
password_file.close()
password_file = open(f"{filepath}\passwords.txt", "r")
password_file_content = password_file.read()
password_file.close()
password_file = open(f"{filepath}\passwords.txt", "a")
if f"{encrypted_service},{encrypted_username},{encrypted_password}" in password_file_content:
password_file.close()
return("The account already exists.")
else:
password_file.write(f"{encrypted_service},{encrypted_username},{encrypted_password}\n")
password_file.close()
return f"Account saved for {service.title()} with username {username} and password {password}."
Code I'm using to retrieve the information:
# The account retrieval function #
def get_account(service, filepath, key):
encrypted_service = key.encrypt(bytes(service, "utf-8"))
encrypted_service = encrypted_service.decode("utf-8")
password_file = open(f"{filepath}\passwords.txt")
lines = password_file.read().split("\n")
word = f"{encrypted_service}"
# Getting the line with the account details #
for i in lines:
index = lines.index(i)
myline = lines[index]
encrypted_account_content = myline.split(",")
print(encrypted_account_content)
print(f"service is: {encrypted_service}")
if encrypted_account_content.count(encrypted_service) != 0:
# Decrypting the account details #
username = encrypted_account_content[1]
decrypted_username = key.decrypt(bytes(username, "utf-8"))
decrypted_username = decrypted_username.decode("utf-8")
password = encrypted_account_content[2]
decrypted_password = key.decrypt(bytes(password, "utf-8"))
decrypted_password = decrypted_password.decode("utf-8")
return f"Service: {service.title()}\nUsername: {decrypted_username}\nPassword: {decrypted_password}"
else:
return "Account not found. Please try again."
Any proper encryption will use randomization, so that the result will always be different, and you won't be able to tell that the plaintext was the same. That's needed to achieve semantic security. In practice, initialization vectors and modes of operation like CBC or CTR are used. The cryptography library you're using does it out of the box (with CBC mode).
You will either have to store service as a plaintext, which shouldn't significantly reduce the overall security, or decrypt each service field in order to find the needed record.
Currently, I am designing a credential database meant to hold a servername, username, and password in a text file. However, for the servername and passwords I am trying to make them unique (no two credentials / logins can have the same servername AND password, usernames can be duplicated).
For example,
# This is NOT acceptable because the servername is duplicated.
servername : username : password
google : john : 123
google : john : 321
# This is acceptable because the servername and password are unique.
servername : username : password
google : john : 123
yahoo : john : 321
I have searched stackoverflow for some answers but I was not able to find exactly what I am looking for. In my program's current state, when credentials are entered, they are simply listed right underneath the existing one and continue to add each additional credential without checking for duplicates.
# Creates the file "passdatabase.txt" for use with this code.
with open('passdatabase.txt', 'a+') as searchfile:
searchfile.close()
# Here I initialize the variable / count for the while loop
x = 1
print("\n======================="+
"\nYou chose to add a credential."
"\n-----------------------")
# User enters in a server name to be saved.
addservername = input("Server Name: ")
# User enters in a username to be saved.
addusername = input("Username: ")
# User enters in a password to be saved.
addpassword = input("Password: ")
# All inputs are combined to create a single variable in the format (servername : username : password)
addCredential = addservername + " : " + addusername + " : " + addpassword
# Loops until the user enters a valid submission (either [y] or [n]) to confirm information.
while x == 1:
# Input information is displayed for user to confirm.
confirmCredential = input("~~~~~~~~~~~~~~~~~~~~~~~" +
"\nPlease verify the entered credentials.\n" +
"\nServer Name: [" +
addservername + "]" +
"\nUsername: [" +
addusername + "]" +
"\nPassword: [" +
addpassword + "]" +
"\n\nIs the information correct? (y/n): ")
print("~~~~~~~~~~~~~~~~~~~~~~~")
if confirmCredential == "y":
x = 2
with open('passdatabase.txt', 'r') as searchfile:
lines_seen = set(open('passdatabase.txt'))
for addCredential in (searchfile, 1):
if addCredential not in lines_seen:
with open('passdatabase.txt', 'a+') as searchfile:
# I set the format of the credential to save in the text file as
# servername : username : password.
searchfile.write("\n" + addservername + " : " + addusername + " : " + addpassword)
print("[!] SUCCESS! Your credentials have successfully been stored [!]" +
"\n=======================")
# This breaks the for loop so it does not continue the code.
break
else:
print('Duplicate credential detected!')
elif confirmCredential == "n":
x = 2
print("[!] Your credentials have not been stored [!]" +
"\n=======================")
else:
print("###########################################################" +
"\n[!!!ERROR!!!] PLEASE ENTER EITHER [y] OR [n]. [!!!ERROR!!!]\n" +
"###########################################################")
My questions / requests in one simple list are as follows:
- How do I ensure that the user can only enter in unique entries for their servername and password credentials while allowing duplicate username entries?
- If a duplicate is detected, I would like the code to stop from going further and ask the user for input again until a proper unique credential is provided.
- Should I be using a set or an array? (Data-order does not matter)
- I've seen quite a few duplicate checking scripts that involve the use of 2 .txt files, is this possible with only 1?
- In the future I plan on adding encryption to the password entries, password strength checker, and possibly a credential log (of who has logged in).
- Any input is welcome as I am always willing to learn!
I am fairly new to Python coding and I would really just like some guidance on what I should be looking into next, I'm honestly not sure if I am even headed in the right direction.
Thanks
Maybe you should use sqlite instead of pure text file.
If you really want to implement database feature in a txt file:
Use set for tuple(servername, password), check if exist before appending to file. Remember to load all exist ones from your txt file before ask user to input.
You'd better use a file lock to make sure that only one instance of your script run at the same time.
I have been trying to do some basic search queries, but I am unable to connect to an open LDAP server regardless. I tried a couple of servers, and none of them worked. I used Apache Directory Studio to make sure that the keyword was there but it did not work either way. I tried a variety of different code from different sources.
This was the first one I used
:
https://www.linuxjournal.com/article/6988
import ldap
keyword = "boyle"
def main():
server = "ldap.forumsys.com"
username = "cn=read-only-admin,dc=example,dc=com"
password = "password"
try:
l = ldap.open(server)
l.simple_bind_s(username,password)
print "Bound to server . . . "
l.protocol_version = ldap.VERSION3
print "Searching . . ."
mysearch (l,keyword)
except ldap.LDAPError:
print "Couldnt connect"
def mysearch(l, keyword):
base = ""
scope = ldap.SCOPE_SUBTREE
filter = "cn=" + "*" + keyword + "*"
retrieve_attributes = None
count = 0
result_set = []
timeout = 0
try:
result_id = l.search(base, scope, filter, retrieve_attributes)
while l != 1:
result_id = l.search(base, scope,filter, retrieve_attributes)
result_type, result_data = l.result(result_id, timeout)
if result_data == []:
break
else:
if result_type == ldap.RES_SEARCH_ENTRY:
result_set.append(result_data)
if len (result_set = 0):
print "No Results"
for i in range (len(result_set)):
for entry in result_set[i]:
try:
name = entry[1]['cn'][0]
mail = entry[1]['mail'][0]
#phone = entry[1]['telephonenumber'][0]
#desc = entry[1]['description'][0]
count = count + 1
print name + mail
except:
pass
except ldap.LDAPError, error_message:
print error_message
main()
Every time I ran this program, I received an error
{'desc': u"No such object"}
I also tried this
import ldap
try:
l = ldap.open("ldap.example.com")
except ldap.LDAPError, e:
print e
base_dn = "cn=read-only-admin,dc=example,dc=com"
search_scope = ldap.SCOPE_SUBTREE
retrieve_attributes = None
search_filter = "uid=myuid"
try:
l_search = l.search(base_dn, search_scope, search_filter, retrieve_attributes)
result_status, result_data = l.result(l_search, 0)
print result_data
except ldap.LDAPError, e:
print e
The error on this one was
{'desc': u"Can't contact LDAP server"}
I spent about 5 hours trying to figure this out. I would really appreciate it if you guys could give me some advice. Thanks.
There are several bogus things in there.
I will only comment your first code sample because it can be used by anyone with that public LDAP server.
l = ldap.open(server)
Function ldap.open() is deprecated since many years. You should use function ldap.initialize() with LDAP URI as argument instead like this:
l = ldap.initialize("ldap://ldap.forumsys.com")
l_search = l.search(..)
This is the asynchronous method which just returns a message ID (int) of the underlying OpenLDAP C API (libldap). It's needed if you want to retrieve extended controls returned by the LDAP server along with search results. Is that what you want?
As a beginner you probably want to use the simpler method LDAPObject.search_s() which immediately returns a list of (DN, entry) 2-tuples.
See also: python-ldap -- Sending LDAP requests
while l != 1
This does not make sense at all because l is your LDAPObject instance (LDAP connection object). Note that LDAPObject.search() would raise an exception if it gets an Integer error code from OpenLDAP's libldap. No need to do C-style error checks at this level.
filter = "cn=" + "" + keyword + ""
If keyword can be arbitrary input this is a prone to LDAP injection attacks. Don't do that.
For adding arbitrary input into a LDAP filter use function ldap.filter.escape_filter_chars() to properly escape special characters. Also avoid using variable name filter because it's the name of a built-in Python function and properly enclose the filter in parentheses.
Better example:
ldap_filter = "(cn=*%s*)" % (ldap.filter.escape_filter_chars(keyword))
base = ""
The correct search base you have to use is:
base = "dc=example,dc=com"
Otherwise ldap.NO_SUCH_OBJECT is raised.
So here's a complete example:
import pprint
import ldap
from ldap.filter import escape_filter_chars
BINDDN = "cn=read-only-admin,dc=example,dc=com"
BINDPW = "password"
KEYWORD = "boyle"
ldap_conn = ldap.initialize("ldap://ldap.forumsys.com")
ldap_conn.simple_bind_s(BINDDN, BINDPW)
ldap_filter = "(cn=*%s*)" % (ldap.filter.escape_filter_chars(KEYWORD))
ldap_results = ldap_conn.search_s(
"dc=example,dc=com",
ldap.SCOPE_SUBTREE,
ldap_filter,
)
pprint.pprint(ldap_results)
I am using the Python-LDAP module and trying to make a query on the logged in user. The username will be passed into the query. When I simply type the username in as a string my results come out correctly.
But if I try to pass the (username) variable it returns
LDAPError - FILTER_ERROR: {'desc': u'Bad search filter'} I've tried a number of different combinations but continue to get the same error returned. Any insight here would be great!
Edited for Minimal, Complete, and Verifiable example:
import ldap
LDAP_SERVER = "ldap://myldapserver.domain.ad:389"
username = r"domain\serviceAccount"
password = "Password"
l = ldap.initialize(LDAP_SERVER)
def login(username, password):
try:
l.simple_bind_s(username, password)
base = "OU=Users,OU=Group,DC=domain,DC=ad"
criteria = "(&(objectClass=user)(sAMAccountName=anActualUsername))" #WORKS
criteria = '(&(objectClass=user)(sAMAccountName=%s))' % username #DOESNT WORK
criteria = "(&(objectClass=user)" + "(sAMAccountName=" + username + "))" #DOESNT WORK
attributes = ['displayName']
result = l.search_s(base, ldap.SCOPE_SUBTREE, criteria, attributes)
print result
except ldap.INVALID_CREDENTIALS:
return False
return True
login(username,password)
Did you try to encode your string ?
criteria = ('(&(objectClass=user)(sAMAccountName=%s))' % username).encode('utf8')
In the "WORKS" case, your filter string contains a simple name with no domain:
(&(objectClass=user)(sAMAccountName=bobsmith))
In the "DOESN'T WORK" case, you use a name with a domain:
(&(objectClass=user)(sAMAccountName=domain\serviceAccount)
The character \ is not allowed in a filter string unless it is escaped.
How to fix this depends upon the data present in your ldap server. Perhaps this:
criteria = '(&(objectClass=user)(sAMAccountName=%s))' % (
username if '\\' not in username else username.split('\\')[1])
Or perhaps this:
criteria = '(&(objectClass=user)(sAMAccountName=%s))' % (
ldap.filter.escape_filter_chars(username))
I needed to use ldap.filter.filter_format for proper character escaping.
import ldap.filter
criteria= ldap.filter.filter_format('(&(objectClass=user)(sAMAccountName=%s))', [username])
Try switching single quotes with double quotes.
criteria = "(&(objectClass=user)(sAMAccountName=anActualUsername))" #WORKS
criteria = '(&(objectClass=user)(sAMAccountName=%s))' % username #DOESNT WORK
the second criteria change it to this one (I didn't try with %s but only string):
criteria = "(&(objectClass=user)(sAMAccountName=%s))" % username #SHOULD WORK