-
Bug
-
Resolution: Fixed
-
P3
-
7
-
b120
-
b10
-
x86
-
windows_7
-
Verified
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-2215063 | 7u4 | Weijun Wang | P3 | Closed | Fixed | b01 |
FULL PRODUCT VERSION :
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
The call to LoginContext.login() fails with an exception if the LoginContext is configured to use Krb5LoginModule with the following options:
storeKey="true"
useKeyTab="false"
isInitiator="false"
This appears to be a regression due to an attempt to solve bug '6941083'.
The following source code is taken from Kb5LoginModule.attemptAuthentication(false):
if (ktab == null) {
promptForPass(getPasswdFromSharedState);
builder = new KrbAsReqBuilder(principal, password);
if (isInitiator) {
// XXX Even if isInitiator=false, it might be
// better to do an AS-REQ so that keys can be
// updated with PA info
cred = builder.action().getCreds();
}
if (storeKey) {
encKeys = builder.getKeys();
// When encKeys is empty, the login actually fails.
// For compatibility, exception is thrown in commit().
}
}
This code path results builder.getGeys() being called while the builder's state is 'INIT'.
The builder asserts via checkState() that the state is REQ_OK, hence an exception.
This is a regression, as JRE6 and prior versions called EncryptionKey.acquireSecretKeys()
to obtain the keys in this case.
REGRESSION. Last worked in version 6u26
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See the executable test case below. Program should be run on a Windows machine that is joined to a domain. Replace 'REALM', 'KDC', 'DOMAIN_USER' and 'DOMAIN_USER_PWD' as appropriate before running
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
"Login succeeded" printed to console
ACTUAL -
"Login failed" printed to console.
Exception stacktrace printed to strerr
ERROR MESSAGES/STACK TRACES THAT OCCUR :
javax.security.auth.login.LoginException: java.lang.IllegalStateException: Cannot get keys at REQ_OK state
at sun.security.krb5.KrbAsReqBuilder.checkState(Unknown Source)
at sun.security.krb5.KrbAsReqBuilder.getKeys(Unknown Source)
at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Unknown Source)
at com.sun.security.auth.module.Krb5LoginModule.login(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at javax.security.auth.login.LoginContext.invoke(Unknown Source)
at javax.security.auth.login.LoginContext.access$000(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokePriv(Unknown Source)
at javax.security.auth.login.LoginContext.login(Unknown Source)
at LoginModuleExample.main(LoginModuleExample.java:43)
at javax.security.auth.login.LoginContext.invoke(Unknown Source)
at javax.security.auth.login.LoginContext.access$000(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokePriv(Unknown Source)
at javax.security.auth.login.LoginContext.login(Unknown Source)
at LoginModuleExample.main(LoginModuleExample.java:43)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import com.sun.security.auth.module.Krb5LoginModule;
public class LoginModuleExample {
// Replace with the realm and KDC of the Windows domain.
private static final String REALM = "DEV-DEM.RECOMMIND.COM";
private static final String KDC = "AU-DEV-DC01.dev-dem.recommind.com";
// Replace with the username and password of any account on the
// above domain. Account needs to be enabled and not locked out.
private static final String DOMAIN_USER = "sgr";
private static final String DOMAIN_USER_PWD = "0rodriguez)";
private static final String EXAMPLE_SERVER_LOGIN = "example-server";
/**
* @param args
*/
public static void main(String[] args) {
System.setProperty("java.security.krb5.realm", REALM);
System.setProperty("java.security.krb5.kdc", KDC);
Configuration.setConfiguration(new CustomConfiguration());
try {
CallbackHandler handler = getUsernamePasswordHandler(DOMAIN_USER, DOMAIN_USER_PWD);
LoginContext loginContext = new LoginContext(EXAMPLE_SERVER_LOGIN, handler);
loginContext.login();
System.out.println("Login succeeded");
}
catch (Exception e) {
System.out.println("Login failed");
e.printStackTrace();
}
}
private static CallbackHandler getUsernamePasswordHandler(final String username, final String password) {
final CallbackHandler handler = new CallbackHandler() {
public void handle(final Callback[] callback) {
for (int i = 0; i < callback.length; i++) {
if (callback[i] instanceof NameCallback) {
final NameCallback nameCallback = (NameCallback) callback[i];
nameCallback.setName(username);
}
else if (callback[i] instanceof PasswordCallback) {
final PasswordCallback passCallback = (PasswordCallback) callback[i];
passCallback.setPassword(password.toCharArray());
}
}
}
};
return handler;
}
final static class CustomConfiguration extends Configuration {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
AppConfigurationEntry[] entries = new AppConfigurationEntry[0];
if (name.equals(EXAMPLE_SERVER_LOGIN)) {
String krbModule = Krb5LoginModule.class.getName();
LoginModuleControlFlag flag = LoginModuleControlFlag.REQUIRED;
Map<String, String> options = new HashMap<String, String>();
options.put("storeKey", "true");
options.put("useKeyTab", "false");
options.put("isInitiator", "false");
AppConfigurationEntry entry = new AppConfigurationEntry(krbModule, flag, options);
entries = new AppConfigurationEntry[] { entry };
}
return entries;
}
}
}
---------- END SOURCE ----------
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
The call to LoginContext.login() fails with an exception if the LoginContext is configured to use Krb5LoginModule with the following options:
storeKey="true"
useKeyTab="false"
isInitiator="false"
This appears to be a regression due to an attempt to solve bug '6941083'.
The following source code is taken from Kb5LoginModule.attemptAuthentication(false):
if (ktab == null) {
promptForPass(getPasswdFromSharedState);
builder = new KrbAsReqBuilder(principal, password);
if (isInitiator) {
// XXX Even if isInitiator=false, it might be
// better to do an AS-REQ so that keys can be
// updated with PA info
cred = builder.action().getCreds();
}
if (storeKey) {
encKeys = builder.getKeys();
// When encKeys is empty, the login actually fails.
// For compatibility, exception is thrown in commit().
}
}
This code path results builder.getGeys() being called while the builder's state is 'INIT'.
The builder asserts via checkState() that the state is REQ_OK, hence an exception.
This is a regression, as JRE6 and prior versions called EncryptionKey.acquireSecretKeys()
to obtain the keys in this case.
REGRESSION. Last worked in version 6u26
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See the executable test case below. Program should be run on a Windows machine that is joined to a domain. Replace 'REALM', 'KDC', 'DOMAIN_USER' and 'DOMAIN_USER_PWD' as appropriate before running
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
"Login succeeded" printed to console
ACTUAL -
"Login failed" printed to console.
Exception stacktrace printed to strerr
ERROR MESSAGES/STACK TRACES THAT OCCUR :
javax.security.auth.login.LoginException: java.lang.IllegalStateException: Cannot get keys at REQ_OK state
at sun.security.krb5.KrbAsReqBuilder.checkState(Unknown Source)
at sun.security.krb5.KrbAsReqBuilder.getKeys(Unknown Source)
at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Unknown Source)
at com.sun.security.auth.module.Krb5LoginModule.login(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at javax.security.auth.login.LoginContext.invoke(Unknown Source)
at javax.security.auth.login.LoginContext.access$000(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokePriv(Unknown Source)
at javax.security.auth.login.LoginContext.login(Unknown Source)
at LoginModuleExample.main(LoginModuleExample.java:43)
at javax.security.auth.login.LoginContext.invoke(Unknown Source)
at javax.security.auth.login.LoginContext.access$000(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokePriv(Unknown Source)
at javax.security.auth.login.LoginContext.login(Unknown Source)
at LoginModuleExample.main(LoginModuleExample.java:43)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import com.sun.security.auth.module.Krb5LoginModule;
public class LoginModuleExample {
// Replace with the realm and KDC of the Windows domain.
private static final String REALM = "DEV-DEM.RECOMMIND.COM";
private static final String KDC = "AU-DEV-DC01.dev-dem.recommind.com";
// Replace with the username and password of any account on the
// above domain. Account needs to be enabled and not locked out.
private static final String DOMAIN_USER = "sgr";
private static final String DOMAIN_USER_PWD = "0rodriguez)";
private static final String EXAMPLE_SERVER_LOGIN = "example-server";
/**
* @param args
*/
public static void main(String[] args) {
System.setProperty("java.security.krb5.realm", REALM);
System.setProperty("java.security.krb5.kdc", KDC);
Configuration.setConfiguration(new CustomConfiguration());
try {
CallbackHandler handler = getUsernamePasswordHandler(DOMAIN_USER, DOMAIN_USER_PWD);
LoginContext loginContext = new LoginContext(EXAMPLE_SERVER_LOGIN, handler);
loginContext.login();
System.out.println("Login succeeded");
}
catch (Exception e) {
System.out.println("Login failed");
e.printStackTrace();
}
}
private static CallbackHandler getUsernamePasswordHandler(final String username, final String password) {
final CallbackHandler handler = new CallbackHandler() {
public void handle(final Callback[] callback) {
for (int i = 0; i < callback.length; i++) {
if (callback[i] instanceof NameCallback) {
final NameCallback nameCallback = (NameCallback) callback[i];
nameCallback.setName(username);
}
else if (callback[i] instanceof PasswordCallback) {
final PasswordCallback passCallback = (PasswordCallback) callback[i];
passCallback.setPassword(password.toCharArray());
}
}
}
};
return handler;
}
final static class CustomConfiguration extends Configuration {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
AppConfigurationEntry[] entries = new AppConfigurationEntry[0];
if (name.equals(EXAMPLE_SERVER_LOGIN)) {
String krbModule = Krb5LoginModule.class.getName();
LoginModuleControlFlag flag = LoginModuleControlFlag.REQUIRED;
Map<String, String> options = new HashMap<String, String>();
options.put("storeKey", "true");
options.put("useKeyTab", "false");
options.put("isInitiator", "false");
AppConfigurationEntry entry = new AppConfigurationEntry(krbModule, flag, options);
entries = new AppConfigurationEntry[] { entry };
}
return entries;
}
}
}
---------- END SOURCE ----------
- backported by
-
JDK-2215063 Krb5LoginModule.login() throws an exception if used without a keytab
-
- Closed
-
- relates to
-
JDK-6941083 take salt out of PrincipalName
-
- Closed
-