diff --git a/include/Http.h b/include/Http.h index 3ff2cd5..35fb7b5 100644 --- a/include/Http.h +++ b/include/Http.h @@ -3,8 +3,6 @@ #include #include #include -#include -#include #include #include @@ -19,10 +17,6 @@ namespace fs = std::filesystem; -namespace Crypto { -constexpr size_t RSA_DEFAULT_KEYLENGTH { 2048 }; -} - namespace Http { std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr); std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const httplib::Headers& headers = {}); @@ -32,8 +26,6 @@ namespace Status { const std::string ErrorString = "-1"; namespace Server { - void SetupEnvironment(); - // todo: Add non TLS Server Instance, this one is TLS only class THttpServerInstance { public: THttpServerInstance(); @@ -46,15 +38,5 @@ namespace Server { private: std::thread mThread; }; - // todo: all of these functions are likely unsafe, - // todo: replace with something that's managed by a domain specific crypto library - class Tx509KeypairGenerator { - public: - static long GenerateRandomId(); - static bool EnsureTLSConfigExists(); - static X509* GenerateCertificate(EVP_PKEY& pkey); - static EVP_PKEY* GenerateKey(); - static void GenerateAndWriteToDisk(const fs::path& KeyFilePath, const fs::path& CertFilePath); - }; } } diff --git a/src/Http.cpp b/src/Http.cpp index aeb66d5..8edc249 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -7,17 +7,13 @@ #include "httplib.h" #include +#include #include -#include -#include -#include -#include #include -fs::path Http::Server::THttpServerInstance::KeyFilePath; -fs::path Http::Server::THttpServerInstance::CertFilePath; + // TODO: Add sentry error handling back -namespace json = rapidjson; +using json = nlohmann::json; std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) { httplib::SSLClient client(host, port); @@ -146,147 +142,6 @@ std::string Http::Status::ToString(int Code) { } } -long Http::Server::Tx509KeypairGenerator::GenerateRandomId() { - std::random_device R; - std::default_random_engine E1(R()); - std::uniform_int_distribution UniformDist(0, ULONG_MAX); - return UniformDist(E1); -} - -// Http::Server::THttpServerInstance::THttpServerInstance() { } -EVP_PKEY* Http::Server::Tx509KeypairGenerator::GenerateKey() { - /** - * Allocate memory for the pkey - */ - EVP_PKEY* PKey = EVP_PKEY_new(); - if (PKey == nullptr) { - beammp_error("Could not allocate memory for X.509 private key (PKEY) generation."); - throw std::runtime_error { std::string { "X.509 PKEY allocation error" } }; - } - BIGNUM* E = BN_new(); - beammp_assert(E); // TODO: replace all these asserts with beammp_errors - unsigned char three = 3; - BIGNUM* EErr = BN_bin2bn(&three, sizeof(three), E); - beammp_assert(EErr); - RSA* Rsa = RSA_new(); - beammp_assert(Rsa); - int Ret = RSA_generate_key_ex(Rsa, Crypto::RSA_DEFAULT_KEYLENGTH, E, nullptr); - beammp_assert(Ret == 1); - BN_free(E); - if (!EVP_PKEY_assign_RSA(PKey, Rsa)) { - EVP_PKEY_free(PKey); - beammp_error(std::string("Could not generate " + std::to_string(Crypto::RSA_DEFAULT_KEYLENGTH) + "-bit RSA key.")); - throw std::runtime_error { std::string("X.509 RSA key generation error") }; - } - // todo: figure out if returning by reference instead of passing pointers is a security breach - return PKey; -} - -X509* Http::Server::Tx509KeypairGenerator::GenerateCertificate(EVP_PKEY& PKey) { - X509* X509 = X509_new(); - if (X509 == nullptr) { - X509_free(X509); - beammp_error("Could not allocate memory for X.509 certificate generation."); - throw std::runtime_error { std::string("X.509 certificate generation error") }; - } - - /**Set the metadata of the certificate*/ - ASN1_INTEGER_set(X509_get_serialNumber(X509), GenerateRandomId()); - - /**Set the cert validity to a year*/ - X509_gmtime_adj(X509_get_notBefore(X509), 0); - X509_gmtime_adj(X509_get_notAfter(X509), 31536000L); - - /**Set the public key of the cert*/ - X509_set_pubkey(X509, &PKey); - - X509_NAME* Name = X509_get_subject_name(X509); - - /**Set cert metadata*/ - X509_NAME_add_entry_by_txt(Name, "C", MBSTRING_ASC, (unsigned char*)"GB", -1, -1, 0); - X509_NAME_add_entry_by_txt(Name, "O", MBSTRING_ASC, (unsigned char*)"BeamMP Ltd.", -1, -1, 0); - X509_NAME_add_entry_by_txt(Name, "CN", MBSTRING_ASC, (unsigned char*)"localhost", -1, -1, 0); - - X509_set_issuer_name(X509, Name); - - // TODO: Hashing with sha256 might cause problems, check later - if (!X509_sign(X509, &PKey, EVP_sha1())) { - X509_free(X509); - beammp_error("Could not sign X.509 certificate."); - throw std::runtime_error { std::string("X.509 certificate signing error") }; - } - return X509; -} - -void Http::Server::Tx509KeypairGenerator::GenerateAndWriteToDisk(const fs::path& KeyFilePath, const fs::path& CertFilePath) { - // todo: generate directories for ssl keys - FILE* KeyFile = std::fopen(reinterpret_cast(KeyFilePath.c_str()), "wb"); - if (!KeyFile) { - beammp_error("Could not create file 'key.pem', check your permissions"); - throw std::runtime_error("Could not create file 'key.pem'"); - } - - EVP_PKEY* PKey = Http::Server::Tx509KeypairGenerator::GenerateKey(); - - bool WriteOpResult = PEM_write_PrivateKey(KeyFile, PKey, nullptr, nullptr, 0, nullptr, nullptr); - fclose(KeyFile); - - if (!WriteOpResult) { - beammp_error("Could not write to file 'key.pem', check your permissions"); - throw std::runtime_error("Could not write to file 'key.pem'"); - } - - FILE* CertFile = std::fopen(reinterpret_cast(CertFilePath.c_str()), "wb"); // x509 file - if (!CertFile) { - beammp_error("Could not create file 'cert.pem', check your permissions"); - throw std::runtime_error("Could not create file 'cert.pem'"); - } - - X509* x509 = Http::Server::Tx509KeypairGenerator::GenerateCertificate(*PKey); - WriteOpResult = PEM_write_X509(CertFile, x509); - fclose(CertFile); - - if (!WriteOpResult) { - beammp_error("Could not write to file 'cert.pem', check your permissions"); - throw std::runtime_error("Could not write to file 'cert.pem'"); - } - EVP_PKEY_free(PKey); - X509_free(x509); - return; -} - -bool Http::Server::Tx509KeypairGenerator::EnsureTLSConfigExists() { - if (fs::is_regular_file(Application::Settings.SSLKeyPath) - && fs::is_regular_file(Application::Settings.SSLCertPath)) { - return true; - } else { - return false; - } -} - -void Http::Server::SetupEnvironment() { - if (!Application::Settings.HTTPServerUseSSL) { - return; - } - auto parent = fs::path(Application::Settings.SSLKeyPath).parent_path(); - if (!fs::exists(parent)) - fs::create_directories(parent); - - Application::TSettings defaultSettings {}; - if (!Tx509KeypairGenerator::EnsureTLSConfigExists()) { - beammp_warn(std::string("No default TLS Key / Cert found. " - "IF YOU HAVE NOT MODIFIED THE SSLKeyPath OR SSLCertPath VALUES " - "THIS IS NORMAL ON FIRST STARTUP! BeamMP will generate it's own certs in the default directory " - "(Check for permissions or corrupted key-/certfile)")); - Tx509KeypairGenerator::GenerateAndWriteToDisk(defaultSettings.SSLKeyPath, defaultSettings.SSLCertPath); - Http::Server::THttpServerInstance::KeyFilePath = defaultSettings.SSLKeyPath; - Http::Server::THttpServerInstance::CertFilePath = defaultSettings.SSLCertPath; - } else { - Http::Server::THttpServerInstance::KeyFilePath = Application::Settings.SSLKeyPath; - Http::Server::THttpServerInstance::CertFilePath = Application::Settings.SSLCertPath; - } -} - Http::Server::THttpServerInstance::THttpServerInstance() { Application::SetSubsystemStatus("HTTPServer", Application::Status::Starting); mThread = std::thread(&Http::Server::THttpServerInstance::operator(), this); @@ -324,48 +179,14 @@ void Http::Server::THttpServerInstance::operator()() try { break; } } - res.set_content(SystemsBad == 0 ? "0" : "1", "text/plain"); + res.set_content( + json { + { "ok", SystemsBad == 0 }, + } + .dump(), + "application/json"); res.status = 200; }); - /* - HttpLibServerInstance->Get("/status", [](const httplib::Request&, httplib::Response& res) { - try { - json::Document response; - response.SetObject(); - rapidjson::Document::AllocatorType& Allocator = response.GetAllocator(); - // add to response - auto& Server = LuaAPI::MP::Engine->Server(); - size_t CarCount = 0; - size_t GuestCount = 0; - json::Value Array(rapidjson::kArrayType); - LuaAPI::MP::Engine->Server().ForEachClient([&](std::weak_ptr Client) -> bool { - if (!Client.expired()) { - auto Locked = Client.lock(); - CarCount += Locked->GetCarCount(); - GuestCount += Locked->IsGuest() ? 1 : 0; - json::Value Player(json::kObjectType); - Player.AddMember("name", json::StringRef(Locked->GetName().c_str()), Allocator); - Player.AddMember("id", Locked->GetID(), Allocator); - Array.PushBack(Player, Allocator); - } - return true; - }); - response.AddMember("players", Array, Allocator); - response.AddMember("player_count", Server.ClientCount(), Allocator); - response.AddMember("guest_count", GuestCount, Allocator); - response.AddMember("car_count", CarCount, Allocator); - - // compile & send response - json::StringBuffer sb; - json::Writer writer(sb); - response.Accept(writer); - res.set_content(sb.GetString(), "application/json"); - } catch (const std::exception& e) { - beammp_error("Exception in /status endpoint: " + std::string(e.what())); - res.status = 500; - } - }); - */ // magic endpoint HttpLibServerInstance->Get({ 0x2f, 0x6b, 0x69, 0x74, 0x74, 0x79 }, [](const httplib::Request&, httplib::Response& res) { res.set_content(std::string(Magic), "text/plain");