Question

I'm investigating the scripting of various LDAP operations. However, I've hit a bit of a speed bump with Active Directory user creation.

The following LDIF fails when I load it in via the ldapmodify command:

dn: CN=Frank,CN=Users,DC=domain,dc=local
changeType: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: Frank
userPrincipalName: frank@domain.local
sAMAccountName: frank
givenName: Frank
sn: Stein
displayName: Frank Stein
description: Frankenstein's User
userAccountControl: 512
unicodePwd: "AnExamplePassword1!"

When attempting to add the user via LDIF, I used the following command:

ldapmodify -H 'ldaps://<ip-of-server>:636' -D 'DOMAIN\Administrator' -x -W -f frank-add.ldif

This fails with the following error:

ldap_add: Server is unwilling to perform (53)
        additional info: 0000001F: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0

This is a problem with the password policy denying the user.

However, the following Python script works:

#!/usr/bin/python

import ldap
import ldap.modlist as modlist

AD_LDAP_URL='ldaps://<ip-of-server>:636'
ADMIN_USER='DOMAIN\Administrator'
# User must be authorized to create accounts, naturally.
ADMIN_PASSWORD='password for ADMIN_USER'
BASE_DN='dc=domain,dc=local'

username='frank'
firstname='Frank'
surname='Stein'
displayName = "Frank Stein"

password='AnExamplePassword1!'
# The value of password still needs to adhere to the domain's password policy.
unicode_pass = unicode('\"' + password + '\"', 'iso-8859-1')
password_value = unicode_pass.encode('utf-16-le')

l = ldap.initialize(AD_LDAP_URL)
l.simple_bind_s(ADMIN_USER, ADMIN_PASSWORD)

dn=str('CN=%s,CN=Users,DC=domain,dc=local' % firstname)

attrs = {}

attrs['objectclass'] = ['top','person','organizationalPerson','user']
attrs['cn'] = str(username)
attrs['sAMAccountname'] = str(username)
attrs['unicodePwd'] = str(password_value)
attrs['givenName'] = str(firstname)
attrs['sn'] = str(surname)
attrs['displayName'] = str(displayName)
attrs['description'] = str("Frankenstein's User")
attrs['userPrincipalName'] = str("%s@domain.local" % username)
attrs['userAccountControl'] = str(512)

ldif = modlist.addModlist(attrs)
l.add_s(dn,ldif)

Using the Python script, I am immediately able to sign in using the user's password (minus the quotes that were escaped out). I can still trigger the same "Unwilling to Perform" error by picking a password like 'password' that is too simple. However, in this case the password being used is the same.

So far as I can see, the operations should be identical. The difference that breaks the LDIF file is the way that I deal with the quotes that I need to enclose the password in. Creation via LDIF succeeds if I make a disabled account by setting the value of userAccountControl to 544 and not including a password. However, this means that I would need to manually go and reset the user's password.

So far, I've tried the following password formats via LDIF:

  • Without quotes.
  • Plain quotes.
  • Escaped quotes via \
  • Escaped quotes via ASCII: {\22}
  • Using Python to Base64-encode the password (With and without quotes, and with the format of the LDIF modified to unicodePwd::)

While I'm happy that I have a working method of adding users via the Python, I'm still a bit confused about how to properly escape out password values when using LDIF files and ldapmodify. Is there an alternate method that I'm not considering?

Was it helpful?

Solution

Why not use ldifde and unicode base64 encode the password as described here: http://support.microsoft.com/kb/263991

Your python script seems to be encoding the password as unicode / base64. Perhaps your password needs to be encoded in your ldif file (with the quotes when encoding) rather than plain text as you are doing in your example.

Eg:

unicodePwd:: IgBBAG4ARQB4AGEAbQBwAGwAZQBQAGEAcwBzAHcAbwByAGQAMQAhACIA

For the example password you provided.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top