File openssh-6.6p1-agent_locking_hardening.patch of Package openssh-askpass-gnome
# HG changeset patch
# Parent 8605dc0a7ce54648502196034871c8e6462c03ce
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