Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #5535 - Adding support for regex include/exclude of Protocols to SslContextFactory #5538

Merged
merged 4 commits into from Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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