From 7e4e14dde5fbfade46311fbf75386d5371062d7d Mon Sep 17 00:00:00 2001 From: gcontini <1121667+gcontini@users.noreply.github.com> Date: 周六, 24 10月 2020 19:59:33 +0800 Subject: [PATCH] review of disk strategy linux --- src/templates/licensecc_properties.h.in | 20 + .gitignore | 1 src/library/hw_identifier/identification_strategy.cpp | 7 test/library/os_linux_test.cpp | 67 ++++- src/library/os/os.h | 18 - src/inspector/inspector.cpp | 26 + src/library/os/linux/os_linux.cpp | 292 ++++++++++++++++---------- doc/usage/issue-licenses.md | 4 doc/index.rst | 4 src/library/os/windows/os_win.cpp | 61 ++--- doc/conf.py | 1 /dev/null | 0 src/library/hw_identifier/disk_strategy.cpp | 77 +++---- src/library/LicenseReader.cpp | 2 .travis.yml | 2 doc/usage/Hardware-identifiers.rst | 48 +++ test/functional/hw_identifier_it_test.cpp | 19 - src/library/hw_identifier/disk_strategy.hpp | 4 18 files changed, 380 insertions(+), 273 deletions(-) diff --git a/.gitignore b/.gitignore index 6728aa5..7e97712 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ **/CMakeCache.txt **/CMakeFiles /CMakeSettings.json +/.venv/ diff --git a/.travis.yml b/.travis.yml index 2b5e856..370681f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -278,7 +278,7 @@ - doxygen - graphviz before_script: - - pip3 install setuptools sphinx sphinx_rtd_theme breathe sphinx-markdown-tables recommonmark sphinx-rtd-theme sphinx-sitemap sphinxemoji + - pip3 install setuptools sphinx sphinx_rtd_theme breathe sphinx-markdown-tables recommonmark sphinx-sitemap sphinxemoji - cd build && cmake -DCMAKE_INSTALL_PREFIX=../../install .. script: - make documentation diff --git a/build/.gitkeep b/build/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/build/.gitkeep +++ /dev/null diff --git a/doc/conf.py b/doc/conf.py index 7e4e7ed..1b14e55 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -39,6 +39,7 @@ # Breathe Configuration breathe_default_project = "licensecc" +breathe_domain_by_extension = {"h" : "cpp"} # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/doc/index.rst b/doc/index.rst index c711991..2eeec3e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -44,7 +44,7 @@ ******************* The software is made by 4 main sub-components: -* ``licensecc`` : the C++ library with a C api (the part you have to integrate in your software) with minimal (or no) external dependencies. This is the project you're currently looking at. +* `licensecc` : the C++ library with a C api (the part you have to integrate in your software) with minimal (or no) external dependencies. This is the project you're currently looking at. * ``lccinspector`` : a license debugger to be sent to the final customer to diagnose licensing problems or for calculating the hardware id before issuing the license. * ``lccgen`` : a license generator (github project `lcc-license-generator`_ ) to initialize the library and generate the licenses. * ``examples`` : usage samples (github project `examples <https://github.com/open-license-manager/examples>`_ ). @@ -119,7 +119,7 @@ How to use ************** -The `examples`_ repository shows various ways to integrate ``licensecc`` into your project. +The `examples`_ repository shows various ways to integrate `licensecc` into your project. .. _examples: https://github.com/open-license-manager/examples diff --git a/doc/usage/Hardware-identifiers.rst b/doc/usage/Hardware-identifiers.rst index 414ef00..f43896a 100644 --- a/doc/usage/Hardware-identifiers.rst +++ b/doc/usage/Hardware-identifiers.rst @@ -44,16 +44,48 @@ For instance if containers are used to avoid to pollute the external distribution it makes perfect sense to have an hardware identifier, if users are running dockers in a kubernetes cluster in the cloud it makes no sense at all. -************************ -Identifier Generation -************************ -`Licensecc` is able to identify which virtual environment the user is running in and select the appropriate generation -strategy. Below the identifier generation workflow used by the :ref:`identify_pc <api/public_api:Public api>` method. +************************************************* +Hardware Identifier Generation +************************************************* + +The licensed application will call the api method :ref:`identify_pc <api/public_api:Public api>` to generate an hardware +identifier and print it out to the user, the user then will contact the software licensor to get an appropriate license. + +The licensed application can either decide an identification strategy by passing it in the ``identify_pc`` parameter ``hw_id_method`` +(see: :cpp:enum:`LCC_API_HW_IDENTIFICATION_STRATEGY` ) or let `licensecc` automatically choose how to generate the +identifier (by passing `hw_id_method=STRATEGY_DEFAULT`). +In this case `licensecc` is able to identify which virtual environment the user is running in and select the appropriate generation +strategy. + +Below the full identifier generation workflow used by the :ref:`identify_pc <api/public_api:Public api>` method. .. figure:: ../_static/pc-id-selection.png -First of all it takes in account the application specified parameter -If the licensed software uses `STRATEGY_DEFAULT` and the strategy generates an unstable identifier it is possible to ask the user to -set the environemnt variable. +Default identifier generation (implementation details) +======================================================= +This section describes the inner working of the default hardware identifer strategy. + +When the licensed software calls :ref:`identify_pc <api/public_api:Public api>` with :cpp:enumerator:`LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DEFAULT` +the identifier generation will follow these steps: + + - It will first look to the environment variable ``IDENTIFICATION_STRATEGY``. If set it will use the identification strategy in that variable. + - It will try to determine which virtual environment the licensed software is running in. + * If no virtual environment found it will use the strategies in :c:macro:`LCC_BARE_TO_METAL_STRATEGIES`, it will try them one by one until the first one succeeds. + * If it detects it's running in a Virtual Machine it will try the strategies in :c:macro:`LCC_VM_STRATEGIES`, it will try them one by one until the first one succeeds. + +if you're interested in implementing your own hardware identification strategy you can have a look to the library + :ref:`extension points <api/extend:Tweak hardware signature generator>`. + +.. TIP: + + If `licensecc` is generating a bad hardware identifier (eg. 'AAAA-AAAA-AAAA') software licensor can ask the user + to set the environment variable ``IDENTIFICATION_STRATEGY`` and try again. Or he can send the user the `lccinspector` + to generate all the possible identifiers for that machine. + + +.. NOTE:: + + `licensecc` will try to validate the identifier using the same strategy that was used to generate it, regardless + of what is the default method now in use. eg: disk identifiers will always be validated by ``DiskStrategy``. diff --git a/doc/usage/issue-licenses.md b/doc/usage/issue-licenses.md index 6bc5dc9..b977bbe 100644 --- a/doc/usage/issue-licenses.md +++ b/doc/usage/issue-licenses.md @@ -40,7 +40,7 @@ ``` cd projects/DEFAULT #(or whatever your project name is) -lcc license issue --client-signature XXXX-XXXX-XXXX-XXXX -o licenses/{license-file-name}.lic +lcc license issue --client-signature XXXX-XXXX-XXXX -o licenses/{license-file-name}.lic ``` usually this command is issued in the host machine where you compiled `licensecc` @@ -52,7 +52,7 @@ |base64,b | the license is encoded for inclusion in environment variables | |valid-from | Specify the start of the validity for this license. Format YYYY-MM-DD. If not specified defaults to today. | |valid-to | The expire date for this license. Format YYYY-MM-DD. If not specified the license won't expire | -|client-signature | The signature of the hardware where the licensed software will run. It should be in the format XXXX-XXXX-XXXX-XXXX. If not specified the license won't be linked to a specific pc. | +|client-signature | The signature of the hardware where the licensed software will run. It should be in the format XXXX-XXXX-XXXX. If not specified the license won't be linked to a specific pc. | |output-file-name | License output file path. | |extra-data | Application specific data. They'll be returned when calling the `acquire_license` method | |feature-names | Comma separated list of features to license. See `multi-feature` discussion. | diff --git a/src/inspector/inspector.cpp b/src/inspector/inspector.cpp index 40cd281..640071d 100644 --- a/src/inspector/inspector.cpp +++ b/src/inspector/inspector.cpp @@ -10,15 +10,13 @@ #include "../library/os/dmi_info.hpp" #include "../library/os/cpu_info.hpp" #include "../library/os/dmi_info.hpp" +#include "../library/os/network.hpp" using namespace std; using namespace license::os; -const map<int, string> stringByStrategyId = {{STRATEGY_DEFAULT, "DEFAULT"}, - {STRATEGY_ETHERNET, "MAC"}, - {STRATEGY_IP_ADDRESS, "IP"}, - {STRATEGY_DISK_NUM, "Disk1"}, - {STRATEGY_DISK_LABEL, "Disk2"}}; +const map<int, string> stringByStrategyId = { + {STRATEGY_DEFAULT, "DEFAULT"}, {STRATEGY_ETHERNET, "MAC"}, {STRATEGY_IP_ADDRESS, "IP"}, {STRATEGY_DISK, "Disk"}}; const unordered_map<int, string> descByVirtDetail = {{BARE_TO_METAL, "No virtualization"}, {VMWARE, "Vmware"}, @@ -98,6 +96,24 @@ cout << "Virtualiz. detail:" << descByVirtDetail.find(exec_env_info.virtualization_detail)->second << endl; cout << "Cloud provider :" << descByCloudProvider.find(exec_env_info.cloud_provider)->second << endl; + std::vector<license::os::OsAdapterInfo> adapterInfos; + FUNCTION_RETURN ret = license::os::getAdapterInfos(adapterInfos); + if (ret == FUNCTION_RETURN::FUNC_RET_OK) { + for (auto osAdapter : adapterInfos) { + cout << "Network adapter [" << osAdapter.id << "]: " << osAdapter.description << endl; + cout << " ip address [" << static_cast<unsigned int>(osAdapter.ipv4_address[3]) << "-" + << static_cast<unsigned int>(osAdapter.ipv4_address[2]) << "-" + << static_cast<unsigned int>(osAdapter.ipv4_address[1]) << "-" + << static_cast<unsigned int>(osAdapter.ipv4_address[0]) << "]" << endl; + cout << " mac address ["; + for (int i = 0; i < 8; i++) { + // print mac + } + } + } else { + cout << "problem in getting adapter informations:" << ret << endl; + } + license::os::CpuInfo cpu; cout << "Cpu Vendor :" << cpu.vendor() << endl; cout << "Cpu Brand :" << cpu.brand() << endl; diff --git a/src/library/LicenseReader.cpp b/src/library/LicenseReader.cpp index 6f46d91..4d2792c 100644 --- a/src/library/LicenseReader.cpp +++ b/src/library/LicenseReader.cpp @@ -80,7 +80,7 @@ * sw_version_to = (optional int) * from_date = YYYY-MM-DD (optional) * to_date = YYYY-MM-DD (optional) - * client_signature = XXXX-XXXX-XXXX-XXXX (optional string 16) + * client_signature = XXXX-XXXX-XXXX (optional string 16) * sig = XXXXXXXXXX (mandatory, 1024) * application_data = xxxxxxxxx (optional string 16) */ diff --git a/src/library/hw_identifier/disk_strategy.cpp b/src/library/hw_identifier/disk_strategy.cpp index 64142f7..5e50761 100644 --- a/src/library/hw_identifier/disk_strategy.cpp +++ b/src/library/hw_identifier/disk_strategy.cpp @@ -12,74 +12,63 @@ namespace license { namespace hw_identifier { -static FUNCTION_RETURN generate_disk_pc_id(vector<array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA>> &v_disk_id, - bool use_id) { - size_t disk_num = 0; - size_t available_disk_info = 0; - FUNCTION_RETURN result_diskinfos; +static array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> generate_id_by_sn(const DiskInfo &disk_info) { + array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> a_disk_id; + a_disk_id.fill(0); + size_t size = min(a_disk_id.size(), sizeof(disk_info.disk_sn)); + memcpy(&a_disk_id[0], disk_info.disk_sn, size); - result_diskinfos = getDiskInfos(nullptr, &disk_num); + return a_disk_id; +} + +static array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> generate_id_by_label(const DiskInfo &disk_info) { + array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> a_disk_id; + a_disk_id.fill(0); + strncpy((char *)&a_disk_id[0], disk_info.label, a_disk_id.size() - 1); + return a_disk_id; +} + +static FUNCTION_RETURN generate_disk_pc_id(vector<array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA>> &v_disk_id) { + std::vector<DiskInfo> disk_infos; + FUNCTION_RETURN result_diskinfos = getDiskInfos(disk_infos); if (result_diskinfos != FUNC_RET_OK && result_diskinfos != FUNC_RET_BUFFER_TOO_SMALL) { return result_diskinfos; } - if (disk_num == 0) { + if (disk_infos.size() == 0) { return FUNC_RET_NOT_AVAIL; } - size_t mem = disk_num * sizeof(DiskInfo); - DiskInfo *diskInfos = (DiskInfo *)malloc(mem); - if (diskInfos == nullptr) { - return FUNC_RET_NOT_AVAIL; - } - memset(diskInfos, 0, mem); - result_diskinfos = getDiskInfos(diskInfos, &disk_num); - if (result_diskinfos != FUNC_RET_OK) { - free(diskInfos); - return result_diskinfos; - } - for (unsigned int i = 0; i < disk_num; i++) { - char firstChar = use_id ? diskInfos[i].label[0] : diskInfos[i].disk_sn[0]; - available_disk_info += firstChar == 0 ? 0 : 1; - } - if (available_disk_info == 0) { - free(diskInfos); - return FUNC_RET_NOT_AVAIL; - } - v_disk_id.reserve(available_disk_info); + v_disk_id.reserve(disk_infos.size() * 2); for (int j = 0; j < 2; j++) { - int preferred = (j == 0 ? 1 : 0); - for (unsigned int i = 0; i < disk_num; i++) { - array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> a_disk_id; - a_disk_id.fill(0); - if (use_id) { - if (diskInfos[i].disk_sn[0] != 0 && diskInfos[i].preferred == preferred) { - size_t size = min(a_disk_id.size(), sizeof(&diskInfos[i].disk_sn)); - memcpy(&a_disk_id[0], diskInfos[i].disk_sn, size); + bool preferred = (j == 0); + for (unsigned int i = 0; i < disk_infos.size(); i++) { + if (disk_infos[i].preferred == preferred) { + if (disk_infos[i].sn_initialized) { + array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> a_disk_id = generate_id_by_sn(disk_infos[i]); v_disk_id.push_back(a_disk_id); } - } else { - if (diskInfos[i].label[0] != 0 && diskInfos[i].preferred == preferred) { - strncpy((char *)&a_disk_id[0], diskInfos[i].label, a_disk_id.size() - 1); - v_disk_id.push_back(a_disk_id); + if (disk_infos[i].label_initialized) { + array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> l_disk_id = generate_id_by_label(disk_infos[i]); + v_disk_id.push_back(l_disk_id); + } + if (preferred) { + break; } } } } - free(diskInfos); return FUNC_RET_OK; } - -DiskStrategy::DiskStrategy(bool use_id) : m_use_id(use_id) {} DiskStrategy::~DiskStrategy() {} LCC_API_HW_IDENTIFICATION_STRATEGY DiskStrategy::identification_strategy() const { - return m_use_id ? STRATEGY_DISK_NUM : STRATEGY_DISK_LABEL; + return LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DISK; } std::vector<HwIdentifier> DiskStrategy::alternative_ids() const { vector<array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA>> data; - FUNCTION_RETURN result = generate_disk_pc_id(data, m_use_id); + FUNCTION_RETURN result = generate_disk_pc_id(data); vector<HwIdentifier> identifiers; if (result == FUNC_RET_OK) { identifiers.reserve(data.size()); diff --git a/src/library/hw_identifier/disk_strategy.hpp b/src/library/hw_identifier/disk_strategy.hpp index 33e3909..2ca990b 100644 --- a/src/library/hw_identifier/disk_strategy.hpp +++ b/src/library/hw_identifier/disk_strategy.hpp @@ -14,10 +14,8 @@ namespace hw_identifier { class DiskStrategy : public IdentificationStrategy { -private: - bool m_use_id; public: - DiskStrategy(bool use_id); + inline DiskStrategy(){}; virtual ~DiskStrategy(); virtual LCC_API_HW_IDENTIFICATION_STRATEGY identification_strategy() const; virtual std::vector<HwIdentifier> alternative_ids() const; diff --git a/src/library/hw_identifier/identification_strategy.cpp b/src/library/hw_identifier/identification_strategy.cpp index 794b97b..ad9cf46 100644 --- a/src/library/hw_identifier/identification_strategy.cpp +++ b/src/library/hw_identifier/identification_strategy.cpp @@ -45,11 +45,8 @@ case STRATEGY_IP_ADDRESS: result = unique_ptr<IdentificationStrategy>(dynamic_cast<IdentificationStrategy*>(new Ethernet(true))); break; - case STRATEGY_DISK_NUM: - result = unique_ptr<IdentificationStrategy>(dynamic_cast<IdentificationStrategy*>(new DiskStrategy(true))); - break; - case STRATEGY_DISK_LABEL: - result = unique_ptr<IdentificationStrategy>(dynamic_cast<IdentificationStrategy*>(new DiskStrategy(false))); + case STRATEGY_DISK: + result = unique_ptr<IdentificationStrategy>(dynamic_cast<IdentificationStrategy*>(new DiskStrategy())); break; default: throw logic_error("strategy not supported"); diff --git a/src/library/os/linux/os_linux.cpp b/src/library/os/linux/os_linux.cpp index 45c2f61..d4c7cd7 100644 --- a/src/library/os/linux/os_linux.cpp +++ b/src/library/os/linux/os_linux.cpp @@ -3,7 +3,13 @@ #include <stdio.h> #include <cerrno> #include <cstring> +#include <iostream> +#include <fstream> +#include <unordered_map> #include <string> +#include <stdio.h> +#include <string.h> +#include <sstream> #include "../os.h" #include "../../base/logger.h" @@ -14,6 +20,13 @@ #include <valgrind/memcheck.h> #endif +#ifdef USE_DISK_MODEL +#define PARSE_ID_FUNC parse_disk_id +#define ID_FOLDER "/dev/disk/by-id/" +#else +#define PARSE_ID_FUNC parseUUID +#define ID_FOLDER "/dev/disk/by-uuid/" +#endif #ifdef USE_DBUS #include <dbus-1.0/dbus/dbus.h> #endif @@ -55,138 +68,197 @@ free(hexuuid); } -#define MAX_UNITS 40 -FUNCTION_RETURN getDiskInfos(DiskInfo *diskInfos, size_t *disk_info_size) { - struct stat mount_stat, sym_stat; - /*static char discard[1024]; - char device[64], name[64], type[64]; - */ - char cur_dir[MAX_PATH]; - struct mntent *ent; +static void parse_disk_id(const char *uuid, unsigned char *buffer_out, size_t out_size) { + unsigned int i; + size_t len = strlen(uuid); + memset(buffer_out, 0, out_size); + for (i = 0; i < len; i++) { + buffer_out[i % out_size] = buffer_out[i % out_size] ^ uuid[i]; + } +} - int maxDrives, currentDrive, i, drive_found; - __ino64_t *statDrives = NULL; - DiskInfo *tmpDrives = NULL; - FILE *aFile = NULL; - DIR *disk_by_uuid_dir = NULL, *disk_by_label = NULL; +/** + * int id; + char device[MAX_PATH]; + unsigned char disk_sn[8]; + char label[255]; + int preferred; + * @param blkidfile + * @param diskInfos_out + * @return + */ + +static std::string getAttribute(const std::string &source, const std::string &attrName) { + std::string attr_namefull = attrName + "=\""; + std::size_t startpos = source.find(attr_namefull) + attr_namefull.size(); + std::size_t endpos = source.find("\"", startpos); + return source.substr(startpos, endpos - startpos); +} + +FUNCTION_RETURN parse_blkid(const std::string &blkid_file_content, std::vector<DiskInfo> &diskInfos_out) { + DiskInfo diskInfo; + int diskNum = 0; + for (std::size_t oldpos = 0, pos = 0; (pos = blkid_file_content.find("</device>", oldpos)) != std::string::npos; + oldpos = pos + 1) { + std::string cur_dev = blkid_file_content.substr(oldpos, pos); + diskInfo.id = diskNum++; + std::string device = cur_dev.substr(cur_dev.find_last_of(">") + 1); + strncpy(diskInfo.device, device.c_str(), MAX_PATH); + std::string label = getAttribute(cur_dev, "PARTLABEL"); + strncpy(diskInfo.label, label.c_str(), 255); + std::string disk_sn = getAttribute(cur_dev, "UUID"); + parseUUID(disk_sn.c_str(), diskInfo.disk_sn, sizeof(diskInfo.disk_sn)); + std::string disk_type = getAttribute(cur_dev, "TYPE"); + // unlikely that somebody put the swap on a removable disk. + // this is a first rough guess on what can be a preferred disk for blkid devices + // just in case /etc/fstab can't be accessed or it is not up to date. + diskInfo.preferred = (disk_type == "swap"); + diskInfos_out.push_back(diskInfo); + } + return FUNCTION_RETURN::FUNC_RET_OK; +} + +#define BLKID_LOCATIONS {"/run/blkid/blkid.tab", "/etc/blkid.tab"}; + +static FUNCTION_RETURN getDiskInfos_blkid(std::vector<DiskInfo> &diskInfos) { + const char *strs[] = BLKID_LOCATIONS; + bool can_read = false; + std::stringstream buffer; + for (int i = 0; i < sizeof(strs) / sizeof(const char *); i++) { + const char *location = strs[i]; + std::ifstream t(location); + if (t.is_open()) { + buffer << t.rdbuf(); + can_read = true; + break; + } + } + if (!can_read) { + return FUNCTION_RETURN::FUNC_RET_NOT_AVAIL; + } + + return parse_blkid(buffer.str(), diskInfos); +} + +#define MAX_UNITS 40 +FUNCTION_RETURN getDiskInfos_dev(std::vector<DiskInfo> &diskInfos) { struct dirent *dir = NULL; + struct stat sym_stat; FUNCTION_RETURN result; - if (diskInfos != NULL) { - maxDrives = *disk_info_size; - tmpDrives = diskInfos; + DIR *disk_by_uuid_dir = opendir(ID_FOLDER); + if (disk_by_uuid_dir == nullptr) { + LOG_DEBUG("Open " ID_FOLDER " fail"); } else { - maxDrives = MAX_UNITS; - tmpDrives = (DiskInfo *)malloc(sizeof(DiskInfo) * maxDrives); - } - memset(tmpDrives, 0, sizeof(DiskInfo) * maxDrives); - statDrives = (__ino64_t *)malloc(maxDrives * sizeof(__ino64_t)); - memset(statDrives, 0, sizeof(__ino64_t) * maxDrives); - - aFile = setmntent("/proc/mounts", "r"); - if (aFile == NULL) { - /*proc not mounted*/ - free(tmpDrives); - free(statDrives); - return FUNC_RET_ERROR; - } - - currentDrive = 0; - while (NULL != (ent = getmntent(aFile)) && currentDrive < maxDrives) { - if ((strncmp(ent->mnt_type, "ext", 3) == 0 || strncmp(ent->mnt_type, "xfs", 3) == 0 || - strncmp(ent->mnt_type, "vfat", 4) == 0 || strncmp(ent->mnt_type, "ntfs", 4) == 0 || - strncmp(ent->mnt_type, "btr", 3) == 0) && - ent->mnt_fsname != NULL && strncmp(ent->mnt_fsname, "/dev/", 5) == 0) { - if (stat(ent->mnt_fsname, &mount_stat) == 0) { - drive_found = -1; - for (i = 0; i < currentDrive; i++) { - if (statDrives[i] == mount_stat.st_ino) { - drive_found = i; - } - } - if (drive_found == -1) { - LOG_DEBUG("mntent fs:[%s],dir:[%s],inode:[%d]\n", ent->mnt_fsname, ent->mnt_dir, - (unsigned long int)mount_stat.st_ino); - strncpy(tmpDrives[currentDrive].device, ent->mnt_fsname, 255 - 1); - statDrives[currentDrive] = mount_stat.st_ino; - drive_found = currentDrive; - currentDrive++; - } - if (strcmp(ent->mnt_dir, "/") == 0) { - strcpy(tmpDrives[drive_found].label, "root"); - LOG_DEBUG("drive %s set to preferred\n", ent->mnt_fsname); - tmpDrives[drive_found].preferred = 1; - } else { - tmpDrives[drive_found].preferred = 0; - } - } else { - LOG_DEBUG("Error %s during stat of %s \n", std::strerror(errno), ent->mnt_fsname); + const std::string base_dir(ID_FOLDER "/"); + while ((dir = readdir(disk_by_uuid_dir)) != nullptr && diskInfos.size() < MAX_UNITS) { + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0 || + strncmp(dir->d_name, "usb", 3) == 0) { + continue; } - } - } - endmntent(aFile); - if (diskInfos == NULL) { - *disk_info_size = currentDrive; - free(tmpDrives); - result = (currentDrive > 0) ? FUNC_RET_OK : FUNC_RET_NOT_AVAIL; - } else if (*disk_info_size >= currentDrive) { - disk_by_uuid_dir = opendir("/dev/disk/by-uuid"); - if (disk_by_uuid_dir == nullptr) { - LOG_WARN("Open /dev/disk/by-uuid fail"); - free(statDrives); - return FUNC_RET_ERROR; - } - result = FUNC_RET_OK; - *disk_info_size = currentDrive; - - while ((dir = readdir(disk_by_uuid_dir)) != nullptr) { - std::string cur_dir("/dev/disk/by-uuid/"); - cur_dir += dir->d_name; - - bool found = false; + std::string cur_dir = base_dir + dir->d_name; if (stat(cur_dir.c_str(), &sym_stat) == 0) { - for (i = 0; i < currentDrive; i++) { - if (sym_stat.st_ino == statDrives[i]) { - found = true; - parseUUID(dir->d_name, tmpDrives[i].disk_sn, sizeof(tmpDrives[i].disk_sn)); -#ifndef NDEBUG - VALGRIND_CHECK_VALUE_IS_DEFINED(tmpDrives[i].device); - LOG_DEBUG("uuid %d %s %02x%02x%02x%02x", i, tmpDrives[i].device, tmpDrives[i].disk_sn[0], - tmpDrives[i].disk_sn[1], tmpDrives[i].disk_sn[2], tmpDrives[i].disk_sn[3]); -#endif + DiskInfo tmpDiskInfo; + tmpDiskInfo.id = sym_stat.st_ino; + ssize_t len = ::readlink(cur_dir.c_str(), tmpDiskInfo.device, sizeof(tmpDiskInfo.device) - 1); + if (len != -1) { + tmpDiskInfo.device[len] = '\0'; + PARSE_ID_FUNC(dir->d_name, tmpDiskInfo.disk_sn, sizeof(tmpDiskInfo.disk_sn)); + tmpDiskInfo.sn_initialized = true; + tmpDiskInfo.label_initialized = false; + tmpDiskInfo.preferred = false; + bool found = false; + for (auto diskInfo : diskInfos) { + if (tmpDiskInfo.id == diskInfo.id) { + found = true; + break; + } } - } - if (!found) { - LOG_DEBUG("Drive [%s], num [%d] inode [%d] did not match any existing drive", cur_dir.c_str(), - (unsigned long int)sym_stat.st_ino); + if (!found) { + diskInfos.push_back(tmpDiskInfo); + } + } else { + LOG_DEBUG("Error %s during readlink of %s", std::strerror(errno), cur_dir.c_str()); } } else { LOG_DEBUG("Error %s during stat of %s", std::strerror(errno), cur_dir.c_str()); } } closedir(disk_by_uuid_dir); + } - disk_by_label = opendir("/dev/disk/by-label"); - if (disk_by_label != nullptr) { - while ((dir = readdir(disk_by_label)) != nullptr) { - strcpy(cur_dir, "/dev/disk/by-label/"); - strcat(cur_dir, dir->d_name); - if (stat(cur_dir, &sym_stat) == 0) { - for (i = 0; i < currentDrive; i++) { - if (sym_stat.st_ino == statDrives[i]) { - strncpy(tmpDrives[i].label, dir->d_name, 255 - 1); - LOG_DEBUG("label %d %s %s", i, tmpDrives[i].label, tmpDrives[i].device); - } + result = diskInfos.size() > 0 ? FUNCTION_RETURN::FUNC_RET_OK : FUNCTION_RETURN::FUNC_RET_NOT_AVAIL; + const std::string label_dir("/dev/disk/by-label"); + DIR *disk_by_label = opendir(label_dir.c_str()); + if (disk_by_label != nullptr) { + while ((dir = readdir(disk_by_label)) != nullptr) { + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) { + continue; + } + std::string cur_disk_label = label_dir + "/" + dir->d_name; + if (stat(cur_disk_label.c_str(), &sym_stat) == 0) { + bool found = false; + for (auto diskInfo : diskInfos) { + if (((int)sym_stat.st_ino) == diskInfo.id) { + strncpy(diskInfo.label, dir->d_name, 255 - 1); + diskInfo.label_initialized = true; + break; } } + } else { + LOG_DEBUG("Stat %s for fail:F %s", cur_disk_label, std::strerror(errno)); } - closedir(disk_by_label); } + closedir(disk_by_label); } else { - result = FUNC_RET_BUFFER_TOO_SMALL; + LOG_DEBUG("Open %s for reading disk labels fail", label_dir); } - free(statDrives); + + return result; +} + +/** + * Try to determine removable devices: as a first guess removable devices doesn't have + * an entry in /etc/fstab + * + * @param diskInfos + */ +static void set_preferred_disks(std::vector<DiskInfo> &diskInfos) { + FILE *fstabFile = setmntent("/etc/fstab", "r"); + if (fstabFile == nullptr) { + /*fstab not accessible*/ + return; + } + struct mntent *ent; + while (nullptr != (ent = getmntent(fstabFile))) { + bool found = false; + for (auto disk_info : diskInfos) { + if (strcmp(ent->mnt_fsname, disk_info.device) == 0) { + disk_info.preferred = true; + break; + } + } + } + endmntent(fstabFile); + return; +} + +/** + * First try to read disk_infos from /dev/disk/by-id folder, if fails try to use + * blkid cache to see what's in there, then try to exclude removable disks + * looking at /etc/fstab + * @param diskInfos_out vector used to output the disk informations + * @return + */ +FUNCTION_RETURN getDiskInfos(std::vector<DiskInfo> &disk_infos) { + FUNCTION_RETURN result = getDiskInfos_dev(disk_infos); + if (result != FUNCTION_RETURN::FUNC_RET_OK) { + result = getDiskInfos_blkid(disk_infos); + } + if (result == FUNCTION_RETURN::FUNC_RET_OK) { + set_preferred_disks(disk_infos); + } return result; } diff --git a/src/library/os/os.h b/src/library/os/os.h index fa1862f..36a1c69 100644 --- a/src/library/os/os.h +++ b/src/library/os/os.h @@ -8,31 +8,32 @@ #ifndef OS_DEPENDENT_HPP_ #define OS_DEPENDENT_HPP_ -#ifdef __cplusplus -extern "C" { -#endif - #include <stddef.h> #include <string.h> #include <ctype.h> #include <sys/types.h> // definition of size_t #include <stdlib.h> +#include <vector> #ifdef __unix__ #include <unistd.h> #include <stdbool.h> #endif + +#include <licensecc/datatypes.h> #include "../base/base.h" typedef struct { int id; char device[MAX_PATH]; unsigned char disk_sn[8]; + bool sn_initialized; char label[255]; - int preferred; + bool label_initialized; + bool preferred; } DiskInfo; -FUNCTION_RETURN getDiskInfos(DiskInfo* diskInfos, size_t* disk_info_size); +FUNCTION_RETURN getDiskInfos(std::vector<DiskInfo>& diskInfos); FUNCTION_RETURN getUserHomePath(char[MAX_PATH]); FUNCTION_RETURN getModuleName(char buffer[MAX_PATH]); FUNCTION_RETURN getMachineName(unsigned char identifier[6]); @@ -56,7 +57,6 @@ */ FUNCTION_RETURN getOsSpecificIdentifier(unsigned char identifier[6]); -// FUNCTION_RETURN verifySignature(const char* stringToVerify, const char* signatureB64); #ifdef _WIN32 #define SETENV(VAR, VAL) _putenv_s(VAR, VAL); @@ -64,10 +64,6 @@ #else #define SETENV(VAR, VAL) setenv(VAR, VAL, 1); #define UNSETENV(P) unsetenv(P); -#endif - -#ifdef __cplusplus -} #endif #endif /* OS_DEPENDENT_HPP_ */ diff --git a/src/library/os/windows/os_win.cpp b/src/library/os/windows/os_win.cpp index b6094da..cfa6b0d 100644 --- a/src/library/os/windows/os_win.cpp +++ b/src/library/os/windows/os_win.cpp @@ -21,77 +21,63 @@ return result; } -//http://www.ok-soft-gmbh.com/ForStackOverflow/EnumMassStorage.c -//http://stackoverflow.com/questions/3098696/same-code-returns-diffrent-result-on-windows7-32-bit-system +// http://www.ok-soft-gmbh.com/ForStackOverflow/EnumMassStorage.c +// http://stackoverflow.com/questions/3098696/same-code-returns-diffrent-result-on-windows7-32-bit-system #define MAX_UNITS 30 -//bug check return with diskinfos == null func_ret_ok -FUNCTION_RETURN getDiskInfos(DiskInfo * diskInfos, size_t * disk_info_size) { +// bug check return with diskinfos == null func_ret_ok +FUNCTION_RETURN getDiskInfos(std::vector<DiskInfo>& diskInfos) { DWORD fileMaxLen; size_t ndrives = 0; DWORD fileFlags; char volName[MAX_PATH], fileSysName[MAX_PATH]; DWORD volSerial = 0; const DWORD dwSize = MAX_PATH; - char szLogicalDrives[MAX_PATH] = { 0 }; - + char szLogicalDrives[MAX_PATH] = {0}; FUNCTION_RETURN return_value = FUNC_RET_NOT_AVAIL; const DWORD dwResult = GetLogicalDriveStrings(dwSize, szLogicalDrives); - if (dwResult > 0 && dwResult <= MAX_PATH) { + if (dwResult > 0) { return_value = FUNC_RET_OK; char* szSingleDrive = szLogicalDrives; while (*szSingleDrive && ndrives < MAX_UNITS) { - // get the next drive UINT driveType = GetDriveType(szSingleDrive); if (driveType == DRIVE_FIXED) { - BOOL success = GetVolumeInformation(szSingleDrive, volName, MAX_PATH, - &volSerial, &fileMaxLen, &fileFlags, fileSysName, - MAX_PATH); + BOOL success = GetVolumeInformation(szSingleDrive, volName, MAX_PATH, &volSerial, &fileMaxLen, + &fileFlags, fileSysName, MAX_PATH); if (success) { LOG_INFO("drive : %s", szSingleDrive); LOG_INFO("Volume Name : %s", volName); LOG_INFO("Volume Serial : 0x%x", volSerial); LOG_DEBUG("Max file length : %d", fileMaxLen); LOG_DEBUG("Filesystem : %s", fileSysName); - if (diskInfos != NULL) { - if (ndrives < (int)*disk_info_size) { - diskInfos[ndrives].id = (int)ndrives; - strncpy(diskInfos[ndrives].device, volName, - min(std::size_t{MAX_PATH}, sizeof(volName)) - 1); - strncpy(diskInfos[ndrives].label, fileSysName, - min(sizeof(diskInfos[ndrives].label), sizeof(fileSysName)) - 1); - memcpy(diskInfos[ndrives].disk_sn, &volSerial, sizeof(DWORD)); - diskInfos[ndrives].preferred = (szSingleDrive[0] == 'C'); - } else { - return_value = FUNC_RET_BUFFER_TOO_SMALL; - } - } + DiskInfo diskInfo; + diskInfo.id = (int)ndrives; + strncpy(diskInfo.device, volName, min(std::size_t{MAX_PATH}, sizeof(volName)) - 1); + strncpy(diskInfo.label, fileSysName, + min(sizeof(diskInfos[ndrives].label), sizeof(fileSysName)) - 1); + memcpy(diskInfo.disk_sn, &volSerial, sizeof(DWORD)); + diskInfo.preferred = (szSingleDrive[0] == 'C'); + diskInfos.push_back(diskInfo); ndrives++; } else { - LOG_WARN("Unable to retrieve information of '%s'", szSingleDrive); + LOG_DEBUG("Unable to retrieve information of '%s'", szSingleDrive); } } else { - LOG_INFO("This volume is not fixed : %s, type: %d", szSingleDrive); + LOG_DEBUG("This volume is not fixed : %s, type: %d", szSingleDrive); } - szSingleDrive += strlen(szSingleDrive) + 1; } } - if (diskInfos == NULL || *disk_info_size == 0) { - if (ndrives > 0) { - return_value = FUNC_RET_OK; - } else { - return_value = FUNC_RET_NOT_AVAIL; - LOG_INFO("No fixed drive was detected"); - } - *disk_info_size = ndrives; + if (diskInfos.size() > 0) { + return_value = FUNC_RET_OK; } else { - *disk_info_size = min(ndrives, *disk_info_size); + return_value = FUNC_RET_NOT_AVAIL; + LOG_INFO("No fixed drive were detected"); } + return return_value; } - FUNCTION_RETURN getModuleName(char buffer[MAX_PATH]) { FUNCTION_RETURN result = FUNC_RET_OK; @@ -101,4 +87,3 @@ } return result; } - diff --git a/src/templates/licensecc_properties.h.in b/src/templates/licensecc_properties.h.in index 15f6452..1dd89e8 100644 --- a/src/templates/licensecc_properties.h.in +++ b/src/templates/licensecc_properties.h.in @@ -78,8 +78,9 @@ enum LCC_API_HW_IDENTIFICATION_STRATEGY { /** * \brief Default strategy. - * - * This strategy try to detect which virtual environment the software is running in. + * + * This strategy first checks the content of the environment variable `IDENTIFICATION_STRATEGY`. If the variable is defined it will use the + * strategy specified in there, if not defined it will try to detect which virtual environment the software is running in. * - If no virtual environment is detected it will try the strategies defined in ::LCC_BARE_TO_METAL_STRATEGIES * - If it detects the software is running in a virtual machine it will use ::LCC_VM_STRATEGIES * - If it detects the software is running in a docker or in an LXC it will use ::LCC_DOCKER_STRATEGIES or @@ -89,10 +90,15 @@ STRATEGY_DEFAULT = -1, STRATEGY_ETHERNET = 0, STRATEGY_IP_ADDRESS = 1, - STRATEGY_DISK_NUM = 2, - STRATEGY_DISK_LABEL = 3, - STRATEGY_MEMORY_CPU_SIZE = 4, - STRATEGY_HOST_NAME = 5, + STRATEGY_DISK = 2, + /** + * Not yet implemented + */ + STRATEGY_CPU_SIZE = 3, + /** + * Not yet implemented + */ + STRATEGY_HOST_NAME = 4, STRATEGY_NONE = -2 }; @@ -103,7 +109,7 @@ /** * List the strategies used when no virtual envrionment is detected */ -#define LCC_BARE_TO_METAL_STRATEGIES { STRATEGY_ETHERNET, STRATEGY_DISK_LABEL, STRATEGY_NONE } +#define LCC_BARE_TO_METAL_STRATEGIES { STRATEGY_DISK, STRATEGY_HOST_NAME, STRATEGY_NONE } /** * List the strategies used when the software is executing in a virtual machine */ diff --git a/test/functional/hw_identifier_it_test.cpp b/test/functional/hw_identifier_it_test.cpp index 7f200ee..ae92cb5 100644 --- a/test/functional/hw_identifier_it_test.cpp +++ b/test/functional/hw_identifier_it_test.cpp @@ -49,23 +49,12 @@ BOOST_AUTO_TEST_CASE(volid_lic_file) { HwIdentifier identifier_out; - size_t disk_num; - FUNCTION_RETURN result_diskinfos = getDiskInfos(nullptr, &disk_num); - if ((result_diskinfos == FUNC_RET_BUFFER_TOO_SMALL || result_diskinfos == FUNC_RET_OK) && disk_num > 0) { - generate_and_verify_license(LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DISK_NUM, "volid_lic_file"); + vector<DiskInfo> diskInfos; + FUNCTION_RETURN result_diskinfos = getDiskInfos(diskInfos); + if ((result_diskinfos == FUNC_RET_BUFFER_TOO_SMALL || result_diskinfos == FUNC_RET_OK) && diskInfos.size() > 0) { + generate_and_verify_license(LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DISK, "volid_lic_file"); } else { BOOST_TEST_MESSAGE("No disk found skipping testing disk hardware identifier"); - } -} - -BOOST_AUTO_TEST_CASE(volume_name_lic_file) { - HwIdentifier identifier_out; - size_t disk_num; - FUNCTION_RETURN result_diskinfos = getDiskInfos(nullptr, &disk_num); - if ((result_diskinfos == FUNC_RET_BUFFER_TOO_SMALL || result_diskinfos == FUNC_RET_OK) && disk_num > 0) { - generate_and_verify_license(LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DISK_LABEL, "volume_name_lic_file"); - } else { - BOOST_TEST_MESSAGE("No disk found skipping testing volume name disk hardware identifier"); } } diff --git a/test/library/os_linux_test.cpp b/test/library/os_linux_test.cpp index b44beb4..3a481d6 100644 --- a/test/library/os_linux_test.cpp +++ b/test/library/os_linux_test.cpp @@ -9,39 +9,64 @@ #include "../../src/library/os/os.h" #include "../../src/library/os/execution_environment.hpp" +FUNCTION_RETURN parse_blkid(const std::string &blkid_file_content, std::vector<DiskInfo> &diskInfos_out); + namespace license { namespace test { using namespace std; -using namespace os; +// using namespace os; BOOST_AUTO_TEST_CASE(read_disk_id) { os::ExecutionEnvironment exec_env; LCC_API_VIRTUALIZATION_SUMMARY virt = exec_env.virtualization(); + vector<DiskInfo> disk_infos; + FUNCTION_RETURN result = getDiskInfos(disk_infos); if (virt == LCC_API_VIRTUALIZATION_SUMMARY::NONE || virt == LCC_API_VIRTUALIZATION_SUMMARY::VM) { - DiskInfo *diskInfos = NULL; - size_t disk_info_size = 0; - FUNCTION_RETURN result = getDiskInfos(NULL, &disk_info_size); BOOST_CHECK_EQUAL(result, FUNC_RET_OK); - BOOST_CHECK_GT(disk_info_size, 0); - diskInfos = (DiskInfo*) malloc(sizeof(DiskInfo) * disk_info_size); - result = getDiskInfos(diskInfos, &disk_info_size); - BOOST_CHECK_EQUAL(result, FUNC_RET_OK); - BOOST_CHECK_GT(mstrnlen_s(diskInfos[0].device, sizeof(diskInfos[0].device)), 0); - BOOST_CHECK_GT(mstrnlen_s(diskInfos[0].label, sizeof diskInfos[0].label), 0); - bool all_zero = true; - for (int i = 0; i < sizeof(diskInfos[0].disk_sn) && all_zero; i++) { - all_zero = (diskInfos[0].disk_sn[i] == 0); + BOOST_REQUIRE_MESSAGE(disk_infos.size() > 0, "Found some disk"); + bool preferred_found = false; + bool uuid_found = false; + bool label_found = false; + + for (auto disk_info : disk_infos) { + uuid_found |= disk_info.sn_initialized; + preferred_found |= disk_info.preferred; + label_found |= disk_info.label_initialized; + + if (disk_info.sn_initialized) { + bool all_zero = true; + for (int i = 0; i < sizeof(disk_info.disk_sn) && all_zero; i++) { + all_zero = (disk_info.disk_sn[i] == 0); + } + BOOST_CHECK_MESSAGE(!all_zero, "disksn is not all zero"); + } } - BOOST_CHECK_MESSAGE(!all_zero, "disksn is not all zero"); - free(diskInfos); + BOOST_CHECK_MESSAGE(uuid_found, "At least one UUID initialized"); + BOOST_CHECK_MESSAGE(label_found, "At least one label found"); + BOOST_CHECK_MESSAGE(preferred_found, "At least one standard 'root' file system"); } else if (virt == LCC_API_VIRTUALIZATION_SUMMARY::CONTAINER) { - // docker or lxc diskInfo is not meaningful - DiskInfo *diskInfos = NULL; - size_t disk_info_size = 0; - FUNCTION_RETURN result = getDiskInfos(NULL, &disk_info_size); + // in docker or lxc diskInfo is very likely not to find any good disk. BOOST_CHECK_EQUAL(result, FUNC_RET_NOT_AVAIL); + BOOST_REQUIRE_MESSAGE(disk_infos.size() == 0, "Found no disk"); } } -} // namespace test -} // namespace license +BOOST_AUTO_TEST_CASE(parse_blkid_file) { + const string blkid_content = + "<device DEVNO=\"0x0803\" TIME=\"1603155692.238672\" " + "UUID=\"baccfd49-5203-4e34-9b8b-a2bbaf9b4e24\" TYPE=\"swap\" PARTLABEL=\"Linux swap\" " + "PARTUUID=\"7d84b1a8-5492-4651-b720-61c723fb8c69\">/dev/sda3</device>" + "<device DEVNO=\"0x10302\" TIME=\"1603155692.253094\" UUID=\"d1b5b096-5e58-4e4f-af39-be12038c9bed\" " + "TYPE=\"ext4\" PARTLABEL=\"Linux filesystem\" PARTUUID=\"3d742821-3167-43fa-9f22-e9bea9a9ce64\">" + "/dev/nvme0n1p2</device>"; + vector<DiskInfo> disk_infos; + FUNCTION_RETURN result = parse_blkid(blkid_content, disk_infos); + BOOST_CHECK_EQUAL(result, FUNC_RET_OK); + BOOST_CHECK_MESSAGE(disk_infos.size() == 2, "Two disks found"); + BOOST_CHECK_MESSAGE(string("Linux swap") == disk_infos[0].label, "Label parsed OK"); + BOOST_CHECK_MESSAGE(string("/dev/sda3") == disk_infos[0].device, "device parsed"); + BOOST_CHECK_MESSAGE(disk_infos[0].preferred, "Preferred found"); +} + +} // namespace test +} // namespace license -- Gitblit v1.9.1