Blob Blame History Raw
diff -urN gnutls-3.3.8.orig/lib/crypto-backend.h gnutls-3.3.8/lib/crypto-backend.h
--- gnutls-3.3.8.orig/lib/crypto-backend.h	2014-07-29 22:22:47.000000000 +0200
+++ gnutls-3.3.8/lib/crypto-backend.h	2014-11-18 09:52:15.420936655 +0100
@@ -77,6 +77,7 @@
 
 typedef struct gnutls_crypto_rnd {
 	int (*init) (void **ctx);
+	int (*check) (void **ctx);
 	int (*rnd) (void *ctx, int level, void *data, size_t datasize);
 	void (*rnd_refresh) (void *ctx);
 	void (*deinit) (void *ctx);
diff -urN gnutls-3.3.8.orig/lib/gnutls_global.c gnutls-3.3.8/lib/gnutls_global.c
--- gnutls-3.3.8.orig/lib/gnutls_global.c	2014-09-04 21:05:54.000000000 +0200
+++ gnutls-3.3.8/lib/gnutls_global.c	2014-11-18 09:57:18.851879610 +0100
@@ -207,6 +207,16 @@
 
 	_gnutls_init++;
 	if (_gnutls_init > 1) {
+		if (_gnutls_init == 2 && _gnutls_init_ret == 0) {
+			/* some applications may close the urandom fd 
+			 * before calling gnutls_global_init(). in that
+			 * case reopen it */
+			ret = _gnutls_rnd_check();
+			if (ret < 0) {
+				gnutls_assert();
+				goto out;
+			}
+		}
 		ret = _gnutls_init_ret;
 		goto out;
 	}
diff -urN gnutls-3.3.8.orig/lib/nettle/rnd.c gnutls-3.3.8/lib/nettle/rnd.c
--- gnutls-3.3.8.orig/lib/nettle/rnd.c	2014-07-29 22:25:07.000000000 +0200
+++ gnutls-3.3.8/lib/nettle/rnd.c	2014-11-18 09:52:15.420936655 +0100
@@ -255,6 +255,15 @@
 	return 0;
 }
 
+/* This is called when gnutls_global_init() is called for second time.
+ * It must check whether any resources are still available.
+ * The particular problem it solves is to verify that the urandom fd is still
+ * open (for applications that for some reason closed all fds */
+static int wrap_nettle_rnd_check(void **ctx)
+{
+	return _rnd_system_entropy_check();
+}
+
 static int
 wrap_nettle_rnd_nonce(void *_ctx, void *data, size_t datasize)
 {
@@ -363,6 +372,7 @@
 
 gnutls_crypto_rnd_st _gnutls_rnd_ops = {
 	.init = wrap_nettle_rnd_init,
+	.check = wrap_nettle_rnd_check,
 	.deinit = wrap_nettle_rnd_deinit,
 	.rnd = wrap_nettle_rnd,
 	.rnd_refresh = wrap_nettle_rnd_refresh,
diff -urN gnutls-3.3.8.orig/lib/nettle/rnd-common.c gnutls-3.3.8/lib/nettle/rnd-common.c
--- gnutls-3.3.8.orig/lib/nettle/rnd-common.c	2014-08-03 14:22:42.000000000 +0200
+++ gnutls-3.3.8/lib/nettle/rnd-common.c	2014-11-18 10:25:56.962112669 +0100
@@ -37,6 +37,10 @@
 #include <rnd-common.h>
 #include <hash-pjw-bare.h>
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
 /* gnulib wants to claim strerror even if it cannot provide it. WTF */
 #undef strerror
 
@@ -94,6 +98,11 @@
 
 get_entropy_func _rnd_get_system_entropy = _rnd_get_system_entropy_win32;
 
+int _rnd_system_entropy_check(void)
+{
+	return 0;
+}
+
 int _rnd_system_entropy_init(void)
 {
 	int old;
@@ -127,7 +136,8 @@
 #include <locks.h>
 #include "egd.h"
 
-static int device_fd = -1;
+int _gnutls_urandom_fd = -1;
+static mode_t _gnutls_urandom_fd_mode = 0;
 
 static int _rnd_get_system_entropy_urandom(void* _rnd, size_t size)
 {
@@ -137,7 +147,7 @@
 	for (done = 0; done < size;) {
 		int res;
 		do {
-			res = read(device_fd, rnd + done, size - done);
+			res = read(_gnutls_urandom_fd, rnd + done, size - done);
 		} while (res < 0 && errno == EINTR);
 
 		if (res <= 0) {
@@ -168,7 +178,7 @@
 
 	for (done = 0; done < size;) {
 		res =
-		    _rndegd_read(&device_fd, rnd + done, size - done);
+		    _rndegd_read(&_gnutls_urandom_fd, rnd + done, size - done);
 		if (res <= 0) {
 			if (res < 0) {
 				_gnutls_debug_log("Failed to read egd.\n");
@@ -186,31 +196,53 @@
 
 get_entropy_func _rnd_get_system_entropy = NULL;
 
+int _rnd_system_entropy_check(void)
+{
+	int ret;
+	struct stat st;
+
+	ret = fstat(_gnutls_urandom_fd, &st);
+	if (ret < 0 || st.st_mode != _gnutls_urandom_fd_mode) {
+		return _rnd_system_entropy_init();
+	}
+	return 0;
+}
+
 int _rnd_system_entropy_init(void)
 {
-int old;
+	int old;
+	struct stat st;
 	
-	device_fd = open("/dev/urandom", O_RDONLY);
-	if (device_fd < 0) {
+	_gnutls_urandom_fd = open("/dev/urandom", O_RDONLY);
+	if (_gnutls_urandom_fd < 0) {
 		_gnutls_debug_log("Cannot open urandom!\n");
 		goto fallback;
 	}
 
-	old = fcntl(device_fd, F_GETFD);
+	old = fcntl(_gnutls_urandom_fd, F_GETFD);
 	if (old != -1)
-		fcntl(device_fd, F_SETFD, old | FD_CLOEXEC);
+		fcntl(_gnutls_urandom_fd, F_SETFD, old | FD_CLOEXEC);
+
+	if (fstat(_gnutls_urandom_fd, &st) >= 0) {
+		_gnutls_urandom_fd_mode = st.st_mode;
+	}
 
 	_rnd_get_system_entropy = _rnd_get_system_entropy_urandom;
 
 	return 0;
 fallback:
-	device_fd = _rndegd_connect_socket();
-	if (device_fd < 0) {
+	_gnutls_urandom_fd = _rndegd_connect_socket();
+	if (_gnutls_urandom_fd < 0) {
 		_gnutls_debug_log("Cannot open egd socket!\n");
 		return
 			gnutls_assert_val
 			(GNUTLS_E_RANDOM_DEVICE_ERROR);
 	}
+
+	if (fstat(_gnutls_urandom_fd, &st) >= 0) {
+		_gnutls_urandom_fd_mode = st.st_mode;
+	}
+
 	_rnd_get_system_entropy = _rnd_get_system_entropy_egd;
 	
 	return 0;
@@ -218,9 +250,9 @@
 
 void _rnd_system_entropy_deinit(void)
 {
-	if (device_fd >= 0) {
-		close(device_fd);
-		device_fd = -1;
+	if (_gnutls_urandom_fd >= 0) {
+		close(_gnutls_urandom_fd);
+		_gnutls_urandom_fd = -1;
 	}
 }
 #endif
diff -urN gnutls-3.3.8.orig/lib/nettle/rnd-common.h gnutls-3.3.8/lib/nettle/rnd-common.h
--- gnutls-3.3.8.orig/lib/nettle/rnd-common.h	2014-07-29 22:22:47.000000000 +0200
+++ gnutls-3.3.8/lib/nettle/rnd-common.h	2014-11-18 09:52:15.420936655 +0100
@@ -50,6 +50,7 @@
 void _rnd_get_event(struct event_st *e);
 
 int _rnd_system_entropy_init(void);
+int _rnd_system_entropy_check(void);
 void _rnd_system_entropy_deinit(void);
 
 typedef int (*get_entropy_func)(void* rnd, size_t size);
diff -urN gnutls-3.3.8.orig/lib/random.h gnutls-3.3.8/lib/random.h
--- gnutls-3.3.8.orig/lib/random.h	2014-07-29 22:22:47.000000000 +0200
+++ gnutls-3.3.8/lib/random.h	2014-11-18 09:58:19.458267672 +0100
@@ -48,4 +48,13 @@
 void _gnutls_rnd_deinit(void);
 int _gnutls_rnd_init(void);
 
+inline static int _gnutls_rnd_check(void)
+{
+       return _gnutls_rnd_ops.check(gnutls_rnd_ctx);
+}
+
+#ifndef _WIN32
+extern int _gnutls_urandom_fd;
+#endif
+
 #endif
diff -urN gnutls-3.3.8.orig/tests/init_fds.c gnutls-3.3.8/tests/init_fds.c
--- gnutls-3.3.8.orig/tests/init_fds.c	1970-01-01 01:00:00.000000000 +0100
+++ gnutls-3.3.8/tests/init_fds.c	2014-11-18 10:01:10.484365302 +0100
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 Nikos Mavrogiannopoulos
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#include "utils.h"
+
+/* See <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=760476>. */
+
+void doit(void)
+{
+#ifndef _WIN32
+	int res;
+	unsigned i;
+	int serial = 0;
+	char buf[128];
+
+	res = read(3, buf, 16);
+	if (res == 16)
+		serial = 1;
+
+	/* close all descriptors */
+	for (i=3;i<1024;i++)
+		close(i);
+
+	res = gnutls_global_init();
+	if (res != 0)
+		fail("global_init\n");
+
+	if (serial != 0) {
+		res = read(3, buf, 16);
+		if (res != 16) {
+			fail("could not open fd, or OS doesn't assign fds in a serial way (%d)\n", res);
+		}
+	}
+
+	res = gnutls_global_init();
+	if (res != 0)
+		fail("global_init2\n");
+
+	gnutls_rnd_refresh();
+
+	res = gnutls_rnd(GNUTLS_RND_RANDOM, buf, sizeof(buf));
+	if (res != 0)
+		fail("gnutls_rnd\n");
+
+	gnutls_global_deinit();
+
+	if (debug)
+		success("init-close success\n");
+#else
+	return;
+#endif
+}
diff -urN gnutls-3.3.8.orig/tests/Makefile.am gnutls-3.3.8/tests/Makefile.am
--- gnutls-3.3.8.orig/tests/Makefile.am	2014-09-13 13:08:01.000000000 +0200
+++ gnutls-3.3.8/tests/Makefile.am	2014-11-18 10:01:10.483365293 +0100
@@ -84,7 +84,7 @@
 	 mini-cert-status mini-rsa-psk global-init sec-params \
 	 fips-test mini-global-load name-constraints x509-extensions \
 	 long-session-id mini-x509-callbacks-intr \
-	 crlverify
+	 crlverify init_fds
 
 if ENABLE_OCSP
 ctests += ocsp