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.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            @Override
156            protected void processFilter(
157                            HttpServletRequest request, HttpServletResponse response,
158                            FilterChain filterChain)
159                    throws Exception {
160    
161                    // Type 1 NTLM requests from browser can (and should) always immediately
162                    // be replied to with an Type 2 NTLM response, no matter whether we're
163                    // yet logging in or whether it is much later in the session.
164    
165                    HttpSession session = request.getSession(false);
166    
167                    long companyId = PortalInstances.getCompanyId(request);
168    
169                    String authorization = GetterUtil.getString(
170                            request.getHeader(HttpHeaders.AUTHORIZATION));
171    
172                    if (authorization.startsWith("NTLM")) {
173                            NtlmManager ntlmManager = getNtlmManager(companyId);
174    
175                            byte[] src = Base64.decode(authorization.substring(5));
176    
177                            if (src[8] == 1) {
178                                    byte[] serverChallenge = new byte[8];
179    
180                                    _secureRandom.nextBytes(serverChallenge);
181    
182                                    byte[] challengeMessage = ntlmManager.negotiate(
183                                            src, serverChallenge);
184    
185                                    authorization = Base64.encode(challengeMessage);
186    
187                                    response.setContentLength(0);
188                                    response.setHeader(
189                                            HttpHeaders.WWW_AUTHENTICATE, "NTLM " + authorization);
190                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
191    
192                                    response.flushBuffer();
193    
194                                    _portalCache.put(request.getRemoteAddr(), serverChallenge);
195    
196                                    // Interrupt filter chain, send response. Browser will
197                                    // immediately post a new request.
198    
199                                    return;
200                            }
201    
202                            byte[] serverChallenge = _portalCache.get(request.getRemoteAddr());
203    
204                            if (serverChallenge == null) {
205                                    response.setContentLength(0);
206                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
207                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
208    
209                                    response.flushBuffer();
210    
211                                    return;
212                            }
213    
214                            NtlmUserAccount ntlmUserAccount = null;
215    
216                            try {
217                                    ntlmUserAccount = ntlmManager.authenticate(
218                                            src, serverChallenge);
219                            }
220                            catch (Exception e) {
221                                    if (_log.isErrorEnabled()) {
222                                            _log.error("Unable to perform NTLM authentication", e);
223                                    }
224                            }
225                            finally {
226                                    _portalCache.remove(request.getRemoteAddr());
227                            }
228    
229                            if (ntlmUserAccount == null) {
230                                    response.setContentLength(0);
231                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
232                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
233    
234                                    response.flushBuffer();
235    
236                                    return;
237                            }
238    
239                            if (_log.isDebugEnabled()) {
240                                    _log.debug("NTLM remote user " + ntlmUserAccount.getUserName());
241                            }
242    
243                            request.setAttribute(
244                                    WebKeys.NTLM_REMOTE_USER, ntlmUserAccount.getUserName());
245    
246                            if (session != null) {
247                                    session.setAttribute(
248                                            WebKeys.NTLM_USER_ACCOUNT, ntlmUserAccount);
249                            }
250                    }
251    
252                    String path = request.getPathInfo();
253    
254                    if ((path != null) && path.endsWith("/login")) {
255                            NtlmUserAccount ntlmUserAccount = null;
256    
257                            if (session != null) {
258                                    ntlmUserAccount = (NtlmUserAccount)session.getAttribute(
259                                            WebKeys.NTLM_USER_ACCOUNT);
260                            }
261    
262                            if (ntlmUserAccount == null) {
263                                    response.setContentLength(0);
264                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
265                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
266    
267                                    response.flushBuffer();
268    
269                                    return;
270                            }
271                    }
272    
273                    processFilter(NtlmPostFilter.class, request, response, filterChain);
274            }
275    
276            private static Log _log = LogFactoryUtil.getLog(NtlmFilter.class);
277    
278            private Map<Long, NtlmManager> _ntlmManagers =
279                    new ConcurrentHashMap<Long, NtlmManager>();
280            private PortalCache<String, byte[]> _portalCache =
281                    SingleVMPoolUtil.getCache(NtlmFilter.class.getName());
282            private SecureRandom _secureRandom = new SecureRandom();
283    
284    }