/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: DSAMECallbackHandler.java,v 1.7 2008/08/19 19:08:54 veiming Exp $ * * Portions Copyrighted 2014-2016 ForgeRock AS. */ package com.sun.identity.authentication.service; import static org.forgerock.openam.utils.Time.*; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import com.sun.identity.shared.debug.Debug; import com.sun.identity.authentication.spi.PagePropertiesCallback; /** * This class is the OpenAM implementation for Java * CallbackHandler and it can be passed to underlying * auth services so that it may interact with other components to retrieve * specific authentication data, such as usernames and passwords, or to display * certain information, such as error and warning messages. */ public class DSAMECallbackHandler implements CallbackHandler { // this should be read by AuthContext in order to receive // what callbacks the module needs static Debug debug = Debug.getInstance("amCallback"); AMLoginContext am; private boolean isPureJAAS; LoginState loginState; // this will be sent by AuthContext for module to read. Callback[] submitRequiredInfo = null; static AuthThreadManager authThreadManager ; String sid = null; private static final DSAMECallbackHandlerError HANDLER_ERROR = new DSAMECallbackHandlerError("return from DSAMECallback"); /** * Creates DSAMECallbackHandler object and it associates * login thread and login state with callback hndler * @param am AMLoginContext for this callback */ public DSAMECallbackHandler(AMLoginContext am, boolean isPureJAAS) { this.am = am; this.isPureJAAS = isPureJAAS; this.authThreadManager= am.authThread; this.loginState = am.getLoginState(); } private void setPageTimeout(Callback[] callbacks) { long pageTimeOut = getTimeOut(callbacks); loginState.setPageTimeOut(pageTimeOut); long lastCallbackSent = currentTimeMillis(); loginState.setLastCallbackSent(lastCallbackSent); } /** *

Retrieves or displays the information requested in the * provided Callbacks. * *

This method implementation checks the * instance(s) of the Callback object(s) passed in * to retrieve or display the requested information. * @param callbacks an array of Callback objects provided * by an underlying security service which contains * the information requested to be retrieved or displayed. * * @exception java.io.IOException if an input or output error occurs. * @exception UnsupportedCallbackException if the implementation of this * method does not support one or more of the Callbacks * specified in the callbacks parameter. */ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { if (debug.messageEnabled()) { debug.message("callback handler method"); } if ((callbacks.length == 1) && (callbacks[0] instanceof LoginStateCallback)) { debug.message("LoginState Callbacks"); ((LoginStateCallback) callbacks[0]).setLoginState(loginState); } else { if (isPureJAAS) { setPageTimeout(callbacks); loginState.setSubmittedCallback(null,am) ; loginState.setReceivedCallback(callbacks,am) ; Thread thread = Thread.currentThread(); if (debug.messageEnabled()) { debug.message("waiting for submitted info " + thread); } callbacks = am.submitCallbackInfo(); // check if the thread had timedout if (authThreadManager.isTimedOut(thread)) { loginState.setTimedOut(true); loginState.setReceivedCallback(null,am) ; authThreadManager.removeFromHash(thread,"timedOutHash"); throw new IOException(AMAuthErrorCode.AUTH_TIMEOUT); } // check if there is a timeout checkLoginTimeout(); } else { if (loginState.getSubmittedInfo() != null) { debug.message("DSAMEHandler: found submitted callbacks !"); // check if there is a timeout checkLoginTimeout(); Callback[] callbacks2 = loginState.getSubmittedInfo(); copyCallbacks(callbacks, callbacks2); loginState.setReceivedCallback_NoThread(null); } else { setPageTimeout(callbacks); loginState.setReceivedCallback_NoThread(callbacks) ; debug.message("Set callbacks, throwing java.lang.Error."); throw HANDLER_ERROR; } } if (debug.messageEnabled()) { debug.message("DSAMECAllbackhandler..."+ callbacks); } if (isPureJAAS) { loginState.setReceivedCallback(null,am) ; } } } /** * Clones callbacks from cb2 to cb1. * @param cb1 new callbacks will be cloned from. * @param cb2 original callbacks will be cloned to. */ private void copyCallbacks(Callback[] cb1, Callback[] cb2) { int len1 = cb1.length; int len2 = cb2.length; if (len1 == len2) { for (int m = 0; m < len1; m++) { if (cb1[m] != cb2[m]) { cb1[m] = cb2[m]; } } } else { int indx1 = 0; int indx2 = 0; while (indx1 < len1 && cb1[indx1] instanceof PagePropertiesCallback) { indx1 ++; } while (indx2 < len2 && cb2[indx2] instanceof PagePropertiesCallback) { indx2 ++; } int n = len1 - indx1; if (n > len2 - indx2) { n = len2 - indx2; } for (int m = 0; m < n; m++ ) { if (cb1[indx1] != cb2[indx2]) { cb1[indx1] = cb2[indx2]; } indx1 ++; indx2 ++; } } } /** * Returns timeout value , 60 secs assumed if no timeout value found * @param callbacks checked for timeout. * @return timeout value for callbacks. */ long getTimeOut(Callback[] callbacks) { long pageTimeOut = 60; if (callbacks != null && callbacks[0] instanceof PagePropertiesCallback) { PagePropertiesCallback pagePropertyCallback = (PagePropertiesCallback) callbacks[0]; pageTimeOut = new Integer(pagePropertyCallback.getTimeOutValue()).longValue(); } return pageTimeOut; } /** * Check login time out. LoginTimeoutException will be thrown if the login * time out has been reached. * @exception IOException must be thrown if the login timeout * has been reached. */ void checkLoginTimeout() throws IOException { long lastCallbackSent = loginState.getLastCallbackSent(); long pageTimeOut = loginState.getPageTimeOut(); long now = currentTimeMillis(); if ((lastCallbackSent + ((pageTimeOut-3)*1000)) < now) { debug.message("Page Timeout"); loginState.setTimedOut(true); loginState.setReceivedCallback(null,am) ; throw new IOException(AMAuthErrorCode.AUTH_TIMEOUT); } } /** * This error is used to control the authentication processing: if there is a problem while processing the * callbacks this error prevents the auth framework consider this error as an authentication module failure. */ public static class DSAMECallbackHandlerError extends Error { public DSAMECallbackHandlerError(String message) { super(message); } @Override public synchronized Throwable fillInStackTrace() { //As this is an expected error that's used for flow control, we do not need to populate the stacktrace. return this; } } }