diff compat/getentropy.c @ 0:a7e41e1a79c8

Initial revision
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Thu, 19 Jan 2017 22:39:51 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/compat/getentropy.c	Thu Jan 19 22:39:51 2017 +0100
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* needed for syscall(2) on Linux */
+#define	_GNU_SOURCE
+
+/* Linux >= 3.17 has getrandom(2) system call */
+#ifdef	__linux__
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <linux/random.h>
+#ifdef	SYS_getrandom
+#define	HAVE_GETRANDOM
+#endif /* SYS_getrandom */
+#endif /* __linux__ */
+/*
+ * on unknown Unix systems without getentropy(2) or Linux without getrandom(2)
+ * fall back to * reading from /dev/(u)random
+ */
+#ifndef	HAVE_GETRANDOM
+#include <stdio.h>
+#ifndef	RANDOM_DEVICE
+#ifdef	__linux__
+/* on Linux /dev/urandom should be good enough */
+#define	RANDOM_DEVICE	"/dev/urandom"
+#else /* __linux__ */
+/* on unknown Unix systems use the possibly blocking /dev/random */
+#define	RANDOM_DEVICE	"/dev/random"
+#endif /* __linux__ */
+#endif /* !RANDOM_DEVICE */
+#endif /* !HAVE_GETRANDOM */
+#include <errno.h>
+
+#ifdef	HAVE_GETRANDOM
+static int
+getentropy_linux_getrandom(void *buf, size_t buf_len)
+{
+	int retval;
+
+	retval = syscall(SYS_getrandom, buf, buf_len, 0);
+	if (retval < 0) {
+		return (-1);
+	} else if ((size_t)retval != buf_len) {
+		errno = EIO;
+		return (-1);
+	}
+
+	return (0);
+}
+#else
+static int
+getentropy_dev_random(void *buf, size_t buf_len)
+{
+	FILE	*fp;
+	int	saved_errno;
+
+	fp = fopen(RANDOM_DEVICE, "r");
+	if (fp == NULL) {
+		return (-1);
+	}
+	if (fread(buf, 1, buf_len, fp) != buf_len) {
+		saved_errno = errno;
+		fclose(fp);
+		errno = saved_errno;
+		return (-1);
+	}
+	if (fclose(fp) != 0) {
+		return (-1);
+	}
+
+	return (0);
+}
+#endif /* HAVE_GETRANDOM */
+
+int
+getentropy(void *buf, size_t buf_len)
+{
+	if (buf_len > 256) {
+		errno = EIO;
+		return (-1);
+	}
+
+	return (
+#ifdef	HAVE_GETRANDOM
+	    getentropy_linux_getrandom(
+#else /* HAVE_GETRANDOM */
+	    getentropy_dev_random(
+#endif /* HAVE_GETRANDOM */
+	    buf, buf_len));
+}