Tag Archives | active directory

ADFS 2.0 – Choose Your Attributes Wisely

If you’ve read my last few posts you’ll be aware that I’m in the middle of implementing ADFS 2.0 for Web SSO. SalesForce for starters, with more to follow.  I’m yet to put it into production but I was thinking today and just having a bit of a sanity check and something occurred to me.  We send LDAP attributes as claims, the attributes are accepted by our service provider as law. They trust our federation service – that’s what federation is all about. Trust.  There are number of mechanisms that make it very difficult for someone to spoof an assertion. On the whole, the SAML protocol can be considered very secure.  What it can’t do is guarantee the validity of the source LDAP attribute.

 

Consider the scenario above.  We’re going to send the User’s telephone number as a claim.  Maybe unlikely but it could happen, maybe you’ve got a SaS provider and you’ve already got 500 users in the system and telephone number is the only field you know is accurate between you and them. Unlikely? I know.   But that’s not the point.

The issue is this – in Active Directory the attribute telephoneNumber, along with a few other attributes is by default, self writeable.

Once Dave figure’s out that the telephone number is significant he’ll update his phone number in AD to Bob’s phone number, launch the SaS app and will be logged in as Bob.

 

While there are only a few self writeable attributes in AD and they’re not ones you’d likely use for federation, it’s important to keep the whole picture in mind and the problem could go beyond self writeable attributes.  A couple of other situations I can think of off the top of my head:

  • Active directory self service applications which allows users to update attributes which aren’t normally self writeable
  • Identity management systems which synchronise other systems to Active Directory.  Not a problem in itself but you might be moving the point authorization for a specific application without realising it.

So choose your attributes wisely and make sure you know how, why, when and by whom or what they are written to before you decide to send them as federation claims.

4

SetPassword over domain trust fails – COMException 0x8007202

After breaking my head over this for about 3 days and finally coming up with a solution I thought Id share.

Here’s the situation:

  • 2 Forests with a one way external trust between them.
    • Domain B in Forest B trusts Domain A in forest A.
  • An ASP.NET Application running in Domain A
    • Configured to impersonate.
    • Application Pool running under a service account from domain A which is trusted for delegation
    • Application URL (DNS name) is registered as an SPN against the above service account used to the application pool.
  • A user (Bob) from Domain A has permission to modify properties and reset passwords of users in Domain B
  • When using the Active Directory users and computers MMC Bob is able to reset passwords and modify properties of users in the trusting domain(Domain B) I.e. Permissions are ok!
  • When Bob uses the ASP.NET application (System.DirectoryServices.DirectoryEntry) Bob is able to modify properties of users in the trusting domain. I.e. Bob’s creditneials are sucessfuly delegated to the domain controller in the trusitng domain. I.e. Delegation is working!
  • When the ASP.NET application specifies Bob’s username/password in the DirectoryEntry object e.g. DirectoryEnty de = new DirectoryEntry(“LDAP://domainB.com/CN=Steve,ou=test,dc=domainb,dc=com”,”bob”,”password1″) then invokes the setpassword method, the password is successfully set. I.e. SSL is working correctly. Necessary firewall ports are open etc
  • When Bob uses the ASP.NET application and invokes the setpassword method now using delegation a COMException 0x80072020 is thrown.

I’ve spent some hours trying to work this out, network dumps with WireShark (no good for LDAPS), Kerberos logging on DCs and webserver, etc etc. I have also opened up the firewall to allow DirectoryEntry.Invoke(“setpassword”) to try his other methods. I just can’t get this to work in a domain trust environment with delegation. I have however found that using System.DirectoryServices.Protocols to reset the password in a trusting domain with a delegated credential does work! For this I’m very relieved; I was almost at the point where I was going to go back to our dev guys and tell them to switch back to using a superuser account instead of the delegation method which I had been pushing on them for weeks!

Frustrating though that I still don’t know why this method works where the standard System.DirectoryServices method doesn’t!

This example was taken from chapter 10.16 of The .NET Developer’s Guide to Directory Services Programming by Joe Kaplan and Ryan Dunn. I strongly recommend you get this book.  It’s not the newest book out but it’s still packed with really relevant stuff and lots of “gold nuggets” from 2 guys who really know their stuff.

using System;
using System.DirectoryServices.Protocols;
using System.Net;
using System.Text;

public class PasswordModifier
{
    public static void Main()
    {
        NetworkCredential credential = new NetworkCredential(
            "someuser",
            "Password1",
            "domain"
            );
        DirectoryConnection connection;

        try
        {

            //change these options to use Kerberos encryption
            connection = GetConnection(
                "domain.com:636",
                credential,
                true
                );

            ChangePassword(
                connection,
                "CN=someuser,CN=users,DC=domain,DC=com",
                "Password1",
                "Password2"
                );

            Console.WriteLine("Password modified!");
            IDisposable disposable = connection as IDisposable;

                if (disposable != null)
                    disposable.Dispose();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private static DirectoryConnection GetConnection(
            string server,
            NetworkCredential credential,
            bool useSsl
            )
        {
            LdapConnection connection =
               new LdapConnection(server);

            if (useSsl)
            {
                connection.SessionOptions.SecureSocketLayer = true;
            }
            else
            {
               connection.SessionOptions.Sealing = true;
            }

            connection.Bind(credential);
            return connection;
        }

        private static void ChangePassword(
            DirectoryConnection connection,
            string userDN,
            string oldPassword,
            string newPassword
            )
        {
            DirectoryAttributeModification deleteMod =
                new DirectoryAttributeModification();
            deleteMod.Name = "unicodePwd";
            deleteMod.Add(GetPasswordData(oldPassword));
            deleteMod.Operation= DirectoryAttributeOperation.Delete;

            DirectoryAttributeModification addMod =
                new DirectoryAttributeModification();
            addMod.Name = "unicodePwd";
            addMod.Add(GetPasswordData(newPassword));
            addMod.Operation = DirectoryAttributeOperation.Add;

        ModifyRequest request = new ModifyRequest(
            userDN,
            deleteMod,
            addMod
            );

        DirectoryResponse response =
            connection.SendRequest(request);
    }

    private static void SetPassword(
        DirectoryConnection connection,
        string userDN,
        string password
        )
    {

        DirectoryAttributeModification pwdMod =
            new DirectoryAttributeModification();
        pwdMod.Name = "unicodePwd";
        pwdMod.Add(GetPasswordData(password));
        pwdMod.Operation = DirectoryAttributeOperation.Replace;

        ModifyRequest request = new ModifyRequest(
            userDN,
            pwdMod
            );

        DirectoryResponse response =
            connection.SendRequest(request);

    }

    private static byte[] GetPasswordData(string password)
    {
        string formattedPassword;
        formattedPassword = String.Format("\"{0}\"", password);
        return (Encoding.Unicode.GetBytes(formattedPassword));
    }
}

In my case this is somehow related to the domain trust but I guess any time 0x8007202 appears during setpassword and you just can’t get it to go away I’d suggest giving this method a go!

0