mirror of
https://github.com/qpdf/qpdf.git
synced 2024-11-01 03:12:29 +00:00
d71f05ca07
This makes all integer type conversions that have potential data loss explicit with calls that do range checks and raise an exception. After this commit, qpdf builds with no warnings when -Wsign-conversion -Wconversion is used with gcc or clang or when -W3 -Wd4800 is used with MSVC. This significantly reduces the likelihood of potential crashes from bogus integer values. There are some parts of the code that take int when they should take size_t or an offset. Such places would make qpdf not support files with more than 2^31 of something that usually wouldn't be so large. In the event that such a file shows up and is valid, at least qpdf would raise an error in the right spot so the issue could be legitimately addressed rather than failing in some weird way because of a silent overflow condition.
136 lines
3.5 KiB
C++
136 lines
3.5 KiB
C++
#include <qpdf/SecureRandomDataProvider.hh>
|
|
|
|
#include <qpdf/qpdf-config.h>
|
|
#include <qpdf/QUtil.hh>
|
|
#ifdef _WIN32
|
|
# include <windows.h>
|
|
# include <direct.h>
|
|
# include <io.h>
|
|
# ifndef SKIP_OS_SECURE_RANDOM
|
|
# include <wincrypt.h>
|
|
# endif
|
|
#endif
|
|
|
|
SecureRandomDataProvider::SecureRandomDataProvider()
|
|
{
|
|
}
|
|
|
|
SecureRandomDataProvider::~SecureRandomDataProvider()
|
|
{
|
|
}
|
|
|
|
#ifdef SKIP_OS_SECURE_RANDOM
|
|
|
|
void
|
|
SecureRandomDataProvider::provideRandomData(unsigned char* data, size_t len)
|
|
{
|
|
throw std::logic_error("SecureRandomDataProvider::provideRandomData called when support was not compiled in");
|
|
}
|
|
|
|
RandomDataProvider*
|
|
SecureRandomDataProvider::getInstance()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
#ifdef _WIN32
|
|
|
|
class WindowsCryptProvider
|
|
{
|
|
public:
|
|
WindowsCryptProvider()
|
|
{
|
|
if (!CryptAcquireContext(&crypt_prov,
|
|
"Container",
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
0))
|
|
{
|
|
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
|
|
defined(__clang__))
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wold-style-cast"
|
|
# pragma GCC diagnostic ignored "-Wsign-compare"
|
|
# pragma GCC diagnostic ignored "-Wsign-conversion"
|
|
#endif
|
|
if (GetLastError() == NTE_BAD_KEYSET)
|
|
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
|
|
defined(__clang__))
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
{
|
|
if (! CryptAcquireContext(&crypt_prov,
|
|
"Container",
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_NEWKEYSET))
|
|
{
|
|
throw std::runtime_error(
|
|
"unable to acquire crypt context with new keyset");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("unable to acquire crypt context");
|
|
}
|
|
}
|
|
}
|
|
~WindowsCryptProvider()
|
|
{
|
|
// Ignore error
|
|
CryptReleaseContext(crypt_prov, 0);
|
|
}
|
|
|
|
HCRYPTPROV crypt_prov;
|
|
};
|
|
#endif
|
|
|
|
void
|
|
SecureRandomDataProvider::provideRandomData(unsigned char* data, size_t len)
|
|
{
|
|
#if defined(_WIN32)
|
|
|
|
// Optimization: make the WindowsCryptProvider static as long as
|
|
// it can be done in a thread-safe fashion.
|
|
WindowsCryptProvider c;
|
|
if (! CryptGenRandom(c.crypt_prov, static_cast<DWORD>(len),
|
|
reinterpret_cast<BYTE*>(data)))
|
|
{
|
|
throw std::runtime_error("unable to generate secure random data");
|
|
}
|
|
|
|
#elif defined(RANDOM_DEVICE)
|
|
|
|
// Optimization: wrap the file open and close in a class so that
|
|
// the file is closed in a destructor, then make this static to
|
|
// keep the file handle open. Only do this if it can be done in a
|
|
// thread-safe fashion.
|
|
FILE* f = QUtil::safe_fopen(RANDOM_DEVICE, "rb");
|
|
size_t fr = fread(data, 1, len, f);
|
|
fclose(f);
|
|
if (fr != len)
|
|
{
|
|
throw std::runtime_error(
|
|
"unable to read " +
|
|
QUtil::uint_to_string(len) +
|
|
" bytes from " + std::string(RANDOM_DEVICE));
|
|
}
|
|
|
|
#else
|
|
|
|
# error "Don't know how to generate secure random numbers on this platform. See random number generation in the top-level README.md"
|
|
|
|
#endif
|
|
}
|
|
|
|
RandomDataProvider*
|
|
SecureRandomDataProvider::getInstance()
|
|
{
|
|
static SecureRandomDataProvider instance;
|
|
return &instance;
|
|
}
|
|
|
|
#endif
|