Changeset View
Changeset View
Standalone View
Standalone View
src/randomenv.cpp
// Copyright (c) 2009-2010 Satoshi Nakamoto | // Copyright (c) 2009-2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2019 The Bitcoin Core developers | // Copyright (c) 2009-2019 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#if defined(HAVE_CONFIG_H) | |||||
#include <config/bitcoin-config.h> | |||||
#endif | |||||
#include <randomenv.h> | #include <randomenv.h> | ||||
#include <clientversion.h> | |||||
#include <crypto/sha512.h> | #include <crypto/sha512.h> | ||||
#include <support/cleanse.h> | #include <support/cleanse.h> | ||||
#include <util/time.h> // for GetTime() | #include <util/time.h> // for GetTime() | ||||
#ifdef WIN32 | #ifdef WIN32 | ||||
#include <compat.h> // for Windows API | #include <compat.h> // for Windows API | ||||
#endif | #endif | ||||
#include <algorithm> | #include <algorithm> | ||||
#include <chrono> | #include <chrono> | ||||
#include <climits> | |||||
#include <thread> | #include <thread> | ||||
#include <vector> | #include <vector> | ||||
#include <cstdint> | #include <cstdint> | ||||
#include <cstring> | #include <cstring> | ||||
#ifndef WIN32 | #ifndef WIN32 | ||||
#include <sys/types.h> // must go before a number of other headers | |||||
#include <fcntl.h> | |||||
#include <netinet/in.h> | |||||
#include <sys/resource.h> | |||||
#include <sys/socket.h> | |||||
#include <sys/stat.h> | |||||
#include <sys/time.h> | #include <sys/time.h> | ||||
#include <sys/types.h> | #include <sys/utsname.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#endif | #endif | ||||
#ifdef __MACH__ | #ifdef __MACH__ | ||||
#include <mach/clock.h> | #include <mach/clock.h> | ||||
#include <mach/mach.h> | #include <mach/mach.h> | ||||
#include <mach/mach_time.h> | #include <mach/mach_time.h> | ||||
#endif | #endif | ||||
#if HAVE_DECL_GETIFADDRS | |||||
#include <ifaddrs.h> | |||||
#endif | |||||
//! Necessary on some platforms | |||||
extern char **environ; | |||||
namespace { | namespace { | ||||
void RandAddSeedPerfmon(CSHA512 &hasher) { | void RandAddSeedPerfmon(CSHA512 &hasher) { | ||||
#ifdef WIN32 | #ifdef WIN32 | ||||
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom | // Don't need this on Linux, OpenSSL automatically uses /dev/urandom | ||||
// Seed with the entire set of perfmon data | // Seed with the entire set of perfmon data | ||||
// This can take up to 2 seconds, so only do it every 10 minutes | // This can take up to 2 seconds, so only do it every 10 minutes | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | template <typename T> CSHA512 &operator<<(CSHA512 &hasher, const T &data) { | ||||
static_assert( | static_assert( | ||||
!std::is_same<typename std::decay<T>::type, const uint8_t *>::value, | !std::is_same<typename std::decay<T>::type, const uint8_t *>::value, | ||||
"Calling operator<<(CSHA512, const uint8_t*) is " | "Calling operator<<(CSHA512, const uint8_t*) is " | ||||
"probably not what you want"); | "probably not what you want"); | ||||
hasher.Write((const uint8_t *)&data, sizeof(data)); | hasher.Write((const uint8_t *)&data, sizeof(data)); | ||||
return hasher; | return hasher; | ||||
} | } | ||||
#ifndef WIN32 | |||||
void AddSockaddr(CSHA512 &hasher, const struct sockaddr *addr) { | |||||
if (addr == nullptr) { | |||||
return; | |||||
} | |||||
switch (addr->sa_family) { | |||||
case AF_INET: | |||||
hasher.Write((const uint8_t *)addr, sizeof(sockaddr_in)); | |||||
break; | |||||
case AF_INET6: | |||||
hasher.Write((const uint8_t *)addr, sizeof(sockaddr_in6)); | |||||
break; | |||||
default: | |||||
hasher.Write((const uint8_t *)&addr->sa_family, | |||||
sizeof(addr->sa_family)); | |||||
} | |||||
} | |||||
void AddFile(CSHA512 &hasher, const char *path) { | |||||
struct stat sb = {}; | |||||
int f = open(path, O_RDONLY); | |||||
size_t total = 0; | |||||
if (f != -1) { | |||||
uint8_t fbuf[4096]; | |||||
int n; | |||||
hasher.Write((const uint8_t *)&f, sizeof(f)); | |||||
if (fstat(f, &sb) == 0) { | |||||
hasher << sb; | |||||
} | |||||
do { | |||||
n = read(f, fbuf, sizeof(fbuf)); | |||||
if (n > 0) { | |||||
hasher.Write(fbuf, n); | |||||
} | |||||
total += n; | |||||
/* not bothering with EINTR handling. */ | |||||
} while (n == sizeof(fbuf) && | |||||
total < 1048576); // Read only the first 1 Mbyte | |||||
close(f); | |||||
} | |||||
} | |||||
void AddPath(CSHA512 &hasher, const char *path) { | |||||
struct stat sb = {}; | |||||
if (stat(path, &sb) == 0) { | |||||
hasher.Write((const uint8_t *)path, strlen(path) + 1); | |||||
hasher << sb; | |||||
} | |||||
} | |||||
#endif | |||||
} // namespace | } // namespace | ||||
void RandAddDynamicEnv(CSHA512 &hasher) { | void RandAddDynamicEnv(CSHA512 &hasher) { | ||||
RandAddSeedPerfmon(hasher); | RandAddSeedPerfmon(hasher); | ||||
// Various clocks | // Various clocks | ||||
#ifdef WIN32 | #ifdef WIN32 | ||||
FILETIME ftime; | FILETIME ftime; | ||||
GetSystemTimeAsFileTime(&ftime); | GetSystemTimeAsFileTime(&ftime); | ||||
hasher << ftime; | hasher << ftime; | ||||
#else | #else | ||||
#ifndef __MACH__ | #ifndef __MACH__ | ||||
// On non-MacOS systems, use various clock_gettime() calls. | // On non-MacOS systems, use various clock_gettime() calls. | ||||
struct timespec ts; | struct timespec ts = {}; | ||||
#ifdef CLOCK_MONOTONIC | #ifdef CLOCK_MONOTONIC | ||||
clock_gettime(CLOCK_MONOTONIC, &ts); | clock_gettime(CLOCK_MONOTONIC, &ts); | ||||
hasher << ts.tv_sec << ts.tv_nsec; | hasher << ts; | ||||
#endif | #endif | ||||
#ifdef CLOCK_REALTIME | #ifdef CLOCK_REALTIME | ||||
clock_gettime(CLOCK_REALTIME, &ts); | clock_gettime(CLOCK_REALTIME, &ts); | ||||
hasher << ts.tv_sec << ts.tv_nsec; | hasher << ts; | ||||
#endif | #endif | ||||
#ifdef CLOCK_BOOTTIME | #ifdef CLOCK_BOOTTIME | ||||
clock_gettime(CLOCK_BOOTTIME, &ts); | clock_gettime(CLOCK_BOOTTIME, &ts); | ||||
hasher << ts.tv_sec << ts.tv_nsec; | hasher << ts.tv_sec << ts.tv_nsec; | ||||
#endif | #endif | ||||
#else | #else | ||||
// On MacOS use mach_absolute_time (number of CPU ticks since boot) as a | // On MacOS use mach_absolute_time (number of CPU ticks since boot) as a | ||||
// replacement for CLOCK_MONOTONIC, and clock_get_time for CALENDAR_CLOCK as | // replacement for CLOCK_MONOTONIC, and clock_get_time for CALENDAR_CLOCK as | ||||
// a replacement for CLOCK_REALTIME. | // a replacement for CLOCK_REALTIME. | ||||
hasher << mach_absolute_time(); | hasher << mach_absolute_time(); | ||||
// From https://gist.github.com/jbenet/1087739 | // From https://gist.github.com/jbenet/1087739 | ||||
clock_serv_t cclock; | clock_serv_t cclock; | ||||
mach_timespec_t mts; | mach_timespec_t mts = {}; | ||||
if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) == | if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) == | ||||
KERN_SUCCESS && | KERN_SUCCESS && | ||||
clock_get_time(cclock, &mts) == KERN_SUCCESS) { | clock_get_time(cclock, &mts) == KERN_SUCCESS) { | ||||
hasher << mts.tv_sec << mts.tv_nsec; | hasher << mts; | ||||
mach_port_deallocate(mach_task_self(), cclock); | mach_port_deallocate(mach_task_self(), cclock); | ||||
} | } | ||||
#endif | #endif | ||||
// gettimeofday is available on all UNIX systems, but only has microsecond | // gettimeofday is available on all UNIX systems, but only has microsecond | ||||
// precision. | // precision. | ||||
struct timeval tv; | struct timeval tv = {}; | ||||
gettimeofday(&tv, nullptr); | gettimeofday(&tv, nullptr); | ||||
hasher << tv.tv_sec << tv.tv_usec; | hasher << tv; | ||||
#endif | #endif | ||||
// Probably redundant, but also use all the clocks C++11 provides: | // Probably redundant, but also use all the clocks C++11 provides: | ||||
hasher << std::chrono::system_clock::now().time_since_epoch().count(); | hasher << std::chrono::system_clock::now().time_since_epoch().count(); | ||||
hasher << std::chrono::steady_clock::now().time_since_epoch().count(); | hasher << std::chrono::steady_clock::now().time_since_epoch().count(); | ||||
hasher | hasher | ||||
<< std::chrono::high_resolution_clock::now().time_since_epoch().count(); | << std::chrono::high_resolution_clock::now().time_since_epoch().count(); | ||||
#ifndef WIN32 | |||||
// Current resource usage. | |||||
struct rusage usage = {}; | |||||
if (getrusage(RUSAGE_SELF, &usage) == 0) { | |||||
hasher << usage; | |||||
} | |||||
#endif | |||||
#ifdef __linux__ | |||||
AddFile(hasher, "/proc/diskstats"); | |||||
AddFile(hasher, "/proc/vmstat"); | |||||
AddFile(hasher, "/proc/schedstat"); | |||||
AddFile(hasher, "/proc/zoneinfo"); | |||||
AddFile(hasher, "/proc/meminfo"); | |||||
AddFile(hasher, "/proc/softirqs"); | |||||
AddFile(hasher, "/proc/stat"); | |||||
AddFile(hasher, "/proc/self/schedstat"); | |||||
AddFile(hasher, "/proc/self/status"); | |||||
#endif | |||||
// Stack and heap location | |||||
void *addr = malloc(4097); | |||||
hasher << &addr << addr; | |||||
free(addr); | |||||
} | } | ||||
void RandAddStaticEnv(CSHA512 &hasher) { | void RandAddStaticEnv(CSHA512 &hasher) { | ||||
// Some compile-time static properties | |||||
hasher << (CHAR_MIN < 0) << sizeof(void *) << sizeof(long) << sizeof(int); | |||||
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) | |||||
hasher << __GNUC__ << __GNUC_MINOR__ << __GNUC_PATCHLEVEL__; | |||||
#endif | |||||
#ifdef _MSC_VER | |||||
hasher << _MSC_VER; | |||||
#endif | |||||
hasher << __cplusplus; | |||||
#ifdef _XOPEN_VERSION | |||||
hasher << _XOPEN_VERSION; | |||||
#endif | |||||
#ifdef __VERSION__ | |||||
const char *COMPILER_VERSION = __VERSION__; | |||||
hasher.Write((const uint8_t *)COMPILER_VERSION, | |||||
strlen(COMPILER_VERSION) + 1); | |||||
#endif | |||||
// Bitcoin client version | |||||
hasher << CLIENT_VERSION; | |||||
// Memory locations | |||||
hasher << &hasher << &RandAddStaticEnv << &malloc << &errno << &environ; | |||||
// Hostname | |||||
char hname[256]; | |||||
if (gethostname(hname, 256) == 0) { | |||||
hasher.Write((const uint8_t *)hname, strnlen(hname, 256)); | |||||
} | |||||
#if HAVE_DECL_GETIFADDRS | |||||
// Network interfaces | |||||
struct ifaddrs *ifad = NULL; | |||||
getifaddrs(&ifad); | |||||
struct ifaddrs *ifit = ifad; | |||||
while (ifit != NULL) { | |||||
hasher.Write((const uint8_t *)&ifit, sizeof(ifit)); | |||||
hasher.Write((const uint8_t *)ifit->ifa_name, | |||||
strlen(ifit->ifa_name) + 1); | |||||
hasher.Write((const uint8_t *)&ifit->ifa_flags, | |||||
sizeof(ifit->ifa_flags)); | |||||
AddSockaddr(hasher, ifit->ifa_addr); | |||||
AddSockaddr(hasher, ifit->ifa_netmask); | |||||
AddSockaddr(hasher, ifit->ifa_dstaddr); | |||||
ifit = ifit->ifa_next; | |||||
} | |||||
freeifaddrs(ifad); | |||||
#endif | |||||
#ifndef WIN32 | |||||
// UNIX kernel information | |||||
struct utsname name; | |||||
if (uname(&name) != -1) { | |||||
hasher.Write((const uint8_t *)&name.sysname, strlen(name.sysname) + 1); | |||||
hasher.Write((const uint8_t *)&name.nodename, | |||||
strlen(name.nodename) + 1); | |||||
hasher.Write((const uint8_t *)&name.release, strlen(name.release) + 1); | |||||
hasher.Write((const uint8_t *)&name.version, strlen(name.version) + 1); | |||||
hasher.Write((const uint8_t *)&name.machine, strlen(name.machine) + 1); | |||||
} | |||||
/* Path and filesystem provided data */ | |||||
AddPath(hasher, "/"); | |||||
AddPath(hasher, "."); | |||||
AddPath(hasher, "/tmp"); | |||||
AddPath(hasher, "/home"); | |||||
AddPath(hasher, "/proc"); | |||||
#ifdef __linux__ | |||||
AddFile(hasher, "/proc/cmdline"); | |||||
AddFile(hasher, "/proc/cpuinfo"); | |||||
AddFile(hasher, "/proc/version"); | |||||
#endif | |||||
AddFile(hasher, "/etc/passwd"); | |||||
AddFile(hasher, "/etc/group"); | |||||
AddFile(hasher, "/etc/hosts"); | |||||
AddFile(hasher, "/etc/resolv.conf"); | |||||
AddFile(hasher, "/etc/timezone"); | |||||
AddFile(hasher, "/etc/localtime"); | |||||
/* TODO: sysctl's for OSX to fetch information not available from /proc */ | |||||
#endif | |||||
// Env variables | |||||
if (environ) { | |||||
for (size_t i = 0; environ[i]; ++i) { | |||||
hasher.Write((const uint8_t *)environ[i], strlen(environ[i])); | |||||
} | |||||
} | |||||
// Process, thread, user, session, group, ... ids. | |||||
#ifdef WIN32 | #ifdef WIN32 | ||||
hasher << GetCurrentProcessId() << GetCurrentThreadId(); | hasher << GetCurrentProcessId() << GetCurrentThreadId(); | ||||
#else | #else | ||||
hasher << getpid(); | hasher << getpid() << getppid() << getsid(0) << getpgid(0) << getuid() | ||||
<< geteuid() << getgid() << getegid(); | |||||
#endif | #endif | ||||
hasher << std::this_thread::get_id(); | hasher << std::this_thread::get_id(); | ||||
} | } |