You all remember the maximum 2 hops for Kerberos right.. well in Microsoft land it works a little different and it is possible to create a multiple tier Kerberos delegation structure.
Basically we want the following to happen:
Client->IIS1->IIS2->IIS3->IIS4 where all hops require Kerberos authentication
In this case, IIS1, IIS2 and IIS3 need to be trusted for delegation. In my test lab I’ve used (http://support.microsoft.com/kb/314404) for the setup..
Reach To IIS server on IIS1
User Id = ROOTDOMAINAdministratorThe Negotiate method was used!
The user was logged on using Kerberos.
Attempt to connect to http://IIS2.rootdomain.local/default.asp by using ServerXMLHTTP
Receiver Status Text: OK (200)
Reach To IIS server on IIS2
User Id = ROOTDOMAINAdministratorThe Negotiate method was used!
The user was logged on using Kerberos.
Attempt to connect to http://IIS3.rootdomain.local/default.asp by using ServerXMLHTTP
Receiver Status Text: OK (200)
Reach To IIS server on IIS3
User Id = ROOTDOMAINAdministratorThe Negotiate method was used!
The user was logged on using Kerberos.
Attempt to connect to http://IIS4.rootdomain.local/default.asp by using ServerXMLHTTP
Receiver Status Text: OK (200)
Reach To IIS server on IIS4
User Id = ROOTDOMAINAdministratorThe Negotiate method was used!
The user was logged on using Kerberos.
So the log file shows you it all works!.. please keep the above information in mind when designing security for your applications!
(explanation: In Microsoft land, each trusted for delegation object requests tickets on behalf of the user instead of using the forwarded ticket from the user). If a trusted object requests a ticket for the next service which is also trusted for delegation the forwardable flag is not cleared, therefore the next hop can re-request tickets on behalf of the user for the next hop. )
So if we look at the traffic on IIS2 (called SQL01 here and IIS3 is called IS02).. you see IIS2 server requesting a ticket on behalf of the Administrator (user account) for the IIS3 service (IS02.rootdomain.local)
*****************************************************************
Constraint Delegation
So you want to set it up for constraint delegation.. users who’s computers are not on the domain must be able to login to the same application.. that’s where things go wrong!..
Reach To IIS server on SP01
User Id = ROOTDOMAINAdministratorThe Negotiate method was used!
The user was logged on using Kerberos.
Please do not refresh this page.
�
Attempt to connect to http://SQL01.rootdomain.local/default.asp by using ServerXMLHTTP
Receiver Status Text: OK (200)
Reach To IIS server on SQL01
User Id = ROOTDOMAINAdministratorThe Negotiate method was used!
The user was logged on using Kerberos.
Please do not refresh this page.
�
Attempt to connect to http://IS02.rootdomain.local/default.asp by using ServerXMLHTTP
Receiver Status Text: Unauthorized (401)
You are not authorized to view this page
looks like we can only have two hops.. lets have a look …..
We set the First IIS server to use Constraint Delegation (all services) towards the 2nd IIS server. Again, we take a look at the network..
In the network layer we see the challenge from the IIS webserver (highlighted).. and then NO Kerberos request for a new ticket on behalf of the user.. the servers reconnects to the IIS server and (192.168.10.3) cannot provide it credentials for Kerberos. The webserver switches over to NLTM (Challenge). The server can only answer to the NTLM challenge with the server’s key (or Service Account) and therefore breaking the delegation model. (access denied message)
So the question lies in what the first IIS webserver does when requesting a ticket on behalf of the user in Constraint and unConstraint delegation. Again we switch over to the network capture..
In a non constraint delegation model, the 1st webserver requests a ticket with options: 4081000 (forwardable, Renewable, Canonicalize) for the 2nd webserver (url sql01.rootdomain.local).
Now as we enable constraint delegation (allowing users to logon with another authentication protocol) we see the difference:
The ticket itself has the Contrained Delegation flag set! and that should probably be why this ticket cannot be used to get another ticket on behalf of the user on the 2nd webserver.
* The quest continues..
I did another packetsniff let’s take a look at what happens in order:
The client connects to the webserver and retrieves an unauthorized:
17 -7172.250000 192.168.10.2 192.168.10.14 HTTP HTTP/1.1 401 Unauthorized (text/html)
The client requests is Ticket Granting Ticket
19 -7172.203125 192.168.10.14 192.168.10.1 KRB5 AS-REQ
KDCOptions: 40810010 (Forwardable, Renewable, Canonicalize, Renewable OK)
Client Name (Principal): Administrator
Realm: ROOTDOMAIN.LOCAL
The client receives it’s AS
20 -7172.171875 192.168.10.1 192.168.10.14 KRB5 AS-REP
Then the client wants it’s TGS (service ticket)
21 -7172.156250 192.168.10.14 192.168.10.1 KRB5 TGS-REQ
KDCOptions: 40800000 (Forwardable, Renewable)
Server Name (Service and Instance): HTTP/sp01.rootdomain.local
Name: HTTP
Name: sp01.rootdomain.local
The client receives the ticket
22 -7172.140625 192.168.10.1 192.168.10.14 KRB5 TGS-REP
And re-connect to the webserver with authorization
24 -7172.109375 192.168.10.14 192.168.10.2 HTTP GET / HTTP/1.1�
[truncated] Authorization: Negotiate IFCQYGKwYBBQUCoIIE/TCCBPmgJDAiBgkqhkiC
Let’s take it from there and switch to the SP01.rootdomain.local server
The first thing we see is our LDP_DEEPSPACE check (see other posting)
19 -7193.078125 192.168.10.2 192.168.10.1 TCP ltp-deepspace > kerberos [SYN] Seq=0 Win=65535 Len=0 MSS=1460
Although we gave the kerberos ticket for the user already, the next thing we see is another request:
23 -7193.062500 192.168.10.2 192.168.10.1 KRB5 TGS-REQ
KDCOptions: 40830000 (Forwardable, Renewable, Constrained Delegation, Canonicalize)
Server Name (Service and Host): host/sp01.rootdomain.local
Name-type: Service and Host (3)
Name: host
Name: sp01.rootdomain.local
Note that 192.168.10.2 IS the host itself and that the host is requesting a ticket for it’self!, lets compare that to the unconstraint request in another packet sniff: The first packet we see there is the request for the next hop, so the client itself is NOT requesting a host ticket when unconstraint delegation is used:>
28 -7198.734375 192.168.10.2 192.168.10.1 KRB5 TGS-REQ
KDCOptions: 40810000 (Forwardable, Renewable, Canonicalize)
Server Name (Service and Instance): HTTP/sql01.rootdomain.local
Name-type: Service and Instance (2)
Name: HTTP
Name: sql01.rootdomain.local
Followed by another request immediately:
30 -7198.734375 192.168.10.2 192.168.10.1 KRB5 TGS-REQ
KDCOptions: 60810010 (Forwardable, Forwarded, Renewable, Canonicalize, Renewable OK)
Server Name (Service and Instance): krbtgt/ROOTDOMAIN.LOCAL�
Name-type: Service and Instance (2)
Name: krbtgt
Name: ROOTDOMAIN.LOCAL
Back to the original constraint delegation packet.. after requesting a ticket for the host, an error is thrown back:�
25 -7193.031250 192.168.10.1 192.168.10.2 KRB5 KRB Error: KRB5KDC_ERR_BADOPTION NT Status: STATUS_NO_MATCH
error_code: KRB5KDC_ERR_BADOPTION (13)
Server Name (Service and Host): host/sp01.rootdomain.local
Name-type: Service and Host (3)
Name: host
Name: sp01.rootdomain.local
e-data PA-PW-SALT
Value: 720200C00000000003000000
NT Status: STATUS_NO_MATCH (0xc0000272)
Appearantly we are not receiving the kerberos ticket, and there is no retry.. the next packet is the next hop request:
34 -7192.640625 192.168.10.2 192.168.10.3 HTTP GET /default.asp HTTP/1.1
37 -7192.062500 192.168.10.3 192.168.10.2 HTTP HTTP/1.1 401 Unauthorized (text/html)
And the request for the next hop ticket:
43 -7192.046875 192.168.10.2 192.168.10.1 KRB5 TGS-REQ
KDCOptions: 40830000 (Forwardable, Renewable, Constrained Delegation, Canonicalize)
Server Name (Service and Instance): HTTP/sql01.rootdomain.local
Name-type: Service and Instance (2)
Name: HTTP
Name: sql01.rootdomain.local
The SQL01 hop tries the same request for the host ticket but receives the same error:
34 -7186.406250 192.168.10.1 192.168.10.3 KRB5 KRB Error: KRB5KDC_ERR_BADOPTION NT Status: STATUS_NO_MATCH
Now lets investigate the ERRor..
I’ve enabled kerberos error logging (debug level) and found:
(
How to turn on debug output
There are a number of ways to view the debug output from Kerberos. The easiest way is by logging the debug output to a file and then opening this file in Notepad.
1. | Click Start, click Run, type regedit.exe, and then press ENTER.
Caution |
2. | Open the following registry key:
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlLsaKerberosParameters |
3. | Create the following entry:
Value: KerbDebugLevel Type: DWORD Data: c0000043 (this value will print the most standard set of debug messages. Try it first. If you still want to see more output, set it to ffffffff). |
4. | Create the following entry in the same registry location:
Value: LogToFile Type: DWORD Data: 1 |
5. | Reproduce the error |
6. | Open the file lsass.log, located in the System32 directory of your Windows folder. You can find the debug output inside this file. |
)
384.488> Kerb-Trace: KerbCreateTokenFromTicket for ROOTDOMAINAdministrator, (null)
384.488> Kerb-Trace: SpAcceptLsaModeContext called KerbMapContext ContextAttributes 0x5, 0
384.500> Kerb-SPN: Found in SPN Cache 00111770 384.500> Kerb-Bnd: KerbInsertBinding binding cache disabled
384.500> Kerb-Bnd: Calling kdc 192.168.10.1 for realm ROOTDOMAIN.LOCAL
384.500> KSupp-Trace: Calling KDC: 192.168.10.1
384.472> Kerb-Trace: KerbCreateTokenFromTicket for ROOTDOMAINAdministrator, (null)
384.472> Kerb-LSess: KerbCreateLogonSessionFromTicket creating logon session for 0:0x29766, accepting 0:0x3e4, client Administrator@ROOTDOMAIN.LOCAL
384.472> Kerb-Trace: SpAcceptLsaModeContext called KerbMapContext ContextAttributes 0x5, 0
384.600> Kerb-Bnd: KerbInsertBinding binding cache disabled
384.600> Kerb-Bnd: Calling kdc 192.168.10.1 for realm ROOTDOMAIN.LOCAL
384.600> KSupp-Trace: Calling KDC: 192.168.10.1
384.480> Kerb-Cred: Acquiring cred, S4U required
384.600> Kerb-Warn: KerbGetTgsTicket failed to unpack KDC reply: 0x3c
384.600> KSupp-Warning: KerbUnpackData failed to unpack typed data, trying error method data
384.600> KSupp-Error: KerbUnpackErrorData received failure from kdc 0xd KLIN(0) NTSTATUS(0xc0000272)
384.484> Kerb-SPN: Found in SPN Cache 00111770 384.484> Kerb-S4u: Trying S4UProxy for ls 0009E9F8
384.600> Kerb-S4u: No match on S4UTarget
384.600> Kerb-Warn: Failed S4Uproxy request c0000272(8)
384.484> Kerb-Bnd: KerbInsertBinding binding cache disabled
384.484> Kerb-Bnd: Calling kdc 192.168.10.1 for realm ROOTDOMAIN.LOCAL
384.484> KSupp-Trace: Calling KDC: 192.168.10.1
So the request for a ticket on behalf of the client fails, therefore the next hop SQL01 can still be reached by the client’s initial ticket, however that ticket is limited to only 2 hops. This is also seen on the client (XP) side when requesting the active tickets:
Cached Tickets: (2)
Server: krbtgt/ROOTDOMAIN.LOCAL@ROOTDOMAIN.LOCAL
KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
End Time: 8/15/2008 21:07:02
Renew Time: 8/22/2008 11:07:02
Server: HTTP/sp01.rootdomain.local@ROOTDOMAIN.LOCAL
KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
End Time: 8/15/2008 21:07:02
Renew Time: 8/22/2008 11:07:02
So after the 2nd hop (SQL) the initial ticket is not valid anymore and cannot be forwarded to the next hop. Access Denied is the result..