001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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                                                    response.setHeader(
197                                                            HttpHeaders.WWW_AUTHENTICATE, "NTLM");
198                                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
199                                                    response.setContentLength(0);
200    
201                                                    response.flushBuffer();
202    
203                                                    return;
204                                            }
205    
206                                            NtlmUserAccount ntlmUserAccount = null;
207    
208                                            try {
209                                                    ntlmUserAccount = ntlmManager.authenticate(
210                                                            src, serverChallenge);
211                                            }
212                                            catch (Exception e) {
213                                                    if (_log.isErrorEnabled()) {
214                                                            _log.error(
215                                                                    "Unable to perform NTLM authentication", e);
216                                                    }
217                                            }
218                                            finally {
219                                                    _serverChallenges.remove(request.getRemoteAddr());
220                                            }
221    
222                                            if (ntlmUserAccount == null) {
223                                                    response.setHeader(
224                                                            HttpHeaders.WWW_AUTHENTICATE, "NTLM");
225                                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
226                                                    response.setContentLength(0);
227    
228                                                    response.flushBuffer();
229    
230                                                    return;
231                                            }
232    
233                                            if (_log.isDebugEnabled()) {
234                                                    _log.debug(
235                                                            "NTLM remote user " +
236                                                                    ntlmUserAccount.getUserName());
237                                            }
238    
239                                            request.setAttribute(
240                                                    WebKeys.NTLM_REMOTE_USER,
241                                                    ntlmUserAccount.getUserName());
242    
243                                            if (session != null) {
244                                                    session.setAttribute(
245                                                            WebKeys.NTLM_USER_ACCOUNT, ntlmUserAccount);
246                                            }
247                                    }
248                            }
249    
250                            String path = request.getPathInfo();
251    
252                            if ((path != null) && path.endsWith("/login")) {
253                                    NtlmUserAccount ntlmUserAccount = null;
254    
255                                    if (session != null) {
256                                            ntlmUserAccount = (NtlmUserAccount)session.getAttribute(
257                                                    WebKeys.NTLM_USER_ACCOUNT);
258                                    }
259    
260                                    if (ntlmUserAccount == null) {
261                                            response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
262                                            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
263                                            response.setContentLength(0);
264    
265                                            response.flushBuffer();
266    
267                                            return;
268                                    }
269                            }
270                    }
271    
272                    processFilter(NtlmPostFilter.class, request, response, filterChain);
273            }
274    
275            private static Log _log = LogFactoryUtil.getLog(NtlmFilter.class);
276    
277            private Map<Long, NtlmManager> _ntlmManagers =
278                    new ConcurrentHashMap<Long, NtlmManager>();
279            private SecureRandom _secureRandom = new SecureRandom();
280            private PortalCache _serverChallenges = SingleVMPoolUtil.getCache(
281                    NtlmFilter.class.getName());
282    
283    }