333 lines
12 KiB
Java
333 lines
12 KiB
Java
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package org.apache.catalina.valves;
|
|
|
|
import java.security.cert.X509Certificate;
|
|
import java.util.Arrays;
|
|
import java.util.logging.Level;
|
|
|
|
import org.junit.Assert;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
|
|
import org.apache.catalina.Globals;
|
|
import org.apache.catalina.Valve;
|
|
import org.apache.catalina.connector.Connector;
|
|
import org.apache.catalina.connector.Request;
|
|
import org.apache.tomcat.unittest.TesterLogValidationFilter;
|
|
import org.easymock.EasyMock;
|
|
|
|
public class TestSSLValve {
|
|
|
|
public static class MockRequest extends Request {
|
|
|
|
public MockRequest() {
|
|
setConnector(EasyMock.createMock(Connector.class));
|
|
setCoyoteRequest(new org.apache.coyote.Request());
|
|
}
|
|
|
|
@Override
|
|
public void setAttribute(String name, Object value) {
|
|
getCoyoteRequest().getAttributes().put(name, value);
|
|
}
|
|
|
|
@Override
|
|
public Object getAttribute(String name) {
|
|
return getCoyoteRequest().getAttribute(name);
|
|
}
|
|
|
|
public void setHeader(String header, String value) {
|
|
getCoyoteRequest().getMimeHeaders().setValue(header).setString(value);
|
|
}
|
|
|
|
public void addHeader(String header, String value) {
|
|
getCoyoteRequest().getMimeHeaders().addValue(header).setString(value);
|
|
}
|
|
}
|
|
|
|
private static final String[] CERTIFICATE_LINES = new String[] { "-----BEGIN CERTIFICATE-----",
|
|
"MIIFXTCCA0WgAwIBAgIJANFf3YTJgYifMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV",
|
|
"BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX",
|
|
"aWRnaXRzIFB0eSBMdGQwHhcNMTcwNTI2MjEzNjM3WhcNMTgwNTI2MjEzNjM3WjBF",
|
|
"MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50",
|
|
"ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC",
|
|
"CgKCAgEA2ykNBanZz4cVITNpZcWNVmErUzqgSNrK361mj9vEdB1UkHatwal9jVrR",
|
|
"QvFgfiZ8Gl+/85t0ebJhJ+rIr1ww6JE7v2s2MThENj95K5EwZOmgvw+CBlBYsFIz",
|
|
"8BtjlVYy+v7RaGPXfjrFkexQP9UIaiIIog2ClDZirRvb+QxS930/YW5Qo+X6EX6W",
|
|
"/m/HvlorD25U4ni2FQ0y+EMO2e1jD88cAAMoP5f+Mf6NBK8I6yUeaSuMq7WqtHGV",
|
|
"e4F1WOg5z9J5c/M69rB0iQr5NUQwZ1mPYf5Kr0P6+TLh8DJphbVvmHJyT3bgofeV",
|
|
"JYl/kdjiXS5P/jwY9tfmhu04tsyzopWRUFCcj5zCiqZYaMn0wtDn08KaAh9oOlg8",
|
|
"Z6mJ9i5EybkLm63W7z7LxuM+qnYzq4wKkKdx8hbpASwPqzJkJeXFL/LzhKdZuHiR",
|
|
"clgPVYnm98URwhObh073dKguG/gkhcnpXcVBBVdVTJZYGBvTpQh0afXd9bcBwOzY",
|
|
"t4MDpGiQB2fLzBOEZhQ37kUcWPmZw5bNPxhx4yE96Md0rx/Gu4ipAHuqLemb1SL5",
|
|
"uWNesVmgY3OXaIamQIm9BCwkf8mMvoYdAT+lukTUZLtJ6s2w+Oxnl10tmb+6sTXy",
|
|
"UB3WcBTp/o3YjAyJPnM1Wq6nVNQ4W2+NbV5purGAP09sumxeJj8CAwEAAaNQME4w",
|
|
"HQYDVR0OBBYEFCGOYMvymUG2ZZT+lK4LvwEvx731MB8GA1UdIwQYMBaAFCGOYMvy",
|
|
"mUG2ZZT+lK4LvwEvx731MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB",
|
|
"AG6m4nDYCompUtRVude1qulwwAaYEMHyIIsfymI8uAE7d2o4bGjVpAUOdH/VWSOp",
|
|
"Rzx0oK6K9cHyiBlKHw5zSZdqcRi++tDX3P9Iy5tXO//zkhMEnSpk6RF2+9JXtyhx",
|
|
"Gma4yAET1yES+ybiFT21uZrGCC9r69rWG8JRZshc4RVWGwZsd0zrATVqfY0mZurm",
|
|
"xLgU4UOvkTczjlrLiklwwU68M1DLcILJ5FZGTWeTJ/q1wpIn9isK2siAW/VOcbuG",
|
|
"xdbGladnIFv+iQfuZG0yjcuMsBFsQiXi6ONM8GM+dr+61V63/1s73jYcOToEsTMM",
|
|
"3bHeVffoSkhZvOGTRCI6QhK9wqnIKhAYqu+NbV4OphfE3gOaK+T1cASXUtSQPXoa",
|
|
"sEoIVmbQsWRBhWvYShVqvINsH/hAT3Cf/+SslprtQUqiyt2ljdgrRFZdoyB3S7ky",
|
|
"KWoZRvHRj2cKU65LVYwx6U1A8SGmViz4aHMSai0wwKzOVv9MGHeRaVhlMmMsbdfu",
|
|
"wKoKJv0xYoVwEh1rB8TH8PjbL+6eFLeZXYVZnH71d5JHCghZ8W0a11ZuYkscSQWk",
|
|
"yoTBqEpJloWksrypqp3iL4PAL5+KkB2zp66+MVAg8LcEDFJggBBJCtv4SCWV7ZOB",
|
|
"WLu8gep+XCwSn0Wb6D3eFs4DoIiMvQ6g2rS/pk7o5eWj", "-----END CERTIFICATE-----" };
|
|
|
|
private SSLValve valve = new SSLValve();
|
|
|
|
private MockRequest mockRequest = new MockRequest();
|
|
private Valve mockNext = EasyMock.createMock(Valve.class);
|
|
|
|
|
|
@Before
|
|
public void setUp() throws Exception {
|
|
valve.setNext(mockNext);
|
|
mockNext.invoke(mockRequest, null);
|
|
EasyMock.replay(mockNext);
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslHeader() {
|
|
final String headerName = "myheader";
|
|
final String headerValue = "BASE64_HEADER_VALUE";
|
|
mockRequest.setHeader(headerName, headerValue);
|
|
|
|
Assert.assertEquals(headerValue, valve.mygetHeader(mockRequest, headerName));
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslHeaderNull() {
|
|
final String headerName = "myheader";
|
|
mockRequest.setHeader(headerName, null);
|
|
|
|
Assert.assertNull(valve.mygetHeader(mockRequest, headerName));
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslHeaderNullModHeader() {
|
|
final String headerName = "myheader";
|
|
final String nullModHeaderValue = "(null)";
|
|
mockRequest.setHeader(headerName, nullModHeaderValue);
|
|
|
|
Assert.assertNull(valve.mygetHeader(mockRequest, nullModHeaderValue));
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslHeaderNullName() throws Exception {
|
|
Assert.assertNull(valve.mygetHeader(mockRequest, null));
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslHeaderMultiples() throws Exception {
|
|
final String headerName = "myheader";
|
|
final String headerValue = "BASE64_HEADER_VALUE";
|
|
mockRequest.addHeader(headerName, headerValue);
|
|
mockRequest.addHeader(headerName, "anyway won't be found");
|
|
|
|
Assert.assertEquals(headerValue, valve.mygetHeader(mockRequest, headerName));
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslClientCertHeaderSingleSpace() throws Exception {
|
|
String singleSpaced = certificateSingleLine(" ");
|
|
mockRequest.setHeader(valve.getSslClientCertHeader(), singleSpaced);
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
assertCertificateParsed();
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslClientCertHeaderMultiSpace() throws Exception {
|
|
String singleSpaced = certificateSingleLine(" ");
|
|
mockRequest.setHeader(valve.getSslClientCertHeader(), singleSpaced);
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
assertCertificateParsed();
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslClientCertHeaderTab() throws Exception {
|
|
String singleSpaced = certificateSingleLine("\t");
|
|
mockRequest.setHeader(valve.getSslClientCertHeader(), singleSpaced);
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
assertCertificateParsed();
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslClientCertNull() throws Exception {
|
|
TesterLogValidationFilter f = TesterLogValidationFilter.add(null, "", null,
|
|
"org.apache.catalina.valves.SSLValve");
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
EasyMock.verify(mockNext);
|
|
Assert.assertNull(mockRequest.getAttribute(Globals.CERTIFICATES_ATTR));
|
|
Assert.assertEquals(0, f.getMessageCount());
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslClientCertShorter() throws Exception {
|
|
mockRequest.setHeader(valve.getSslClientCertHeader(), "shorter than hell");
|
|
|
|
TesterLogValidationFilter f = TesterLogValidationFilter.add(null, "", null,
|
|
"org.apache.catalina.valves.SSLValve");
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
EasyMock.verify(mockNext);
|
|
Assert.assertNull(mockRequest.getAttribute(Globals.CERTIFICATES_ATTR));
|
|
Assert.assertEquals(0, f.getMessageCount());
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslClientCertIgnoredBegin() throws Exception {
|
|
String[] linesBegin = Arrays.copyOf(CERTIFICATE_LINES, CERTIFICATE_LINES.length);
|
|
linesBegin[0] = "3fisjcme3kdsakasdfsadkafsd3";
|
|
String begin = certificateSingleLine(linesBegin, " ");
|
|
mockRequest.setHeader(valve.getSslClientCertHeader(), begin);
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
assertCertificateParsed();
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslClientCertBadFormat() throws Exception {
|
|
String[] linesDeleted = Arrays.copyOf(CERTIFICATE_LINES, CERTIFICATE_LINES.length / 2);
|
|
String deleted = certificateSingleLine(linesDeleted, " ");
|
|
mockRequest.setHeader(valve.getSslClientCertHeader(), deleted);
|
|
|
|
TesterLogValidationFilter f = TesterLogValidationFilter.add(Level.WARNING, null,
|
|
"java.security.cert.CertificateException", "org.apache.catalina.valves.SSLValve");
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
EasyMock.verify(mockNext);
|
|
Assert.assertNull(mockRequest.getAttribute(Globals.CERTIFICATES_ATTR));
|
|
Assert.assertEquals(1, f.getMessageCount());
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testClientCertProviderNotFound() throws Exception {
|
|
EasyMock.expect(mockRequest.getConnector().getProperty("clientCertProvider")).andStubReturn("wontBeFound");
|
|
EasyMock.replay(mockRequest.getConnector());
|
|
mockRequest.setHeader(valve.getSslClientCertHeader(), certificateSingleLine(" "));
|
|
|
|
TesterLogValidationFilter f = TesterLogValidationFilter.add(Level.SEVERE, null,
|
|
"java.security.NoSuchProviderException", "org.apache.catalina.valves.SSLValve");
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
Assert.assertNull(mockRequest.getAttribute(Globals.CERTIFICATES_ATTR));
|
|
Assert.assertEquals(1, f.getMessageCount());
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslCipherHeaderPresent() throws Exception {
|
|
String cipher = "ciphered-with";
|
|
mockRequest.setHeader(valve.getSslCipherHeader(), cipher);
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
Assert.assertEquals(cipher, mockRequest.getAttribute(Globals.CIPHER_SUITE_ATTR));
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslSessionIdHeaderPresent() throws Exception {
|
|
String session = "ssl-session";
|
|
mockRequest.setHeader(valve.getSslSessionIdHeader(), session);
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
Assert.assertEquals(session, mockRequest.getAttribute(Globals.SSL_SESSION_ID_ATTR));
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSslCipherUserKeySizeHeaderPresent() throws Exception {
|
|
Integer keySize = Integer.valueOf(452);
|
|
mockRequest.setHeader(valve.getSslCipherUserKeySizeHeader(), String.valueOf(keySize));
|
|
|
|
valve.invoke(mockRequest, null);
|
|
|
|
Assert.assertEquals(keySize, mockRequest.getAttribute(Globals.KEY_SIZE_ATTR));
|
|
}
|
|
|
|
|
|
@Test(expected = NumberFormatException.class)
|
|
public void testSslCipherUserKeySizeHeaderBadFormat() throws Exception {
|
|
mockRequest.setHeader(valve.getSslCipherUserKeySizeHeader(), "not-an-integer");
|
|
|
|
try {
|
|
valve.invoke(mockRequest, null);
|
|
} catch (NumberFormatException e) {
|
|
Assert.assertNull(mockRequest.getAttribute(Globals.KEY_SIZE_ATTR));
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
|
|
private static String certificateSingleLine(String[] lines, String separator) {
|
|
StringBuilder singleSpaced = new StringBuilder();
|
|
for (String current : lines) {
|
|
singleSpaced.append(current).append(separator);
|
|
}
|
|
singleSpaced.deleteCharAt(singleSpaced.length() - 1);
|
|
return singleSpaced.toString();
|
|
}
|
|
|
|
|
|
private static String certificateSingleLine(String separator) {
|
|
return certificateSingleLine(CERTIFICATE_LINES, separator);
|
|
}
|
|
|
|
|
|
private void assertCertificateParsed() throws Exception {
|
|
TesterLogValidationFilter f = TesterLogValidationFilter.add(null, "", null,
|
|
"org.apache.catalina.valves.SSLValve");
|
|
|
|
EasyMock.verify(mockNext);
|
|
|
|
X509Certificate[] certificates = (X509Certificate[]) mockRequest.getAttribute(Globals.CERTIFICATES_ATTR);
|
|
Assert.assertNotNull(certificates);
|
|
Assert.assertEquals(1, certificates.length);
|
|
Assert.assertNotNull(certificates[0]);
|
|
Assert.assertEquals(0, f.getMessageCount());
|
|
}
|
|
} |