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]  | 

One of the tricks you can do with the Active Directory Federation Services (ADFS) home realm discovery process is get a user to skip the home realm discovery page completely if you embed a query string in the application URL that tells ADFS what realm to use.  The query string is:

whr=xxxxxx

where xxxxxx is the federation URI of the partner (which they tell you when you set up your federation, or you create when you are setting up your test lab).  That typically looks like:

urn:federation:myorganization

Thus, the whole url might look like:

http://www.joekaplan.net/?whr=urn:federation.myorganization

(no, this site is not federation-enabled and won't be any time soon...)

Using these home realm query strings is very handy, not only for getting your own organization's users to the target app quicker by allowing them to skip a page that may potentially have many choices, but also just for testing.  The query string overrides the persistent cookie you may have that identifies your home realm, so you can use this to avoid having to delete your cookies all the time.

However, if you want to refer to the resource partner's account store with this trick, you don't use the resource partner's federation URI.  Instead, you use the "built-in" URI:

urn:federation:self

I'm sure this is probably documented somewhere (or maybe not; the ADFS docs have a ways to go...), but I had to figure it out the hard way and I thought I'd share.

Monday, July 31, 2006 4:57:52 PM (Central Daylight Time, UTC-05:00)  #    Comments [3]  | 

The Problem

So, let's say you are building a claims-based application for Active Directory Federation Services (ADFS) and you want to use VS.NET 2005 to do this.  Alternately, let's say you are customizing some of the built in pages that come with the federation server or federation server proxy and want to use VS.NET for that.

As things stand today, there is a minor annoying friction point here as the ADFS installer doesn't provide a nice clean way to set a reference to the code you need in System.Web.Security.SingleSignOn and such.  Even if you are developing on a 2003 box (which a lot of serious web devs do, although I still stumble along with XP as its the company standard) and actually install ADFS, you don't see these assemblies in the .NET tab on the add reference UI.  The problem is that the ADFS installer puts the assemblies directly in the GAC and doesn't leave a copy on the "normal" file system (which the compilers actually need), nor does it bother to create a registry key under:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx

(which is the little bit of magic that gets your assembly in the .NET tab).  Multiply this by a large team working on a claims app, and this can be a drag.

The Solution

So, what's a dev to do?  Well, you can definitely copy the assemblies yourself, GAC them and create a registry key and probably do all that with a little batch file if you are so inclined.  Or, you can go over the top and create your own MSI installer package that does this for you.

As a little experiment to see if I could do this quickly and learn how to do some new stuff in MSI that  thought I'd need later on, I did the latter.  It took about an hour over lunch one day.

I'd love to just distribute the msi package directly, but I won't consider doing that unless MS gives me permission to do so (which I don't expect to get).  So, that won't happen.  However, I will tell you how to build your own (fairly) painlessly.  For this, you will need:

Ingredients

  • One copy of Windows Installer XML (WiX) 2.0 (3.0 might work as well, but I didn't bother trying)
  • One copy of the ADFS.msi package that comes with R2 (dig around on the install media for it), or alternately, the 3 assemblies that come with ADFS that are installed the GAC that you hoisted from a normal install
  • One copy of my sample WiX file that will create the MSI (see link at bottom)

Recipe

  1. Place the vsadfs.wxs file in a new clean directory on your file system
  2. Use dark.exe from the WiX distro to reverse engineer ADFS.msi in order to get its files into a handy .cab format, ignoring the error from Dark (<wixpath>\dark -x bin -out adfs.wxs adfs.msi)
  3. Grab the three assembly files (S.Web.Security.SSO *) from the cab file and stick them in a directory called "files" underneath the directory where you put my vsadfs.wxs file
  4. Run these two commands from the command prompt, filling in the path to your WiX binary install where appropriate (unless you have that on your path, obviously):
    1. <wixpath>\candle vsadfs.wxs
    2. <wixpath>\light -out vsadfs.msi vsadfs.wixobj <wixpath>\wixui.wixlib -loc <wixpath>\WixUI_en-us.wxl

You now have a working msi installer that will install (and uninstall and repair!) these things for you.  You may notice that the UI thing I used, WiXUI, shows a license screen and shows the CPL license there.  That's because we didn't supply a license.rtf file in our directory, so WiXUI picked up the one that comes with WiX in its directory.  Feel free to change it.  If you want to eliminate that dialog completely, you are on your own.

Caveats

  • This will NOT give you a working version of an ADFS-enabled web server!  You cannot use this to actually generate a SingleSignOnIdentity object to be used for testing.  You would need some sort of a mock object thingy to do that for you (may that's next?).  All it really does is let set a reference in VS.NET and compile the code, but that's better than what you had before.
  • Additionally, my current installer uses different component GUIDs for the version of the files that go in the GAC.  I'm not sure if it would be the right thing to reuse the component GUIDs from the real ADFS install which puts these components in the GAC or not.  The only time this might be an issue was if you ran this installer on a box that already has ADFS installed.  I'm not sure what would happen, but right now it seems better to keep them separate. 
  • The installer allows specify the install location if you want to change it and breaks the VS.NET and GAC install stuff into separate features so that you don't have to install both if you don't want.  This can be customized via the feature tree dialog.
  • The installer does not bother to check if you have VS.NET 2005 or even .NET 2.0 installed.  This would be a good sanity check for mass market usage, but I'm hoping the users of this are not too insane. 
  • The installer defaults to a "per-machine" install instead of per-user.
  • Repackaging Microsoft's installer is probably a violation of something and may void your warranty or get you in some sort of trouble that I had not fully considered when offering this suggestion.

The Future

I'm hoping that future versions of ADFS will have this scenario already considered so that things like this won't be necessary.  Perhaps we can convince the team to ship this as part of the product or as a free download/sample solution?  I wouldn't be surprised to see these assemblies in a future version of the .NET Framework as well, which would mean that we'd get this for free another way.  Until then...

(Updated 2 Aug 2006 changed to zip file)

vsadfs.zip (1.54 KB)
Monday, July 31, 2006 4:18:01 AM (Central Daylight Time, UTC-05:00)  #    Comments [3]  | 
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