001    /**
002     * Copyright (c) 2000-2010 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.HttpHeaders;
023    import com.liferay.portal.kernel.util.GetterUtil;
024    import com.liferay.portal.kernel.util.PropsKeys;
025    import com.liferay.portal.kernel.util.Validator;
026    import com.liferay.portal.security.ldap.LDAPSettingsUtil;
027    import com.liferay.portal.security.ntlm.NtlmManager;
028    import com.liferay.portal.security.ntlm.NtlmUserAccount;
029    import com.liferay.portal.servlet.filters.BasePortalFilter;
030    import com.liferay.portal.util.PortalInstances;
031    import com.liferay.portal.util.PrefsPropsUtil;
032    import com.liferay.portal.util.PropsUtil;
033    import com.liferay.portal.util.PropsValues;
034    import com.liferay.portal.util.WebKeys;
035    
036    import java.security.SecureRandom;
037    
038    import java.util.Iterator;
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            public void init(FilterConfig filterConfig) {
066                    try {
067                            NtlmHttpFilter ntlmFilter = new NtlmHttpFilter();
068    
069                            ntlmFilter.init(filterConfig);
070    
071                            Properties properties = PropsUtil.getProperties("jcifs.", false);
072    
073                            Iterator<Map.Entry<Object, Object>> itr =
074                                    properties.entrySet().iterator();
075    
076                            while (itr.hasNext()) {
077                                    Map.Entry<Object, Object> entry = itr.next();
078    
079                                    String key = (String)entry.getKey();
080                                    String value = (String)entry.getValue();
081    
082                                    Config.setProperty(key, value);
083                            }
084                    }
085                    catch (Exception e) {
086                            _log.error(e, e);
087                    }
088            }
089    
090            protected Log getLog() {
091                    return _log;
092            }
093    
094            protected NtlmManager getNtlmManager(long companyId)
095                    throws SystemException {
096    
097                    String domain = PrefsPropsUtil.getString(
098                            companyId, PropsKeys.NTLM_DOMAIN, PropsValues.NTLM_DOMAIN);
099                    String domainController =  PrefsPropsUtil.getString(
100                            companyId, PropsKeys.NTLM_DOMAIN_CONTROLLER,
101                            PropsValues.NTLM_DOMAIN_CONTROLLER);
102                    String domainControllerName =  PrefsPropsUtil.getString(
103                            companyId, PropsKeys.NTLM_DOMAIN_CONTROLLER_NAME,
104                            PropsValues.NTLM_DOMAIN_CONTROLLER_NAME);
105                    String serviceAccount =  PrefsPropsUtil.getString(
106                            companyId, PropsKeys.NTLM_SERVICE_ACCOUNT,
107                            PropsValues.NTLM_SERVICE_ACCOUNT);
108                    String servicePassword =  PrefsPropsUtil.getString(
109                            companyId, PropsKeys.NTLM_SERVICE_PASSWORD,
110                            PropsValues.NTLM_SERVICE_PASSWORD);
111    
112                    NtlmManager ntlmManager = _ntlmManagers.get(companyId);
113    
114                    if (ntlmManager == null) {
115                            ntlmManager = new NtlmManager(
116                                    domain, domainController, domainControllerName, serviceAccount,
117                                    servicePassword);
118    
119                            _ntlmManagers.put(companyId, ntlmManager);
120                    }
121                    else {
122                            if (!Validator.equals(ntlmManager.getDomain(), domain) ||
123                                    !Validator.equals(
124                                            ntlmManager.getDomainController(), domainController) ||
125                                    !Validator.equals(
126                                            ntlmManager.getDomainControllerName(),
127                                            domainControllerName) ||
128                                    !Validator.equals(
129                                            ntlmManager.getServiceAccount(), serviceAccount) ||
130                                    !Validator.equals(
131                                             ntlmManager.getServicePassword(), servicePassword)) {
132    
133                                    ntlmManager.setConfiguration(
134                                            domain, domainController, domainControllerName,
135                                            serviceAccount, servicePassword);
136                            }
137                    }
138    
139                    return ntlmManager;
140            }
141    
142            protected void processFilter(
143                            HttpServletRequest request, HttpServletResponse response,
144                            FilterChain filterChain)
145                    throws Exception {
146    
147                    long companyId = PortalInstances.getCompanyId(request);
148    
149                    if (LDAPSettingsUtil.isNtlmEnabled(companyId)) {
150    
151                            // Type 1 NTLM requests from browser can (and should) always
152                            // immediately be replied to with an Type 2 NTLM response, no
153                            // matter whether we're yet logging in or whether it is much
154                            // later in the session.
155    
156                            HttpSession session = request.getSession(false);
157    
158                            String authorization = GetterUtil.getString(
159                                    request.getHeader(HttpHeaders.AUTHORIZATION));
160    
161                            if (authorization.startsWith("NTLM")) {
162                                    NtlmManager ntlmManager = getNtlmManager(companyId);
163    
164                                    byte[] src = Base64.decode(authorization.substring(5));
165    
166                                    if (src[8] == 1) {
167                                            byte[] serverChallenge = new byte[8];
168    
169                                            _secureRandom.nextBytes(serverChallenge);
170    
171                                            byte[] challengeMessage = ntlmManager.negotiate(
172                                                    src, serverChallenge);
173    
174                                            authorization = Base64.encode(challengeMessage);
175    
176                                            response.setHeader(
177                                                    HttpHeaders.WWW_AUTHENTICATE, "NTLM " + authorization);
178                                            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
179                                            response.setContentLength(0);
180    
181                                            response.flushBuffer();
182    
183                                            _serverChallenges.put(
184                                                    request.getRemoteAddr(), serverChallenge);
185    
186                                            // Interrupt filter chain, send response. Browser will
187                                            // immediately post a new request.
188    
189                                            return;
190                                    }
191                                    else {
192                                            byte[] serverChallenge = (byte[])_serverChallenges.get(
193                                                    request.getRemoteAddr());
194    
195                                            if (serverChallenge == null) {
196                                                    return;
197                                            }
198    
199                                            NtlmUserAccount ntlmUserAccount = null;
200    
201                                            try {
202                                                    ntlmUserAccount = ntlmManager.authenticate(
203                                                            src, serverChallenge);
204                                            }
205                                            catch (Exception e) {
206                                                    if (_log.isErrorEnabled()) {
207                                                            _log.error(
208                                                                    "Unable to perform NTLM authentication", e);
209                                                    }
210                                            }
211                                            finally {
212                                                    _serverChallenges.remove(request.getRemoteAddr());
213                                            }
214    
215                                            if (ntlmUserAccount == null) {
216                                                    return;
217                                            }
218    
219                                            if (_log.isDebugEnabled()) {
220                                                    _log.debug(
221                                                            "NTLM remote user " +
222                                                                    ntlmUserAccount.getUserName());
223                                            }
224    
225                                            request.setAttribute(
226                                                    WebKeys.NTLM_REMOTE_USER,
227                                                    ntlmUserAccount.getUserName());
228    
229                                            if (session != null) {
230                                                    session.setAttribute(
231                                                            WebKeys.NTLM_USER_ACCOUNT, ntlmUserAccount);
232                                            }
233                                    }
234                            }
235    
236                            String path = request.getPathInfo();
237    
238                            if ((path != null) && path.endsWith("/login")) {
239                                    NtlmUserAccount ntlmUserAccount = null;
240    
241                                    if (session != null) {
242                                            ntlmUserAccount = (NtlmUserAccount)session.getAttribute(
243                                                    WebKeys.NTLM_USER_ACCOUNT);
244                                    }
245    
246                                    if (ntlmUserAccount == null) {
247                                            response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
248                                            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
249                                            response.setContentLength(0);
250    
251                                            response.flushBuffer();
252    
253                                            return;
254                                    }
255                            }
256                    }
257    
258                    processFilter(NtlmPostFilter.class, request, response, filterChain);
259            }
260    
261            private static Log _log = LogFactoryUtil.getLog(NtlmFilter.class);
262    
263            private Map<Long, NtlmManager> _ntlmManagers =
264                    new ConcurrentHashMap<Long, NtlmManager>();
265            private SecureRandom _secureRandom = new SecureRandom();
266            private PortalCache _serverChallenges = SingleVMPoolUtil.getCache(
267                    NtlmFilter.class.getName());
268    
269    }