(root)/
tar-1.35/
gnu/
getrandom.c
       1  /* Obtain a series of random bytes.
       2  
       3     Copyright 2020-2023 Free Software Foundation, Inc.
       4  
       5     This file is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     This file is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Written by Paul Eggert.  */
      19  
      20  #include <config.h>
      21  
      22  #include <sys/random.h>
      23  
      24  #include <errno.h>
      25  #include <fcntl.h>
      26  #include <unistd.h>
      27  
      28  #if defined _WIN32 && ! defined __CYGWIN__
      29  # define WIN32_LEAN_AND_MEAN
      30  # include <windows.h>
      31  # if HAVE_BCRYPT_H
      32  #  include <bcrypt.h>
      33  # else
      34  #  define NTSTATUS LONG
      35  typedef void * BCRYPT_ALG_HANDLE;
      36  #  define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
      37  #  if HAVE_LIB_BCRYPT
      38  extern NTSTATUS WINAPI BCryptGenRandom (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG);
      39  #  endif
      40  # endif
      41  # if !HAVE_LIB_BCRYPT
      42  #  include <wincrypt.h>
      43  #  ifndef CRYPT_VERIFY_CONTEXT
      44  #   define CRYPT_VERIFY_CONTEXT 0xF0000000
      45  #  endif
      46  # endif
      47  #endif
      48  
      49  #include "minmax.h"
      50  
      51  #if defined _WIN32 && ! defined __CYGWIN__
      52  
      53  /* Don't assume that UNICODE is not defined.  */
      54  # undef LoadLibrary
      55  # define LoadLibrary LoadLibraryA
      56  # undef CryptAcquireContext
      57  # define CryptAcquireContext CryptAcquireContextA
      58  
      59  # if !HAVE_LIB_BCRYPT
      60  
      61  /* Avoid warnings from gcc -Wcast-function-type.  */
      62  #  define GetProcAddress \
      63      (void *) GetProcAddress
      64  
      65  /* BCryptGenRandom with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag works only
      66     starting with Windows 7.  */
      67  typedef NTSTATUS (WINAPI * BCryptGenRandomFuncType) (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG);
      68  static BCryptGenRandomFuncType BCryptGenRandomFunc = NULL;
      69  static BOOL initialized = FALSE;
      70  
      71  static void
      72  initialize (void)
      73  {
      74    HMODULE bcrypt = LoadLibrary ("bcrypt.dll");
      75    if (bcrypt != NULL)
      76      {
      77        BCryptGenRandomFunc =
      78          (BCryptGenRandomFuncType) GetProcAddress (bcrypt, "BCryptGenRandom");
      79      }
      80    initialized = TRUE;
      81  }
      82  
      83  # else
      84  
      85  #  define BCryptGenRandomFunc BCryptGenRandom
      86  
      87  # endif
      88  
      89  #else
      90  /* These devices exist on all platforms except native Windows.  */
      91  
      92  /* Name of a device through which the kernel returns high quality random
      93     numbers, from an entropy pool.  When the pool is empty, the call blocks
      94     until entropy sources have added enough bits of entropy.  */
      95  # ifndef NAME_OF_RANDOM_DEVICE
      96  #  define NAME_OF_RANDOM_DEVICE "/dev/random"
      97  # endif
      98  
      99  /* Name of a device through which the kernel returns random or pseudo-random
     100     numbers.  It uses an entropy pool, but, in order to avoid blocking, adds
     101     bits generated by a pseudo-random number generator, as needed.  */
     102  # ifndef NAME_OF_NONCE_DEVICE
     103  #  define NAME_OF_NONCE_DEVICE "/dev/urandom"
     104  # endif
     105  
     106  #endif
     107  
     108  /* Set BUFFER (of size LENGTH) to random bytes under the control of FLAGS.
     109     Return the number of bytes written (> 0).
     110     Upon error, return -1 and set errno.  */
     111  ssize_t
     112  getrandom (void *buffer, size_t length, unsigned int flags)
     113  #undef getrandom
     114  {
     115  #if defined _WIN32 && ! defined __CYGWIN__
     116    /* BCryptGenRandom, defined in <bcrypt.h>
     117       <https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom>
     118       with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag
     119       works in Windows 7 and newer.  */
     120    static int bcrypt_not_working /* = 0 */;
     121    if (!bcrypt_not_working)
     122      {
     123  # if !HAVE_LIB_BCRYPT
     124        if (!initialized)
     125          initialize ();
     126  # endif
     127        if (BCryptGenRandomFunc != NULL
     128            && BCryptGenRandomFunc (NULL, buffer, length,
     129                                    BCRYPT_USE_SYSTEM_PREFERRED_RNG)
     130               == 0 /*STATUS_SUCCESS*/)
     131          return length;
     132        bcrypt_not_working = 1;
     133      }
     134  # if !HAVE_LIB_BCRYPT
     135    /* CryptGenRandom, defined in <wincrypt.h>
     136       <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom>
     137       works in older releases as well, but is now deprecated.
     138       CryptAcquireContext, defined in <wincrypt.h>
     139       <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta>  */
     140    {
     141      static int crypt_initialized /* = 0 */;
     142      static HCRYPTPROV provider;
     143      if (!crypt_initialized)
     144        {
     145          if (CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL,
     146                                   CRYPT_VERIFY_CONTEXT))
     147            crypt_initialized = 1;
     148          else
     149            crypt_initialized = -1;
     150        }
     151      if (crypt_initialized >= 0)
     152        {
     153          if (!CryptGenRandom (provider, length, buffer))
     154            {
     155              errno = EIO;
     156              return -1;
     157            }
     158          return length;
     159        }
     160    }
     161  # endif
     162    errno = ENOSYS;
     163    return -1;
     164  #elif HAVE_GETRANDOM
     165    return getrandom (buffer, length, flags);
     166  #else
     167    static int randfd[2] = { -1, -1 };
     168    bool devrandom = (flags & GRND_RANDOM) != 0;
     169    int fd = randfd[devrandom];
     170  
     171    if (fd < 0)
     172      {
     173        static char const randdevice[][MAX (sizeof NAME_OF_NONCE_DEVICE,
     174                                            sizeof NAME_OF_RANDOM_DEVICE)]
     175          = { NAME_OF_NONCE_DEVICE, NAME_OF_RANDOM_DEVICE };
     176        int oflags = (O_RDONLY + O_CLOEXEC
     177                      + (flags & GRND_NONBLOCK ? O_NONBLOCK : 0));
     178        fd = open (randdevice[devrandom], oflags);
     179        if (fd < 0)
     180          {
     181            if (errno == ENOENT || errno == ENOTDIR)
     182              errno = ENOSYS;
     183            return -1;
     184          }
     185        randfd[devrandom] = fd;
     186      }
     187  
     188    return read (fd, buffer, length);
     189  #endif
     190  }