Tuesday, March 11, 2008

It seems like I've deteriorated into semi-annual blog posts.  Sigh.  At least the discussion groups at www.directoryprogramming.net continue to flourish and we are seeing a nice uptick in activity on the ADFS board there.  I think the writing may be on the wall for me as a blogger, but who knows.  Perhaps I'll get back on the wagon.

Anyway, thanks to all the people who came to see my talks at DEC this year.  I hope you enjoyed visiting my town and you got a lot out of the conference itself.  DEC is one of my favorites and I'm happy to see it continue to do well.  I got a lot of nice feedback on both of my talks and I'm always interested to hear what you thought.

My first talk this year was on customizing ADFS.  To my knowledge, this type of stuff has never been talked about publicly before, so the session was a bit of an experiment.  I essentially tried to cover all the different types of things you should and could do to ADFS V1 or 1.1 (2003 R2 server or 2008 server) to make it do different things.  It started off discussing some cosmetics to apply to the pages displayed by the FS, moved through ADAM account store tweaks and then covered custom claim transformation modules and some advanced hacks/mods such as non-Windows authentication.  Some of that stuff has been discussed here on this blog and nearly all of it was based on stuff we've actually done at work, so basically real world experience.  My fear was that the subject matter would be a little over the audience's head since it wasn't introductory at all (assumed you already knew ADFS pretty well) and covered some developer stuff which might have seemed alien to many of the audience members who tend to be more of the IT Pro sort.  Still, I think it was valuable stuff.

I mentioned a sample custom claim transform module that I'd post the source to.  I'll follow that up in a separate post shortly.

The audiences for all the ADFS talks were pretty small by comparison to the big AD talks, but this doesn't surprise me.  Not only is ADFS still a new thing, but to a great extent it is a luxury for most DEC attendees to be able to go to those sessions.  ADFS largely assumes that both the directory and the identity provisioning stuff is all a solved problem and that we have rich repositories filled with security principals whose identity is trustworthy and stuffed with useful metadata that can be converted to claims.  For many, that is not (yet) a solved problem at all and federated identity is still largely wishful thinking.  Still, you have to start somewhere.

My second talk was essentially the talk I've done at DEC now for 3 years, although this time modified heavily to cover the new .NET 3.5 stuff in System.DirectoryServices.AccountManagement.  I was really dreading this talk and didn't finish the slides for it before the conference, so between the last minute PPT work and the anxiety, I only got about an hour of sleep.

Still, the talk seemed to go off pretty well.  I was in the big room this time and in one of the last slots of the conference, so I had low expectations for turnout.  It seemed like I had a pretty good crowd though (not the Dean and Joe show, but I'll take it!) and people seemed to be into it.  I think it was probably the best version of that talk I've done to date, so all in all I'm quite happy.  I miss having Ryan there to bounce stuff around with, but so it goes.

Thanks also to Donovan for inviting me up for his case studies talk and giving me an opportunity to talk about some of the real world stuff we are doing with federation at work and talking about the process and legal stuff as much as the technical aspects.  People clearly have as many if not more questions about those things than the engineering parts.

I think I finally get CardSpace now, especially as it applies to the enterprise, and am looking forward to having a chance to get it running internally.  That should be interesting.  Stuart, I need some bits I can actually deploy.  :)  Thanks to Pamela Dingle for coming to DEC and bringing both the CardSpace love and the non-MS platform perspective.  I'm anxious to go to a conference where everyone knows her and no one knows me at all and see how I do.

She's got a nice follow up on the Wook Lee Challenge this year which I played a tiny role in.

Customizing-ADFS.zip (1.06 MB)

DotNet-DS-Programming.zip (1.27 MB)

Wednesday, March 12, 2008 3:41:56 AM (Central Daylight Time, UTC-05:00)  #    Comments [3]  | 
Friday, April 27, 2007

I was lucky enough to attend DEC again this year and was even more lucky to have been asked to speak due to an unfortunate last minute cancellation.  This year, I presented on a variation of the same type of stuff that Ryan and I presented on at DEC 2006.  This year, I had to fly solo as Ryan could not attend.  :(

Here's what we did differently this time around:

  • No PowerShell (DEC already had 2 PowerShell sessions, so why bother?)
  • Focus on some new Longhorn LDAP and AD features (Fine Grained Password Policy)
  • A "slideware" overview of what's coming in .NET 3.5 "Orcas" with the new System.DirectoryServices.AccountManagement namespace (formerly known as the Principal API).

I'd like to thank all of those who attended.  I hope you enjoyed the talk and hope that some of you got free books.  I apologize if I could not accomodate all of you.  :(  Thanks to the Addison-Wesley marketing team for providing the books for your enjoyment.

For those of you interested in the Snippet Compiler tool I used in my demos, you can find it here.

The slides and code for the demos are attached and I did get around to converting them to VB for all of you VB people (I'm a VB.NET guy too; I really don't know why I coded all the demos in C# :)). 

Note that my application of the "in chain" matching rule turns out to be incorrect usage.  Don't do it like that!  Read more here.  I feel silly.

Note that if you are confused about which API to use, S.DS or S.DS.P, I discussed that in some detail here.  There is really no right answer, but hopefully that helps. 

To ask us any specific questions about LDAP programming, please use the book's discussion forum.  This is the only place that Ryan and I both use together.

As always, DEC is a treat and I really enjoyed all the conversations and interaction and am happy to see ADFS gaining a little traction.  Now, about that hot chicken...

DEC2007.zip (536.52 KB)
Friday, April 27, 2007 3:58:34 PM (Central Daylight Time, UTC-05:00)  #    Comments [1]  | 

Microsoft has added a new LDAP feature to AD in Windows 2003 SP2 and Longhorn server called the LDAP_MATCHING_RULE_IN_CHAIN.  Essentially, it is an extension filter type that allows you to search withing the content of a distinguished name-syntax attribute and do matching throughout the entire chain of linked values instead of just within the immediate values.  The docs are here and the syntax looks like this:

(memberOf:1.2.840.113556.1.4.1941:=CN=some group,CN=xxxx,DC=xxxx,DC=xxxx)

I showed some examples of this in my talk at DEC where I used some searches with and without the extension filter type to show traversal of some nested group membership.

Ryan also wrote about this a while ago and discovered that while useful, this technique is very slow for expanding group membership and it seems to be much faster to just use recursive searches.

As it turns out, the problem is that we were trying to use this feature incorrectly.  The "In Chain" filter type should NOT be used for transitive link expansion!  It is intended to be used for matching only.  Perhaps that's why they called it LDAP_MATCHING_RULE_IN_CHAIN.  :)

As such, you should really only use it in a base level query.  If you use the filter shown above in a base level query, it will still tell you if the object that is used as the search root is a member of the specified group anywhere in the nesting chain, but it will perform fine.  If you use it for anything else, you are asking for trouble. 

Now, when will we get some sort of transitive link expansion widget that actually works well for this purpose?

Friday, April 27, 2007 2:24:21 PM (Central Daylight Time, UTC-05:00)  #    Comments [2]  | 
Thursday, January 25, 2007

After some lofty goals of getting this blog jumpstarted back in the fall, I did just the opposite and let it drop into a black hole.  To all of my faithful readers, I humbly apologize.

As part of an effort to gain some momentum again, I thought I'd try to rattle off a quick post here.  Today's topic is intended as a continuation of my article on series of "things you can do in System.DirectoryServices.Protocols that you can't do in System.DirectoryServices".  We already discussed the ability to do Digest authentication and process server certificates.  Today's topic shows how to use S.DS.P's ability to invoke arbitrary extended operations using the ExtendedRequest/ExtendedResponse message types.

Briefly, extended requests in LDAP allow directory vendors to create whole new types of operations that are not built in to the base LDAP specification.  Essentially, the directory advertises that it supports a specific type of extended operation via the "supportedExtension" attribute in RootDSE (not loaded by default, so make sure you add it to your attribute list!).  If the client knows how to pass in the data to the extension operation and knows how to interpret the results, it can invoke the extended operation.  If not, too bad.

One such common extended LDAP operation is the "Who Am I?" operation as defined by RFC 4532.  Basically, the intent is to allow the LDAP client to issue the extended operation and receive a response that provides information about the identity of the user who is currently authenticated to the current LDAP connection via a previous bind operation (if a bind was performed). 

Interestingly, ADAM now supports the "Who Am I?" operation, as will Active Directory in the Longhorn server time frame.  As such, it makes an excellent target for investigation here as "Who Am I?" currently has no strongly typed wrapper in S.DS.P (yet) and is also exceedingly easy to both call and interpret the returned results.  I just learned about the existence of this thing via a thread on the exceedingly great mailing list "activedir.org".  I figured I should go ahead and give it a whirl to see how it works. 

Without further ado, I humbly submit a few lines of code that demonstrate binding to an ADAM instance on localhost on the default port (389) as the currently logged on user and then invoking the "Who Am I?" extended request to find out my own Windows user name (in case I forgot it :)). 

public class AdamWhoAmI
{
    public static void Main()
    {
        using (LdapConnection con = new LdapConnection("localhost"))
        {
            con.Bind(); //use default credentials. Current user can bind to ADAM...
            
            ExtendedRequest whoami = new ExtendedRequest("1.3.6.1.4.1.4203.1.11.3"); 

            //whoami OID shown above, as per RFC


            ExtendedResponse whoamiResult = (ExtendedResponse) con.SendRequest(whoami);
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(whoamiResult.ResponseValue));

            //the result is a simple byte array that happens to contain UTF8 text
        }
        Console.ReadLine();
    }
}

Wow, pretty fancy stuff!  This actually compiled and ran on my very first try, which usually doesn't work out.  As such, I feel confident in describing this as "exceedingly simple".  On my machine, the result looks something like:

u:machine\joe

The interesting thing here is that this "just works" and it shows a semi-practical thing you can do with this particular extensibility mechanism in LDAP that just happens to not be exposed in System.DirectoryServices at all.  I'm sure there are some practical uses for this, but the main point is just to show how to use this feature with the simplest example possible.

Thursday, January 25, 2007 5:32:39 PM (Central Standard Time, UTC-06:00)  #    Comments [0]  | 
Saturday, October 28, 2006

One of my favorite softies, Eric Fleischman, is doing some S.DS.P programming these days.  He demonstrates a useful technique, which is how to do WKGUID searches to find well-known objects in AD or ADAM.  For those not familiar with this, AD and ADAM have a set of "well-known" objects that the directory always contains and applications may need to find to do reasonable things.  However, the names of these objects may be internationalized, so it may not be possible to find them by name across different implementations of the directory.  By referencing them by the "well-known" GUID, a published value, you can always find them.

One of the things we didn't do in our book was provide matching S.DS.P samples for most of the techniques.  In fact, we barely covered it at all.  I still have some regrets about this, although I also think we probably did the right thing.  If the book had been 800 pages due to all the extra samples, that would have been tough.  I do think there might be a place to supplement the material with online materials at some point.  I'm not sure another print tome dedicated to the subject would ever sell very well as the audience is probably limited and likely needs the material less than than the audience we originally wrote the book for (I could be wrong about this).  The internal debate continues...

One of the things that Eric does hit on is the somewhat sad state of affairs of the MSDN DS API documentation.  The S.DS.P docs are especially heinous, as they include almost NO samples and even have some stuff documented as wrong or misleading.  For example, the documentation for the DistinguishedName property on the SearchRequest class doesn't even succeed in identifying that as the search BASE of the search, instead calling it the "object to search for".  If it wasn't for the fact that there is no other obvious choice, an experienced LDAP programmer might not even understand what it is doing!

Additionally, the SDK docs for the directories themselves (AD and ADAM) are still mostly written for the unmanaged VB or C++ programmers with little attention to the managed code devs (although ADAM is much better about that).  However, you won't find a single S.DS.P sample in any of that.  It is as if the API didn't exist.  The managed code docs are also spread out between the .NET SDK and the Directory Services SDK, so it is hard to know where to look to get help.

There are two things missing here. 

  • The S.DS.P docs are just embarassing and must be improved.  I'll give it some more time. 
  • The overall DS API documentation lacks strategy and makes the platform harder to consume than it needs to be.  Sure, that helps drive sales of our book, but trust me, we didn't write it to make money.

On the bright side, there is a lot of low hanging fruit available here. :)

Saturday, October 28, 2006 4:13:16 AM (Central Daylight Time, UTC-05:00)  #    Comments [1]  | 
Friday, October 06, 2006

An interesting thread popped up on the newsgroups today involving a poster who was trying to prevent his LDAP client from doing client certificate authentication by using a feature in the LDAP API that allows you to supply a callback function that handles the client certificate selection process.  Unfortunately, the poster found what he thought was a memory leak.

The intrepid Joe Richards jumped in and with the help of the trusty MVP source access, his brain and a helpful MS employee, found the bug, reported it and created a workaround.  Nice job!  Joe goes into excruciating detail, so I won't add anything to his investigation.

The interesting thing for .NET developers is that this issue potentially affects users of System.DirectoryServices.Protocols (SDS.P).  SDS.P provides a direct wrapper around the leaking callback function in the LDAP API with the QueryClientCertificateDelegate method.  I did a a little Reflectoring and it looks to me like the workaround Joe suggested isn't implemented in SDS.P.

Thus, if you are using this callback, you could have the same memory leak.  The thing that really sucks is that Joe's workaround requires that you have a pointer in the allocated structure so that you can pass it into the appropriate deallocation function.  However, the .NET function prototype conveniently marshals all the data for you into an array of byte arrays, so you no longer have the pointer.  As such, you can't implement the workaround in .NET at all.

As such, in order to deal with this situation, you probably need to the QFE that Joe mentions (or perhaps a patch to SDS.P, although to my knowledge no such thing exists). 

I discussed this particular function's friend, VerifyServerCertificateCallback, in my posting describing how to check a domain controller certificate expiration date on its SSL/LDAP cert.  The QueryClientCertificateCallback is useful when you need to pick a specific client cert, or when you want to prevent the automatic Schannel layer for looking for a client certificate at all (which is what the original poster was doing).  The client certificate negotiation can be really slow and isn't really helpful in a lot of circumstances if you don't want to try to use client certificate authentication to bind with (which is another black hole of MS LDAP client documentation void best saved for another post). 

Saturday, October 07, 2006 1:35:50 AM (Central Daylight Time, UTC-05:00)  #    Comments [1]  | 
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]  | 
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]  | 
Thursday, August 03, 2006

In a previous posting, I mentioned that even though System.DirectoryServices (SDS) and System.DirectoryServices.Protocols (SDS.P) share a lot of overlap in functionality, there are some important things that can only be done in SDS.P.  One that I mentioned is handling server and client certificates that are using when doing SSL/LDAP.

Both SDS and SDS.P support SSL/LDAP.  Basically, if your server is configured to support SSL (and both AD and ADAM can support this, although neither have the required certificates provisioned "out of the box"), you can just turn on SSL LDAP with the appropriate setting and it will just work.  However, SDS doesn't give you much control over the process.  It doesn't expose any information about the certificate the server supplied, nor does it let you choose which client certificate to use (if a choice is possible).  Additionally, if there is a problem with the server's certificate such a certificate trust issue or expiration, with SDS, that is automatically counted as an error.

In SDS.P, we get more control.  Not only can I inspect the certificate to find out information about it, but i can select my own client certificate and I have control over whether or not to trust the server's certificate if I want.  I can thus choose to ignore potential problems (at my peril, of course) and connect anyway.

SDS.P implements the client and server certificate verification stuff via callbacks implemented as delegates, specifically the QueryClientCertificateCallback and VerifyServerCertificateCallback delegates.  In order to use them, you create a method with the matching signature of the delegate and then set the LdapSessionOptions.QueryClientCertificate or LdapSessionOptions.VerifyServerCertificate properties for options set up on your LdapConnection.  Do this before you bind.  BTW, the LdapSessionOptions class is where a lot of the cool advanced features are in SDS.P that allow you access to features not exposed by SDS.  It is worth spending some time staring at it.  :)

Here is a brief code snippet example of doing this in C#:

LdapConnection con =
new LdapConnection(new LdapDirectoryIdentifier(dc + ":636", true, false));
con.SessionOptions.SecureSocketLayer = true;
con.SessionOptions.VerifyServerCertificate =
new VerifyServerCertificateCallback(ServerCallback);
con.SessionOptions.QueryClientCertificate =
new QueryClientCertificateCallback(ClientCallback);

Here, the method ServerCallback looks something like this:

public static bool ServerCallback(
LdapConnection connection,
X509Certificate certificate
)
{
//do some stuff here
}

The Sample App

Now, let's put this together into a useful example.  In my scenario here, I have an AD domain with third-party issued certificates that I'm responsible for maintaining by hand.  If these certificates expire, my apps that use SSL/LDAP may break and cause me embarassment and scorn amongst my colleagues (or worse).  As such, I'd like to have a list of the certificates on my DCs in order of expiration, so that I can plan my renewal/replacement work in advance.

The sample code shows how we can use the VerifyServerCertificateCallback to simply grab the server's current certificate and find its expiration date.  It shoves that into an in memory data structure and eventually dumps out the results to the console.  The sample shows the power of the new System.DirectoryServices.ActiveDirectory (SDS.AD) namespace to quickly and easily enumerate the DCs in my domain with no hassle.  This also shows how these two different LDAP stacks can be integrated to do the things that each is best at.

For good measure, I tried to make the sample fast.  We might have a lot of DCs, so instead of querying them one at a time and waiting for all that network stuff to happen, why not use the .NET thread pool and let it hit them "simultaneously" (more or less)?  Try that with that goofy script stuff.  :)

I wrote this sample using Jeff Key's fantastic Snippet Compiler 2, which is a nice tool for rattling off quicky .NET stuff.  It doesn't make you bust out VS.NET and create a whole project for throwaway stuff.  It also doesn't make you buy VS.NET, although Microsoft has made that easier with the express editions.  Sure, you can always use the free SDK, but this is quite a bit easier to use than notepad and a hand-written MSBuild file.  You'll need assembly references to mscorlib,system,system.directoryservices and system.directoryservices.protocols.  You'll obviously need the .NET 2.0 runtime installed to use the binary.

Enjoy!  All feedback is appreciated.

adcertexp.zip (4.66 KB)
Thursday, August 03, 2006 6:34:49 PM (Central Daylight Time, UTC-05:00)  #    Comments [7]  | 
Sunday, July 30, 2006
A Tale of Two LDAP Stacks

As you may or may not know, .NET contains two completely different ways to program LDAP.  Version 1.x contained the System.DirectoryServices (SDS) namespace, which is a managed wrapper around ADSI and supports LDAP programming via the LDAP ADSI provider.  This is what most people tend to think of when then think of LDAP programming in .NET. 2.0 adds a bunch of new features, but is generally speaking the exact same thing for S.DS.

However, .NET 2.0 also contains something new and entirely different called System.DirectoryServices.Protocols (SDS.P).  Instead of using ADSI, SDS.P interops directly with Microsoft's LDAP API (implemented in wldap32.dll), which is a lower level mechanism for doing LDAP that provides complete control over all of the features exposed by the LDAP in general, including some things that are simply not possible with ADSI (either due to the the design of ADSI or the fact that they things are obscure enough to have never been wrapped).  Another interesting aspect of SDS.P is that it also supports the Directory Services Markup Language (DSML) protocol, which basically let's you talk LDAP using SOAP over HTTP.  Microsoft actually ships a DSML server for AD and ADAM, but it is surprisingly unpopular, especially given all the focus on SOAP these days.  One of the  big problems for adoption with DSML in the past was that you had to do all the SOAP stuff in raw XML as there was no WSDL to generate friendly proxies, so maybe having an easy to program API will change that?  I've never used it.  :)  Anyway, DSML and "normal" TCP/IP LDAP are actually implemented using a provider model, which makes the object model pretty easy to consume for both.  Most of it is identical except for the initial connection setup.

It Used to Be So Simple...

So, which to use?  In the past, there was a great natural disparity between pure LDAP programming and ADSI programming, as LDAP only supported C or C++ language bindings, while ADSI was designed from the go for scripting and OLE automation and was vastly easier to consume.  Even if you really needed some of the features you could only get with pure LDAP, the C compiler requirement tended to price most people right out of the skill set needed to accomplish such things.

However, that's really no longer true in .NET 2.0.  While it is definitely the case that SDS.P is generally more complicated to do the same stuff and will often take 2x as much code, the difference between the two is actually much smaller.  For example, I can now program SDS and SDS.P in VB.NET and can actually mix the two in the same program if I'm so inclined.  The gap is much narrower.

When Ryan and I first started working on the book, .NET 2.0 was still out on the horizon and we hadn't really seen much SDS.P yet.  We came from a background of having built some nasty stuff using SDS and felt like we had some really deeply useful information to share there.  However, as the writing took us longer and longer and .NET 2.0 got closer and closer, we realized that a) we really needed to cover .NET 2.0, and b) that SDS.P was a part of the story that we needed to figure out a way to tell.

The approach we took in the book to deal with this was a little mixed in my opinion.  We really didn't cover much SDS.P at all.  To the extent that we taught a lot of the fundamentals of programming AD and ADAM that could apply to any protocol, I think we were successful in providing a lot of information that could have been easily translated to SDS.P.  However, we didn't really take the approach of building up the basics like we did with SDS.  Instead, we basically just dove into SDS.P when we ran out of room in SDS, leaving some of our SDS.P examples as fairly hairy (the async one comes to mind here, especially as the first code sample to use SDS.P!).  Of course, if we had done the opposite, the book would have been much longer and we might have never finished, so there is that...

The other thing is that Ryan and I are both nerds and SDS.P is kind of like a new toy, so we are both now tempted to use it for everything, even if it takes twice as long, just because it is interesting.  However, that doesn't make any practical sense, especially when someone else is paying for your time and may have less skilled developers inheriting your stuff!  So, when to use SDS.P?  I'll submit my humble list and then try to show some examples of some of this stuff over the next few weeks:

When You Really DO Need SDS.P

  • You really do need to do DSML (maybe I'll meet one of you someday :))
  • Digest authentication against ADAM
  • Explicit control over client or server certificate handling with SSL/LDAP
  • Phantom root queries
  • Fast concurrent binding in AD 2003 and ADAM (not the same as the FastBind flag in SDS!)
  • Access to the LDAP Compare operation
  • Working with LDAP V2 directories that support a custom schema (nothing MS ships...)
  • Applications where explicit control over the LDAP connection is important for scalability, such as programmatic LDAP authentication, or web applications that use Kerberos delegation to acces the directory and support many many different simultaneous users (ADSI really fights you here)
  • Applications where the ADSI schema mapping mechanism is more of a problem than a boon
  • Server apps where there can be a real scalability benefit to using the full asynch capabilities of SDS.P
  • Needing to change credentials when chasing a referral (a nasty situation, but sometimes this happens)

There will also always be people who come from an LDAP or JNDI background, for whom SDS.P will probably feel more comfortable.  Additionally, if one were building a fully abstracted DAL over an LDAP store, the incremental difference in using SDS.P might not be so much, especially if you invested up front in some code gen.  But otherwise, SDS is probably still the sweet spot for most of us.

In my next post on this topic, I'll show how you can do something with SDS.P and SSL that you simply cannot do in SDS--get the expiration date of each certificate on each DC in your forest!  If you use external certificates and need to renew by hand, this is pretty handy.

Sunday, July 30, 2006 9:00:33 PM (Central Daylight Time, UTC-05:00)  #    Comments [5]  | 

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