1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.servlet.filters.sso.ntlm;
24  
25  import com.liferay.portal.kernel.log.Log;
26  import com.liferay.portal.kernel.log.LogFactoryUtil;
27  import com.liferay.portal.kernel.servlet.HttpHeaders;
28  import com.liferay.portal.kernel.util.GetterUtil;
29  import com.liferay.portal.kernel.util.StringPool;
30  import com.liferay.portal.kernel.util.Validator;
31  import com.liferay.portal.security.ldap.PortalLDAPUtil;
32  import com.liferay.portal.servlet.filters.BasePortalFilter;
33  import com.liferay.portal.util.PortalInstances;
34  import com.liferay.portal.util.PrefsPropsUtil;
35  import com.liferay.portal.util.PropsKeys;
36  import com.liferay.portal.util.PropsValues;
37  import com.liferay.portal.util.WebKeys;
38  import com.liferay.util.servlet.filters.DynamicFilterConfig;
39  
40  import javax.servlet.FilterChain;
41  import javax.servlet.FilterConfig;
42  import javax.servlet.http.HttpServletRequest;
43  import javax.servlet.http.HttpServletResponse;
44  import javax.servlet.http.HttpSession;
45  
46  import jcifs.UniAddress;
47  
48  import jcifs.http.NtlmHttpFilter;
49  import jcifs.http.NtlmSsp;
50  
51  import jcifs.ntlmssp.Type1Message;
52  import jcifs.ntlmssp.Type2Message;
53  
54  import jcifs.smb.NtlmPasswordAuthentication;
55  import jcifs.smb.SmbSession;
56  
57  import jcifs.util.Base64;
58  
59  /**
60   * <a href="NtlmFilter.java.html"><b><i>View Source</i></b></a>
61   *
62   * @author Bruno Farache
63   * @author Marcus Schmidke
64   * @author Brian Wing Shun Chan
65   * @author Wesley Gong
66   */
67  public class NtlmFilter extends BasePortalFilter {
68  
69      public void init(FilterConfig filterConfig) {
70          try {
71              NtlmHttpFilter ntlmFilter = new NtlmHttpFilter();
72  
73              ntlmFilter.init(filterConfig);
74          }
75          catch (Exception e) {
76              _log.error(e, e);
77          }
78  
79          _filterConfig = new DynamicFilterConfig(filterConfig);
80      }
81  
82      protected Log getLog() {
83          return _log;
84      }
85  
86      protected void processFilter(
87              HttpServletRequest request, HttpServletResponse response,
88              FilterChain filterChain)
89          throws Exception {
90  
91          long companyId = PortalInstances.getCompanyId(request);
92  
93          if (PortalLDAPUtil.isNtlmEnabled(companyId)) {
94              String domainController = _filterConfig.getInitParameter(
95                  "jcifs.http.domainController");
96              String domain = _filterConfig.getInitParameter(
97                  "jcifs.smb.client.domain");
98  
99              String preferencesDomainController = PrefsPropsUtil.getString(
100                 companyId, PropsKeys.NTLM_DOMAIN_CONTROLLER,
101                 PropsValues.NTLM_DOMAIN_CONTROLLER);
102             String preferencesDomain = PrefsPropsUtil.getString(
103                 companyId, PropsKeys.NTLM_DOMAIN, PropsValues.NTLM_DOMAIN);
104 
105             if (!Validator.equals(
106                     domainController, preferencesDomainController) ||
107                 !Validator.equals(domain, preferencesDomain)) {
108 
109                 domainController = preferencesDomainController;
110                 domain = preferencesDomain;
111 
112                 _filterConfig.addInitParameter(
113                     "jcifs.http.domainController", domainController);
114                 _filterConfig.addInitParameter(
115                     "jcifs.smb.client.domain", domain);
116 
117                 super.init(_filterConfig);
118             }
119 
120             if (_log.isDebugEnabled()) {
121                 _log.debug("Host " + domainController);
122                 _log.debug("Domain " + domain);
123             }
124 
125             // Type 1 NTLM requests from browser can (and should) always
126             // immediately be replied to with an Type 2 NTLM response, no
127             // matter whether we're yet logging in or whether it is much
128             // later in the session.
129 
130             String authorization = GetterUtil.getString(
131                 request.getHeader(HttpHeaders.AUTHORIZATION));
132 
133             if (authorization.startsWith("NTLM")) {
134                 byte[] src = Base64.decode(authorization.substring(5));
135 
136                 if (src[8] == 1) {
137                     UniAddress dc = UniAddress.getByName(
138                         domainController, true);
139 
140                     byte[] challenge = SmbSession.getChallenge(dc);
141 
142                     Type1Message type1 = new Type1Message(src);
143                     Type2Message type2 = new Type2Message(
144                         type1, challenge, null);
145 
146                     authorization = Base64.encode(type2.toByteArray());
147 
148                     response.setHeader(
149                         HttpHeaders.WWW_AUTHENTICATE, "NTLM " + authorization);
150                     response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
151                     response.setContentLength(0);
152 
153                     response.flushBuffer();
154 
155                     // Interrupt filter chain, send response. Browser will
156                     // immediately post a new request.
157 
158                     return;
159                 }
160             }
161 
162             String path = request.getPathInfo();
163 
164             if ((path != null) && path.endsWith("/login")) {
165                 NtlmPasswordAuthentication ntlm = negotiate(
166                     request, response, false);
167 
168                 if (ntlm == null) {
169                     return;
170                 }
171 
172                 String remoteUser = ntlm.getName();
173 
174                 int pos = remoteUser.indexOf(StringPool.BACK_SLASH);
175 
176                 if (pos != -1) {
177                     remoteUser = remoteUser.substring(pos + 1);
178                 }
179 
180                 if (_log.isDebugEnabled()) {
181                     _log.debug("NTLM remote user " + remoteUser);
182                 }
183 
184                 request.setAttribute(WebKeys.NTLM_REMOTE_USER, remoteUser);
185             }
186         }
187 
188         processFilter(NtlmPostFilter.class, request, response, filterChain);
189     }
190 
191     protected NtlmPasswordAuthentication negotiate(
192             HttpServletRequest request, HttpServletResponse response,
193             boolean skipAuthentication)
194         throws Exception {
195 
196         NtlmPasswordAuthentication ntlm = null;
197 
198         HttpSession session = request.getSession(false);
199 
200         String authorization = GetterUtil.getString(
201             request.getHeader(HttpHeaders.AUTHORIZATION));
202 
203         if (_log.isDebugEnabled()) {
204             _log.debug("Authorization header " + authorization);
205         }
206 
207         if (authorization.startsWith("NTLM ")) {
208             String domainController = _filterConfig.getInitParameter(
209                 "jcifs.http.domainController");
210 
211             UniAddress uniAddress = UniAddress.getByName(
212                 domainController, true);
213 
214             if (_log.isDebugEnabled()) {
215                 _log.debug("Address " + uniAddress);
216             }
217 
218             byte[] challenge = SmbSession.getChallenge(uniAddress);
219 
220             ntlm = NtlmSsp.authenticate(request, response, challenge);
221 
222             try {
223                 SmbSession.logon(uniAddress, ntlm);
224             }
225             catch (Exception e) {
226                 response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
227                 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
228                 response.setContentLength(0);
229 
230                 response.flushBuffer();
231 
232                 return null;
233             }
234 
235             session.setAttribute("NtlmHttpAuth", ntlm);
236         }
237         else {
238             if (session != null) {
239                 ntlm = (NtlmPasswordAuthentication)session.getAttribute(
240                     "NtlmHttpAuth");
241             }
242 
243             if (ntlm == null) {
244                 response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "NTLM");
245                 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
246                 response.setContentLength(0);
247 
248                 response.flushBuffer();
249 
250                 return null;
251             }
252         }
253 
254         if (_log.isDebugEnabled()) {
255             _log.debug("Password authentication " + ntlm);
256         }
257 
258         return ntlm;
259     }
260 
261     private static Log _log = LogFactoryUtil.getLog(NtlmFilter.class);
262 
263     private DynamicFilterConfig _filterConfig;
264 
265 }