123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 javax.security.auth.login;
- import java.io.IOException;
- import java.security.AccessController;
- import java.security.AccessControlContext;
- import java.security.PrivilegedExceptionAction;
- import java.security.PrivilegedActionException;
- import java.security.Security;
- import java.util.HashMap;
- import java.util.Map;
- import javax.security.auth.Subject;
- import javax.security.auth.callback.CallbackHandler;
- import javax.security.auth.callback.Callback;
- import javax.security.auth.callback.UnsupportedCallbackException;
- import javax.security.auth.spi.LoginModule;
- import javax.security.auth.AuthPermission;
- import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
- import org.apache.harmony.auth.internal.nls.Messages;
- public class LoginContext {
- private static final String DEFAULT_CALLBACK_HANDLER_PROPERTY = "auth.login.defaultCallbackHandler"; //$NON-NLS-1$
- /*
- * Integer constants which serve as a replacement for the corresponding
- * LoginModuleControlFlag.* constants. These integers are used later as
- * index in the arrays - see loginImpl() and logoutImpl() methods
- */
- private static final int OPTIONAL = 0;
- private static final int REQUIRED = 1;
- private static final int REQUISITE = 2;
- private static final int SUFFICIENT = 3;
- // Subject to be used for this LoginContext's operations
- private Subject subject;
- /*
- * Shows whether the subject was specified by user (true) or was created by
- * this LoginContext itself (false).
- */
- private boolean userProvidedSubject;
- // Shows whether we use installed or user-provided Configuration
- private boolean userProvidedConfig;
- // An user's AccessControlContext, used when user specifies
- private AccessControlContext userContext;
- /*
- * Either a callback handler passed by the user or a wrapper for the user's
- * specified handler - see init() below.
- */
- private CallbackHandler callbackHandler;
- /*
- * An array which keeps the instantiated and init()-ialized login modules
- * and their states
- */
- private Module[] modules;
- // Stores a shared state
- private Map<String, ?> sharedState;
- // A context class loader used to load [mainly] LoginModules
- private ClassLoader contextClassLoader;
- // Shows overall status - whether this LoginContext was successfully logged
- private boolean loggedIn;
- public LoginContext(String name) throws LoginException {
- super();
- init(name, null, null, null);
- }
- public LoginContext(String name, CallbackHandler cbHandler) throws LoginException {
- super();
- if (cbHandler == null) {
- throw new LoginException(Messages.getString("auth.34")); //$NON-NLS-1$
- }
- init(name, null, cbHandler, null);
- }
- public LoginContext(String name, Subject subject) throws LoginException {
- super();
- if (subject == null) {
- throw new LoginException(Messages.getString("auth.03")); //$NON-NLS-1$
- }
- init(name, subject, null, null);
- }
- public LoginContext(String name, Subject subject, CallbackHandler cbHandler)
- throws LoginException {
- super();
- if (subject == null) {
- throw new LoginException(Messages.getString("auth.03")); //$NON-NLS-1$
- }
- if (cbHandler == null) {
- throw new LoginException(Messages.getString("auth.34")); //$NON-NLS-1$
- }
- init(name, subject, cbHandler, null);
- }
- public LoginContext(String name, Subject subject, CallbackHandler cbHandler,
- Configuration config) throws LoginException {
- super();
- init(name, subject, cbHandler, config);
- }
- // Does all the machinery needed for the initialization.
- private void init(String name, Subject subject, final CallbackHandler cbHandler,
- Configuration config) throws LoginException {
- userProvidedSubject = (this.subject = subject) != null;
- //
- // Set config
- //
- if (name == null) {
- throw new LoginException(Messages.getString("auth.00")); //$NON-NLS-1$
- }
- if (config == null) {
- config = Configuration.getAccessibleConfiguration();
- } else {
- userProvidedConfig = true;
- }
- SecurityManager sm = System.getSecurityManager();
- if (sm != null && !userProvidedConfig) {
- sm.checkPermission(new AuthPermission("createLoginContext." + name));//$NON-NLS-1$
- }
- AppConfigurationEntry[] entries = config.getAppConfigurationEntry(name);
- if (entries == null) {
- if (sm != null && !userProvidedConfig) {
- sm.checkPermission(new AuthPermission("createLoginContext.other")); //$NON-NLS-1$
- }
- entries = config.getAppConfigurationEntry("other"); //$NON-NLS-1$
- if (entries == null) {
- throw new LoginException(Messages.getString("auth.35", name)); //$NON-NLS-1$
- }
- }
- modules = new Module[entries.length];
- for (int i = 0; i < modules.length; i++) {
- modules[i] = new Module(entries[i]);
- }
- //
- // Set CallbackHandler and this.contextClassLoader
- //
- /*
- * as some of the operations to be executed (i.e. get*ClassLoader,
- * getProperty, class loading) are security-checked, then combine all of
- * them into a single doPrivileged() call.
- */
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
- public Void run() throws Exception {
- // First, set the 'contextClassLoader'
- contextClassLoader = Thread.currentThread().getContextClassLoader();
- if (contextClassLoader == null) {
- contextClassLoader = ClassLoader.getSystemClassLoader();
- }
- // then, checks whether the cbHandler is set
- if (cbHandler == null) {
- // well, let's try to find it
- String klassName = Security
- .getProperty(DEFAULT_CALLBACK_HANDLER_PROPERTY);
- if (klassName == null || klassName.length() == 0) {
- return null;
- }
- Class<?> klass = Class.forName(klassName, true, contextClassLoader);
- callbackHandler = (CallbackHandler) klass.newInstance();
- } else {
- callbackHandler = cbHandler;
- }
- return null;
- }
- });
- } catch (PrivilegedActionException ex) {
- Throwable cause = ex.getCause();
- throw (LoginException) new LoginException(Messages.getString("auth.36")).initCause(cause);//$NON-NLS-1$
- }
- if (userProvidedConfig) {
- userContext = AccessController.getContext();
- } else if (callbackHandler != null) {
- userContext = AccessController.getContext();
- callbackHandler = new ContextedCallbackHandler(callbackHandler);
- }
- }
- public Subject getSubject() {
- if (userProvidedSubject || loggedIn) {
- return subject;
- }
- return null;
- }
- /**
- * Warning: calling the method more than once may result in undefined
- * behaviour if logout() method is not invoked before.
- */
- public void login() throws LoginException {
- PrivilegedExceptionAction<Void> action = new PrivilegedExceptionAction<Void>() {
- public Void run() throws LoginException {
- loginImpl();
- return null;
- }
- };
- try {
- if (userProvidedConfig) {
- AccessController.doPrivileged(action, userContext);
- } else {
- AccessController.doPrivileged(action);
- }
- } catch (PrivilegedActionException ex) {
- throw (LoginException) ex.getException();
- }
- }
- /**
- * The real implementation of login() method whose calls are wrapped into
- * appropriate doPrivileged calls in login().
- */
- private void loginImpl() throws LoginException {
- if (subject == null) {
- subject = new Subject();
- }
- if (sharedState == null) {
- sharedState = new HashMap<String, Object>();
- }
- // PHASE 1: Calling login()-s
- Throwable firstProblem = null;
- int[] logged = new int[4];
- int[] total = new int[4];
- for (Module module : modules) {
- try {
- // if a module fails during Class.forName(), then it breaks overall
- // attempt - see catch() below
- module.create(subject, callbackHandler, sharedState);
- if (module.module.login()) {
- ++total[module.getFlag()];
- ++logged[module.getFlag()];
- if (module.getFlag() == SUFFICIENT) {
- break;
- }
- }
- } catch (Throwable ex) {
- if (firstProblem == null) {
- firstProblem = ex;
- }
- if (module.klass == null) {
- /*
- * an exception occurred during class lookup - overall
- * attempt must fail a little trick: increase the REQUIRED's
- * number - this will look like a failed REQUIRED module
- * later, so overall attempt will fail
- */
- ++total[REQUIRED];
- break;
- }
- ++total[module.getFlag()];
- // something happened after the class was loaded
- if (module.getFlag() == REQUISITE) {
- // ... and no need to walk down anymore
- break;
- }
- }
- }
- // end of PHASE1,
- // Let's decide whether we have either overall success or a total failure
- boolean fail = true;
- /*
- * Note: 'failed[xxx]!=0' is not enough to check.
- *
- * Use 'logged[xx] != total[xx]' instead. This is because some modules
- * might not be counted as 'failed' if an exception occurred during
- * preload()/Class.forName()-ing. But, such modules still get counted in
- * the total[].
- */
- // if any REQ* module failed - then it's failure
- if (logged[REQUIRED] != total[REQUIRED] || logged[REQUISITE] != total[REQUISITE]) {
- // fail = true;
- } else {
- if (total[REQUIRED] == 0 && total[REQUISITE] == 0) {
- // neither REQUIRED nor REQUISITE was configured.
- // must have at least one SUFFICIENT or OPTIONAL
- if (logged[OPTIONAL] != 0 || logged[SUFFICIENT] != 0) {
- fail = false;
- }
- //else { fail = true; }
- } else {
- fail = false;
- }
- }
- int commited[] = new int[4];
- // clear it
- total[0] = total[1] = total[2] = total[3] = 0;
- if (!fail) {
- // PHASE 2:
- for (Module module : modules) {
- if (module.klass != null) {
- ++total[module.getFlag()];
- try {
- module.module.commit();
- ++commited[module.getFlag()];
- } catch (Throwable ex) {
- if (firstProblem == null) {
- firstProblem = ex;
- }
- }
- }
- }
- }
- // need to decide once again
- fail = true;
- if (commited[REQUIRED] != total[REQUIRED] || commited[REQUISITE] != total[REQUISITE]) {
- //fail = true;
- } else {
- if (total[REQUIRED] == 0 && total[REQUISITE] == 0) {
- /*
- * neither REQUIRED nor REQUISITE was configured. must have at
- * least one SUFFICIENT or OPTIONAL
- */
- if (commited[OPTIONAL] != 0 || commited[SUFFICIENT] != 0) {
- fail = false;
- } else {
- //fail = true;
- }
- } else {
- fail = false;
- }
- }
- if (fail) {
- // either login() or commit() failed. aborting...
- for (Module module : modules) {
- try {
- module.module.abort();
- } catch ( /*LoginException*/Throwable ex) {
- if (firstProblem == null) {
- firstProblem = ex;
- }
- }
- }
- if (firstProblem instanceof PrivilegedActionException
- && firstProblem.getCause() != null) {
- firstProblem = firstProblem.getCause();
- }
- if (firstProblem instanceof LoginException) {
- throw (LoginException) firstProblem;
- }
- throw (LoginException) new LoginException(Messages.getString("auth.37")).initCause(firstProblem); //$NON-NLS-1$
- }
- loggedIn = true;
- }
- public void logout() throws LoginException {
- PrivilegedExceptionAction<Void> action = new PrivilegedExceptionAction<Void>() {
- public Void run() throws LoginException {
- logoutImpl();
- return null;
- }
- };
- try {
- if (userProvidedConfig) {
- AccessController.doPrivileged(action, userContext);
- } else {
- AccessController.doPrivileged(action);
- }
- } catch (PrivilegedActionException ex) {
- throw (LoginException) ex.getException();
- }
- }
- /**
- * The real implementation of logout() method whose calls are wrapped into
- * appropriate doPrivileged calls in logout().
- */
- private void logoutImpl() throws LoginException {
- if (subject == null) {
- throw new LoginException(Messages.getString("auth.38")); //$NON-NLS-1$
- }
- loggedIn = false;
- Throwable firstProblem = null;
- int total = 0;
- for (Module module : modules) {
- try {
- module.module.logout();
- ++total;
- } catch (Throwable ex) {
- if (firstProblem == null) {
- firstProblem = ex;
- }
- }
- }
- if (firstProblem != null || total == 0) {
- if (firstProblem instanceof PrivilegedActionException
- && firstProblem.getCause() != null) {
- firstProblem = firstProblem.getCause();
- }
- if (firstProblem instanceof LoginException) {
- throw (LoginException) firstProblem;
- }
- throw (LoginException) new LoginException(Messages.getString("auth.37")).initCause(firstProblem); //$NON-NLS-1$
- }
- }
- /**
- * <p>A class that servers as a wrapper for the CallbackHandler when we use
- * installed Configuration, but not a passed one. See API docs on the
- * LoginContext.</p>
- *
- * <p>Simply invokes the given handler with the given AccessControlContext.</p>
- */
- private class ContextedCallbackHandler implements CallbackHandler {
- private final CallbackHandler hiddenHandlerRef;
- ContextedCallbackHandler(CallbackHandler handler) {
- super();
- this.hiddenHandlerRef = handler;
- }
- public void handle(final Callback[] callbacks) throws IOException,
- UnsupportedCallbackException {
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
- public Void run() throws IOException, UnsupportedCallbackException {
- hiddenHandlerRef.handle(callbacks);
- return null;
- }
- }, userContext);
- } catch (PrivilegedActionException ex) {
- if (ex.getCause() instanceof UnsupportedCallbackException) {
- throw (UnsupportedCallbackException) ex.getCause();
- }
- throw (IOException) ex.getCause();
- }
- }
- }
- /**
- * A private class that stores an instantiated LoginModule.
- */
- private final class Module {
- // An initial info about the module to be used
- AppConfigurationEntry entry;
- // A mapping of LoginModuleControlFlag onto a simple int constant
- int flag;
- // The LoginModule itself
- LoginModule module;
- // A class of the module
- Class<?> klass;
- Module(AppConfigurationEntry entry) {
- this.entry = entry;
- LoginModuleControlFlag flg = entry.getControlFlag();
- if (flg == LoginModuleControlFlag.OPTIONAL) {
- flag = OPTIONAL;
- } else if (flg == LoginModuleControlFlag.REQUISITE) {
- flag = REQUISITE;
- } else if (flg == LoginModuleControlFlag.SUFFICIENT) {
- flag = SUFFICIENT;
- } else {
- flag = REQUIRED;
- //if(flg!=LoginModuleControlFlag.REQUIRED) throw new Error()
- }
- }
- int getFlag() {
- return flag;
- }
- /**
- * Loads class of the LoginModule, instantiates it and then calls
- * initialize().
- */
- void create(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState)
- throws LoginException {
- String klassName = entry.getLoginModuleName();
- if (klass == null) {
- try {
- klass = Class.forName(klassName, false, contextClassLoader);
- } catch (ClassNotFoundException ex) {
- throw (LoginException) new LoginException(Messages.getString(
- "auth.39", klassName)).initCause(ex); //$NON-NLS-1$
- }
- }
- if (module == null) {
- try {
- module = (LoginModule) klass.newInstance();
- } catch (IllegalAccessException ex) {
- throw (LoginException) new LoginException(Messages.getString(
- "auth.3A", klassName)) //$NON-NLS-1$
- .initCause(ex);
- } catch (InstantiationException ex) {
- throw (LoginException) new LoginException(Messages.getString(
- "auth.3A", klassName)) //$NON-NLS-1$
- .initCause(ex);
- }
- module.initialize(subject, callbackHandler, sharedState, entry.getOptions());
- }
- }
- }
- }
|