/* * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed 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. * * Contributors: * Nuxeo - initial API and implementation * * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $ */ package org.nuxeo.ecm.platform.ui.web.auth.ntlm; import java.io.IOException; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import jcifs.Config; import jcifs.UniAddress; import jcifs.http.NtlmSsp; import jcifs.smb.NtlmChallenge; import jcifs.smb.NtlmPasswordAuthentication; import jcifs.smb.SmbAuthException; import jcifs.smb.SmbSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo; import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin; import static jcifs.smb.NtStatus.NT_STATUS_ACCESS_VIOLATION; public class NTLMAuthenticator implements NuxeoAuthenticationPlugin { private static final String JCIFS_PREFIX = "jcifs."; public static final String JCIFS_NETBIOS_CACHE_POLICY = "jcifs.netbios.cachePolicy"; public static final String JCIFS_SMB_CLIENT_SO_TIMEOUT = "jcifs.smb.client.soTimeout"; public static final String JCIFS_HTTP_LOAD_BALANCE = "jcifs.http.loadBalance"; public static final String JCIFS_HTTP_DOMAIN_CONTROLLER = "jcifs.http.domainController"; public static final String JCIFS_SMB_CLIENT_DOMAIN = "jcifs.smb.client.domain"; public static final boolean FORCE_SESSION_CREATION = true; public static final String NTLM_HTTP_AUTH_SESSION_KEY = "NtlmHttpAuth"; public static final String NTLM_HTTP_CHAL_SESSION_KEY = "NtlmHttpChal"; protected static String defaultDomain; protected static String domainController; protected static boolean loadBalance; private static final Log log = LogFactory.getLog(NTLMAuthenticator.class); public List<String> getUnAuthenticatedURLPrefix() { return null; } public Boolean handleLoginPrompt(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String baseURL) { log.debug("Handle NTLM login prompt"); NtlmPasswordAuthentication ntlm = null; HttpSession httpSession = httpRequest.getSession(FORCE_SESSION_CREATION); if (httpSession != null) { ntlm = (NtlmPasswordAuthentication) httpSession.getAttribute(NTLM_HTTP_AUTH_SESSION_KEY); } if (httpSession == null || ntlm == null) { log.debug("Sending NTLM Challenge/Response request to browser"); httpResponse.setHeader("WWW-Authenticate", "NTLM"); httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.setContentLength(0); try { httpResponse.flushBuffer(); } catch (IOException e) { log.error("Error while flushing buffer:" + e.getMessage()); e.printStackTrace(); } return true; } else { log.debug("No NTLM Prompt done since NTLM Auth was found :" + ntlm.getUsername()); return false; } } public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest httpRequest, HttpServletResponse httpResponse) { log.debug("NTML handleRetrieveIdentity"); NtlmPasswordAuthentication ntlm; try { ntlm = negotiate(httpRequest, httpResponse, false); } catch (IOException | ServletException e) { log.error("NTLM negotiation failed : " + e.getMessage(), e); return null; } if (ntlm == null) { log.debug("Negotiation returned a null NTLM token"); return null; } else { log.debug("Negotiation succeed and returned a NTLM token, creating UserIdentificationInfo"); String userName = ntlm.getUsername(); log.debug("ntlm.getUsername() = " + userName); if (userName.startsWith(ntlm.getDomain())) { userName = userName.replace(ntlm.getDomain() + "/", ""); } log.debug("userName = " + userName); String password = ntlm.getPassword(); if (password == null || "".equals(password)) { // we don't get the NTLM password, so we have to trust NTLM auth UserIdentificationInfo userInfo = new UserIdentificationInfo(ntlm.getUsername(), "ITrustNTLM"); userInfo.setLoginPluginName("Trusting_LM"); return userInfo; } else { return new UserIdentificationInfo(ntlm.getUsername(), ntlm.getPassword()); } } } public void initPlugin(Map<String, String> parameters) { Config.setProperty(JCIFS_SMB_CLIENT_SO_TIMEOUT, "300000"); Config.setProperty(JCIFS_NETBIOS_CACHE_POLICY, "1200"); // init CIFS from parameters for (String name : parameters.keySet()) { if (name.startsWith(JCIFS_PREFIX)) { Config.setProperty(name, parameters.get(name)); } } // get params from CIFS config defaultDomain = Config.getProperty(JCIFS_SMB_CLIENT_DOMAIN); domainController = Config.getProperty(JCIFS_HTTP_DOMAIN_CONTROLLER); if (domainController == null) { domainController = defaultDomain; loadBalance = Config.getBoolean(JCIFS_HTTP_LOAD_BALANCE, true); } } public Boolean needLoginPrompt(HttpServletRequest httpRequest) { String useragent = httpRequest.getHeader("User-Agent").toLowerCase(); // only prompt on windows platform if (!useragent.contains("windows")) { log.debug("No NTLM LoginPrompt : User does not use Win32"); return false; } log.debug("NTLM LoginPrompt Needed"); return true; } public static NtlmPasswordAuthentication negotiate(HttpServletRequest req, HttpServletResponse resp, boolean skipAuthentication) throws IOException, ServletException { log.debug("NTLM negotiation starts"); String msg = req.getHeader("Authorization"); log.debug("NTLM negotiation header = " + msg); NtlmPasswordAuthentication ntlm; if (msg != null && msg.startsWith("NTLM ")) { HttpSession ssn = req.getSession(); byte[] challenge; UniAddress dc; if (loadBalance) { NtlmChallenge chal = (NtlmChallenge) ssn.getAttribute(NTLM_HTTP_CHAL_SESSION_KEY); if (chal == null) { chal = SmbSession.getChallengeForDomain(); ssn.setAttribute(NTLM_HTTP_CHAL_SESSION_KEY, chal); } dc = chal.dc; challenge = chal.challenge; } else { dc = UniAddress.getByName(domainController, true); dc = UniAddress.getByName(dc.getHostAddress(), true); challenge = SmbSession.getChallenge(dc); } ntlm = NtlmSsp.authenticate(req, resp, challenge); if (ntlm == null) { log.debug("NtlmSsp.authenticate returned null"); return null; } log.debug("NtlmSsp.authenticate succeed"); log.debug("Domain controller is " + dc.getHostName()); if (ntlm.getDomain() != null) { log.debug("NtlmSsp.authenticate => domain = " + ntlm.getDomain()); } else { log.debug("NtlmSsp.authenticate => null domain"); } if (ntlm.getUsername() != null) { log.debug("NtlmSsp.authenticate => userName = " + ntlm.getUsername()); } else { log.debug("NtlmSsp.authenticate => userName = null"); } if (ntlm.getPassword() != null) { log.debug("NtlmSsp.authenticate => password = " + ntlm.getPassword()); } else { log.debug("NtlmSsp.authenticate => password = null"); } /* negotiation complete, remove the challenge object */ ssn.removeAttribute(NTLM_HTTP_CHAL_SESSION_KEY); if (!skipAuthentication) { try { log.debug("Trying to logon NTLM session on dc " + dc.toString()); SmbSession.logon(dc, ntlm); log.debug(ntlm + " successfully authenticated against " + dc); } catch (SmbAuthException sae) { log.error(ntlm.getName() + ": 0x" + jcifs.util.Hexdump.toHexString(sae.getNtStatus(), 8) + ": " + sae); if (sae.getNtStatus() == NT_STATUS_ACCESS_VIOLATION) { /* * Server challenge no longer valid for externally supplied password hashes. */ ssn = req.getSession(false); if (ssn != null) { ssn.removeAttribute(NTLM_HTTP_AUTH_SESSION_KEY); } } resp.setHeader("WWW-Authenticate", "NTLM"); resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); resp.setContentLength(0); resp.flushBuffer(); return null; } req.getSession().setAttribute(NTLM_HTTP_AUTH_SESSION_KEY, ntlm); } } else { log.debug("NTLM negotiation header is null"); return null; } return ntlm; } }