In my last post I talked about how Azure AD does Kerberos Single Sign-On. Conceptually it’s a simple process, but when you dig into the details of the implementation, there are some serious hurdles to overcome.
The Active Directory side of things is straightforward — it’s just a matter of manually creating an SPN and keeping the secret in sync. It gets really complicated on the Azure AD side of things though.
Consider the history of web-based Kerberos. IIS has supported this for decades by way of an ISAPI HTTP module that parses out the header and hands it off to the Negotiate SSP, which eventually gets it into the Kerberos SSP within LSASS to validate. This is a useful design because it moves any secret keys out of the IIS worker process and into the protected LSASS process. The problem with this is that you don’t have a whole lot of control over the validation process.
There are a number of knobs you can fiddle with in the process like specifying custom SPN credentials using AcquireCredentialHandle as well as what sort of flags you want to use, but as far as I can tell that’s about the extent of what you can modify because the SSP makes some specific demands that the ticket comes from a trusted authority, which in this case is the domain which the machine is joined. This seriously limits our abilities to use the built in Windows functions in the cloud.
One interesting behavior worth noting though is that you can sort of see that a ticket is validated even if its not from a trusted authority because the audit entries show up differently. It logs it as a decrypted ticket, but that it’s from an unknown authority and therefore cannot be accepted. This leads me to believe you might be able to get in the middle of it and validate it yourself, but I haven’t been able to figure it out despite my best efforts.
Given these limitations I decided to see what I can do about creating a library that lets you authenticate Kerberos tickets manually. Now, there are a number of available libraries out there, but they’re either written in Java (eww) or C/C++ (oww), and they’re difficult to understand. Enter Kerberos.NET, a managed ticket parser and validator.
It’s pretty easy to use. Create an instance of the validator, pass in the security key, and validate a ticket:
Here’s the cool thing: it’s written entirely in managed code and doesn’t take any hard dependencies on native components. That means it’s reasonably portable and can run any environment where ASP.NET Core can run.
The caveat of course is that I haven’t tried it yet.
Now, some of you might be wondering why on earth you want to use something like this over a web-friendly federation protocol like SAML or OpenID Connect. Well, frankly, you shouldn’t use it (gasp!) — federated protocols are a better solution these days. But if you need real Kerberos out in the cloud you can use …a vetted option like the SPNEGO module for Nginx (I don’t actually know if it’s vetted, but you get my point). In other words don’t use this library unless someone much smarter than me has vetted it (and there are plenty of people that fit that requirement).
Additionally, there are some important things to keep in mind if you do decide to use this module.
- It currently only supports RC4 (I know, I know, but AES CTS is painful in .NET)
- The validation mechanism hasn’t been vetted
- The token cache is not self-managing (it’ll leak memory like a sieve)
- It assumes crypto-validation is good enough — it doesn’t check that SPNs match
- It doesn’t parse the PAC data structure and you therefore don’t get access to any of that useful data
There are more reasons not to use this library too I’m sure.
For those wondering about the picture of the puppy… Kerberos (AKA cerberus) is the name of the three-headed dog guarding Hades’ underworld. It probably stands for “spotted”, meaning Hades named his dog Spot.