LCOV - code coverage report
Current view: top level - Python - random.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 44 101 43.6 %
Date: 2017-04-19 Functions: 5 7 71.4 %

          Line data    Source code
       1             : #include "Python.h"
       2             : #ifdef MS_WINDOWS
       3             : #include <windows.h>
       4             : #else
       5             : #include <fcntl.h>
       6             : #if defined(HAVE_GETRANDOM) || defined(HAVE_GETENTROPY)
       7             : #include <sys/random.h>
       8             : #endif
       9             : #endif
      10             : 
      11             : #ifdef Py_DEBUG
      12             : int _Py_HashSecret_Initialized = 0;
      13             : #else
      14             : static int _Py_HashSecret_Initialized = 0;
      15             : #endif
      16             : 
      17             : #ifdef MS_WINDOWS
      18             : typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\
      19             :               LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\
      20             :               DWORD dwFlags );
      21             : typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\
      22             :               BYTE *pbBuffer );
      23             : 
      24             : static CRYPTGENRANDOM pCryptGenRandom = NULL;
      25             : /* This handle is never explicitly released. Instead, the operating
      26             :    system will release it when the process terminates. */
      27             : static HCRYPTPROV hCryptProv = 0;
      28             : 
      29             : static int
      30             : win32_urandom_init(int raise)
      31             : {
      32             :     HINSTANCE hAdvAPI32 = NULL;
      33             :     CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
      34             : 
      35             :     /* Obtain handle to the DLL containing CryptoAPI. This should not fail. */
      36             :     hAdvAPI32 = GetModuleHandle("advapi32.dll");
      37             :     if(hAdvAPI32 == NULL)
      38             :         goto error;
      39             : 
      40             :     /* Obtain pointers to the CryptoAPI functions. This will fail on some early
      41             :        versions of Win95. */
      42             :     pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(
      43             :                                hAdvAPI32, "CryptAcquireContextA");
      44             :     if (pCryptAcquireContext == NULL)
      45             :         goto error;
      46             : 
      47             :     pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,
      48             :                                                      "CryptGenRandom");
      49             :     if (pCryptGenRandom == NULL)
      50             :         goto error;
      51             : 
      52             :     /* Acquire context */
      53             :     if (! pCryptAcquireContext(&hCryptProv, NULL, NULL,
      54             :                                PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
      55             :         goto error;
      56             : 
      57             :     return 0;
      58             : 
      59             : error:
      60             :     if (raise)
      61             :         PyErr_SetFromWindowsErr(0);
      62             :     else
      63             :         Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
      64             :     return -1;
      65             : }
      66             : 
      67             : /* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
      68             :    API. Return 0 on success, or -1 on error. */
      69             : static int
      70             : win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
      71             : {
      72             :     Py_ssize_t chunk;
      73             : 
      74             :     if (hCryptProv == 0)
      75             :     {
      76             :         if (win32_urandom_init(raise) == -1)
      77             :             return -1;
      78             :     }
      79             : 
      80             :     while (size > 0)
      81             :     {
      82             :         chunk = size > INT_MAX ? INT_MAX : size;
      83             :         if (!pCryptGenRandom(hCryptProv, chunk, buffer))
      84             :         {
      85             :             /* CryptGenRandom() failed */
      86             :             if (raise)
      87             :                 PyErr_SetFromWindowsErr(0);
      88             :             else
      89             :                 Py_FatalError("Failed to initialized the randomized hash "
      90             :                         "secret using CryptoGen)");
      91             :             return -1;
      92             :         }
      93             :         buffer += chunk;
      94             :         size -= chunk;
      95             :     }
      96             :     return 0;
      97             : }
      98             : 
      99             : /* Issue #25003: Don't use getentropy() on Solaris (available since
     100             :  * Solaris 11.3), it is blocking whereas os.urandom() should not block. */
     101             : #elif defined(HAVE_GETENTROPY) && !defined(sun)
     102             : #define PY_GETENTROPY 1
     103             : 
     104             : /* Fill buffer with size pseudo-random bytes generated by getentropy().
     105             :    Return 0 on success, or raise an exception and return -1 on error.
     106             :    If fatal is nonzero, call Py_FatalError() instead of raising an exception
     107             :    on error. */
     108             : static int
     109             : py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
     110             : {
     111             :     while (size > 0) {
     112             :         Py_ssize_t len = size < 256 ? size : 256;
     113             :         int res;
     114             : 
     115             :         if (!fatal) {
     116             :             Py_BEGIN_ALLOW_THREADS
     117             :             res = getentropy(buffer, len);
     118             :             Py_END_ALLOW_THREADS
     119             : 
     120             :             if (res < 0) {
     121             :                 PyErr_SetFromErrno(PyExc_OSError);
     122             :                 return -1;
     123             :             }
     124             :         }
     125             :         else {
     126             :             res = getentropy(buffer, len);
     127             :             if (res < 0)
     128             :                 Py_FatalError("getentropy() failed");
     129             :         }
     130             : 
     131             :         buffer += len;
     132             :         size -= len;
     133             :     }
     134             :     return 0;
     135             : }
     136             : #endif
     137             : 
     138             : #ifdef __VMS
     139             : /* Use openssl random routine */
     140             : #include <openssl/rand.h>
     141             : static int
     142             : vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
     143             : {
     144             :     if (RAND_pseudo_bytes(buffer, size) < 0) {
     145             :         if (raise) {
     146             :             PyErr_Format(PyExc_ValueError,
     147             :                          "RAND_pseudo_bytes");
     148             :         } else {
     149             :             Py_FatalError("Failed to initialize the randomized hash "
     150             :                           "secret using RAND_pseudo_bytes");
     151             :         }
     152             :         return -1;
     153             :     }
     154             :     return 0;
     155             : }
     156             : #endif /* __VMS */
     157             : 
     158             : 
     159             : #if !defined(MS_WINDOWS) && !defined(__VMS)
     160             : 
     161             : static struct {
     162             :     int fd;
     163             :     dev_t st_dev;
     164             :     ino_t st_ino;
     165             : } urandom_cache = { -1 };
     166             : 
     167             : /* Read size bytes from /dev/urandom into buffer.
     168             :    Call Py_FatalError() on error. */
     169             : static void
     170           0 : dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
     171             : {
     172             :     int fd;
     173             :     Py_ssize_t n;
     174             : 
     175             :     assert (0 < size);
     176             : 
     177           0 :     fd = open("/dev/urandom", O_RDONLY);
     178           0 :     if (fd < 0)
     179           0 :         Py_FatalError("Failed to open /dev/urandom");
     180             : 
     181           0 :     while (0 < size)
     182             :     {
     183             :         do {
     184           0 :             n = read(fd, buffer, (size_t)size);
     185           0 :         } while (n < 0 && errno == EINTR);
     186           0 :         if (n <= 0)
     187             :         {
     188             :             /* stop on error or if read(size) returned 0 */
     189           0 :             Py_FatalError("Failed to read bytes from /dev/urandom");
     190           0 :             break;
     191             :         }
     192           0 :         buffer += n;
     193           0 :         size -= (Py_ssize_t)n;
     194             :     }
     195           0 :     close(fd);
     196           0 : }
     197             : 
     198             : /* Read size bytes from /dev/urandom into buffer.
     199             :    Return 0 on success, raise an exception and return -1 on error. */
     200             : static int
     201           3 : dev_urandom_python(char *buffer, Py_ssize_t size)
     202             : {
     203             :     int fd;
     204             :     Py_ssize_t n;
     205             :     struct stat st;
     206             :     int attr;
     207             : 
     208           3 :     if (size <= 0)
     209           0 :         return 0;
     210             : 
     211           3 :     if (urandom_cache.fd >= 0) {
     212             :         /* Does the fd point to the same thing as before? (issue #21207) */
     213           0 :         if (fstat(urandom_cache.fd, &st)
     214           0 :             || st.st_dev != urandom_cache.st_dev
     215           0 :             || st.st_ino != urandom_cache.st_ino) {
     216             :             /* Something changed: forget the cached fd (but don't close it,
     217             :                since it probably points to something important for some
     218             :                third-party code). */
     219           0 :             urandom_cache.fd = -1;
     220             :         }
     221             :     }
     222           3 :     if (urandom_cache.fd >= 0)
     223           0 :         fd = urandom_cache.fd;
     224             :     else {
     225             :         Py_BEGIN_ALLOW_THREADS
     226           3 :         fd = open("/dev/urandom", O_RDONLY);
     227             :         Py_END_ALLOW_THREADS
     228           3 :         if (fd < 0)
     229             :         {
     230           0 :             if (errno == ENOENT || errno == ENXIO ||
     231           0 :                 errno == ENODEV || errno == EACCES)
     232           0 :                 PyErr_SetString(PyExc_NotImplementedError,
     233             :                                 "/dev/urandom (or equivalent) not found");
     234             :             else
     235           0 :                 PyErr_SetFromErrno(PyExc_OSError);
     236           0 :             return -1;
     237             :         }
     238             : 
     239             :         /* try to make the file descriptor non-inheritable, ignore errors */
     240           3 :         attr = fcntl(fd, F_GETFD);
     241           3 :         if (attr >= 0) {
     242           3 :             attr |= FD_CLOEXEC;
     243           3 :             (void)fcntl(fd, F_SETFD, attr);
     244             :         }
     245             : 
     246           3 :         if (urandom_cache.fd >= 0) {
     247             :             /* urandom_fd was initialized by another thread while we were
     248             :                not holding the GIL, keep it. */
     249           0 :             close(fd);
     250           0 :             fd = urandom_cache.fd;
     251             :         }
     252             :         else {
     253           3 :             if (fstat(fd, &st)) {
     254           0 :                 PyErr_SetFromErrno(PyExc_OSError);
     255           0 :                 close(fd);
     256           0 :                 return -1;
     257             :             }
     258             :             else {
     259           3 :                 urandom_cache.fd = fd;
     260           3 :                 urandom_cache.st_dev = st.st_dev;
     261           3 :                 urandom_cache.st_ino = st.st_ino;
     262             :             }
     263             :         }
     264             :     }
     265             : 
     266             :     Py_BEGIN_ALLOW_THREADS
     267             :     do {
     268             :         do {
     269           3 :             n = read(fd, buffer, (size_t)size);
     270           3 :         } while (n < 0 && errno == EINTR);
     271           3 :         if (n <= 0)
     272           0 :             break;
     273           3 :         buffer += n;
     274           3 :         size -= (Py_ssize_t)n;
     275           3 :     } while (0 < size);
     276             :     Py_END_ALLOW_THREADS
     277             : 
     278           3 :     if (n <= 0)
     279             :     {
     280             :         /* stop on error or if read(size) returned 0 */
     281           0 :         if (n < 0)
     282           0 :             PyErr_SetFromErrno(PyExc_OSError);
     283             :         else
     284           0 :             PyErr_Format(PyExc_RuntimeError,
     285             :                          "Failed to read %zi bytes from /dev/urandom",
     286             :                          size);
     287           0 :         return -1;
     288             :     }
     289           3 :     return 0;
     290             : }
     291             : 
     292             : static void
     293           3 : dev_urandom_close(void)
     294             : {
     295           3 :     if (urandom_cache.fd >= 0) {
     296           3 :         close(urandom_cache.fd);
     297           3 :         urandom_cache.fd = -1;
     298             :     }
     299           3 : }
     300             : 
     301             : 
     302             : #endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
     303             : 
     304             : /* Fill buffer with pseudo-random bytes generated by a linear congruent
     305             :    generator (LCG):
     306             : 
     307             :        x(n+1) = (x(n) * 214013 + 2531011) % 2^32
     308             : 
     309             :    Use bits 23..16 of x(n) to generate a byte. */
     310             : static void
     311           0 : lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
     312             : {
     313             :     size_t index;
     314             :     unsigned int x;
     315             : 
     316           0 :     x = x0;
     317           0 :     for (index=0; index < size; index++) {
     318           0 :         x *= 214013;
     319           0 :         x += 2531011;
     320             :         /* modulo 2 ^ (8 * sizeof(int)) */
     321           0 :         buffer[index] = (x >> 16) & 0xff;
     322             :     }
     323           0 : }
     324             : 
     325             : /* Fill buffer with size pseudo-random bytes from the operating system random
     326             :    number generator (RNG). It is suitable for most cryptographic purposes
     327             :    except long living private keys for asymmetric encryption.
     328             : 
     329             :    Return 0 on success, raise an exception and return -1 on error. */
     330             : int
     331           3 : _PyOS_URandom(void *buffer, Py_ssize_t size)
     332             : {
     333           3 :     if (size < 0) {
     334           0 :         PyErr_Format(PyExc_ValueError,
     335             :                      "negative argument not allowed");
     336           0 :         return -1;
     337             :     }
     338           3 :     if (size == 0)
     339           0 :         return 0;
     340             : 
     341             : #ifdef MS_WINDOWS
     342             :     return win32_urandom((unsigned char *)buffer, size, 1);
     343             : #elif defined(PY_GETENTROPY)
     344             :     return py_getentropy(buffer, size, 0);
     345             : #else
     346             : # ifdef __VMS
     347             :     return vms_urandom((unsigned char *)buffer, size, 1);
     348             : # else
     349           3 :     return dev_urandom_python((char*)buffer, size);
     350             : # endif
     351             : #endif
     352             : }
     353             : 
     354             : void
     355           6 : _PyRandom_Init(void)
     356             : {
     357             :     char *env;
     358           6 :     void *secret = &_Py_HashSecret;
     359           6 :     Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
     360             : 
     361           6 :     if (_Py_HashSecret_Initialized)
     362           3 :         return;
     363           3 :     _Py_HashSecret_Initialized = 1;
     364             : 
     365             :     /*
     366             :       By default, hash randomization is disabled, and only
     367             :       enabled if PYTHONHASHSEED is set to non-empty or if
     368             :       "-R" is provided at the command line:
     369             :     */
     370           3 :     if (!Py_HashRandomizationFlag) {
     371             :         /* Disable the randomized hash: */
     372           3 :         memset(secret, 0, secret_size);
     373           3 :         return;
     374             :     }
     375             : 
     376             :     /*
     377             :       Hash randomization is enabled.  Generate a per-process secret,
     378             :       using PYTHONHASHSEED if provided.
     379             :     */
     380             : 
     381           0 :     env = Py_GETENV("PYTHONHASHSEED");
     382           0 :     if (env && *env != '\0' && strcmp(env, "random") != 0) {
     383           0 :         char *endptr = env;
     384             :         unsigned long seed;
     385           0 :         seed = strtoul(env, &endptr, 10);
     386           0 :         if (*endptr != '\0'
     387           0 :             || seed > 4294967295UL
     388           0 :             || (errno == ERANGE && seed == ULONG_MAX))
     389             :         {
     390           0 :             Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
     391             :                           "in range [0; 4294967295]");
     392             :         }
     393           0 :         if (seed == 0) {
     394             :             /* disable the randomized hash */
     395           0 :             memset(secret, 0, secret_size);
     396             :         }
     397             :         else {
     398           0 :             lcg_urandom(seed, (unsigned char*)secret, secret_size);
     399             :         }
     400             :     }
     401             :     else {
     402             : #ifdef MS_WINDOWS
     403             :         (void)win32_urandom((unsigned char *)secret, secret_size, 0);
     404             : #elif __VMS
     405             :         vms_urandom((unsigned char *)secret, secret_size, 0);
     406             : #elif defined(PY_GETENTROPY)
     407             :         (void)py_getentropy(secret, secret_size, 1);
     408             : #else
     409           0 :         dev_urandom_noraise(secret, secret_size);
     410             : #endif
     411             :     }
     412             : }
     413             : 
     414             : void
     415           3 : _PyRandom_Fini(void)
     416             : {
     417             : #ifdef MS_WINDOWS
     418             :     if (hCryptProv) {
     419             :         CryptReleaseContext(hCryptProv, 0);
     420             :         hCryptProv = 0;
     421             :     }
     422             : #elif defined(PY_GETENTROPY)
     423             :     /* nothing to clean */
     424             : #else
     425           3 :     dev_urandom_close();
     426             : #endif
     427           3 : }

Generated by: LCOV version 1.10