It's OK. Xuelei
On 9/21/2011 12:26 PM, Weijun Wang wrote: > > > On 09/21/2011 11:52 AM, Xuelei Fan wrote: >> On 9/20/2011 1:38 PM, Weijun Wang wrote: >>> Any more comment? >>> >>> Thanks >>> Max >>> >>> On 09/16/2011 01:57 PM, Weijun Wang wrote: >>>> >>>> >>>> On 09/16/2011 01:45 PM, Xuelei Fan wrote: >>>>> My first impression about the fix: >>>>> 1. Do you want to update getKeys() comments about isInitiator value? >>>> >>>> OK. I'll simply remove the "(in JAAS words, isInitiator=true and >>>> storeKey=true)" line. >>>> >> Do you also need to remove "This is used when the client supplies >> passowrd but need ...". I think it is the same as "in JAAS words, ...". > > But that's where it's called. Maybe I should say more: > > This is used when the client supplies password but need keys > to act as an acceptor. It can be called when the state is INIT > and returned keys are generated with default salt, or, after > AS-REQ is performed, might be updated by KDC. > >> >>>>> >>>>> 2. It seems that when isInitiator is true, getKeys() should throw >>>>> exception if the state is not OK, right? If so, it looks like that the >>>>> update of getKeys() will not throw ISE if the state is INIT. Does INIT >>>>> state is enough in such case? >>>> >>>> The KrbAsReqBuilder class does not know about isInitiator so it cannot >>>> make any decision based on it. I'll leave that to its caller. When >>>> username/password is provided, getKeys() can be called as soon as the >>>> object is created, and it returns keys with default salt. If AS-REQ is >>>> performed and state goes OK and the method is called again, the salt >>>> might be updated in PA-DATA. >>>> >> I'm not sure whether the getKeys() return the expected keys when the >> state is INIT. It should be OK as it is an internal class, but as >> requires that the caller always looks like: >> >> if (isInitiator) { >> // action to update the state to OK >> } >> encKeys = builder.getKeys(); > > Correct. In Krb5LoginModule, you can see > > 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 is the only place where this method is called. > >> >> Is getKeys() only be called by Krb5LoginModule.java? If not, I think we >> may need to rethink about the change of the behaviors. Otherwise, it is >> OK. However, is it a little simpler to update builder.getKeys() to >> builder.getKeys(boolean isInitiator)? > > Yes. But I won't add automatic AS-REQ inside it. The argument will only > be used for state check. Is that OK? > > Thanks > Max > > >> >> Xuelei >> >>>> Thanks >>>> Max >>>> >>>> >>>>> >>>>> Xuelei >>>>> >>>>> On Sep 16, 2011, at 12:00 PM, Weijun Wang<weijun.w...@oracle.com> >>>>> wrote: >>>>> >>>>>> Hi All >>>>>> >>>>>> Code changes at >>>>>> >>>>>> http://cr.openjdk.java.net/~weijun/7089889/webrev.00/ >>>>>> >>>>>> KrbAsReqBuilder maintains a state enforcement: at the beginning it's >>>>>> INIT, after doing an AS-REQ it's OK, and we used to only allow >>>>>> getKeys() to be called when the state is OK. Now if an acceptor has >>>>>> isInitiator=false, the OK state will never be reached. In most cases, >>>>>> an acceptor uses a keytab so getKeys() is not called. However, if it >>>>>> uses username/password (most likely in a peer-to-peer case and the >>>>>> acceptor chooses not to login), getKeys() is needed and an exception >>>>>> is thrown. >>>>>> >>>>>> This code change makes getKeys() callable at both INIT and OK states. >>>>>> In order to do this, the checkState() method now accepts varargs. >>>>>> >>>>>> I'll backport this to 7u2, and since 7u2 is now in phase 2, I think I >>>>>> need at least 2 reviewers. >>>>>> >>>>>> Thanks >>>>>> Max >>>>>> >>>>>> -------- Original Message -------- >>>>>> >>>>>> *Change Request ID*: 7089889 >>>>>> >>>>>> *Synopsis*: Krb5LoginModule.login() throws an exception if used >>>>>> without a keytab >>>>>> >>>>>> >>>>>> === *Description* >>>>>> ============================================================ >>>>>> 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 ---------- >>>>>> >>>>>> >>