JDK-8058290 : JAAS Krb5LoginModule has suspect ticket-renewal logic, relies on clockskew grace
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.security
  • Affected Version: 7u55
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2014-09-12
  • Updated: 2020-02-26
  • Resolved: 2015-07-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9 Other
9 b74Fixed openjdk7uFixed
Related Reports
Duplicate :  
Relates :  
Description
In Krb5LoginModule, when using the native ticket cache (useTicketCache=true)
with the renewal option enabled (renewTGT=true), the module will attempt to 
renew a renewable TGT.  The method Krb5LoginModule#isCurrent(...) is used to 
decide whether to trigger the renewal request (renewTGT=true) or ignore that 
ticket (with a debug message) and proceed with other authentication options 
when (renewTGT=false). 

The check in isCurrent() is correct according to its name - it checks whether 
the ticket has expired or not, that is, whether our current time is beyond 
the ticket end time: 

    private boolean isCurrent(Credentials creds) 
    { 
        Date endTime = creds.getEndTime(); 
        if (endTime != null) { 
            return (System.currentTimeMillis() <= endTime.getTime()); 
        } 
        return true; 
    } 

However, according to RFC4120 [1] "The Kerberos Network Authentication 
Service (V5)", section 2.3 "Renewable Tickets": 

<rfc1420> 
Renewable tickets have two "expiration times": the first is when the current 
instance of the ticket expires, and the second is the latest permissible 
value for an individual expiration time.  An application client must 
periodically (i.e., before it expires) present a renewable ticket to the KDC, 
with the RENEW option set in the KDC request. 
</rfc1420> 

That is, expired tickets cannot be renewed, regardless of the renewable 
period.  A ticket is renewable provided that we are within its (fixed) 
renewable period, AND the ticket has not already expired.  For example, a TGT 
may be issued with validity from now and an expiry 24 hours hence, and with a 
renewable period of 30 days.  The ticket can be renewed each day for 30 days, 
but it must be renewed at least once per day, otherwise it will expire.     
Expired tickets cannot be renewed.  See also the comment for "kinit -R": 

-R  requests renewal of the ticket-granting ticket.   Note  that  an expired     
ticket  cannot  be renewed, even if the ticket is still within its renewable 
life. 

The above logic would appear to be oblivious to this logic (it does not 
attempt to renew until it thinks the ticket has expired), and therefore 
should never work, or very rarely at best when renewed at exactly the right 
time, and you would think that this would be an easily spotted problem.  In 
fact ticket-renewal will succeed for a period after the ticket expires, due 
to client/KDC allowances for clock-skew, as configured in krb5.conf variable 
"clockskew", which defaults to 300 (seconds, or 5 minutes).  So someone 
testing this code, who attempts to renew a ticket-cache ticket that has 
technically expired (but recently), will see it renew successfully due to 
clock-skew allowances. 
. 
This logic is problematic for the following reasons, as we rely on the 
clockskew setting (which we don't control and is not visible) for a grace 
period to renew an otherwise already expired ticket (which is not normally 
allowed).  In the above example, we have a 24 hour period each day in the 
month to renew the TGT, but we rely on whatever clockskew is set (default 5 
minutes), so the success window for this is reduced to that period. 
. 
The correct logic would be to divide the ticket-lifetime (endTime - 
startTime), multiply it by a (fixed?) renew threshold, say half-way or 0.5 
(after 12h per example), and then compare the currentTimeMillis() to that 
time, and if we are more than half-way expired, trigger the renewal.  This is 
a standard Kerberos approach to deal with issues such as KDC outages or 
infrequent inspection of the current TGT (idle application sessions). 

[1] http://www.ietf.org/rfc/rfc4120.txt