From 8640d90350151c3b7f27b781c9272fafe1408ea3 Mon Sep 17 00:00:00 2001 From: Peter Sykora Date: Mon, 22 Apr 2024 21:44:57 +0200 Subject: [PATCH] [impl] Linux support implementation --- CMakeLists.txt | 11 +- api/{OSUtils.cpp => OSUtils_win.cpp} | 0 api/SystemParams.h | 3 + api/SystemParamsProvider.h | 5 + api/SystemParamsProvider_linux.h | 23 ++ conanfile.txt | 6 +- src/LicenseClient.cpp | 16 +- src/SafeFileUtils.cpp | 7 + src/SystemParams.cpp | 3 + src/SystemParamsProvider_linux.cpp | 474 +++++++++++++++++++++++++++ test/libLicenseClientTest.cpp | Bin 186 -> 87 bytes 11 files changed, 540 insertions(+), 8 deletions(-) rename api/{OSUtils.cpp => OSUtils_win.cpp} (100%) create mode 100644 api/SystemParamsProvider_linux.h create mode 100644 src/SystemParamsProvider_linux.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c329a41..5e8ebf6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,11 +43,12 @@ set(api "api/SystemParams.h" "api/SystemParamsProvider.h" "api/SystemParamsProvider_win.h" + "api/SystemParamsProvider_linux.h" ) source_group("api" FILES ${api}) set(src - "api/OSUtils.cpp" + "api/OSUtils_win.cpp" "src/CachedDownloader.cpp" "src/CryptoUtils.cpp" "src/HashUtils.cpp" @@ -62,6 +63,7 @@ set(src "src/SafeFileUtils.h" "src/SystemParams.cpp" "src/SystemParamsProvider_win.cpp" + "src/SystemParamsProvider_linux.cpp" ) source_group("src" FILES ${src}) @@ -70,6 +72,13 @@ set(ALL_FILES ${src} ) +if(UNIX) + set_source_files_properties(api/OSUtils_win.cpp PROPERTIES HEADER_FILE_ONLY TRUE) + set_source_files_properties(src/SystemParamsProvider_win.cpp PROPERTIES HEADER_FILE_ONLY TRUE) +else(WIN32) + set_source_files_properties(src/SystemParamsProvider_linux.cpp PROPERTIES HEADER_FILE_ONLY TRUE) +endif() + ################################################################################ # Target ################################################################################ diff --git a/api/OSUtils.cpp b/api/OSUtils_win.cpp similarity index 100% rename from api/OSUtils.cpp rename to api/OSUtils_win.cpp diff --git a/api/SystemParams.h b/api/SystemParams.h index bb2af81..99576c4 100644 --- a/api/SystemParams.h +++ b/api/SystemParams.h @@ -7,10 +7,13 @@ struct SystemParamTypes { static const std::string biosSerialNum; static const std::string computerUUID; + static const std::string computerSerial; static const std::string diskSerialNum; static const std::string osId; static const std::string nicMac; static const std::string mainboardSerialNum; + static const std::string cpuIdModel; + static const std::string cpuIdHypervisor; }; typedef std::map SystemParams; diff --git a/api/SystemParamsProvider.h b/api/SystemParamsProvider.h index 9533ccc..f25b77d 100644 --- a/api/SystemParamsProvider.h +++ b/api/SystemParamsProvider.h @@ -5,4 +5,9 @@ #include "SystemParamsProvider_win.h" typedef SystemParamsProvider_win SystemParamsProvider; +#else + +#include "SystemParamsProvider_linux.h" +typedef SystemParamsProvider_linux SystemParamsProvider; + #endif // WIN32 diff --git a/api/SystemParamsProvider_linux.h b/api/SystemParamsProvider_linux.h new file mode 100644 index 0000000..6615247 --- /dev/null +++ b/api/SystemParamsProvider_linux.h @@ -0,0 +1,23 @@ +#pragma once + +#include "SystemParams.h" + +#include +#include + +namespace detail +{ + class SystemParamsProvider_linuxImpl; +} + +class SystemParamsProvider_linux final +{ +public: + SystemParamsProvider_linux(); + ~SystemParamsProvider_linux(); + +public: + SystemParams retrieveSystemParams(); +private: + std::unique_ptr m_impl; +}; diff --git a/conanfile.txt b/conanfile.txt index 205a141..b437e1b 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -9,8 +9,8 @@ cmake_find_package_multi [options] libzip:shared=False +libzip:crypto=False libcurl:shared=False -libcurl:with_openssl=False -libcurl:with_winssl=True +libcurl:with_ssl=openssl boost:shared=False -cryptopp:shared=False \ No newline at end of file +cryptopp:shared=False diff --git a/src/LicenseClient.cpp b/src/LicenseClient.cpp index 893b24c..1821096 100644 --- a/src/LicenseClient.cpp +++ b/src/LicenseClient.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include @@ -72,8 +74,8 @@ typedef std::string ParamHash; ParamHash fletcher64(const std::string& input) { std::vector buf((input.size() + sizeof(uint32_t) - 1) / sizeof(uint32_t), 0); - std::copy_n(std::begin(input), input.size(), stdext::make_checked_array_iterator((std::string::pointer)buf.data(), buf.size() * sizeof(uint32_t))); - auto resInt = ::fletcher64(buf.data(), buf.size()); + std::memcpy((std::string::pointer)buf.data(), input.data(), input.size()); + auto resInt = ::fletcher64(buf.data(), static_cast(buf.size())); return intToHex(resInt); } @@ -84,7 +86,13 @@ int countUniqueCharacters(std::string_view str) { chars.insert(ch); } - return chars.size(); + return static_cast(chars.size()); +} + +std::string filterPunctuation(std::string input) +{ + boost::remove_erase_if(input, boost::is_any_of(". :;-_/")); + return input; } SystemParams skipEmptyParams(const SystemParams& systemParams) @@ -92,7 +100,7 @@ SystemParams skipEmptyParams(const SystemParams& systemParams) SystemParams result; for (const auto& entry : systemParams) { - if (!entry.second.empty() && entry.second.length() > 1 && countUniqueCharacters(entry.second) > 1) + if (!entry.second.empty() && entry.second.length() > 1 && countUniqueCharacters(filterPunctuation(entry.second)) > 1) { result[entry.first] = entry.second; } diff --git a/src/SafeFileUtils.cpp b/src/SafeFileUtils.cpp index 28c5bf1..79dacc5 100644 --- a/src/SafeFileUtils.cpp +++ b/src/SafeFileUtils.cpp @@ -1,5 +1,8 @@ #include "SafeFileUtils.h" + +#ifdef _WIN32 #include +#endif namespace fs = boost::filesystem; @@ -12,7 +15,11 @@ void safeRemoveFile(const fs::path& p, const fs::path& tempFolder) catch (fs::filesystem_error& e) { if (e.code().category() == boost::system::system_category() +#ifdef _WIN32 && e.code().value() == ERROR_ACCESS_DENIED) +#else + && e.code().value() == EACCES) +#endif { auto cleanupFolder = tempFolder; diff --git a/src/SystemParams.cpp b/src/SystemParams.cpp index d1b273c..9458df8 100644 --- a/src/SystemParams.cpp +++ b/src/SystemParams.cpp @@ -2,7 +2,10 @@ const std::string SystemParamTypes::biosSerialNum{ "biosSerialNum" }; const std::string SystemParamTypes::computerUUID{ "computerUUID" }; +const std::string SystemParamTypes::computerSerial{ "computerSerial" }; const std::string SystemParamTypes::diskSerialNum{ "diskSerialNum" }; const std::string SystemParamTypes::osId{ "osId" }; const std::string SystemParamTypes::nicMac{ "nicMac" }; const std::string SystemParamTypes::mainboardSerialNum{ "mainboardSerialNum" }; +const std::string SystemParamTypes::cpuIdModel{ "cpuIdModel" }; +const std::string SystemParamTypes::cpuIdHypervisor{ "cpuIdHypervisor" }; diff --git a/src/SystemParamsProvider_linux.cpp b/src/SystemParamsProvider_linux.cpp new file mode 100644 index 0000000..9e23203 --- /dev/null +++ b/src/SystemParamsProvider_linux.cpp @@ -0,0 +1,474 @@ +#include "SystemParamsProvider_linux.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace bp = boost::process; //we will assume this for all further examples + +namespace +{ + template< typename T > + std::string intToHex( T i ) + { + std::ostringstream stream; + stream << std::setfill ('0') << std::setw(sizeof(T)*2) + << std::hex << i; + return stream.str(); + } + + std::vector splitString(const std::string& str, char c = ' ') + { + std::vector result; + + auto it = str.cbegin(); + do + { + auto firstIt = it; + + while(*it != c && it != str.cend()) { + ++it; + } + + result.emplace_back(firstIt, it); + } while (it++ != str.cend()); + + return result; + } + + std::optional readFileLine(const std::string& fileName) + { + std::string line; + std::ifstream myfile(fileName); + if (myfile.is_open()) { + BOOST_LOG_TRIVIAL(debug) << "File " << fileName << " successfully open"; + if (getline (myfile,line)) { + return line; + } + myfile.close(); + } + return {}; + } + + std::optional loadOsId() + { + BOOST_LOG_TRIVIAL(debug) << "Trying to find Operating system ID..."; + auto machineId = readFileLine("/var/lib/dbus/machine-id"); + if (machineId.value_or("").empty()) { + machineId = readFileLine("/etc/machine-id"); + } + return machineId; + } + + std::optional loadBoardSerial() + { + BOOST_LOG_TRIVIAL(debug) << "Trying to find mainboard serial number..."; + return readFileLine("/sys/class/dmi/id/board_serial"); + } + + std::optional loadComputerUUID() + { + BOOST_LOG_TRIVIAL(debug) << "Trying to find product UUID..."; + return readFileLine("/sys/class/dmi/id/product_uuid"); + } + + std::optional loadComputerSerial() + { + BOOST_LOG_TRIVIAL(debug) << "Trying to find computer serial number..."; + auto productSerial = readFileLine("/sys/class/dmi/id/product_serial"); + + if(!productSerial.value_or("").empty()) { + auto productFamily = readFileLine("/sys/class/dmi/id/product_family"); + auto productName = readFileLine("/sys/class/dmi/id/product_name"); + + std::ostringstream os; + os << productFamily.value() << "/" << productName.value() << "/" << productSerial.value(); + return os.str(); + } + + return {}; + } + + struct CpuIdInfo { + uint32_t vendorId; + std::string vendorString; + uint32_t modelId; + bool isHypervisor; + std::string serialNumber; + }; + + CpuIdInfo loadCpuId() + { + BOOST_LOG_TRIVIAL(debug) << "Trying to find processor idenfication..."; + uint32_t a, b, c, d; + __cpuid (0 /* vendor string */, a, b, c, d); + std::cout << "EAX: " << std::hex << std::setw(8) << std::setfill('0') << a << "\nEBX: " << b << "\nECX: " << c << "\nEDX: " << d << "\n"; + uint32_t vendorId = a; + uint32_t vendorString[] = {b, d, c, 0}; + + __cpuid (1 /* Processor Info and Feature Bits */, a, b, c, d); + std::cout << "EAX: " << std::hex << std::setw(8) << std::setfill('0') << a << "\nEBX: " << b << "\nECX: " << c << "\nEDX: " << d << "\n"; + bool isHypervisor = c & 0x80000000; + uint32_t modelId = a & 0b00001111111111110011111111111111; + + __cpuid (3 /* Processor Serial Number */, a, b, c, d); + std::ostringstream oss; + oss << std::hex << std::setw(8) << std::setfill('0') << a << "-" + << std::hex << std::setw(8) << std::setfill('0') << b << "-" + << std::hex << std::setw(8) << std::setfill('0') << c << "-" + << std::hex << std::setw(8) << std::setfill('0') << d; + + return CpuIdInfo {vendorId, std::string((const char*)vendorString), modelId, isHypervisor, oss.str()}; + } + + std::optional> matchParameter( + const std::string& line, + const std::string& parameterRegex, + const std::string& valueRegex = "([0-9a-fA-F\\-_]+)$" + ) + { + std::regex line_regex(parameterRegex + ": " + valueRegex); + std::smatch line_match; + if(std::regex_search(line, line_match, line_regex)) { + BOOST_LOG_TRIVIAL(trace) << "Matched parameter pair " << line_match[1] << ": " << line_match[2]; + return std::make_pair(line_match[1], line_match[2]); + } + return {}; + } + + struct DmiDecodeParamTypes + { + static const std::string biosSerialNum; + static const std::string systemUUID; + static const std::string systemSerialNum; + static const std::string systemFamily; + static const std::string systemProductNumber; + static const std::string baseboardSerialNumber; + }; + + const std::string DmiDecodeParamTypes::systemUUID = "systemUUID"; + const std::string DmiDecodeParamTypes::systemSerialNum = "systemSerialNum"; + const std::string DmiDecodeParamTypes::systemFamily = "systemFamily"; + const std::string DmiDecodeParamTypes::systemProductNumber = "systemProductNumber"; + const std::string DmiDecodeParamTypes::baseboardSerialNumber = "baseboardSerialNumber"; + + std::map read_dmidecode() + { + std::error_code ec; + std::map output; + bp::ipstream is; //reading pipe-stream + bp::child c("/usr/sbin/dmidecode", ec, bp::std_out > is, bp::std_err > bp::null); + + if(ec) { + return output; + } + + std::regex section_regex("DMI type ([0-9]+)"); + + std::string line; + int section = -1; + while (std::getline(is, line)) { + if (!line.empty() && line[0] != '\t') { + std::smatch section_match; + if(std::regex_search(line, section_match, section_regex)) { + section = std::atoi(section_match[1].str().c_str()); + BOOST_LOG_TRIVIAL(debug) << "Found section: " << section_match[1]; + } + continue; + } + + if (section == 1) { + { + const auto matchedParameter = matchParameter(line, "(UUID)"); + if (matchedParameter.has_value()) { + output["systemUUID"] = matchedParameter.value().second; + continue; + } + } + { + const auto matchedParameter = matchParameter(line, "(Serial Number)", "([0-9a-zA-Z\\-_]+)$"); + if (matchedParameter.has_value()) { + output["systemSerialNumber"] = matchedParameter.value().second; + continue; + } + } + { + const auto matchedParameter = matchParameter(line, "(Family)", "([0-9a-zA-Z\\-_ ]+)$"); + if (matchedParameter.has_value()) { + output["systemFamily"] = matchedParameter.value().second; + continue; + } + } + { + const auto matchedParameter = matchParameter(line, "(Product name)", "([0-9a-zA-Z\\-_ ]+)$"); + if (matchedParameter.has_value()) { + output["systemProductName"] = matchedParameter.value().second; + continue; + } + } + } + else if (section == 2) { + { + const auto matchedParameter = matchParameter(line, "(Serial Number)", "([0-9a-zA-Z\\-_]+)$"); + if (matchedParameter.has_value()) { + output["baseboardSerialNumber"] = matchedParameter.value().second; + continue; + } + } + } + + } + + c.wait(ec); + + return output; + } + + std::map readMacAddresses() + { + std::error_code ec; + std::map output; + bp::ipstream is; //reading pipe-stream + bp::child c("/usr/bin/find /sys/class/net -mindepth 1 -maxdepth 1 ! -name lo -printf \"%P: \" -execdir /usr/bin/cat {}/address ;", ec, bp::std_out > is, bp::std_err > bp::null); + if (ec) { + return output; + } + + std::regex section_regex("([0-9a-zA-Z\\-_]+): ([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2})"); + + std::string line; + int section = -1; + while (std::getline(is, line)) { + if (!line.empty() && line[0] == 'e') { + std::smatch section_match; + if(std::regex_match(line, section_match, section_regex)) { + BOOST_LOG_TRIVIAL(debug) << "Found device : " << section_match[1] << " with MAC : " << section_match[2]; + output[section_match[1]] = section_match[2]; + } + continue; + } + } + + c.wait(ec); + + return output; + } + + struct LsBlkResultEntry{ + std::string mountPoint; + std::string parentName; + std::string name; + std::string model; + std::string serial; + std::string type; + }; + + std::optional parseLsBlkResultEntry(const std::string& line) + { + auto cols = splitString(line, ' '); + if(cols.size() == 6) { + return LsBlkResultEntry{ + cols[0],cols[1],cols[2],cols[3],cols[4],cols[5] + }; + } + return {}; + } + + std::optional readDiskSerial() + { + std::error_code ec; + bp::ipstream is; //reading pipe-stream + bp::child c("/usr/bin/lsblk -oMOUNTPOINT,PKNAME,NAME,MODEL,SERIAL,TYPE --raw", ec, bp::std_out > is, bp::std_err > bp::null); + + if (ec) { + return {}; + } + + std::vector cmdOut; + + std::string line; + int section = -1; + while (std::getline(is, line)) { + if (!line.empty()) { + const auto entry = parseLsBlkResultEntry(line); + if (entry.has_value()) { + cmdOut.push_back(entry.value()); + } + } + } + c.wait(ec); + if(ec) { + return {}; + } + + // Find best mountpoint + LsBlkResultEntry const* bestEntry = nullptr; + for (const auto& entry: cmdOut) { + if(entry.mountPoint == "/") { + bestEntry = &entry; + } + else if(entry.mountPoint == "/boot" && (bestEntry == nullptr || bestEntry->mountPoint == "/sysroot")) { + bestEntry = &entry; + } + else if(entry.mountPoint == "/sysroot" && bestEntry == nullptr) { + bestEntry = &entry; + } + } + + if(bestEntry == nullptr) + { + return {}; + } + + BOOST_LOG_TRIVIAL(debug) << "Found best mount point: " << bestEntry->mountPoint; + + int depth = 0; + while(depth < 10 && !bestEntry->parentName.empty()) { + ++depth; + bool found = false; + for (const auto& entry: cmdOut) { + if (entry.name == bestEntry->parentName) { + bestEntry = &entry; + found = true; + break; + } + } + if (!found) { + break; + } + } + + if (bestEntry->type == "disk" && !bestEntry->serial.empty()) { + return bestEntry->serial; + } + + return {}; + } +} + +namespace detail +{ + class SystemParamsProvider_linuxImpl final + { + public: + SystemParamsProvider_linuxImpl() + { + } + + ~SystemParamsProvider_linuxImpl() + { + } + + public: + SystemParams retrieveSystemParams() + { + SystemParams result; +// result[SystemParamTypes::biosSerialNum] = processiosData(biosFuture.get()); + { + const auto val = loadComputerUUID(); + if (val.has_value()) { + result[SystemParamTypes::computerUUID] = val.value(); + } + } + { + const auto val = loadComputerSerial(); + if (val.has_value()) { + result[SystemParamTypes::computerSerial] = val.value(); + } + } + { + const auto val = loadOsId(); + if (val.has_value()) { + result[SystemParamTypes::osId] = val.value(); + } + } + { + const auto val = loadBoardSerial(); + if (val.has_value()) { + result[SystemParamTypes::mainboardSerialNum] = val.value(); + } + } + + auto cpuIdInfo = loadCpuId(); + BOOST_LOG_TRIVIAL(trace) << "Vendor ID: " << cpuIdInfo.vendorId << std::endl; + BOOST_LOG_TRIVIAL(trace) << "Model ID: " << cpuIdInfo.modelId << std::endl; + BOOST_LOG_TRIVIAL(trace) << "Vendor String: " << cpuIdInfo.vendorString << std::endl; + BOOST_LOG_TRIVIAL(trace) << "Is hypervisor: " << cpuIdInfo.isHypervisor << std::endl; + BOOST_LOG_TRIVIAL(trace) << "Serial number: " << cpuIdInfo.serialNumber << std::endl; + + result[SystemParamTypes::cpuIdModel] = intToHex(cpuIdInfo.vendorId) + intToHex(cpuIdInfo.modelId); + if(cpuIdInfo.isHypervisor) { + result[SystemParamTypes::cpuIdHypervisor] = "true"; + } + + const auto macAddresses = readMacAddresses(); + if(macAddresses.size() > 0) + { + result[SystemParamTypes::nicMac] = macAddresses.begin()->second; + } + + const auto diskSerial = readDiskSerial(); + if (diskSerial.has_value()) { + result[SystemParamTypes::diskSerialNum] = diskSerial.value(); + } + + auto result2 = read_dmidecode(); + for (auto it = result2.cbegin(); it != result2.cend(); ++it) { + std::cout << it->first << ": " << it->second << std::endl; + } + + if (result.find(SystemParamTypes::mainboardSerialNum) == result.end() && + result2.find(DmiDecodeParamTypes::baseboardSerialNumber) != result2.end()) { + result[SystemParamTypes::mainboardSerialNum] = result2[DmiDecodeParamTypes::baseboardSerialNumber]; + } + + if (result.find(SystemParamTypes::computerUUID) == result.end() && + result2.find(DmiDecodeParamTypes::systemUUID) != result2.end()) { + result[SystemParamTypes::computerUUID] = result2[DmiDecodeParamTypes::systemUUID]; + } + + if (result.find(SystemParamTypes::computerSerial) == result.end() && + result2.find(DmiDecodeParamTypes::systemSerialNum) != result2.end()) { + if(!result2[DmiDecodeParamTypes::systemUUID].empty()) { + auto productFamily = result2.find(DmiDecodeParamTypes::systemFamily); + auto productName = result2.find(DmiDecodeParamTypes::systemProductNumber); + + std::ostringstream os; + os << ((productFamily != result2.end()) ? productFamily->second : "") + << "/" << ((productName != result2.end()) ? productName->second : "") + << "/" << result2[DmiDecodeParamTypes::systemUUID]; + result[SystemParamTypes::computerSerial] = os.str(); + } + } + + return result; + } + + private: + + private: + + private: + }; +} + +SystemParamsProvider_linux::SystemParamsProvider_linux() + : m_impl(std::make_unique()) +{ +} + +SystemParamsProvider_linux::~SystemParamsProvider_linux() +{ +} + +SystemParams SystemParamsProvider_linux::retrieveSystemParams() +{ + return m_impl->retrieveSystemParams(); +} diff --git a/test/libLicenseClientTest.cpp b/test/libLicenseClientTest.cpp index 7cbb46997cf7368889295729d50299c20b716b29..e73488203b18b589dcc37f6b21334e8ae10ae585 100644 GIT binary patch literal 87 zcmW;BF%Ez*2mrvnuiyo;yXxrZ2P&bFga9cl{$AsHhX^XyA;gxvsj$__SqnwL3Of%m ePNZk)(CP(8IKT1y^k^lez