File openssh-6.6p1-agent_locking_hardening.patch of Package openssh.10219

# HG changeset patch
# Parent  f7e1af51abd41372c38787a5e427e95bbc314b4c
Use a salted hash of the lock passphrase instead of plain text and do
constant-time comparisons of it. Should prevent leaking any information about
it via timing, pointed out by Ryan Castellucci.  Add a 0.1s incrementing delay
for each failed unlock attempt up to 10s.

CVE-2015-5352
bsc#936695

upstream commit 9173d0fbe44de7ebcad8a15618e13a8b8d78902e

diff --git a/openssh-6.6p1/ssh-agent.c b/openssh-6.6p1/ssh-agent.c
--- a/openssh-6.6p1/ssh-agent.c
+++ b/openssh-6.6p1/ssh-agent.c
@@ -124,18 +124,22 @@ int max_fd = 0;
 pid_t parent_pid = -1;
 time_t parent_alive_interval = 0;
 
 /* pathname and directory for AUTH_SOCKET */
 char socket_name[MAXPATHLEN];
 char socket_dir[MAXPATHLEN];
 
 /* locking */
+#define LOCK_SIZE	32
+#define LOCK_SALT_SIZE	16
+#define LOCK_ROUNDS	1
 int locked = 0;
-char *lock_passwd = NULL;
+char lock_passwd[LOCK_SIZE];
+char lock_salt[LOCK_SALT_SIZE];
 
 extern char *__progname;
 
 /* Default lifetime in seconds (0 == forever) */
 static long lifetime = 0;
 
 static void
 close_socket(SocketEntry *e)
@@ -543,32 +547,54 @@ send:
 	buffer_put_char(&e->output,
 	    success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
 }
 
 /* XXX todo: encrypt sensitive data with passphrase */
 static void
 process_lock_agent(SocketEntry *e, int lock)
 {
-	int success = 0;
-	char *passwd;
+	int success = 0, delay;
+	char *passwd, passwdhash[LOCK_SIZE];
+	static u_int fail_count = 0;
+	size_t pwlen;
 
-	passwd = buffer_get_string(&e->request, NULL);
-	if (locked && !lock && strcmp(passwd, lock_passwd) == 0) {
-		locked = 0;
-		explicit_bzero(lock_passwd, strlen(lock_passwd));
-		free(lock_passwd);
-		lock_passwd = NULL;
-		success = 1;
+	passwd = buffer_get_string(&e->request, &pwlen);
+	if (pwlen == 0) {
+		debug("empty password not supported");
+	} else if (locked && !lock) {
+		if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt),
+		    passwdhash, sizeof(passwdhash), LOCK_ROUNDS) < 0)
+			fatal("bcrypt_pbkdf");
+		if (timingsafe_bcmp(passwdhash, lock_passwd, LOCK_SIZE) == 0) {
+			debug("agent unlocked");
+			locked = 0;
+			fail_count = 0;
+			explicit_bzero(lock_passwd, sizeof(lock_passwd));
+			success = 1;
+		} else {
+			/* delay in 0.1s increments up to 10s */
+			if (fail_count < 100)
+				fail_count++;
+			delay = 100000 * fail_count;
+			debug("unlock failed, delaying %0.1lf seconds",
+			    (double)delay/1000000);
+			usleep(delay);
+		}
+		explicit_bzero(passwdhash, sizeof(passwdhash));
 	} else if (!locked && lock) {
+		debug("agent locked");
 		locked = 1;
-		lock_passwd = xstrdup(passwd);
+		arc4random_buf(lock_salt, sizeof(lock_salt));
+		if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt),
+		    lock_passwd, sizeof(lock_passwd), LOCK_ROUNDS) < 0)
+			fatal("bcrypt_pbkdf");
 		success = 1;
 	}
-	explicit_bzero(passwd, strlen(passwd));
+	explicit_bzero(passwd, pwlen);
 	free(passwd);
 
 	buffer_put_int(&e->output, 1);
 	buffer_put_char(&e->output,
 	    success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
 }
 
 static void
openSUSE Build Service is sponsored by