From 0554324ec6bbc2071f5d1f8ad211a1643e29eb1f Mon Sep 17 00:00:00 2001
From: Benjamin Tissoires <benjamin.tissoires@gmail.com>
Date: Tue, 4 Apr 2017 19:13:38 +0200
Subject: [PATCH] Use getentropy() if arc4random_buf() is not available

This allows to fix CVE-2017-2625 on Linux platforms without pulling in
libbsd.
The libc getentropy() is available since glibc 2.25 but also on OpenBSD.
For Linux, we need at least a v3.17 kernel. If the recommended
arc4random_buf() function is not available, emulate it by first trying
to use getentropy() on a supported glibc and kernel. If the call fails,
fall back to the current (vulnerable) code.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
Reviewed-by: Mark Kettenis <kettenis@openbsd.org>
Reviewed-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
---
 GenKey.c        | 31 ++++++++++++++++++++++++++-----
 configure.ac |  2 +-
 2 files changed, 27 insertions(+), 6 deletions(-)

Index: libXdmcp-1.0.2/GenKey.c
===================================================================
--- libXdmcp-1.0.2.orig/GenKey.c
+++ libXdmcp-1.0.2/GenKey.c
@@ -67,10 +67,11 @@ getbits (long data, unsigned char *dst)
 #define getpid(x) _getpid(x)
 #endif
 
-void
-XdmcpGenerateKey (XdmAuthKeyPtr key)
-{
 #ifndef HAVE_ARC4RANDOM_BUF
+
+static void
+emulate_getrandom_buf (char *auth, int len)
+{
     long    lowbits, highbits;
 
     srandom ((int)getpid() ^ time((Time_t *)0));
@@ -78,7 +79,152 @@ XdmcpGenerateKey (XdmAuthKeyPtr key)
     highbits = random ();
     getbits (lowbits, key->data);
     getbits (highbits, key->data + 4);
+}
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // needed on SLE11 for O_CLOEXEC
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef SYS_getrandom
+#include <linux/random.h>
+#include <sys/syscall.h>
+#endif
+
+int getentropy_urandom(void *buffer, size_t length)
+{
+	int random_fd = -1;
+	ssize_t res = -1;
+	size_t filled = 0;
+
+	if( length > 256 )
+	{
+		errno = EIO;
+		return -1;
+	}
+
+	random_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
+
+	if( random_fd == -1 )
+	{
+		return -1;
+	}
+
+	while( filled < length )
+	{
+		res = read(random_fd, (char*)buffer + filled, length - filled);
+
+		if( res == -1 )
+		{
+			// shouldn't actually happen acc. to man(4) random,
+			// but you never know
+			if( errno == EINTR )
+				continue;
+
+			close(random_fd);
+			return -1;
+		}
+		else if( res == 0 )
+		{
+			// no more bytes available? should not happen
+			errno = EIO;
+			close(random_fd);
+			return -1;
+		}
+
+		filled += res;
+	}
+
+	close(random_fd);
+	return 0;
+}
+
+int getentropy_getrandom(void *buffer, size_t length)
+{
+	int res;
+	size_t filled = 0;
+
+	if( length > 256 )
+	{
+		errno = EIO;
+		return -1;
+	}
+
+	while( filled < length )
+	{
+#ifdef SYS_getrandom
+		/*
+		 * glibc does not contain a syscall wrapper for this in older
+		 * versions
+		 */
+		res = syscall(SYS_getrandom, (char*)buffer + filled, length - filled, 0);
 #else
+#	warning no getrandom
+		errno = ENOSYS;
+		return -1;
+#endif // SYS_getrandom
+
+		if( res == -1 )
+		{
+			if( errno == EINTR )
+				continue;
+
+			return -1;
+		}
+		else if( res == 0 )
+		{
+			// no more bytes available? should not happen
+			errno = EIO;
+			return -1;
+		}
+
+		filled += res;
+	}
+
+	return 0;
+}
+
+int getentropy_emulate(void *buffer, size_t length)
+{
+	/*
+	 * check at runtime whether we have a getrandom system call available,
+	 * otherwise fall back to urandom approach. autoconf check for
+	 * getrandom() does not work, because there's been no declaration for
+	 * it for years.
+	 */
+	int res = getentropy_getrandom(buffer, length);
+
+	if( res == -1 && errno == ENOSYS )
+	{
+		return getentropy_urandom(buffer, length);
+	}
+
+	return res;
+}
+
+static void
+arc4random_buf (void *auth, int len)
+{
+    int	    ret;
+
+#if HAVE_GETENTROPY
+    /* weak emulation of arc4random through the getentropy libc call */
+    ret = getentropy (auth, len);
+#else
+    ret = getentropy_emulate (auth, len);
+#endif /* HAVE_GETENTROPY */
+    if (ret == 0)
+	return;
+
+    emulate_getrandom_buf (auth, len);
+}
+
+#endif /* !defined(HAVE_ARC4RANDOM_BUF) */
+
+void
+XdmcpGenerateKey (XdmAuthKeyPtr key)
+{
     arc4random_buf(key->data, 8);
-#endif
 }
Index: libXdmcp-1.0.2/configure.ac
===================================================================
--- libXdmcp-1.0.2.orig/configure.ac
+++ libXdmcp-1.0.2/configure.ac
@@ -46,7 +46,7 @@ AM_PROG_LIBTOOL
 AC_PROG_MAKE_SET
 
 AC_CHECK_LIB([bsd], [arc4random_buf])
-AC_CHECK_FUNCS([srand48 lrand48 arc4random_buf])
+AC_CHECK_FUNCS([srand48 lrand48 arc4random_buf getentropy])
 
 AC_SEARCH_LIBS([recvfrom],[socket])
 
