Hi Max, I'd like to propose a fix for 8239385 [1].
Webrev.00: * http://cr.openjdk.java.net/~mbalao/webrevs/8239385/8239385.webrev.00/ The goal behind these changes is to align the Kerberos client default 'canonicalize' behavior to the MIT's one -an analysis of the latter is available at the end of this email for reference-. There are a couple of reasons for that: 1) many applications are not ready to deal with cname changes in their authorization schemes, so the previous default behavior may be too disruptive; and, 2) old KDCs -such as Windows AD 2008- may change the cname when 'canonicalize' is sent from the client but do not include a ENC-PA-REP flag (nor the associated data structure) in the response. Note: cname changes without proper authentication should not be allowed. This change set us apart from a strict interpretation of the RFC 6806 [2], but will enhance compatibility and should not pose a security risk. Changes per file: * src/java.security.jgss/share/classes/sun/security/krb5/Config.java * When a krb5 configuration refresh happens, we need to update the cached 'canonicalize' value stored in ReferralsState class. Same pattern than for other krb5 configurations. * src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java * Read and cache-store of krb5 'canonicalize' configuration value. * 'canonicalize' flag will be sent in AS-REQs only if set to true in the krb5 configuration. false by default. * AS referrals allowed only if 'canonicalize' was sent or an NT-ENTERPRISE cname was used * Usage of NT-ENTERPRISE implies 'canonicalize' (despite the actual flag may not be set). This mimics the MIT client behavior. * Note: OpenJDK will bring APIs for using NT-ENTERPRISE clients on a separate enhancement. * If there is an unexpected KDC failure, the number of max referrals has not been exceeded and 'canonicalize' was set, retry without it. Given that we are not tightening 'canonicalize' to NT-ENTERPRISE (as we would do in a strict interpretation of RFC 6806), we no longer need to check that the cname type is different than NT-ENTERPRISE to retry. * Improved debug output and comments * src/java.security.jgss/share/classes/sun/security/krb5/KrbKdcRep.java * When checking the AS-REP, the usage of an NT-ENTERPRISE cname is considered equal to sending 'canonicalize' (same as in KrbAsReqBuilder.java). * When checking ENC-PA-REP in line 141, 'canonicalize' is not relevant: whether or not ENC-PA-REP is required in the KDC response has been already checked before. In fact, 'canonicalize' may be not sent but we require a ENC-PA-REP in the AS-REP because an NT-ENTERPRISE cname was used and the cname was changed by the KDC. * Note: from a client side, sending an NT-ENTERPRISE cname means that the cname can change in the response. Windows AD 2016, however, does not change it unless 'canonicalize' flag is explicitly set in the request. * Improved comments * test/jdk/sun/security/krb5/auto/ReferralsTest.java * Test for the use-case in which 'canonicalize' is not set, and a referral must fail. Contributed by Max (@weijun), with enhanced comments and cleanup function. Testing: * No regressions observed in jdk/sun/security/krb5 * ReferralsTest extended to cover the new use case * No regressions found in my internal Windows AD 2016 environment In parallel to this RFR, I'll initiate a CSR process to introduce the "canonicalize" krb5 configuration (whose default value is 'false'). Thanks, Martin.- -- [1] - https://bugs.openjdk.java.net/browse/JDK-8239385 [2] - https://tools.ietf.org/html/rfc6806 -- MIT Kerberos client analysis AS-REQs analysis ........................................ When requesting a TGT (through the AS protocol), CANONICALIZE flag is set in the AS-REQ only if 'canonicalize' parameter or configuration options are set [2] (false is the default value). When receiving a KDC_ERR_WRONG_REALM referral response from a KDC, the referral will be followed only if CANONICALIZE was set in the corresponding AS-REQ or if the cname principal was of NT-ENTERPRISE type [3] [4]. When analyzing the final AS-REP (after all referrals), there is a check to enforce that either CANONICALIZE was set in the AS-REQ or the client is a NT-ENTERPRISE principal (among other checks) [5]. kinit's documentation states that -E (for NT-ENTERPRISE principals) implies CANONICALIZE. "Implies" does not mean that the CANONICALIZE flag is set; only that referrals will be followed and cname changes are accepted as if CANONICALIZE were set. In my observations, the Windows 2016 AD does not change the cname unless CANONICALIZE flag is explicitly set in the AS-REQ. In conclusion, referrals are followed only if 'canonicalize' is set or if the cname is an NT-ENTERPRISE principal. Given that OpenJDK does not currently support NT-ENTERPRISE principals in its user interface, the MIT equivalent behavior would be to disable AS-REQ referrals by default (when CANONICALIZE is not set). AP-REQs analysis ........................................ When requesting a TGS, the MIT kvno client tries with referrals first [6] [7]. If there is an error -there are some checks to deal with old Active Directories-, a fallback without referrals is tried [8]. Depending on whether referrals are enabled (STATE_REFERRALS) or not (STATE_NON_REFERRAL), the CANONICALIZE flag may be set in the request here [9]. It's worth noticing that: 1) referrals processing requires to be in STATE_REFERRALS state and that means that CANONICALIZE was set in the AP-REQ; 2) CANONICALIZE value for TGSs does not seem to come from a user-configurable option (as it was for AS-REQs) and is set by default; and 3) there is a fallback mechanism as we already have in OpenJDK. -- [1] - https://github.com/krb5/krb5/tree/97e3c42b2a89a2ec60eb93d3f974769e3e3cbdc5 [2] - https://github.com/krb5/krb5/blob/97e3c42b2a89a2ec60eb93d3f974769e3e3cbdc5/src/lib/krb5/krb/get_in_tkt.c#L969 [3] - https://github.com/krb5/krb5/blob/97e3c42b2a89a2ec60eb93d3f974769e3e3cbdc5/src/lib/krb5/krb/get_in_tkt.c#L1536 [4] - https://github.com/krb5/krb5/blob/97e3c42b2a89a2ec60eb93d3f974769e3e3cbdc5/src/lib/krb5/krb/get_in_tkt.c#L1587 [5] - https://github.com/krb5/krb5/blob/97e3c42b2a89a2ec60eb93d3f974769e3e3cbdc5/src/lib/krb5/krb/get_in_tkt.c#L227 [6] - https://github.com/krb5/krb5/blob/97e3c42b2a89a2ec60eb93d3f974769e3e3cbdc5/src/lib/krb5/krb/get_creds.c#L1072 [7] - https://github.com/krb5/krb5/blob/97e3c42b2a89a2ec60eb93d3f974769e3e3cbdc5/src/lib/krb5/krb/get_creds.c#L620 [8] - https://github.com/krb5/krb5/blob/97e3c42b2a89a2ec60eb93d3f974769e3e3cbdc5/src/lib/krb5/krb/get_creds.c#L479 [9] - https://github.com/krb5/krb5/blob/97e3c42b2a89a2ec60eb93d3f974769e3e3cbdc5/src/lib/krb5/krb/get_creds.c#L338