JDK-8028780 : JDK KRB5 module throws OutOfMemoryError when CCache is corrupt
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.security
  • Affected Version: 7u40
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • Submitted: 2013-11-15
  • Updated: 2016-08-26
  • Resolved: 2013-12-30
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9 Other
8u40Fixed 9 b01Fixed openjdk7uFixed
Description
FULL PRODUCT VERSION :
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
$uname -a
Linux INVRLX61PPQA19 2.6.32-131.0.15.el6.x86_64 #1 SMP Tue May 10 15:42:40 EDT 2011 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
I created credential cache file using kinit and copied to another location/machine using FTP. During this step the file became corrupt. Whenever I executed the program to fetch credential from this cache file I hit OutOfMemoryError.

REGRESSION.  Last worked in version 7u40

ADDITIONAL REGRESSION INFORMATION:
The program is not tested with earlier version of JDK and hence not sure if it is a regression but the JDK documentation mentions that the API will throw a valid exception whenever credential cache file is corrupt.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a file based credential cache using kinit command
2. File can be corrupted by manually editing it (Insert a few characters in the middle of the file)
3. Set KRB5CCNAME to the file created by kinit in case it is not set (it should be set by default)
3. Compile the below program and run it


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
API should throw a exception with a valid mesage instead of LoginException containing OutOfMemoryError
ACTUAL -
com.kerberosauth.KerberosAuthException: Cannot get credential because [java.lang.OutOfMemoryError: Java heap space
at sun.security.krb5.internal.ccache.CCacheInputStream.readData(CCacheInputStream.java:259)
at sun.security.krb5.internal.ccache.CCacheInputStream.readCred(CCacheInputStream.java:382)
at sun.security.krb5.internal.ccache.FileCredentialsCache.load(FileCredentialsCache.java:189)
at sun.security.krb5.internal.ccache.FileCredentialsCache.acquireInstance(FileCredentialsCache.java:84)
at sun.security.krb5.internal.ccache.CredentialsCache.getInstance(CredentialsCache.java:83)
at sun.security.krb5.Credentials.acquireTGTFromCache(Credentials.java:330)
at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:631)
at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:584)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:784)
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:203)
at javax.security.auth.login.LoginContext$5.run(LoginContext.java:721)
at javax.security.auth.login.LoginContext$5.run(LoginContext.java:719)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokeCreatorPriv(LoginContext.java:718)
at javax.security.auth.login.LoginContext.login(LoginContext.java:590)
at com.kerberosauth.impl.gss.GSSLoginModule.getGSSCredential(GSSLoginModule.java:59)
at com.kerberosauth.impl.KerberosAuthManagerImpl.getLoggedInUserCredential(KerberosAuthManagerImpl.java:170)
at com.kerberosauth.impl.KAMSSOTest.main(KAMSSOTest.java:20)
].
at com.kerberosauth.impl.gss.GSSLoginModule.getGSSCredential(GSSLoginModule.java:61)
at com.kerberosauth.impl.KerberosAuthManagerImpl.getLoggedInUserCredential(KerberosAuthManagerImpl.java:170)
at com.kerberosauth.impl.KerberosAuthManagerImpl.acquireCredential(KerberosAuthManagerImpl.java:72)
at com.kerberosauth.impl.KerberosAuthManagerImpl.acquireCredential(KerberosAuthManagerImpl.java:105)
at com.kerberosauth.impl.KAMSSOTest.main(KAMSSOTest.java:20)
Caused by: javax.security.auth.login.LoginException: java.lang.OutOfMemoryError: Java heap space
at sun.security.krb5.internal.ccache.CCacheInputStream.readData(CCacheInputStream.java:259)
at sun.security.krb5.internal.ccache.CCacheInputStream.readCred(CCacheInputStream.java:382)
at sun.security.krb5.internal.ccache.FileCredentialsCache.load(FileCredentialsCache.java:189)
at sun.security.krb5.internal.ccache.FileCredentialsCache.acquireInstance(FileCredentialsCache.java:84)
at sun.security.krb5.internal.ccache.CredentialsCache.getInstance(CredentialsCache.java:83)
at sun.security.krb5.Credentials.acquireTGTFromCache(Credentials.java:330)
at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:631)
at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:584)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:784)
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:203)
at javax.security.auth.login.LoginContext$5.run(LoginContext.java:721)
at javax.security.auth.login.LoginContext$5.run(LoginContext.java:719)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokeCreatorPriv(LoginContext.java:718)
at javax.security.auth.login.LoginContext.login(LoginContext.java:590)
at com.kerberosauth.impl.gss.GSSLoginModule.getGSSCredential(GSSLoginModule.java:59)

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.OutOfMemoryError: Java heap space

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
// TODO: Main method has to be written. It should just call "getGSSCredential" method with an instance of GSSManager

GSSCredential getGSSCredential(GSSManager manager) throws KerberosAuthException {
LoginContext lc = lc = new LoginContext("lc", null, null, new GSSSunLoginConfiguration());
try {
lc.login();
} catch (LoginException e) {
throw new KerberosAuthException(e, KamMsg.ACQUIRE_CREDENTIAL_FAILURE_error, e.getMessage());
}credType = GSSCredential.INITIATE_AND_ACCEPT;
}

try {
return (GSSCredential) Subject.doAs(lc.getSubject(), new SubjectAction(manager, GSSCredential.INITIATE_ONLY, 0));
} catch (PrivilegedActionException exception) {
Throwable t = exception.getCause();
throw new KerberosAuthException(t, AuthModuleMsg.ACQUIRE_CREDENTIAL_FAILURE_error, t.getMessage());
}
}


private static final class GSSSunLoginConfiguration extends Configuration {

private AppConfigurationEntry configEntry;

/** Login configuration for OS logged in user */
public GSSSunLoginConfiguration() {
Map<String, String> params = new HashMap<String, String>();

params.put("isInitiator", Boolean.TRUE.toString());
params.put("doNotPrompt", Boolean.TRUE.toString());
params.put("useTicketCache", Boolean.TRUE.toString());

configEntry = new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, params);
}

public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
return new AppConfigurationEntry[] {
configEntry
};
}
}

private static final class SubjectAction implements PrivilegedExceptionAction<GSSCredential> {

private final GSSManager gssManager;

private final int credentialType;

private final int credentialLifetime;

private SubjectAction(GSSManager mgr, int credType, int lifetime) {
gssManager = mgr;
credentialType = credType;
credentialLifetime = lifetime;
}

public GSSCredential run() throws GSSException {
return gssManager.createCredential(null, credentialLifetime, new Oid("1.2.840.113554.1.2.2"), credentialType);
}
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
No workaround
Comments
This is P4 and I cannot see a threat that someone else can place a corrupt ccache file to a client's machine to trigger this error. Won't fix it in jdk 8. Said that, a webrev is ready at http://cr.openjdk.java.net/~weijun/8028780/webrev.00/. My target now is 9/8u20/7u80.
22-11-2013

There are multiple places that does not check the size before allocating an array, like adlength = read(4); data = new byte[adlength]; Should use sun.misc.IOUtils::readFully.
22-11-2013

Max, can you triage this one?
21-11-2013

Didn't check whether it's applicable to jdk8, since the reproducer code isn't ready to go.
21-11-2013

Removing 'regression' label as the reproducer hasn't been tested with earlier versions of JDK.
21-11-2013