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.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.http.NtlmHttpFilter;
053    
054    import jcifs.util.Base64;
055    
056    /**
057     * @author Bruno Farache
058     * @author Marcus Schmidke
059     * @author Brian Wing Shun Chan
060     * @author Wesley Gong
061     * @author Marcellus Tavares
062     * @author Michael C. Han
063     */
064    public class NtlmFilter extends BasePortalFilter {
065    
066            @Override
067            public void init(FilterConfig filterConfig) {
068                    try {
069                            NtlmHttpFilter ntlmFilter = new NtlmHttpFilter();
070    
071                            ntlmFilter.init(filterConfig);
072    
073                            Properties properties = PropsUtil.getProperties("jcifs.", false);
074    
075                            Iterator<Map.Entry<Object, Object>> itr =
076                                    properties.entrySet().iterator();
077    
078                            while (itr.hasNext()) {
079                                    Map.Entry<Object, Object> entry = itr.next();
080    
081                                    String key = (String)entry.getKey();
082                                    String value = (String)entry.getValue();
083    
084                                    Config.setProperty(key, value);
085                            }
086                    }
087                    catch (Exception e) {
088                            _log.error(e, e);
089                    }
090            }
091    
092            @Override
093            public boolean isFilterEnabled(
094                    HttpServletRequest request, HttpServletResponse response) {
095    
096                    try {
097                            long companyId = PortalInstances.getCompanyId(request);
098    
099                            if (BrowserSnifferUtil.isIe(request) &&
100                                    AuthSettingsUtil.isNtlmEnabled(companyId)) {
101    
102                                    return true;
103                            }
104                    }
105                    catch (Exception e) {
106                            _log.error(e, e);
107                    }
108    
109                    return false;
110            }
111    
112            @Override
113            protected Log getLog() {
114                    return _log;
115            }
116    
117            protected NtlmManager getNtlmManager(long companyId)
118                    throws SystemException {
119    
120                    String domain = PrefsPropsUtil.getString(
121                            companyId, PropsKeys.NTLM_DOMAIN, PropsValues.NTLM_DOMAIN);
122                    String domainController = PrefsPropsUtil.getString(
123                            companyId, PropsKeys.NTLM_DOMAIN_CONTROLLER,
124                            PropsValues.NTLM_DOMAIN_CONTROLLER);
125                    String domainControllerName = PrefsPropsUtil.getString(
126                            companyId, PropsKeys.NTLM_DOMAIN_CONTROLLER_NAME,
127                            PropsValues.NTLM_DOMAIN_CONTROLLER_NAME);
128                    String serviceAccount = PrefsPropsUtil.getString(
129                            companyId, PropsKeys.NTLM_SERVICE_ACCOUNT,
130                            PropsValues.NTLM_SERVICE_ACCOUNT);
131                    String servicePassword = PrefsPropsUtil.getString(
132                            companyId, PropsKeys.NTLM_SERVICE_PASSWORD,
133                            PropsValues.NTLM_SERVICE_PASSWORD);
134    
135                    NtlmManager ntlmManager = _ntlmManagers.get(companyId);
136    
137                    if (ntlmManager == null) {
138                            ntlmManager = new NtlmManager(
139                                    domain, domainController, domainControllerName, serviceAccount,
140                                    servicePassword);
141    
142                            _ntlmManagers.put(companyId, ntlmManager);
143                    }
144                    else {
145                            if (!Validator.equals(ntlmManager.getDomain(), domain) ||
146                                    !Validator.equals(
147                                            ntlmManager.getDomainController(), domainController) ||
148                                    !Validator.equals(
149                                            ntlmManager.getDomainControllerName(),
150                                            domainControllerName) ||
151                                    !Validator.equals(
152                                            ntlmManager.getServiceAccount(), serviceAccount) ||
153                                    !Validator.equals(
154                                             ntlmManager.getServicePassword(), servicePassword)) {
155    
156                                    ntlmManager.setConfiguration(
157                                            domain, domainController, domainControllerName,
158                                            serviceAccount, servicePassword);
159                            }
160                    }
161    
162                    return ntlmManager;
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                            byte[] src = Base64.decode(authorization.substring(5));
186    
187                            if (src[8] == 1) {
188                                    byte[] serverChallenge = new byte[8];
189    
190                                    _secureRandom.nextBytes(serverChallenge);
191    
192                                    byte[] challengeMessage = ntlmManager.negotiate(
193                                            src, serverChallenge);
194    
195                                    authorization = Base64.encode(challengeMessage);
196    
197                                    response.setContentLength(0);
198                                    response.setHeader(
199                                            HttpHeaders.WWW_AUTHENTICATE, "NTLM " + authorization);
200                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
201    
202                                    response.flushBuffer();
203    
204                                    _portalCache.put(request.getRemoteAddr(), serverChallenge);
205    
206                                    // Interrupt filter chain, send response. Browser will
207                                    // immediately post a new request.
208    
209                                    return;
210                            }
211    
212                            byte[] serverChallenge = (byte[])_portalCache.get(
213                                    request.getRemoteAddr());
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(request.getRemoteAddr());
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                    }
283    
284                    processFilter(NtlmPostFilter.class, request, response, filterChain);
285            }
286    
287            private static Log _log = LogFactoryUtil.getLog(NtlmFilter.class);
288    
289            private Map<Long, NtlmManager> _ntlmManagers =
290                    new ConcurrentHashMap<Long, NtlmManager>();
291            private PortalCache _portalCache = SingleVMPoolUtil.getCache(
292                    NtlmFilter.class.getName());
293            private SecureRandom _secureRandom = new SecureRandom();
294    
295    }