PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Source/JNA/waffle-tomcat85/src/main/java/waffle/apache/MixedAuthenticator.java

http://github.com/dblock/waffle
Java | 307 lines | 184 code | 49 blank | 74 comment | 29 complexity | 9b81ad0cdf51f72ac7730391efce6525 MD5 | raw file
Possible License(s): MIT, MPL-2.0-no-copyleft-exception, Apache-2.0, LGPL-3.0, LGPL-2.1, GPL-2.0
  1. /*
  2. * MIT License
  3. *
  4. * Copyright (c) 2010-2020 The Waffle Project Contributors: https://github.com/Waffle/waffle/graphs/contributors
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in all
  14. * copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. * SOFTWARE.
  23. */
  24. package waffle.apache;
  25. import com.sun.jna.platform.win32.Win32Exception;
  26. import java.io.IOException;
  27. import java.security.Principal;
  28. import java.util.Base64;
  29. import javax.servlet.RequestDispatcher;
  30. import javax.servlet.ServletContext;
  31. import javax.servlet.ServletException;
  32. import javax.servlet.http.HttpServletResponse;
  33. import javax.servlet.http.HttpSession;
  34. import org.apache.catalina.LifecycleException;
  35. import org.apache.catalina.connector.Request;
  36. import org.apache.catalina.realm.GenericPrincipal;
  37. import org.apache.tomcat.util.descriptor.web.LoginConfig;
  38. import org.slf4j.LoggerFactory;
  39. import waffle.util.AuthorizationHeader;
  40. import waffle.util.NtlmServletRequest;
  41. import waffle.windows.auth.IWindowsIdentity;
  42. import waffle.windows.auth.IWindowsSecurityContext;
  43. /**
  44. * Mixed Negotiate + Form Authenticator.
  45. *
  46. * @author dblock[at]dblock[dot]org
  47. */
  48. public class MixedAuthenticator extends WaffleAuthenticatorBase {
  49. /**
  50. * Instantiates a new mixed authenticator.
  51. */
  52. public MixedAuthenticator() {
  53. super();
  54. this.log = LoggerFactory.getLogger(MixedAuthenticator.class);
  55. this.info = "waffle.apache.MixedAuthenticator/1.0";
  56. this.log.debug("[waffle.apache.MixedAuthenticator] loaded");
  57. }
  58. @Override
  59. public synchronized void startInternal() throws LifecycleException {
  60. this.log.info("[waffle.apache.MixedAuthenticator] started");
  61. super.startInternal();
  62. }
  63. @Override
  64. public synchronized void stopInternal() throws LifecycleException {
  65. super.stopInternal();
  66. this.log.info("[waffle.apache.MixedAuthenticator] stopped");
  67. }
  68. @Override
  69. public boolean authenticate(final Request request, final HttpServletResponse response) {
  70. // realm: fail if no realm is configured
  71. if (this.context == null || this.context.getRealm() == null) {
  72. this.log.warn("missing context/realm");
  73. this.sendError(response, HttpServletResponse.SC_SERVICE_UNAVAILABLE);
  74. return false;
  75. }
  76. this.log.debug("{} {}, contentlength: {}", request.getMethod(), request.getRequestURI(),
  77. Integer.valueOf(request.getContentLength()));
  78. final boolean negotiateCheck = request.getParameter("j_negotiate_check") != null;
  79. this.log.debug("negotiateCheck: {}", Boolean.valueOf(negotiateCheck));
  80. final boolean securityCheck = request.getParameter("j_security_check") != null;
  81. this.log.debug("securityCheck: {}", Boolean.valueOf(securityCheck));
  82. final Principal principal = request.getUserPrincipal();
  83. final AuthorizationHeader authorizationHeader = new AuthorizationHeader(request);
  84. final boolean ntlmPost = authorizationHeader.isNtlmType1PostAuthorizationHeader();
  85. this.log.debug("authorization: {}, ntlm post: {}", authorizationHeader, Boolean.valueOf(ntlmPost));
  86. final LoginConfig loginConfig = this.context.getLoginConfig();
  87. if (principal != null && !ntlmPost) {
  88. this.log.debug("previously authenticated user: {}", principal.getName());
  89. return true;
  90. } else if (negotiateCheck) {
  91. if (!authorizationHeader.isNull()) {
  92. boolean negotiateResult = this.negotiate(request, response, authorizationHeader);
  93. if (!negotiateResult) {
  94. this.redirectTo(request, response, loginConfig.getErrorPage());
  95. }
  96. return negotiateResult;
  97. }
  98. this.log.debug("authorization required");
  99. this.sendUnauthorized(response);
  100. return false;
  101. } else if (securityCheck) {
  102. final boolean postResult = this.post(request, response);
  103. if (!postResult) {
  104. this.redirectTo(request, response, loginConfig.getErrorPage());
  105. }
  106. return postResult;
  107. } else {
  108. this.redirectTo(request, response, loginConfig.getLoginPage());
  109. return false;
  110. }
  111. }
  112. /**
  113. * Negotiate.
  114. *
  115. * @param request
  116. * the request
  117. * @param response
  118. * the response
  119. * @param authorizationHeader
  120. * the authorization header
  121. * @return true, if successful
  122. */
  123. private boolean negotiate(final Request request, final HttpServletResponse response,
  124. final AuthorizationHeader authorizationHeader) {
  125. final String securityPackage = authorizationHeader.getSecurityPackage();
  126. // maintain a connection-based session for NTLM tokens
  127. final String connectionId = NtlmServletRequest.getConnectionId(request);
  128. this.log.debug("security package: {}, connection id: {}", securityPackage, connectionId);
  129. final boolean ntlmPost = authorizationHeader.isNtlmType1PostAuthorizationHeader();
  130. if (ntlmPost) {
  131. // type 1 NTLM authentication message received
  132. this.auth.resetSecurityToken(connectionId);
  133. }
  134. final byte[] tokenBuffer = authorizationHeader.getTokenBytes();
  135. this.log.debug("token buffer: {} byte(s)", Integer.valueOf(tokenBuffer.length));
  136. // log the user in using the token
  137. IWindowsSecurityContext securityContext;
  138. try {
  139. securityContext = this.auth.acceptSecurityToken(connectionId, tokenBuffer, securityPackage);
  140. } catch (final Win32Exception e) {
  141. this.log.warn("error logging in user: {}", e.getMessage());
  142. this.log.trace("", e);
  143. this.sendUnauthorized(response);
  144. return false;
  145. }
  146. this.log.debug("continue required: {}", Boolean.valueOf(securityContext.isContinue()));
  147. final byte[] continueTokenBytes = securityContext.getToken();
  148. if (continueTokenBytes != null && continueTokenBytes.length > 0) {
  149. final String continueToken = Base64.getEncoder().encodeToString(continueTokenBytes);
  150. this.log.debug("continue token: {}", continueToken);
  151. response.addHeader("WWW-Authenticate", securityPackage + " " + continueToken);
  152. }
  153. try {
  154. if (securityContext.isContinue() || ntlmPost) {
  155. response.setHeader("Connection", "keep-alive");
  156. response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
  157. response.flushBuffer();
  158. return false;
  159. }
  160. } catch (final IOException e) {
  161. this.log.warn("error logging in user: {}", e.getMessage());
  162. this.log.trace("", e);
  163. this.sendUnauthorized(response);
  164. return false;
  165. }
  166. // create and register the user principal with the session
  167. final IWindowsIdentity windowsIdentity = securityContext.getIdentity();
  168. // disable guest login
  169. if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
  170. this.log.warn("guest login disabled: {}", windowsIdentity.getFqn());
  171. this.sendUnauthorized(response);
  172. return false;
  173. }
  174. try {
  175. this.log.debug("logged in user: {} ({})", windowsIdentity.getFqn(), windowsIdentity.getSidString());
  176. final GenericPrincipal genericPrincipal = this.createPrincipal(windowsIdentity);
  177. this.log.debug("roles: {}", String.join(", ", genericPrincipal.getRoles()));
  178. // create a session associated with this request if there's none
  179. final HttpSession session = request.getSession(true);
  180. this.log.debug("session id: {}", session == null ? "null" : session.getId());
  181. this.register(request, response, genericPrincipal, securityPackage, genericPrincipal.getName(), null);
  182. this.log.info("successfully logged in user: {}", genericPrincipal.getName());
  183. } finally {
  184. windowsIdentity.dispose();
  185. }
  186. return true;
  187. }
  188. /**
  189. * Post.
  190. *
  191. * @param request
  192. * the request
  193. * @param response
  194. * the response
  195. * @return true, if successful
  196. */
  197. private boolean post(final Request request, final HttpServletResponse response) {
  198. final String username = request.getParameter("j_username");
  199. final String password = request.getParameter("j_password");
  200. this.log.debug("logging in: {}", username);
  201. IWindowsIdentity windowsIdentity;
  202. try {
  203. windowsIdentity = this.auth.logonUser(username, password);
  204. } catch (final Exception e) {
  205. this.log.error(e.getMessage());
  206. this.log.trace("", e);
  207. return false;
  208. }
  209. // disable guest login
  210. if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
  211. this.log.warn("guest login disabled: {}", windowsIdentity.getFqn());
  212. return false;
  213. }
  214. try {
  215. this.log.debug("successfully logged in {} ({})", username, windowsIdentity.getSidString());
  216. final GenericPrincipal genericPrincipal = this.createPrincipal(windowsIdentity);
  217. this.log.debug("roles: {}", String.join(", ", genericPrincipal.getRoles()));
  218. // create a session associated with this request if there's none
  219. final HttpSession session = request.getSession(true);
  220. this.log.debug("session id: {}", session == null ? "null" : session.getId());
  221. this.register(request, response, genericPrincipal, "FORM", genericPrincipal.getName(), null);
  222. this.log.info("successfully logged in user: {}", genericPrincipal.getName());
  223. } finally {
  224. windowsIdentity.dispose();
  225. }
  226. return true;
  227. }
  228. /**
  229. * Redirect to.
  230. *
  231. * @param request
  232. * the request
  233. * @param response
  234. * the response
  235. * @param url
  236. * the url
  237. */
  238. private void redirectTo(final Request request, final HttpServletResponse response, final String url) {
  239. try {
  240. this.log.debug("redirecting to: {}", url);
  241. final ServletContext servletContext = this.context.getServletContext();
  242. final RequestDispatcher disp = servletContext.getRequestDispatcher(url);
  243. disp.forward(request.getRequest(), response);
  244. } catch (final IOException | ServletException e) {
  245. this.log.error(e.getMessage());
  246. this.log.trace("", e);
  247. throw new RuntimeException(e);
  248. }
  249. }
  250. /**
  251. * XXX The 'doAuthenticate' is intended to replace 'authenticate' for needs like ours. In order to support old and
  252. * new at this time, we will continue to have both for time being.
  253. */
  254. @Override
  255. protected boolean doAuthenticate(final Request request, final HttpServletResponse response) throws IOException {
  256. return this.authenticate(request, response);
  257. }
  258. }