Skip to content

Commit

Permalink
Merge pull request #5538 from eclipse/jetty-9.4.x-5531-ssl-dump-proto…
Browse files Browse the repository at this point in the history
…cols

Issue #5535 - Adding support for regex include/exclude of Protocols to SslContextFactory
  • Loading branch information
joakime committed Oct 30, 2020
2 parents dadd299 + 249cb02 commit 1f727cd
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 62 deletions.
Expand Up @@ -47,14 +47,12 @@
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;
Expand Down Expand Up @@ -140,7 +138,7 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
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<>();
Expand Down Expand Up @@ -526,6 +524,8 @@ public String[] getExcludeProtocols()
}

/**
* 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[])}
*/
Expand All @@ -536,15 +536,17 @@ public void setExcludeProtocols(String... protocols)
}

/**
* @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)
{
_excludeProtocols.addAll(Arrays.asList(protocol));
}

/**
* @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")
Expand All @@ -554,7 +556,9 @@ public String[] getIncludeProtocols()
}

/**
* @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)
Expand All @@ -564,7 +568,7 @@ public void setIncludeProtocols(String... protocols)
}

/**
* @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")
Expand All @@ -574,7 +578,7 @@ public String[] getExcludeCipherSuites()
}

/**
* 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[])}
Expand All @@ -586,6 +590,8 @@ public void setExcludeCipherSuites(String... cipherSuites)
}

/**
* 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)
Expand All @@ -594,7 +600,7 @@ public void addExcludeCipherSuites(String... cipher)
}

/**
* @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")
Expand All @@ -604,7 +610,7 @@ public String[] getIncludeCipherSuites()
}

/**
* 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[])}
Expand Down Expand Up @@ -1357,28 +1363,10 @@ protected PKIXBuilderParameters newPKIXBuilderParameters(KeyStore trustStore, Co
*/
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]);
}
Expand All @@ -1393,18 +1381,10 @@ public void selectProtocols(String[] enabledProtocols, String[] supportedProtoco
*/
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)
Expand All @@ -1417,39 +1397,58 @@ protected void selectCipherSuites(String[] enabledCipherSuites, String[] support
_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)
selected.addAll(Arrays.asList(enabled));
}
else
{
// process include patterns
for (String includedItem : included)
{
Matcher m = p.matcher(supportedCipherSuite);
if (m.matches())
Pattern pattern = Pattern.compile(includedItem);
boolean added = false;
for (String supportedItem : supported)
{
added = true;
selectedCiphers.add(supportedCipherSuite);
if (pattern.matcher(supportedItem).matches())
{
added = true;
selected.add(supportedItem);
}
}
if (!added)
LOG.info("No {} matching '{}' is supported", type, includedItem);
}
if (!added)
LOG.info("No Cipher matching '{}' is supported", cipherSuite);
}

// 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();
}
}
}

/**
Expand Down
Expand Up @@ -55,6 +55,7 @@
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;
Expand Down Expand Up @@ -95,6 +96,38 @@ public void testSLOTH() throws Exception
}
}

@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
{
Expand Down

0 comments on commit 1f727cd

Please sign in to comment.