/* * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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 org.wso2.carbon.identity.application.authenticator.iwa.servlet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.application.authenticator.iwa.IWAAuthenticator; import org.wso2.carbon.identity.application.authenticator.iwa.IWAConstants; import org.wso2.carbon.identity.application.authenticator.iwa.IWAServiceDataHolder; import org.wso2.carbon.identity.core.util.IdentityUtil; import waffle.servlet.AutoDisposableWindowsPrincipal; import waffle.servlet.NegotiateSecurityFilter; import waffle.servlet.WindowsPrincipal; import waffle.servlet.spi.SecurityFilterProvider; import waffle.servlet.spi.SecurityFilterProviderCollection; import waffle.util.AuthorizationHeader; import waffle.windows.auth.IWindowsAuthProvider; import waffle.windows.auth.IWindowsIdentity; import waffle.windows.auth.IWindowsImpersonationContext; import waffle.windows.auth.PrincipalFormat; import waffle.windows.auth.impl.WindowsAuthProviderImpl; import javax.security.auth.Subject; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.net.URLEncoder; import java.security.Principal; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; /** * This class handles the IWA login requests. The implementation is based on the NegotiateSecurityFilter class. */ public class IWAServelet extends HttpServlet { public static final String PRINCIPAL_SESSION_KEY = NegotiateSecurityFilter.class .getName() + ".PRINCIPAL"; private static Log log = LogFactory.getLog(IWAServelet.class); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String commonAuthURL = IdentityUtil.getServerURL(IWAConstants.COMMON_AUTH_EP, false, true); String param = request.getParameter(IWAConstants.IWA_PARAM_STATE); if (param == null) { throw new IllegalArgumentException(IWAConstants.IWA_PARAM_STATE + " parameter is null."); } commonAuthURL += "?" + IWAConstants.IWA_PARAM_STATE + "=" + URLEncoder.encode(param, IWAConstants.UTF_8) + "&" + IWAAuthenticator.IWA_PROCESSED + "=1"; if (doFilterPrincipal(request)) { // previously authenticated user response.sendRedirect(commonAuthURL); return; } AuthorizationHeader authorizationHeader = new AuthorizationHeader(request); // authenticate user if (!authorizationHeader.isNull()) { // log the user in using the token IWindowsIdentity windowsIdentity; try { windowsIdentity = IWAServiceDataHolder.getInstance().getProviders().doFilter(request, response); if (windowsIdentity == null) { return; } } catch (IOException e) { log.warn("error logging in user.", e); sendUnauthorized(response, true); return; } IWindowsImpersonationContext ctx = null; try { if (!IWAServiceDataHolder.getInstance().isAllowGuestLogin() && windowsIdentity.isGuest()) { log.warn("guest login disabled: " + windowsIdentity.getFqn()); sendUnauthorized(response, true); return; } if (log.isDebugEnabled()) { log.debug("logged in user: " + windowsIdentity.getFqn() + " (" + windowsIdentity.getSidString() + ")"); } HttpSession session = request.getSession(true); if (session == null) { throw new ServletException("Expected HttpSession"); } Subject subject = (Subject) session.getAttribute(IWAConstants.SUBJECT_ATTRIBUTE); if (subject == null) { subject = new Subject(); } WindowsPrincipal windowsPrincipal; if (IWAServiceDataHolder.getInstance().isImpersonate()) { windowsPrincipal = new AutoDisposableWindowsPrincipal(windowsIdentity, IWAServiceDataHolder.getInstance().getPrincipalFormat(), IWAServiceDataHolder.getInstance().getRoleFormat()); } else { windowsPrincipal = new WindowsPrincipal(windowsIdentity, IWAServiceDataHolder.getInstance().getPrincipalFormat(), IWAServiceDataHolder.getInstance().getRoleFormat()); } if (log.isDebugEnabled()) { log.debug("roles: " + windowsPrincipal.getRolesString()); } subject.getPrincipals().add(windowsPrincipal); session.setAttribute(IWAConstants.SUBJECT_ATTRIBUTE, subject); log.info("Successfully logged in user: " + windowsIdentity.getFqn()); request.getSession().setAttribute(PRINCIPAL_SESSION_KEY, windowsPrincipal); if (IWAServiceDataHolder.getInstance().isImpersonate()) { if (log.isDebugEnabled()) { log.debug("impersonating user"); } ctx = windowsIdentity.impersonate(); } } finally { if (IWAServiceDataHolder.getInstance().isImpersonate() && ctx != null) { if (log.isDebugEnabled()) { log.debug("terminating impersonation"); } ctx.revertToSelf(); } else { windowsIdentity.dispose(); } } response.sendRedirect(commonAuthURL); return; } if (log.isDebugEnabled()) { log.debug("authorization required"); } sendUnauthorized(response, false); } /** * Check whether the request is already authenticated using IWA * * @param request The HttpServletRequest * @return * @throws IOException * @throws ServletException */ private boolean doFilterPrincipal(HttpServletRequest request) throws IOException, ServletException { Principal principal = request.getUserPrincipal(); if (principal == null) { HttpSession session = request.getSession(false); if (session != null) { principal = (Principal) session.getAttribute(PRINCIPAL_SESSION_KEY); } } if (principal == null) { // no principal in this request return false; } if (IWAServiceDataHolder.getInstance().getProviders().isPrincipalException(request)) { // the providers signal to authenticate despite an existing principal, eg. NTLM post return false; } // user already authenticated if (principal instanceof WindowsPrincipal) { if (log.isDebugEnabled()) { log.debug("previously authenticated Windows user: " + principal.getName()); } WindowsPrincipal windowsPrincipal = (WindowsPrincipal) principal; if (IWAServiceDataHolder.getInstance().isImpersonate() && windowsPrincipal.getIdentity() == null) { // This can happen when the session has been serialized then de-serialized // and because the IWindowsIdentity field is transient. In this case re-ask an // authentication to get a new identity. return false; } IWindowsImpersonationContext ctx = null; if (IWAServiceDataHolder.getInstance().isImpersonate()) { if (log.isDebugEnabled()) { log.debug("re-impersonating user"); } ctx = windowsPrincipal.getIdentity().impersonate(); } if (IWAServiceDataHolder.getInstance().isImpersonate() && ctx != null) { if (log.isDebugEnabled()) { log.debug("terminating impersonation"); } ctx.revertToSelf(); } } else { if (log.isDebugEnabled()) { log.debug("previously authenticated user: " + principal.getName()); } } return true; } /** * Send response as unauthorized * * @param response * @param close whether to close the connection or to keep it alive */ private void sendUnauthorized(HttpServletResponse response, boolean close) { try { IWAServiceDataHolder.getInstance().getProviders().sendUnauthorized(response); if (close) { response.setHeader(IWAConstants.HTTP_CONNECTION_HEADER, IWAConstants.CONNECTION_CLOSE); } else { response.setHeader(IWAConstants.HTTP_CONNECTION_HEADER, IWAConstants.CONNECTION_KEEP_ALIVE); } response.sendError(HttpServletResponse.SC_UNAUTHORIZED); response.flushBuffer(); } catch (IOException e) { log.error("Error when sending unauthorized response.", e); } } @Override public void init(ServletConfig config) throws ServletException { Map<String, String> implParameters = new HashMap<String, String>(); String authProvider = null; String[] providerNames = null; if (config != null) { Enumeration parameterNames = config.getInitParameterNames(); while (parameterNames.hasMoreElements()) { String parameterName = (String) parameterNames.nextElement(); String parameterValue = config .getInitParameter(parameterName); if (parameterName.equals(IWAConstants.PRINCIPAL_FORMAT)) { IWAServiceDataHolder.getInstance().setPrincipalFormat(PrincipalFormat.valueOf(parameterValue)); } else if (parameterName.equals(IWAConstants.ROLE_FORMAT)) { IWAServiceDataHolder.getInstance().setRoleFormat(PrincipalFormat.valueOf(parameterValue)); } else if (parameterName.equals(IWAConstants.ALLOW_GUEST_LOGIN)) { IWAServiceDataHolder.getInstance().setAllowGuestLogin(Boolean.parseBoolean(parameterValue)); } else if (parameterName.equals(IWAConstants.IMPERSONATE)) { IWAServiceDataHolder.getInstance().setImpersonate(Boolean.parseBoolean(parameterValue)); } else if (parameterName.equals(IWAConstants.SECURITY_FILTER_PROVIDERS)) { providerNames = parameterValue.split("\\s+"); } else if (parameterName.equals(IWAConstants.AUTH_PROVIDER)) { authProvider = parameterValue; } else { implParameters.put(parameterName, parameterValue); } } } if (authProvider != null) { try { IWAServiceDataHolder.getInstance().setAuth( (IWindowsAuthProvider) Class.forName(authProvider).getConstructor().newInstance()); } catch (Exception e) { throw new ServletException("Error loading '" + authProvider, e); } } if (IWAServiceDataHolder.getInstance().getAuth() == null) { IWAServiceDataHolder.getInstance().setAuth(new WindowsAuthProviderImpl()); } if (providerNames != null) { IWAServiceDataHolder.getInstance().setProviders(new SecurityFilterProviderCollection( providerNames, IWAServiceDataHolder.getInstance().getAuth())); } // create default providers if none specified if (IWAServiceDataHolder.getInstance().getProviders() == null) { if (log.isDebugEnabled()) { log.debug("initializing default security filter providers"); } IWAServiceDataHolder.getInstance().setProviders(new SecurityFilterProviderCollection( IWAServiceDataHolder.getInstance().getAuth())); } // apply provider implementation parameters for (Map.Entry<String, String> implParameter : implParameters.entrySet()) { String[] classAndParameter = implParameter.getKey().split("/", 2); if (classAndParameter.length == 2) { try { if (log.isDebugEnabled()) { log.debug("Setting " + classAndParameter[0] + ", " + classAndParameter[1] + "=" + implParameter.getValue()); } SecurityFilterProvider provider = IWAServiceDataHolder.getInstance().getProviders().getByClassName( classAndParameter[0]); provider.initParameter(classAndParameter[1], implParameter.getValue()); } catch (ClassNotFoundException e) { throw new ServletException("Invalid class: " + classAndParameter[0] + " in " + implParameter .getKey(), e); } } else { throw new ServletException("Invalid parameter: " + implParameter.getKey()); } } } }