randomenv.cpp raw

   1  // Copyright (c) 2009-2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-2022 The Limenka developers
   3  // Distributed under the MIT software license, see the accompanying
   4  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  
   6  #include <limenka-build-config.h> // IWYU pragma: keep
   7  
   8  #include <randomenv.h>
   9  
  10  #include <clientversion.h>
  11  #include <compat/compat.h>
  12  #include <compat/cpuid.h>
  13  #include <crypto/sha512.h>
  14  #include <span.h>
  15  #include <support/cleanse.h>
  16  #include <util/time.h>
  17  
  18  #include <algorithm>
  19  #include <atomic>
  20  #include <cstdint>
  21  #include <cstring>
  22  #include <chrono>
  23  #include <climits>
  24  #include <thread>
  25  #include <vector>
  26  
  27  #include <sys/types.h> // must go before a number of other headers
  28  
  29  #ifdef WIN32
  30  #include <windows.h>
  31  #else
  32  #include <fcntl.h>
  33  #include <netinet/in.h>
  34  #include <sys/resource.h>
  35  #include <sys/socket.h>
  36  #include <sys/stat.h>
  37  #include <sys/time.h>
  38  #include <sys/utsname.h>
  39  #include <unistd.h>
  40  #endif
  41  #if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS
  42  #include <ifaddrs.h>
  43  #endif
  44  #ifdef HAVE_SYSCTL
  45  #include <sys/sysctl.h>
  46  #ifdef HAVE_VM_VM_PARAM_H
  47  #include <vm/vm_param.h>
  48  #endif
  49  #ifdef HAVE_SYS_RESOURCES_H
  50  #include <sys/resources.h>
  51  #endif
  52  #ifdef HAVE_SYS_VMMETER_H
  53  #include <sys/vmmeter.h>
  54  #endif
  55  #endif
  56  #if defined(HAVE_STRONG_GETAUXVAL)
  57  #include <sys/auxv.h>
  58  #endif
  59  
  60  #ifndef _MSC_VER
  61  extern char** environ; // NOLINT(readability-redundant-declaration): Necessary on some platforms
  62  #endif
  63  
  64  namespace {
  65  
  66  /** Helper to easily feed data into a CSHA512.
  67   *
  68   * Note that this does not serialize the passed object (like stream.h's << operators do).
  69   * Its raw memory representation is used directly.
  70   */
  71  template<typename T>
  72  CSHA512& operator<<(CSHA512& hasher, const T& data) {
  73      static_assert(!std::is_same<typename std::decay<T>::type, char*>::value, "Calling operator<<(CSHA512, char*) is probably not what you want");
  74      static_assert(!std::is_same<typename std::decay<T>::type, unsigned char*>::value, "Calling operator<<(CSHA512, unsigned char*) is probably not what you want");
  75      static_assert(!std::is_same<typename std::decay<T>::type, const char*>::value, "Calling operator<<(CSHA512, const char*) is probably not what you want");
  76      static_assert(!std::is_same<typename std::decay<T>::type, const unsigned char*>::value, "Calling operator<<(CSHA512, const unsigned char*) is probably not what you want");
  77      hasher.Write((const unsigned char*)&data, sizeof(data));
  78      return hasher;
  79  }
  80  
  81  #ifndef WIN32
  82  void AddSockaddr(CSHA512& hasher, const struct sockaddr *addr)
  83  {
  84      if (addr == nullptr) return;
  85      switch (addr->sa_family) {
  86      case AF_INET:
  87          hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in));
  88          break;
  89      case AF_INET6:
  90          hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in6));
  91          break;
  92      default:
  93          hasher.Write((const unsigned char*)&addr->sa_family, sizeof(addr->sa_family));
  94      }
  95  }
  96  
  97  void AddFile(CSHA512& hasher, const char *path)
  98  {
  99      struct stat sb = {};
 100      int f = open(path, O_RDONLY);
 101      size_t total = 0;
 102      if (f != -1) {
 103          unsigned char fbuf[4096];
 104          int n;
 105          hasher.Write((const unsigned char*)&f, sizeof(f));
 106          if (fstat(f, &sb) == 0) hasher << sb;
 107          do {
 108              n = read(f, fbuf, sizeof(fbuf));
 109              if (n > 0) hasher.Write(fbuf, n);
 110              total += n;
 111              /* not bothering with EINTR handling. */
 112          } while (n == sizeof(fbuf) && total < 1048576); // Read only the first 1 Mbyte
 113          close(f);
 114      }
 115  }
 116  
 117  void AddPath(CSHA512& hasher, const char *path)
 118  {
 119      struct stat sb = {};
 120      if (stat(path, &sb) == 0) {
 121          hasher.Write((const unsigned char*)path, strlen(path) + 1);
 122          hasher << sb;
 123      }
 124  }
 125  #endif
 126  
 127  #ifdef HAVE_SYSCTL
 128  template<int... S>
 129  void AddSysctl(CSHA512& hasher)
 130  {
 131      int CTL[sizeof...(S)] = {S...};
 132      unsigned char buffer[65536];
 133      size_t siz = 65536;
 134      int ret = sysctl(CTL, sizeof...(S), buffer, &siz, nullptr, 0);
 135      if (ret == 0 || (ret == -1 && errno == ENOMEM)) {
 136          hasher << sizeof(CTL);
 137          hasher.Write((const unsigned char*)CTL, sizeof(CTL));
 138          if (siz > sizeof(buffer)) siz = sizeof(buffer);
 139          hasher << siz;
 140          hasher.Write(buffer, siz);
 141      }
 142  }
 143  #endif
 144  
 145  #ifdef HAVE_GETCPUID
 146  void inline AddCPUID(CSHA512& hasher, uint32_t leaf, uint32_t subleaf, uint32_t& ax, uint32_t& bx, uint32_t& cx, uint32_t& dx)
 147  {
 148      GetCPUID(leaf, subleaf, ax, bx, cx, dx);
 149      hasher << leaf << subleaf << ax << bx << cx << dx;
 150  }
 151  
 152  void AddAllCPUID(CSHA512& hasher)
 153  {
 154      uint32_t ax, bx, cx, dx;
 155      // Iterate over all standard leaves
 156      AddCPUID(hasher, 0, 0, ax, bx, cx, dx); // Returns max leaf in ax
 157      uint32_t max = ax;
 158      for (uint32_t leaf = 1; leaf <= max && leaf <= 0xFF; ++leaf) {
 159          uint32_t maxsub = 0;
 160          for (uint32_t subleaf = 0; subleaf <= 0xFF; ++subleaf) {
 161              AddCPUID(hasher, leaf, subleaf, ax, bx, cx, dx);
 162              // Iterate subleafs for leaf values 4, 7, 11, 13
 163              if (leaf == 4) {
 164                  if ((ax & 0x1f) == 0) break;
 165              } else if (leaf == 7) {
 166                  if (subleaf == 0) maxsub = ax;
 167                  if (subleaf == maxsub) break;
 168              } else if (leaf == 11) {
 169                  if ((cx & 0xff00) == 0) break;
 170              } else if (leaf == 13) {
 171                  if (ax == 0 && bx == 0 && cx == 0 && dx == 0) break;
 172              } else {
 173                  // For any other leaf, stop after subleaf 0.
 174                  break;
 175              }
 176          }
 177      }
 178      // Iterate over all extended leaves
 179      AddCPUID(hasher, 0x80000000, 0, ax, bx, cx, dx); // Returns max extended leaf in ax
 180      uint32_t ext_max = ax;
 181      for (uint32_t leaf = 0x80000001; leaf <= ext_max && leaf <= 0x800000FF; ++leaf) {
 182          AddCPUID(hasher, leaf, 0, ax, bx, cx, dx);
 183      }
 184  }
 185  #endif
 186  } // namespace
 187  
 188  void RandAddDynamicEnv(CSHA512& hasher)
 189  {
 190      // Various clocks
 191  #ifdef WIN32
 192      FILETIME ftime;
 193      GetSystemTimeAsFileTime(&ftime);
 194      hasher << ftime;
 195  #else
 196      struct timespec ts = {};
 197  #    ifdef CLOCK_MONOTONIC
 198      clock_gettime(CLOCK_MONOTONIC, &ts);
 199      hasher << ts;
 200  #    endif
 201  #    ifdef CLOCK_REALTIME
 202      clock_gettime(CLOCK_REALTIME, &ts);
 203      hasher << ts;
 204  #    endif
 205  #    ifdef CLOCK_BOOTTIME
 206      clock_gettime(CLOCK_BOOTTIME, &ts);
 207      hasher << ts;
 208  #    endif
 209      // gettimeofday is available on all UNIX systems, but only has microsecond precision.
 210      struct timeval tv = {};
 211      gettimeofday(&tv, nullptr);
 212      hasher << tv;
 213  #endif
 214      // Probably redundant, but also use all the standard library clocks:
 215      hasher << std::chrono::system_clock::now().time_since_epoch().count();
 216      hasher << std::chrono::steady_clock::now().time_since_epoch().count();
 217      hasher << std::chrono::high_resolution_clock::now().time_since_epoch().count();
 218  
 219  #ifndef WIN32
 220      // Current resource usage.
 221      struct rusage usage = {};
 222      if (getrusage(RUSAGE_SELF, &usage) == 0) hasher << usage;
 223  #endif
 224  
 225  #ifdef __linux__
 226      AddFile(hasher, "/proc/diskstats");
 227      AddFile(hasher, "/proc/vmstat");
 228      AddFile(hasher, "/proc/schedstat");
 229      AddFile(hasher, "/proc/zoneinfo");
 230      AddFile(hasher, "/proc/meminfo");
 231      AddFile(hasher, "/proc/softirqs");
 232      AddFile(hasher, "/proc/stat");
 233      AddFile(hasher, "/proc/self/schedstat");
 234      AddFile(hasher, "/proc/self/status");
 235  #endif
 236  
 237  #ifdef HAVE_SYSCTL
 238  #  ifdef CTL_KERN
 239  #    if defined(KERN_PROC) && defined(KERN_PROC_ALL)
 240      AddSysctl<CTL_KERN, KERN_PROC, KERN_PROC_ALL>(hasher);
 241  #    endif
 242  #  endif
 243  #  ifdef CTL_HW
 244  #    ifdef HW_DISKSTATS
 245      AddSysctl<CTL_HW, HW_DISKSTATS>(hasher);
 246  #    endif
 247  #  endif
 248  #  ifdef CTL_VM
 249  #    ifdef VM_LOADAVG
 250      AddSysctl<CTL_VM, VM_LOADAVG>(hasher);
 251  #    endif
 252  #    ifdef VM_TOTAL
 253      AddSysctl<CTL_VM, VM_TOTAL>(hasher);
 254  #    endif
 255  #    ifdef VM_METER
 256      AddSysctl<CTL_VM, VM_METER>(hasher);
 257  #    endif
 258  #  endif
 259  #endif
 260  
 261      // Stack and heap location
 262      void* addr = malloc(4097);
 263      hasher << &addr << addr;
 264      free(addr);
 265  }
 266  
 267  void RandAddStaticEnv(CSHA512& hasher)
 268  {
 269      // Some compile-time static properties
 270      hasher << (CHAR_MIN < 0) << sizeof(void*) << sizeof(long) << sizeof(int);
 271  #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
 272      hasher << __GNUC__ << __GNUC_MINOR__ << __GNUC_PATCHLEVEL__;
 273  #endif
 274  #ifdef _MSC_VER
 275      hasher << _MSC_VER;
 276  #endif
 277      hasher << __cplusplus;
 278  #ifdef _XOPEN_VERSION
 279      hasher << _XOPEN_VERSION;
 280  #endif
 281  #ifdef __VERSION__
 282      const char* COMPILER_VERSION = __VERSION__;
 283      hasher.Write((const unsigned char*)COMPILER_VERSION, strlen(COMPILER_VERSION) + 1);
 284  #endif
 285  
 286      // Limenka client version
 287      hasher << CLIENT_VERSION;
 288  
 289  #if defined(HAVE_STRONG_GETAUXVAL)
 290      // Information available through getauxval()
 291  #  ifdef AT_HWCAP
 292      hasher << getauxval(AT_HWCAP);
 293  #  endif
 294  #  ifdef AT_HWCAP2
 295      hasher << getauxval(AT_HWCAP2);
 296  #  endif
 297  #  ifdef AT_RANDOM
 298      const unsigned char* random_aux = (const unsigned char*)getauxval(AT_RANDOM);
 299      if (random_aux) hasher.Write(random_aux, 16);
 300  #  endif
 301  #  ifdef AT_PLATFORM
 302      const char* platform_str = (const char*)getauxval(AT_PLATFORM);
 303      if (platform_str) hasher.Write((const unsigned char*)platform_str, strlen(platform_str) + 1);
 304  #  endif
 305  #  ifdef AT_EXECFN
 306      const char* exec_str = (const char*)getauxval(AT_EXECFN);
 307      if (exec_str) hasher.Write((const unsigned char*)exec_str, strlen(exec_str) + 1);
 308  #  endif
 309  #endif // HAVE_STRONG_GETAUXVAL
 310  
 311  #ifdef HAVE_GETCPUID
 312      AddAllCPUID(hasher);
 313  #endif
 314  
 315      // Memory locations
 316      hasher << &hasher << &RandAddStaticEnv << &malloc << &errno << &environ;
 317  
 318      // Hostname
 319  #ifdef WIN32
 320      constexpr DWORD max_size = MAX_COMPUTERNAME_LENGTH + 1;
 321      char hname[max_size];
 322      DWORD size = max_size;
 323      if (GetComputerNameA(hname, &size) != 0) {
 324          hasher.Write(UCharCast(hname), size);
 325      }
 326  #else
 327      char hname[256];
 328      if (gethostname(hname, 256) == 0) {
 329          hasher.Write((const unsigned char*)hname, strnlen(hname, 256));
 330      }
 331  #endif
 332  
 333  #if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS
 334      // Network interfaces
 335      struct ifaddrs *ifad = nullptr;
 336      getifaddrs(&ifad);
 337      struct ifaddrs *ifit = ifad;
 338      while (ifit != nullptr) {
 339          hasher.Write((const unsigned char*)&ifit, sizeof(ifit));
 340          hasher.Write((const unsigned char*)ifit->ifa_name, strlen(ifit->ifa_name) + 1);
 341          hasher.Write((const unsigned char*)&ifit->ifa_flags, sizeof(ifit->ifa_flags));
 342          AddSockaddr(hasher, ifit->ifa_addr);
 343          AddSockaddr(hasher, ifit->ifa_netmask);
 344          AddSockaddr(hasher, ifit->ifa_dstaddr);
 345          ifit = ifit->ifa_next;
 346      }
 347      freeifaddrs(ifad);
 348  #endif
 349  
 350  #ifndef WIN32
 351      // UNIX kernel information
 352      struct utsname name;
 353      if (uname(&name) != -1) {
 354          hasher.Write((const unsigned char*)&name.sysname, strlen(name.sysname) + 1);
 355          hasher.Write((const unsigned char*)&name.nodename, strlen(name.nodename) + 1);
 356          hasher.Write((const unsigned char*)&name.release, strlen(name.release) + 1);
 357          hasher.Write((const unsigned char*)&name.version, strlen(name.version) + 1);
 358          hasher.Write((const unsigned char*)&name.machine, strlen(name.machine) + 1);
 359      }
 360  
 361      /* Path and filesystem provided data */
 362      AddPath(hasher, "/");
 363      AddPath(hasher, ".");
 364      AddPath(hasher, "/tmp");
 365      AddPath(hasher, "/home");
 366      AddPath(hasher, "/proc");
 367  #ifdef __linux__
 368      AddFile(hasher, "/proc/cmdline");
 369      AddFile(hasher, "/proc/cpuinfo");
 370      AddFile(hasher, "/proc/version");
 371  #endif
 372      AddFile(hasher, "/etc/passwd");
 373      AddFile(hasher, "/etc/group");
 374      AddFile(hasher, "/etc/hosts");
 375      AddFile(hasher, "/etc/resolv.conf");
 376      AddFile(hasher, "/etc/timezone");
 377      AddFile(hasher, "/etc/localtime");
 378  #endif
 379  
 380      // For MacOS/BSDs, gather data through sysctl instead of /proc. Not all of these
 381      // will exist on every system.
 382  #ifdef HAVE_SYSCTL
 383  #  ifdef CTL_HW
 384  #    ifdef HW_MACHINE
 385      AddSysctl<CTL_HW, HW_MACHINE>(hasher);
 386  #    endif
 387  #    ifdef HW_MODEL
 388      AddSysctl<CTL_HW, HW_MODEL>(hasher);
 389  #    endif
 390  #    ifdef HW_NCPU
 391      AddSysctl<CTL_HW, HW_NCPU>(hasher);
 392  #    endif
 393  #    ifdef HW_PHYSMEM
 394      AddSysctl<CTL_HW, HW_PHYSMEM>(hasher);
 395  #    endif
 396  #    ifdef HW_USERMEM
 397      AddSysctl<CTL_HW, HW_USERMEM>(hasher);
 398  #    endif
 399  #    ifdef HW_MACHINE_ARCH
 400      AddSysctl<CTL_HW, HW_MACHINE_ARCH>(hasher);
 401  #    endif
 402  #    ifdef HW_REALMEM
 403      AddSysctl<CTL_HW, HW_REALMEM>(hasher);
 404  #    endif
 405  #    ifdef HW_CPU_FREQ
 406      AddSysctl<CTL_HW, HW_CPU_FREQ>(hasher);
 407  #    endif
 408  #    ifdef HW_BUS_FREQ
 409      AddSysctl<CTL_HW, HW_BUS_FREQ>(hasher);
 410  #    endif
 411  #    ifdef HW_CACHELINE
 412      AddSysctl<CTL_HW, HW_CACHELINE>(hasher);
 413  #    endif
 414  #  endif
 415  #  ifdef CTL_KERN
 416  #    ifdef KERN_BOOTFILE
 417       AddSysctl<CTL_KERN, KERN_BOOTFILE>(hasher);
 418  #    endif
 419  #    ifdef KERN_BOOTTIME
 420       AddSysctl<CTL_KERN, KERN_BOOTTIME>(hasher);
 421  #    endif
 422  #    ifdef KERN_CLOCKRATE
 423       AddSysctl<CTL_KERN, KERN_CLOCKRATE>(hasher);
 424  #    endif
 425  #    ifdef KERN_HOSTID
 426       AddSysctl<CTL_KERN, KERN_HOSTID>(hasher);
 427  #    endif
 428  #    ifdef KERN_HOSTUUID
 429       AddSysctl<CTL_KERN, KERN_HOSTUUID>(hasher);
 430  #    endif
 431  #    ifdef KERN_HOSTNAME
 432       AddSysctl<CTL_KERN, KERN_HOSTNAME>(hasher);
 433  #    endif
 434  #    ifdef KERN_OSRELDATE
 435       AddSysctl<CTL_KERN, KERN_OSRELDATE>(hasher);
 436  #    endif
 437  #    ifdef KERN_OSRELEASE
 438       AddSysctl<CTL_KERN, KERN_OSRELEASE>(hasher);
 439  #    endif
 440  #    ifdef KERN_OSREV
 441       AddSysctl<CTL_KERN, KERN_OSREV>(hasher);
 442  #    endif
 443  #    ifdef KERN_OSTYPE
 444       AddSysctl<CTL_KERN, KERN_OSTYPE>(hasher);
 445  #    endif
 446  #    ifdef KERN_POSIX1
 447       AddSysctl<CTL_KERN, KERN_OSREV>(hasher);
 448  #    endif
 449  #    ifdef KERN_VERSION
 450       AddSysctl<CTL_KERN, KERN_VERSION>(hasher);
 451  #    endif
 452  #  endif
 453  #endif
 454  
 455      // Env variables
 456      if (environ) {
 457          for (size_t i = 0; environ[i]; ++i) {
 458              hasher.Write((const unsigned char*)environ[i], strlen(environ[i]));
 459          }
 460      }
 461  
 462      // Process, thread, user, session, group, ... ids.
 463  #ifdef WIN32
 464      hasher << GetCurrentProcessId() << GetCurrentThreadId();
 465  #else
 466      hasher << getpid() << getppid() << getsid(0) << getpgid(0) << getuid() << geteuid() << getgid() << getegid();
 467  #endif
 468      hasher << std::this_thread::get_id();
 469  }
 470