From 9b08106112fcad9e2d4d4153d8278f6e84ec062c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20ZHOU?= Date: Thu, 7 Mar 2024 10:16:24 +0100 Subject: [PATCH] WAB-9186: Support multiple RDS (using CAL Per Device). --- src/acl/file_system_license_store.hpp | 103 ++++++++++++++---- src/acl/license_api.hpp | 42 +++++-- src/acl/module_manager/create_module_rdp.cpp | 1 + src/core/RDP/lic.hpp | 2 + src/mod/rdp/rdp_negociation.cpp | 88 ++++++++++----- src/mod/rdp/rdp_negociation.hpp | 10 +- src/mod/rdp/rdp_negociation_data.cpp | 3 +- src/mod/rdp/rdp_negociation_data.hpp | 5 +- src/mod/rdp/rdp_params.hpp | 4 + tests/acl/test_license_api.cpp | 98 +++++++++++++++-- .../fixtures/test_license_api_license.hpp | 6 + 11 files changed, 290 insertions(+), 72 deletions(-) diff --git a/src/acl/file_system_license_store.hpp b/src/acl/file_system_license_store.hpp index a7ef6d7e7b..e894147939 100644 --- a/src/acl/file_system_license_store.hpp +++ b/src/acl/file_system_license_store.hpp @@ -39,13 +39,61 @@ class FileSystemLicenseStore : public LicenseApi {} // The functions shall return empty bytes_view to indicate the error. - bytes_view get_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, writable_bytes_view out, bool enable_log) override + bytes_view get_license_v1(char const* client_name, char const* target_ip, uint32_t version, char const* scope, char const* company_name, char const* product_id, std::array& hwid, writable_bytes_view out, bool enable_log) override + { + char license_index[2048] = {}; + ::snprintf(license_index, sizeof(license_index) - 1, "%s_0x%08X_%s_%s_%s", target_ip, version, scope, company_name, product_id); + license_index[sizeof(license_index) - 1] = '\0'; + std::replace(std::begin(license_index), std::end(license_index), ' ', '-'); + LOG_IF(enable_log, LOG_INFO, "FileSystemLicenseStore::get_license_v1(): LicenseIndex=\"%s\"", license_index); + + char filename[4096] = {}; + ::snprintf(filename, sizeof(filename) - 1, "%s/%s/%s", + license_path.c_str(), client_name, license_index); + filename[sizeof(filename) - 1] = '\0'; + + if (unique_fd ufd{::open(filename, O_RDONLY)}) { + size_t number_of_bytes_read = ::read(ufd.fd(), hwid.data(), hwid.size()); + if (number_of_bytes_read != sizeof(hwid)) { + LOG(LOG_ERR, "FileSystemLicenseStore::get_license_v1: license file truncated (1) : expected %zu, got %zu", sizeof(hwid), number_of_bytes_read); + } + else { + uint32_t license_size = 0; + number_of_bytes_read = ::read(ufd.fd(), &license_size, sizeof(license_size)); + if (number_of_bytes_read != sizeof(license_size)) { + LOG(LOG_ERR, "FileSystemLicenseStore::get_license_v1: license file truncated (2) : expected %zu, got %zu", sizeof(license_size), number_of_bytes_read); + } + else { + if (out.size() >= license_size) + { + number_of_bytes_read = ::read(ufd.fd(), out.data(), license_size); + if (number_of_bytes_read != license_size) { + LOG(LOG_ERR, "FileSystemLicenseStore::get_license_v1: license file truncated (3) : expected %u, got %zu", license_size, number_of_bytes_read); + } + else { + LOG(LOG_INFO, "FileSystemLicenseStore::get_license_v1: LicenseSize=%u", license_size); + + return bytes_view { out.data(), license_size }; + } + } + } + } + } + else { + LOG(LOG_WARNING, "FileSystemLicenseStore::get_license_v1: Failed to open license file! Path=\"%s\" errno=%s(%d)", filename, strerror(errno), errno); + } + + return bytes_view { out.data(), 0 }; + } + + // The functions shall return empty bytes_view to indicate the error. + bytes_view get_license_v0(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, writable_bytes_view out, bool enable_log) override { char license_index[2048] = {}; ::snprintf(license_index, sizeof(license_index) - 1, "0x%08X_%s_%s_%s", version, scope, company_name, product_id); license_index[sizeof(license_index) - 1] = '\0'; std::replace(std::begin(license_index), std::end(license_index), ' ', '-'); - LOG_IF(enable_log, LOG_INFO, "FileSystemLicenseStore::get_license(): LicenseIndex=\"%s\"", license_index); + LOG_IF(enable_log, LOG_INFO, "FileSystemLicenseStore::get_license_v0(): LicenseIndex=\"%s\"", license_index); char filename[4096] = {}; ::snprintf(filename, sizeof(filename) - 1, "%s/%s/%s", @@ -56,17 +104,17 @@ class FileSystemLicenseStore : public LicenseApi uint32_t license_size = 0; size_t number_of_bytes_read = ::read(ufd.fd(), &license_size, sizeof(license_size)); if (number_of_bytes_read != sizeof(license_size)) { - LOG(LOG_ERR, "FileSystemLicenseStore::get_license: license file truncated (1) : expected %zu, got %zu", sizeof(license_size), number_of_bytes_read); + LOG(LOG_ERR, "FileSystemLicenseStore::get_license_v0: license file truncated (1) : expected %zu, got %zu", sizeof(license_size), number_of_bytes_read); } else { if (out.size() >= license_size) { number_of_bytes_read = ::read(ufd.fd(), out.data(), license_size); if (number_of_bytes_read != license_size) { - LOG(LOG_ERR, "FileSystemLicenseStore::get_license: license file truncated (2) : expected %u, got %zu", license_size, number_of_bytes_read); + LOG(LOG_ERR, "FileSystemLicenseStore::get_license_v0: license file truncated (2) : expected %u, got %zu", license_size, number_of_bytes_read); } else { - LOG(LOG_INFO, "FileSystemLicenseStore::get_license: LicenseSize=%u", license_size); + LOG(LOG_INFO, "FileSystemLicenseStore::get_license_v0: LicenseSize=%u", license_size); return bytes_view { out.data(), license_size }; } @@ -74,16 +122,16 @@ class FileSystemLicenseStore : public LicenseApi } } else { - LOG(LOG_WARNING, "FileSystemLicenseStore::get_license: Failed to open license file! Path=\"%s\" errno=%s(%d)", filename, strerror(errno), errno); + LOG(LOG_WARNING, "FileSystemLicenseStore::get_license_v0: Failed to open license file! Path=\"%s\" errno=%s(%d)", filename, strerror(errno), errno); } return bytes_view { out.data(), 0 }; } - bool put_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, bytes_view in, bool enable_log) override + bool put_license(char const* client_name, char const* target_ip, uint32_t version, char const* scope, char const* company_name, char const* product_id, std::array const& hwid, bytes_view in, bool enable_log) override { char license_index[2048] = {}; - ::snprintf(license_index, sizeof(license_index) - 1, "0x%08X_%s_%s_%s", version, scope, company_name, product_id); + ::snprintf(license_index, sizeof(license_index) - 1, "%s_0x%08X_%s_%s_%s", target_ip, version, scope, company_name, product_id); license_index[sizeof(license_index) - 1] = '\0'; std::replace_if(std::begin(license_index), std::end(license_index), [](unsigned char c) { return (' ' == c); }, '-'); @@ -112,31 +160,38 @@ class FileSystemLicenseStore : public LicenseApi if (fd != -1) { unique_fd ufd{fd}; uint32_t const license_size = in.size(); - if (sizeof(license_size) == ::write(ufd.fd(), &license_size, sizeof(license_size))) { - if (license_size == ::write(ufd.fd(), in.data(), in.size())) { - char filename[6145] = {}; - ::snprintf(filename, sizeof(filename) - 1, "%s/%s", license_dir_path, license_index); - filename[sizeof(filename) - 1] = '\0'; - - if (::rename(filename_temporary, filename) == 0) { - return true; + if (hwid.size() == ::write(ufd.fd(), hwid.data(), hwid.size())) { + if (sizeof(license_size) == ::write(ufd.fd(), &license_size, sizeof(license_size))) { + if (license_size == ::write(ufd.fd(), in.data(), in.size())) { + char filename[6145] = {}; + ::snprintf(filename, sizeof(filename) - 1, "%s/%s", license_dir_path, license_index); + filename[sizeof(filename) - 1] = '\0'; + + if (::rename(filename_temporary, filename) == 0) { + return true; + } + + LOG( LOG_ERR + , "FileSystemLicenseStore::put_license: failed to rename the (temporary) license file! " + "temporary_filename=\"%s\" filename=\"%s\" errno=%s(%d)" + , filename_temporary, filename, strerror(errno), errno); + ::unlink(filename_temporary); + } + else { + LOG( LOG_ERR + , "FileSystemLicenseStore::put_license: Failed to write (temporary) license file (1)! filename=\"%s\" errno=%s(%d)" + , filename_temporary, strerror(errno), errno); } - - LOG( LOG_ERR - , "FileSystemLicenseStore::put_license: failed to rename the (temporary) license file! " - "temporary_filename=\"%s\" filename=\"%s\" errno=%s(%d)" - , filename_temporary, filename, strerror(errno), errno); - ::unlink(filename_temporary); } else { LOG( LOG_ERR - , "FileSystemLicenseStore::put_license: Failed to write (temporary) license file (1)! filename=\"%s\" errno=%s(%d)" + , "FileSystemLicenseStore::put_license: Failed to write (temporary) license file (2)! filename=\"%s\" errno=%s(%d)" , filename_temporary, strerror(errno), errno); } } else { LOG( LOG_ERR - , "FileSystemLicenseStore::put_license: Failed to write (temporary) license file (2)! filename=\"%s\" errno=%s(%d)" + , "FileSystemLicenseStore::put_license: Failed to write (temporary) license file (3)! filename=\"%s\" errno=%s(%d)" , filename_temporary, strerror(errno), errno); } } diff --git a/src/acl/license_api.hpp b/src/acl/license_api.hpp index 1d240a9e20..6ccdff1039 100644 --- a/src/acl/license_api.hpp +++ b/src/acl/license_api.hpp @@ -20,6 +20,7 @@ #pragma once +#include "core/RDP/lic.hpp" #include "utils/sugar/bytes_view.hpp" #include "utils/sugar/noncopyable.hpp" @@ -28,17 +29,40 @@ struct LicenseApi : noncopyable virtual ~LicenseApi() = default; // The functions shall return empty bytes_view to indicate the error. - virtual bytes_view get_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, - char const* product_id, writable_bytes_view out, bool enable_log) = 0; + virtual bytes_view get_license_v1(char const* client_name, char const* target_ip, uint32_t version, char const* scope, + char const* company_name, char const* product_id, std::array& hwid, writable_bytes_view out, + bool enable_log) = 0; - virtual bool put_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, - char const* product_id, bytes_view in, bool enable_log) = 0; + // The functions shall return empty bytes_view to indicate the error. + virtual bytes_view get_license_v0(char const* client_name, uint32_t version, char const* scope, + char const* company_name, char const* product_id, writable_bytes_view out, + bool enable_log) = 0; + + virtual bool put_license(char const* client_name, char const* target_ip, uint32_t version, char const* scope, char const* company_name, + char const* product_id, std::array const& hwid, bytes_view in, bool enable_log) = 0; }; struct NullLicenseStore : LicenseApi { - bytes_view get_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, - char const* product_id, writable_bytes_view out, bool enable_log) override + bytes_view get_license_v1(char const* client_name, char const* target_ip, uint32_t version, char const* scope, + char const* company_name, char const* product_id, std::array& hwid, writable_bytes_view out, + bool enable_log) override + { + (void)client_name; + (void)target_ip; + (void)version; + (void)scope; + (void)company_name; + (void)product_id; + (void)hwid; + (void)enable_log; + + return bytes_view(out.data(), 0); + } + + bytes_view get_license_v0(char const* client_name, uint32_t version, char const* scope, + char const* company_name, char const* product_id, writable_bytes_view out, + bool enable_log) override { (void)client_name; (void)version; @@ -50,14 +74,16 @@ struct NullLicenseStore : LicenseApi return bytes_view(out.data(), 0); } - bool put_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, - char const* product_id, bytes_view in, bool enable_log) override + bool put_license(char const* client_name, char const* target_ip, uint32_t version, char const* scope, char const* company_name, + char const* product_id, std::array const& hwid, bytes_view in, bool enable_log) override { (void)client_name; + (void)target_ip; (void)version; (void)scope; (void)company_name; (void)product_id; + (void)hwid; (void)in; (void)enable_log; diff --git a/src/acl/module_manager/create_module_rdp.cpp b/src/acl/module_manager/create_module_rdp.cpp index 53f20680a7..2fec50d7f8 100644 --- a/src/acl/module_manager/create_module_rdp.cpp +++ b/src/acl/module_manager/create_module_rdp.cpp @@ -676,6 +676,7 @@ ModPack create_mod_rdp( mod_rdp_params.replace_null_pointer_by_default_pointer = ini.get(); mod_rdp_params.large_pointer_support = ini.get(); mod_rdp_params.load_balance_info = ini.get().c_str(); + mod_rdp_params.target_ip = ini.get().c_str(); // ======================= File System Params =================== { diff --git a/src/core/RDP/lic.hpp b/src/core/RDP/lic.hpp index 2fe4fccf8a..d86893a71b 100644 --- a/src/core/RDP/lic.hpp +++ b/src/core/RDP/lic.hpp @@ -39,6 +39,8 @@ #include "core/error.hpp" #include "system/ssl_rc4.hpp" +#include + // Sent by server: // 0x01 LICENSE_REQUEST Indicates a License Request PDU ([MS-RDPELE] section 2.2.2.1). diff --git a/src/mod/rdp/rdp_negociation.cpp b/src/mod/rdp/rdp_negociation.cpp index 417b1092c7..200f96b63d 100644 --- a/src/mod/rdp/rdp_negociation.cpp +++ b/src/mod/rdp/rdp_negociation.cpp @@ -26,7 +26,6 @@ #include "core/log_id.hpp" #include "core/app_path.hpp" #include "core/RDP/autoreconnect.hpp" -#include "core/RDP/lic.hpp" #include "core/RDP/tpdu_buffer.hpp" #include "core/RDP/gcc/userdata/mcs_channels.hpp" #include "core/RDP/gcc/userdata/sc_core.hpp" @@ -268,9 +267,10 @@ RdpNegociation::RdpNegociation( , has_managed_drive(has_managed_drive) , convert_remoteapp_to_desktop(convert_remoteapp_to_desktop) , send_channel_index(0) - , license_client_name(info.hostname) + , real_client_name(info.hostname) , license_store(license_store) , use_license_store(mod_rdp_params.use_license_store) + , target_ip(mod_rdp_params.target_ip) , build_number(info.build) , forward_build_number(mod_rdp_params.forward_client_build_number) { @@ -1292,17 +1292,43 @@ bool RdpNegociation::get_license(InStream & stream, TpduBuffer& buf) ::UTF16toUTF8_buf(SvrLicReq.ProductInfo.ProductId, writable_bytes_view{ProductIdU8, sizeof(ProductIdU8)}); for (uint32_t i = 0; i < SvrLicReq.ScopeList.ScopeCount; ++i) { - bytes_view out = this->license_store.get_license( - this->license_client_name.c_str(), + bytes_view out = this->license_store.get_license_v1( + logon_info.client_name_is_hidden() ? "localhost" : logon_info.hostname().c_str(), + this->target_ip.c_str(), SvrLicReq.ProductInfo.dwVersion, ::char_ptr_cast(SvrLicReq.ScopeList.ScopeArray[i].blobData.data()), ::char_ptr_cast(CompanyNameU8), ::char_ptr_cast(ProductIdU8), + this->hwid, writable_bytes_view(this->lic_layer_license_data, sizeof(lic_layer_license_data)), bool(this->verbose & RDPVerbose::license) ); if (not out.empty()) { + this->has_hwid = true; + } + else + { + out = this->license_store.get_license_v0( + this->real_client_name.c_str(), + SvrLicReq.ProductInfo.dwVersion, + ::char_ptr_cast(SvrLicReq.ScopeList.ScopeArray[i].blobData.data()), + ::char_ptr_cast(CompanyNameU8), + ::char_ptr_cast(ProductIdU8), + writable_bytes_view(this->lic_layer_license_data, sizeof(lic_layer_license_data)), + bool(this->verbose & RDPVerbose::license) + ); + if (not out.empty()) + { + this->get_hwid_by_client_name(this->hwid, this->logon_info.hostname()); + this->has_hwid = true; + } + } + + if (not out.empty()) + { + assert(this->has_hwid); + this->lic_layer_license_size = out.size(); LOG(LOG_INFO, "RdpNegociation: LicenseSize=%zu", this->lic_layer_license_size); @@ -1318,41 +1344,35 @@ bool RdpNegociation::get_license(InStream & stream, TpduBuffer& buf) [this, &username](StreamSize<65535 - 1024>, OutStream & lic_data) { if (this->lic_layer_license_size > 0) { LOG_IF(bool(this->verbose & RDPVerbose::license), LOG_INFO, "RdpNegociation: Client License Info"); - uint8_t hwid[LIC::LICENSE_HWID_SIZE]; - OutStream(hwid).out_uint32_le(2); - - auto const& hostname = this->logon_info.hostname(); - std::size_t const pktlen = LIC::LICENSE_HWID_SIZE - 4; - std::size_t const slen = std::min(pktlen, hostname.size()); - memcpy(hwid + 4, hostname.data(), slen); - memset(hwid + 4 + slen, 0, pktlen - slen); /* Generate a signature for the HWID buffer */ uint8_t signature[LIC::LICENSE_SIGNATURE_SIZE]; uint8_t lenhdr[4]; - OutStream(lenhdr).out_uint32_le(sizeof(hwid)); + OutStream(lenhdr).out_uint32_le(this->hwid.size()); Sign sign(make_array_view(this->lic_layer_license_sign_key)); sign.update(make_array_view(lenhdr)); - sign.update(make_array_view(hwid)); + sign.update(make_array_view(this->hwid)); static_assert(static_cast(SslMd5::DIGEST_LENGTH) == static_cast(LIC::LICENSE_SIGNATURE_SIZE)); sign.final(make_writable_sized_array_view(signature)); /* Now encrypt the HWID */ + uint8_t crypt_hwid[LIC::LICENSE_HWID_SIZE]; + memcpy(crypt_hwid, this->hwid.data(), LIC::LICENSE_HWID_SIZE); SslRC4 rc4; rc4.set_key(make_sized_array_view(this->lic_layer_license_key)); // in, out - rc4.crypt(LIC::LICENSE_HWID_SIZE, hwid, hwid); + rc4.crypt(LIC::LICENSE_HWID_SIZE, crypt_hwid, crypt_hwid); LIC::ClientLicenseInfo_Send( lic_data, this->negociation_result.use_rdp5 ? 3 : 2, this->lic_layer_license_size, this->lic_layer_license_data, - hwid, signature + crypt_hwid, signature ); } else { @@ -1378,7 +1398,6 @@ bool RdpNegociation::get_license(InStream & stream, TpduBuffer& buf) uint8_t out_token[LIC::LICENSE_TOKEN_SIZE]; uint8_t decrypt_token[LIC::LICENSE_TOKEN_SIZE]; - uint8_t hwid[LIC::LICENSE_HWID_SIZE]; uint8_t crypt_hwid[LIC::LICENSE_HWID_SIZE]; uint8_t out_sig[LIC::LICENSE_SIGNATURE_SIZE]; @@ -1390,18 +1409,15 @@ bool RdpNegociation::get_license(InStream & stream, TpduBuffer& buf) // size, in, out rc4_decrypt_token.crypt(LIC::LICENSE_TOKEN_SIZE, decrypt_token, decrypt_token); - /* Generate a signature for a buffer of token and HWID */ - OutStream(hwid).out_uint32_le(2); - - auto const& hostname = this->logon_info.hostname(); - std::size_t const pktlen = LIC::LICENSE_HWID_SIZE - 4; - std::size_t const slen = std::min(pktlen, hostname.size()); - memcpy(hwid + 4, hostname.data(), slen); - memset(hwid + 4 + slen, 0, pktlen - slen); + if (!this->has_hwid) + { + this->get_hwid_by_client_name(this->hwid, this->logon_info.hostname()); + this->has_hwid = true; + } uint8_t sealed_buffer[LIC::LICENSE_TOKEN_SIZE + LIC::LICENSE_HWID_SIZE]; memcpy(sealed_buffer, decrypt_token, LIC::LICENSE_TOKEN_SIZE); - memcpy(sealed_buffer + LIC::LICENSE_TOKEN_SIZE, hwid, LIC::LICENSE_HWID_SIZE); + memcpy(sealed_buffer + LIC::LICENSE_TOKEN_SIZE, this->hwid.data(), this->hwid.size()); uint8_t lenhdr[4]; OutStream(lenhdr).out_uint32_le(sizeof(sealed_buffer)); @@ -1414,7 +1430,7 @@ bool RdpNegociation::get_license(InStream & stream, TpduBuffer& buf) sign.final(make_writable_sized_array_view(out_sig)); /* Now encrypt the HWID */ - memcpy(crypt_hwid, hwid, LIC::LICENSE_HWID_SIZE); + memcpy(crypt_hwid, this->hwid.data(), this->hwid.size()); SslRC4 rc4_hwid; rc4_hwid.set_key(make_sized_array_view(this->lic_layer_license_key)); // size, in, out @@ -1457,11 +1473,13 @@ bool RdpNegociation::get_license(InStream & stream, TpduBuffer& buf) ::UTF16toUTF8_buf(bytes_view(lic.licenseInfo.pbProductId, lic.licenseInfo.cbProductId), writable_bytes_view{ProductIdU8, sizeof(ProductIdU8)}); license_saved = this->license_store.put_license( - this->license_client_name.c_str(), + logon_info.client_name_is_hidden() ? "localhost" : logon_info.hostname().c_str(), + this->target_ip.c_str(), lic.licenseInfo.dwVersion, ::char_ptr_cast(lic.licenseInfo.pbScope), ::char_ptr_cast(CompanyNameU8), ::char_ptr_cast(ProductIdU8), + this->hwid, bytes_view(lic.licenseInfo.pbLicenseInfo, lic.licenseInfo.cbLicenseInfo), bool(this->verbose & RDPVerbose::license) ); @@ -1651,3 +1669,17 @@ RdpNegociationResult const& RdpNegociation::get_result() const noexcept assert(this->state == State::TERMINATED); return this->negociation_result; } + +void RdpNegociation::get_hwid_by_client_name(std::array& hwid_out, + static_string const& client_name) +{ + /* Generate a signature for a buffer of token and HWID */ + OutStream os({hwid_out.data(), hwid_out.size()}); + + os.out_uint32_le(2); + + std::size_t const pktlen = LIC::LICENSE_HWID_SIZE - 4; + std::size_t const slen = std::min(pktlen, client_name.size()); + os.out_copy_bytes(client_name.data(), slen); + os.out_clear_bytes(pktlen - slen); +} \ No newline at end of file diff --git a/src/mod/rdp/rdp_negociation.hpp b/src/mod/rdp/rdp_negociation.hpp index 11c195fb1f..16ed04df8c 100644 --- a/src/mod/rdp/rdp_negociation.hpp +++ b/src/mod/rdp/rdp_negociation.hpp @@ -28,6 +28,7 @@ #include "core/RDP/gcc/userdata/cs_monitor.hpp" #include "core/RDP/gcc/userdata/cs_monitor_ex.hpp" +#include "core/RDP/lic.hpp" #include "core/RDP/logon.hpp" #include "core/RDP/nego.hpp" #include "core/channel_names.hpp" @@ -186,10 +187,12 @@ class RdpNegociation uint8_t client_random[SEC_RANDOM_SIZE] = { 0 }; - std::string license_client_name; + std::string const real_client_name; LicenseApi& license_store; const bool use_license_store; + std::string const target_ip; + /// Client build number. /// Represents the 'clientBuild' field of Client Core Data (TS_UD_CS_CORE) /// as specified in [MS-RDPBCGR] ยง2.2.1.3.2. @@ -201,6 +204,9 @@ class RdpNegociation /// If set to 'false' the default (static) build number will be used. const bool forward_build_number; + std::array hwid; + bool has_hwid = false; + public: RdpNegociation( const ChannelsAuthorizations& channels_authorizations, @@ -249,4 +255,6 @@ class RdpNegociation template void send_data_request(uint16_t channelId, WriterData... writer_data); void send_client_info_pdu(); + + static void get_hwid_by_client_name(std::array& hwid_out, static_string const& client_name); }; diff --git a/src/mod/rdp/rdp_negociation_data.cpp b/src/mod/rdp/rdp_negociation_data.cpp index d1073cba03..b5e790d18a 100644 --- a/src/mod/rdp/rdp_negociation_data.cpp +++ b/src/mod/rdp/rdp_negociation_data.cpp @@ -27,7 +27,8 @@ #include "mod/rdp/rdp_negociation_data.hpp" RdpLogonInfo::RdpLogonInfo(bounded_chars_view<0, HOST_NAME_MAX> hostname, bool hide_client_name, - char const* target_user, bool split_domain) noexcept + char const* target_user, bool split_domain) noexcept : + hide_client_name(hide_client_name) { if (hide_client_name) { this->_hostname.delayed_build([](auto buffer) { diff --git a/src/mod/rdp/rdp_negociation_data.hpp b/src/mod/rdp/rdp_negociation_data.hpp index 1ef46105d3..311c0cc7a6 100644 --- a/src/mod/rdp/rdp_negociation_data.hpp +++ b/src/mod/rdp/rdp_negociation_data.hpp @@ -51,9 +51,12 @@ struct RdpLogonInfo [[nodiscard]] std::string const& domain() const noexcept { return this->_domain; } [[nodiscard]] static_string const& hostname() const noexcept { return this->_hostname; } + [[nodiscard]] bool client_name_is_hidden() const noexcept { return this->hide_client_name; } + private: + bool const hide_client_name; + std::string _username; std::string _domain; static_string _hostname; }; - diff --git a/src/mod/rdp/rdp_params.hpp b/src/mod/rdp/rdp_params.hpp index c310c3bb11..232d86ca8b 100644 --- a/src/mod/rdp/rdp_params.hpp +++ b/src/mod/rdp/rdp_params.hpp @@ -185,6 +185,8 @@ struct ModRDPParams const char * load_balance_info = ""; + const char * target_ip = ""; + bool support_connection_redirection_during_recording = true; bool split_domain = false; @@ -425,6 +427,8 @@ struct ModRDPParams RDP_PARAMS_LOG("%s", s_or_none, load_balance_info); + RDP_PARAMS_LOG("%s", s_or_none, target_ip); + RDP_PARAMS_LOG("%u", from_millisec, remote_app_params.rail_disconnect_message_delay); RDP_PARAMS_LOG("%s", yes_or_no, file_system_params.bogus_ios_rdpdr_virtual_channel); diff --git a/tests/acl/test_license_api.cpp b/tests/acl/test_license_api.cpp index faaeba889a..2fc1f29da2 100644 --- a/tests/acl/test_license_api.cpp +++ b/tests/acl/test_license_api.cpp @@ -262,12 +262,34 @@ RED_AUTO_TEST_CASE(TestWithoutExistingLicense) do_work = false; try { + std::array test_hwid = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + #ifdef GENERATE_TESTING_DATA class CaptureLicenseStore : public LicenseApi { public: + bytes_view get_license_v1(char const* client_name, char const* target_ip, uint32_t version, char const* scope, + char const* company_name, char const* product_id, std::array& hwid, writable_bytes_view out, + bool enable_log) override + { + (void)client_name; + (void)target_ip; + (void)version; + (void)scope; + (void)company_name; + (void)product_id; + (void)hwid; + (void)enable_log; + + return bytes_view(out.data(), 0); + } + // The functions shall return empty bytes_view to indicate the error. - bytes_view get_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, writable_bytes_view out, bool enable_log) override + bytes_view get_license_v0(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, writable_bytes_view out, bool enable_log) override { (void)client_name; (void)version; @@ -280,7 +302,7 @@ RED_AUTO_TEST_CASE(TestWithoutExistingLicense) return bytes_view { out.data(), 0 }; } - bool put_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, bytes_view in, bool enable_log) override + bool put_license(char const* client_name, char const* target_ip, uint32_t version, char const* scope, char const* company_name, char const* product_id, std::array const& hwid, bytes_view in, bool enable_log) override { (void)enable_log; @@ -288,6 +310,10 @@ RED_AUTO_TEST_CASE(TestWithoutExistingLicense) hexdump_c(client_name, ::strlen(client_name)); LOG(LOG_INFO, "/*CaptureLicenseStore */ ;"); + LOG(LOG_INFO, "/*CaptureLicenseStore */ const char target_ip[] ="); + hexdump_c(target_ip, ::strlen(target_ip)); + LOG(LOG_INFO, "/*CaptureLicenseStore */ ;"); + LOG(LOG_INFO, "/*CaptureLicenseStore */ uint32_t license_version = %u", version); LOG(LOG_INFO, "/*CaptureLicenseStore */ ;"); @@ -303,6 +329,10 @@ RED_AUTO_TEST_CASE(TestWithoutExistingLicense) hexdump_c(product_id, ::strlen(product_id)); LOG(LOG_INFO, "/*CaptureLicenseStore */ ;"); + LOG(LOG_INFO, "/*CaptureLicenseStore */ const uint8_t hwid[%zu] = {", hwid.size()); + hexdump_d(hwid); + LOG(LOG_INFO, "/*CaptureLicenseStore */ };"); + LOG(LOG_INFO, "/*CaptureLicenseStore */ const uint8_t license_data[%zu] = {", in.size()); hexdump_d(in); LOG(LOG_INFO, "/*CaptureLicenseStore */ };"); @@ -314,16 +344,33 @@ RED_AUTO_TEST_CASE(TestWithoutExistingLicense) class CompareLicenseStore : public LicenseApi { public: - CompareLicenseStore(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, bytes_view license_data) : + CompareLicenseStore(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, bytes_view hwid, bytes_view license_data) : expected_client_name(client_name), expected_version(version), expected_scope(scope), expected_company_name(company_name), expected_product_id(product_id), + expected_hwid(hwid), expected_license_data(license_data) {} + bytes_view get_license_v1(char const* client_name, char const* target_ip, uint32_t version, char const* scope, + char const* company_name, char const* product_id, std::array& hwid, writable_bytes_view out, + bool enable_log) override + { + (void)client_name; + (void)target_ip; + (void)version; + (void)scope; + (void)company_name; + (void)product_id; + (void)hwid; + (void)enable_log; + + return bytes_view(out.data(), 0); + } + // The functions shall return empty bytes_view to indicate the error. - bytes_view get_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, writable_bytes_view out, bool enable_log) override + bytes_view get_license_v0(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, writable_bytes_view out, bool enable_log) override { (void)client_name; (void)version; @@ -336,9 +383,10 @@ RED_AUTO_TEST_CASE(TestWithoutExistingLicense) return bytes_view { out.data(), 0 }; } - bool put_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, bytes_view in, bool enable_log) override + bool put_license(char const* client_name, char const* target_ip, uint32_t version, char const* scope, char const* company_name, char const* product_id, std::array const& hwid, bytes_view in, bool enable_log) override { (void)enable_log; + (void)target_ip; RED_CHECK_EQ(client_name, this->expected_client_name); RED_CHECK_EQ(version, this->expected_version); @@ -346,6 +394,10 @@ RED_AUTO_TEST_CASE(TestWithoutExistingLicense) RED_CHECK_EQ(company_name, this->expected_company_name); RED_CHECK_EQ(product_id, this->expected_product_id); + RED_REQUIRE_EQ(hwid.size(), this->expected_hwid.size()); + + RED_CHECK(hwid == this->expected_hwid); + RED_REQUIRE_EQ(in.size(), this->expected_license_data.size()); RED_CHECK(in == this->expected_license_data); @@ -359,9 +411,10 @@ RED_AUTO_TEST_CASE(TestWithoutExistingLicense) std::string_view expected_scope; std::string_view expected_company_name; std::string_view expected_product_id; + bytes_view expected_hwid; bytes_view expected_license_data; } license_store(license_client_name, license_version, license_scope, license_company_name, - license_product_id, bytes_view(license_data, sizeof(license_data))); + license_product_id, test_hwid, bytes_view(license_data, sizeof(license_data))); #endif #ifdef GENERATE_TESTING_DATA @@ -414,16 +467,39 @@ RED_AUTO_TEST_CASE(TestWithExistingLicense) class ReplayLicenseStore : public LicenseApi { public: - ReplayLicenseStore(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, bytes_view license_data) : + ReplayLicenseStore(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, bytes_view hwid, bytes_view license_data) : expected_client_name(client_name), expected_version(version), expected_scope(scope), expected_company_name(company_name), expected_product_id(product_id), + expected_hwid(hwid), expected_license_data(license_data) {} + bytes_view get_license_v1(char const* client_name, char const* target_ip, uint32_t version, char const* scope, char const* company_name, char const* product_id, std::array& hwid, writable_bytes_view out, bool enable_log) override + { + (void)enable_log; + (void)target_ip; + + RED_CHECK_EQ(client_name, this->expected_client_name); + RED_CHECK_EQ(version, this->expected_version); + RED_CHECK_EQ(scope, this->expected_scope); + RED_CHECK_EQ(company_name, this->expected_company_name); + RED_CHECK_EQ(product_id, this->expected_product_id); + + ::memcpy(hwid.data(), this->expected_hwid.data(), hwid.size()); + + RED_REQUIRE_GE(out.size(), this->expected_license_data.size()); + + size_t const effective_license_size = std::min(out.size(), this->expected_license_data.size()); + + ::memcpy(out.data(), this->expected_license_data.data(), effective_license_size); + + return bytes_view { out.data(), effective_license_size }; + } + // The functions shall return empty bytes_view to indicate the error. - bytes_view get_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, writable_bytes_view out, bool enable_log) override + bytes_view get_license_v0(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, writable_bytes_view out, bool enable_log) override { (void)enable_log; @@ -442,10 +518,12 @@ RED_AUTO_TEST_CASE(TestWithExistingLicense) return bytes_view { out.data(), effective_license_size }; } - bool put_license(char const* client_name, uint32_t version, char const* scope, char const* company_name, char const* product_id, bytes_view in, bool enable_log) override + bool put_license(char const* client_name, char const* target_ip, uint32_t version, char const* scope, char const* company_name, char const* product_id, std::array const& hwid, bytes_view in, bool enable_log) override { (void)enable_log; + (void)hwid; (void)in; + (void)target_ip; RED_CHECK_EQ(client_name, this->expected_client_name); RED_CHECK_EQ(version, this->expected_version); @@ -462,6 +540,7 @@ RED_AUTO_TEST_CASE(TestWithExistingLicense) std::string_view expected_scope; std::string_view expected_company_name; std::string_view expected_product_id; + bytes_view expected_hwid; bytes_view expected_license_data; }; @@ -471,6 +550,7 @@ RED_AUTO_TEST_CASE(TestWithExistingLicense) license_scope, license_company_name, license_product_id, + make_array_view(license_hwdi), make_array_view(license_data) ); diff --git a/tests/includes/fixtures/test_license_api_license.hpp b/tests/includes/fixtures/test_license_api_license.hpp index acac99fd5d..9e888de505 100644 --- a/tests/includes/fixtures/test_license_api_license.hpp +++ b/tests/includes/fixtures/test_license_api_license.hpp @@ -13,6 +13,12 @@ const char license_company_name[] = const char license_product_id[] = /* 0000 */ "\x41\x30\x32" // A02 ; + +const char license_hwdi[LIC::LICENSE_HWID_SIZE] = { +/* 0000 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, // 0123456789ABCDEF +/* 0010 */ 0x00, 0x00, 0x00, 0x00, // .... +}; + const uint8_t license_data[1978] = { /* 0000 */ 0x30, 0x82, 0x07, 0xb6, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, // 0.....*.H....... /* 0010 */ 0x82, 0x07, 0xa7, 0x30, 0x82, 0x07, 0xa3, 0x02, 0x01, 0x01, 0x31, 0x00, 0x30, 0x0b, 0x06, 0x09, // ...0......1.0...