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.
This commit is contained in:
Dale Glass 2021-03-14 01:11:34 +01:00
parent 5e2cc4c4bf
commit 1ed767df41

View file

@ -17,6 +17,9 @@
#include <SettingHandle.h>
#include <SettingManager.h>
#include <DependencyManager.h>
#include <QFile>
#include <QCryptographicHash>
#include <QDirIterator>
#ifdef Q_OS_WIN
#include <Windows.h>
@ -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