(root)/
findutils-4.9.0/
gnulib-tests/
getrandom.c
       1  /* Obtain a series of random bytes.
       2  
       3     Copyright 2020-2022 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 <stdbool.h>
      27  #include <unistd.h>
      28  
      29  #if defined _WIN32 && ! defined __CYGWIN__
      30  # define WIN32_LEAN_AND_MEAN
      31  # include <windows.h>
      32  # if HAVE_BCRYPT_H
      33  #  include <bcrypt.h>
      34  # else
      35  #  define NTSTATUS LONG
      36  typedef void * BCRYPT_ALG_HANDLE;
      37  #  define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
      38  #  if HAVE_LIB_BCRYPT
      39  extern NTSTATUS WINAPI BCryptGenRandom (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG);
      40  #  endif
      41  # endif
      42  # if !HAVE_LIB_BCRYPT
      43  #  include <wincrypt.h>
      44  #  ifndef CRYPT_VERIFY_CONTEXT
      45  #   define CRYPT_VERIFY_CONTEXT 0xF0000000
      46  #  endif
      47  # endif
      48  #endif
      49  
      50  #include "minmax.h"
      51  
      52  #if defined _WIN32 && ! defined __CYGWIN__
      53  
      54  /* Don't assume that UNICODE is not defined.  */
      55  # undef LoadLibrary
      56  # define LoadLibrary LoadLibraryA
      57  # undef CryptAcquireContext
      58  # define CryptAcquireContext CryptAcquireContextA
      59  
      60  # if !HAVE_LIB_BCRYPT
      61  
      62  /* Avoid warnings from gcc -Wcast-function-type.  */
      63  #  define GetProcAddress \
      64      (void *) GetProcAddress
      65  
      66  /* BCryptGenRandom with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag works only
      67     starting with Windows 7.  */
      68  typedef NTSTATUS (WINAPI * BCryptGenRandomFuncType) (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG);
      69  static BCryptGenRandomFuncType BCryptGenRandomFunc = NULL;
      70  static BOOL initialized = FALSE;
      71  
      72  static void
      73  initialize (void)
      74  {
      75    HMODULE bcrypt = LoadLibrary ("bcrypt.dll");
      76    if (bcrypt != NULL)
      77      {
      78        BCryptGenRandomFunc =
      79          (BCryptGenRandomFuncType) GetProcAddress (bcrypt, "BCryptGenRandom");
      80      }
      81    initialized = TRUE;
      82  }
      83  
      84  # else
      85  
      86  #  define BCryptGenRandomFunc BCryptGenRandom
      87  
      88  # endif
      89  
      90  #else
      91  /* These devices exist on all platforms except native Windows.  */
      92  
      93  /* Name of a device through which the kernel returns high quality random
      94     numbers, from an entropy pool.  When the pool is empty, the call blocks
      95     until entropy sources have added enough bits of entropy.  */
      96  # ifndef NAME_OF_RANDOM_DEVICE
      97  #  define NAME_OF_RANDOM_DEVICE "/dev/random"
      98  # endif
      99  
     100  /* Name of a device through which the kernel returns random or pseudo-random
     101     numbers.  It uses an entropy pool, but, in order to avoid blocking, adds
     102     bits generated by a pseudo-random number generator, as needed.  */
     103  # ifndef NAME_OF_NONCE_DEVICE
     104  #  define NAME_OF_NONCE_DEVICE "/dev/urandom"
     105  # endif
     106  
     107  #endif
     108  
     109  /* Set BUFFER (of size LENGTH) to random bytes under the control of FLAGS.
     110     Return the number of bytes written (> 0).
     111     Upon error, return -1 and set errno.  */
     112  ssize_t
     113  getrandom (void *buffer, size_t length, unsigned int flags)
     114  #undef getrandom
     115  {
     116  #if defined _WIN32 && ! defined __CYGWIN__
     117    /* BCryptGenRandom, defined in <bcrypt.h>
     118       <https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom>
     119       with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag
     120       works in Windows 7 and newer.  */
     121    static int bcrypt_not_working /* = 0 */;
     122    if (!bcrypt_not_working)
     123      {
     124  # if !HAVE_LIB_BCRYPT
     125        if (!initialized)
     126          initialize ();
     127  # endif
     128        if (BCryptGenRandomFunc != NULL
     129            && BCryptGenRandomFunc (NULL, buffer, length,
     130                                    BCRYPT_USE_SYSTEM_PREFERRED_RNG)
     131               == 0 /*STATUS_SUCCESS*/)
     132          return length;
     133        bcrypt_not_working = 1;
     134      }
     135  # if !HAVE_LIB_BCRYPT
     136    /* CryptGenRandom, defined in <wincrypt.h>
     137       <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom>
     138       works in older releases as well, but is now deprecated.
     139       CryptAcquireContext, defined in <wincrypt.h>
     140       <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta>  */
     141    {
     142      static int crypt_initialized /* = 0 */;
     143      static HCRYPTPROV provider;
     144      if (!crypt_initialized)
     145        {
     146          if (CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL,
     147                                   CRYPT_VERIFY_CONTEXT))
     148            crypt_initialized = 1;
     149          else
     150            crypt_initialized = -1;
     151        }
     152      if (crypt_initialized >= 0)
     153        {
     154          if (!CryptGenRandom (provider, length, buffer))
     155            {
     156              errno = EIO;
     157              return -1;
     158            }
     159          return length;
     160        }
     161    }
     162  # endif
     163    errno = ENOSYS;
     164    return -1;
     165  #elif HAVE_GETRANDOM
     166    return getrandom (buffer, length, flags);
     167  #else
     168    static int randfd[2] = { -1, -1 };
     169    bool devrandom = (flags & GRND_RANDOM) != 0;
     170    int fd = randfd[devrandom];
     171  
     172    if (fd < 0)
     173      {
     174        static char const randdevice[][MAX (sizeof NAME_OF_NONCE_DEVICE,
     175                                            sizeof NAME_OF_RANDOM_DEVICE)]
     176          = { NAME_OF_NONCE_DEVICE, NAME_OF_RANDOM_DEVICE };
     177        int oflags = (O_RDONLY + O_CLOEXEC
     178                      + (flags & GRND_NONBLOCK ? O_NONBLOCK : 0));
     179        fd = open (randdevice[devrandom], oflags);
     180        if (fd < 0)
     181          {
     182            if (errno == ENOENT || errno == ENOTDIR)
     183              errno = ENOSYS;
     184            return -1;
     185          }
     186        randfd[devrandom] = fd;
     187      }
     188  
     189    return read (fd, buffer, length);
     190  #endif
     191  }