# use timing safe byte compare when working with sensitive data
# (i.e. always ompare whole buffers and do not bail out on first
# mismatch)
#
# based on upstream patches starting with:
# changeset:   10074:470f59b04205
# user:        djm
# date:        Fri Jul 16 03:57:51 2010 +0000
# files:       ChangeLog auth-rsa.c channels.c jpake.c key.c misc.c misc.h monitor.c packet.c ssh-rsa.c
# description:
# - djm@cvs.openbsd.org 2010/07/13 11:52:06
#      [auth-rsa.c channels.c jpake.c key.c misc.c misc.h monitor.c]
#      [packet.c ssh-rsa.c]
#      implement a timing_safe_cmp() function to compare memory without leaking
#      timing information by short-circuiting like memcmp() and use it for
#      some of the more sensitive comparisons (though nothing high-value was
#      readily attackable anyway); "looks ok" markus@
# 
diff -Nadpru8 openssh-5.1p1.orig/auth-rsa.c openssh-5.1p1.timing/auth-rsa.c
--- openssh-5.1p1.orig/auth-rsa.c	2008-07-02 14:37:30.000000000 +0200
+++ openssh-5.1p1.timing/auth-rsa.c	2011-07-29 21:34:23.580848431 +0200
@@ -108,17 +108,17 @@ auth_rsa_verify_response(Key *key, BIGNU
 	memset(buf, 0, 32);
 	BN_bn2bin(challenge, buf + 32 - len);
 	MD5_Init(&md);
 	MD5_Update(&md, buf, 32);
 	MD5_Update(&md, session_id, 16);
 	MD5_Final(mdbuf, &md);
 
 	/* Verify that the response is the original challenge. */
-	if (memcmp(response, mdbuf, 16) != 0) {
+	if (timingsafe_bcmp(response, mdbuf, 16) != 0) {
 		/* Wrong answer. */
 		return (0);
 	}
 	/* Correct answer. */
 	return (1);
 }
 
 /*
diff -Nadpru8 openssh-5.1p1.orig/channels.c openssh-5.1p1.timing/channels.c
--- openssh-5.1p1.orig/channels.c	2011-07-29 14:00:23.174524291 +0200
+++ openssh-5.1p1.timing/channels.c	2011-07-29 21:34:23.581848434 +0200
@@ -896,17 +896,17 @@ x11_open_helper(Buffer *b)
 	/* Check if authentication protocol matches. */
 	if (proto_len != strlen(x11_saved_proto) ||
 	    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
 		debug2("X11 connection uses different authentication protocol.");
 		return -1;
 	}
 	/* Check if authentication data matches our fake data. */
 	if (data_len != x11_fake_data_len ||
-	    memcmp(ucp + 12 + ((proto_len + 3) & ~3),
+	    timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3),
 		x11_fake_data, x11_fake_data_len) != 0) {
 		debug2("X11 auth data does not match fake data.");
 		return -1;
 	}
 	/* Check fake data length */
 	if (x11_fake_data_len != x11_saved_data_len) {
 		error("X11 fake_data_len %d != saved_data_len %d",
 		    x11_fake_data_len, x11_saved_data_len);
diff -Nadpru8 openssh-5.1p1.orig/configure.ac openssh-5.1p1.timing/configure.ac
--- openssh-5.1p1.orig/configure.ac	2011-07-29 14:00:23.165524285 +0200
+++ openssh-5.1p1.timing/configure.ac	2011-07-30 23:44:08.526013531 +0200
@@ -1378,16 +1378,17 @@ AC_CHECK_FUNCS( \
 	strmode \
 	strnvis \
 	strtonum \
 	strtoll \
 	strtoul \
 	swap32 \
 	sysconf \
 	tcgetpgrp \
+	timingsafe_bcmp \
 	truncate \
 	unsetenv \
 	updwtmpx \
 	vasprintf \
 	vhangup \
 	vsnprintf \
 	waitpid \
 )
diff -Nadpru8 openssh-5.1p1.orig/monitor.c openssh-5.1p1.timing/monitor.c
--- openssh-5.1p1.orig/monitor.c	2008-07-11 09:36:48.000000000 +0200
+++ openssh-5.1p1.timing/monitor.c	2011-07-29 21:34:23.583848438 +0200
@@ -489,17 +489,17 @@ monitor_read(struct monitor *pmonitor, s
 }
 
 /* allowed key state */
 static int
 monitor_allowed_key(u_char *blob, u_int bloblen)
 {
 	/* make sure key is allowed */
 	if (key_blob == NULL || key_bloblen != bloblen ||
-	    memcmp(key_blob, blob, key_bloblen))
+	    timingsafe_bcmp(key_blob, blob, key_bloblen))
 		return (0);
 	return (1);
 }
 
 static void
 monitor_reset_key_state(void)
 {
 	/* reset state */
@@ -1087,24 +1087,24 @@ monitor_valid_userblob(u_char *data, u_i
 	buffer_init(&b);
 	buffer_append(&b, data, datalen);
 
 	if (datafellows & SSH_OLD_SESSIONID) {
 		p = buffer_ptr(&b);
 		len = buffer_len(&b);
 		if ((session_id2 == NULL) ||
 		    (len < session_id2_len) ||
-		    (memcmp(p, session_id2, session_id2_len) != 0))
+		    (timingsafe_bcmp(p, session_id2, session_id2_len) != 0))
 			fail++;
 		buffer_consume(&b, session_id2_len);
 	} else {
 		p = buffer_get_string(&b, &len);
 		if ((session_id2 == NULL) ||
 		    (len != session_id2_len) ||
-		    (memcmp(p, session_id2, session_id2_len) != 0))
+		    (timingsafe_bcmp(p, session_id2, session_id2_len) != 0))
 			fail++;
 		xfree(p);
 	}
 	if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST)
 		fail++;
 	p = buffer_get_string(&b, NULL);
 	if (strcmp(authctxt->user, p) != 0) {
 		logit("wrong user name passed to monitor: expected %s != %.100s",
@@ -1142,17 +1142,17 @@ monitor_valid_hostbasedblob(u_char *data
 	int fail = 0;
 
 	buffer_init(&b);
 	buffer_append(&b, data, datalen);
 
 	p = buffer_get_string(&b, &len);
 	if ((session_id2 == NULL) ||
 	    (len != session_id2_len) ||
-	    (memcmp(p, session_id2, session_id2_len) != 0))
+	    (timingsafe_bcmp(p, session_id2, session_id2_len) != 0))
 		fail++;
 	xfree(p);
 
 	if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST)
 		fail++;
 	p = buffer_get_string(&b, NULL);
 	if (strcmp(authctxt->user, p) != 0) {
 		logit("wrong user name passed to monitor: expected %s != %.100s",
@@ -1661,19 +1661,19 @@ static Kex *
 mm_get_kex(Buffer *m)
 {
 	Kex *kex;
 	void *blob;
 	u_int bloblen;
 
 	kex = xcalloc(1, sizeof(*kex));
 	kex->session_id = buffer_get_string(m, &kex->session_id_len);
-	if ((session_id2 == NULL) ||
-	    (kex->session_id_len != session_id2_len) ||
-	    (memcmp(kex->session_id, session_id2, session_id2_len) != 0))
+	if (session_id2 == NULL ||
+	    kex->session_id_len != session_id2_len ||
+	    timingsafe_bcmp(kex->session_id, session_id2, session_id2_len) != 0)
 		fatal("mm_get_get: internal error: bad session id");
 	kex->we_need = buffer_get_int(m);
 	kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
 	kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
 	kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
 	kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
 	kex->server = 1;
 	kex->hostkey_type = buffer_get_int(m);
diff -Nadpru8 openssh-5.1p1.orig/openbsd-compat/Makefile.in openssh-5.1p1.timing/openbsd-compat/Makefile.in
--- openssh-5.1p1.orig/openbsd-compat/Makefile.in	2008-06-08 19:32:29.000000000 +0200
+++ openssh-5.1p1.timing/openbsd-compat/Makefile.in	2011-07-29 21:29:11.659344114 +0200
@@ -11,17 +11,17 @@ LD=@LD@
 CFLAGS=@CFLAGS@
 CPPFLAGS=-I. -I.. -I$(srcdir) -I$(srcdir)/.. @CPPFLAGS@ @DEFS@
 LIBS=@LIBS@
 AR=@AR@
 RANLIB=@RANLIB@
 INSTALL=@INSTALL@
 LDFLAGS=-L. @LDFLAGS@
 
-OPENBSD=base64.o basename.o bindresvport.o daemon.o dirname.o fmt_scaled.o getcwd.o getgrouplist.o getopt.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o readpassphrase.o realpath.o rresvport.o setenv.o setproctitle.o sha2.o sigact.o strlcat.o strlcpy.o strmode.o strsep.o strtonum.o strtoll.o strtoul.o vis.o
+OPENBSD=base64.o basename.o bindresvport.o daemon.o dirname.o fmt_scaled.o getcwd.o getgrouplist.o getopt.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o readpassphrase.o realpath.o rresvport.o setenv.o setproctitle.o sha2.o sigact.o strlcat.o strlcpy.o strmode.o strsep.o strtonum.o strtoll.o strtoul.o timingsafe_bcmp.o vis.o
 
 COMPAT=bsd-arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-poll.o bsd-snprintf.o bsd-statvfs.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xmmap.o xcrypt.o
 
 PORTS=port-aix.o port-irix.o port-linux.o port-solaris.o port-tun.o port-uw.o
 
 .c.o:
 	$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
 
diff -Nadpru8 openssh-5.1p1.orig/openbsd-compat/openbsd-compat.h openssh-5.1p1.timing/openbsd-compat/openbsd-compat.h
--- openssh-5.1p1.orig/openbsd-compat/openbsd-compat.h	2008-06-08 19:32:29.000000000 +0200
+++ openssh-5.1p1.timing/openbsd-compat/openbsd-compat.h	2011-07-29 21:24:49.783922148 +0200
@@ -195,16 +195,20 @@ long long strtonum(const char *, long lo
 #ifndef HAVE_VASPRINTF
 int vasprintf(char **, const char *, va_list);
 #endif
 
 #ifndef HAVE_VSNPRINTF
 int vsnprintf(char *, size_t, const char *, va_list);
 #endif
 
+#ifndef HAVE_TIMINGSAFE_BCMP
+int timingsafe_bcmp(const void *, const void *, size_t);
+#endif
+
 void *xmmap(size_t size);
 char *xcrypt(const char *password, const char *salt);
 char *shadow_pw(struct passwd *pw);
 
 /* rfc2553 socket API replacements */
 #include "fake-rfc2553.h"
 
 /* Routines for a single OS platform */
diff -Nadpru8 openssh-5.1p1.orig/openbsd-compat/timingsafe_bcmp.c openssh-5.1p1.timing/openbsd-compat/timingsafe_bcmp.c
--- openssh-5.1p1.orig/openbsd-compat/timingsafe_bcmp.c	1970-01-01 01:00:00.000000000 +0100
+++ openssh-5.1p1.timing/openbsd-compat/timingsafe_bcmp.c	2011-07-29 21:23:48.844824158 +0200
@@ -0,0 +1,35 @@
+/*	$OpenBSD: timingsafe_bcmp.c,v 1.1 2010/09/24 13:33:00 matthew Exp $	*/
+/*
+ * Copyright (c) 2010 Damien Miller.  All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/string/timingsafe_bcmp.c */
+
+#include "includes.h"
+#ifndef HAVE_TIMINGSAFE_BCMP
+
+int
+timingsafe_bcmp(const void *b1, const void *b2, size_t n)
+{
+	const unsigned char *p1 = b1, *p2 = b2;
+	int ret = 0;
+
+	for (; n > 0; n--)
+		ret |= *p1++ ^ *p2++;
+	return (ret != 0);
+}
+
+#endif /* TIMINGSAFE_BCMP */
+
diff -Nadpru8 openssh-5.1p1.orig/packet.c openssh-5.1p1.timing/packet.c
--- openssh-5.1p1.orig/packet.c	2008-07-11 09:36:48.000000000 +0200
+++ openssh-5.1p1.timing/packet.c	2011-07-29 21:34:23.584848440 +0200
@@ -1180,17 +1180,17 @@ packet_read_poll2(u_int32_t *seqnr_p)
 	/*
 	 * compute MAC over seqnr and packet,
 	 * increment sequence number for incoming packet
 	 */
 	if (mac && mac->enabled) {
 		macbuf = mac_compute(mac, p_read.seqnr,
 		    buffer_ptr(&incoming_packet),
 		    buffer_len(&incoming_packet));
-		if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0)
+		if (timingsafe_bcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0)
 			packet_disconnect("Corrupted MAC on input.");
 		DBG(debug("MAC #%d ok", p_read.seqnr));
 		buffer_consume(&input, mac->mac_len);
 	}
 	if (seqnr_p != NULL)
 		*seqnr_p = p_read.seqnr;
 	if (++p_read.seqnr == 0)
 		logit("incoming seqnr wraps around");
diff -Nadpru8 openssh-5.1p1.orig/ssh-rsa.c openssh-5.1p1.timing/ssh-rsa.c
--- openssh-5.1p1.orig/ssh-rsa.c	2006-09-01 07:38:37.000000000 +0200
+++ openssh-5.1p1.timing/ssh-rsa.c	2011-07-29 21:30:27.229466129 +0200
@@ -242,21 +242,21 @@ openssh_RSA_verify(int type, u_char *has
 		error("RSA_public_decrypt failed: %s",
 		    ERR_error_string(ERR_get_error(), NULL));
 		goto done;
 	}
 	if (len < 0 || (u_int)len != hlen + oidlen) {
 		error("bad decrypted len: %d != %d + %d", len, hlen, oidlen);
 		goto done;
 	}
-	if (memcmp(decrypted, oid, oidlen) != 0) {
+	if (timingsafe_bcmp(decrypted, oid, oidlen) != 0) {
 		error("oid mismatch");
 		goto done;
 	}
-	if (memcmp(decrypted + oidlen, hash, hlen) != 0) {
+	if (timingsafe_bcmp(decrypted + oidlen, hash, hlen) != 0) {
 		error("hash mismatch");
 		goto done;
 	}
 	ret = 1;
 done:
 	if (decrypted)
 		xfree(decrypted);
 	return ret;
