Skip to content

Commit

Permalink
dbtools: Remove openssl dependency when compiling with Visual Studio;…
Browse files Browse the repository at this point in the history
… assorted bug fixes.
  • Loading branch information
shawnw committed Mar 13, 2018
1 parent 483e58e commit 306fadc
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 55 deletions.
2 changes: 2 additions & 0 deletions config.h.in
Expand Up @@ -425,6 +425,8 @@ typedef bool _Bool;

#if defined(__MINGW32__) || defined(__MINGW64__)
#include <w32api.h>
#undef _WIN32_WINNT
#undef WINVER
#define _WIN32_WINNT Windows7
#define WINVER Windows7
#endif
Expand Down
14 changes: 11 additions & 3 deletions dbtools/CMakeLists.txt
Expand Up @@ -10,7 +10,11 @@ if(MSVC)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
endif()

find_package(OpenSSL REQUIRED)
if(WIN32)
find_library(BCRYPT_LIBRARY bcrypt)
else()
find_package(OpenSSL REQUIRED)
endif()
find_package(Boost REQUIRED COMPONENTS iostreams program_options)
check_include_file_cxx(boost/container/flat_map.hpp HAVE_BOOST_CONTAINERS)
check_include_file_cxx(boost/utility/string_view.hpp HAVE_BOOST_STRING_VIEW)
Expand Down Expand Up @@ -82,14 +86,18 @@ endif()
target_include_directories(grepdb PRIVATE ${Boost_INCLUDE_DIRS})
target_link_libraries(grepdb dbio ${MY_LIBRARIES})

add_executable(pwutil pwutil.cpp)
add_executable(pwutil pwutil.cpp hasher.cpp)
if(SUPPORTS_CXX17)
set_property(TARGET pwutil PROPERTY CXX_STANDARD 17)
else()
set_property(TARGET pwutil PROPERTY CXX_STANDARD 14)
endif()
target_include_directories(pwutil PRIVATE ${OPENSSL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
target_link_libraries(pwutil dbio OpenSSL::Crypto ${MY_LIBRARIES})
if(WIN32)
target_link_libraries(pwutil dbio ${BCRYPT_LIBRARY} ${MY_LIBRARIES})
else()
target_link_libraries(pwutil dbio OpenSSL::Crypto ${MY_LIBRARIES})
endif()

add_custom_target(indent COMMAND ${INDENT} -i --style=file *.cpp *.h
COMMENT "Formatting source files")
Expand Down
8 changes: 4 additions & 4 deletions dbtools/README.md
Expand Up @@ -47,13 +47,13 @@ Windows, using Visual Studio 2017
on that page.

3. Install the following packages if needed (Via `.\vcpkg install`):
**openssl boost-iostreams boost-program-options boost-algorithm
boost-container** (This might take a while)
**boost-iostreams boost-program-options boost-algorithm boost-container**
(This might take a while)

vcpkg supports both 32-bit and 64-bit targets. You might need to
explicitly request the one you want to use (64 bit is suggested),
by `.\vcpkg install openssl:x64-windows` or `.\vcpkg install
openssl:x86-windows`.
by `.\vcpkg install boost-iostreams:x64-windows` or `.\vcpkg install
boost-iostreams:x86-windows`.

### Actually building

Expand Down
4 changes: 2 additions & 2 deletions dbtools/database.cpp
Expand Up @@ -335,7 +335,7 @@ read_database(const std::string &name, COMP compress_type, bool vrbse)
if (verbose) {
std::cerr << "Reading from " << name << '\n';
}
dbin.push(io::file_source{name});
dbin.push(io::file_source{name, std::ios_base::in | std::ios_base::binary});
}

dbin.exceptions(std::istream::badbit);
Expand Down Expand Up @@ -380,7 +380,7 @@ write_database(const database &db, const std::string &name, COMP compress_type)
if (name == "-") {
dbout.push(std::cout);
} else {
dbout.push(io::file_sink{name});
dbout.push(io::file_sink{name, std::ios_base::out | std::ios_base::binary});
}

if (!dbout) {
Expand Down
151 changes: 151 additions & 0 deletions dbtools/hasher.cpp
@@ -0,0 +1,151 @@
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <memory>
#include <stdexcept>
#include <random>
#include <ctime>

#ifdef WIN32
#include <Windows.h>
#include <Bcrypt.h>
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS 0
#endif
#else
#include <openssl/sha.h>
#endif

#include "hasher.h"

std::string
password_hasher::salt(void)
{
static const char salts[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

static std::random_device rd;
static std::ranlux48 rng{rd()};
std::uniform_int_distribution<int> pick(0, sizeof salts - 1);

char salt[2];
salt[0] = salts[pick(rng)];
salt[1] = salts[pick(rng)];

return std::string(salt, 2);
}

std::string
password_hasher::make_password(const std::string &plain)
{
std::ostringstream sink;

auto saltchars = salt();

auto hashed = hash(saltchars, plain);

sink << "2:" << algo() << ':' << saltchars << std::hex << std::setfill('0');
for (unsigned char b : hashed) {
sink << std::setw(2) << static_cast<unsigned>(b);
}
sink << std::dec << ':' << std::time(nullptr);

return sink.str();
}

#ifdef WIN32

class win32_sha256_hasher : public password_hasher
{
public:
win32_sha256_hasher();
~win32_sha256_hasher();

protected:
const char *
algo() const override
{
return "sha256";
}
std::string hash(const std::string &, const std::string &) override;

private:
BCRYPT_ALG_HANDLE balgo;
DWORD hashlen = 0;
};

win32_sha256_hasher::win32_sha256_hasher()
{
if (BCryptOpenAlgorithmProvider(&balgo, BCRYPT_SHA256_ALGORITHM, NULL, 0) !=
STATUS_SUCCESS) {
throw std::runtime_error("Unable to open bcrypt algorithm provider");
}
ULONG cbhash = 0;
if (BCryptGetProperty(balgo, BCRYPT_HASH_LENGTH, (PBYTE) &hashlen,
sizeof(hashlen), &cbhash, 0) != STATUS_SUCCESS) {
BCryptCloseAlgorithmProvider(balgo, 0);
throw std::runtime_error("Unable to fetch hash length");
}
}

win32_sha256_hasher::~win32_sha256_hasher()
{
BCryptCloseAlgorithmProvider(balgo, 0);
}

std::string
win32_sha256_hasher::hash(const std::string &saltchars,
const std::string &plain)
{
BCRYPT_HASH_HANDLE hfun;
if (BCryptCreateHash(balgo, &hfun, NULL, 0, NULL, 0, 0) != STATUS_SUCCESS) {
throw std::runtime_error("Unable to create bcrypt hash algorithm");
}
BCryptHashData(hfun, (PUCHAR) saltchars.data(),
static_cast<ULONG>(saltchars.size()), 0);
BCryptHashData(hfun, (PUCHAR) plain.data(), static_cast<ULONG>(plain.size()),
0);
std::string hashed(hashlen, '\0');
BCryptFinishHash(hfun, (PUCHAR) hashed.data(), hashlen, 0);
BCryptDestroyHash(hfun);
return hashed;
}

#else

class openssl_sha256_hasher : public password_hasher
{
protected:
const char *
algo() const override
{
return "sha256";
}
std::string hash(const std::string &, const std::string &) override;
};

std::string
openssl_sha256_hasher::hash(const std::string &saltchars,
const std::string &plain)
{
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, saltchars.data(), saltchars.size());
SHA256_Update(&ctx, plain.data(), plain.size());
std::string hashed(SHA256_DIGEST_LENGTH, '\0');
SHA256_Final(reinterpret_cast<unsigned char *>(&hashed[0]), &ctx);
return hashed;
}

#endif

std::unique_ptr<password_hasher>
make_password_hasher()
{
#ifdef WIN32
return std::make_unique<win32_sha256_hasher>();
#else
return std::make_unique<openssl_sha256_hasher>();
#endif
}
15 changes: 15 additions & 0 deletions dbtools/hasher.h
@@ -0,0 +1,15 @@
#pragma once

class password_hasher
{
public:
virtual ~password_hasher(){};
std::string make_password(const std::string &);

protected:
std::string salt(void);
virtual const char *algo() const = 0;
virtual std::string hash(const std::string &, const std::string &) = 0;
};

std::unique_ptr<password_hasher> make_password_hasher();
55 changes: 11 additions & 44 deletions dbtools/pwutil.cpp
@@ -1,61 +1,26 @@
#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
#include <random>

#include <openssl/sha.h>
#include <boost/program_options.hpp>

#include "database.h"
#include "hasher.h"

using namespace std::literals::string_literals;

std::string
make_password_string(const std::string &plain)
{
static const unsigned char salts[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

static std::random_device rd;
static std::ranlux48 rng{rd()};
std::uniform_int_distribution<int> pick(0, sizeof salts - 1);

unsigned char salt[2];
salt[0] = salts[pick(rng)];
salt[1] = salts[pick(rng)];

SHA512_CTX ctx;
SHA512_Init(&ctx);
SHA512_Update(&ctx, salt, 2);
SHA512_Update(&ctx, reinterpret_cast<const unsigned char *>(plain.c_str()),
plain.size());
unsigned char hashed[SHA512_DIGEST_LENGTH];
SHA512_Final(hashed, &ctx);

std::ostringstream sink;

sink << "2:sha512:" << salt[0] << salt[1] << std::hex;
for (auto byte : hashed) {
sink << static_cast<unsigned>(byte);
}
sink << std::dec << ':' << std::time(nullptr);

return sink.str();
}

void
update_password(database &db, dbref who, const std::string &newpass)
update_password(database &db, dbref who, const std::string &newpass,
password_hasher *hash)
{
auto xyxxy = db.objects[who].attribs.find("XYXXY");
if (xyxxy != db.objects[who].attribs.end()) {
// Update existing attribute
xyxxy->second.data = make_password_string(newpass);
xyxxy->second.data = hash->make_password(newpass);
} else {
// Add a new attribute if not already present
attrib newxyxxy = db.attribs["XYXXY"];
newxyxxy.creator = 1;
newxyxxy.data = make_password_string(newpass);
newxyxxy.data = hash->make_password(newpass);
db.objects[who].attribs.emplace("XYXXY", std::move(newxyxxy));
}
}
Expand All @@ -76,7 +41,7 @@ main(int argc, char **argv)
",j", po::value<int>(&comp)->implicit_value(COMP::BZ2, "")->zero_tokens(),
"compressed with bzip2")("inplace,i", po::bool_switch(&inplace),
"update database in place")(
"dbref,d", po::value<dbref>(&who)->default_value(-1),
"dbref,d", po::value<int>(&who)->default_value(-1),
"Player to modify")("all,a", po::bool_switch(&all), "Modify all players")(
"clear,c", po::bool_switch(&clear), "Erase password")(
"password,p", po::value<std::string>(&newpass)->default_value("hunter2"),
Expand Down Expand Up @@ -112,12 +77,14 @@ main(int argc, char **argv)
std::string input_db = "-";

if (vm.count("input-file")) {
input_db = vm["input-file"].as<std::vector<std::string>>().front();
input_db = vm["input-file"].as<std::string>();
}

auto db = read_database(input_db, static_cast<COMP>(comp));
db.fix_up();

auto hash = make_password_hasher();

if (who >= 0) {
if (static_cast<std::size_t>(who) >= db.objects.size()) {
std::cerr << "Object #" << who << " is out of range!\n";
Expand All @@ -130,15 +97,15 @@ main(int argc, char **argv)
if (clear) {
db.objects[who].attribs.erase("XYXXY");
} else {
update_password(db, who, newpass);
update_password(db, who, newpass, hash.get());
}
} else if (all) {
for (auto &obj : db.objects) {
if (obj.type == dbtype::PLAYER) {
if (clear) {
obj.attribs.erase("XYXXY");
} else {
update_password(db, obj.num, newpass);
update_password(db, obj.num, newpass, hash.get());
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/mycrypt.c
Expand Up @@ -119,7 +119,8 @@ safe_hash_byname(const char *algo, const char *plaintext, int len, char *buff,
BCRYPT_ALG_HANDLE balgo;
BCRYPT_HASH_HANDLE hfun;
PUCHAR hash;
ULONG hashlen = 0, cbhash = 0;
DWORD hashlen = 0;
ULONG cbhash = 0;
int r;

dgst = lookup_bcrypt_algo(algo);
Expand Down Expand Up @@ -151,7 +152,7 @@ safe_hash_byname(const char *algo, const char *plaintext, int len, char *buff,
BCryptCloseAlgorithmProvider(balgo, 0);
return 1;
}
if (BCryptGetProperty(balgo, BCRYPT_HASH_LENGTH, (PBYTE)&hashlen, sizeof(DWORD),
if (BCryptGetProperty(balgo, BCRYPT_HASH_LENGTH, (PBYTE)&hashlen, sizeof(hashlen),
&cbhash, 0) != STATUS_SUCCESS) {
if (inplace_err)
safe_str(T("#-1 UNSUPPORTED DIGEST TYPE"), buff, bp);
Expand Down

0 comments on commit 306fadc

Please sign in to comment.