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.security.ldap.PortalLDAPUtil;
31  import com.liferay.portal.servlet.filters.BasePortalFilter;
32  import com.liferay.portal.util.PortalInstances;
33  import com.liferay.portal.util.PrefsPropsUtil;
34  import com.liferay.portal.util.PropsKeys;
35  import com.liferay.portal.util.PropsValues;
36  import com.liferay.portal.util.WebKeys;
37  import com.liferay.util.servlet.filters.DynamicFilterConfig;
38  
39  import javax.servlet.FilterChain;
40  import javax.servlet.FilterConfig;
41  import javax.servlet.http.HttpServletRequest;
42  import javax.servlet.http.HttpServletResponse;
43  import javax.servlet.http.HttpSession;
44  
45  import jcifs.Config;
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   *
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              if ((domainController == null) && (domain == null)) {
100                 domainController = PrefsPropsUtil.getString(
101                     companyId, PropsKeys.NTLM_DOMAIN_CONTROLLER,
102                     PropsValues.NTLM_DOMAIN_CONTROLLER);
103                 domain = PrefsPropsUtil.getString(
104                     companyId, PropsKeys.NTLM_DOMAIN, PropsValues.NTLM_DOMAIN);
105 
106                 _filterConfig.addInitParameter(
107                     "jcifs.http.domainController", domainController);
108                 _filterConfig.addInitParameter(
109                     "jcifs.smb.client.domain", domain);
110 
111                 super.init(_filterConfig);
112 
113                 if (_log.isDebugEnabled()) {
114                     _log.debug("Host " + domainController);
115                     _log.debug("Domain " + domain);
116                 }
117             }
118 
119             // Type 1 NTLM requests from browser can (and should) always
120             // immediately be replied to with an Type 2 NTLM response, no
121             // matter whether we're yet logging in or whether it is much
122             // later in the session.
123 
124             String authorization = GetterUtil.getString(
125                 request.getHeader(HttpHeaders.AUTHORIZATION));
126 
127             if (authorization.startsWith("NTLM")) {
128                 byte[] src = Base64.decode(authorization.substring(5));
129 
130                 if (src[8] == 1) {
131                     UniAddress dc = UniAddress.getByName(
132                         Config.getProperty("jcifs.http.domainController"),
133                         true);
134 
135                     byte[] challenge = SmbSession.getChallenge(dc);
136 
137                     Type1Message type1 = new Type1Message(src);
138                     Type2Message type2 = new Type2Message(
139                         type1, challenge, null);
140 
141                     authorization = Base64.encode(type2.toByteArray());
142 
143                     response.setHeader(
144                         HttpHeaders.WWW_AUTHENTICATE, "NTLM " + authorization);
145                     response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
146                     response.setContentLength(0);
147 
148                     response.flushBuffer();
149 
150                     // Interrupt filter chain, send response. Browser will
151                     // immediately post a new request.
152 
153                     return;
154                 }
155             }
156 
157             String path = request.getPathInfo();
158 
159             if (path != null && path.endsWith("/login")) {
160                 NtlmPasswordAuthentication ntlm = negotiate(
161                     request, response, false);
162 
163                 if (ntlm == null) {
164                     return;
165                 }
166 
167                 String remoteUser = ntlm.getName();
168 
169                 int pos = remoteUser.indexOf(StringPool.BACK_SLASH);
170 
171                 if (pos != -1) {
172                     remoteUser = remoteUser.substring(pos + 1);
173                 }
174 
175                 if (_log.isDebugEnabled()) {
176                     _log.debug("NTLM remote user " + remoteUser);
177                 }
178 
179                 request.setAttribute(WebKeys.NTLM_REMOTE_USER, remoteUser);
180             }
181         }
182 
183         processFilter(NtlmPostFilter.class, request, response, filterChain);
184     }
185 
186     protected NtlmPasswordAuthentication negotiate(
187             HttpServletRequest request, HttpServletResponse response,
188             boolean skipAuthentication)
189         throws Exception {
190 
191         NtlmPasswordAuthentication ntlm = null;
192 
193         HttpSession session = request.getSession(false);
194 
195         String authorization = GetterUtil.getString(
196             request.getHeader(HttpHeaders.AUTHORIZATION));
197 
198         if (_log.isDebugEnabled()) {
199             _log.debug("Authorization header " + authorization);
200         }
201 
202         if (authorization.startsWith("NTLM ")) {
203             String domainController = Config.getProperty(
204                 "jcifs.http.domainController");
205 
206             UniAddress uniAddress = UniAddress.getByName(
207                 domainController, true);
208 
209             if (_log.isDebugEnabled()) {
210                 _log.debug("Address " + uniAddress);
211             }
212 
213             byte[] challenge = SmbSession.getChallenge(uniAddress);
214 
215             ntlm = NtlmSsp.authenticate(request, response, challenge);
216 
217             session.setAttribute("NtlmHttpAuth", ntlm);
218         }
219         else {
220             if (session != null) {
221                 ntlm = (NtlmPasswordAuthentication)session.getAttribute(
222                     "NtlmHttpAuth");
223             }
224 
225             if (ntlm == null) {
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 
236         if (_log.isDebugEnabled()) {
237             _log.debug("Password authentication " + ntlm);
238         }
239 
240         return ntlm;
241     }
242 
243     private static Log _log = LogFactoryUtil.getLog(NtlmFilter.class);
244 
245     private DynamicFilterConfig _filterConfig;
246 
247 }