diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java index f874b921882..aea1a272e43 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java @@ -24,6 +24,8 @@ import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_TOKEN_FI import static org.apache.hadoop.security.UGIExceptionMessages.*; import static org.apache.hadoop.util.PlatformName.IBM_JAVA; +import com.google.common.annotations.VisibleForTesting; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -45,6 +47,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import javax.security.auth.DestroyFailedException; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.kerberos.KerberosPrincipal; @@ -76,8 +79,6 @@ import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.StringUtils; import org.apache.hadoop.util.Time; - -import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -1163,10 +1164,40 @@ public class UserGroupInformation { reloginFromKeytab(); } + // if the first kerberos ticket is not TGT, then remove and destroy it since + // the kerberos library of jdk always use the first kerberos ticket as TGT. + // See HADOOP-13433 for more details. + private void fixKerberosTicketOrder() { + Set creds = getSubject().getPrivateCredentials(); + synchronized (creds) { + for (Iterator iter = creds.iterator(); iter.hasNext();) { + Object cred = iter.next(); + if (cred instanceof KerberosTicket) { + KerberosTicket ticket = (KerberosTicket) cred; + if (!ticket.getServer().getName().startsWith("krbtgt")) { + LOG.warn( + "The first kerberos ticket is not TGT" + + "(the server principal is {}), remove and destroy it.", + ticket.getServer()); + iter.remove(); + try { + ticket.destroy(); + } catch (DestroyFailedException e) { + LOG.warn("destroy ticket failed", e); + } + } else { + return; + } + } + } + } + LOG.warn("Warning, no kerberos ticket found while attempting to renew ticket"); + } + /** * Re-Login a user in from a keytab file. Loads a user identity from a keytab * file and logs them in. They become the currently logged-in user. This - * method assumes that {@link #loginUserFromKeytab(String, String)} had + * method assumes that {@link #loginUserFromKeytab(String, String)} had * happened already. * The Subject field of this UserGroupInformation object is updated to have * the new credentials. @@ -1176,11 +1207,12 @@ public class UserGroupInformation { @InterfaceAudience.Public @InterfaceStability.Evolving public synchronized void reloginFromKeytab() throws IOException { - if (!isSecurityEnabled() || - user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS || - !isKeytab) + if (!isSecurityEnabled() + || user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS + || !isKeytab) { return; - + } + long now = Time.now(); if (!shouldRenewImmediatelyForTests && !hasSufficientTimeElapsed(now)) { return; @@ -1192,12 +1224,12 @@ public class UserGroupInformation { now < getRefreshTime(tgt)) { return; } - + LoginContext login = getLogin(); if (login == null || keytabFile == null) { throw new KerberosAuthException(MUST_FIRST_LOGIN_FROM_KEYTAB); } - + long start = 0; // register most recent relogin attempt user.setLastLogin(now); @@ -1220,6 +1252,7 @@ public class UserGroupInformation { } start = Time.now(); login.login(); + fixKerberosTicketOrder(); metrics.loginSuccess.add(Time.now() - start); setLogin(login); } @@ -1231,7 +1264,7 @@ public class UserGroupInformation { kae.setPrincipal(keytabPrincipal); kae.setKeytabFile(keytabFile); throw kae; - } + } } /** @@ -1245,10 +1278,11 @@ public class UserGroupInformation { @InterfaceAudience.Public @InterfaceStability.Evolving public synchronized void reloginFromTicketCache() throws IOException { - if (!isSecurityEnabled() || - user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS || - !isKrbTkt) + if (!isSecurityEnabled() + || user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS + || !isKrbTkt) { return; + } LoginContext login = getLogin(); if (login == null) { throw new KerberosAuthException(MUST_FIRST_LOGIN); @@ -1276,15 +1310,15 @@ public class UserGroupInformation { LOG.debug("Initiating re-login for " + getUserName()); } login.login(); + fixKerberosTicketOrder(); setLogin(login); } catch (LoginException le) { KerberosAuthException kae = new KerberosAuthException(LOGIN_FAILURE, le); kae.setUser(getUserName()); throw kae; - } + } } - /** * Log a user in from a keytab file. Loads a user identity from a keytab * file and login them in. This new user does not affect the currently