diff options
author | Jack Lloyd <[email protected]> | 2017-09-03 17:19:41 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-09-03 17:19:41 -0400 |
commit | e996d77c9cf787014c48fd2fe48abed19ff852a5 (patch) | |
tree | 38ef34138f44e8f59066865332abaf7932093687 /src | |
parent | 102948ada37eb278ac3ea248f1421f9b751c8906 (diff) | |
parent | e09ad6e89a25001273b40081fe83b174d8041fe6 (diff) |
Merge GH #1180 Refactorings to support Windows Phone
Diffstat (limited to 'src')
-rw-r--r-- | src/build-data/buildh.in | 13 | ||||
-rw-r--r-- | src/build-data/os/winphone.txt | 26 | ||||
-rw-r--r-- | src/build-data/policy/bsi.txt | 1 | ||||
-rw-r--r-- | src/build-data/policy/modern.txt | 1 | ||||
-rw-r--r-- | src/build-data/policy/nist.txt | 1 | ||||
-rw-r--r-- | src/lib/entropy/cryptoapi_rng/es_capi.cpp | 95 | ||||
-rw-r--r-- | src/lib/entropy/cryptoapi_rng/es_capi.h | 44 | ||||
-rw-r--r-- | src/lib/entropy/cryptoapi_rng/info.txt | 20 | ||||
-rw-r--r-- | src/lib/entropy/entropy_srcs.cpp | 69 | ||||
-rw-r--r-- | src/lib/entropy/win32_stats/es_win32.cpp | 2 | ||||
-rw-r--r-- | src/lib/rng/system_rng/info.txt | 8 | ||||
-rw-r--r-- | src/lib/rng/system_rng/system_rng.cpp | 251 | ||||
-rw-r--r-- | src/lib/utils/dyn_load/dyn_load.cpp | 1 | ||||
-rw-r--r-- | src/lib/utils/http_util/http_util.cpp | 156 | ||||
-rw-r--r-- | src/lib/utils/mem_ops.cpp | 39 | ||||
-rw-r--r-- | src/lib/utils/os_utils.cpp | 280 | ||||
-rw-r--r-- | src/lib/utils/os_utils.h | 33 |
17 files changed, 560 insertions, 480 deletions
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in index 6e60718ee..e1337318e 100644 --- a/src/build-data/buildh.in +++ b/src/build-data/buildh.in @@ -117,15 +117,14 @@ /* * Specifies (in order) the list of entropy sources that will be used -* to seed an in-memory RNG. The first in the default list: "rdseed" and "rdrand" -* do not count as contributing any entropy -* but are included as they are fast and help protect against a -* seriously broken system RNG. +* to seed an in-memory RNG. The first in the default list: "rdseed" +* and "rdrand" do not count as contributing any entropy but are +* included as they are fast and help protect against a seriously +* broken system RNG. */ #define BOTAN_ENTROPY_DEFAULT_SOURCES \ - { "rdseed", "rdrand", "darwin_secrandom", "getentropy", "dev_random", \ - "win32_cryptoapi", "proc_walk", "system_stats" } - + { "rdseed", "rdrand", "darwin_secrandom", "getentropy", \ + "dev_random", "system_rng", "proc_walk", "system_stats" } /* Multiplier on a block cipher's native parallelism */ #define BOTAN_BLOCK_CIPHER_PAR_MULT 4 diff --git a/src/build-data/os/winphone.txt b/src/build-data/os/winphone.txt new file mode 100644 index 000000000..ee43ee702 --- /dev/null +++ b/src/build-data/os/winphone.txt @@ -0,0 +1,26 @@ +os_type windows + +# ? +program_suffix .exe +obj_suffix obj +static_suffix lib +building_shared_supported no + +install_root c:\\Botan +doc_dir docs + +install_cmd_data "copy" +install_cmd_exec "copy" + +<target_features> +crypto_ng +gmtime_s +loadlibrary +mkgmtime +query_perf_counter +rtlsecurezeromemory +#stl_filesystem_msvc +threads +filesystem +sockets +</target_features> diff --git a/src/build-data/policy/bsi.txt b/src/build-data/policy/bsi.txt index 0f053c337..f66f634ed 100644 --- a/src/build-data/policy/bsi.txt +++ b/src/build-data/policy/bsi.txt @@ -57,7 +57,6 @@ aes_ssse3 clmul # entropy sources -cryptoapi_rng darwin_secrandom dev_random proc_walk diff --git a/src/build-data/policy/modern.txt b/src/build-data/policy/modern.txt index 588fc7fc8..fb435636c 100644 --- a/src/build-data/policy/modern.txt +++ b/src/build-data/policy/modern.txt @@ -76,7 +76,6 @@ rdrand_rng system_rng # entropy sources -cryptoapi_rng darwin_secrandom dev_random proc_walk diff --git a/src/build-data/policy/nist.txt b/src/build-data/policy/nist.txt index 50948d4dc..ad7a552dc 100644 --- a/src/build-data/policy/nist.txt +++ b/src/build-data/policy/nist.txt @@ -61,7 +61,6 @@ sha2_32_armv8 clmul # entropy sources -cryptoapi_rng darwin_secrandom dev_random proc_walk diff --git a/src/lib/entropy/cryptoapi_rng/es_capi.cpp b/src/lib/entropy/cryptoapi_rng/es_capi.cpp deleted file mode 100644 index 58503c66d..000000000 --- a/src/lib/entropy/cryptoapi_rng/es_capi.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* -* Win32 CryptoAPI EntropySource -* (C) 1999-2009,2016 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include <botan/internal/es_capi.h> -#include <botan/parsing.h> -#define NOMINMAX 1 -#define _WINSOCKAPI_ // stop windows.h including winsock.h -#include <windows.h> -#include <wincrypt.h> - -namespace Botan { - -namespace { - -class CSP_Handle_Impl : public Win32_CAPI_EntropySource::CSP_Handle - { - public: - explicit CSP_Handle_Impl(uint64_t capi_provider) - { - m_valid = ::CryptAcquireContext(&m_handle, - nullptr, - nullptr, - static_cast<DWORD>(capi_provider), - CRYPT_VERIFYCONTEXT); - } - - ~CSP_Handle_Impl() - { - if(m_valid) - ::CryptReleaseContext(m_handle, 0); - } - - size_t gen_random(uint8_t out[], size_t n) const - { - if(m_valid && ::CryptGenRandom(m_handle, static_cast<DWORD>(n), out)) - return n; - return 0; - } - - private: - bool m_valid; - HCRYPTPROV m_handle; - }; - -} - -/* -* Gather Entropy from Win32 CAPI -*/ -size_t Win32_CAPI_EntropySource::poll(RandomNumberGenerator& rng) - { - secure_vector<uint8_t> buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); - size_t bits = 0; - - for(size_t i = 0; i != m_csp_provs.size(); ++i) - { - size_t got = m_csp_provs[i]->gen_random(buf.data(), buf.size()); - - if(got > 0) - { - rng.add_entropy(buf.data(), got); - bits += got * 8; - } - } - - return bits; - } - -/* -* Win32_Capi_Entropysource Constructor -*/ -Win32_CAPI_EntropySource::Win32_CAPI_EntropySource(const std::string& provs) - { - for(std::string prov_name : split_on(provs, ':')) - { - DWORD prov_type; - - if(prov_name == "RSA_FULL") - prov_type = PROV_RSA_FULL; - else if(prov_name == "INTEL_SEC") - prov_type = PROV_INTEL_SEC; - else if(prov_name == "RNG") - prov_type = PROV_RNG; - else - continue; - - m_csp_provs.push_back(std::unique_ptr<CSP_Handle>(new CSP_Handle_Impl(prov_type))); - } - } - -} diff --git a/src/lib/entropy/cryptoapi_rng/es_capi.h b/src/lib/entropy/cryptoapi_rng/es_capi.h deleted file mode 100644 index 8439e62fa..000000000 --- a/src/lib/entropy/cryptoapi_rng/es_capi.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Win32 CAPI EntropySource -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_ENTROPY_SRC_WIN32_CAPI_H__ -#define BOTAN_ENTROPY_SRC_WIN32_CAPI_H__ - -#include <botan/entropy_src.h> -#include <vector> - -namespace Botan { - -/** -* Win32 CAPI Entropy Source -*/ -class Win32_CAPI_EntropySource final : public Entropy_Source - { - public: - std::string name() const override { return "win32_cryptoapi"; } - - size_t poll(RandomNumberGenerator& rng) override; - - /** - * Win32_Capi_Entropysource Constructor - * @param provs list of providers, separated by ':' - */ - explicit Win32_CAPI_EntropySource(const std::string& provs = ""); - - class CSP_Handle - { - public: - virtual ~CSP_Handle() {} - virtual size_t gen_random(uint8_t out[], size_t n) const = 0; - }; - private: - std::vector<std::unique_ptr<CSP_Handle>> m_csp_provs; - }; - -} - -#endif diff --git a/src/lib/entropy/cryptoapi_rng/info.txt b/src/lib/entropy/cryptoapi_rng/info.txt deleted file mode 100644 index 21b42f2a0..000000000 --- a/src/lib/entropy/cryptoapi_rng/info.txt +++ /dev/null @@ -1,20 +0,0 @@ -<defines> -ENTROPY_SRC_CAPI -> 20131128 -</defines> - -<header:internal> -es_capi.h -</header:internal> - -# We'll just assume CAPI is there; this is OK except for 3.x, early -# versions of 95, and maybe NT 3.5 -<os> -windows -cygwin -mingw -</os> - -<libs> -windows -> advapi32.lib -mingw -> advapi32 -</libs> diff --git a/src/lib/entropy/entropy_srcs.cpp b/src/lib/entropy/entropy_srcs.cpp index d9d5cfe4b..76546c5da 100644 --- a/src/lib/entropy/entropy_srcs.cpp +++ b/src/lib/entropy/entropy_srcs.cpp @@ -8,6 +8,10 @@ #include <botan/entropy_src.h> #include <botan/rng.h> +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include <botan/system_rng.h> +#endif + #if defined(BOTAN_HAS_ENTROPY_SRC_RDRAND) #include <botan/internal/rdrand.h> #endif @@ -20,10 +24,6 @@ #include <botan/internal/dev_random.h> #endif -#if defined(BOTAN_HAS_ENTROPY_SRC_CAPI) - #include <botan/internal/es_capi.h> -#endif - #if defined(BOTAN_HAS_ENTROPY_SRC_WIN32) #include <botan/internal/es_win32.h> #endif @@ -42,65 +42,86 @@ namespace Botan { +#if defined(BOTAN_HAS_SYSTEM_RNG) + +namespace { + +class System_RNG_EntropySource : public Entropy_Source + { + public: + size_t poll(RandomNumberGenerator& rng) override + { + const size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS; + rng.reseed_from_rng(system_rng(), poll_bits); + return poll_bits; + } + + std::string name() const override { return system_rng().name(); } + }; + +} + +#endif + std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name) { - if(name == "rdrand") +#if defined(BOTAN_HAS_SYSTEM_RNG) + if(name == "system_rng" || name == "win32_cryptoapi") { + return std::unique_ptr<Entropy_Source>(new System_RNG_EntropySource); + } +#endif + #if defined(BOTAN_HAS_ENTROPY_SRC_RDRAND) + if(name == "rdrand") + { return std::unique_ptr<Entropy_Source>(new Intel_Rdrand); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_RDSEED) if(name == "rdseed") { -#if defined(BOTAN_HAS_ENTROPY_SRC_RDSEED) return std::unique_ptr<Entropy_Source>(new Intel_Rdseed); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_DARWIN_SECRANDOM) if(name == "darwin_secrandom") { -#if defined(BOTAN_HAS_ENTROPY_SRC_DARWIN_SECRANDOM) return std::unique_ptr<Entropy_Source>(new Darwin_SecRandom); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY) if(name == "getentropy") { -#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY) return std::unique_ptr<Entropy_Source>(new Getentropy); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM) if(name == "dev_random") { -#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM) return std::unique_ptr<Entropy_Source>(new Device_EntropySource(BOTAN_SYSTEM_RNG_POLL_DEVICES)); -#endif } - - if(name == "win32_cryptoapi") - { -#if defined(BOTAN_HAS_ENTROPY_SRC_CAPI) - return std::unique_ptr<Entropy_Source>(new Win32_CAPI_EntropySource("RSA_FULL")); #endif - } +#if defined(BOTAN_HAS_ENTROPY_SRC_PROC_WALKER) if(name == "proc_walk") { -#if defined(BOTAN_HAS_ENTROPY_SRC_PROC_WALKER) const std::string root_dir = BOTAN_ENTROPY_PROC_FS_PATH; if(!root_dir.empty()) return std::unique_ptr<Entropy_Source>(new ProcWalking_EntropySource(root_dir)); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32) if(name == "system_stats") { -#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32) return std::unique_ptr<Entropy_Source>(new Win32_EntropySource); -#endif } +#endif return std::unique_ptr<Entropy_Source>(); } diff --git a/src/lib/entropy/win32_stats/es_win32.cpp b/src/lib/entropy/win32_stats/es_win32.cpp index 520848615..0e7056550 100644 --- a/src/lib/entropy/win32_stats/es_win32.cpp +++ b/src/lib/entropy/win32_stats/es_win32.cpp @@ -6,7 +6,9 @@ */ #include <botan/internal/es_win32.h> + #define NOMINMAX 1 +#define _WINSOCKAPI_ // stop windows.h including winsock.h #include <windows.h> #include <tlhelp32.h> diff --git a/src/lib/rng/system_rng/info.txt b/src/lib/rng/system_rng/info.txt index 3df7dd387..1ea7eb388 100644 --- a/src/lib/rng/system_rng/info.txt +++ b/src/lib/rng/system_rng/info.txt @@ -24,4 +24,12 @@ openbsd qnx solaris windows +winphone </os> + +<libs> +windows -> advapi32.lib +winphone -> bcrypt.lib + +mingw -> advapi32 +</libs> diff --git a/src/lib/rng/system_rng/system_rng.cpp b/src/lib/rng/system_rng/system_rng.cpp index 1c3b8e50a..9ee306991 100644 --- a/src/lib/rng/system_rng/system_rng.cpp +++ b/src/lib/rng/system_rng/system_rng.cpp @@ -1,6 +1,6 @@ /* * System RNG -* (C) 2014,2015 Jack Lloyd +* (C) 2014,2015,2017 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,128 +9,188 @@ #include <botan/build.h> #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + #define NOMINMAX 1 + #define _WINSOCKAPI_ // stop windows.h including winsock.h + #include <windows.h> + #include <wincrypt.h> -#define NOMINMAX 1 -#include <windows.h> -#include <wincrypt.h> +#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) + #include <bcrypt.h> #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - -#include <stdlib.h> + #include <stdlib.h> #else - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> - + #include <sys/types.h> + #include <sys/stat.h> + #include <fcntl.h> + #include <unistd.h> + #include <errno.h> #endif namespace Botan { namespace { +#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + class System_RNG_Impl final : public RandomNumberGenerator { public: - System_RNG_Impl(); - ~System_RNG_Impl(); + System_RNG_Impl() + { + if(!CryptAcquireContext(&m_prov, nullptr, nullptr, + BOTAN_SYSTEM_RNG_CRYPTOAPI_PROV_TYPE, CRYPT_VERIFYCONTEXT)) + throw Exception("System_RNG failed to acquire crypto provider"); + } - bool is_seeded() const override { return true; } + ~System_RNG_Impl() + { + ::CryptReleaseContext(m_prov, 0); + } + + void randomize(uint8_t buf[], size_t len) override + { + ::CryptGenRandom(m_prov, static_cast<DWORD>(len), buf); + } + + void add_entropy(const uint8_t in[], size_t length) override + { + /* + There is no explicit ConsumeRandom, but all values provided in + the call are incorporated into the state. + */ + std::vector<uint8_t> buf(in, in + length); + ::CryptGenRandom(m_prov, static_cast<DWORD>(buf.size()), buf.data()); + } + bool is_seeded() const override { return true; } void clear() override {} + std::string name() const override { return "cryptoapi"; } + private: + HCRYPTPROV m_prov; + }; - void randomize(uint8_t out[], size_t len) override; +#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) - void add_entropy(const uint8_t in[], size_t length) override; +class System_RNG_Impl final : public RandomNumberGenerator + { + public: + System_RNG_Impl() + { + NTSTATUS ret = ::BCryptOpenAlgorithmProvider(&m_prov, + BCRYPT_RNG_ALGORITHM, + MS_PRIMITIVE_PROVIDER, 0); + if(ret != STATUS_SUCCESS) + throw Exception("System_RNG failed to acquire crypto provider"); + } + + ~System_RNG_Impl() + { + ::BCryptCloseAlgorithmProvider(m_prov, 0); + } - std::string name() const override; + void randomize(uint8_t buf[], size_t len) override + { + ::BCryptGenRandom(m_prov, static_cast<PUCHAR>(buf), static_cast<ULONG>(len), 0); + } + void add_entropy(const uint8_t in[], size_t length) override + { + /* + There is a flag BCRYPT_RNG_USE_ENTROPY_IN_BUFFER to provide + entropy inputs, but it is ignored in Windows 8 and later. + */ + } + + bool is_seeded() const override { return true; } + void clear() override {} + std::string name() const override { return "crypto_ng"; } private: -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) - HCRYPTPROV m_prov; -#else - int m_fd; -#endif + BCRYPT_ALG_HANDLE m_handle; }; -std::string System_RNG_Impl::name() const - { -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) - return "cryptoapi"; #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - return "arc4random"; -#else - return BOTAN_SYSTEM_RNG_DEVICE; -#endif - } -System_RNG_Impl::System_RNG_Impl() +class System_RNG_Impl final : public RandomNumberGenerator { -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + public: + // No constructor or destructor needed as no userland state maintained - if(!CryptAcquireContext(&m_prov, nullptr, nullptr, BOTAN_SYSTEM_RNG_CRYPTOAPI_PROV_TYPE, CRYPT_VERIFYCONTEXT)) - throw Exception("System_RNG failed to acquire crypto provider"); + void randomize(uint8_t buf[], size_t len) override + { + ::arc4random_buf(buf, len); + } + + void add_entropy(const uint8_t[], size_t) override { /* ignored */ } + bool is_seeded() const override { return true; } + void clear() override {} + std::string name() const override { return "arc4random"; } + }; -#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - // Nothing to do, arc4random(3) works from the beginning. #else -#ifndef O_NOCTTY - #define O_NOCTTY 0 -#endif +// Read a random device - m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY); - - // Cannot open in read-write mode. Fall back to read-only - // Calls to add_entropy will fail, but randomize will work - if(m_fd < 0) - m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY); +class System_RNG_Impl final : public RandomNumberGenerator + { + public: + System_RNG_Impl() + { + #ifndef O_NOCTTY + #define O_NOCTTY 0 + #endif - if(m_fd < 0) - throw Exception("System_RNG failed to open RNG device"); -#endif - } + m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY); -System_RNG_Impl::~System_RNG_Impl() - { -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) - ::CryptReleaseContext(m_prov, 0); -#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - // Nothing to do. -#else - ::close(m_fd); - m_fd = -1; -#endif - } + /* + Cannot open in read-write mode. Fall back to read-only, + calls to add_entropy will fail, but randomize will work + */ + if(m_fd < 0) + m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY); -void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len) - { -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) - /* - There is no explicit ConsumeRandom, but all values provided in - the call are incorporated into the state. + if(m_fd < 0) + throw Exception("System_RNG failed to open RNG device"); + } - TODO: figure out a way to avoid this copy. Byte at a time updating - seems worse than the allocation. + ~System_RNG_Impl() + { + ::close(m_fd); + m_fd = -1; + } - for(size_t i = 0; i != len; ++i) - { - uint8_t b = input[i]; - ::CryptGenRandom(m_prov, 1, &b); - } - */ + void randomize(uint8_t buf[], size_t len) override; + void add_entropy(const uint8_t in[], size_t length) override; + bool is_seeded() const override { return true; } + void clear() override {} + std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; } + private: + int m_fd; + }; - if(len > 0) +void System_RNG_Impl::randomize(uint8_t buf[], size_t len) + { + while(len) { - secure_vector<uint8_t> buf(input, input + len); - ::CryptGenRandom(m_prov, static_cast<DWORD>(buf.size()), buf.data()); + ssize_t got = ::read(m_fd, buf, len); + + if(got < 0) + { + if(errno == EINTR) + continue; + throw Exception("System_RNG read failed error " + std::to_string(errno)); + } + if(got == 0) + throw Exception("System_RNG EOF on device"); // ?!? + + buf += got; + len -= got; } -#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - // arc4random(3) reseeds itself from the OpenBSD kernel, not from user land. -#else + } + +void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len) + { while(len) { ssize_t got = ::write(m_fd, input, len); @@ -162,34 +222,9 @@ void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len) input += got; len -= got; } -#endif } -void System_RNG_Impl::randomize(uint8_t buf[], size_t len) - { -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) - ::CryptGenRandom(m_prov, static_cast<DWORD>(len), buf); -#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - ::arc4random_buf(buf, len); -#else - while(len) - { - ssize_t got = ::read(m_fd, buf, len); - - if(got < 0) - { - if(errno == EINTR) - continue; - throw Exception("System_RNG read failed error " + std::to_string(errno)); - } - if(got == 0) - throw Exception("System_RNG EOF on device"); // ?!? - - buf += got; - len -= got; - } #endif - } } diff --git a/src/lib/utils/dyn_load/dyn_load.cpp b/src/lib/utils/dyn_load/dyn_load.cpp index 1f33dc761..2b24e5f6c 100644 --- a/src/lib/utils/dyn_load/dyn_load.cpp +++ b/src/lib/utils/dyn_load/dyn_load.cpp @@ -13,6 +13,7 @@ #include <dlfcn.h> #elif defined(BOTAN_TARGET_OS_HAS_LOADLIBRARY) #define NOMINMAX 1 + #define _WINSOCKAPI_ // stop windows.h including winsock.h #include <windows.h> #endif diff --git a/src/lib/utils/http_util/http_util.cpp b/src/lib/utils/http_util/http_util.cpp index 4b0db03a5..73efb7adc 100644 --- a/src/lib/utils/http_util/http_util.cpp +++ b/src/lib/utils/http_util/http_util.cpp @@ -9,55 +9,10 @@ #include <botan/http_util.h> #include <botan/parsing.h> #include <botan/hex.h> +#include <botan/internal/os_utils.h> #include <botan/internal/stl_util.h> #include <sstream> -#if defined(BOTAN_HAS_BOOST_ASIO) - - /* - * We don't need serial port support anyway, and asking for it - * causes macro conflicts with Darwin's termios.h when this - * file is included in the amalgamation. GH #350 - */ - #define BOOST_ASIO_DISABLE_SERIAL_PORT - #include <boost/asio.hpp> - -#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) -#if defined(BOTAN_TARGET_OS_IS_WINDOWS) - #include <winsock2.h> - #include <WS2tcpip.h> - -namespace { - -int close(int fd) - { - return ::closesocket(fd); - } - -int read(int s, void* buf, size_t len) - { - return ::recv(s, reinterpret_cast<char*>(buf), static_cast<int>(len), 0); - } - -int write(int s, const char* buf, size_t len) - { - return ::send(s, reinterpret_cast<const char*>(buf), static_cast<int>(len), 0); - } - -} - -typedef size_t ssize_t; -#else - #include <sys/types.h> - #include <sys/socket.h> - #include <netdb.h> - #include <unistd.h> - #include <netinet/in.h> -#endif -#else - //#warning "No network support enabled in http_util" -#endif - namespace Botan { namespace HTTP { @@ -71,111 +26,36 @@ namespace { std::string http_transact(const std::string& hostname, const std::string& message) { -#if defined(BOTAN_HAS_BOOST_ASIO) - using namespace boost::asio::ip; - - boost::asio::ip::tcp::iostream tcp; - - tcp.connect(hostname, "http"); - - if(!tcp) - throw HTTP_Error("HTTP connection to " + hostname + " failed"); - - tcp << message; - tcp.flush(); - - std::ostringstream oss; - oss << tcp.rdbuf(); + std::unique_ptr<OS::Socket> socket; - return oss.str(); -#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) - -#if defined(BOTAN_TARGET_OS_IS_WINDOWS) - WSAData wsa_data; - WORD wsa_version = MAKEWORD(2, 2); - - if (::WSAStartup(wsa_version, &wsa_data) != 0) + try { - throw HTTP_Error("WSAStartup() failed: " + std::to_string(WSAGetLastError())); + socket = OS::open_socket(hostname, "http"); + if(!socket) + throw Exception("No socket support enabled in build"); } - - if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) + catch(std::exception& e) { - ::WSACleanup(); - throw HTTP_Error("Could not find a usable version of Winsock.dll"); + throw HTTP_Error("HTTP connection to " + hostname + " failed: " + e.what()); } -#endif - - hostent* host_addr = ::gethostbyname(hostname.c_str()); - uint16_t port = 80; - - if(!host_addr) - throw HTTP_Error("Name resolution failed for " + hostname); - - if(host_addr->h_addrtype != AF_INET) // FIXME - throw HTTP_Error("Hostname " + hostname + " resolved to non-IPv4 address"); - - struct socket_raii { - socket_raii(int fd) : m_fd(fd) {} - ~socket_raii() - { - ::close(m_fd); -#if defined(BOTAN_TARGET_OS_IS_WINDOWS) - ::WSACleanup(); -#endif - } - int m_fd; - }; - - int fd = ::socket(PF_INET, SOCK_STREAM, 0); - if(fd == -1) - throw HTTP_Error("Unable to create TCP socket"); - socket_raii raii(fd); - sockaddr_in socket_info; - ::memset(&socket_info, 0, sizeof(socket_info)); - socket_info.sin_family = AF_INET; - socket_info.sin_port = htons(port); - - ::memcpy(&socket_info.sin_addr, - host_addr->h_addr, - host_addr->h_length); - - socket_info.sin_addr = *reinterpret_cast<struct in_addr*>(host_addr->h_addr); // FIXME - - if(::connect(fd, reinterpret_cast<sockaddr*>(&socket_info), sizeof(struct sockaddr)) != 0) - throw HTTP_Error("HTTP connection to " + hostname + " failed"); - - size_t sent_so_far = 0; - while(sent_so_far != message.size()) - { - size_t left = message.size() - sent_so_far; - ssize_t sent = ::write(fd, &message[sent_so_far], left); - - if(sent < 0) - throw HTTP_Error("write to HTTP server failed, error '" + std::string(::strerror(errno)) + "'"); - else - sent_so_far += static_cast<size_t>(sent); - } + // Blocks until entire message has been written + socket->write(reinterpret_cast<const uint8_t*>(message.data()), + message.size()); std::ostringstream oss; - std::vector<char> buf(1024); // arbitrary size + std::vector<uint8_t> buf(BOTAN_DEFAULT_BUFFER_SIZE); while(true) { - ssize_t got = ::read(fd, buf.data(), buf.size()); + const size_t got = socket->read(buf.data(), buf.size()); + if(got == 0) // EOF + break; - if(got < 0) - throw HTTP_Error("read from HTTP server failed, error '" + std::string(::strerror(errno)) + "'"); - else if(got > 0) - oss.write(buf.data(), static_cast<std::streamsize>(got)); - else - break; // EOF + oss.write(reinterpret_cast<const char*>(buf.data()), + static_cast<std::streamsize>(got)); } - return oss.str(); -#else - throw HTTP_Error("Cannot connect to " + hostname + ": network code disabled in build"); -#endif + return oss.str(); } } diff --git a/src/lib/utils/mem_ops.cpp b/src/lib/utils/mem_ops.cpp deleted file mode 100644 index c81d4fac2..000000000 --- a/src/lib/utils/mem_ops.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* -* Memory Scrubbing -* (C) 2012,2015,2016 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include <botan/mem_ops.h> - -#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY) - #define NOMINMAX 1 - #include <windows.h> -#endif - -namespace Botan { - -void secure_scrub_memory(void* ptr, size_t n) - { -#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY) - ::RtlSecureZeroMemory(ptr, n); -#elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1) - /* - Call memset through a static volatile pointer, which the compiler - should not elide. This construct should be safe in conforming - compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and - Clang 3.8 both create code that saves the memset address in the - data segment and uncondtionally loads and jumps to that address. - */ - static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset; - (memset_ptr)(ptr, 0, n); -#else - volatile uint8_t* p = reinterpret_cast<volatile uint8_t*>(ptr); - - for(size_t i = 0; i != n; ++i) - p[i] = 0; -#endif - } - -} diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp index d08e7e040..f6ac38c0a 100644 --- a/src/lib/utils/os_utils.cpp +++ b/src/lib/utils/os_utils.cpp @@ -12,6 +12,17 @@ #include <botan/mem_ops.h> #include <chrono> +#if defined(BOTAN_HAS_BOOST_ASIO) + + /* + * We don't need serial port support anyway, and asking for it + * causes macro conflicts with Darwin's termios.h when this + * file is included in the amalgamation. GH #350 + */ + #define BOOST_ASIO_DISABLE_SERIAL_PORT + #include <boost/asio.hpp> +#endif + #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) #include <sys/types.h> #include <sys/mman.h> @@ -19,15 +30,280 @@ #include <unistd.h> #include <signal.h> #include <setjmp.h> -#endif + #include <sys/socket.h> + #include <netinet/in.h> + #include <netdb.h> -#if defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW) +#elif defined(BOTAN_TARGET_OS_TYPE_IS_WINDOWS) #define NOMINMAX 1 + #include <winsock2.h> + #include <WS2tcpip.h> #include <windows.h> #endif namespace Botan { +std::unique_ptr<OS::Socket> +OS::open_socket(const std::string& hostname, + const std::string& service) + { +#if defined(BOTAN_HAS_BOOST_ASIO) + class Asio_Socket : public OS::Socket + { + public: + Asio_Socket(const std::string& hostname, const std::string& service) : + m_tcp(m_io) + { + boost::asio::ip::tcp::resolver resolver(m_io); + boost::asio::ip::tcp::resolver::query query(hostname, service); + boost::asio::connect(m_tcp, resolver.resolve(query)); + } + + void write(const uint8_t buf[], size_t len) + { + boost::asio::write(m_tcp, boost::asio::buffer(buf, len)); + } + + size_t read(uint8_t buf[], size_t len) + { + boost::system::error_code error; + size_t got = m_tcp.read_some(boost::asio::buffer(buf, len), error); + + if(error) + { + if(error == boost::asio::error::eof) + return 0; + throw boost::system::system_error(error); // Some other error. + } + + return got; + } + + private: + boost::asio::io_service m_io; + boost::asio::ip::tcp::socket m_tcp; + }; + + return std::unique_ptr<OS::Socket>(new Asio_Socket(hostname, service)); + +#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) + + class Winsock_Socket : public OS::Socket + { + public: + Winsock_Socket(const std::string& hostname, const std::string& service) + { + WSAData wsa_data; + WORD wsa_version = MAKEWORD(2, 2); + + if (::WSAStartup(wsa_version, &wsa_data) != 0) + { + throw Exception("WSAStartup() failed: " + std::to_string(WSAGetLastError())); + } + + if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) + { + ::WSACleanup(); + throw Exception("Could not find a usable version of Winsock.dll"); + } + + addrinfo hints; + ::memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + addrinfo* res; + + if(::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res) != 0) + { + throw Exception("Name resolution failed for " + hostname); + } + + for(addrinfo* rp = res; (m_socket < 0) && (rp != nullptr); rp = rp->ai_next) + { + m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + // unsupported socket type? + if(m_socket == INVALID_SOCKET) + continue; + + if(::connect(m_socket, rp->ai_addr, rp->ai_addrlen) != 0) + { + ::closesocket(m_socket); + m_socket = INVALID_SOCKET; + continue; + } + } + + ::freeaddrinfo(res); + + if(m_socket == INVALID_SOCKET) + { + throw Exception("Connecting to " + hostname + + " for service " + service + " failed"); + } + } + + ~Winsock_Socket() + { + ::closesocket(m_socket); + m_socket = INVALID_SOCKET; + ::WSACleanup(); + } + + void write(const uint8_t buf[], size_t len) + { + size_t sent_so_far = 0; + while(sent_so_far != len) + { + const size_t left = len - sent_so_far; + int sent = ::send(m_socket, + reinterpret_cast<const char*>(buf + sent_so_far), + static_cast<int>(left), + 0); + + if(sent == SOCKET_ERROR) + throw Exception("Socket write failed with error " + + std::to_string(::WSAGetLastError())); + else + sent_so_far += static_cast<size_t>(sent); + } + } + + size_t read(uint8_t buf[], size_t len) + { + int got = ::recv(m_socket, + reinterpret_cast<char*>(buf), + static_cast<int>(len), 0); + + if(got == SOCKET_ERROR) + throw Exception("Socket read failed with error " + + std::to_string(::WSAGetLastError())); + return static_cast<size_t>(got); + } + + private: + SOCKET m_socket = INVALID_SOCKET; + }; + + return std::unique_ptr<OS::Socket>(new Winsock_Socket(hostname, service)); + +#elif defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) + + class BSD_Socket : public OS::Socket + { + public: + BSD_Socket(const std::string& hostname, const std::string& service) + { + addrinfo hints; + ::memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + addrinfo* res; + + if(::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res) != 0) + { + throw Exception("Name resolution failed for " + hostname); + } + + m_fd = -1; + + for(addrinfo* rp = res; (m_fd < 0) && (rp != nullptr); rp = rp->ai_next) + { + m_fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if(m_fd < 0) + { + // unsupported socket type? + continue; + } + + if(::connect(m_fd, rp->ai_addr, rp->ai_addrlen) != 0) + { + ::close(m_fd); + m_fd = -1; + continue; + } + } + + ::freeaddrinfo(res); + + if(m_fd < 0) + { + throw Exception("Connecting to " + hostname + + " for service " + service + " failed"); + } + } + + ~BSD_Socket() + { + ::close(m_fd); + m_fd = -1; + } + + void write(const uint8_t buf[], size_t len) + { + size_t sent_so_far = 0; + while(sent_so_far != len) + { + const size_t left = len - sent_so_far; + ssize_t sent = ::write(m_fd, &buf[sent_so_far], left); + if(sent < 0) + throw Exception("Socket write failed with error '" + + std::string(::strerror(errno)) + "'"); + else + sent_so_far += static_cast<size_t>(sent); + } + } + + size_t read(uint8_t buf[], size_t len) + { + ssize_t got = ::read(m_fd, buf, len); + + if(got < 0) + throw Exception("Socket read failed with error '" + + std::string(::strerror(errno)) + "'"); + return static_cast<size_t>(got); + } + + private: + int m_fd; + }; + + return std::unique_ptr<OS::Socket>(new BSD_Socket(hostname, service)); + +#else + // No sockets for you + return std::unique_ptr<Socket>(); +#endif + } + +// Not defined in OS namespace for historical reasons +void secure_scrub_memory(void* ptr, size_t n) + { + // TODO support explicit_bzero + +#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY) + ::RtlSecureZeroMemory(ptr, n); + +#elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1) + /* + Call memset through a static volatile pointer, which the compiler + should not elide. This construct should be safe in conforming + compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and + Clang 3.8 both create code that saves the memset address in the + data segment and uncondtionally loads and jumps to that address. + */ + static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset; + (memset_ptr)(ptr, 0, n); +#else + + volatile uint8_t* p = reinterpret_cast<volatile uint8_t*>(ptr); + + for(size_t i = 0; i != n; ++i) + p[i] = 0; +#endif + } + uint32_t OS::get_process_id() { #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h index cae1192f1..2146e11b3 100644 --- a/src/lib/utils/os_utils.h +++ b/src/lib/utils/os_utils.h @@ -23,6 +23,39 @@ namespace OS { * this hasn't been tested. */ + +/** +* A wrapper around a simple blocking TCP socket +*/ +class BOTAN_DLL Socket + { + public: + /** + * The socket will be closed upon destruction + */ + virtual ~Socket() {}; + + /** + * Write to the socket. Blocks until all bytes sent. + * Throws on error. + */ + virtual void write(const uint8_t buf[], size_t len) = 0; + + /** + * Reads up to len bytes, returns bytes written to buf. + * Returns 0 on EOF. Throws on error. + */ + virtual size_t read(uint8_t buf[], size_t len) = 0; + }; + +/** +* Open up a socket. Will throw on error. Returns null if sockets are +* not available on this platform. +*/ +std::unique_ptr<Socket> +BOTAN_DLL open_socket(const std::string& hostname, + const std::string& service); + /** * @return process ID assigned by the operating system. * On Unix and Windows systems, this always returns a result |