#include "Python.h"
#ifdef MS_WINDOWS
#include
#else
#include
#if defined(HAVE_SYS_RANDOM_H) && (defined(HAVE_GETRANDOM) || defined(HAVE_GETENTROPY))
#include
#endif
#endif
#ifdef Py_DEBUG
int _Py_HashSecret_Initialized = 0;
#else
static int _Py_HashSecret_Initialized = 0;
#endif
#ifdef MS_WINDOWS
typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\
LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\
DWORD dwFlags );
typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\
BYTE *pbBuffer );
static CRYPTGENRANDOM pCryptGenRandom = NULL;
/* This handle is never explicitly released. Instead, the operating
system will release it when the process terminates. */
static HCRYPTPROV hCryptProv = 0;
static int
win32_urandom_init(int raise)
{
HINSTANCE hAdvAPI32 = NULL;
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
/* Obtain handle to the DLL containing CryptoAPI. This should not fail. */
hAdvAPI32 = GetModuleHandle("advapi32.dll");
if(hAdvAPI32 == NULL)
goto error;
/* Obtain pointers to the CryptoAPI functions. This will fail on some early
versions of Win95. */
pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(
hAdvAPI32, "CryptAcquireContextA");
if (pCryptAcquireContext == NULL)
goto error;
pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,
"CryptGenRandom");
if (pCryptGenRandom == NULL)
goto error;
/* Acquire context */
if (! pCryptAcquireContext(&hCryptProv, NULL, NULL,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
goto error;
return 0;
error:
if (raise)
PyErr_SetFromWindowsErr(0);
else
Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
return -1;
}
/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
API. Return 0 on success, or -1 on error. */
static int
win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
{
Py_ssize_t chunk;
if (hCryptProv == 0)
{
if (win32_urandom_init(raise) == -1)
return -1;
}
while (size > 0)
{
chunk = size > INT_MAX ? INT_MAX : size;
if (!pCryptGenRandom(hCryptProv, chunk, buffer))
{
/* CryptGenRandom() failed */
if (raise)
PyErr_SetFromWindowsErr(0);
else
Py_FatalError("Failed to initialized the randomized hash "
"secret using CryptoGen)");
return -1;
}
buffer += chunk;
size -= chunk;
}
return 0;
}
/* Issue #25003: Don't use getentropy() on Solaris (available since
Solaris 11.3), it is blocking whereas os.urandom() should not block.
Issue #29188: Don't use getentropy() on Linux since the glibc 2.24
implements it with the getrandom() syscall which can fail with ENOSYS,
and this error is not supported in py_getentropy() and getrandom() is called
with flags=0 which blocks until system urandom is initialized, which is not
the desired behaviour to seed the Python hash secret nor for os.urandom():
see the PEP 524 which was only implemented in Python 3.6. */
#elif defined(HAVE_GETENTROPY) && !defined(sun) && !defined(linux)
#define PY_GETENTROPY 1
/* Fill buffer with size pseudo-random bytes generated by getentropy().
Return 0 on success, or raise an exception and return -1 on error.
If fatal is nonzero, call Py_FatalError() instead of raising an exception
on error. */
static int
py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
{
while (size > 0) {
Py_ssize_t len = size < 256 ? size : 256;
int res;
if (!fatal) {
Py_BEGIN_ALLOW_THREADS
res = getentropy(buffer, len);
Py_END_ALLOW_THREADS
if (res < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
}
else {
res = getentropy(buffer, len);
if (res < 0)
Py_FatalError("getentropy() failed");
}
buffer += len;
size -= len;
}
return 0;
}
#endif
#ifdef __VMS
/* Use openssl random routine */
#include
static int
vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
{
if (RAND_pseudo_bytes(buffer, size) < 0) {
if (raise) {
PyErr_Format(PyExc_ValueError,
"RAND_pseudo_bytes");
} else {
Py_FatalError("Failed to initialize the randomized hash "
"secret using RAND_pseudo_bytes");
}
return -1;
}
return 0;
}
#endif /* __VMS */
#if !defined(MS_WINDOWS) && !defined(__VMS)
static struct {
int fd;
dev_t st_dev;
ino_t st_ino;
} urandom_cache = { -1 };
/* Read size bytes from /dev/urandom into buffer.
Call Py_FatalError() on error. */
static void
dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
{
int fd;
Py_ssize_t n;
assert (0 < size);
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
Py_FatalError("Failed to open /dev/urandom");
while (0 < size)
{
do {
n = read(fd, buffer, (size_t)size);
} while (n < 0 && errno == EINTR);
if (n