Skip to content

Commit

Permalink
HADOOP-13433 Race in UGI.reloginFromKeytab. Contributed by Duo Zhang.
Browse files Browse the repository at this point in the history
  • Loading branch information
xiao-chen committed Feb 9, 2017
1 parent bc3e188 commit 18dfff8
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 17 deletions.
2 changes: 2 additions & 0 deletions hadoop-common-project/hadoop-common/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ Release 2.7.4 - UNRELEASED

HADOOP-14001. Improve delegation token validity checking. (aajisaka)

HADOOP-13433 Race in UGI.reloginFromKeytab. (Duo Zhang via xiao)

Release 2.7.3 - 2016-08-25

INCOMPATIBLE CHANGES
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS;
import static org.apache.hadoop.util.PlatformName.IBM_JAVA;

import com.google.common.annotations.VisibleForTesting;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
Expand All @@ -40,6 +42,7 @@
import java.util.Map;
import java.util.Set;

import javax.security.auth.DestroyFailedException;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.kerberos.KerberosPrincipal;
Expand Down Expand Up @@ -71,8 +74,6 @@
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.util.Time;

import com.google.common.annotations.VisibleForTesting;

/**
* User and group information for Hadoop.
* This class wraps around a JAAS Subject and provides methods to determine the
Expand Down Expand Up @@ -1037,24 +1038,54 @@ public synchronized void checkTGTAndReloginFromKeytab() throws IOException {
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<Object> creds = getSubject().getPrivateCredentials();
synchronized (creds) {
for (Iterator<Object> 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 " + ticket.getServer() + "), remove" +
" and destroy it.");
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.
* @throws IOException on a failure
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized void reloginFromKeytab()
throws IOException {
if (!isSecurityEnabled() ||
user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
!isKeytab)
public synchronized void reloginFromKeytab() throws IOException {
if (!isSecurityEnabled()
|| user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS
|| !isKeytab) {
return;

}

long now = Time.now();
if (!shouldRenewImmediatelyForTests && !hasSufficientTimeElapsed(now)) {
return;
Expand All @@ -1066,12 +1097,12 @@ public synchronized void reloginFromKeytab()
now < getRefreshTime(tgt)) {
return;
}

LoginContext login = getLogin();
if (login == null || keytabFile == null) {
throw new IOException("loginUserFromKeyTab must be done first");
}

long start = 0;
// register most recent relogin attempt
user.setLastLogin(now);
Expand All @@ -1094,6 +1125,7 @@ HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, getSubject(),
}
start = Time.now();
login.login();
fixKerberosTicketOrder();
metrics.loginSuccess.add(Time.now() - start);
setLogin(login);
}
Expand All @@ -1115,12 +1147,12 @@ HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, getSubject(),
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public synchronized void reloginFromTicketCache()
throws IOException {
if (!isSecurityEnabled() ||
user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
!isKrbTkt)
public synchronized void reloginFromTicketCache() throws IOException {
if (!isSecurityEnabled()
|| user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS
|| !isKrbTkt) {
return;
}
LoginContext login = getLogin();
if (login == null) {
throw new IOException("login must be done first");
Expand Down Expand Up @@ -1148,13 +1180,13 @@ public synchronized void reloginFromTicketCache()
LOG.debug("Initiating re-login for " + getUserName());
}
login.login();
fixKerberosTicketOrder();
setLogin(login);
} catch (LoginException le) {
throw new IOException("Login failure for " + getUserName(), le);
}
}


/**
* 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
Expand Down

0 comments on commit 18dfff8

Please sign in to comment.