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