diff --git a/git-teaspeak b/git-teaspeak index 2c92822..6b6e877 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit 2c928229b1aab0306a02d2b7820fd93f3a3623b9 +Subproject commit 6b6e8774b8cb37158d6ee09658559e7dff4dd1c3 diff --git a/license/LicenseClientMain.cpp b/license/LicenseClientMain.cpp index 28d3dd6..9ac3914 100644 --- a/license/LicenseClientMain.cpp +++ b/license/LicenseClientMain.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using namespace std; using namespace std::chrono; @@ -23,12 +24,43 @@ using namespace license; inline bool isValid() { return (end.time_since_epoch().count() == 0 || std::chrono::system_clock::now() < this->end); } }; */ + +std::array intermediate_key{0x02, 0x00, 0x2d, 0x6b, 0xe3, 0x4d, 0x3c, 0xbb, 0x19, 0x1e, 0x46, 0x25, 0x72, 0x22, 0xa3, 0x53, 0x6d, 0x2d, 0xc3, 0xd1, 0x2c, 0xc8, 0xea, 0xf2, 0xf8, 0xe5, 0xd5, 0x0f, 0x6c, 0x7f, 0xeb, 0x63, 0x8a, 0xc4, 0x37, 0xb2, 0x33, 0x5e, 0x06, 0x31, 0xcf, 0x2c, 0xc6, 0xbe, 0x5e, 0x9c, 0xf1, 0xe6, 0xb3, 0xc3, 0x69, 0xd7, 0xf0, 0x05, 0x90, 0x2f, 0x65, 0x03, 0x60, 0x12, 0xa0, 0x20, 0x83, 0xe2, 0x6b, 0x4f, 0x46, 0xab, 0xfd, 0xa6, 0x22, 0x0b, 0x11, 0x9a, 0x34, 0xfe, 0x7b, 0xa3, 0x1b, 0x12, 0xce, 0x30, 0xf7, 0x0a, 0x32, 0x26, 0x23, 0x10, 0xfe, 0x58, 0xb6, 0xc7, 0x5d, 0x3d, 0xc6, 0x14, 0xdc, 0x10, 0xa0, 0xc0, 0x78, 0x10, 0x45, 0x41, 0x36, 0x1a, 0x1c, 0x9f, 0x60, 0x25, 0xd9, 0x69, 0xc9, 0x0b, 0xb7, 0xb4, 0x64, 0xab, 0x6c, 0x22, 0xff, 0xaf, 0x86, 0x26, 0x94, 0xcc, 0x7f, 0x59, 0x52, 0xa3, 0x56, 0x7f, 0x3e, 0x86, 0x04, 0x4c, 0xe0, 0x0e, 0xb3, 0xb1, 0x23, 0x51, 0xf7, 0xf0, 0x14, 0x5d, 0xfd, 0x48, 0xfb, 0x16, 0xe6, 0xc6, 0xca, 0xf2, 0x8d, 0xc8, 0xce, 0xf1, 0x2b, 0x12, 0x9e, 0xd1, 0x7a, 0x80, 0x3a, 0x9b, 0x46, 0xe7, 0xca, 0x34, 0x04, 0xae, 0x3d, 0x12, 0xcd, 0x4a, 0xc6, 0xe1, 0xf1, 0xe4, 0xd8, 0xca, 0x68, 0x36, 0xd3, 0x94, 0x0d, 0xef, 0x93, 0x86, 0xb9, 0x3a, 0xa4, 0x10, 0x52}; int main(int ac, char** av){ auto state = evthread_use_pthreads(); assert(state == 0); string error; + uint8_t errc = 0; +#if 0 + std::array prv_key{}; + auto license = v2::License::create({}, prv_key); + assert(license->private_data_editable()); + license->push_entry(system_clock::now(), system_clock::now() + hours{24 * 365 * 1000}, "TeaSpeak (Test)"); + assert(license->write_private_data({0})); + error = license->write(errc); + assert(errc == 0); + + cout << "Intermediate: " << hex; + for(size_t index = 0; index < error.length(); index++) { + uint8_t e = error[index]; + cout << " 0x" << (e <= 0xF ? "0" : "") << (uint32_t) e << ","; + } + cout << endl; + cout << "Intermediate: " << base64::encode(error) << endl; + +#endif + auto license = v2::License::read(intermediate_key.data(), intermediate_key.size(), errc); + + license->push_entry(system_clock::now(), system_clock::now() + hours{24 * 365 * 1000}, "TeaSpeak Official server", "contact@teaspeak.de"); + assert(license->write_private_data({0})); + error = license->write(errc); + assert(errc == 0); + __asm__("nop"); + cout << "Errc: " << errc << endl; + cout << "Write: " << base64::encode(error) << endl; +#if 0 std::array private_key, public_key; std::random_device rd; @@ -51,6 +83,7 @@ int main(int ac, char** av){ cout << endl; return true; +#endif #if 0 srand(system_clock::now().time_since_epoch().count()); diff --git a/license/shared/License.cpp b/license/shared/License.cpp index 9419371..44ac1a6 100644 --- a/license/shared/License.cpp +++ b/license/shared/License.cpp @@ -119,7 +119,6 @@ namespace license::v2 { } std::shared_ptr License::create(const std::vector> &hierarchy, const std::array &prv_key) { - assert(!hierarchy.empty()); auto result = shared_ptr(new License{}); result->_version = 2; @@ -175,20 +174,24 @@ namespace license::v2 { /* "decode" the data */ { - auto index = 0; - while(index + 4 < decoded_buffer_length) { - auto& memory = *(uint32_t*) (&*decoded_buffer + index); + auto index = sizeof(header); + auto index_decoded = 0; + while(index + 4 < length) { + auto& memory = *(uint32_t*) (&*decoded_buffer + index_decoded); memory = *(uint32_t*) (buffer + index); memory ^= (uint32_t) crypt_key_gen(); index += 4; + index_decoded += 4; } - while(index < decoded_buffer_length) { - auto& memory = *(uint8_t*) (&*decoded_buffer + index); + while(index < length) { + auto& memory = *(uint8_t*) (&*decoded_buffer + index_decoded); memory = *(uint8_t*) (buffer + index); memory ^= (uint8_t) crypt_key_gen(); index++; + index_decoded++; } } + memcpy(&body_header, &*decoded_buffer, sizeof(body_header)); if(decoded_buffer_length < sizeof(body_header) + body_header.length_hierarchy + body_header.length_private_data) { error = 2; /* buffer too small */ @@ -243,7 +246,7 @@ namespace license::v2 { return result; } - std::string License::write(int &error) { + std::string License::write(uint8_t &error) { if(!this->private_data || !this->private_buffer_length) { error = 2; /* missing private data */ return ""; @@ -278,8 +281,8 @@ namespace license::v2 { error = 3; /* failed to write hierarchy */ return ""; } - body_header.length_hierarchy = offset - begin_offset; + body_header.length_hierarchy = offset - begin_offset; digest::sha1((char*) &*buffer + begin_offset, body_header.length_hierarchy, body_header.checksum_hierarchy); } @@ -335,6 +338,8 @@ namespace license::v2 { } bool License::write_private_data(const LicensePrivateWriteOptions& write_options) { + if(this->_hierarchy.empty()) return false; + uint8_t private_key[64]; //ed25519_sign requires 64 bytes (may it expects a public key in front?) if(!this->private_data->calculate_private_key(private_key, this->_hierarchy.size() - 1)) return false; @@ -409,7 +414,7 @@ namespace license::v2 { std::array result{}; ge_p3_tobytes((uint8_t*) result.data(), &parent_key); - return {}; + return result; } bool License::push_entry(const std::shared_ptr &entry, size_t* index) { @@ -542,7 +547,7 @@ namespace license::v2 { bool LicensePrivate::write(uint8_t *buffer, size_t &offset, size_t length, const LicensePrivateWriteOptions& options) { if(options.precalculated_key_index < -1) { if(buffer) { - if((offset + 2) < length) return false; + if((offset + 2) > length) return false; *(buffer + offset++) = 0; /* no precalculated private key */ *(buffer + offset++) = 0; /* no raw private keys */ } else { @@ -553,23 +558,24 @@ namespace license::v2 { if(index < 0) return false; /* we will NEVER write the root key */ if(buffer) { - if((offset + 2 + 32) < length) return false; + if((offset + 2 + 32) > length) return false; *(buffer + offset++) = 1; { *(buffer + offset++) = index; if(!this->calculate_private_key(buffer + offset, index)) return false; + offset += 32; } - if((offset + 1) < length) return false; + if((offset + 1) > length) return false; auto& private_key_count = *(buffer + offset++); private_key_count = 0; for(auto& [key_index, key] : this->private_keys) { if(key_index <= index) continue; - if((offset + 1 + key.size()) < length) return false; + if((offset + 1 + key.size()) > length) return false; *(buffer + offset++) = key_index; memcpy(buffer + offset, key.data(), key.size()); @@ -592,12 +598,12 @@ namespace license::v2 { } if(buffer) { - if((offset + 4) < length) return false; + if((offset + 4) > length) return false; *(uint32_t*) (buffer + offset) = this->meta_data.size(); offset += 4; for(auto& [key, value] : this->meta_data) { - if((offset + 3 + key.length() + value.length()) < length) return false; + if((offset + 3 + key.length() + value.length()) > length) return false; *(buffer + offset++) = key.length(); *(uint16_t*)(buffer + offset) = value.length(); @@ -625,7 +631,7 @@ namespace license::v2 { auto hierarchy = handle->hierarchy(); auto base_index = this->precalculated_private_key_index; - if(base_index >= hierarchy.size()) return false; + if(base_index >= (int64_t) hierarchy.size()) return false; if(base_index < -1) return true; /* means we don't have a private key */ while(base_index < hierarchy.size()) { @@ -635,12 +641,12 @@ namespace license::v2 { return true; } - bool LicensePrivate::private_key_calculable(uint8_t index) const { + bool LicensePrivate::private_key_calculable(int index) const { auto handle = this->_handle.lock(); if(!handle) return false; auto hierarchy = handle->hierarchy(); - if(index >= hierarchy.size()) return false; + if(index >= (int64_t) hierarchy.size()) return false; auto base_index = this->precalculated_private_key_index; @@ -726,6 +732,9 @@ namespace license::v2 { if(!result->allocate_read_body(body_length)) return nullptr; if(body_length > 0) { + result->allocate_read_body(body_length); + if(!result->read_body) return nullptr; + result->read_body_length = body_length; memcpy(result->read_body, buffer + offset, body_length); offset += body_length; } @@ -779,11 +788,11 @@ namespace license::v2 { return true; } - size_t length = 42 + this->read_body_length, offset = 0; - auto buffer = (uint8_t*) malloc(length); + size_t length = 43 + this->read_body_length, offset = 0; + auto buffer = unique_ptr((uint8_t*) malloc(length), ::free); if(!buffer) return false; - if(this->write(buffer, offset, length)) { - digest::sha512((char*) buffer, length, this->_hash.data()); + if(this->write(&*buffer, offset, length)) { + digest::sha512((char*) &*buffer, length, this->_hash.data()); this->_hash_set = true; } @@ -800,14 +809,53 @@ namespace license::v2 { std::shared_ptr Intermediate::create(const uint8_t *pub_key, const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point & end, const std::string &description) { assert(description.size() < 256); - auto result = shared_ptr(new HierarchyEntry{hierarchy::Intermediate::type, pub_key, begin, end}); + auto buffer_length = description.size() + 1; - if(!result || !result->allocate_read_body(description.size() + 1)) return nullptr; - memcpy(result->read_body + 1, description.data(), description.length()); - *result->read_body = (uint8_t) description.length(); + uint8_t* buffer; + auto result = BodyInterpreter::_create(pub_key, begin, end, buffer_length, buffer); + if(!result) return nullptr; + + memcpy(buffer + 1, description.data(), description.length()); + *buffer = (uint8_t) description.length(); return result; } + + bool Server::has_username() { + return *(this->_memory + 1) > 0; + } + + std::string_view Server::contact_email() { + return {(const char*) this->_memory + 2, *this->_memory}; + } + + std::string_view Server::username() { + return {(const char*) this->_memory + 2 + *this->_memory, *(this->_memory + 1)}; + } + + std::shared_ptr Server::create(const uint8_t *pub_key, const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point & end, const std::string &email, const std::optional &username) { + assert(email.size() < 256); + assert(!username.has_value() || username->size() < 256); + auto buffer_length = 2 + email.size() + (username.has_value() ? username->length() : 0); + + uint8_t* buffer; + auto result = BodyInterpreter::_create(pub_key, begin, end, buffer_length, buffer); + if(!result) return nullptr; + + memcpy(buffer + 2, email.data(), email.length()); + *buffer = (uint8_t) email.length(); + + if(username.has_value()) + memcpy(buffer + 2 + email.length(), username->data(), username->length()); + *(buffer + 1) = (uint8_t) (username.has_value() ? username->length() : 0); + + return result; + } + + std::shared_ptr Ephemeral::create(const uint8_t *pub_key, const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point & end) { + uint8_t* buffer; + return BodyInterpreter::_create(pub_key, begin, end, 0, buffer); + } } static int test() { diff --git a/license/shared/License.h b/license/shared/License.h index 1532d1b..2c545bc 100644 --- a/license/shared/License.h +++ b/license/shared/License.h @@ -84,7 +84,7 @@ namespace license { std::array generate_public_key(const uint8_t* /* public key root */, int /* length */ = -1) const; - std::string write(int& /* error */); + std::string write(uint8_t& /* error */); bool private_data_editable() const; bool write_private_data(const LicensePrivateWriteOptions& /* write options */); @@ -128,7 +128,7 @@ namespace license { void register_raw_private_key(uint8_t /* index */, const uint8_t* /* key */); bool has_raw_private_key(uint8_t /* index */) const; - bool private_key_calculable(uint8_t /* index */) const; + bool private_key_calculable(int /* index */) const; bool calculate_private_key(uint8_t* /* response */, uint8_t /* index */) const; private: std::weak_ptr _handle; @@ -141,11 +141,11 @@ namespace license { }; namespace hierarchy { - struct Intermediate; + struct BodyInterpreter; } struct HierarchyEntry { - friend struct hierarchy::Intermediate; + friend struct hierarchy::BodyInterpreter; public: ~HierarchyEntry(); @@ -172,12 +172,12 @@ namespace license { template inline I interpret_as() const { - assert(this->interpret_as()); + assert(this->interpretable_as()); return I{this->read_body, this->read_body_length}; } template - inline bool interpretable_as() const { return I::type == this->_entry_type; } + inline bool interpretable_as() const { return I::_type == this->_entry_type; } inline bool hash(uint8_t* /* hash result [64] */) const; protected: @@ -207,20 +207,54 @@ namespace license { }; namespace hierarchy { + struct type { + enum value : uint8_t { + Intermediate = 1, + Server = 2, + Ephemeral = 8 + }; + + static constexpr const char* name(const value& value) { + switch (value) { + case Intermediate: + return "Intermediate"; + case Server: + return "Server"; + case Ephemeral: + return "Ephemeral"; + + default: + return "Unknown"; + } + } + }; struct BodyInterpreter { public: BodyInterpreter() = delete; protected: + template + static std::shared_ptr _create( + const uint8_t *pub_key, + const std::chrono::system_clock::time_point &begin, + const std::chrono::system_clock::time_point &end, + size_t buffer_size, uint8_t*& buffer_ptr + ) { + auto result = std::shared_ptr(new HierarchyEntry{T::_type, pub_key, begin, end}); + if(!result || !result->allocate_read_body(buffer_size)) return nullptr; + result->read_body_length = buffer_size; + buffer_ptr = result->read_body; + return result; + } + BodyInterpreter(const uint8_t* memory, size_t length) { this->_memory = memory; this->_length = length; } const uint8_t* _memory = nullptr; size_t _length = 0; }; struct Intermediate : public BodyInterpreter { - friend struct HierarchyEntry; public: - static constexpr uint8_t type = 1; + static constexpr uint8_t _type = type::Intermediate; static std::shared_ptr create( const uint8_t* /* public key */, const std::chrono::system_clock::time_point& /* begin */, @@ -232,6 +266,38 @@ namespace license { private: Intermediate(const uint8_t* memory, size_t length) : BodyInterpreter(memory, length) {} }; + + struct Server : public BodyInterpreter { + public: + static constexpr uint8_t _type = type::Server; + static std::shared_ptr create( + const uint8_t* /* public key */, + const std::chrono::system_clock::time_point& /* begin */, + const std::chrono::system_clock::time_point& /* end */, + + const std::string& /* email */, + const std::optional& /* value */ + ); + + std::string_view contact_email(); + + bool has_username(); + std::string_view username(); + private: + Server(const uint8_t* memory, size_t length) : BodyInterpreter(memory, length) {} + }; + + struct Ephemeral : public BodyInterpreter { + public: + static constexpr uint8_t _type = type::Ephemeral; + static std::shared_ptr create( + const uint8_t* /* public key */, + const std::chrono::system_clock::time_point& /* begin */, + const std::chrono::system_clock::time_point& /* end */ + ); + private: + Ephemeral(const uint8_t* memory, size_t length) : BodyInterpreter(memory, length) {} + }; } } diff --git a/server/src/client/ConnectedClientNotifyHandler.cpp b/server/src/client/ConnectedClientNotifyHandler.cpp index 9c69962..4f913b6 100644 --- a/server/src/client/ConnectedClientNotifyHandler.cpp +++ b/server/src/client/ConnectedClientNotifyHandler.cpp @@ -160,7 +160,7 @@ bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_ bool ConnectedClient::notifyChannelGroupList() { Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelgrouplist" : ""); int index = 0; - for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(false)) { + for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(true)) { if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) { cmd[index]["cgid"] = group->groupId(); } else { diff --git a/server/src/server/VoiceIOManager.cpp b/server/src/server/VoiceIOManager.cpp index 315f726..3f23d08 100644 --- a/server/src/server/VoiceIOManager.cpp +++ b/server/src/server/VoiceIOManager.cpp @@ -106,6 +106,19 @@ void VoiceIOManager::adjustExecutors(size_t size) { } } +IOEventLoop::~IOEventLoop() { + if(this->executor.joinable()) { + auto handle = this->executor.native_handle(); + timespec timeout{}; + clock_gettime(CLOCK_REALTIME, &timeout); + timeout.tv_sec += 5; + auto join_result = pthread_timedjoin_np(handle, nullptr, &timeout); + if(join_result == EBUSY) + logError(LOG_INSTANCE, "Failed to shutdown IO event loop. Error: {}", join_result); + } + assert(!this->executor.joinable()); +} + /** * @warning executor lock must be locked! */ diff --git a/server/src/server/VoiceIOManager.h b/server/src/server/VoiceIOManager.h index 4a99d8b..de36ddf 100644 --- a/server/src/server/VoiceIOManager.h +++ b/server/src/server/VoiceIOManager.h @@ -53,16 +53,7 @@ namespace ts { struct IOEventLoop { IOEventLoop() = default; - ~IOEventLoop() { - if(this->executor.joinable()) { - auto handle = this->executor.native_handle(); - timespec timeout{}; - clock_gettime(CLOCK_REALTIME, &timeout); - timeout.tv_sec += 5; - auto join_result = pthread_timedjoin_np(handle, nullptr, &timeout); - } - assert(!this->executor.joinable()); - } + ~IOEventLoop(); int bound_thread = -1; /* -1 represents that this loop is bound to no thread at all */ diff --git a/shared b/shared index cb73d9d..e30c030 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit cb73d9df3258eaf59e4799c9e54d5f865e891734 +Subproject commit e30c03029fb2be1a84a2706cf9bdd4f28cce9051