Thursday, June 21, 2012

LINQ to LDAP: How do passwords work?

A couple hours of trial and error I have finally figured out setting passwords in Active Directory and Lightweight Directory Services. As a result of this I ported all of the dynamic functionality to some extension methods for LdapConnection.

Modifying passwords has been in the back of my head ever since the question was asked here. If I had looked closer at the answer on StackOverflow I would have seen that the solution was for Sun-One. If there's one thing I've learned working on LINQ to LDAP it's that every server has been implemented a little differently.

So let's get down to it. The first thing I learned is userPassword in AD LDS is not the "real" password attribute. That would be unicodePwd. I then received a very helpful "An operation error occurred." A few searches later and I found out that means you'll have to use port 636 instead of 389. Turns out Microsoft does not allow changing the password over a non SSL connection. However, you don't have to explicitly set SSL to true if you're post Windows 2000 (I hope you are). Okie dokie let's fire this up.

"The LDAP server is unavailable."

You'll get this error if SSL is not on for your server. This lead me to generating a self signed certificate and installing it for my local instance. You can read about the how and why here. Alright, now my server is responding to requests on 636, but I'm getting "A value in the request is invalid." That's when I found this post explaining how to do this the right way. I wish I had found that first. So here's the resulting code:



For those using AD LDS and you don't have SSL on, let me save you some trouble. I found this post later allowing you to enable modifying the password over port 389. Well I hope this was helpful.

update
I should probably add that you should not enable setting passwords over an unencrypted connection. I am working in a test environment so it's no big deal for me.

6 comments:

  1. Good job and thanks for the links!

    ReplyDelete
  2. as soon as I include Map(u => u.UnicodePwd).Named("unicodePwd"); into the mapping, I get the DirectoryOperationException (The server cannot handle directory requests).
    User object is mapped as user (objectClass) and person (objectCategory). The same thing happens if I try to set the UserPrincipalName!

    ReplyDelete
  3. You cannot map UnicodePwd. It will never be returned when you query for it and setting it must be done over SSL and it requires special behavior. I plan on taking a look at adding some special mapping to try to support it, but for now you will need a method to handle setting / resetting passwords specifically.

    As far as UserPrincipalName goes, you have to see what response your directory returned so you can see why it failed.

    ReplyDelete
  4. LINQ to LDAP is a great project. Nice Job!

    I haven't tried, but won't this .ChangePassword() code bypass any password policy enforced on the server?

    ReplyDelete
  5. Thanks!

    According to this article http://technet.microsoft.com/en-us/library/cc875814.aspx#ECAA if your password policy is enforced at the domain level ChangePassword should cause an error if it doesn't conform to the policy. I'll have to test it to confirm.

    ReplyDelete
  6. I'll have to spin up a domain in a VM to test. AD LDS doesn't have built in support for password policies beyond "never expire." I did find some more evidence suggesting that an error will occur if the password does not meet the requirements http://en.csharp-online.net/User_Management_with_Active_Directory%E2%80%94Understanding_Password_Policy_and_Security.

    ReplyDelete