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