Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x

This commit is contained in:
Joakim Erdfelt 2020-10-30 13:12:50 -05:00
commit 3aba60c31f
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
2 changed files with 132 additions and 100 deletions

View File

@ -47,14 +47,12 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.HostnameVerifier;
@ -142,7 +140,7 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
private final Set<String> _excludeProtocols = new LinkedHashSet<>();
private final Set<String> _includeProtocols = new LinkedHashSet<>();
private final Set<String> _excludeCipherSuites = new LinkedHashSet<>();
private final List<String> _includeCipherSuites = new ArrayList<>();
private final Set<String> _includeCipherSuites = new LinkedHashSet<>();
private final Map<String, X509> _aliasX509 = new HashMap<>();
private final Map<String, X509> _certHosts = new HashMap<>();
private final Map<String, X509> _certWilds = new HashMap<>();
@ -515,6 +513,8 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
}
/**
* You can either use the exact Protocol name or a a regular expression.
*
* @param protocols The array of protocol names to exclude from
* {@link SSLEngine#setEnabledProtocols(String[])}
*/
@ -525,7 +525,9 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
}
/**
* @param protocol Protocol names to add to {@link SSLEngine#setEnabledProtocols(String[])}
* You can either use the exact Protocol name or a a regular expression.
*
* @param protocol Protocol name patterns to add to {@link SSLEngine#setEnabledProtocols(String[])}
*/
public void addExcludeProtocols(String... protocol)
{
@ -533,7 +535,7 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
}
/**
* @return The array of protocol names to include in
* @return The array of protocol name patterns to include in
* {@link SSLEngine#setEnabledProtocols(String[])}
*/
@ManagedAttribute("The included TLS protocols")
@ -543,7 +545,9 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
}
/**
* @param protocols The array of protocol names to include in
* You can either use the exact Protocol name or a a regular expression.
*
* @param protocols The array of protocol name patterns to include in
* {@link SSLEngine#setEnabledProtocols(String[])}
*/
public void setIncludeProtocols(String... protocols)
@ -553,7 +557,7 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
}
/**
* @return The array of cipher suite names to exclude from
* @return The array of cipher suite name patterns to exclude from
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
@ManagedAttribute("The excluded cipher suites")
@ -563,7 +567,7 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
}
/**
* You can either use the exact cipher suite name or a a regular expression.
* You can either use the exact Cipher suite name or a a regular expression.
*
* @param cipherSuites The array of cipher suite names to exclude from
* {@link SSLEngine#setEnabledCipherSuites(String[])}
@ -575,6 +579,8 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
}
/**
* You can either use the exact Cipher suite name or a a regular expression.
*
* @param cipher Cipher names to add to {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public void addExcludeCipherSuites(String... cipher)
@ -583,7 +589,7 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
}
/**
* @return The array of cipher suite names to include in
* @return The array of Cipher suite names to include in
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
@ManagedAttribute("The included cipher suites")
@ -593,7 +599,7 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
}
/**
* You can either use the exact cipher suite name or a a regular expression.
* You can either use the exact Cipher suite name or a a regular expression.
*
* @param cipherSuites The array of cipher suite names to include in
* {@link SSLEngine#setEnabledCipherSuites(String[])}
@ -1246,28 +1252,10 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
*/
public void selectProtocols(String[] enabledProtocols, String[] supportedProtocols)
{
Set<String> selectedProtocols = new LinkedHashSet<>();
// Set the starting protocols - either from the included or enabled list
if (!_includeProtocols.isEmpty())
{
// Use only the supported included protocols
for (String protocol : _includeProtocols)
{
if (Arrays.asList(supportedProtocols).contains(protocol))
selectedProtocols.add(protocol);
else
LOG.info("Protocol {} not supported in {}", protocol, Arrays.asList(supportedProtocols));
}
}
else
selectedProtocols.addAll(Arrays.asList(enabledProtocols));
// Remove any excluded protocols
selectedProtocols.removeAll(_excludeProtocols);
List<String> selectedProtocols = processIncludeExcludePatterns("Protocols", enabledProtocols, supportedProtocols, _includeProtocols, _excludeProtocols);
if (selectedProtocols.isEmpty())
LOG.warn("No selected protocols from {}", Arrays.asList(supportedProtocols));
LOG.warn("No selected Protocols from {}", Arrays.asList(supportedProtocols));
_selectedProtocols = selectedProtocols.toArray(new String[0]);
}
@ -1282,18 +1270,10 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
*/
protected void selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites)
{
List<String> selectedCiphers = new ArrayList<>();
// Set the starting ciphers - either from the included or enabled list
if (_includeCipherSuites.isEmpty())
selectedCiphers.addAll(Arrays.asList(enabledCipherSuites));
else
processIncludeCipherSuites(supportedCipherSuites, selectedCiphers);
removeExcludedCipherSuites(selectedCiphers);
List<String> selectedCiphers = processIncludeExcludePatterns("Cipher Suite", enabledCipherSuites, supportedCipherSuites, _includeCipherSuites, _excludeCipherSuites);
if (selectedCiphers.isEmpty())
LOG.warn("No supported ciphers from {}", Arrays.asList(supportedCipherSuites));
LOG.warn("No supported Cipher Suite from {}", Arrays.asList(supportedCipherSuites));
Comparator<String> comparator = getCipherComparator();
if (comparator != null)
@ -1306,39 +1286,58 @@ public abstract class SslContextFactory extends AbstractLifeCycle implements Dum
_selectedCipherSuites = selectedCiphers.toArray(new String[0]);
}
protected void processIncludeCipherSuites(String[] supportedCipherSuites, List<String> selectedCiphers)
private List<String> processIncludeExcludePatterns(String type, String[] enabled, String[] supported, Set<String> included, Set<String> excluded)
{
for (String cipherSuite : _includeCipherSuites)
List<String> selected = new ArrayList<>();
// Set the starting list - either from the included or enabled list
if (included.isEmpty())
{
Pattern p = Pattern.compile(cipherSuite);
boolean added = false;
for (String supportedCipherSuite : supportedCipherSuites)
{
Matcher m = p.matcher(supportedCipherSuite);
if (m.matches())
{
added = true;
selectedCiphers.add(supportedCipherSuite);
}
}
if (!added)
LOG.info("No Cipher matching '{}' is supported", cipherSuite);
selected.addAll(Arrays.asList(enabled));
}
else
{
// process include patterns
for (String includedItem : included)
{
Pattern pattern = Pattern.compile(includedItem);
boolean added = false;
for (String supportedItem : supported)
{
if (pattern.matcher(supportedItem).matches())
{
added = true;
selected.add(supportedItem);
}
}
if (!added)
LOG.info("No {} matching '{}' is supported", type, includedItem);
}
}
// process exclude patterns
for (String excludedItem : excluded)
{
Pattern pattern = Pattern.compile(excludedItem);
selected.removeIf(selectedItem -> pattern.matcher(selectedItem).matches());
}
return selected;
}
/**
* @deprecated no replacement
*/
@Deprecated
protected void processIncludeCipherSuites(String[] supportedCipherSuites, List<String> selectedCiphers)
{
}
/**
* @deprecated no replacement
*/
@Deprecated
protected void removeExcludedCipherSuites(List<String> selectedCiphers)
{
for (String excludeCipherSuite : _excludeCipherSuites)
{
Pattern excludeCipherPattern = Pattern.compile(excludeCipherSuite);
for (Iterator<String> i = selectedCiphers.iterator(); i.hasNext(); )
{
String selectedCipherSuite = i.next();
Matcher m = excludeCipherPattern.matcher(selectedCipherSuite);
if (m.matches())
i.remove();
}
}
}
/**

View File

@ -30,6 +30,7 @@ import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -46,7 +47,6 @@ import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@ -55,6 +55,7 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItemInArray;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.matchesRegex;
import static org.hamcrest.Matchers.not;
@ -69,25 +70,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class SslContextFactoryTest
{
private SslContextFactory cf;
@BeforeEach
public void setUp() throws Exception
{
cf = new SslContextFactory.Server();
java.security.cert.CertPathBuilder certPathBuilder = java.security.cert.CertPathBuilder.getInstance("PKIX");
java.security.cert.PKIXRevocationChecker revocationChecker = (java.security.cert.PKIXRevocationChecker)certPathBuilder.getRevocationChecker();
revocationChecker.setOptions(java.util.EnumSet.of(
java.security.cert.PKIXRevocationChecker.Option.valueOf("PREFER_CRLS"),
java.security.cert.PKIXRevocationChecker.Option.valueOf("SOFT_FAIL"),
java.security.cert.PKIXRevocationChecker.Option.valueOf("NO_FALLBACK")));
cf.setPkixCertPathChecker(revocationChecker);
}
@Test
public void testSLOTH() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
cf.setKeyStorePassword("storepwd");
cf.start();
@ -95,9 +81,13 @@ public class SslContextFactoryTest
// cf.dump(System.out, "");
List<SslSelectionDump> dumps = cf.selectionDump();
SslSelectionDump cipherDump = dumps.stream()
Optional<SslSelectionDump> cipherSuiteDumpOpt = dumps.stream()
.filter((dump) -> dump.type.contains("Cipher Suite"))
.findFirst().get();
.findFirst();
assertTrue(cipherSuiteDumpOpt.isPresent(), "Cipher Suite dump section should exist");
SslSelectionDump cipherDump = cipherSuiteDumpOpt.get();
for (String enabledCipher : cipherDump.enabled)
{
@ -105,9 +95,42 @@ public class SslContextFactoryTest
}
}
@Test
public void testDumpExcludedProtocols() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
cf.setExcludeProtocols("TLSv1\\.?[01]?");
cf.start();
// Confirm behavior in engine
assertThat(cf.newSSLEngine().getEnabledProtocols(), not(hasItemInArray("TLSv1.1")));
assertThat(cf.newSSLEngine().getEnabledProtocols(), not(hasItemInArray("TLSv1")));
// Confirm output in dump
List<SslSelectionDump> dumps = cf.selectionDump();
Optional<SslSelectionDump> protocolDumpOpt = dumps.stream()
.filter((dump) -> dump.type.contains("Protocol"))
.findFirst();
assertTrue(protocolDumpOpt.isPresent(), "Protocol dump section should exist");
SslSelectionDump protocolDump = protocolDumpOpt.get();
long countTls11Enabled = protocolDump.enabled.stream().filter((t) -> t.contains("TLSv1.1")).count();
long countTls11Disabled = protocolDump.disabled.stream().filter((t) -> t.contains("TLSv1.1")).count();
assertThat("Enabled Protocols TLSv1.1 count", countTls11Enabled, is(0L));
assertThat("Disabled Protocols TLSv1.1 count", countTls11Disabled, is(1L));
// Uncomment to show dump in console.
// cf.dump(System.out, "");
}
@Test
public void testDumpIncludeTlsRsa() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
cf.setKeyStorePassword("storepwd");
cf.setIncludeCipherSuites("TLS_RSA_.*");
cf.setExcludeCipherSuites("BOGUS"); // just to not exclude anything
@ -124,9 +147,15 @@ public class SslContextFactoryTest
.collect(Collectors.toList());
List<String> selectedSuites = Arrays.asList(cf.getSelectedCipherSuites());
SslSelectionDump cipherDump = dumps.stream()
Optional<SslSelectionDump> cipherSuiteDumpOpt = dumps.stream()
.filter((dump) -> dump.type.contains("Cipher Suite"))
.findFirst().get();
.findFirst();
assertTrue(cipherSuiteDumpOpt.isPresent(), "Cipher Suite dump section should exist");
SslSelectionDump cipherDump = cipherSuiteDumpOpt.get();
assertThat("Dump Enabled List size is equal to selected list size", cipherDump.enabled.size(), is(selectedSuites.size()));
for (String expectedCipherSuite : tlsRsaSuites)
@ -139,6 +168,7 @@ public class SslContextFactoryTest
@Test
public void testNoTsFileKs() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
cf.setKeyStorePassword("storepwd");
cf.start();
@ -149,6 +179,7 @@ public class SslContextFactoryTest
@Test
public void testNoTsSetKs() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
KeyStore ks = KeyStore.getInstance("PKCS12");
try (InputStream keystoreInputStream = this.getClass().getResourceAsStream("keystore.p12"))
{
@ -164,13 +195,7 @@ public class SslContextFactoryTest
@Test
public void testNoTsNoKs() throws Exception
{
cf.start();
assertNotNull(cf.getSslContext());
}
@Test
public void testTrustAll() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
cf.start();
assertNotNull(cf.getSslContext());
}
@ -178,6 +203,7 @@ public class SslContextFactoryTest
@Test
public void testNoTsResourceKs() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
Resource keystoreResource = Resource.newSystemResource("keystore.p12");
cf.setKeyStoreResource(keystoreResource);
@ -193,6 +219,7 @@ public class SslContextFactoryTest
@Test
public void testResourceTsResourceKs() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
Resource keystoreResource = Resource.newSystemResource("keystore.p12");
Resource truststoreResource = Resource.newSystemResource("keystore.p12");
@ -209,6 +236,7 @@ public class SslContextFactoryTest
@Test
public void testResourceTsWrongPWResourceKs() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
Resource keystoreResource = Resource.newSystemResource("keystore.p12");
Resource truststoreResource = Resource.newSystemResource("keystore.p12");
@ -219,7 +247,7 @@ public class SslContextFactoryTest
try (StacklessLogging ignore = new StacklessLogging(AbstractLifeCycle.class))
{
IOException x = assertThrows(IOException.class, () -> cf.start());
IOException x = assertThrows(IOException.class, cf::start);
assertThat(x.getMessage(), containsString("password was incorrect"));
}
}
@ -227,6 +255,7 @@ public class SslContextFactoryTest
@Test
public void testNoKeyConfig()
{
SslContextFactory.Server cf = new SslContextFactory.Server();
try (StacklessLogging ignore = new StacklessLogging(AbstractLifeCycle.class))
{
IllegalStateException x = assertThrows(IllegalStateException.class, () ->
@ -241,6 +270,7 @@ public class SslContextFactoryTest
@Test
public void testSetExcludeCipherSuitesRegex() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
cf.setExcludeCipherSuites(".*RC4.*");
cf.start();
SSLEngine sslEngine = cf.newSSLEngine();
@ -255,6 +285,7 @@ public class SslContextFactoryTest
@Test
public void testSetIncludeCipherSuitesRegex() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
cf.setIncludeCipherSuites(".*ECDHE.*", ".*WIBBLE.*");
cf.start();
@ -270,6 +301,7 @@ public class SslContextFactoryTest
@Test
public void testProtocolAndCipherSettingsAreNPESafe()
{
SslContextFactory.Server cf = new SslContextFactory.Server();
assertNotNull(cf.getExcludeProtocols());
assertNotNull(cf.getIncludeProtocols());
assertNotNull(cf.getExcludeCipherSuites());
@ -279,6 +311,7 @@ public class SslContextFactoryTest
@Test
public void testSNICertificates() throws Exception
{
SslContextFactory.Server cf = new SslContextFactory.Server();
Resource keystoreResource = Resource.newSystemResource("snikeystore.p12");
cf.setKeyStoreResource(keystoreResource);
@ -319,9 +352,9 @@ public class SslContextFactoryTest
@Test
public void testNonDefaultKeyStoreTypeUsedForTrustStore() throws Exception
{
cf = new SslContextFactory.Server();
cf.setKeyStoreResource(Resource.newSystemResource("keystore.jks"));
cf.setKeyStoreType("jks");
SslContextFactory.Server cf = new SslContextFactory.Server();
cf.setKeyStoreResource(Resource.newSystemResource("keystore.p12"));
cf.setKeyStoreType("pkcs12");
cf.setKeyStorePassword("storepwd");
cf.start();
cf.stop();
@ -337,7 +370,7 @@ public class SslContextFactoryTest
@Test
public void testClientSslContextFactory() throws Exception
{
cf = new SslContextFactory.Client();
SslContextFactory.Client cf = new SslContextFactory.Client();
cf.start();
assertEquals("HTTPS", cf.getEndpointIdentificationAlgorithm());
@ -346,7 +379,7 @@ public class SslContextFactoryTest
@Test
public void testServerSslContextFactory() throws Exception
{
cf = new SslContextFactory.Server();
SslContextFactory.Server cf = new SslContextFactory.Server();
cf.start();
assertNull(cf.getEndpointIdentificationAlgorithm());