SPNEGO auth fails if client proposes MS krb5 OID
- Tweak incoming oid order in spnego token to workaround jdk regression. - Fixes #34
This commit is contained in:
@@ -32,6 +32,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.ietf.jgss.GSSContext;
|
||||
import org.ietf.jgss.GSSCredential;
|
||||
import org.ietf.jgss.GSSException;
|
||||
import org.ietf.jgss.GSSManager;
|
||||
import org.ietf.jgss.GSSName;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -158,11 +159,20 @@ public class SunJaasKerberosTicketValidator implements KerberosTicketValidator,
|
||||
|
||||
@Override
|
||||
public KerberosTicketValidation run() throws Exception {
|
||||
byte[] responseToken = new byte[0];
|
||||
GSSName gssName = null;
|
||||
GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
|
||||
byte[] responseToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
|
||||
GSSName gssName = context.getSrcName();
|
||||
if (gssName == null) {
|
||||
throw new BadCredentialsException("GSSContext name of the context initiator is null");
|
||||
boolean first = true;
|
||||
while (!context.isEstablished()) {
|
||||
if (first) {
|
||||
kerberosTicket = tweakJdkRegression(kerberosTicket);
|
||||
}
|
||||
responseToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
|
||||
gssName = context.getSrcName();
|
||||
if (gssName == null) {
|
||||
throw new BadCredentialsException("GSSContext name of the context initiator is null");
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
if (!holdOnToGSSContext) {
|
||||
context.dispose();
|
||||
@@ -205,4 +215,53 @@ public class SunJaasKerberosTicketValidator implements KerberosTicketValidator,
|
||||
|
||||
}
|
||||
|
||||
private static byte[] tweakJdkRegression(byte[] token) throws GSSException {
|
||||
|
||||
// Due to regression in 8u40/8u45 described in
|
||||
// https://bugs.openjdk.java.net/browse/JDK-8078439
|
||||
// try to tweak token package if it looks like it has
|
||||
// OID's in wrong order
|
||||
//
|
||||
// 0000: 60 82 06 5C 06 06 2B 06 01 05 05 02 A0 82 06 50
|
||||
// 0010: 30 82 06 4C A0 30 30 2E |06 09 2A 86 48 82 F7 12
|
||||
// 0020: 01 02 02|06 09 2A 86 48 86 F7 12 01 02 02 06|0A
|
||||
// 0030: 2B 06 01 04 01 82 37 02 02 1E 06 0A 2B 06 01 04
|
||||
// 0040: 01 82 37 02 02 0A A2 82 06 16 04 82 06 12 60 82
|
||||
//
|
||||
// In above package first token is in position 24 and second
|
||||
// in 35 with both having size 11.
|
||||
//
|
||||
// We simple check if we have these two in this order and swap
|
||||
//
|
||||
// Below code would create two arrays, lets just create that
|
||||
// manually because it doesn't change
|
||||
// Oid GSS_KRB5_MECH_OID = new Oid("1.2.840.113554.1.2.2");
|
||||
// Oid MS_KRB5_MECH_OID = new Oid("1.2.840.48018.1.2.2");
|
||||
// byte[] der1 = GSS_KRB5_MECH_OID.getDER();
|
||||
// byte[] der2 = MS_KRB5_MECH_OID.getDER();
|
||||
|
||||
// 0000: 06 09 2A 86 48 86 F7 12 01 02 02
|
||||
// 0000: 06 09 2A 86 48 82 F7 12 01 02 02
|
||||
|
||||
if (token == null || token.length < 48) {
|
||||
return token;
|
||||
}
|
||||
|
||||
int[] toCheck = new int[] { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x82, 0xF7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2A,
|
||||
0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x02 };
|
||||
|
||||
for (int i = 0; i < 22; i++) {
|
||||
if ((byte) toCheck[i] != token[i + 24]) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] nt = new byte[token.length];
|
||||
System.arraycopy(token, 0, nt, 0, 24);
|
||||
System.arraycopy(token, 35, nt, 24, 11);
|
||||
System.arraycopy(token, 24, nt, 35, 11);
|
||||
System.arraycopy(token, 46, nt, 46, token.length - 24 - 11 - 11);
|
||||
return nt;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.kerberos.authentication.sun;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
|
||||
public class SunJaasKerberosTicketValidatorTests {
|
||||
|
||||
// copy of token taken from a test where windows host
|
||||
// is trying to authenticate with spnego. nothing sensitive here
|
||||
private static String header = "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"
|
||||
+ "eL2PHBfvkne/FgxC";
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testJdkMsKrb5OIDRegressionTweak() throws Exception {
|
||||
thrown.expect(BadCredentialsException.class);
|
||||
thrown.expectMessage(not(containsString("GSSContext name of the context initiator is null")));
|
||||
thrown.expectMessage(containsString("Kerberos validation not succesful"));
|
||||
SunJaasKerberosTicketValidator validator = new SunJaasKerberosTicketValidator();
|
||||
byte[] kerberosTicket = Base64.decode(header.getBytes());
|
||||
validator.validateTicket(kerberosTicket);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user