diff --git a/src/log/LogUtils.cpp b/src/log/LogUtils.cpp index db4ea03..dbf53c0 100644 --- a/src/log/LogUtils.cpp +++ b/src/log/LogUtils.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -66,8 +67,15 @@ namespace logger { if(!logFile.parent_path().empty()) fs::create_directories(logFile.parent_path()); - auto fileSink = make_shared(logFile.string(), 1024 * 1024 * 50, 12); - sinks.push_back(fileSink); + try { + auto fileSink = make_shared(logFile.string(), 1024 * 1024 * 50, 12); + sinks.push_back(fileSink); + } catch(std::exception& ex) { + if(group != 0 && group != -1) + logger(0)->critical("Failed to create file for new log group: {}", ex.what()); + else + terminal::instance()->writeMessage("§4[CRITICAL] §eFailed to create main log file: " + string{ex.what()}, false); + } } else { path = "/dev/null (" + to_string(serverId) + ")"; } @@ -140,6 +148,7 @@ namespace logger { loggerEntry.second.reset(); } loggers.clear(); + spdlog::drop_all(); logConfig = nullptr; terminalSink = nullptr; diff --git a/src/misc/hex.h b/src/misc/hex.h index f14e489..c3f9d2a 100644 --- a/src/misc/hex.h +++ b/src/misc/hex.h @@ -15,6 +15,19 @@ namespace hex { output[idx++] = static_cast(beg + ((elm & 0x0F) >> 0)); } + return std::string(output, len); + } + inline std::string hex(const std::string& input){ + size_t len = input.length() * 2; + char output[len]; + size_t idx = 0; + for (char elm : input) { + auto lower = ((uint8_t) elm >> 4U) & 0xFU; + auto upper = ((uint8_t) elm & 0xFU) >> 0U; + output[idx++] = static_cast(lower > 9 ? 'a' + (lower - 9) : '0' + lower); + output[idx++] = static_cast(upper > 9 ? 'a' + (upper - 9) : '0' + upper); + } + return std::string(output, len); } } \ No newline at end of file diff --git a/src/ssl/SSLManager.cpp b/src/ssl/SSLManager.cpp index 2b2f73f..052b38a 100644 --- a/src/ssl/SSLManager.cpp +++ b/src/ssl/SSLManager.cpp @@ -111,11 +111,18 @@ do { \ return nullptr; \ } while(false) -std::shared_ptr SSLManager::initializeContext(const std::string &key, std::string &privateKey, std::string &certificate, std::string &error, bool raw, const std::shared_ptr& generator) { +std::shared_ptr SSLManager::initializeContext(const std::string &context, std::string &privateKey, std::string &certificate, std::string &error, bool raw, const std::shared_ptr& generator) { auto load = this->loadContext(privateKey, certificate, error, raw, generator); if(!load) return nullptr; - this->contexts[key] = load; + { + lock_guard lock{this->context_lock}; + this->contexts[context] = load; + } + if(context.find(this->web_ctx_prefix) == 0) { + lock_guard lock{this->_web_options_lock}; + this->_web_options.reset(); + } return load; } @@ -123,10 +130,59 @@ std::shared_ptr SSLManager::initializeSSLKey(const std::string &key, auto load = this->loadSSL(rsaKey, error, raw); if(!load) return nullptr; - this->rsa[key] = load; + { + lock_guard lock{this->context_lock}; + this->rsa[key] = load; + } return load; } +bool SSLManager::rename_context(const std::string &old_name, const std::string &new_name) { + { + lock_guard lock{this->context_lock}; + if(this->contexts.count(old_name) == 0) + return false; + auto old = std::move(this->contexts[old_name]); + this->contexts.erase(old_name); + this->contexts[new_name] = std::move(old); + } + + if(old_name.find(this->web_ctx_prefix) == 0 || new_name.find(this->web_ctx_prefix) == 0) { + lock_guard lock{this->_web_options_lock}; + this->_web_options.reset(); + } + return true; +} + +bool SSLManager::unregister_context(const std::string &context) { + { + lock_guard lock{this->context_lock}; + if(this->contexts.erase(context) == 0) + return false; + } + if(context.find(this->web_ctx_prefix) == 0) { + lock_guard lock{this->_web_options_lock}; + this->_web_options.reset(); + } + return true; +} + +void SSLManager::unregister_web_contexts() { + { + lock_guard lock{this->context_lock}; + decltype(this->contexts) ctxs{this->contexts}; + for(auto& [key, _] : ctxs) { + (void) _; + if(key.find(this->web_ctx_prefix) == 0) { + this->contexts.erase(key); + } + } + } + + lock_guard lock{this->_web_options_lock}; + this->_web_options.reset(); +} + EVP_PKEY* SSLGenerator::generateKey() { auto key = std::unique_ptr(EVP_PKEY_new(), ::EVP_PKEY_free); @@ -164,82 +220,95 @@ X509* SSLGenerator::generateCertificate(EVP_PKEY* key) { } //TODO passwords -std::shared_ptr SSLManager::loadContext(std::string &rawKey, std::string &rawCert, std::string &error, bool rawData, const shared_ptr& generator) { - std::shared_ptr certBio = nullptr; - std::shared_ptr keyBio = nullptr; - std::shared_ptr cert = nullptr; +std::shared_ptr SSLManager::loadContext(std::string &rawKey, std::string &rawCert, std::string &error, bool is_raw, const shared_ptr& generator) { + std::shared_ptr bio_certificate = nullptr; + std::shared_ptr bio_private_key = nullptr; + std::shared_ptr certificate = nullptr; std::shared_ptr key = nullptr; - bool flagCertModified = false; - bool flagKeyModified = false; + bool allow_generate_cert{false}, allow_generate_key{false}; + bool certificate_modified{false}, key_modified{false}; std::shared_ptr context = nullptr; std::shared_ptr result = nullptr; - if(rawData) { - certBio = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); - BIO_write(certBio.get(), rawCert.c_str(), rawCert.length()); + if(is_raw) { + if(!rawKey.empty()) { + bio_private_key = shared_ptr(BIO_new_mem_buf(rawKey.data(), rawKey.length()), ::BIO_free); - keyBio = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); - BIO_write(keyBio.get(), rawKey.c_str(), rawKey.length()); + if(!rawCert.empty()) { + bio_certificate = shared_ptr(BIO_new_mem_buf(rawCert.data(), rawCert.length()), ::BIO_free); + } else { + allow_generate_cert = true; + } + } else { + allow_generate_cert = true; + allow_generate_key = true; + } + if(!bio_certificate) bio_certificate = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); + if(!bio_private_key) bio_private_key = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); } else { - auto keyPath = fs::u8path(rawKey); - auto certPath = fs::u8path(rawCert); + auto key_path = fs::u8path(rawKey); + auto certificate_path = fs::u8path(rawCert); - if(!fs::exists(keyPath)) { - if(!generator) { - error = "Missing key file"; + auto key_exists = fs::exists(key_path); + auto certificate_exists = fs::exists(certificate_path); + + if(!key_exists) { + try { + if(key_path.has_parent_path()) + fs::create_directories(key_path.parent_path()); + } catch (fs::filesystem_error& ex) { + error = "failed to create keys file parent path: " + std::string{ex.what()}; return nullptr; } - try { - if(keyPath.has_parent_path()) - fs::create_directories(keyPath.parent_path()); - } catch (fs::filesystem_error& error) { - logError("Could not create key directory: " + string(error.what())); - } - { - std::ofstream { keyPath }; - } + std::ofstream {key_path}; + allow_generate_key = true; } - if(!fs::exists(certPath)) { - if(!generator) { - error = "Missing certificate file"; + if(!certificate_exists) { + try { + if(certificate_path.has_parent_path()) + fs::create_directories(certificate_path.parent_path()); + } catch (fs::filesystem_error& ex) { + error = "failed to create certificate file parent path: " + std::string{ex.what()}; return nullptr; } - try { - if(certPath.has_parent_path()) - fs::create_directories(certPath.parent_path()); - } catch (fs::filesystem_error& error) { - logError("Could not create certificate directory: " + string(error.what())); - } - { - std::ofstream { certPath }; - } + std::ofstream {certificate_path}; + allow_generate_cert = true; + } else if(!key_exists) { + error = "missing private key"; + return nullptr; } - auto mode = generator ? "rw" : "r"; - certBio = shared_ptr(BIO_new_file(rawCert.c_str(), mode), ::BIO_free); - if(!certBio) SSL_ERROR("Could not load certificate: "); - keyBio = shared_ptr(BIO_new_file(rawKey.c_str(), mode), ::BIO_free); - if(!keyBio) SSL_ERROR("Could not load key: "); + { + auto mode = allow_generate_cert ? "rw" : "r"; + bio_certificate = shared_ptr(BIO_new_file(rawCert.c_str(), mode), ::BIO_free); + if(!bio_certificate) SSL_ERROR("Could not open certificate: "); + } + + { + auto mode = allow_generate_key ? "rw" : "r"; + bio_private_key = shared_ptr(BIO_new_file(rawKey.c_str(), mode), ::BIO_free); + if(!bio_private_key) SSL_ERROR("Could not open key: "); + } } - cert = shared_ptr(PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr), ::X509_free); - if(!cert && !generator) SSL_ERROR("Could not read certificate: "); + certificate = shared_ptr(PEM_read_bio_X509(bio_certificate.get(), nullptr, nullptr, nullptr), ::X509_free); + if(!certificate && (!generator || !allow_generate_cert)) SSL_ERROR("Could not read certificate: "); - key = shared_ptr(PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr), ::EVP_PKEY_free); - if(!key && !generator) SSL_ERROR("Could not read key: "); + key = shared_ptr(PEM_read_bio_PrivateKey(bio_private_key.get(), nullptr, nullptr, nullptr), ::EVP_PKEY_free); + if(!key && (!generator || !allow_generate_key)) SSL_ERROR("Could not read key: "); if(!key) { key = shared_ptr(generator->generateKey(), ::EVP_PKEY_free); - flagKeyModified = true; + key_modified = true; } - if(!cert) { - cert = shared_ptr(generator->generateCertificate(key.get()), ::X509_free); - flagCertModified = true; + if(!certificate) { + certificate = shared_ptr(generator->generateCertificate(key.get()), ::X509_free); + certificate_modified = true; } //Create context @@ -248,88 +317,86 @@ std::shared_ptr SSLManager::loadContext(std::string &rawKey, std::st if (SSL_CTX_use_PrivateKey(context.get(), key.get()) <= 0) SSL_ERROR("Could not use private key: "); - if (SSL_CTX_use_certificate(context.get(), cert.get()) <= 0) SSL_ERROR("Could not use certificate: "); + if (SSL_CTX_use_certificate(context.get(), certificate.get()) <= 0) SSL_ERROR("Could not use certificate: "); result = std::make_shared(); result->context = context; - result->certificate = cert; + result->certificate = certificate; result->privateKey = key; - if(flagCertModified) { - if(!rawData) { - certBio = shared_ptr(BIO_new_file(rawCert.c_str(), "w"), ::BIO_free); - if(PEM_write_bio_X509(certBio.get(), cert.get()) != 1) SSL_ERROR("Could not write new certificate: "); - flagCertModified = false; + if(key_modified) { + if(!is_raw) { + bio_private_key = shared_ptr(BIO_new_file(rawKey.c_str(), "w"), ::BIO_free); + if(PEM_write_bio_PrivateKey(bio_private_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr) != 1) SSL_ERROR("Could not write new key: "); } else { - assert(false); //TODO implement with membuf - /* - void* ptr = nullptr; - auto length = BIO_get_mem_data(certBio, &ptr); - if(!ptr) SSL_ERROR("Could not get cert bio mem pointer: "); - if(length <= 0) SSL_ERROR("Could not get cert bio mem length (" + to_string(length) + "): "); - rawCert.reserve(length); - memcpy((void*) rawCert.data(), ptr, length); - */ + bio_private_key = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); + if(PEM_write_bio_PrivateKey(bio_private_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr) != 1) SSL_ERROR("Could not write new key: "); + + const uint8_t* mem_ptr{nullptr}; + size_t length{0}; + if(!BIO_mem_contents(&*bio_private_key, &mem_ptr, &length)) SSL_ERROR("Failed to get mem contents: "); + if(!mem_ptr || length < 0) SSL_ERROR("Could not get private key mem pointer/invalid length: "); + rawKey.reserve(length); + memcpy(rawKey.data(), mem_ptr, length); } } - if(flagKeyModified) { - if(!rawData) { - keyBio = shared_ptr(BIO_new_file(rawKey.c_str(), "w"), ::BIO_free); - if(PEM_write_bio_PrivateKey(keyBio.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr) != 1) SSL_ERROR("Could not write new key: "); - flagKeyModified = false; + if(certificate_modified) { + if(!is_raw) { + bio_certificate = shared_ptr(BIO_new_file(rawCert.c_str(), "w"), ::BIO_free); + if(PEM_write_bio_X509(bio_certificate.get(), certificate.get()) != 1) SSL_ERROR("Could not write new certificate: "); } else { - assert(false); //TODO implement with membuf - /* - void* ptr = nullptr; - auto length = BIO_get_mem_data(certBio, &ptr); - if(!ptr) SSL_ERROR("Could not get cert bio mem pointer: "); - if(length <= 0) SSL_ERROR("Could not get cert bio mem length (" + to_string(length) + "): "); + bio_certificate = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); + if(PEM_write_bio_X509(bio_certificate.get(), certificate.get()) != 1) SSL_ERROR("Could not write new certificate: "); + + const uint8_t* mem_ptr{nullptr}; + size_t length{0}; + if(!BIO_mem_contents(&*bio_private_key, &mem_ptr, &length)) SSL_ERROR("Failed to get mem contents: "); + if(!mem_ptr || length < 0) SSL_ERROR("Could not get cert bio mem pointer/invalid length: "); rawCert.reserve(length); - memcpy((void*) rawCert.data(), ptr, length); - */ + memcpy(rawCert.data(), mem_ptr, length); } } return result; } -std::shared_ptr SSLManager::loadSSL(const std::string &rawKey, std::string &error, bool rawData, bool readPublic) { - std::shared_ptr keyBio = nullptr; - std::shared_ptr key = nullptr; - std::shared_ptr result = make_shared(); +std::shared_ptr SSLManager::loadSSL(const std::string &key_data, std::string &error, bool rawData, bool readPublic) { + std::shared_ptr key_bio{nullptr}; + std::shared_ptr key{nullptr}; + auto result = make_shared(); // SSL_CTX_set_ecdh_auto(ctx, 1); if(rawData) { - keyBio = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); - BIO_write(keyBio.get(), rawKey.c_str(), rawKey.length()); + key_bio = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); + BIO_write(key_bio.get(), key_data.c_str(), key_data.length()); } else { - auto keyPath = fs::u8path(rawKey); + auto key_path = fs::u8path(key_data); - if(!fs::exists(keyPath)) { + if(!fs::exists(key_path)) { try { - if(keyPath.has_parent_path()) - fs::create_directories(keyPath.parent_path()); + if(key_path.has_parent_path()) + fs::create_directories(key_path.parent_path()); } catch (fs::filesystem_error& error) { logError("Could not create key directory: " + string(error.what())); } { - std::ofstream { keyPath }; + std::ofstream { key_path }; } } - keyBio = shared_ptr(BIO_new_file(rawKey.c_str(), "r"), ::BIO_free); - if(!keyBio) SSL_ERROR("Could not load key: "); + key_bio = shared_ptr(BIO_new_file(key_data.c_str(), "r"), ::BIO_free); + if(!key_bio) SSL_ERROR("Could not load key: "); } if(readPublic) - key = shared_ptr(PEM_read_bio_PUBKEY(keyBio.get(), nullptr, nullptr, nullptr), ::EVP_PKEY_free); + key = shared_ptr(PEM_read_bio_PUBKEY(key_bio.get(), nullptr, nullptr, nullptr), ::EVP_PKEY_free); else - key = shared_ptr(PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr), ::EVP_PKEY_free); + key = shared_ptr(PEM_read_bio_PrivateKey(key_bio.get(), nullptr, nullptr, nullptr), ::EVP_PKEY_free); result->contains_private = !readPublic; if(!key) { if(readPublic) { SSL_ERROR("Could not read key!"); - } else return this->loadSSL(rawKey, error, rawData, true); + } else return this->loadSSL(key_data, error, rawData, true); } result->key = key; @@ -352,15 +419,16 @@ std::shared_ptr SSLManager::web_ssl_options() { this->_web_options->context_method = TLS_method(); this->_web_options->free_unused_keypairs = false; /* we dont want our keys get removed */ - string web_prefix = "web_"; + + lock_guard ctx_lock{this->context_lock}; for(auto& context : this->contexts) { auto name = context.first; - if(name.length() < web_prefix.length()) + if(name.length() < this->web_ctx_prefix.length()) continue; - if(name.substr(0, web_prefix.length()) != web_prefix) + if(name.substr(0, this->web_ctx_prefix.length()) != this->web_ctx_prefix) continue; - auto servername = context.first.substr(web_prefix.length()); + auto servername = context.first.substr(this->web_ctx_prefix.length()); if(servername == "default") { this->_web_options->default_keypair({context.second->privateKey, context.second->certificate}); } else { diff --git a/src/ssl/SSLManager.h b/src/ssl/SSLManager.h index 5fa83fb..1efdf7c 100644 --- a/src/ssl/SSLManager.h +++ b/src/ssl/SSLManager.h @@ -6,12 +6,11 @@ #include #include -namespace ts { - namespace ssl { +namespace ts::ssl { struct SSLContext { - std::shared_ptr context = nullptr; - std::shared_ptr privateKey = nullptr; - std::shared_ptr certificate = nullptr; + std::shared_ptr context{nullptr}; + std::shared_ptr privateKey{nullptr}; + std::shared_ptr certificate{nullptr}; }; struct SSLGenerator { @@ -35,6 +34,10 @@ namespace ts { bool initialize(); void printDetails(); + bool unregister_context(const std::string& /* key */); + bool rename_context(const std::string& /* old key */, const std::string& /* new key */); /* if new already exists it will be dropped */ + void unregister_web_contexts(); + std::shared_ptr initializeSSLKey(const std::string &key, const std::string &rsaKey, std::string &error, bool raw = false); std::shared_ptr initializeContext(const std::string& key, std::string& privateKey, std::string& certificate, std::string& error, bool raw = false, const std::shared_ptr& = nullptr); @@ -48,15 +51,16 @@ namespace ts { std::shared_ptr getQueryContext() { return this->getContext("query"); } private: + std::mutex context_lock{}; std::map> contexts; std::map> rsa; - std::mutex _web_options_lock; - bool _web_disabled = false; + const std::string web_ctx_prefix{"web_"}; + std::mutex _web_options_lock{}; + bool _web_disabled{false}; std::shared_ptr _web_options; std::shared_ptr loadContext(std::string& rawKey, std::string& rawCert, std::string& error, bool rawData = false, const std::shared_ptr& = nullptr); - std::shared_ptr loadSSL(const std::string &key, std::string &error, bool rawData = false, bool readPublic = false); + std::shared_ptr loadSSL(const std::string &key_data, std::string &error, bool rawData = false, bool readPublic = false); }; - } -} \ No newline at end of file + } \ No newline at end of file