001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.servlet.filters.sso.ntlm;
016    
017    import com.liferay.portal.kernel.cache.PortalCache;
018    import com.liferay.portal.kernel.cache.SingleVMPoolUtil;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.servlet.BrowserSnifferUtil;
023    import com.liferay.portal.kernel.servlet.HttpHeaders;
024    import com.liferay.portal.kernel.util.GetterUtil;
025    import com.liferay.portal.kernel.util.PropsKeys;
026    import com.liferay.portal.kernel.util.Validator;
027    import com.liferay.portal.security.auth.AuthSettingsUtil;
028    import com.liferay.portal.security.ntlm.NtlmManager;
029    import com.liferay.portal.security.ntlm.NtlmUserAccount;
030    import com.liferay.portal.servlet.filters.BasePortalFilter;
031    import com.liferay.portal.util.PortalInstances;
032    import com.liferay.portal.util.PrefsPropsUtil;
033    import com.liferay.portal.util.PropsUtil;
034    import com.liferay.portal.util.PropsValues;
035    import com.liferay.portal.util.WebKeys;
036    
037    import java.security.SecureRandom;
038    
039    import java.util.Iterator;
040    import java.util.Map;
041    import java.util.Properties;
042    import java.util.concurrent.ConcurrentHashMap;
043    
044    import javax.servlet.FilterChain;
045    import javax.servlet.FilterConfig;
046    import javax.servlet.http.HttpServletRequest;
047    import javax.servlet.http.HttpServletResponse;
048    import javax.servlet.http.HttpSession;
049    
050    import jcifs.Config;
051    
052    import jcifs.util.Base64;
053    
054    /**
055     * @author Bruno Farache
056     * @author Marcus Schmidke
057     * @author Brian Wing Shun Chan
058     * @author Wesley Gong
059     * @author Marcellus Tavares
060     * @author Michael C. Han
061     */
062    public class NtlmFilter extends BasePortalFilter {
063    
064            @Override
065            public void init(FilterConfig filterConfig) {
066                    super.init(filterConfig);
067    
068                    try {
069                            Properties properties = PropsUtil.getProperties("jcifs.", false);
070    
071                            Iterator<Map.Entry<Object, Object>> itr =
072                                    properties.entrySet().iterator();
073    
074                            while (itr.hasNext()) {
075                                    Map.Entry<Object, Object> entry = itr.next();
076    
077                                    String key = (String)entry.getKey();
078                                    String value = (String)entry.getValue();
079    
080                                    Config.setProperty(key, value);
081                            }
082                    }
083                    catch (Exception e) {
084                            _log.error(e, e);
085                    }
086            }
087    
088            @Override
089            public boolean isFilterEnabled(
090                    HttpServletRequest request, HttpServletResponse response) {
091    
092                    try {
093                            long companyId = PortalInstances.getCompanyId(request);
094    
095                            if (BrowserSnifferUtil.isIe(request) &&
096                                    AuthSettingsUtil.isNtlmEnabled(companyId)) {
097    
098                                    return true;
099                            }
100                    }
101                    catch (Exception e) {
102                            _log.error(e, e);
103                    }
104    
105                    return false;
106            }
107    
108            @Override
109            protected Log getLog() {
110                    return _log;
111            }
112    
113            protected NtlmManager getNtlmManager(long companyId)
114                    throws SystemException {
115    
116                    String domain = PrefsPropsUtil.getString(
117                            companyId, PropsKeys.NTLM_DOMAIN, PropsValues.NTLM_DOMAIN);
118                    String domainController = PrefsPropsUtil.getString(
119                            companyId, PropsKeys.NTLM_DOMAIN_CONTROLLER,
120                            PropsValues.NTLM_DOMAIN_CONTROLLER);
121                    String domainControllerName = PrefsPropsUtil.getString(
122                            companyId, PropsKeys.NTLM_DOMAIN_CONTROLLER_NAME,
123                            PropsValues.NTLM_DOMAIN_CONTROLLER_NAME);
124                    String serviceAccount = PrefsPropsUtil.getString(
125                            companyId, PropsKeys.NTLM_SERVICE_ACCOUNT,
126                            PropsValues.NTLM_SERVICE_ACCOUNT);
127                    String servicePassword = PrefsPropsUtil.getString(
128                            companyId, PropsKeys.NTLM_SERVICE_PASSWORD,
129                            PropsValues.NTLM_SERVICE_PASSWORD);
130    
131                    NtlmManager ntlmManager = _ntlmManagers.get(companyId);
132    
133                    if (ntlmManager == null) {
134                            ntlmManager = new NtlmManager(
135                                    domain, domainController, domainControllerName, serviceAccount,
136                                    servicePassword);
137    
138                            _ntlmManagers.put(companyId, ntlmManager);
139                    }
140                    else {
141                            if (!Validator.equals(ntlmManager.getDomain(), domain) ||
142                                    !Validator.equals(
143                                            ntlmManager.getDomainController(), domainController) ||
144                                    !Validator.equals(
145                                            ntlmManager.getDomainControllerName(),
146                                            domainControllerName) ||
147                                    !Validator.equals(
148                                            ntlmManager.getServiceAccount(), serviceAccount) ||
149                                    !Validator.equals(
150                                            ntlmManager.getServicePassword(), servicePassword)) {
151    
152                                    ntlmManager.setConfiguration(
153                                            domain, domainController, domainControllerName,
154                                            serviceAccount, servicePassword);
155                            }
156                    }
157    
158                    return ntlmManager;
159            }
160    
161            @Override
162            protected void processFilter(
163                            HttpServletRequest request, HttpServletResponse response,
164                            FilterChain filterChain)
165                    throws Exception {
166    
167                    // Type 1 NTLM requests from browser can (and should) always immediately
168                    // be replied to with an Type 2 NTLM response, no matter whether we're
169                    // yet logging in or whether it is much later in the session.
170    
171                    HttpSession session = request.getSession(false);
172    
173                    long companyId = PortalInstances.getCompanyId(request);
174    
175                    String authorization = GetterUtil.getString(
176                            request.getHeader(HttpHeaders.AUTHORIZATION));
177    
178                    if (authorization.startsWith("NTLM")) {
179                            NtlmManager ntlmManager = getNtlmManager(companyId);
180    
181                            byte[] src = Base64.decode(authorization.substring(5));
182    
183                            if (src[8] == 1) {
184                                    byte[] serverChallenge = new byte[8];
185    
186                                    _secureRandom.nextBytes(serverChallenge);
187    
188                                    byte[] challengeMessage = ntlmManager.negotiate(
189                                            src, serverChallenge);
190    
191                                    authorization = Base64.encode(challengeMessage);
192    
193                                    response.setContentLength(0);
194                                    response.setHeader(
195                                            HttpHeaders.WWW_AUTHENTICATE, "NTLM " + authorization);
196                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
197    
198                                    response.flushBuffer();
199    
200                                    _portalCache.put(request.getRemoteAddr(), serverChallenge);
201    
202                                    // Interrupt filter chain, send response. Browser will
203                                    // immediately post a new request.
204    
205                                    return;
206                            }
207    
208                            byte[] serverChallenge = (byte[])_portalCache.get(
209                                    request.getRemoteAddr());
210    
211                            if (serverChallenge == null) {
212                                    response.setContentLength(0);
213                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
214                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
215    
216                                    response.flushBuffer();
217    
218                                    return;
219                            }
220    
221                            NtlmUserAccount ntlmUserAccount = null;
222    
223                            try {
224                                    ntlmUserAccount = ntlmManager.authenticate(
225                                            src, serverChallenge);
226                            }
227                            catch (Exception e) {
228                                    if (_log.isErrorEnabled()) {
229                                            _log.error("Unable to perform NTLM authentication", e);
230                                    }
231                            }
232                            finally {
233                                    _portalCache.remove(request.getRemoteAddr());
234                            }
235    
236                            if (ntlmUserAccount == null) {
237                                    response.setContentLength(0);
238                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
239                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
240    
241                                    response.flushBuffer();
242    
243                                    return;
244                            }
245    
246                            if (_log.isDebugEnabled()) {
247                                    _log.debug("NTLM remote user " + ntlmUserAccount.getUserName());
248                            }
249    
250                            request.setAttribute(
251                                    WebKeys.NTLM_REMOTE_USER, ntlmUserAccount.getUserName());
252    
253                            if (session != null) {
254                                    session.setAttribute(
255                                            WebKeys.NTLM_USER_ACCOUNT, ntlmUserAccount);
256                            }
257                    }
258    
259                    String path = request.getPathInfo();
260    
261                    if ((path != null) && path.endsWith("/login")) {
262                            NtlmUserAccount ntlmUserAccount = null;
263    
264                            if (session != null) {
265                                    ntlmUserAccount = (NtlmUserAccount)session.getAttribute(
266                                            WebKeys.NTLM_USER_ACCOUNT);
267                            }
268    
269                            if (ntlmUserAccount == null) {
270                                    response.setContentLength(0);
271                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
272                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
273    
274                                    response.flushBuffer();
275    
276                                    return;
277                            }
278                    }
279    
280                    processFilter(NtlmPostFilter.class, request, response, filterChain);
281            }
282    
283            private static Log _log = LogFactoryUtil.getLog(NtlmFilter.class);
284    
285            private Map<Long, NtlmManager> _ntlmManagers =
286                    new ConcurrentHashMap<Long, NtlmManager>();
287            private PortalCache _portalCache = SingleVMPoolUtil.getCache(
288                    NtlmFilter.class.getName());
289            private SecureRandom _secureRandom = new SecureRandom();
290    
291    }