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