1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.servlet.filters.sso.ntlm;
16  
17  import com.liferay.portal.kernel.log.Log;
18  import com.liferay.portal.kernel.log.LogFactoryUtil;
19  import com.liferay.portal.kernel.servlet.HttpHeaders;
20  import com.liferay.portal.kernel.util.GetterUtil;
21  import com.liferay.portal.kernel.util.PropsKeys;
22  import com.liferay.portal.kernel.util.StringPool;
23  import com.liferay.portal.kernel.util.Validator;
24  import com.liferay.portal.security.ldap.LDAPSettingsUtil;
25  import com.liferay.portal.servlet.filters.BasePortalFilter;
26  import com.liferay.portal.util.PortalInstances;
27  import com.liferay.portal.util.PrefsPropsUtil;
28  import com.liferay.portal.util.PropsUtil;
29  import com.liferay.portal.util.PropsValues;
30  import com.liferay.portal.util.WebKeys;
31  import com.liferay.util.servlet.filters.DynamicFilterConfig;
32  
33  import java.util.Iterator;
34  import java.util.Map;
35  import java.util.Properties;
36  
37  import javax.servlet.FilterChain;
38  import javax.servlet.FilterConfig;
39  import javax.servlet.http.HttpServletRequest;
40  import javax.servlet.http.HttpServletResponse;
41  import javax.servlet.http.HttpSession;
42  
43  import jcifs.Config;
44  import jcifs.UniAddress;
45  
46  import jcifs.http.NtlmHttpFilter;
47  import jcifs.http.NtlmSsp;
48  
49  import jcifs.ntlmssp.Type1Message;
50  import jcifs.ntlmssp.Type2Message;
51  
52  import jcifs.smb.NtlmPasswordAuthentication;
53  import jcifs.smb.SmbSession;
54  
55  import jcifs.util.Base64;
56  
57  /**
58   * <a href="NtlmFilter.java.html"><b><i>View Source</i></b></a>
59   *
60   * @author Bruno Farache
61   * @author Marcus Schmidke
62   * @author Brian Wing Shun Chan
63   * @author Wesley Gong
64   */
65  public class NtlmFilter extends BasePortalFilter {
66  
67      public void init(FilterConfig filterConfig) {
68          try {
69              NtlmHttpFilter ntlmFilter = new NtlmHttpFilter();
70  
71              ntlmFilter.init(filterConfig);
72  
73              Properties properties = PropsUtil.getProperties("jcifs.", false);
74  
75              Iterator<Map.Entry<Object, Object>> itr =
76                  properties.entrySet().iterator();
77  
78              while (itr.hasNext()) {
79                  Map.Entry<Object, Object> entry = itr.next();
80  
81                  String key = (String)entry.getKey();
82                  String value = (String)entry.getValue();
83  
84                  Config.setProperty(key, value);
85              }
86          }
87          catch (Exception e) {
88              _log.error(e, e);
89          }
90  
91          _filterConfig = new DynamicFilterConfig(filterConfig);
92      }
93  
94      protected Log getLog() {
95          return _log;
96      }
97  
98      protected void processFilter(
99              HttpServletRequest request, HttpServletResponse response,
100             FilterChain filterChain)
101         throws Exception {
102 
103         long companyId = PortalInstances.getCompanyId(request);
104 
105         if (LDAPSettingsUtil.isNtlmEnabled(companyId)) {
106             String domainController = _filterConfig.getInitParameter(
107                 "jcifs.http.domainController");
108             String domain = _filterConfig.getInitParameter(
109                 "jcifs.smb.client.domain");
110 
111             String preferencesDomainController = PrefsPropsUtil.getString(
112                 companyId, PropsKeys.NTLM_DOMAIN_CONTROLLER,
113                 PropsValues.NTLM_DOMAIN_CONTROLLER);
114             String preferencesDomain = PrefsPropsUtil.getString(
115                 companyId, PropsKeys.NTLM_DOMAIN, PropsValues.NTLM_DOMAIN);
116 
117             if (!Validator.equals(
118                     domainController, preferencesDomainController) ||
119                 !Validator.equals(domain, preferencesDomain)) {
120 
121                 domainController = preferencesDomainController;
122                 domain = preferencesDomain;
123 
124                 _filterConfig.addInitParameter(
125                     "jcifs.http.domainController", domainController);
126                 _filterConfig.addInitParameter(
127                     "jcifs.smb.client.domain", domain);
128 
129                 super.init(_filterConfig);
130             }
131 
132             if (_log.isDebugEnabled()) {
133                 _log.debug("Host " + domainController);
134                 _log.debug("Domain " + domain);
135             }
136 
137             // Type 1 NTLM requests from browser can (and should) always
138             // immediately be replied to with an Type 2 NTLM response, no
139             // matter whether we're yet logging in or whether it is much
140             // later in the session.
141 
142             String authorization = GetterUtil.getString(
143                 request.getHeader(HttpHeaders.AUTHORIZATION));
144 
145             if (authorization.startsWith("NTLM")) {
146                 byte[] src = Base64.decode(authorization.substring(5));
147 
148                 if (src[8] == 1) {
149                     UniAddress dc = UniAddress.getByName(
150                         domainController, true);
151 
152                     byte[] challenge = SmbSession.getChallenge(dc);
153 
154                     Type1Message type1 = new Type1Message(src);
155                     Type2Message type2 = new Type2Message(
156                         type1, challenge, null);
157 
158                     authorization = Base64.encode(type2.toByteArray());
159 
160                     response.setHeader(
161                         HttpHeaders.WWW_AUTHENTICATE, "NTLM " + authorization);
162                     response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
163                     response.setContentLength(0);
164 
165                     response.flushBuffer();
166 
167                     // Interrupt filter chain, send response. Browser will
168                     // immediately post a new request.
169 
170                     return;
171                 }
172             }
173 
174             String path = request.getPathInfo();
175 
176             if ((path != null) && path.endsWith("/login")) {
177                 NtlmPasswordAuthentication ntlm = negotiate(
178                     request, response, false);
179 
180                 if (ntlm == null) {
181                     return;
182                 }
183 
184                 String remoteUser = ntlm.getName();
185 
186                 int pos = remoteUser.indexOf(StringPool.BACK_SLASH);
187 
188                 if (pos != -1) {
189                     remoteUser = remoteUser.substring(pos + 1);
190                 }
191 
192                 if (_log.isDebugEnabled()) {
193                     _log.debug("NTLM remote user " + remoteUser);
194                 }
195 
196                 request.setAttribute(WebKeys.NTLM_REMOTE_USER, remoteUser);
197             }
198         }
199 
200         processFilter(NtlmPostFilter.class, request, response, filterChain);
201     }
202 
203     protected NtlmPasswordAuthentication negotiate(
204             HttpServletRequest request, HttpServletResponse response,
205             boolean skipAuthentication)
206         throws Exception {
207 
208         NtlmPasswordAuthentication ntlm = null;
209 
210         HttpSession session = request.getSession(false);
211 
212         String authorization = GetterUtil.getString(
213             request.getHeader(HttpHeaders.AUTHORIZATION));
214 
215         if (_log.isDebugEnabled()) {
216             _log.debug("Authorization header " + authorization);
217         }
218 
219         if (authorization.startsWith("NTLM ")) {
220             String domainController = _filterConfig.getInitParameter(
221                 "jcifs.http.domainController");
222 
223             UniAddress uniAddress = UniAddress.getByName(
224                 domainController, true);
225 
226             if (_log.isDebugEnabled()) {
227                 _log.debug("Address " + uniAddress);
228             }
229 
230             byte[] challenge = SmbSession.getChallenge(uniAddress);
231 
232             ntlm = NtlmSsp.authenticate(request, response, challenge);
233 
234             try {
235                 SmbSession.logon(uniAddress, ntlm);
236             }
237             catch (Exception e) {
238                 response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
239                 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
240                 response.setContentLength(0);
241 
242                 response.flushBuffer();
243 
244                 return null;
245             }
246 
247             session.setAttribute("NtlmHttpAuth", ntlm);
248         }
249         else {
250             if (session != null) {
251                 ntlm = (NtlmPasswordAuthentication)session.getAttribute(
252                     "NtlmHttpAuth");
253             }
254 
255             if (ntlm == null) {
256                 response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
257                 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
258                 response.setContentLength(0);
259 
260                 response.flushBuffer();
261 
262                 return null;
263             }
264         }
265 
266         if (_log.isDebugEnabled()) {
267             _log.debug("Password authentication " + ntlm);
268         }
269 
270         return ntlm;
271     }
272 
273     private static Log _log = LogFactoryUtil.getLog(NtlmFilter.class);
274 
275     private DynamicFilterConfig _filterConfig;
276 
277 }