// ********************************************************************** // // Copyright (c) 2003-2012 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #if defined(_MSC_VER) && (_MSC_VER > 1400) # define _CRT_RAND_S #endif #include #include #include #ifdef _WIN32 # include #else # include # include #endif using namespace std; using namespace IceUtil; #if !defined(_WIN32) || !defined(_MSC_VER) || (_MSC_VER < 1400) namespace { // // The static mutex is required to lazy initialize the file // descriptor for /dev/urandom (Unix) or the cryptographic // context (Windows). // // Also, unfortunately on Linux (at least up to 2.6.9), concurrent // access to /dev/urandom can return the same value. Search for // "Concurrent access to /dev/urandom" in the linux-kernel mailing // list archive for additional details. Since /dev/urandom on other // platforms is usually a port from Linux, this problem could be // widespread. Therefore, we serialize access to /dev/urandom using a // static mutex. // Mutex* staticMutex = 0; #ifdef _WIN32 HCRYPTPROV context = NULL; #else int fd = -1; #endif class Init { public: Init() { staticMutex = new IceUtil::Mutex; } ~Init() { #ifdef _WIN32 if(context != NULL) { CryptReleaseContext(context, 0); context = NULL; } #else if(fd != -1) { close(fd); fd = -1; } #endif delete staticMutex; staticMutex = 0; } }; Init init; } #endif void IceUtilInternal::generateRandom(char* buffer, int size) { #ifdef _WIN32 # if defined(_MSC_VER) && (_MSC_VER >= 1400) for(int i = 0; i < size; ++i) { buffer[i] = random(256); } # else // // It's not clear from the Microsoft documentation if CryptGenRandom // can be called concurrently from several threads. To be on the safe // side, we also serialize calls to to CryptGenRandom with the static // mutex. // IceUtilInternal::MutexPtrLock lock(staticMutex); if(context == NULL) { if(!CryptAcquireContext(&context, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { throw SyscallException(__FILE__, __LINE__, GetLastError()); } } if(!CryptGenRandom(context, size, reinterpret_cast(buffer))) { throw SyscallException(__FILE__, __LINE__, GetLastError()); } # endif #else // // Serialize access to /dev/urandom; see comment above. // IceUtilInternal::MutexPtrLock lock(staticMutex); if(fd == -1) { fd = open("/dev/urandom", O_RDONLY); if(fd == -1) { assert(0); throw SyscallException(__FILE__, __LINE__, errno); } } // // Limit the number of attempts to 20 reads to avoid // a potential "for ever" loop // int reads = 0; size_t index = 0; while(reads <= 20 && index != static_cast(size)) { ssize_t bytesRead = read(fd, buffer + index, static_cast(size) - index); if(bytesRead == -1 && errno != EINTR) { SyscallException ex(__FILE__, __LINE__, errno); cerr << "Reading /dev/urandom failed:\n" << ex << endl; assert(0); throw ex; } else { index += bytesRead; reads++; } } if(index != static_cast(size)) { assert(0); throw SyscallException(__FILE__, __LINE__, 0); } #endif } unsigned int IceUtilInternal::random(int limit) { unsigned int r; #if defined(_MSC_VER) && (_MSC_VER > 1400) errno_t err = rand_s(&r); if(err != 0) { SyscallException ex(__FILE__, __LINE__, errno); cerr << "rand_s failed:\n" << ex << endl; assert(0); throw ex; } #else generateRandom(reinterpret_cast(&r), static_cast(sizeof(unsigned int))); #endif if(limit > 0) { r = r % limit; } return r; }