heimdal icon indicating copy to clipboard operation
heimdal copied to clipboard

kgetcred does not obtain S4U2Proxy ticket if cache already has non-delegated service ticket

Open RobCrowston opened this issue 3 years ago • 6 comments

Describe the bug kgetcred will not obtain a second service ticket (for the same service but for a different client) using S4U2Proxy impersonation for the second ticket, provided it already has a non-delegated ticket.

Reproduction The realm is configured with three principals:

  • client
  • service1
  • service2 service1 is configured to delegate (via S4U2Proxy) to service2.

We can first obtain a normal (non-delegated) ticket as service1 to service2:

$ kinit -c /tmp/service service1
$ kgetcred -c /tmp/service service2   # first obtain ticket for service2 as service1
$ klist -c /tmp/service -v                                                                                           
Credentials cache: FILE:/tmp/service                                                                                                                                                 
        Principal: service1@REALM                                                                                                                                   
    Cache version: 4                                                                                                                                                                 
                                                                                                                                                                                     
Server: krbtgt/REALM@REALM                                                                                                                                   
Client: service1@REALM                                                                                                                                              
Ticket etype: aes256-cts-hmac-sha1-96, kvno 1                                                                                                                                        
Session key: aes128-cts-hmac-sha1-96                                                                                                                                                 
Ticket length: 441                                                                                                                                                                   
Auth time:  Sep  5 14:38:55 2021                                                                                                                                                     
End time:   Sep  6 14:38:55 2021                                                                                                                                                     
Ticket flags: enc-pa-rep, pre-authent, initial, forwardable                                                                                                                          
Addresses: addressless                                                                                                                                                               
                                                                                                                                                                                     
Server: service2@REALM   
Client: service1@REALM
Ticket etype: aes128-cts-hmac-sha1-96, kvno 1                                                                                                                                        
Ticket length: 446                                                                                                                                                                   
Auth time:  Sep  5 14:38:55 2021                                                          
Start time: Sep  5 14:39:05 2021
End time:   Sep  6 14:38:55 2021
Ticket flags: transited-policy-checked, pre-authent, forwardable
Addresses: addressless

Then try to obtain the delegated credential as client@REALM.

$ kinit client
$ kgetcred --out-cache=/tmp/evidence service1 # obtain ticket as client to service1
$ kgetcred -c /tmp/service --delegation-credential-cache=/tmp/evidence service2 # service1 can use the evidence ticket to obtain a delegated ticket as client to service2
$ klist -c /tmp/service -v                                                                                         
Credentials cache: FILE:/tmp/service                                                                                                                                                 
        Principal: service1@REALM                                                                                                                                   
    Cache version: 4                                                                                                                                                                 
                                                                                                                                                                                     
Server: krbtgt/REALM@REALM                                                                                                                                   
Client: service1@REALM                                                                                                                                              
Ticket etype: aes256-cts-hmac-sha1-96, kvno 1                                                                                                                                        
Session key: aes128-cts-hmac-sha1-96                                                                                                                                                 
Ticket length: 441                                                                                                                                                                   
Auth time:  Sep  5 14:38:55 2021                                                                                                                                                     
End time:   Sep  6 14:38:55 2021                                                                                                                                                     
Ticket flags: enc-pa-rep, pre-authent, initial, forwardable                                                                                                                          
Addresses: addressless                                                                                                                                                               
                                                                                                                                                                                     
Server: service2@REALM   
Client: service1@REALM
Ticket etype: aes128-cts-hmac-sha1-96, kvno 1                                                                                                                                        
Ticket length: 446                                                                                                                                                                   
Auth time:  Sep  5 14:38:55 2021                                                          
Start time: Sep  5 14:39:05 2021
End time:   Sep  6 14:38:55 2021
Ticket flags: transited-policy-checked, pre-authent, forwardable
Addresses: addressless

If we reverse the steps and obtain the S4U2Proxy ticket first, we can obtain both service2 tickets, one for the service1 and one for client.

$ kinit -c /tmp/service service1
$ kgetcred -c /tmp/service --delegation-credential-cache=/tmp/evidence service2
$ kgetcred -c /tmp/service service2                                         
$ klist -c /tmp/service -v                                                       
Credentials cache: FILE:/tmp/service
        Principal: service1@REALM
    Cache version: 4

Server: krbtgt/REALM@REALM
Client: service1@REALM
Ticket etype: aes256-cts-hmac-sha1-96, kvno 1
Session key: aes128-cts-hmac-sha1-96
Ticket length: 441
Auth time:  Sep  5 14:59:23 2021
End time:   Sep  6 14:59:23 2021
Ticket flags: enc-pa-rep, pre-authent, initial, forwardable
Addresses: addressless

Server: service2@REALM
Client: client@REALM
Ticket etype: aes128-cts-hmac-sha1-96, kvno 1
Ticket length: 446
Auth time:  Sep  5 14:59:23 2021
Start time: Sep  5 14:59:28 2021
End time:   Sep  6 14:59:23 2021
Ticket flags: transited-policy-checked, pre-authent, forwardable
Addresses: addressless

Server: service2@REALM
Client: service1@REALM
Ticket etype: aes128-cts-hmac-sha1-96, kvno 1
Ticket length: 481
Auth time:  Sep  5 14:59:23 2021
Start time: Sep  5 14:59:37 2021
End time:   Sep  6 14:59:23 2021
Ticket flags: transited-policy-checked, pre-authent, forwardable
Addresses: addressless

Expected behavior kgetcred should obtain a fresh service ticket if the cache does not yet have one for the specified client, regardless of whether the cache holds tickets for the same service but for different clients.

RobCrowston avatar Sep 05 '21 14:09 RobCrowston

A little further investigation: impersonating multiple clients to the same service via S4U2Proxy works, until I have a single non-delegated ticket to that service, at which point I can't obtain any more tickets.

RobCrowston avatar Sep 05 '21 14:09 RobCrowston

kgetcred should obtain a fresh service ticket if the cache does not yet have one for the specified client

The problem is the the client isn't really specified, it's encrypted inside the evidence ticket which we may be unable to open, e.g. we only have service1's tgt and not long-term keys, or we have keys of different enc-type.

In MIT we fixed it this way: https://github.com/krb5/krb5/commit/148b317e1eb5df28dad96679cb4b8a07c62d4786

BTW, PR #598 is a bit related.

iboukris avatar Sep 05 '21 17:09 iboukris

Perhaps the easiest fix is to skip check_cc() (and therefore always fetch a fresh ticket) if we’re using an evidence ticket. I think that is what happens now if the only tickets in the cache for the requested service have a client name set (whether or not the request is for a client we already have).

RobCrowston avatar Sep 05 '21 18:09 RobCrowston

Perhaps the easiest fix is to skip check_cc() (and therefore always fetch a fresh ticket) if we’re using an evidence ticket.

This isn't ideal, beyond the overhead of fetching a new ticket, repeated requests might grow up the cache indefinitely (this could be worked around, but I think caching by 2nd ticket like in MIT is better).

iboukris avatar Sep 05 '21 20:09 iboukris

In a closer look it appears that the original intent was as @RobCrowston suggested, to simply avoid the cache check when doing S4U2Proxy, and this is just a logical bug at: https://github.com/heimdal/heimdal/blob/master/lib/krb5/get_cred.c#L1070

I think the condition should read: if (impersonate_principal == NULL && !flags.b.cname_in_addl_tkt)

So maybe we can go with that as a fix, until someone comes with something better (if I get the chance I'll try to save the encoded ticket in out_creds.second_ticket and match on 2nd ticket like in MIT).

iboukris avatar Sep 27 '21 19:09 iboukris

Good point. Do you want to submit a PR?

lhoward avatar Sep 27 '21 22:09 lhoward