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 #include +#include +#include +#include + /* 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 #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 +#endif + +#include +#include +#include +#include + +#include "utils.h" + +/* See . */ + +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