001    /**
002     * Copyright (c) 2000-2012 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.Map;
040    import java.util.Properties;
041    import java.util.concurrent.ConcurrentHashMap;
042    
043    import javax.servlet.FilterChain;
044    import javax.servlet.FilterConfig;
045    import javax.servlet.http.HttpServletRequest;
046    import javax.servlet.http.HttpServletResponse;
047    import javax.servlet.http.HttpSession;
048    
049    import jcifs.Config;
050    
051    import jcifs.http.NtlmHttpFilter;
052    
053    import jcifs.util.Base64;
054    
055    /**
056     * @author Bruno Farache
057     * @author Marcus Schmidke
058     * @author Brian Wing Shun Chan
059     * @author Wesley Gong
060     * @author Marcellus Tavares
061     * @author Michael C. Han
062     */
063    public class NtlmFilter extends BasePortalFilter {
064    
065            @Override
066            public void init(FilterConfig filterConfig) {
067                    super.init(filterConfig);
068    
069                    try {
070                            NtlmHttpFilter ntlmFilter = new NtlmHttpFilter();
071    
072                            ntlmFilter.init(filterConfig);
073    
074                            Properties properties = PropsUtil.getProperties("jcifs.", false);
075    
076                            for (Map.Entry<Object, Object> entry : properties.entrySet()) {
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 = _portalCache.get(request.getRemoteAddr());
209    
210                            if (serverChallenge == null) {
211                                    response.setContentLength(0);
212                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
213                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
214    
215                                    response.flushBuffer();
216    
217                                    return;
218                            }
219    
220                            NtlmUserAccount ntlmUserAccount = null;
221    
222                            try {
223                                    ntlmUserAccount = ntlmManager.authenticate(
224                                            src, serverChallenge);
225                            }
226                            catch (Exception e) {
227                                    if (_log.isErrorEnabled()) {
228                                            _log.error("Unable to perform NTLM authentication", e);
229                                    }
230                            }
231                            finally {
232                                    _portalCache.remove(request.getRemoteAddr());
233                            }
234    
235                            if (ntlmUserAccount == null) {
236                                    response.setContentLength(0);
237                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
238                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
239    
240                                    response.flushBuffer();
241    
242                                    return;
243                            }
244    
245                            if (_log.isDebugEnabled()) {
246                                    _log.debug("NTLM remote user " + ntlmUserAccount.getUserName());
247                            }
248    
249                            request.setAttribute(
250                                    WebKeys.NTLM_REMOTE_USER, ntlmUserAccount.getUserName());
251    
252                            if (session != null) {
253                                    session.setAttribute(
254                                            WebKeys.NTLM_USER_ACCOUNT, ntlmUserAccount);
255                            }
256                    }
257    
258                    String path = request.getPathInfo();
259    
260                    if ((path != null) && path.endsWith("/login")) {
261                            NtlmUserAccount ntlmUserAccount = null;
262    
263                            if (session != null) {
264                                    ntlmUserAccount = (NtlmUserAccount)session.getAttribute(
265                                            WebKeys.NTLM_USER_ACCOUNT);
266                            }
267    
268                            if (ntlmUserAccount == null) {
269                                    response.setContentLength(0);
270                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
271                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
272    
273                                    response.flushBuffer();
274    
275                                    return;
276                            }
277                    }
278    
279                    processFilter(NtlmPostFilter.class, request, response, filterChain);
280            }
281    
282            private static Log _log = LogFactoryUtil.getLog(NtlmFilter.class);
283    
284            private Map<Long, NtlmManager> _ntlmManagers =
285                    new ConcurrentHashMap<Long, NtlmManager>();
286            private PortalCache<String, byte[]> _portalCache =
287                    SingleVMPoolUtil.getCache(NtlmFilter.class.getName());
288            private SecureRandom _secureRandom = new SecureRandom();
289    
290    }