From 1ed767df41ae029ba8cb8f8164132871c02be390 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 14 Mar 2021 01:11:34 +0100 Subject: [PATCH] Implement Linux machine fingerprints We do this by using either /etc/machine-id, or hashing the MACs of all non-virtual networking devices. We make this fingerpint unique to our platform by adding our own string to the hash, and add some stretching for good measure. This way Vircadia's fingerprint isn't going to match the system's ID or anything another application generates. --- libraries/networking/src/FingerprintUtils.cpp | 79 ++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 216e0f28dd..83a4f19703 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #ifdef Q_OS_WIN #include @@ -36,8 +39,80 @@ QUuid FingerprintUtils::_machineFingerprint { QUuid() }; QString FingerprintUtils::getMachineFingerprintString() { QString uuidString; #ifdef Q_OS_LINUX - // sadly need to be root to get smbios guid from linux, so - // for now lets do nothing. + // As per the documentation: + // https://man7.org/linux/man-pages/man5/machine-id.5.html + // + // we use the machine id as a base, but add an application-specific key to it. + // If machine-id isn't available, we try hardware networking devices instead. + + QCryptographicHash hash(QCryptographicHash::Keccak_256); + + QFile machineIdFile("/etc/machine-id"); + if (!machineIdFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + // No machine ID, probably no systemd. + qCWarning(networking) << "Failed to open /etc/machine-id"; + + QDir netDevicesDir("/sys/class/net"); + QFileInfoList netDevicesInfo = netDevicesDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); + + if (netDevicesInfo.empty()) { + // Let getMachineFingerprint handle this contingency + qCWarning(networking) << "Failed to find any hardware networking devices"; + return ""; + } + + for(auto& fileInfo : netDevicesInfo) { + + if (fileInfo.isSymLink() && fileInfo.symLinkTarget().contains("virtual")) { + // symlink points to something like: + // ../../devices/virtual/net/lo + // these are not real devices and have random IDs, so we + // don't care about them. + continue; + } + + QFile addressFile(fileInfo.filePath() + "/address"); + if (addressFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCDebug(networking) << "Adding contents of " << addressFile.fileName(); + hash.addData(addressFile.readAll()); + } else { + qCWarning(networking) << "Failed to read " << addressFile.fileName(); + } + } + } else { + QByteArray data = machineIdFile.readAll(); + hash.addData(data); + } + + // Makes this hash unique to us + hash.addData("Vircadia"); + + // Stretching + for (int i=0; i < 65535; i++) { + hash.addData(hash.result()); + } + + QByteArray result = hash.result(); + result.resize(128 / 8); // GUIDs are 128 bit numbers + + // Set UUID version to 4, ensuring it's a valid UUID + result[6] = (result[6] & 0x0F) | 0x40; + + // Of course, Qt couldn't be nice and just parse something like: + // 1b1b9d6d45c2473bac13dc3011ff58d6 + // + // So we have to turn it into: + // {1b1b9d6d-45c2-473b-ac13-dc3011ff58d6} + + uuidString = result.toHex(); + uuidString.insert(20, '-'); + uuidString.insert(16, '-'); + uuidString.insert(12, '-'); + uuidString.insert(8, '-'); + uuidString.prepend("{"); + uuidString.append("}"); + + qCDebug(networking) << "Linux machine fingerprint:" << uuidString; #endif //Q_OS_LINUX #ifdef Q_OS_MAC