A while back I wrote a blog post about setting up Kerberos constrained delegation. As a bit of an re-introduction, a lot of the value of the Kerberos authentication protocol is that it allows an application or service to impersonate a user in order to get resources on that users behalf. This impersonation is also called delegation, and is most commonly seen in the “trusted for delegation” settings.
Active Directory in Server 2003 introduced new Kerberos capabilities. One of those was constrained delegation. Constrained delegation assumes that scenario where an application or service is impersonating a user in order to resources on that users behalf, but limits or constrains that impersonation so that it can only go to specific remote resources. The term “double hop”, though a bit over used in Kerberos discussions, comes in handy in this explanation because we can simply say that the application or service that is impersonating our users (the first hop) can only do its second hop to specific computer(s) and get specific resource(s) on that remote host.
This feature is sometimes referred to by the acronym KCD, or Kerberos Constrained Delegation. As you know, nothing worthwhile should be without an acronym.
This is an added layer of security. Consider one of your impersonated users to be the head of your accounting department. If this user is impersonated at a web server, for example, but that web service account is constrained to delegate only to a SQL server and service then that web service account couldn’t connect to another remote computer-or even that SQL server-and access an Excel spreadsheet as the head of accounting. That is because the SPNs are for the SQL service use MSSQLSvc and access to remote files across the network uses the CIFS service and SPN. That scenario assumes that the web service code or identity has been compromised in some manner already of course or else it would not be trying to do something naughty like that it in the first place.
So KCD is about limits; limiting where an identity can impersonate a user. The logical decision in implementing this is to make the number of places that the service can impersonate the user a small number. This would limit the security exposure of that impersonated user. This is a logical limit from a security perspective since if you plan to impersonate users to a large number of “second hop” hosts and services then why use constrained delegation at all? Why not use “traditional” delegation without constraint?
I’m revisiting constrained delegation in this blog post because I learned something new about it that I wanted to pass along. I learned that there is practical limit to constrained delegation as well.
Here’s how this came up. This customer has a scenario where they were required to use constrained delegation but were uncertain of the full list of services which they needed to add to the Delegation tab for the service account. They knew that there was a set list of remote servers which they would need to be trusted for delegation to (a bit less than 20 different servers) but just didn’t know which services to specify. In the user interface for this in Active Directory Users and Computers (dsa.msc), you’ll recall, the object picker presents you with a choice of all of the services that a server hosts once you select that server in the Delegation tab setting for Kerberos. This list is drawn from the servicePrincipalName (SPN) attribute of that server.
When in doubt select them all if you need to have things working in a hurry. That’s just what this customer did, however when they were adding the last server to that list they clicked OK but got the error below:
The second part of this message “The administrative limit of this request was exceeded” is one that you may recognize as a LDAP type response. The big question was why are we running into this message when we try to add more constrained delegation entries?
To answer that we need to discuss what actually happens when adding entries for constrained delegation in the Delegation folder tab of a service account. When you select a computer to delegate to and choose the services for it as well what really happens is that the AD attribute of msDs-AllowedToDelegateTo (also known as A2D2) for that service account gets those servicePrincipalNames added to its list.
Any attribute in AD has a schema definition which defines what properties and behaviors that attribute will have. For msDs-AllowedToDelegateTo it is here . Here’s a paste of the part of that schema definition which is relevant to this discussion:
CN ms-DS-Allowed-To-Delegate-To
Ldap-Display-Name msDS-AllowedToDelegateTo
Size 0 to 64K
Update Privilege -
Update Frequency Infrequently
Attribute-Id 1.2.840.113556.1.4.1787
System-Id-Guid 800d94d7-b7a1-42a1-b14d-7cae1423d07f
Syntax String(Unicode) .
I’ve put a portion above into bold text-the size information. Not all attributes define a maximum size in the schema, but here we see one. So that tells us that the maximum sized set of data we can put into that attribute is 64K of Unicode data. How many does that really equate to?
Below is the approximate amount I was able to add in my test environment before I also reached the size limit related error, dumped from my test service account using LDP.EXE.
Expanding base 'CN=SvcAccount,CN=Users,DC=adatum,DC=com'...
Result <0>: (null)
Matched DNs:
Getting 1 entries:
>> Dn: CN=SvcAccount,CN=Users,DC=adatum,DC=com
5> objectClass: top; person; organizationalPerson; user; computer;
1> cn: SvcAccount;
1> distinguishedName: CN=SvcAccount,CN=Users,DC=adatum,DC=com;
<snip>
1> sAMAccountName: SVCACCOUNT$;
1> sAMAccountType: 805306369;
1> objectCategory: CN=Computer,CN=Schema,CN=Configuration,DC=adatum,DC=com;
1> isCriticalSystemObject: FALSE;
2> dSCorePropagationData: 07/30/2008 09:58:24 Central Standard Time Central Daylight Time; 30650/29691/8424 21052:37:2544 UNC;
1189> msDS-AllowedToDelegateTo: msiserver/testcomputer106.adatum.com; msiserver/testcomputer107.adatum.com; msdtc/testcomputer103.adatum.com; msdtc/testcomputer104.adatum.com; msdtc/testcomputer105.adatum.com; msdtc/testcomputer106.adatum.com; msdtc/testcomputer107.adatum.com; messenger/testcomputer103.adatum.com; messenger/testcomputer104.adatum.com; messenger/testcomputer105.adatum.com; messenger/testcomputer106.adatum.com; messenger/testcomputer107.adatum.com; mcsvc/testcomputer103.adatum.com; mcsvc/testcomputer104.adatum.com; mcsvc/testcomputer105.adatum.com; mcsvc/testcomputer106.adatum.com; mcsvc/testcomputer107.adatum.com; iisadmin/testcomputer103.adatum.com; iisadmin/testcomputer104.adatum.com; iisadmin/testcomputer105.adatum.com; iisadmin/testcomputer106.adatum.com; iisadmin/testcomputer107.adatum.com; ias/testcomputer103.adatum.com; ias/testcomputer104.adatum.com; ias/testcomputer105.adatum.com; ias/testcomputer106.adatum.com; ias/testcomputer107.adatum.com; http/testcomputer103.adatum.com; http/testcomputer104.adatum.com; http/testcomputer105.adatum.com; http/testcomputer106.adatum.com; http/testcomputer107.adatum.com; host/testcomputer103.adatum.com; host/testcomputer104.adatum.com; host/testcomputer105.adatum.com; host/testcomputer106.adatum.com; host/testcomputer107.adatum.com; fax/testcomputer103.adatum.com; fax/testcomputer104.adatum.com; fax/testcomputer105.adatum.com; fax/testcomputer106.adatum.com; fax/testcomputer107.adatum.com; eventsystem/testcomputer103.adatum.com; eventsystem/testcomputer104.adatum.com; eventsystem/testcomputer105.adatum.com; eventsystem/testcomputer106.adatum.com; eventsystem/testcomputer107.adatum.com; eventlog/testcomputer103.adatum.com; eventlog/testcomputer104.adatum.com; eventlog/testcomputer105.adatum.com; eventlog/testcomputer106.adatum.com; eventlog/testcomputer107.adatum.com; dnscache/testcomputer103.adatum.com; dnscache/testcomputer104.adatum.com; dnscache/testcomputer105.adatum.com; dnscache/testcomputer106.adatum.com; dnscache/testcomputer107.adatum.com; dns/...
-----------
Using LDP to display an object is handy for this since it shows us the number of entries present in a multi valued attribute. In mine, above, I have successfully added 1189, but when I try to add a few more I get the size limit error. That tells us that the limit is around 1100 or so entries but is there a better way to see this?
Some folks like the Support Tool DSAStat.exe for that. It is not my favorite tool ever, mainly since it can be cryptic to read. Using it to view this attribute with the command below is no different.
C:>dsastat -b:CN=SvcAccount,CN=Users,DC=adatum,DC=com -gcattrs:msDS-AllowedToDelegateTo >dsastat.txt
Most of the results are not applicable to this concern, but here’s the part that helps here:
Bytes per object:
Object Bytes
NULL-OBJECTCLASS 30575
. . . . . . . . . . . . . .
Bytes per server:
Server Bytes
ADFSACCOUNT 30575
For Unicode attributes we take approximately twice that amount for storage, so that takes us to our maximum amount. So in order to add more entries once we’ve reached that point we need to remove some entries from that list-we’ve reached the limitation of what the directory will allow into that attribute per its schema definition.
This begs a further question of whether so very many entries should be present in A2D2 to begin with. The logical answer for that is ‘no’. Adding a large number of entries into the A2D2 or msDs-AllowedToDelegateTo list ultimately works around the reason to use constrained delegation in the first place: limiting where the trusted identity can impersonate the user. To reduce this attack surface area the alternative idea is to use different service accounts rather than a single one, or simply try not to add services into the list which you don’t need. We’ll call that latter idea the “Don’t Select All” idea.
For those that will be out of the office over the next week I wish you an early Happy Holidays! I hope you have a safe, warm and relaxing time.