1   /**
2    * Copyright (c) 2000-2008 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.util.StringPool;
26  import com.liferay.portal.security.ldap.PortalLDAPUtil;
27  import com.liferay.portal.util.PortalInstances;
28  import com.liferay.portal.util.PrefsPropsUtil;
29  import com.liferay.portal.util.PropsUtil;
30  import com.liferay.portal.util.WebKeys;
31  import com.liferay.util.servlet.filters.DynamicFilterConfig;
32  
33  import java.io.IOException;
34  
35  import javax.servlet.FilterChain;
36  import javax.servlet.FilterConfig;
37  import javax.servlet.ServletException;
38  import javax.servlet.ServletRequest;
39  import javax.servlet.ServletResponse;
40  import javax.servlet.http.HttpServletRequest;
41  import javax.servlet.http.HttpServletResponse;
42  import javax.servlet.http.HttpSession;
43  
44  import jcifs.Config;
45  import jcifs.UniAddress;
46  
47  import jcifs.http.NtlmHttpFilter;
48  import jcifs.http.NtlmSsp;
49  
50  import jcifs.ntlmssp.Type1Message;
51  import jcifs.ntlmssp.Type2Message;
52  
53  import jcifs.smb.NtlmPasswordAuthentication;
54  import jcifs.smb.SmbSession;
55  
56  import jcifs.util.Base64;
57  
58  import org.apache.commons.logging.Log;
59  import org.apache.commons.logging.LogFactory;
60  
61  /**
62   * <a href="NtlmFilter.java.html"><b><i>View Source</i></b></a>
63   *
64   * @author Bruno Farache
65   * @author Marcus Schmidke
66   *
67   */
68  public class NtlmFilter extends NtlmHttpFilter {
69  
70      public void init(FilterConfig config) throws ServletException {
71          super.init(config);
72  
73          _config = new DynamicFilterConfig(config);
74      }
75  
76      public void doFilter(
77              ServletRequest req, ServletResponse res, FilterChain chain)
78          throws IOException, ServletException {
79  
80          try {
81              HttpServletRequest httpReq = (HttpServletRequest)req;
82              HttpServletResponse httpRes = (HttpServletResponse)res;
83  
84              long companyId = PortalInstances.getCompanyId(httpReq);
85  
86              if (PortalLDAPUtil.isNtlmEnabled(companyId)) {
87                  if ((_config.getInitParameter("jcifs.http.domainController")
88                          == null) &&
89                      (_config.getInitParameter("jcifs.smb.client.domain")
90                          == null)) {
91  
92                      String domainController = PrefsPropsUtil.getString(
93                          companyId, PropsUtil.NTLM_DOMAIN_CONTROLLER);
94  
95                      String domain = PrefsPropsUtil.getString(
96                          companyId, PropsUtil.NTLM_DOMAIN);
97  
98                      _config.addInitParameter(
99                          "jcifs.http.domainController", domainController);
100 
101                     _config.addInitParameter(
102                         "jcifs.smb.client.domain", domain);
103 
104                     super.init(_config);
105 
106                     if (_log.isDebugEnabled()) {
107                         _log.debug("Host " + domainController);
108                         _log.debug("Domain " + domain);
109                     }
110                 }
111 
112                 // Type 1 NTLM requests from browser can (and should) always
113                 // immediately be replied to with an Type 2 NTLM response, no
114                 // matter whether we're yet logging in or whether it is much
115                 // later in the session.
116 
117                 String msg = httpReq.getHeader("Authorization");
118 
119                 if (msg != null && msg.startsWith("NTLM")) {
120                     byte[] src = Base64.decode(msg.substring(5));
121 
122                     if (src[8] == 1) {
123                         UniAddress dc = UniAddress.getByName(
124                             Config.getProperty("jcifs.http.domainController"),
125                             true);
126 
127                         byte[] challenge = SmbSession.getChallenge(dc);
128 
129                         Type1Message type1 = new Type1Message(src);
130                         Type2Message type2 = new Type2Message(
131                             type1, challenge, null);
132 
133                         msg = Base64.encode(type2.toByteArray());
134 
135                         httpRes.setHeader("WWW-Authenticate", "NTLM " + msg);
136                         httpRes.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
137                         httpRes.setContentLength(0);
138 
139                         httpRes.flushBuffer();
140 
141                         // Interrupt filter chain, send response. Browser will
142                         // immediately post a new request.
143 
144                         return;
145                     }
146                 }
147 
148                 String path = httpReq.getPathInfo();
149 
150                 if (path != null && path.endsWith("/login")) {
151                     NtlmPasswordAuthentication ntlm = negotiate(
152                         httpReq, httpRes, false);
153 
154                     if (ntlm == null) {
155                         return;
156                     }
157 
158                     String remoteUser = ntlm.getName();
159 
160                     int pos = remoteUser.indexOf(StringPool.BACK_SLASH);
161 
162                     if (pos != -1) {
163                         remoteUser = remoteUser.substring(pos + 1);
164                     }
165 
166                     if (_log.isDebugEnabled()) {
167                         _log.debug("NTLM remote user " + remoteUser);
168                     }
169 
170                     req.setAttribute(WebKeys.NTLM_REMOTE_USER, remoteUser);
171                 }
172             }
173         }
174         catch (Exception e) {
175             _log.error(e);
176         }
177 
178         chain.doFilter(req, res);
179     }
180 
181     public NtlmPasswordAuthentication negotiate(
182             HttpServletRequest req, HttpServletResponse res,
183             boolean skipAuthentication)
184         throws IOException, ServletException {
185 
186         NtlmPasswordAuthentication ntlm = null;
187 
188         HttpSession ses = req.getSession(false);
189 
190         String authorizationHeader = req.getHeader("Authorization");
191 
192         if (_log.isDebugEnabled()) {
193             _log.debug("Authorization header " + authorizationHeader);
194         }
195 
196         if ((authorizationHeader != null) && (
197             (authorizationHeader.startsWith("NTLM ")))) {
198 
199             String domainController = Config.getProperty(
200                 "jcifs.http.domainController");
201 
202             UniAddress uniAddress = UniAddress.getByName(
203                 domainController, true);
204 
205             if (_log.isDebugEnabled()) {
206                 _log.debug("Address " + uniAddress);
207             }
208 
209             byte[] challenge = SmbSession.getChallenge(uniAddress);
210 
211             ntlm = NtlmSsp.authenticate(req, res, challenge);
212 
213             ses.setAttribute("NtlmHttpAuth", ntlm);
214         }
215         else {
216             if (ses != null) {
217                 ntlm = (NtlmPasswordAuthentication)ses.getAttribute(
218                     "NtlmHttpAuth");
219             }
220 
221             if (ntlm == null) {
222                 res.setHeader("WWW-Authenticate", "NTLM");
223                 res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
224                 res.setContentLength(0);
225 
226                 res.flushBuffer();
227 
228                 return null;
229             }
230         }
231 
232         if (_log.isDebugEnabled()) {
233             _log.debug("Password authentication " + ntlm);
234         }
235 
236         return ntlm;
237     }
238 
239     private static Log _log = LogFactory.getLog(NtlmFilter.class);
240 
241     private DynamicFilterConfig _config;
242 
243 }