diff --git a/README.txt b/README.txt index da7815ea..7bd3c50b 100644 --- a/README.txt +++ b/README.txt @@ -22,3 +22,38 @@ Please feel free to contact us. We welcome feedback of any kind! Contact: support@trilead.com or go to the public forum at http://www.trilead.com Zurich, March 2008 + +## Algorithm filters + +The algorithm filters can be used to restrict the algorithms that are used by the library. +The filters will remove the algorithms you specify from the SSH algorithms negotiation between client and server. +The library supports the following algorithm filters: +* Kex (Key Exchange) algorithm filter +* Host Key algorithm filter +* Encryption algorithm filter +* MAC algorithm filter + +To filter algorithms enable the filters using the following System properties: + + +* com.trilead.ssh2.jenkins.FilterKexAlgorithms.enabled (default: true) +* com.trilead.ssh2.jenkins.FilterHostKeyAlgorithms.enabled (default: true) +* com.trilead.ssh2.jenkins.FilterMacAlgorithms.enabled (default: true) +* com.trilead.ssh2.jenkins.FilterEncrytionAlgorithms.enabled (default: true) + +The algorithms are specified as a comma separated list of algorithm names. +To configure the algorithm filters, use the following System properties: + +* com.trilead.ssh2.jenkins.FilterKexAlgorithms.algorithms +* com.trilead.ssh2.jenkins.FilterHostKeyAlgorithms.algorithms +* com.trilead.ssh2.jenkins.FilterMacAlgorithms.algorithms +* com.trilead.ssh2.jenkins.FilterEncrytionAlgorithms.algorithms + +Example +```bash +java -Dcom.trilead.ssh2.jenkins.FilterKexAlgorithms.enabled=true \ + -Dcom.trilead.ssh2.jenkins.FilterKexAlgorithms.algorithms=diffie-hellman-group-exchange-sha256 \ + -jar jenkins.war +``` + +To check the algorithms filtered by the library, check the `Filter*` classes at the package [`com.trilead.ssh2.jenkins`](src/com/trilead/ssh2/jenkins). \ No newline at end of file diff --git a/src/com/trilead/ssh2/crypto/CryptoWishList.java b/src/com/trilead/ssh2/crypto/CryptoWishList.java index 0430fd0f..d4f32dc5 100644 --- a/src/com/trilead/ssh2/crypto/CryptoWishList.java +++ b/src/com/trilead/ssh2/crypto/CryptoWishList.java @@ -3,6 +3,10 @@ import com.trilead.ssh2.crypto.cipher.BlockCipherFactory; import com.trilead.ssh2.crypto.digest.MessageMac; +import com.trilead.ssh2.jenkins.FilterEncrytionAlgorithms; +import com.trilead.ssh2.jenkins.FilterHostKeyAlgorithms; +import com.trilead.ssh2.jenkins.FilterKexAlgorithms; +import com.trilead.ssh2.jenkins.FilterMacAlgorithms; import com.trilead.ssh2.transport.KexManager; @@ -14,10 +18,10 @@ */ public class CryptoWishList { - public String[] kexAlgorithms = KexManager.getDefaultKexAlgorithmList(); - public String[] serverHostKeyAlgorithms = KexManager.getDefaultServerHostkeyAlgorithmList(); - public String[] c2s_enc_algos = BlockCipherFactory.getDefaultCipherList(); - public String[] s2c_enc_algos = BlockCipherFactory.getDefaultCipherList(); - public String[] c2s_mac_algos = MessageMac.getMacs(); - public String[] s2c_mac_algos = MessageMac.getMacs(); -} + public String[] kexAlgorithms = FilterKexAlgorithms.filter(KexManager.getDefaultKexAlgorithmList()); + public String[] serverHostKeyAlgorithms = FilterHostKeyAlgorithms.filter(KexManager.getDefaultServerHostkeyAlgorithmList()); + public String[] c2s_enc_algos = FilterEncrytionAlgorithms.filter(BlockCipherFactory.getDefaultCipherList()); + public String[] s2c_enc_algos = FilterEncrytionAlgorithms.filter(BlockCipherFactory.getDefaultCipherList()); + public String[] c2s_mac_algos = FilterMacAlgorithms.filter(MessageMac.getMacs()); + public String[] s2c_mac_algos = FilterMacAlgorithms.filter(MessageMac.getMacs()); +} \ No newline at end of file diff --git a/src/com/trilead/ssh2/jenkins/FilterAlgorithms.java b/src/com/trilead/ssh2/jenkins/FilterAlgorithms.java new file mode 100644 index 00000000..1494b105 --- /dev/null +++ b/src/com/trilead/ssh2/jenkins/FilterAlgorithms.java @@ -0,0 +1,105 @@ +package com.trilead.ssh2.jenkins; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.trilead.ssh2.log.Logger; + +/** + * Class for filtering algorithms. + * The reason for this filter is that some algorithms have security issues. + * The filter can be disabled by setting the system property {@link #isEnabledFilterProperty} to false. + * The list of algorithms to filter can be set by setting the system property {@link #filteredAlgorithmsProperty}. + */ +public class FilterAlgorithms { + /** + * The system property suffix to enable/disable the filter. + */ + private static final String ALGORITHMS = ".algorithms"; + /** + * The system property suffix to set the list of algorithms to filter. + */ + private static final String ENABLED = ".enabled"; + /** + * The logger. + */ + private static final Logger LOGGER = Logger.getLogger(FilterAlgorithms.class); + /** + * The system property to enable/disable the filter. + */ + private final String isEnabledFilterProperty; + /** + * The system property to set the list of algorithms to filter. + */ + private final String filteredAlgorithmsProperty; + + /** + * The list of algorithms to filter. + */ + private List filteredAlgorithms = new ArrayList<>(); + + /** + * Constructor. + * @param clazz the class name used for the system properties + * @param filteredAlgorithms the list of algorithms to filter + */ + public FilterAlgorithms(String clazz, List filteredAlgorithms) { + this.filteredAlgorithms = filteredAlgorithms; + this.isEnabledFilterProperty = clazz + ENABLED; + this.filteredAlgorithmsProperty = clazz + ALGORITHMS; + } + + /** + * Filter algorithms. + * @param algorithms the algorithms to filter + * @return the filtered algorithms + */ + public String[] filter(String[] algorithms) { + String[] ret = Collections.emptySet().toArray(new String[0]); + if (algorithms != null) { + if (!isEnabled()) { + LOGGER.log(20, "Algorithms filter is disabled"); + ret = algorithms; + } else { + ret = Arrays.stream(algorithms) + .filter(x -> !getFilteredAlgorithms().contains(x)) + .toArray(String[]::new); + } + } else { + LOGGER.log(20, "Algorithms is null"); + } + return ret; + } + + /** + * Check if the filter is enabled. + * @return true if the filter is enabled + */ + private boolean isEnabled() { + return Boolean.parseBoolean(System.getProperty(isEnabledFilterProperty, "true")); + } + + /** + * Get the list of algorithms to filter. + * @return the list of algorithms to filter + */ + private List getFilteredAlgorithms() { + List ret = new ArrayList<>(); + if (System.getProperty(filteredAlgorithmsProperty) != null && !System.getProperty(filteredAlgorithmsProperty).isEmpty()) { + ret = Arrays.asList(System.getProperty(filteredAlgorithmsProperty).split(",")); + } else { + ret = filteredAlgorithms; + } + return ret; + } + + public String getIsEnabledFilterProperty() { + return isEnabledFilterProperty; + } + + public String getFilteredAlgorithmsProperty() { + return filteredAlgorithmsProperty; + } +} diff --git a/src/com/trilead/ssh2/jenkins/FilterEncrytionAlgorithms.java b/src/com/trilead/ssh2/jenkins/FilterEncrytionAlgorithms.java new file mode 100644 index 00000000..0174b47f --- /dev/null +++ b/src/com/trilead/ssh2/jenkins/FilterEncrytionAlgorithms.java @@ -0,0 +1,33 @@ +package com.trilead.ssh2.jenkins; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import com.trilead.ssh2.log.Logger; + +/** + * Filter encryption algorithms. + * The reason for this filter is that some algorithms have security issues. + * The filter can be disabled by setting the system property + * com.trilead.ssh2.jenkins.FilterEncrytionAlgorithms.enabled to false. + * The list of algorithms to filter can be set by setting the system property + * com.trilead.ssh2.jenkins.FilterEncrytionAlgorithms.algorithms (e.g. type01,type02,type03). + */ +public class FilterEncrytionAlgorithms { + /* + * The list of algorithms to filter by default. + */ + private static final List filteredAlgorithms = Collections.emptyList(); + + /** + * Filter algorithms. + * @param algorithms The algorithms to filter. + * @return The filtered algorithms. + */ + public static String[] filter(String[] algorithms) { + FilterAlgorithms filter = new FilterAlgorithms(FilterEncrytionAlgorithms.class.getName(), filteredAlgorithms); + return filter.filter(algorithms); + } +} diff --git a/src/com/trilead/ssh2/jenkins/FilterHostKeyAlgorithms.java b/src/com/trilead/ssh2/jenkins/FilterHostKeyAlgorithms.java new file mode 100644 index 00000000..8aa12f9d --- /dev/null +++ b/src/com/trilead/ssh2/jenkins/FilterHostKeyAlgorithms.java @@ -0,0 +1,32 @@ +package com.trilead.ssh2.jenkins; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.trilead.ssh2.log.Logger; + +/** + * Filter host key algorithms. + * The reason for this filter is that some algorithms have security issues. + * The filter can be disabled by setting the system property + * com.trilead.ssh2.jenkins.FilterHostKeyAlgorithms.enabled to false. + * The list of algorithms to filter can be set by setting the system property + * com.trilead.ssh2.jenkins.FilterHostKeyAlgorithms.algorithms (e.g. type01,type02,type03). + */ +public class FilterHostKeyAlgorithms { + /* + * The list of algorithms to filter. + */ + private static final List filteredAlgorithms = Collections.emptyList(); + + /** + * Filter algorithms. + * @param algorithms The algorithms to filter. + * @return The filtered algorithms. + */ + public static String[] filter(String[] algorithms) { + FilterAlgorithms filter = new FilterAlgorithms(FilterHostKeyAlgorithms.class.getName(), filteredAlgorithms); + return filter.filter(algorithms); + } +} diff --git a/src/com/trilead/ssh2/jenkins/FilterKexAlgorithms.java b/src/com/trilead/ssh2/jenkins/FilterKexAlgorithms.java new file mode 100644 index 00000000..da81c031 --- /dev/null +++ b/src/com/trilead/ssh2/jenkins/FilterKexAlgorithms.java @@ -0,0 +1,34 @@ +package com.trilead.ssh2.jenkins; + +import java.util.ArrayList; +import java.util.List; + +import com.trilead.ssh2.log.Logger; + +/** + * Filter KEX algorithms. + * The reason for this filter is that some algorithms have security issues. + * The filter can be disabled by setting the system property + * com.trilead.ssh2.jenkins.FilterKexAlgorithms.enabled to false. + * The list of algorithms to filter can be set by setting the system property + * com.trilead.ssh2.jenkins.FilterKexAlgorithms.algorithms (e.g. type01,type02,type03). + */ +public class FilterKexAlgorithms { + /* + * The list of algorithms to filter. + */ + private static final List filteredAlgorithms = new ArrayList<>( + List.of( + // Terrapin attack see https://en.wikipedia.org/wiki/Terrapin_attack + "chacha20-poly1305@openssh.com")); + + /** + * Filter algorithms. + * @param algorithms The algorithms to filter. + * @return The filtered algorithms. + */ + public static String[] filter(String[] algorithms) { + FilterAlgorithms filter = new FilterAlgorithms(FilterKexAlgorithms.class.getName(), filteredAlgorithms); + return filter.filter(algorithms); + } +} diff --git a/src/com/trilead/ssh2/jenkins/FilterMacAlgorithms.java b/src/com/trilead/ssh2/jenkins/FilterMacAlgorithms.java new file mode 100644 index 00000000..18c7d1b1 --- /dev/null +++ b/src/com/trilead/ssh2/jenkins/FilterMacAlgorithms.java @@ -0,0 +1,36 @@ +package com.trilead.ssh2.jenkins; + +import java.util.ArrayList; +import java.util.List; + +import com.trilead.ssh2.log.Logger; + +/** + * Filter host key algorithms. + * The reason for this filter is that some algorithms have security issues. + * The filter can be disabled by setting the system property + * com.trilead.ssh2.jenkins.FilterHostKeyAlgorithms.enabled to false. + * The list of algorithms to filter can be set by setting the system property + * com.trilead.ssh2.jenkins.FilterHostKeyAlgorithms.algorithms (e.g. type01,type02,type03). + */ +public class FilterMacAlgorithms { + /* + * The list of algorithms to filter. + */ + private static final List filteredAlgorithms = new ArrayList<>( + List.of( + // Terrapin attack see https://en.wikipedia.org/wiki/Terrapin_attack + "hmac-sha2-512-etm@openssh.com", + // Terrapin attack see https://en.wikipedia.org/wiki/Terrapin_attack + "hmac-sha2-256-etm@openssh.com")); + + /** + * Filter algorithms. + * @param algorithms The algorithms to filter. + * @return The filtered algorithms. + */ + public static String[] filter(String[] algorithms) { + FilterAlgorithms filter = new FilterAlgorithms(FilterMacAlgorithms.class.getName(), filteredAlgorithms); + return filter.filter(algorithms); + } +} diff --git a/src/com/trilead/ssh2/transport/KexManager.java b/src/com/trilead/ssh2/transport/KexManager.java index f9ed387b..6b030468 100644 --- a/src/com/trilead/ssh2/transport/KexManager.java +++ b/src/com/trilead/ssh2/transport/KexManager.java @@ -375,6 +375,7 @@ public static String[] getDefaultKexAlgorithmList() "diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1","ecdh-sha2-nistp256","ecdh-sha2-nistp384","ecdh-sha2-nistp521","curve25519-sha256","curve25519-sha256@libssh.org" }; } + // FIXME this code is not used, the check it makes does not match the implementation in other places. public static void checkKexAlgorithmList(String[] algos) { for (String algo : algos) { diff --git a/test/com/trilead/ssh2/jenkins/FilterAlgorithmsTest.java b/test/com/trilead/ssh2/jenkins/FilterAlgorithmsTest.java new file mode 100644 index 00000000..3bd2c1c5 --- /dev/null +++ b/test/com/trilead/ssh2/jenkins/FilterAlgorithmsTest.java @@ -0,0 +1,74 @@ +package com.trilead.ssh2.jenkins; + +import static org.junit.Assert.assertArrayEquals; + +import java.util.Arrays; + +import org.junit.After; +import org.junit.Test; + +public class FilterAlgorithmsTest { + + @After + public void setUp() { + System.setProperty("foo.enabled", "true"); + System.setProperty("foo.algorithms", ""); + } + + @Test + public void testFilter() { + String[] kexAlgorithms = {"algorithm0", "algorithm2", "algorithm3"}; + String[] expected = {"algorithm2", "algorithm3"}; + String[] filteredAlgorithms = {"algorithm0"}; + FilterAlgorithms filter = new FilterAlgorithms("foo",Arrays.asList(filteredAlgorithms)); + + assertArrayEquals(expected, filter.filter(kexAlgorithms)); + } + + @Test + public void testFilterWithNull() { + String[] expected = {}; + FilterAlgorithms filter = new FilterAlgorithms("foo",null); + + assertArrayEquals(expected, filter.filter(null)); + } + + @Test + public void testFilterWithEmptyArray() { + String[] kexAlgorithms = {}; + String[] expected = {}; + FilterAlgorithms filter = new FilterAlgorithms("foo", Arrays.asList(kexAlgorithms)); + + assertArrayEquals(expected, filter.filter(kexAlgorithms)); + } + + @Test + public void testDisabledFilter() { + String[] kexAlgorithms = {"algorithm0", "algorithm2", "algorithm3"}; + FilterAlgorithms filter = new FilterAlgorithms("foo", Arrays.asList(kexAlgorithms)); + System.setProperty(filter.getIsEnabledFilterProperty(), "false"); + + assertArrayEquals(kexAlgorithms, filter.filter(kexAlgorithms)); + } + + @Test + public void testFilterWithEmptyList() { + String[] kexAlgorithms = {"algorithm0", "algorithm2", "algorithm3"}; + String[] expected = {}; + FilterAlgorithms filter = new FilterAlgorithms("foo", Arrays.asList(kexAlgorithms)); + System.setProperty(filter.getFilteredAlgorithmsProperty(), ""); + + assertArrayEquals(expected, filter.filter(kexAlgorithms)); + } + + @Test + public void testFilterWithCustomList() { + String[] kexAlgorithms = {"algorithm0", "algorithm2", "algorithm3"}; + String[] expected = {"algorithm0"}; + FilterAlgorithms filter = new FilterAlgorithms("foo", Arrays.asList(kexAlgorithms)); + System.setProperty(filter.getFilteredAlgorithmsProperty(), "algorithm2,algorithm3"); + + assertArrayEquals(expected, filter.filter(kexAlgorithms)); + } + +} \ No newline at end of file