diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index acd460b971..8c21525534 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -200,6 +200,32 @@ the daemon can terminate connections if the peer disappears. + By default, true. +sshd.cipher:: ++ +Available ciphers. To permit multiple ciphers, specify multiple +`sshd.cipher` keys in the configuration file, one cipher name +per key. Cipher names starting with `+` are enabled in addition +to the default ciphers, cipher names starting with `-` are removed +from the default cipher set. ++ +Supported ciphers: aes128-cbc, aes128-cbc, aes256-cbc, blowfish-cbc, +3des-cbc, none. ++ +By default, all supported ciphers except `none` are available. + +sshd.mac:: ++ +Available MAC (message authentication code) algorithms. To permit +multiple algorithms, specify multiple `sshd.mac` keys in the +configuration file, one MAC per key. MAC names starting with `+` +are enabled in addition to the default MACs, MAC names starting with +`-` are removed from the default MACs. ++ +Supported MACs: hmac-md5, hmac-md5-96, hmac-sha1, hmac-sha1-96. ++ +By default, all supported MACs are available. + + File `replication.config` ------------------------- diff --git a/src/main/java/com/google/gerrit/server/ssh/GerritSshDaemon.java b/src/main/java/com/google/gerrit/server/ssh/GerritSshDaemon.java index 3bb4b2a596..1f4de57616 100644 --- a/src/main/java/com/google/gerrit/server/ssh/GerritSshDaemon.java +++ b/src/main/java/com/google/gerrit/server/ssh/GerritSshDaemon.java @@ -24,17 +24,16 @@ import org.apache.mina.core.session.IoSession; import org.apache.mina.transport.socket.SocketSessionConfig; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import org.apache.sshd.SshServer; -import org.apache.sshd.common.Cipher; import org.apache.sshd.common.Compression; import org.apache.sshd.common.KeyExchange; import org.apache.sshd.common.KeyPairProvider; -import org.apache.sshd.common.Mac; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.Signature; import org.apache.sshd.common.cipher.AES128CBC; import org.apache.sshd.common.cipher.AES192CBC; import org.apache.sshd.common.cipher.AES256CBC; import org.apache.sshd.common.cipher.BlowfishCBC; +import org.apache.sshd.common.cipher.CipherNone; import org.apache.sshd.common.cipher.TripleDESCBC; import org.apache.sshd.common.compression.CompressionNone; import org.apache.sshd.common.keyprovider.FileKeyPairProvider; @@ -169,8 +168,8 @@ public class GerritSshDaemon extends SshServer { } else { initProviderJce(); } - initCipers(); - initMac(); + initCiphers(cfg); + initMacs(cfg); initSignatures(); initChannels(); initCompression(); @@ -236,20 +235,87 @@ public class GerritSshDaemon extends SshServer { } @SuppressWarnings("unchecked") - private void initCipers() { - setCipherFactories(Arrays.> asList( - new AES128CBC.Factory(), new TripleDESCBC.Factory(), - new BlowfishCBC.Factory(), new AES192CBC.Factory(), - new AES256CBC.Factory())); + private void initCiphers(final RepositoryConfig cfg) { + setCipherFactories(filter(cfg, "cipher", new AES128CBC.Factory(), + new TripleDESCBC.Factory(), new BlowfishCBC.Factory(), + new AES192CBC.Factory(), new AES256CBC.Factory(), + + null, new CipherNone.Factory())); } @SuppressWarnings("unchecked") - private void initMac() { - setMacFactories(Arrays.> asList(new HMACMD5.Factory(), + private void initMacs(final RepositoryConfig cfg) { + setMacFactories(filter(cfg, "mac", new HMACMD5.Factory(), new HMACSHA1.Factory(), new HMACMD596.Factory(), new HMACSHA196.Factory())); } + private static List> filter(final RepositoryConfig cfg, + final String key, final NamedFactory... avail) { + final ArrayList> def = new ArrayList>(); + for (final NamedFactory n : avail) { + if (n == null) { + break; + } + def.add(n); + } + + final String[] want = cfg.getStringList("sshd", null, key); + if (want == null || want.length == 0) { + return def; + } + + boolean didClear = false; + for (final String setting : want) { + String name = setting.trim(); + boolean add = true; + if (name.startsWith("-")) { + add = false; + name = name.substring(1).trim(); + } else if (name.startsWith("+")) { + name = name.substring(1).trim(); + } else if (!didClear) { + didClear = true; + def.clear(); + } + + final NamedFactory n = find(name, avail); + if (n == null) { + final StringBuilder msg = new StringBuilder(); + msg.append("sshd." + key + " = " + name + " unsupported; only "); + for (int i = 0; i < avail.length; i++) { + if (avail[i] == null) { + continue; + } + if (i > 0) { + msg.append(", "); + } + msg.append(avail[i].getName()); + } + msg.append(" is supported"); + log.error(msg.toString()); + } else if (add) { + if (!def.contains(n)) { + def.add(n); + } + } else { + def.remove(n); + } + } + + return def; + } + + private static NamedFactory find(final String name, + final NamedFactory... avail) { + for (final NamedFactory n : avail) { + if (n != null && name.equals(n.getName())) { + return n; + } + } + return null; + } + @SuppressWarnings("unchecked") private void initSignatures() { setSignatureFactories(Arrays.> asList(