-
Bug
-
Resolution: Fixed
-
P4
-
1.0
-
hopper
-
x86
-
windows_2000
-
Verified
Name: nt126004 Date: 02/05/2002
FULL PRODUCT VERSION :
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-b24)
Java HotSpot(TM) Client VM (build 1.3.1-b24, mixed mode)
FULL OPERATING SYSTEM VERSION :
Redhat 6.2
Linux beta 2.2.17 #3 SMP Mon Sep 11 10:47:02 CDT 2000 i686
unknown
ADDITIONAL OPERATING SYSTEMS :
Windows 2000
A DESCRIPTION OF THE PROBLEM :
This is a resubmission with complete test programs
recreating problem.
There appears to be a bug in the JAAS api involved in sub-
classing the Configuration class. By default LoginContext
uses the default implementation of Configuration
(ConfigFile) to load modules and parameters. Each login
module is then initalized when the login method of
LoginContext is called. When using the default
Configuration class, each instance of LoginContext results
in a unique instance of each LoginModule.
When the Configuration class is extended, the LoginContext
class behaves differently. Rather than each new instance of
LoginContext resulting in a new instance of LoginModule,
only one instance of LoginModule is ever instantiated. The
initalize method of LoginModule is only ever called once,
therefore the same instance of CallbackHandler is executed
resulting in the same data every time.
We have recreated this on multiple operating systems and
with different programs an subclasses.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Included is source code to recreate this bug.
Three modules are included:
AuthenticationConfiguration.java - this is the subclass of
Configuration.
GenericLoginModule.java - an extenstion of LoginModule that
prints logic flow (as called by LoginContext)
testJAASBug.java - a program ( with main ) to recreate the
problem.
EXPECTED VERSUS ACTUAL BEHAVIOR :
a new instance of LoginContext should instantiate new
LoginModule Objects. This works fine when using the
defualt Configuration class. When using a subclass, only
one instance of LoginModule is used.
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package jaasconfigurationbug;
/**
* Title: JAAS Configuration Bug
* Description: Project to demonstrate JAAS bug when dealing with subclassing
* of Configuration
* Copyright: Copyright (c) 2001
* Company: Servzone.com
* @author Richard Scott
* @version 1.0
*/
import javax.security.auth.*;
import javax.security.auth.spi.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import java.security.*;
public class TestJAASBug
{
public TestJAASBug()
{
// Initialize Configuration Class with subclass
Configuration.setConfiguration(new AuthenticationConfiguration());
// Now JAAS will use class "AuthenticationConfiguration"
// Set a single LoginModule for JAAS to use
AuthenticationConfiguration.addAppConfigurationEntry
("required", "jaasconfigurationbug.GenericLoginModule", "test=yes");
System.out.println("Instance one of Login Context begin instantiated");
// Instantiate the first LoginContext - the passed String does not
matter because our Configuration subclass does not use it
try
{
LoginContext lc = new LoginContext("anything");
// and login
lc.login();
}
catch (LoginException le)
{
le.printStackTrace();
}
// So far things have worked A-OK!!
// Noe we have a second person needed to login to our server
System.out.println("Instance two of Login Context begin instantiated");
try
{
LoginContext lc2 = new LoginContext("anythingelse");
// and login
lc2.login();
}
catch (LoginException le)
{
le.printStackTrace();
}
// Note that the exact same Instance of GenericLoginModule Executes
// And that initialize is not called again. the correct behavior is for
// each new instance of LoginContext to instantiate a new instance of
// LoginModule. This behavior works with the base Configuration class
// but ALWAYS fails with a subclasse of Configuration.
}
public static void main(String[] args)
{
TestJAASBug test = new TestJAASBug();
}
}
package jaasconfigurationbug;
/**
* Title:
* Description:
* Copyright: Copyright (c) 2001
* Company:
* @author
* @version 1.0
*/
import javax.security.auth.login.Configuration;
import javax.security.auth.login.AppConfigurationEntry;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.HashMap;
public class AuthenticationConfiguration extends Configuration
{
private static ArrayList appConfigurationList = new ArrayList();
public AuthenticationConfiguration()
{
}
/**
* Interface method requiring us to return all the LoginModules we
* know about.
*/
public AppConfigurationEntry[] getAppConfigurationEntry( String
applicationName )
{
AppConfigurationEntry[] ace = new AppConfigurationEntry
[appConfigurationList.size()];
// Weird bug - Java would not le us use the toArray method of
// appConfigurationList - would not cast properly
for (int i=0;i < appConfigurationList.size();i++)
{
ace[i] = (AppConfigurationEntry)appConfigurationList.get(i);
}
return ace;
}
/**
* Interface method that requires us to reload the configuration.
* This method is defined but there is no underluing implementation.
*/
public void refresh()
{
// Right now this is a load once scheme and we will not implement the
// refresh method
}
/**
* Adds a new LoginModule entry from the configuration process
*/
public static boolean addAppConfigurationEntry ( String flag, String
module, String options )
{
AppConfigurationEntry.LoginModuleControlFlag lmcf;
HashMap optionList = null;
StringTokenizer st1 = null;
StringTokenizer st2 = null;
if ( flag.equals("required") )
{
lmcf = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
}
else if ( flag.equals("requisite") )
{
lmcf = AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
}
else if ( flag.equals("sufficient") )
{
lmcf = AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
}
else if ( flag.equals("optional") )
{
lmcf = AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
}
else
{
return false;
}
st1 = new StringTokenizer( options, " ");
optionList = new HashMap();
//Walk through space delimited option list String
// eg. option1=yes option2=no option3=true
while (st1.hasMoreTokens())
{
// get the second Stringtokenizer that seperates by equal sign (=)
st2 = new StringTokenizer( st1.nextToken(), "=");
// make sure there is only one equal sign (=) and
// send the two parts to the Map
if (st2.countTokens() == 2)
{
optionList.put(st2.nextToken() , st2.nextToken());
}
}
//finish up by adding a new entry to the appConfigurationList.
return appConfigurationList.add( new AppConfigurationEntry(module,
lmcf, optionList));
}
}
package jaasconfigurationbug;
/**
* Title: ActiveConnect - Enterprise Edition
* Description: JAAS compliant login module that authenticates useris and
* passwords in the XML Realm
* Copyright: Copyright (c) 2002
* Company: Servzone.com, Inc.
* @author Richard Scott
* @version 1.0
*/
import javax.security.auth.*;
import javax.security.auth.spi.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import java.io.*;
import java.security.*;
import java.util.*;
/**
* Login module that checks a username and password.
*/
public class GenericLoginModule implements LoginModule
{
private Subject subject;
private CallbackHandler callbackHandler;
private boolean loginSucceeded = false;
private boolean commitSucceeded = false;
private String userid;
private char[] password;
private Principal principal;
/**
* * Initialize this login module.
*/
public void initialize(Subject subject,CallbackHandler callbackHandler,
Map sharedState,Map options)
{
System.out.println("Initialize method called for Object " + this);
}
/**
* * Attempt to log a user in.
*/
public boolean login() throws LoginException
{
System.out.println("Login method called for" + this);
return true;
}
/**
* * This is called if all logins succeeded.
*/
public boolean commit() throws LoginException
{
System.out.println("Commit method called for Object " + this);
return true;
}
/**
* * Called if overall login failed.
*/
public boolean abort() throws LoginException
{
System.out.println("Abort method called for Object " + this);
return true;
}
/**
* * Logout the user.
*/
public boolean logout() throws LoginException
{
System.out.println("Logout method called for Object " + this);
return true;
}
}
---------- END SOURCE ----------
(Review ID: 138944)
======================================================================