/** * Waffle (https://github.com/Waffle/waffle) * * Copyright (c) 2010-2016 Application Security, Inc. * * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse * Public License v1.0 which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-v10.html. * * Contributors: Application Security, Inc. */ package waffle.apache; import java.util.Base64; import javax.servlet.ServletException; import org.apache.catalina.Context; import org.apache.catalina.Engine; import org.apache.catalina.LifecycleException; import org.assertj.core.api.Assertions; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import com.sun.jna.platform.win32.Sspi; import com.sun.jna.platform.win32.Sspi.SecBufferDesc; import mockit.Expectations; import mockit.Mocked; import waffle.apache.catalina.SimpleHttpRequest; import waffle.apache.catalina.SimpleHttpResponse; import waffle.mock.MockWindowsAuthProvider; import waffle.windows.auth.IWindowsCredentialsHandle; import waffle.windows.auth.IWindowsIdentity; import waffle.windows.auth.PrincipalFormat; import waffle.windows.auth.impl.WindowsAccountImpl; import waffle.windows.auth.impl.WindowsCredentialsHandleImpl; import waffle.windows.auth.impl.WindowsSecurityContextImpl; /** * Waffle Tomcat Mixed Authenticator Tests. * * @author dblock[at]dblock[dot]org */ public class MixedAuthenticatorTests { /** The authenticator. */ MixedAuthenticator authenticator; /** The context. */ @Mocked Context context; /** The engine. */ @Mocked Engine engine; /** * Sets the up. * * @throws LifecycleException * the lifecycle exception */ @Before public void setUp() throws LifecycleException { this.authenticator = new MixedAuthenticator(); this.authenticator.setContainer(this.context); Assert.assertNotNull(new Expectations() { { MixedAuthenticatorTests.this.context.getParent(); this.result = MixedAuthenticatorTests.this.engine; MixedAuthenticatorTests.this.context.getParent(); this.result = null; } }); this.authenticator.start(); } /** * Tear down. * * @throws LifecycleException * the lifecycle exception */ @After public void tearDown() throws LifecycleException { this.authenticator.stop(); } /** * Test challenge get. */ @Test public void testChallengeGET() { final SimpleHttpRequest request = new SimpleHttpRequest(); request.setMethod("GET"); request.setQueryString("j_negotiate_check"); final SimpleHttpResponse response = new SimpleHttpResponse(); this.authenticator.authenticate(request, response); final String[] wwwAuthenticates = response.getHeaderValues("WWW-Authenticate"); Assert.assertNotNull(wwwAuthenticates); Assert.assertEquals(2, wwwAuthenticates.length); Assert.assertEquals("Negotiate", wwwAuthenticates[0]); Assert.assertEquals("NTLM", wwwAuthenticates[1]); Assert.assertEquals("close", response.getHeader("Connection")); Assert.assertEquals(2, response.getHeaderNames().size()); Assert.assertEquals(401, response.getStatus()); } /** * Test challenge post. */ @Test public void testChallengePOST() { final String securityPackage = "Negotiate"; IWindowsCredentialsHandle clientCredentials = null; WindowsSecurityContextImpl clientContext = null; try { // client credentials handle clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage); clientCredentials.initialize(); // initial client security context clientContext = new WindowsSecurityContextImpl(); clientContext.setPrincipalName(WindowsAccountImpl.getCurrentUsername()); clientContext.setCredentialsHandle(clientCredentials); clientContext.setSecurityPackage(securityPackage); clientContext.initialize(null, null, WindowsAccountImpl.getCurrentUsername()); final SimpleHttpRequest request = new SimpleHttpRequest(); request.setQueryString("j_negotiate_check"); request.setMethod("POST"); request.setContentLength(0); final String clientToken = Base64.getEncoder().encodeToString(clientContext.getToken()); request.addHeader("Authorization", securityPackage + " " + clientToken); final SimpleHttpResponse response = new SimpleHttpResponse(); this.authenticator.authenticate(request, response); Assert.assertTrue(response.getHeader("WWW-Authenticate").startsWith(securityPackage + " ")); Assert.assertEquals("keep-alive", response.getHeader("Connection")); Assert.assertEquals(2, response.getHeaderNames().size()); Assert.assertEquals(401, response.getStatus()); } finally { if (clientContext != null) { clientContext.dispose(); } if (clientCredentials != null) { clientCredentials.dispose(); } } } /** * Test get. */ @Test public void testGet() { final SimpleHttpRequest request = new SimpleHttpRequest(); final SimpleHttpResponse response = new SimpleHttpResponse(); Assert.assertFalse(this.authenticator.authenticate(request, response)); } /** * Test get info. */ @Test public void testGetInfo() { Assertions.assertThat(this.authenticator.getInfo().length()).isGreaterThan(0); } /** * Test negotiate. */ @Test public void testNegotiate() { final String securityPackage = "Negotiate"; IWindowsCredentialsHandle clientCredentials = null; WindowsSecurityContextImpl clientContext = null; try { // client credentials handle clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage); clientCredentials.initialize(); // initial client security context clientContext = new WindowsSecurityContextImpl(); clientContext.setPrincipalName(WindowsAccountImpl.getCurrentUsername()); clientContext.setCredentialsHandle(clientCredentials); clientContext.setSecurityPackage(securityPackage); clientContext.initialize(null, null, WindowsAccountImpl.getCurrentUsername()); // negotiate boolean authenticated = false; final SimpleHttpRequest request = new SimpleHttpRequest(); request.setQueryString("j_negotiate_check"); String clientToken; while (true) { clientToken = Base64.getEncoder().encodeToString(clientContext.getToken()); request.addHeader("Authorization", securityPackage + " " + clientToken); final SimpleHttpResponse response = new SimpleHttpResponse(); authenticated = this.authenticator.authenticate(request, response); if (authenticated) { Assertions.assertThat(response.getHeaderNames().size()).isGreaterThanOrEqualTo(0); break; } Assert.assertTrue(response.getHeader("WWW-Authenticate").startsWith(securityPackage + " ")); Assert.assertEquals("keep-alive", response.getHeader("Connection")); Assert.assertEquals(2, response.getHeaderNames().size()); Assert.assertEquals(401, response.getStatus()); final String continueToken = response.getHeader("WWW-Authenticate") .substring(securityPackage.length() + 1); final byte[] continueTokenBytes = Base64.getDecoder().decode(continueToken); Assertions.assertThat(continueTokenBytes.length).isGreaterThan(0); final SecBufferDesc continueTokenBuffer = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, continueTokenBytes); clientContext.initialize(clientContext.getHandle(), continueTokenBuffer, WindowsAccountImpl.getCurrentUsername()); } Assert.assertTrue(authenticated); } finally { if (clientContext != null) { clientContext.dispose(); } if (clientCredentials != null) { clientCredentials.dispose(); } } } /** * Test post security check. */ @Test public void testPostSecurityCheck() { final SimpleHttpRequest request = new SimpleHttpRequest(); request.setQueryString("j_security_check"); request.addParameter("j_username", "username"); request.addParameter("j_password", "password"); final SimpleHttpResponse response = new SimpleHttpResponse(); Assert.assertFalse(this.authenticator.authenticate(request, response)); } /** * Test programmatic security BOTH. * * @param identity * the identity * @throws ServletException * the servlet exception */ @Test public void testProgrammaticSecurityBoth(@Mocked final IWindowsIdentity identity) throws ServletException { this.authenticator.setAuth(new MockWindowsAuthProvider()); final SimpleHttpRequest request = new SimpleHttpRequest(); request.getMappingData().context = (Context) this.authenticator.getContainer(); request.login(WindowsAccountImpl.getCurrentUsername(), ""); Assert.assertNotNull(new Expectations() { { identity.getFqn(); this.result = "fqn"; identity.getSidString(); this.result = "S-1234"; } }); request.setUserPrincipal(new GenericWindowsPrincipal(identity, PrincipalFormat.BOTH, PrincipalFormat.BOTH)); Assert.assertTrue(request.getUserPrincipal() instanceof GenericWindowsPrincipal); final GenericWindowsPrincipal windowsPrincipal = (GenericWindowsPrincipal) request.getUserPrincipal(); Assert.assertTrue(windowsPrincipal.getSidString().startsWith("S-")); } /** * Test programmatic security SID. * * @param identity * the identity * @throws ServletException * the servlet exception */ @Test public void testProgrammaticSecuritySID(@Mocked final IWindowsIdentity identity) throws ServletException { this.authenticator.setAuth(new MockWindowsAuthProvider()); final SimpleHttpRequest request = new SimpleHttpRequest(); request.getMappingData().context = (Context) this.authenticator.getContainer(); request.login(WindowsAccountImpl.getCurrentUsername(), ""); Assert.assertNotNull(new Expectations() { { identity.getSidString(); this.result = "S-1234"; } }); request.setUserPrincipal(new GenericWindowsPrincipal(identity, PrincipalFormat.SID, PrincipalFormat.SID)); Assert.assertTrue(request.getUserPrincipal() instanceof GenericWindowsPrincipal); final GenericWindowsPrincipal windowsPrincipal = (GenericWindowsPrincipal) request.getUserPrincipal(); Assert.assertTrue(windowsPrincipal.getSidString().startsWith("S-")); } /** * Test programmatic security NONE. * * @param identity * the identity * @throws ServletException * the servlet exception */ @Test public void testProgrammaticSecurityNone(@Mocked final IWindowsIdentity identity) throws ServletException { this.authenticator.setAuth(new MockWindowsAuthProvider()); final SimpleHttpRequest request = new SimpleHttpRequest(); request.getMappingData().context = (Context) this.authenticator.getContainer(); request.login(WindowsAccountImpl.getCurrentUsername(), ""); request.setUserPrincipal(new GenericWindowsPrincipal(identity, PrincipalFormat.NONE, PrincipalFormat.NONE)); Assert.assertTrue(request.getUserPrincipal() instanceof GenericWindowsPrincipal); final GenericWindowsPrincipal windowsPrincipal = (GenericWindowsPrincipal) request.getUserPrincipal(); Assert.assertNull(windowsPrincipal.getSidString()); } /** * Test security check parameters. */ @Test public void testSecurityCheckParameters() { this.authenticator.setAuth(new MockWindowsAuthProvider()); final SimpleHttpRequest request = new SimpleHttpRequest(); request.addParameter("j_security_check", ""); request.addParameter("j_username", WindowsAccountImpl.getCurrentUsername()); request.addParameter("j_password", ""); final SimpleHttpResponse response = new SimpleHttpResponse(); Assert.assertTrue(this.authenticator.authenticate(request, response)); } /** * Test security check query string. */ @Test public void testSecurityCheckQueryString() { this.authenticator.setAuth(new MockWindowsAuthProvider()); final SimpleHttpRequest request = new SimpleHttpRequest(); request.setQueryString("j_security_check"); request.addParameter("j_username", WindowsAccountImpl.getCurrentUsername()); request.addParameter("j_password", ""); final SimpleHttpResponse response = new SimpleHttpResponse(); Assert.assertTrue(this.authenticator.authenticate(request, response)); } }