Configure SSHD maxAuthTries, loginGraceTime, maxConnectionsPerUser

Enable the site administrator to control the SSHD server's limits by
defining how many times a user can prevent an SSH key before we give
up, how long the session is allowed to sit without authentication,
and how many sessions any single user may have.

Change-Id: Ia7da504caa6e741a412dc03cf0e2e167d6d4c612
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 38fd82d..864092e 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1603,6 +1603,41 @@
 +
 By default, 1 plus the number of CPUs available to the JVM.
 
+[[sshd.maxAuthTries]]sshd.maxAuthTries::
++
+Maximum number of authentication attempts before the server
+disconnects the client.  Each public key that a client has loaded
+into its local agent counts as one auth request.  Users can work
+around the server's limit by loading less keys into their agent,
+or selecting a specific key in their `~/.ssh/config` file with
+the `IdentityFile` option.
++
+By default, 6.
+
+[[sshd.loginGraceTime]]sshd.loginGraceTime::
++
+Time in seconds that a client has to authenticate before the server
+automatically terminates their connection.  Values should use common
+unit suffixes to express their setting:
++
+* s, sec, second, seconds
+* m, min, minute, minutes
+* h, hr, hour, hours
+* d, day, days
+
++
+By default, 2 minutes.
+
+[[sshd.maxConnectionsPerUser]]sshd.maxConnectionsPerUser::
++
+Maximum number of concurrent SSH sessions that a user account
+may open at one time.  This is the number of distinct SSH logins
+the each user may have active at one time, and is not related to
+the number of commands a user may issue over a single connection.
+If set to 0, there is no limit.
++
+By default, 64.
+
 [[sshd.cipher]]sshd.cipher::
 +
 Available ciphers.  To permit multiple ciphers, specify multiple
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
index 69dd170..ae7b65d 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
@@ -14,7 +14,12 @@
 
 package com.google.gerrit.sshd;
 
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.gerrit.common.Version;
 import com.google.gerrit.lifecycle.LifecycleListener;
+import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.ssh.SshInfo;
 import com.google.gerrit.server.util.IdGenerator;
@@ -129,6 +134,25 @@
     reuseAddress = cfg.getBoolean("sshd", "reuseaddress", true);
     keepAlive = cfg.getBoolean("sshd", "tcpkeepalive", true);
 
+    getProperties().put(SERVER_IDENTIFICATION,
+        "GerritCodeReview_" + Version.getVersion() //
+            + " (" + super.getVersion() + ")");
+
+    getProperties().put(MAX_AUTH_REQUESTS,
+        String.valueOf(cfg.getInt("sshd", "maxAuthTries", 6)));
+
+    getProperties().put(
+        AUTH_TIMEOUT,
+        String.valueOf(MILLISECONDS.convert(ConfigUtil.getTimeUnit(cfg, "sshd",
+            null, "loginGraceTime", 120, SECONDS), SECONDS)));
+
+    final int maxConnectionsPerUser =
+        cfg.getInt("sshd", "maxConnectionsPerUser", 64);
+    if (0 < maxConnectionsPerUser) {
+      getProperties().put(MAX_CONCURRENT_SESSIONS,
+          String.valueOf(maxConnectionsPerUser));
+    }
+
     if (SecurityUtils.isBouncyCastleRegistered()) {
       initProviderBouncyCastle();
     } else {