Skip to content

Commit

Permalink
limit Diffie-Hellman Group Exchange prime sizes
Browse files Browse the repository at this point in the history
  • Loading branch information
ggrandes committed Jun 28, 2020
1 parent f091d64 commit d0ac8d7
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 0 deletions.
82 changes: 82 additions & 0 deletions src/main/java/org/javastack/sftpserver/ModuliFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.javastack.sftpserver;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
* @see org.apache.sshd.server.kex.Moduli
*/
public class ModuliFilter {
public static final int MODULI_TYPE_SAFE = 2;
public static final int MODULI_TESTS_COMPOSITE = 0x01;

public static ArrayList<String> filterModuli(final URL url, final int minSize, final int maxSize)
throws IOException {
final ArrayList<String> parsed = new ArrayList<>();
try (BufferedReader r = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
for (String line = r.readLine(); line != null; line = r.readLine()) {
line = line.trim();
if (line.isEmpty()) {
continue;
}

if (line.startsWith("#")) {
continue;
}

String[] parts = line.split("\\s+");
// Ensure valid line
if (parts.length != 7) {
continue;
}

// Discard moduli types which are not safe
int type = Integer.parseInt(parts[1]);
if (type != MODULI_TYPE_SAFE) {
continue;
}

// Discard untested moduli
int tests = Integer.parseInt(parts[2]);
if (((tests & MODULI_TESTS_COMPOSITE) != 0) || ((tests & ~MODULI_TESTS_COMPOSITE) == 0)) {
continue;
}

// Discard untried
int tries = Integer.parseInt(parts[3]);
if (tries == 0) {
continue;
}

// Discard unwanted sizes
int size = Integer.parseInt(parts[4]);
if ((size < minSize) || (size > maxSize)) {
continue;
}

parsed.add(line);
}

return parsed;
}
}

public static void writeModuli(final File file, final List<String> lines) throws IOException {
try (BufferedWriter r = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
for (final String str : lines) {
r.write(str);
r.newLine();
}
}
}
}
73 changes: 73 additions & 0 deletions src/main/java/org/javastack/sftpserver/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.Path;
Expand Down Expand Up @@ -64,6 +65,7 @@
import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.channel.ChannelSessionFactory;
import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.kex.Moduli;
import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.scp.ScpCommandFactory;
Expand Down Expand Up @@ -244,6 +246,56 @@ private void hackVersion() {
PropertyResolverUtils.updateProperty(sshd, ServerFactoryManager.SERVER_IDENTIFICATION, "SSHD");
}

/**
* Filter the moduli file contains prime numbers and generators used by
* Diffie-Hellman Group Exchange.
*
* @see org.apache.sshd.common.kex.BuiltinDHFactories#dhgex256
* diffie-hellman-group-exchange-sha256
* @see org.apache.sshd.common.kex.BuiltinDHFactories#dhgex
* diffie-hellman-group-exchange-sha1
* @see org.apache.sshd.server.kex.DHGEXServer
* @see org.apache.sshd.server.kex.Moduli
* @see http://manpages.ubuntu.com/manpages/focal/man5/moduli.5.html
*/
private void hackModuliDHGEX() {
URL srcModuli = null;
final File sysLinuxModuli = new File("/etc/ssh/moduli");
if (sysLinuxModuli.canRead()) {
try {
srcModuli = sysLinuxModuli.toURI().toURL();
LOG.info("Linux moduli file: " + sysLinuxModuli);
} catch (IOException e) {
}
} else {
final String moduliPath = Moduli.INTERNAL_MODULI_RESPATH;
srcModuli = Moduli.class.getResource(moduliPath);
if (srcModuli == null) {
LOG.warn("Missing internal moduli file: " + moduliPath);
}
}
if (srcModuli != null) {
final File newModuli = new File(System.getProperty("java.io.tmpdir", "/tmp/"), "moduli.sftpd");
if (!newModuli.exists() // create
|| (newModuli.length() <= 0) // empty
|| (System.currentTimeMillis() - newModuli.lastModified() > TimeUnit.DAYS.toMillis(1))) { // 1day
try {
LOG.info("Filtering moduli file:" + srcModuli.toExternalForm());
final List<String> data = ModuliFilter.filterModuli(srcModuli, //
db.getMinSizeDHGEX(), db.getMaxSizeDHGEX());
ModuliFilter.writeModuli(newModuli, data);
} catch (IOException e) {
LOG.error("Error filtering moduli: " + e, e);
}
}
if ((newModuli != null) && newModuli.canRead() && (newModuli.length() > 0)) {
LOG.warn("Using moduli file: " + newModuli);
PropertyResolverUtils.updateProperty(sshd, ServerFactoryManager.MODULI_URL,
newModuli.toURI().toString());
}
}
}

public void start() {
LOG.info("Starting");
logger = new ServiceLogger();
Expand All @@ -252,6 +304,7 @@ public void start() {
sshd = SshServer.setUpDefaultServer();
LOG.info("SSHD " + sshd.getVersion());
hackVersion();
hackModuliDHGEX();
setupFactories();
setupKeyPair();
setupScp();
Expand Down Expand Up @@ -335,6 +388,8 @@ public boolean authenticate(final String username, final PublicKey key, final Se
static class Config {
// @see https://stribika.github.io/2015/01/04/secure-secure-shell.html
// @see http://manpages.ubuntu.com/manpages/focal/man5/sshd_config.5.html
public static final int DEFAULT_DHGEX_MIN = 2000;
public static final int DEFAULT_DHGEX_MAX = 8200;
/**
* man 5 sshd_config : KexAlgorithms
*
Expand Down Expand Up @@ -383,6 +438,8 @@ static class Config {
public static final String PROP_KEX_ALGORITHMS = "kexalgorithms";
public static final String PROP_CIPHERS = "ciphers";
public static final String PROP_MACS = "macs";
public static final String PROP_DHGEX_MIN = "dhgex-min";
public static final String PROP_DHGEX_MAX = "dhgex-max";
// HtPasswd config
public static final String PROP_HTPASSWD = BASE + "." + "htpasswd";
public static final String PROP_HT_HOME = "homedirectory";
Expand Down Expand Up @@ -477,6 +534,22 @@ public String getMacs() {
return value;
}

public int getMinSizeDHGEX() {
final String value = getValue(PROP_DHGEX_MIN);
if (value == null) {
return DEFAULT_DHGEX_MIN;
}
return Integer.parseInt(value);
}

public int getMaxSizeDHGEX() {
final String value = getValue(PROP_DHGEX_MAX);
if (value == null) {
return DEFAULT_DHGEX_MAX;
}
return Integer.parseInt(value);
}

// User config
private final String getValue(final String user, final String key) {
if ((user == null) || (key == null))
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/sftpd.policy
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ grant {
permission java.io.FilePermission "${java.io.tmpdir}${/}-", "read, write, delete";
//
permission java.io.FilePermission "/etc/resolv.conf", "read";
permission java.io.FilePermission "/etc/ssh/moduli", "read";
permission java.io.FilePermission "${sftp.home}/-", "read";
permission java.io.FilePermission "${sftp.home}/keys/hostkey.ser", "read, write";
permission java.io.FilePermission "${sftp.home}/keys/hostkey.pem", "read, write";
Expand Down

0 comments on commit d0ac8d7

Please sign in to comment.