--- sshd.c.orig+debian	2009-02-17 15:41:42.000000000 +0000
+++ sshd.c	2009-02-17 15:40:05.000000000 +0000
@@ -1266,6 +1266,204 @@
 	}
 }
 
+/* exponential backoff hack, to prevent brute-force attacks against sshd */
+
+#include <db.h>
+#define backoff_database "/var/lib/ssh/backoff.db"
+#define backoff_database_dir "/var/lib/ssh"
+#define backoff_initial_lockout 1
+#define backoff_max_sleep 16
+  /* backoff_max_sleep = 16 allows "simultaneous" connections */
+
+/* needs -ldb-4 */
+
+int
+exp_backoff_close(DB *db)
+{
+	int fd;
+	if (db->fd(db, &fd) != 0)
+		goto failed;
+	struct flock fl;
+	fl.l_type = F_UNLCK;
+	fl.l_whence = SEEK_SET;
+	fl.l_start = 0;
+	fl.l_len = 1;
+	fcntl(fd, F_SETLK, &fl);
+	if (db->close(db, 0) == -1)
+		return -1;
+	return 0;
+failed:
+	debug("exp_backoff_close failed: %.200s", strerror(errno));
+	db->close(db, 0);
+	return -1;
+}
+
+void
+exp_backoff_db_error_handler(const DB_ENV *dbenv, const char *error_prefix, const char *msg)
+{
+	if (error_prefix != NULL && msg != NULL) {
+		debug("%s: %s", error_prefix, msg);
+	} else if (error_prefix == NULL && msg != NULL) {
+		debug("db error: %s", msg);
+	} else if (error_prefix != NULL && msg == NULL) {
+		debug("%s: error", error_prefix);
+	} else if (error_prefix == NULL && msg == NULL) {
+		debug("db error");
+	}
+}
+
+DB *
+exp_backoff_open_and_lock(const char *remote_ip)
+{
+	remote_ip = remote_ip;  /* ignore */
+	DB *db;
+	int status;
+	if ((status = db_create(&db, NULL, 0))) {
+		debug("exp_backoff_open_and_lock: db_create failed: %s", db_strerror(status));
+		goto failed;
+	}
+	db->set_errcall(db, exp_backoff_db_error_handler);
+	db->set_errpfx(db, "exp_backoff"); 
+	mkdir(backoff_database_dir, 0700);
+	if ((status = db->open(db, NULL, backoff_database, NULL, DB_HASH, DB_CREATE, 0600))) {
+		db->err(db, status, "db->open failed");
+		goto failed;
+	}
+	int fd;
+	if ((status = db->fd(db, &fd) != 0)) {
+		db->err(db, status, "db->fd failed");
+		goto failed;
+	}
+	struct flock fl;
+	fl.l_type = F_WRLCK;
+	fl.l_whence = SEEK_SET;
+	fl.l_start = 0;
+	fl.l_len = 1;
+	if (fcntl(fd, F_SETLKW, &fl) == -1) {
+		debug("exp_backoff_open_and_lock: fcntl(.. F_SETLKW ..) failed: %.200s", strerror(errno));
+		goto failed;
+	}
+	return db;
+failed:
+	exp_backoff_close(db);
+	return NULL;
+}
+
+int
+exp_backoff_check_allowed_to_login(const char *remote_ip)
+{
+	int allowed = 0;
+	int status;
+	time_t when;
+	char when_s[64];
+
+	debug("exp_backoff_check_allowed_to_login: remote_ip = %s", remote_ip);
+
+	DB *db;
+	debug("exp_backoff_check_allowed_to_login");
+	db = exp_backoff_open_and_lock(remote_ip);
+	if (db == NULL)
+		goto failed2;
+	debug("exp_backoff_check_allowed_to_login: opened okay");
+
+	DBT key, data;
+	memset(&key, 0, sizeof(DBT));
+	memset(&data, 0, sizeof(DBT));
+
+	key.data = (char*)remote_ip;
+	key.size = strlen(remote_ip)+1;
+
+	data.data = when_s;
+	data.ulen = sizeof(when_s);
+	data.flags = DB_DBT_USERMEM;
+
+	status = db->get(db, NULL, &key, &data, 0);
+	if (status == DB_NOTFOUND) {
+		debug("exp_backoff_check_allowed_to_login: not found");
+		int count = snprintf(when_s, sizeof(when_s), "%d %d", backoff_initial_lockout, (int)time(NULL));
+		data.size = count + 1;
+		status = db->put(db, NULL, &key, &data, 0);
+		if (status == -1) {
+			db->err(db, status, "db->put %s:%s failed");
+			goto failed;
+		}
+		debug("exp_backoff_check_allowed_to_login: put %s:%s succeeded", (char*)(key.data), (char*)(data.data));
+		allowed = 1;
+	} else if (status == 0) {
+		debug("exp_backoff_check_allowed_to_login: found");
+		int lockout_time;
+		status = sscanf(data.data, "%d %d", &lockout_time, (int *)&when);
+		if (status != 2) {
+			debug("exp_backoff_check_allowed_to_login: sscanf failed: %.200s", strerror(errno));
+			goto failed;
+		}
+		debug("lockout_time %d ; when %d", lockout_time, (int)when);
+
+		allowed = 1;
+		int now;
+		while ((now = time(NULL)) < when) {
+			if (when-now > backoff_max_sleep) {
+				allowed = 0;
+				break;
+			}
+			sleep(when-now);
+			debug("exp_backoff_check_allowed_to_login: sleep done");
+		}
+		if (allowed) {
+			when = now + lockout_time;
+			debug("when %d ; allowed %d", (int)when, (int)time(NULL));
+			lockout_time *= 2;
+
+			int count = snprintf(when_s, sizeof(when_s), "%d %d", lockout_time, (int)when);
+			data.size = count + 1;
+			status = db->put(db, NULL, &key, &data, 0);
+			if (status == -1) {
+				db->err(db, status, "db->put failed");
+				goto failed;
+			}
+			debug("exp_backoff_check_allowed_to_login: put succeeded");
+		}
+	} else {
+		db->err(db, status, "db->get failed");
+		goto failed;
+	}
+
+	if (exp_backoff_close(db) != 0)
+		goto failed2;
+
+	debug("exp_backoff_check_allowed_to_login: returning %d", allowed);
+	return allowed;
+
+failed:
+	exp_backoff_close(db);
+failed2:
+	return -1;
+}
+
+void
+exp_backoff_reset_allowed_to_login(const char *remote_ip)
+{
+	DB *db;
+	int status;
+	debug("exp_backoff_reset_allowed_to_login");
+
+	db = exp_backoff_open_and_lock(remote_ip);
+	if (db == NULL) {
+		return;
+	}
+
+	DBT key;
+	memset(&key, 0, sizeof(DBT));
+
+	key.data = (char*)remote_ip;
+	key.size = strlen(remote_ip)+1;
+
+	if ((status = db->del(db, NULL, &key, 0)) != 0) {
+		db->err(db, status, "db->del failed");
+	}
+
+	exp_backoff_close(db);
+}
 
 /*
  * Main program for the daemon.
@@ -1874,6 +2072,15 @@
 	/* Log the connection. */
 	verbose("Connection from %.500s port %d", remote_ip, remote_port);
 
+	/*
+	 * Exponential backoff - check when that IP address is allowed to log
+	 * in, and exit if not yet.
+	 */
+	int allowed = exp_backoff_check_allowed_to_login(remote_ip);
+	if (!allowed) {
+		exit(0);
+	}
+
 #ifdef USE_SECURITY_SESSION_API
 	/*
 	 * Create a new security session for use by the new user login if
@@ -2012,6 +2219,8 @@
 	}
 #endif
 
+	exp_backoff_reset_allowed_to_login(remote_ip);
+
 	/*
 	 * In privilege separation, we fork another child and prepare
 	 * file descriptor passing.
