Sunday, September 24, 2006

An interesting thread on LDAP and authentication came up again today on activedir.org.  In the conversation, I had suggested that it would be a good feature if AD and ADAM had a setting somewhere that could be enabled that would prevent LDAP simple bind from working if the network channel was not secured (via SSL).  The problem with simple bind (for those not in the know) is that the user's credentials are passed over the network in plaintext, which is potentially a huge security risk (depending on the network).  It is actually even worse (by just a little) that HTTP basic authentication, as the credentials aren't even base64 encoded.

Eric Fleischman then pointed out that ADAM actually already has such a switch.  Tomek blogged about it here, so I'll let him cover the details on how to enable this.

I went on to suggest that AD should also have this feature (off by default for backwards compat most likely).  The problem is that without this feature, the AD admin really has no way to prevent apps that use simple bind for auth from sending plaintext credentials on the network, even if SSL has been configured.  You can't just turn off port 389 access to AD without breaking other stuff as you might be able to do in other directories.  Many of the NOS features of AD rely on port 389.

I also suggested adding an ability to audit simple bind attempts over non-SSL channels, including the IP address, so that admins have a way to track down rogue apps.

In many IT organizations these days, we must specify policies about how we are going to treat sensitive data (like passwords!) on the network and can get ourselves into hot water with our auditors if we are found to not be in compliance with our own policies.  At least with this auditing support in place, violations of the policy can be capture and violators may be traceable.

Also note that with most ADSI (and System.DirectoryServices programming in .NET), simple binds are not used.  Windows secure binds (using GSS-SPNEGO, which amounts to Kerberos or NTLM at the heart of it) are used by default.  You can get a simple bind, but you must do more work.  As such, most ADSI script code is not likely to have this problem.  You do have to be careful though.  We go into this in a great deal more detail in ch. 3 of the book.  Simple binds are very common with cross platform apps, and especially those that use non-MS LDAP libraries, as they often do not have the code they need to implement GSS-SPNEGO at all.  Some of them just use simple bind because it is the lowest common denominator protocol that all LDAP directories must support.

Anyway, it will be interesting to see if any of these suggestions come to fruition.  In the mean time, use SSL with your directories (ADAM too!) when doing simple bind.  Getting certs can be painful, but usually not as painful as getting hacked or failing an audit.  Unless your networks are totally secure between the endpoints and there is no threat of snooping, you need a secure channel. 

Monday, September 25, 2006 3:42:39 AM (Central Daylight Time, UTC-05:00)  #    Comments [3]  | 
Thursday, August 17, 2006

Fast Concurrent Binding Overview

One of the things I alluded to in my post that highlighted the differences in capabilities between the ADSI-based System.DirectoryServices (SDS) namespace and the raw LDAP-based System.DirectoryServices.Protocols (SDS.P) was the ability to access a feature called Fast Concurrent Binding (FCB).

FCB is a feature available in Active Directory 2003 (and later) and in ADAM.  To explain it, first let’s cover what happens with a “regular” bind operation in LDAP against AD or ADAM.  With a normal bind, the user’s credentials are presented to the LDAP server, either in plaintext format or via the Windows security model if using a secure binding protocol.  The server then attempts to authenticate these credentials.  If the credentials are accepted, then the server builds a Windows token for the user which will be used to for performing security checks on future operations and changes the state of the current LDAP connection to an authenticated state so that future requests will continue to use the newly established security context and this operation is not repeated.  This is the main reason why LDAP connections are stateful.

FCB eliminates two parts of this equation.  It authenticates the user’s credentials, but it does not build a Windows token for the user and the connection state stays anonymous.  As such, the bind operation will give you an “up/down” result based on whether the user’s name and password were valid, but it won’t let you do anything else with that connection such as perform searches, except as an anonymous user (which is pretty limited by default in AD 2003 and ADAM).

The big upside of FCB is that the bind operation is MUCH faster than a normal bind because the really expensive operation, expanding the user’s group membership to build the token, is eliminated.  Hence the name fast concurrent binding.*

The concurrent part comes from the fact that multiple users can be authenticated on the same LDAP connection simultaneously without worrying about changing the authenticated state of the connection, so the expense of setting up and tearing down the associated socket can also be eliminated.  This makes it potentially even faster.

The big downside with FCB is that it only supports LDAP simple bind, not SASL, and simple bind does not have a built in mechanism for protecting the credentials on the wire like the various SASL mechanisms do.  As such, it is never safe to use FCB unless some form of transport security is used such as SSL or IPSEC!

When to Use Fast Concurrent Binding

Do use FCB when your application is doing pure authentication of credentials via LDAP.  An example of this might be in a web-based application that uses AD or ADAM as a user store and does LDAP authentication.  If you just need a “yes/no” vote as to whether a username/password combo is good, FCB will give you the best performance and may add a significant scalability increase to your application as a result. 

Do not use FCB if you need to authenticate and then perform operations against the directory on the user’s behalf (a delegation scenario from the architectural perspective, although not necessarily using Windows Kerberos delegation features to implement it).  Since the connection state does not change to “authenticated”, the user’s credentials will not be used to perform subsequent operations and you won’t get what you want.

An obvious place where you might want to use FCB would be in something like the ActiveDirectoryMembershipProvider in ASP.NET 2.0.  My understanding is that the development already thought about this and tried to add this support already.  Thus, you may already have this and not even know it!

Another obvious place for this to be used would be in ADFS for authenticating users in the ADAM store and potentially with users in the AD store as well when used with the Federation Service Proxy (which accepts credentials via a forms or basic auth interface in plaintext form).  As of this writing, I do not believe that ADFS is taking advantage of this feature (or and SDS.P capabilities for that matter).  Note to product team…

A Note on ADSI/SDS and Fast Concurrent Binding

Hey Joe, I see that ADSI and SDS have an AuthenticationTypes flag called FastBind.  Can’t I just use that to get FCB?  Why are you telling me this requires SDS.P?

The answer here is “no”, they are not the same thing, despite their unfortunately similar names!  FastBind in ADSI/SDS actually has nothing to do with the bind operation per say, but has to do with what ADSI does after the bind when you go to access an object.  With FastBind NOT set (the default), ADSI does a base-level search to the object you tried to access to read its objectClass attribute before it executes any other searches to load the property cache and such.  ADSI uses this information to determine which ADSI interfaces to make available.  By default, you get the core interfaces like IADs, but you don’t necessarily get one of the “persistent object” interfaces like IADsUser or IADsGroup unless ADSI knows that it is a user or group.  Without those interfaces, you loose access to members like SetPassword and Add (for group membership additions). 

The reason FastBind is called “Fast” is that this initial search is done in addition to any searches to load the property cache, which generally means that at least two base-level searches to the directory will be required to load any given ADSI object (as you normally use the property cache for something!).  All these extra searches add up to lots of network chatter and can slow certain types of operations to a crawl. 

Note that FastBind is an ADSI/SDS thing though and has no bearing on how things work at the lower level LDAP level, as this notion of the persistent object interfaces and property caches is an ADSI abstraction layer.

How to Implement Fast Concurrent Binding in SDS.P

There really isn’t too much too this.  All you really need to do is turn this option on BEFORE your LdapConnection actually connects to the directory for the first time and you are all set.  Briefly, the code looks something like:

_authConnect.SessionOptions.FastConcurrentBind();

In order to get a more thorough look, I’ll simply point you to the LdapAuth.cs class from our book’s website in ch 12 of the Full Samples to see this in real usage. 

Important Warning
There is a bug in the actual implementation printed in the book (first printing at least; assuming there will be more than one and we actually fix it ?).  A small last minute blunder caused the defect.  Please use the version in the full samples from the website.

There are some important caveats as well:

  • Both the server and client must support FCB.  In the book sample, we actually check the server first via its supportedCapabilities RootDSE attribute to find the specific OID.  On the client requirements, be aware the SDS.P requires the client code to be run on Windows 2003 server or higher.  XP and below does not support this!  Our code tries to handle this gracefully, but you should plan ahead anyway.
  • You are using simple bind, so you need plaintext usernames and passwords and must use a user name syntax supported by simple bind.  For AD, this is NT Account Name (domain\user), UPN or DN.  For ADAM, this is generally UPN or DN, but actually could be a lot of things like displayName as well.  Ch 3 of our book goes into more detail.
  • This is simple bind.  You MUST encrypt the network traffic with SSL or something!

Conclusion

We already covered most of this information in our book, but I don’t think we hit all of these things in as much detail as I have covered here.  Additionally, not all of you will buy the book and this isn’t in the free chapter, and we want you to be successful with this somewhat mysterious feature, regardless.  Happy fast(er) binding!

* Of course, with all performance optimizations, always measure and never assume.  Premature optimization is one of the seven deadly sins of software engineering, yada-yada-yada.

Thursday, August 17, 2006 5:05:52 PM (Central Daylight Time, UTC-05:00)  #    Comments [0]  | 
Wednesday, August 09, 2006

In a newsgroup thread today, William Stacey taught me some stuff about salted password hashes stored in a traditional relational database.  Apparently, this is not the way the cool kids do it anymore (although MS still has plenty of guidance suggesting to use this approach).  Apparently, the those in the know use an implementation like Secure Remote Password (SRP-6a).

William couldn't find a .NET implementation, so he built one!  You can find his implementation here.

Thursday, August 10, 2006 1:52:50 AM (Central Daylight Time, UTC-05:00)  #    Comments [0]  | 
Monday, August 07, 2006

Authentication Mechanisms in SDS

In ADSI and System.DirectoryServicess (SDS), we are really only given two choices for how authentication will be performed when we do a bind operation.  We can specify the "Secure" flag (AuthenticationTypes.Secure), and ADSI will attempt to the bind to the remote directory using Windows SSPI authentication with the Negotiate protocol.  As you may know, the Negotiate protocol is the primary authentication protocol in Windows since Windows 2000 and selects between using Kerberos (always preferred) and NTLM (there for backwards compatibility).  For the really nerdy of you out there, the Negotiate protocol is implemented by Microsoft's LDAP API using the GSS-SPNEGO SASL mechanism.

The other option in SDS is to NOT specify AuthenticationTypes.Secure and it will attempt to use an LDAP simple bind instead*.  The only authentication mechanism defined by LDAP specification is the simple bind, so every directory implements it.  As such, it ends up being the cross-platform lowest common denominator approach.  The problem with the simple bind is that it is totally insecure by itself.  Simple bind passes the user's plaintext credentials on the network, so unless some sort of channel encryption is provided (like SSL, the defacto cross-platform standard here as well), anyone who can sniff the wire traffic can recover the user's password.  This sort of thing is generally frowned on my security enthusiasts.  :(

Digest Authentication in LDAP API/SDS.P

There is, however, a richer set of authentication mechanisms supported by Microsoft's LDAP API than what ADSI/SDS get to use, and these are available to System.DirectoryServices.Protocols (SDS.P).  One of the most interesting of these is the Digest authentication protocol.  Digest is a standard, secure authentication protocol that does not involve the exchange of plaintext credentials and is implemented by AD and ADAM, as well as some other LDAP directories.  In SDS.P, we can use it very easily.  Here is a short sample illustrating this (and no, there is no ADAM instance at adam.joekaplan.net; dream on...):

public static bool DoDigestAuth(NetworkCredential cred)
{
    const int LDAP_INVALID_CREDENTIALS = 49;
    LdapConnection conn =
        new LdapConnection("adam.joekaplan.net:389");
    conn.AuthType = AuthType.Digest;
    conn.Credential = cred;

    try
    {
        conn.Bind();
        return true;
    }
    catch (LdapException ex)
    {
        if (ex.ErrorCode == LDAP_INVALID_CREDENTIALS)
        {
            return false;
        }
        else
        {
            throw;
        }
    }
}

Here, we have a trivial little sample that shows how this might be done.  Note that we probably wouldn't create a new LdapConnection object each time this is called and we'd also try to find a way to clean that up with a proper Dispose, but the point here is to illustrate that all we really have to do is set the AuthType property to AuthType.Digest and it works.  The credentials are specified with a standard NetworkCredential object.  For Digest, just specify the username and password, not the domain parameter.

Why is This Important?

Digest authentication is really interesting particularly for ADAM, as it is supported by ADAM principals.  Before ADAM supported Digest auth (as of SP1), the only way to authenticate an ADAM principal was with a simple bind.  That wasn't secure (see above), so SSL was required to make it secure.  However, SSL certs are not always easy to procure and take extra effort to install, so many people were missing this and were insecure.  :(

The other cool thing with Digest is that since it is implemented through Microsoft's SSPI model, it can also be used as a mechanism for getting channel encryption and signing, just like negotiate auth supports today**.  This means that not only can you get secure binding for free, but all of your network traffic after the bind can be encrypted and signed for free (no SSL required).  This is done through setting the Signing and Sealing properties to true on the LdapSessionOptions class.  This, in turn, allows a way to do LDAP password operations on ADAM principals without using SSL and without having to change the setting in ADAM to turn off the requirement for a secure channel on password ops (another big security frown sandwich there...). 

One great scenario for this feature would be to use it with ADFS for their ADAM Account Store support.  This would allow secure authentication without requiring SSL out of the box and would also provide free channel encryption.  Product team please take note!

* There is a feature in Windows Server 2003 SP1 ADSI will try Digest auth when the Secure flag is specified and ADSI detects that the server supports Digest but not negotiate auth.  However, this doesn't help us for AD or ADAM, since they both support negotiate auth.  The problem with negotiate auth for ADAM is that it is only used for authenticating Windows users, not ADAM principals.

** There appears to be a bug in the implementation in ADAM SP1 where the channel encryption feature only works if the network traffic is NOT on the loopback (localhost) port.  This is different from the way negotiate channel encryption works.  This appears to be an oversight in the implementation and not an underlying issue in the design, so presumably this will get fixed someday.

Monday, August 07, 2006 2:31:20 PM (Central Daylight Time, UTC-05:00)  #    Comments [0]  | 
Tuesday, August 01, 2006

When I was at TechEd this year, I ran into a few people asking how to integrate SecurID authentication into ADFS.  As it currently stands, Microsoft has no direct support for this, or any other authentication mechanism besides AD/ADAM password authentication and client certificate auth.  Hopefully in the future, Microsoft will make the account store and authentication mechanisms a first class extensibility point, but for now, we must hack.  :)

As you can probably tell by the article title, I have gotten this working in my organization's environment, even though it is not supported by MS.  So, how do we make this work?  First, I'll explain the overall principles of how we glue these things together and then I'll share how I actually did it.  In part 2 of the posting, I'll talk about some other ways that we might get this working.

Basic Principles

In Windows Server 2003 and Active Directory 2003, Microsoft implemented an important and useful extension to the Kerberos authentication protocol called "Service for User" (S4U), aka Protocol Transition.  S4U adds the ability for a service to authenticate a client with a non-Kerberos protocol and then transition to a Kerberos-based identity for accessing Windows resources on local or remote services (hence "protocol transition").  Protocol transition is something that can be done automatically by services like IIS when a user authenticates with Basic, Digest or NTLM, but it can also be called programmatically and used by non-Windows authentication systems such as SecurID.

To use S4U, your code must execute on a Windows Server 2003 machine (or higher) AND your Active Directory must be 2003 forest functional level.  If you are doing ADFS, you already have the former.  However, since ADFS supports both Windows 2000 Server and Windows Server 2003 AD, you might not have the latter.  If you don't, you are basically SOL for now.  Upgrade!

When using S4U programmatically, you are basically calling the Windows API LsaLogonUser with the S4U option specified.  When usings the S4U option, LsaLogonUser only requires you to know the user's UPN, not their password, in order to get a Windows logon token for them.  There are some restrictions on how this token can be used, but that really isn't too important for this discussion.  Keith covers this stuff in detail in his book and several articles anyway.  One other nice thing is that .NET (as of 1.1 and higher) has a very easy way to call LsaLogonUser for S4U with the WindowsIdentity constructor that just takes the UPN.  It is that simple.

So, now we know we need to use S4U to get a Windows token for the user and we'll need their UPN to call this.  How does the Windows token then help us with ADFS?  Well, the ADFS logon service (LS) has a method that allows a user to authenticate given their Windows token.  In fact, the /ls/auth/integrated directory on the federation server does just this.  It is set to use Windows Integrated authentication (IWA) in IIS.  IIS logs the user on with IWA, passes the corresponding Windows token to ASP.NET.  Then, the ADFS HTTP Module grabs that and calls the correct method to log you into ADFS and now you have a federation token. 

An interesting side note is that ADFS itself uses protocol transition in the web agent for token-based applications to achieve similar things (assuming again that you have 2003 AD; otherwise it uses its custom authentication package). 

So, our basic approach with SecurID authentication is to:

  1. Authenticate the user with SecurID using one of RSA's supported methods for web authentication
  2.  Get the user's UPN somehow (perhaps an LDAP query?)
  3. Use S4U to get a Windows token for the user
  4. Use the appropriate method on the LS to authenticate with ADFS using the token

This is cake!  :)

How I Did This

Ok, I cheated a little bit.  In addition to using RSA's SecurID/ACE Server product in our company, we also use their ClearTrust web SSO product (now apparently called Access Manager, but I didn't get that memo).  ClearTrust already basically let's me do steps 1-3 above using their standard product when the protocol transition/S4U feature is enabled.  Since it runs as an ISAPI Filter/ISAPI Extension (via a wildcard map in IIS), it runs before any .NET code executes.  All I had to do was configure ClearTrust on my /ls/auth/integrated directory, and ADFS doesn't know the difference between IIS having used IWA or some other thing.  The LS just grabs the Windows token from where it expects to find it in ASP.NET and proceeds with logon.  It really is cake and actually worked the first time I tried it.

The hardest part was instructing ClearTrust to NOT execute on any resources other than the /ls/auth/integrated directory, which essentially came down to a lengthy URL exclusion list in the ClearTrust configuration.  Anyone who has used ClearTrust probably already knows how to do that.  I will probably experiment with alternate directory structures to simplify this a bit more in the future.

Since ClearTrust is a web SSO product, it also contains a signout function that is implemented by navigating to a page.  We integrated this into the ADFS signout by using the same technique it does: we referred to the ClearTrust signout page in a hidden image tag on the normal signout page.  That results in our ClearTrust cookie being cleared.

One thing I should make clear is that we did this customization on the FS, not the FS-P (Federation Service Proxy).  I don't see a reason why you couldn't do this there too, but you'd need to mess with it a bit more as the FS-P is configured out of the box to do its own forms auth instead of redirecting to the /ls/auth/integrated directory.

One important caveat with SecurID auth is that it basically requires forms authentication, especially to support PIN operations.  This basically implies that you can't use the "basic" authentication support built in to ADFS here.  As such, you should probably turn off that option in your ADFS configuration.  That may also mean that you some SharePoint/Office integration stuff breaks though.  I'm not sure how to reconcile that problem.  Forms auth and Office don't like each other.

Summary

So anyway, that's the basic concept.  If anyone needs additional details, let me know and I'll try to expand on this.

In Part 2 of this post, I'll try to expand on how one might accomplish if one were not so blessed with a working ClearTrust infrastructure in place to do the hard part.  Note that I've never actually done this part, so some of it might just be wild supposition and it might take some help from someone else out there who really wants this to make it happen.  We'll see.  In the meantime, I hope the blueprint was helpful.

Tuesday, August 01, 2006 5:08:18 PM (Central Daylight Time, UTC-05:00)  #    Comments [3]  | 
Monday, July 31, 2006

One of the things you run into with ASP.NET apps doing authorization using Windows security is that you often need a way to find out the authenticated user's identity and security group memberships for troubleshooting.  This is especially useful under ADFS, where your Windows token can go through a mapping process based on claims received from an external organization and bear no resemblance to an actual user in your AD forest.

This is just an ugly sample page in ASP.NET (using VB.NET, but I'll do C# on request if that's really important; we're talking about 10 lines of code here guys...) that dumps out the authenticated user's groups and name.

The core function looks like this:

Private Sub Page_Load( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs _
    ) Handles MyBase.Load

_nameLabel.Text = User.Identity.Name
Dim groupSidHtml As New System.Text.Stringbuilder
Dim sids As System.Security.Principal.IdentityReferenceCollection = _
    DirectCast(User.Identity, System.Security.Principal.WindowsIdentity).Groups
Dim names As System.Security.Principal.IdentityReferenceCollection = _
    sids.Translate(GetType(System.Security.Principal.NTAccount))
For Each name as System.Security.Principal.NTAccount In names
    groupSidHTML.AppendFormat("<p>{0}</p>", name.ToString())
Next

_groupLabel.Text = groupSidHTML.ToString()
End Sub

If I were a little less lazy, I probably would have added the imports declarations on the page instead of using the full type names and would have used a repeater and some formatting, but this was quick and dirty.  Feel free to improve it.  The working page can be downloaded at the link at the bottom of the page.

Caveat

Even though token apps allow you to run on prior versions of the .NET framework, this page uses a bunch of .NET 2.0-specific code in it (IdentityReferenceCollection and such), so you must configure the app for .NET 2.0 to use this.  I'm simply not at all interested in writing all the p/invoke stuff to crack the user's token and translate their SIDs into names simply to create a .NET 1.1 solution.  Sorry.  There is lazy and then there is just wasting time...

Also, you must be configured for Windows authentication in ASP.NET, but that should be obvious I hope.

I hope this helps someone figure out what ADFS is actually putting into their Windows token!

(Update, changed the file to a .zip to avoid error mentioned in the comment)

default.zip (.63 KB)
Monday, July 31, 2006 6:44:06 PM (Central Daylight Time, UTC-05:00)  #    Comments [4]  | 
Sunday, July 30, 2006

I've been telling myself I was going to finally get into the blogging racket for almost 2 years now, but even though I've had the hosting all put together for that entire time, it took me forever to actually get it together.  I just had to do it myself instead of using another site...

Anyway, this blog will probably resemble the blogs I already read, in that it will be mostly technical with a focus on building software using Microsoft's .NET platform.  It will probably lean heavily on my specialties, .NET LDAP programming and application security, but will likely also feature other stuff I'm into like application architecture, agile development, identity federation, cryptography, and setup development in MSI using WiX

Speaking of .NET and LDAP, if you've ever heard of me before, it is most likely because you might have stumbled across one of the myriad usenet posts I've made over the last 4-5 years on the Microsoft newsgroups, or perhaps I answered your question directly.  Micrsosoft has actually designated me an MVP in this area, and I've even written a book about this with my intrepid co-author, Ryan Dunn, to further our aim of providing resources for the .NET community in this obscure, but stranglely difficult and increasingly important aspect of software development.

That's all for now.  Maybe some real content next time, eh?

Sunday, July 30, 2006 2:25:14 PM (Central Daylight Time, UTC-05:00)  #    Comments [0]  | 

Theme design by Jelle Druyts