diff --git a/configure b/configure index 200d1dc9..dcfd3c76 100755 --- a/configure +++ b/configure @@ -1875,6 +1875,73 @@ $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. @@ -6572,18 +6639,31 @@ fi done -for ac_header in a.out.h +for ac_header in a.out.h sys/random.h do : - ac_fn_c_check_header_mongrel "$LINENO" "a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default" -if test "x$ac_cv_header_a_out_h" = xyes; then : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF -#define HAVE_A_OUT_H 1 +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done +for ac_func in arc4random getrandom +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + ax_cxx_compile_cxx11_required=true diff --git a/configure.ac b/configure.ac index bc72017c..fe4e8a5a 100644 --- a/configure.ac +++ b/configure.ac @@ -72,7 +72,8 @@ AC_ARG_ENABLE(m32, AC_HEADER_STDC AC_SYS_LARGEFILE AX_PTHREAD -AC_CHECK_HEADERS([a.out.h]) +AC_CHECK_HEADERS([a.out.h sys/random.h]) +AC_CHECK_FUNCS([arc4random getrandom]) AX_CXX_COMPILE_STDCXX(11, noext, mandatory) diff --git a/src/common/linux/guid_creator.cc b/src/common/linux/guid_creator.cc index bfb308ee..c92e69f4 100644 --- a/src/common/linux/guid_creator.cc +++ b/src/common/linux/guid_creator.cc @@ -27,15 +27,22 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "common/linux/eintr_wrapper.h" #include "common/linux/guid_creator.h" #include +#include #include #include #include +#include #include #include +#if defined(HAVE_SYS_RANDOM_H) +#include +#endif + // // GUIDGenerator // @@ -61,28 +68,101 @@ class GUIDGenerator { } static bool CreateGUID(GUID *guid) { - InitOnce(); - guid->data1 = random(); - guid->data2 = (uint16_t)(random()); - guid->data3 = (uint16_t)(random()); - UInt32ToBytes(&guid->data4[0], random()); - UInt32ToBytes(&guid->data4[4], random()); +#if defined(HAVE_ARC4RANDOM) // Android, BSD, ... + CreateGuidFromArc4Random(guid); +#else // Linux + bool success = false; + +#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM) + success = CreateGUIDFromGetrandom(guid); +#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM + if (!success) { + success = CreateGUIDFromDevUrandom(guid); + } + + if (!success) { + CreateGUIDFromRand(guid); + success = true; + } +#endif + + // Put in the version according to RFC 4122. + guid->data3 &= 0x0fff; + guid->data3 |= 0x4000; + + // Put in the variant according to RFC 4122. + guid->data4[0] &= 0x3f; + guid->data4[0] |= 0x80; + return true; } private: +#ifdef HAVE_ARC4RANDOM + static void CreateGuidFromArc4Random(GUID *guid) { + char *buf = reinterpret_cast(guid); + + for (size_t i = 0; i < sizeof(GUID); i += sizeof(uint32_t)) { + uint32_t random_data = arc4random(); + + memcpy(buf + i, &random_data, sizeof(uint32_t)); + } + } +#else static void InitOnce() { pthread_once(&once_control, &InitOnceImpl); } static void InitOnceImpl() { - srandom(time(NULL)); + // time(NULL) is a very poor seed, so lacking anything better mix an + // address into it. We drop the four rightmost bits as they're likely to + // be 0 on almost all architectures. + srand(time(NULL) | ((uintptr_t)&once_control >> 4)); } static pthread_once_t once_control; + +#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM) + static bool CreateGUIDFromGetrandom(GUID *guid) { + char *buf = reinterpret_cast(guid); + int read_bytes = getrandom(buf, sizeof(GUID), GRND_NONBLOCK); + + return (read_bytes == static_cast(sizeof(GUID))); + } +#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM + + // Populate the GUID using random bytes read from /dev/urandom, returns false + // if the GUID wasn't fully populated with random data. + static bool CreateGUIDFromDevUrandom(GUID *guid) { + char *buf = reinterpret_cast(guid); + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + + if (fd == -1) { + return false; + } + + ssize_t read_bytes = HANDLE_EINTR(read(fd, buf, sizeof(GUID))); + close(fd); + + return (read_bytes == static_cast(sizeof(GUID))); + } + + // Populate the GUID using a stream of random bytes obtained from rand(). + static void CreateGUIDFromRand(GUID *guid) { + char *buf = reinterpret_cast(guid); + + InitOnce(); + + for (size_t i = 0; i < sizeof(GUID); i++) { + buf[i] = rand(); + } + } +#endif }; +#ifndef HAVE_ARC4RANDOM pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT; +#endif bool CreateGUID(GUID *guid) { return GUIDGenerator::CreateGUID(guid); diff --git a/src/config.h.in b/src/config.h.in index ee6a67ad..a436d6ba 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -1,11 +1,17 @@ /* src/config.h.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the `arc4random' function. */ +#undef HAVE_ARC4RANDOM + /* Define to 1 if you have the header file. */ #undef HAVE_A_OUT_H /* define if the compiler supports basic C++11 syntax */ #undef HAVE_CXX11 +/* Define to 1 if you have the `getrandom' function. */ +#undef HAVE_GETRANDOM + /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H @@ -27,6 +33,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RANDOM_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H