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 f7aea311769..726e81111d1 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 @@ -1253,7 +1253,10 @@ public class UserGroupInformation { Object cred = iter.next(); if (cred instanceof KerberosTicket) { KerberosTicket ticket = (KerberosTicket) cred; - if (!ticket.getServer().getName().startsWith("krbtgt")) { + if (ticket.isDestroyed() || ticket.getServer() == null) { + LOG.warn("Ticket is already destroyed, remove it."); + iter.remove(); + } else if (!ticket.getServer().getName().startsWith("krbtgt")) { LOG.warn( "The first kerberos ticket is not TGT" + "(the server principal is {}), remove and destroy it.", diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestFixKerberosTicketOrder.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestFixKerberosTicketOrder.java index 4b75a3682b7..cbea393d931 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestFixKerberosTicketOrder.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestFixKerberosTicketOrder.java @@ -155,4 +155,81 @@ public class TestFixKerberosTicketOrder extends KerberosSecurityTestcase { .filter(t -> t.getServer().getName().startsWith(server2Protocol)) .findAny().isPresent()); } + + @Test + public void testWithDestroyedTGT() throws Exception { + UserGroupInformation ugi = + UserGroupInformation.loginUserFromKeytabAndReturnUGI(clientPrincipal, + keytabFile.getCanonicalPath()); + ugi.doAs(new PrivilegedExceptionAction() { + + @Override + public Void run() throws Exception { + SaslClient client = Sasl.createSaslClient( + new String[] {AuthMethod.KERBEROS.getMechanismName()}, + clientPrincipal, server1Protocol, host, props, null); + client.evaluateChallenge(new byte[0]); + client.dispose(); + return null; + } + }); + + Subject subject = ugi.getSubject(); + + // mark the ticket as destroyed + for (KerberosTicket ticket : subject + .getPrivateCredentials(KerberosTicket.class)) { + if (ticket.getServer().getName().startsWith("krbtgt")) { + ticket.destroy(); + break; + } + } + + ugi.fixKerberosTicketOrder(); + + // verify that after fixing, the tgt ticket should be removed + assertFalse("The first ticket is not tgt", + subject.getPrivateCredentials().stream() + .filter(c -> c instanceof KerberosTicket) + .map(c -> ((KerberosTicket) c).getServer().getName()).findFirst() + .isPresent()); + + + // should fail as we send a service ticket instead of tgt to KDC. + intercept(SaslException.class, + () -> ugi.doAs(new PrivilegedExceptionAction() { + + @Override + public Void run() throws Exception { + SaslClient client = Sasl.createSaslClient( + new String[] {AuthMethod.KERBEROS.getMechanismName()}, + clientPrincipal, server2Protocol, host, props, null); + client.evaluateChallenge(new byte[0]); + client.dispose(); + return null; + } + })); + + // relogin to get a new ticket + ugi.reloginFromKeytab(); + + // make sure we can get new service ticket after the relogin. + ugi.doAs(new PrivilegedExceptionAction() { + + @Override + public Void run() throws Exception { + SaslClient client = Sasl.createSaslClient( + new String[] {AuthMethod.KERBEROS.getMechanismName()}, + clientPrincipal, server2Protocol, host, props, null); + client.evaluateChallenge(new byte[0]); + client.dispose(); + return null; + } + }); + + assertTrue("No service ticket for " + server2Protocol + " found", + subject.getPrivateCredentials(KerberosTicket.class).stream() + .filter(t -> t.getServer().getName().startsWith(server2Protocol)) + .findAny().isPresent()); + } } \ No newline at end of file