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]  |  Tracked by:
"Joe Richards find LDAP Client API Bug that Affects S.DS.Protocols" (Joe Kaplan) [Trackback]
"ADAM WhoAmI Control in S.DS.P" (Joe Kaplan) [Trackback]

Friday, August 04, 2006 3:53:09 PM (Central Daylight Time, UTC-05:00)
This is useful stuff and I'm looking forward to more on S.DS.P, this is shaping up to be a great blog!
On cert expiry, this was always a pain on DCs as it required
a reboot to pick the new certificate, just recently a new operational
attribute was introduced to remove the need for the reboot, see KB917268.
Lee Flight
Friday, August 04, 2006 5:31:58 PM (Central Daylight Time, UTC-05:00)
Thanks Lee!

I had no idea about the reboot and the new operational attribute. I've never actually replaced a cert on a production DC (they don't let me near them at work :)), just on my own ADAM instances. That is good to know.

I'll try to keep new stuff coming.
Thursday, March 22, 2007 1:10:45 PM (Central Daylight Time, UTC-05:00)
Hi Joe,

I am resubmitting an earlier comment as I think it may not have registered properly.

I noticed your samples use the ActiveDirectory namespace. Does this mean they are specific to Microsoft's Active Directory product. If so do you have generic LDAP samples. I am working with another type of LDAP server.

I have not yet been able to compile the samples yet as it requires additional assemblies to be defined.

Thanks .. John
Wednesday, April 11, 2007 10:28:50 AM (Central Daylight Time, UTC-05:00)
I tried using your code sample but found that when I set con.SessionOptions.SecureSocketLayer = true, the value doesn't actually change. I've debugged and stepped through the code, and the SecureSocketLayer property doesn't change - it stays false even after being assigned to. I'm working on Vista and suspect this may have something to do with it. I'd appreciate any feedback or ideas you have.
Wednesday, April 11, 2007 2:15:24 PM (Central Daylight Time, UTC-05:00)
Hi Daniel,

I have no idea why you might be having this problem. I don't have a Vista build working right now, so I don't have a good way to try to duplicate this issue, but I can't imaging that this might be OS-specific, as this code is in the same distro of .NET that everyone else gets. I can't repro on my machine. I'm not sure what to recommend to you to do unfortunately.
Monday, May 07, 2007 3:32:35 PM (Central Daylight Time, UTC-05:00)
Hi Joe,

Got your book and am quite pleased with it. I have tried; however, to compile the samples and got some error messages.

Below is a truncated listing of the compiler errors. What is needed to compile it correctly? I could find no reference in your book to nunit.

Thanks .. John.Anderjaska@dsainc.com

301-619-3565

------ Build started: Project: DotNetDevGuide.DirectoryServices, Configuration: Debug Any CPU ------

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets : warning MSB3245: Could not resolve this reference. Could not locate the assembly "nunit.framework, Version=2.2.8.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.DirectoryServices.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.DirectoryServices.Protocols.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /reference:obj\Debug\Interop.ActiveDs.dll /debug+ /debug:full /optimize- /out:obj\Debug\DotNetDevGuide.DirectoryServices.exe /target:exe AdUtils.cs BindingContext.cs Chapter10\DomainPolicy.cs Chapter10\LdapPasswordModifier.cs Chapter10\Lockout.cs Chapter10\PasswordExpires.cs Chapter10\Program.cs Chapter11\Group.cs Chapter11\GroupExpander.cs Chapter11\GroupExpander2.cs Chapter11\Program.cs Chapter12\LdapAuth.cs Chapter12\NTAuth.cs Chapter12\Program.cs Chapter4\Program.cs Chapter5\Async.cs Chapter5\DirSync.cs Chapter5\PartialResults.cs Chapter5\Program.cs Chapter6\Program.cs Chapter7\Program.cs Chapter8\AuthStatusChecker.cs Chapter8\Program.cs Chapter8\SchemaGuidConverter.cs Chapter9\Program.cs Configuration\LdapSettings.cs Chapter3\Program.cs Enums.cs NativeMethods.cs Properties\AssemblyInfo.cs Structs.cs TestUtils.cs Win32.cs

C:\Program Files\Microsoft Visual Studio 8\DotNetGuide\DotNetDevGuide.DirectoryServices\Chapter4\Program.cs(6,7): error CS0246: The type or namespace name 'NUnit' could not be found (are you missing a using directive or an assembly reference?)
Tuesday, May 08, 2007 3:38:39 AM (Central Daylight Time, UTC-05:00)
Hi John,

The "complete" samples are actually Ryan's thing more than mine, so you might get a better answer at the book's web forum (www.directoryprogramming.net).

However, NUnit is just a common unit testing framework you can download from nunit.sourceforge.net. Ryan used it in his complete samples to create a mechanism to actually execute some of the sample code and show how it works instead of creating some weird Windows forms app with a bunch of buttons for you to click. That might have been more intuitive for some people though.

We didn't reference it in the book because it isn't really that relevant to samples themselves (we weren't trying to teach NUnit), and at the time Ryan put together the complete samples, the book had long since gone to print. That stuff came about in the time after we finished writing and started working on the website before the book went live.

The samples probably could use some additional documentation I suppose.

You really don't need NUnit. If you want, you can comment out all of the code that has the [Test] attribute on the method and it should still compile. You just won't have anything to actually run if you do. :) It is a very useful tool though (our developers at work are required to use it for every piece of production code we write, although this doesn't alway happen in practice).

I hope that helps.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Theme design by Jelle Druyts