Hi,

This definitely isn't the first blog post on this topic, and it certainly won't be the last - but hopefully it will bring some peace and understanding to those struggling to get Kerberos working in a live SharePoint deployment.

First off, I must credit Martin Kearn's excellent posts here that were invaluable in getting me started - and will be invaluable to you too.

Why Use Kerberos?

There are a number of good resources that aid understanding Kerberos - the best (imho) being from Keith Brown's .NET security guide, published on the web. If you want to know what Kerberos is, what SPNs are, what delegation is etc. go read items 59-63 of his book here:

http://www.pluralsight.com/wiki/default.aspx/Keith.GuideBook.HomePage

But to summarise the key points from a SharePoint perspective, if you want better authentication performance than NTLM, use Kerberos. If you want to pass your user's credentials through to back end resources (like for example accessing the database exposed through a Business Data Catalog) - then you'll need Kerberos delegation.

Crucial SPN Knowledge

Lets just define the parts of an SPN first - an SPN looks something like this ( a typical SPN for a SQL Server in this case ):

MSSqlSvc/bob.domain.local:1433

Where "MSSqlSvc" is the service class, "bob.domain.local" is the host and 1433 is the port. The port part (including the colon) is optional.

SPNs are basically arbitrary strings that applications "know" how to assemble. They are stored in AD in a multi-valued attribute called servicePrincipalName on user principals. (Multi-valued attributes are what they sound like, attributes that can have none or more values and are typically displayed as a comma-delimited list.)

SetSPN is the tool you should use to create SPNs. When you use SetSPN -A you are just setting this attribute. It's perfectly valid to do this with adsiedit.msc or any other AD editing tool - I don't recommend you do this because SetSPN does some validation to ensure the SPN follows syntax conventions that a direct edit would bypass - I'm just making the point that there is no magic going on and that SPNs are just strings. To set an SPN (with SetSPN or otherwise) you must have permission to write to ServicePrincipalName attribute on the target principal (and that is your least privilege requirement) - although it's a good idea to be able to read them as well.

When a client application wishes to authenticate to a server application with Kerberos, the client must acquire a ticket from the KDC ( = Key Distribution Center which is a role played by Domain Controllers in Windows). To do this, it must tell the KDC which SPN it wants a ticket for. The KDC will take this SPN and look for a principal that has a matching string in its servicePrincipalName attribute.

At the point a couple of things can go wrong: (1) The KDC is unable to find a match in the AD. (2) The KDC finds more than one match in the AD. In either case, you're stuffed and Kerberos authentication will fail. At this point most applications will silently fall back to NTLM authentication and everything will appear to work - unless you need delegation or you have demanded Kerberos authentication. Not many apps demand Kerberos, they usually "Negotiate" an authentication protocol. If you are trying to delegate, then what often happens is that the machine at the end of the second hop gets a NULL identity turning up (see Keith Brown's explanations linked to earlier).

So remember: you can not have two identical SPNs registed to different accounts in your forest. If you do this, neither will work.

So how does the client know which SPN to ask for? Well that's very interesting. It turns out the answer is largely "because it does". The service class and host + port parts of an SPN are completely arbitrary. The client must therefore "know" these things to correctly form an SPN. The one thing it doesn't know (or need to know) is the identity of the principal running the service - the KDC figures this out and passes back a ticket that only the principal against which the SPN is registered will be able to decrypt.

In the case of Internet Explorer, the SPN requested is formed by using a service class of HTTP and a host as specified in the URL, converted to an FQDN (fully qualified domain name) if possible.

Here's the rub: IE never appends the port number to the host name when contacting the KDC for a ticket - even if your server is on a port other than port 80! So for all those SharePoint sites you have on non-80 ports, if you've been specifying a port number in your setspn command, then you haven't been using Kerberos!

Because of the way IE works, its typical to register two SPNs for every web-site. One with the netbios Host name and one with the FQDN host name - this keeps all the bases covered. Just don't put in the port!

So if I ask for http://moss:10000/sites/test for example, then IE will pass an SPN of "HTTP/moss.sharepoint.local" to the KDC (assuming moss is resolved to a box in my sharepoint.local domain) or just "HTTP/moss" if it can't be resolved.

UPDATE: Thanks to Brad Turner for his research on this point - it turns out that there was a fix released for IE that enabled a special registry key setting that changes IE's behaviour so the port number IS included. However, I don't recommend you do this because it is a client side fix, and it's probably hard to guarantee all your clients will have this setting. Even if you use Group Policy to enforce a registry setting, I still don't like it. If you really want to know the details, drop me a line; but be aware I will raise my eyebrow at you!

If you are using host-headers on your website, then use those in your SPN. Remember it's whatever is in the address bar that IE will use to request the SPN. A host-header is set on the dialog you get from the "Advanced..." button on the "Web Site" tab of a web site in IIS 6.0 Manager. It allows IIS to have multiple web-sites on the same port and IIS looks at the address (host header) requested by the client to work out which website to direct the request to. For example, in my MOSS installation I have something like:

portal.sharepoint.local, mysite.sharepoint.local, ssp.sharepoint.local etc. all pointing to the same IP address. The host header can be specified in Central Admin when creating web applications.

You Probably Need Multiple Host Headers

This throws up an interesting point that in most cases this means you will need multiple host-headers in order to be able to use Kerberos in a least privilege environment. Every application pool in a least privilege environment has its own identity. This means that your web applications, which are sitting in different application pools, can't be differentiated by simply using different ports. If you did this, since IE ignores the port when forming its SPN request, the SPNs for each application pool identity would have to be the same - and you can't register the same SPN against multiple accounts! (Oh how I wish this would throw an error!) Thus, a different host header is required to get a Kerberos ticket for each application pool identity in which your SharePoint web applications reside.

How Do I Know Its Working?

Good question. The easiest way is to download Kerbtray.exe (don't worry this says Windows 2000 - it works on all versions). This tool lets you examine the Kerberos tickets for the logon session of the current interactive user only. This means that you can't do something like "Run As" to see the tickets of a service - because you'll be in a different logon session even though its the same principal. However, you can run this tool in a couple of useful places:

  • Run it during initial set up of the farm. At this point the setup account connects to SQL server so you should see a ticket for your SQL box - MSSqlSvc/sql.sharepoint.local:1433 for example.
  • Run it on any client machine connecting to a SharePoint web site. You should see a ticket for the web site in the list - HTTP/portal.sharepoint.local for example.

Also, on SQL Server 2005 you can run a query to check how clients are authenticating. You should see "KERBEROS" in the auth_scheme column:

SELECT login_name, program_name, host_name, auth_scheme
FROM sys.dm_exec_connections C INNER JOIN sys.dm_exec_sessions S
ON C.session_id  = S.session_id

Delegation

If you want to delegate then you need to do more than just create SPNs. You must allow the services that will delegate right to do so. This is described in Keith Brown's guide again. For SharePoint, the services that are most likely to need rights to delegate are the application pool accounts and the SSP service account. You almost certainly don't need to give the computer accounts delegation rights!

A common scenario is BDC delegation where you want your end-users to authenticate against a database directly when querying a BDC web-part for auditing purposes. In this case you just need the application pool hosting the site hosting the web part to have delegation rights - and that's it!

Hope that helps!

James

 ADDENDUM:

Since writing this article the infrastructure update has added some new functionality that includes custom SPNs for SSPs. It's covered in a new technet article and you can read all about it here: http://technet.microsoft.com/en-us/library/cc263449.aspx#section14