On 15/05/2015 11:24, ma...@apache.org wrote: > Author: markt > Date: Fri May 15 10:24:11 2015 > New Revision: 1679534 > > URL: http://svn.apache.org/r1679534 > Log: > Fix a problem with SPNEGO auth and Java 8 update 40 onwards.
I've just found the mailing list posts where the OpenJDK security folks have found and are in the process of fixing this issue. Once there is a Java8 release with a fix, I'll change the default to disabled for this hack. Mark > > Modified: > > tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java > tomcat/trunk/webapps/docs/config/valve.xml > > Modified: > tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java > URL: > http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java?rev=1679534&r1=1679533&r2=1679534&view=diff > ============================================================================== > --- > tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java > (original) > +++ > tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java > Fri May 15 10:24:11 2015 > @@ -22,6 +22,7 @@ import java.security.Principal; > import java.security.PrivilegedAction; > import java.security.PrivilegedActionException; > import java.security.PrivilegedExceptionAction; > +import java.util.LinkedHashMap; > import java.util.regex.Pattern; > > import javax.security.auth.Subject; > @@ -91,6 +92,14 @@ public class SpnegoAuthenticator extends > } > } > > + private boolean applyJava8u40Fix = true; > + public boolean getApplyJava8u40Fix() { > + return applyJava8u40Fix; > + } > + public void setApplyJava8u40Fix(boolean applyJava8u40Fix) { > + this.applyJava8u40Fix = applyJava8u40Fix; > + } > + > > @Override > protected String getAuthMethod() { > @@ -164,6 +173,10 @@ public class SpnegoAuthenticator extends > authorizationBC.getOffset(), > authorizationBC.getLength()); > > + if (getApplyJava8u40Fix()) { > + SpnegoTokenFixer.fix(decoded); > + } > + > if (decoded.length == 0) { > if (log.isDebugEnabled()) { > log.debug(sm.getString( > @@ -331,4 +344,153 @@ public class SpnegoAuthenticator extends > return realm.authenticate(gssContext, storeDelegatedCredential); > } > } > + > + > + /** > + * This class implements a hack around an incompatibility between the > + * SPNEGO implementation in Windows and the SPNEGO implementation in > Java 8 > + * update 40 onwards. It was introduced by the change to fix this bug: > + * https://bugs.openjdk.java.net/browse/JDK-8048194 > + * (note: the change applied is not the one suggested in the bug report) > + * <p> > + * It is not clear to me if Windows, Java or Tomcat is at fault here. I > + * think it is Java but I could be wrong. > + * <p> > + * This hack works by re-ordering the list of mechTypes in the > NegTokenInit > + * token. > + */ > + private static class SpnegoTokenFixer { > + > + public static void fix(byte[] token) { > + SpnegoTokenFixer fixer = new SpnegoTokenFixer(token); > + fixer.fix(); > + } > + > + > + private final byte[] token; > + private int pos = 0; > + > + > + private SpnegoTokenFixer(byte[] token) { > + this.token = token; > + } > + > + > + // Fixes the token in-place > + private void fix() { > + /* > + * Useful references: > + * http://tools.ietf.org/html/rfc4121#page-5 > + * http://tools.ietf.org/html/rfc2743#page-81 > + * https://msdn.microsoft.com/en-us/library/ms995330.aspx > + */ > + > + // Scan until we find the mech types list. If we find anything > + // unexpected, abort the fix process. > + if (!tag(0x60)) return; > + if (!length()) return; > + if (!oid("1.3.6.1.5.5.2")) return; > + if (!tag(0xa0)) return; > + if (!length()) return; > + if (!tag(0x30)) return; > + if (!length()) return; > + if (!tag(0xa0)) return; > + lengthAsInt(); > + if (!tag(0x30)) return; > + // Now at the start of the mechType list. > + // Read the mechTypes into an ordered set > + int mechTypesLen = lengthAsInt(); > + int mechTypesStart = pos; > + LinkedHashMap<String, int[]> mechTypeEntries = new > LinkedHashMap<>(); > + while (pos < mechTypesStart + mechTypesLen) { > + int[] value = new int[2]; > + value[0] = pos; > + String key = oidAsString(); > + value[1] = pos - value[0]; > + mechTypeEntries.put(key, value); > + } > + // Now construct the re-ordered mechType list > + byte[] replacement = new byte[mechTypesLen]; > + int replacementPos = 0; > + > + int[] first = mechTypeEntries.remove("1.2.840.113554.1.2.2"); > + if (first != null) { > + System.arraycopy(token, first[0], replacement, > replacementPos, first[1]); > + replacementPos += first[1]; > + } > + for (int[] markers : mechTypeEntries.values()) { > + System.arraycopy(token, markers[0], replacement, > replacementPos, markers[1]); > + replacementPos += markers[1]; > + } > + > + // Finally, replace the original mechType list with the > re-ordered > + // one. > + System.arraycopy(replacement, 0, token, mechTypesStart, > mechTypesLen); > + } > + > + > + private boolean tag(int expected) { > + return (token[pos++] & 0xFF) == expected; > + } > + > + > + private boolean length() { > + // No need to retain the length - just need to consume it and > make > + // sure it is valid. > + int len = lengthAsInt(); > + return pos + len == token.length; > + } > + > + > + private int lengthAsInt() { > + int len = token[pos++] & 0xFF; > + if (len > 127) { > + int bytes = len - 128; > + len = 0; > + for (int i = 0; i < bytes; i++) { > + len = len << 8; > + len = len + (token[pos++] & 0xff); > + } > + } > + return len; > + } > + > + > + private boolean oid(String expected) { > + return expected.equals(oidAsString()); > + } > + > + > + private String oidAsString() { > + if (!tag(0x06)) return null; > + StringBuilder result = new StringBuilder(); > + int len = lengthAsInt(); > + // First byte is special case > + int v = token[pos++] & 0xFF; > + int c2 = v % 40; > + int c1 = (v - c2) / 40; > + result.append(c1); > + result.append('.'); > + result.append(c2); > + int c = 0; > + boolean write = false; > + for (int i = 1; i < len; i++) { > + int b = token[pos++] & 0xFF; > + if (b > 127) { > + b -= 128; > + } else { > + write = true; > + } > + c = c << 7; > + c += b; > + if (write) { > + result.append('.'); > + result.append(c); > + c = 0; > + write = false; > + } > + } > + return result.toString(); > + } > + } > } > > Modified: tomcat/trunk/webapps/docs/config/valve.xml > URL: > http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/valve.xml?rev=1679534&r1=1679533&r2=1679534&view=diff > ============================================================================== > --- tomcat/trunk/webapps/docs/config/valve.xml (original) > +++ tomcat/trunk/webapps/docs/config/valve.xml Fri May 15 10:24:11 2015 > @@ -1438,6 +1438,17 @@ > > <attributes> > > + <attribute name="applyJava8u40Fix" required="false"> > + <p>A fix introduced in Java 8 update 40 ( > + <a > href="https://bugs.openjdk.java.net/browse/JDK-8048194">JDK-8048194</a>) > + onwards broke SPNEGO authentication for IE with Tomcat running on > + Windows 2008 R2 servers. This option enables a work-around that > allows > + SPNEGO authentication to continue working. The work-around should not > + impact other configurations so it is enabled by default. If > necessary, > + the workaround can be disabled by setting this attribute to > + <code>false</code>.</p> > + </attribute> > + > <attribute name="alwaysUseSession" required="false"> > <p>Should a session always be used once a user is authenticated? This > may offer some performance benefits since the session can then be > used > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org > For additional commands, e-mail: dev-h...@tomcat.apache.org > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org